comparison 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
comparison
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