Mercurial > hg
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 |