##// END OF EJS Templates
tags: support 'instant' tag retrieval (issue548)...
Greg Ward -
r9152:4017291c default
parent child Browse files
Show More
@@ -265,11 +265,17 b' class localrepository(repo.repository):'
265 tags_.findglobaltags(self.ui, self, alltags, tagtypes)
265 tags_.findglobaltags(self.ui, self, alltags, tagtypes)
266 tags_.readlocaltags(self.ui, self, alltags, tagtypes)
266 tags_.readlocaltags(self.ui, self, alltags, tagtypes)
267
267
268 # Build the return dicts. Have to re-encode tag names because
269 # the tags module always uses UTF-8 (in order not to lose info
270 # writing to the cache), but the rest of Mercurial wants them in
271 # local encoding.
268 tags = {}
272 tags = {}
269 for (name, (node, hist)) in alltags.iteritems():
273 for (name, (node, hist)) in alltags.iteritems():
270 if node != nullid:
274 if node != nullid:
271 tags[name] = node
275 tags[encoding.tolocal(name)] = node
272 tags['tip'] = self.changelog.tip()
276 tags['tip'] = self.changelog.tip()
277 tagtypes = dict([(encoding.tolocal(name), value)
278 for (name, value) in tagtypes.iteritems()])
273 return (tags, tagtypes)
279 return (tags, tagtypes)
274
280
275 def tagtype(self, tagname):
281 def tagtype(self, tagname):
@@ -59,7 +59,19 b' def findglobaltags1(ui, repo, alltags, t'
59
59
60 def findglobaltags2(ui, repo, alltags, tagtypes):
60 def findglobaltags2(ui, repo, alltags, tagtypes):
61 '''Same as findglobaltags1(), but with caching.'''
61 '''Same as findglobaltags1(), but with caching.'''
62 (heads, tagfnode, shouldwrite) = _readtagcache(ui, repo)
62 # This is so we can be lazy and assume alltags contains only global
63 # tags when we pass it to _writetagcache().
64 assert len(alltags) == len(tagtypes) == 0, \
65 "findglobaltags() should be called first"
66
67 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
68 if cachetags is not None:
69 assert not shouldwrite
70 # XXX is this really 100% correct? are there oddball special
71 # cases where a global tag should outrank a local tag but won't,
72 # because cachetags does not contain rank info?
73 _updatetags(cachetags, 'global', alltags, tagtypes)
74 return
63
75
64 _debug(ui, "reading tags from %d head(s): %s\n"
76 _debug(ui, "reading tags from %d head(s): %s\n"
65 % (len(heads), map(short, reversed(heads))))
77 % (len(heads), map(short, reversed(heads))))
@@ -82,7 +94,7 b' def findglobaltags2(ui, repo, alltags, t'
82
94
83 # and update the cache (if necessary)
95 # and update the cache (if necessary)
84 if shouldwrite:
96 if shouldwrite:
85 _writetagcache(ui, repo, heads, tagfnode)
97 _writetagcache(ui, repo, heads, tagfnode, alltags)
86
98
87 # Set this to findglobaltags1 to disable tag caching.
99 # Set this to findglobaltags1 to disable tag caching.
88 findglobaltags = findglobaltags2
100 findglobaltags = findglobaltags2
@@ -90,16 +102,17 b' findglobaltags = findglobaltags2'
90 def readlocaltags(ui, repo, alltags, tagtypes):
102 def readlocaltags(ui, repo, alltags, tagtypes):
91 '''Read local tags in repo. Update alltags and tagtypes.'''
103 '''Read local tags in repo. Update alltags and tagtypes.'''
92 try:
104 try:
93 data = encoding.fromlocal(repo.opener("localtags").read())
105 # localtags is in the local encoding; re-encode to UTF-8 on
94 # localtags are stored in the local character set
106 # input for consistency with the rest of this module.
95 # while the internal tag table is stored in UTF-8
107 data = repo.opener("localtags").read()
96 filetags = _readtags(
108 filetags = _readtags(
97 ui, repo, data.splitlines(), "localtags")
109 ui, repo, data.splitlines(), "localtags",
110 recode=encoding.fromlocal)
98 _updatetags(filetags, "local", alltags, tagtypes)
111 _updatetags(filetags, "local", alltags, tagtypes)
99 except IOError:
112 except IOError:
100 pass
113 pass
101
114
102 def _readtags(ui, repo, lines, fn):
115 def _readtags(ui, repo, lines, fn, recode=None):
103 '''Read tag definitions from a file (or any source of lines).
116 '''Read tag definitions from a file (or any source of lines).
104 Return a mapping from tag name to (node, hist): node is the node id
117 Return a mapping from tag name to (node, hist): node is the node id
105 from the last line read for that name, and hist is the list of node
118 from the last line read for that name, and hist is the list of node
@@ -121,7 +134,9 b' def _readtags(ui, repo, lines, fn):'
121 except ValueError:
134 except ValueError:
122 warn(_("cannot parse entry"))
135 warn(_("cannot parse entry"))
123 continue
136 continue
124 name = encoding.tolocal(name.strip()) # stored in UTF-8
137 name = name.strip()
138 if recode:
139 name = recode(name)
125 try:
140 try:
126 nodebin = bin(nodehex)
141 nodebin = bin(nodehex)
127 except TypeError:
142 except TypeError:
@@ -173,11 +188,13 b' def _updatetags(filetags, tagtype, allta'
173 # the repo.
188 # the repo.
174
189
175 def _readtagcache(ui, repo):
190 def _readtagcache(ui, repo):
176 '''Read the tag cache and return a tuple (heads, fnodes,
191 '''Read the tag cache and return a tuple (heads, fnodes, cachetags,
177 shouldwrite). heads is the list of all heads currently in the
192 shouldwrite). If the cache is completely up-to-date, cachetags is a
178 repository (ordered from tip to oldest) and fnodes is a mapping from
193 dict of the form returned by _readtags(); otherwise, it is None and
179 head to .hgtags filenode. Caller is responsible for reading tag
194 heads and fnodes are set. In that case, heads is the list of all
180 info from each head.'''
195 heads currently in the repository (ordered from tip to oldest) and
196 fnodes is a mapping from head to .hgtags filenode. If those two are
197 set, caller is responsible for reading tag info from each head.'''
181
198
182 try:
199 try:
183 cachefile = repo.opener('tags.cache', 'r')
200 cachefile = repo.opener('tags.cache', 'r')
@@ -202,6 +219,8 b' def _readtagcache(ui, repo):'
202 cachefnode = {} # map headnode to filenode
219 cachefnode = {} # map headnode to filenode
203 if cachefile:
220 if cachefile:
204 for line in cachefile:
221 for line in cachefile:
222 if line == "\n":
223 break
205 line = line.rstrip().split()
224 line = line.rstrip().split()
206 cacherevs.append(int(line[0]))
225 cacherevs.append(int(line[0]))
207 headnode = bin(line[1])
226 headnode = bin(line[1])
@@ -210,8 +229,6 b' def _readtagcache(ui, repo):'
210 fnode = bin(line[2])
229 fnode = bin(line[2])
211 cachefnode[headnode] = fnode
230 cachefnode[headnode] = fnode
212
231
213 cachefile.close()
214
215 tipnode = repo.changelog.tip()
232 tipnode = repo.changelog.tip()
216 tiprev = len(repo.changelog) - 1
233 tiprev = len(repo.changelog) - 1
217
234
@@ -221,14 +238,18 b' def _readtagcache(ui, repo):'
221 # have been destroyed by strip or rollback.)
238 # have been destroyed by strip or rollback.)
222 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
239 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
223 _debug(ui, "tag cache: tip unchanged\n")
240 _debug(ui, "tag cache: tip unchanged\n")
224 return (cacheheads, cachefnode, False)
241 tags = _readtags(ui, repo, cachefile, cachefile.name)
242 cachefile.close()
243 return (None, None, tags, False)
244 if cachefile:
245 cachefile.close() # ignore rest of file
225
246
226 repoheads = repo.heads()
247 repoheads = repo.heads()
227
248
228 # Case 2 (uncommon): empty repo; get out quickly and don't bother
249 # Case 2 (uncommon): empty repo; get out quickly and don't bother
229 # writing an empty cache.
250 # writing an empty cache.
230 if repoheads == [nullid]:
251 if repoheads == [nullid]:
231 return ([], {}, False)
252 return ([], {}, {}, False)
232
253
233 # Case 3 (uncommon): cache file missing or empty.
254 # Case 3 (uncommon): cache file missing or empty.
234 if not cacheheads:
255 if not cacheheads:
@@ -277,9 +298,9 b' def _readtagcache(ui, repo):'
277
298
278 # Caller has to iterate over all heads, but can use the filenodes in
299 # Caller has to iterate over all heads, but can use the filenodes in
279 # cachefnode to get to each .hgtags revision quickly.
300 # cachefnode to get to each .hgtags revision quickly.
280 return (repoheads, cachefnode, True)
301 return (repoheads, cachefnode, None, True)
281
302
282 def _writetagcache(ui, repo, heads, tagfnode):
303 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
283
304
284 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
305 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
285 _debug(ui, 'writing cache file %s\n' % cachefile.name)
306 _debug(ui, 'writing cache file %s\n' % cachefile.name)
@@ -306,5 +327,13 b' def _writetagcache(ui, repo, heads, tagf'
306 else:
327 else:
307 cachefile.write('%d %s\n' % (rev, hex(head)))
328 cachefile.write('%d %s\n' % (rev, hex(head)))
308
329
330 # Tag names in the cache are in UTF-8 -- which is the whole reason
331 # we keep them in UTF-8 throughout this module. If we converted
332 # them local encoding on input, we would lose info writing them to
333 # the cache.
334 cachefile.write('\n')
335 for (name, (node, hist)) in cachetags.iteritems():
336 cachefile.write("%s %s\n" % (hex(node), name))
337
309 cachefile.rename()
338 cachefile.rename()
310 cachefile.close()
339 cachefile.close()
@@ -113,10 +113,12 b' patch queue now empty'
113 % qpush with dump of tag cache
113 % qpush with dump of tag cache
114 .hg/tags.cache (pre qpush):
114 .hg/tags.cache (pre qpush):
115 1
115 1
116
116 applying test.patch
117 applying test.patch
117 now at: test.patch
118 now at: test.patch
118 .hg/tags.cache (post qpush):
119 .hg/tags.cache (post qpush):
119 2
120 2
121
120 % pop/push outside repo
122 % pop/push outside repo
121 popping test.patch
123 popping test.patch
122 patch queue now empty
124 patch queue now empty
@@ -4,6 +4,10 b' cacheexists() {'
4 [ -f .hg/tags.cache ] && echo "tag cache exists" || echo "no tag cache"
4 [ -f .hg/tags.cache ] && echo "tag cache exists" || echo "no tag cache"
5 }
5 }
6
6
7 # XXX need to test that the tag cache works when we strip an old head
8 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
9 # same, but stuff has changed behind tip.
10
7 echo "% setup"
11 echo "% setup"
8 mkdir t
12 mkdir t
9 cd t
13 cd t
@@ -46,9 +46,6 b' created new head'
46 tip 8:c4be69a18c11
46 tip 8:c4be69a18c11
47 first 0:acb14030fe0a
47 first 0:acb14030fe0a
48 changeset: 8:c4be69a18c11
48 changeset: 8:c4be69a18c11
49 .hgtags@75d9f02dfe28, line 2: cannot parse entry
50 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
51 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
52 tag: tip
49 tag: tip
53 parent: 3:ac5e980c4dc0
50 parent: 3:ac5e980c4dc0
54 user: test
51 user: test
@@ -80,6 +77,8 b' bbd179dfa0a71671c253b3ae0aa1513b60d199fa'
80 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
77 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
81 3 6fa450212aeb2a21ed616a54aea39a4a27894cd7 7d3b718c964ef37b89e550ebdafd5789e76ce1b0
78 3 6fa450212aeb2a21ed616a54aea39a4a27894cd7 7d3b718c964ef37b89e550ebdafd5789e76ce1b0
82 2 7a94127795a33c10a370c93f731fd9fea0b79af6 0c04f2a8af31de17fab7422878ee5a2dadbc943d
79 2 7a94127795a33c10a370c93f731fd9fea0b79af6 0c04f2a8af31de17fab7422878ee5a2dadbc943d
80
81 78391a272241d70354aa14c874552cad6b51bb42 bar
83 % test tag removal
82 % test tag removal
84 changeset: 5:5f6e8655b1c7
83 changeset: 5:5f6e8655b1c7
85 tag: tip
84 tag: tip
General Comments 0
You need to be logged in to leave comments. Login now