mercurial/tags.py
changeset 9149 abb7d4d43a5f
child 9151 f528d1a93491
equal deleted inserted replaced
9148:b7837f0ed9fe 9149:abb7d4d43a5f
       
     1 # tags.py - read tag info from local repository
       
     2 #
       
     3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
       
     4 # Copyright 2009 Greg Ward <greg@gerg.ca>
       
     5 #
       
     6 # This software may be used and distributed according to the terms of the
       
     7 # GNU General Public License version 2, incorporated herein by reference.
       
     8 
       
     9 # Currently this module only deals with reading tags.  Soon it will grow
       
    10 # support for caching tag info.  Eventually, it could take care of
       
    11 # updating (adding/removing/moving) tags too.
       
    12 
       
    13 from node import bin, hex
       
    14 from i18n import _
       
    15 import encoding
       
    16 import error
       
    17 
       
    18 def findglobaltags(ui, repo, alltags, tagtypes):
       
    19     '''Find global tags in repo by reading .hgtags from every head that
       
    20     has a distinct version of it.  Updates the dicts alltags, tagtypes
       
    21     in place: alltags maps tag name to (node, hist) pair (see _readtags()
       
    22     below), and tagtypes maps tag name to tag type ('global' in this
       
    23     case).'''
       
    24 
       
    25     seen = set()
       
    26     fctx = None
       
    27     ctxs = []                       # list of filectx
       
    28     for node in repo.heads():
       
    29         try:
       
    30             fnode = repo[node].filenode('.hgtags')
       
    31         except error.LookupError:
       
    32             continue
       
    33         if fnode not in seen:
       
    34             seen.add(fnode)
       
    35             if not fctx:
       
    36                 fctx = repo.filectx('.hgtags', fileid=fnode)
       
    37             else:
       
    38                 fctx = fctx.filectx(fnode)
       
    39             ctxs.append(fctx)
       
    40 
       
    41     # read the tags file from each head, ending with the tip
       
    42     for fctx in reversed(ctxs):
       
    43         filetags = _readtags(
       
    44             ui, repo, fctx.data().splitlines(), fctx)
       
    45         _updatetags(filetags, "global", alltags, tagtypes)
       
    46 
       
    47 def readlocaltags(ui, repo, alltags, tagtypes):
       
    48     '''Read local tags in repo.  Update alltags and tagtypes.'''
       
    49     try:
       
    50         data = encoding.fromlocal(repo.opener("localtags").read())
       
    51         # localtags are stored in the local character set
       
    52         # while the internal tag table is stored in UTF-8
       
    53         filetags = _readtags(
       
    54             ui, repo, data.splitlines(), "localtags")
       
    55         _updatetags(filetags, "local", alltags, tagtypes)
       
    56     except IOError:
       
    57         pass
       
    58 
       
    59 def _readtags(ui, repo, lines, fn):
       
    60     '''Read tag definitions from a file (or any source of lines).
       
    61     Return a mapping from tag name to (node, hist): node is the node id
       
    62     from the last line read for that name, and hist is the list of node
       
    63     ids previously associated with it (in file order).  All node ids are
       
    64     binary, not hex.'''
       
    65 
       
    66     filetags = {}               # map tag name to (node, hist)
       
    67     count = 0
       
    68 
       
    69     def warn(msg):
       
    70         ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
       
    71 
       
    72     for line in lines:
       
    73         count += 1
       
    74         if not line:
       
    75             continue
       
    76         try:
       
    77             (nodehex, name) = line.split(" ", 1)
       
    78         except ValueError:
       
    79             warn(_("cannot parse entry"))
       
    80             continue
       
    81         name = encoding.tolocal(name.strip()) # stored in UTF-8
       
    82         try:
       
    83             nodebin = bin(nodehex)
       
    84         except TypeError:
       
    85             warn(_("node '%s' is not well formed") % nodehex)
       
    86             continue
       
    87         if nodebin not in repo.changelog.nodemap:
       
    88             # silently ignore as pull -r might cause this
       
    89             continue
       
    90 
       
    91         # update filetags
       
    92         hist = []
       
    93         if name in filetags:
       
    94             n, hist = filetags[name]
       
    95             hist.append(n)
       
    96         filetags[name] = (nodebin, hist)
       
    97     return filetags
       
    98 
       
    99 def _updatetags(filetags, tagtype, alltags, tagtypes):
       
   100     '''Incorporate the tag info read from one file into the two
       
   101     dictionaries, alltags and tagtypes, that contain all tag
       
   102     info (global across all heads plus local).'''
       
   103 
       
   104     for name, nodehist in filetags.iteritems():
       
   105         if name not in alltags:
       
   106             alltags[name] = nodehist
       
   107             tagtypes[name] = tagtype
       
   108             continue
       
   109 
       
   110         # we prefer alltags[name] if:
       
   111         #  it supercedes us OR
       
   112         #  mutual supercedes and it has a higher rank
       
   113         # otherwise we win because we're tip-most
       
   114         anode, ahist = nodehist
       
   115         bnode, bhist = alltags[name]
       
   116         if (bnode != anode and anode in bhist and
       
   117             (bnode not in ahist or len(bhist) > len(ahist))):
       
   118             anode = bnode
       
   119         ahist.extend([n for n in bhist if n not in ahist])
       
   120         alltags[name] = anode, ahist
       
   121         tagtypes[name] = tagtype
       
   122