[PATCH 07 of 10] Factor tags module out of localrepo (issue548)

Greg Ward greg-hg at gerg.ca
Thu Jul 16 09:42:53 CDT 2009


# HG changeset patch
# User Greg Ward <greg-hg at gerg.ca>
# Date 1247755181 14400
# Node ID b611553591f5fc9a1a46e6ff4e4fac3b53939e41
# Parent  4630649ba193485101b629c0f4b0997d63ab101c
Factor tags module out of localrepo (issue548).

Currently only handles reading tags, and will soon grow support for
tag caching.  Could eventually deal with updating tags too.

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -13,6 +13,7 @@
 import util, extensions, hook, error
 import match as match_
 import merge as merge_
+import tags as tags_
 from lock import release
 import weakref, stat, errno, os, time, inspect
 propertycache = util.propertycache
@@ -258,102 +259,11 @@
         # be one tagtype for all such "virtual" tags?  Or is the status
         # quo fine?
 
-        def readtags(lines, fn):
-            '''Read tag definitions from a file (or any source of
-            lines).  Return a mapping from tag name to (node, hist):
-            node is the node id from the last line read for that name,
-            and hist is the list of node ids previously associated with
-            it (in file order).  All node ids are binary, not hex.'''
-
-            filetags = {}               # map tag name to (node, hist)
-            count = 0
-
-            def warn(msg):
-                self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
-
-            for line in lines:
-                count += 1
-                if not line:
-                    continue
-                try:
-                    (nodehex, name) = line.split(" ", 1)
-                except ValueError:
-                    warn(_("cannot parse entry"))
-                    continue
-                name = encoding.tolocal(name.strip()) # stored in UTF-8
-                try:
-                    nodebin = bin(nodehex)
-                except TypeError:
-                    warn(_("node '%s' is not well formed") % nodehex)
-                    continue
-                if nodebin not in self.changelog.nodemap:
-                    # silently ignore as pull -r might cause this
-                    continue
-
-                # update filetags
-                hist = []
-                if name in filetags:
-                    n, hist = filetags[name]
-                    hist.append(n)
-                filetags[name] = (nodebin, hist)
-            return filetags
-
         alltags = {}                    # map tag name to (node, hist)
         tagtypes = {}
 
-        def updatetags(filetags, tagtype):
-            '''Incorporate the tag info read from one file into the two
-            dictionaries, alltags and tagtypes, that contain all tag
-            info (global across all heads plus local).'''
-
-            for name, nodehist in filetags.iteritems():
-                if name not in alltags:
-                    alltags[name] = nodehist
-                    tagtypes[name] = tagtype
-                    continue
-
-                # we prefer alltags[name] if:
-                #  it supercedes us OR
-                #  mutual supercedes and it has a higher rank
-                # otherwise we win because we're tip-most
-                anode, ahist = nodehist
-                bnode, bhist = alltags[name]
-                if (bnode != anode and anode in bhist and
-                    (bnode not in ahist or len(bhist) > len(ahist))):
-                    anode = bnode
-                ahist.extend([n for n in bhist if n not in ahist])
-                alltags[name] = anode, ahist
-                tagtypes[name] = tagtype
-
-        seen = set()
-        fctx = None
-        ctxs = []                       # list of filectx
-        for node in self.heads():
-            try:
-                fnode = self[node].filenode('.hgtags')
-            except error.LookupError:
-                continue
-            if fnode not in seen:
-                seen.add(fnode)
-                if not fctx:
-                    fctx = self.filectx('.hgtags', fileid=fnode)
-                else:
-                    fctx = fctx.filectx(fnode)
-                ctxs.append(fctx)
-
-        # read the tags file from each head, ending with the tip
-        for fctx in reversed(ctxs):
-            filetags = readtags(fctx.data().splitlines(), fctx)
-            updatetags(filetags, "global")
-
-        try:
-            data = encoding.fromlocal(self.opener("localtags").read())
-            # localtags are stored in the local character set
-            # while the internal tag table is stored in UTF-8
-            filetags = readtags(data.splitlines(), "localtags")
-            updatetags(filetags, "local")
-        except IOError:
-            pass
+        tags_.findglobaltags(self.ui, self, alltags, tagtypes)
+        tags_.readlocaltags(self.ui, self, alltags, tagtypes)
 
         tags = {}
         for (name, (node, hist)) in alltags.iteritems():
