Mercurial > hg
diff mercurial/tags.py @ 9149:abb7d4d43a5f
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.
author | Greg Ward <greg-hg@gerg.ca> |
---|---|
date | Thu, 16 Jul 2009 10:39:41 -0400 |
parents | |
children | f528d1a93491 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/tags.py Thu Jul 16 10:39:41 2009 -0400 @@ -0,0 +1,122 @@ +# tags.py - read tag info from local repository +# +# Copyright 2009 Matt Mackall <mpm@selenic.com> +# Copyright 2009 Greg Ward <greg@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 +