diff --git a/mercurial/tags.py b/mercurial/tags.py
new file mode 100644
--- /dev/null
+++ b/mercurial/tags.py
@@ -0,0 +1,122 @@
+# tags.py - read tag info from local repository
+#
+# Copyright 2009 Matt Mackall <mpm at selenic.com>
+# Copyright 2009 Greg Ward <greg at gerg.ca>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+# Currently this module only deals with reading tags.  Soon it will grow
+# support for caching tag info.  Eventually, it could take care of
+# updating (adding/removing/moving) tags too.
+
+from node import bin, hex
+from i18n import _
+import encoding
+import error
+
+def findglobaltags(ui, repo, alltags, tagtypes):
+    '''Find global tags in repo by reading .hgtags from every head that
+    has a distinct version of it.  Updates the dicts alltags, tagtypes
+    in place: alltags maps tag name to (node, hist) pair (see _readtags()
+    below), and tagtypes maps tag name to tag type ('global' in this
+    case).'''
+
+    seen = set()
+    fctx = None
+    ctxs = []                       # list of filectx
+    for node in repo.heads():
+        try:
+            fnode = repo[node].filenode('.hgtags')
+        except error.LookupError:
+            continue
+        if fnode not in seen:
+            seen.add(fnode)
+            if not fctx:
+                fctx = repo.filectx('.hgtags', fileid=fnode)
+            else:
+                fctx = fctx.filectx(fnode)
+            ctxs.append(fctx)
+
+    # read the tags file from each head, ending with the tip
+    for fctx in reversed(ctxs):
+        filetags = _readtags(
+            ui, repo, fctx.data().splitlines(), fctx)
+        _updatetags(filetags, "global", alltags, tagtypes)
+
+def readlocaltags(ui, repo, alltags, tagtypes):
+    '''Read local tags in repo.  Update alltags and tagtypes.'''
+    try:
+        data = encoding.fromlocal(repo.opener("localtags").read())
+        # localtags are stored in the local character set
+        # while the internal tag table is stored in UTF-8
+        filetags = _readtags(
+            ui, repo, data.splitlines(), "localtags")
+        _updatetags(filetags, "local", alltags, tagtypes)
+    except IOError:
+        pass
+
+def _readtags(ui, repo, lines, fn):
+    '''Read tag definitions from a file (or any source of lines).
+    Return a mapping from tag name to (node, hist): node is the node id
+    from the last line read for that name, and hist is the list of node
+    ids previously associated with it (in file order).  All node ids are
+    binary, not hex.'''
+
+    filetags = {}               # map tag name to (node, hist)
+    count = 0
+
+    def warn(msg):
+        ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
+
+    for line in lines:
+        count += 1
+        if not line:
+            continue
+        try:
+            (nodehex, name) = line.split(" ", 1)
+        except ValueError:
+            warn(_("cannot parse entry"))
+            continue
+        name = encoding.tolocal(name.strip()) # stored in UTF-8
+        try:
+            nodebin = bin(nodehex)
+        except TypeError:
+            warn(_("node '%s' is not well formed") % nodehex)
+            continue
+        if nodebin not in repo.changelog.nodemap:
+            # silently ignore as pull -r might cause this
+            continue
+
+        # update filetags
+        hist = []
+        if name in filetags:
+            n, hist = filetags[name]
+            hist.append(n)
+        filetags[name] = (nodebin, hist)
+    return filetags
+
+def _updatetags(filetags, tagtype, alltags, tagtypes):
+    '''Incorporate the tag info read from one file into the two
+    dictionaries, alltags and tagtypes, that contain all tag
+    info (global across all heads plus local).'''
+
+    for name, nodehist in filetags.iteritems():
+        if name not in alltags:
+            alltags[name] = nodehist
+            tagtypes[name] = tagtype
+            continue
+
+        # we prefer alltags[name] if:
+        #  it supercedes us OR
+        #  mutual supercedes and it has a higher rank
+        # otherwise we win because we're tip-most
+        anode, ahist = nodehist
+        bnode, bhist = alltags[name]
+        if (bnode != anode and anode in bhist and
+            (bnode not in ahist or len(bhist) > len(ahist))):
+            anode = bnode
+        ahist.extend([n for n in bhist if n not in ahist])
+        alltags[name] = anode, ahist
+        tagtypes[name] = tagtype
+


More information about the Mercurial-devel mailing list