##// END OF EJS Templates
tags: remove scary message about corrupt tags cache...
Matt Mackall -
r24532:f5de208a default
parent child Browse files
Show More
@@ -1,391 +1,390 b''
1 # tags.py - read tag info from local repository
1 # tags.py - read tag info from local repository
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
4 # Copyright 2009 Greg Ward <greg@gerg.ca>
4 # Copyright 2009 Greg Ward <greg@gerg.ca>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 # Currently this module only deals with reading and caching tags.
9 # Currently this module only deals with reading and caching tags.
10 # Eventually, it could take care of updating (adding/removing/moving)
10 # Eventually, it could take care of updating (adding/removing/moving)
11 # tags too.
11 # tags too.
12
12
13 from node import nullid, bin, hex, short
13 from node import nullid, bin, hex, short
14 from i18n import _
14 from i18n import _
15 import util
15 import util
16 import encoding
16 import encoding
17 import error
17 import error
18 import errno
18 import errno
19 import time
19 import time
20
20
21 # The tags cache stores information about heads and the history of tags.
21 # The tags cache stores information about heads and the history of tags.
22 #
22 #
23 # The cache file consists of two parts. The first part maps head nodes
23 # The cache file consists of two parts. The first part maps head nodes
24 # to .hgtags filenodes. The second part is a history of tags. The two
24 # to .hgtags filenodes. The second part is a history of tags. The two
25 # parts are separated by an empty line.
25 # parts are separated by an empty line.
26 #
26 #
27 # The first part consists of lines of the form:
27 # The first part consists of lines of the form:
28 #
28 #
29 # <headrev> <headnode> [<hgtagsnode>]
29 # <headrev> <headnode> [<hgtagsnode>]
30 #
30 #
31 # <headrev> is an integer revision and <headnode> is a 40 character hex
31 # <headrev> is an integer revision and <headnode> is a 40 character hex
32 # node for that changeset. These redundantly identify a repository
32 # node for that changeset. These redundantly identify a repository
33 # head from the time the cache was written.
33 # head from the time the cache was written.
34 #
34 #
35 # <tagnode> is the filenode of .hgtags on that head. Heads with no .hgtags
35 # <tagnode> is the filenode of .hgtags on that head. Heads with no .hgtags
36 # file will have no <hgtagsnode> (just 2 values per line).
36 # file will have no <hgtagsnode> (just 2 values per line).
37 #
37 #
38 # The filenode cache is ordered from tip to oldest (which is part of why
38 # The filenode cache is ordered from tip to oldest (which is part of why
39 # <headrev> is there: a quick check of the tip from when the cache was
39 # <headrev> is there: a quick check of the tip from when the cache was
40 # written against the current tip is all that is needed to check whether
40 # written against the current tip is all that is needed to check whether
41 # the cache is up to date).
41 # the cache is up to date).
42 #
42 #
43 # The purpose of the filenode cache is to avoid the most expensive part
43 # The purpose of the filenode cache is to avoid the most expensive part
44 # of finding global tags, which is looking up the .hgtags filenode in the
44 # of finding global tags, which is looking up the .hgtags filenode in the
45 # manifest for each head. This can take over a minute on repositories
45 # manifest for each head. This can take over a minute on repositories
46 # that have large manifests and many heads.
46 # that have large manifests and many heads.
47 #
47 #
48 # The second part of the tags cache consists of lines of the form:
48 # The second part of the tags cache consists of lines of the form:
49 #
49 #
50 # <node> <tag>
50 # <node> <tag>
51 #
51 #
52 # (This format is identical to that of .hgtags files.)
52 # (This format is identical to that of .hgtags files.)
53 #
53 #
54 # <tag> is the tag name and <node> is the 40 character hex changeset
54 # <tag> is the tag name and <node> is the 40 character hex changeset
55 # the tag is associated with.
55 # the tag is associated with.
56 #
56 #
57 # Tags are written sorted by tag name.
57 # Tags are written sorted by tag name.
58 #
58 #
59 # Tags associated with multiple changesets have an entry for each changeset.
59 # Tags associated with multiple changesets have an entry for each changeset.
60 # The most recent changeset (in terms of revlog ordering for the head
60 # The most recent changeset (in terms of revlog ordering for the head
61 # setting it) for each tag is last.
61 # setting it) for each tag is last.
62
62
63 def findglobaltags(ui, repo, alltags, tagtypes):
63 def findglobaltags(ui, repo, alltags, tagtypes):
64 '''Find global tags in a repo.
64 '''Find global tags in a repo.
65
65
66 "alltags" maps tag name to (node, hist) 2-tuples.
66 "alltags" maps tag name to (node, hist) 2-tuples.
67
67
68 "tagtypes" maps tag name to tag type. Global tags always have the
68 "tagtypes" maps tag name to tag type. Global tags always have the
69 "global" tag type.
69 "global" tag type.
70
70
71 The "alltags" and "tagtypes" dicts are updated in place. Empty dicts
71 The "alltags" and "tagtypes" dicts are updated in place. Empty dicts
72 should be passed in.
72 should be passed in.
73
73
74 The tags cache is read and updated as a side-effect of calling.
74 The tags cache is read and updated as a side-effect of calling.
75 '''
75 '''
76 # This is so we can be lazy and assume alltags contains only global
76 # This is so we can be lazy and assume alltags contains only global
77 # tags when we pass it to _writetagcache().
77 # tags when we pass it to _writetagcache().
78 assert len(alltags) == len(tagtypes) == 0, \
78 assert len(alltags) == len(tagtypes) == 0, \
79 "findglobaltags() should be called first"
79 "findglobaltags() should be called first"
80
80
81 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
81 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
82 if cachetags is not None:
82 if cachetags is not None:
83 assert not shouldwrite
83 assert not shouldwrite
84 # XXX is this really 100% correct? are there oddball special
84 # XXX is this really 100% correct? are there oddball special
85 # cases where a global tag should outrank a local tag but won't,
85 # cases where a global tag should outrank a local tag but won't,
86 # because cachetags does not contain rank info?
86 # because cachetags does not contain rank info?
87 _updatetags(cachetags, 'global', alltags, tagtypes)
87 _updatetags(cachetags, 'global', alltags, tagtypes)
88 return
88 return
89
89
90 seen = set() # set of fnode
90 seen = set() # set of fnode
91 fctx = None
91 fctx = None
92 for head in reversed(heads): # oldest to newest
92 for head in reversed(heads): # oldest to newest
93 assert head in repo.changelog.nodemap, \
93 assert head in repo.changelog.nodemap, \
94 "tag cache returned bogus head %s" % short(head)
94 "tag cache returned bogus head %s" % short(head)
95
95
96 fnode = tagfnode.get(head)
96 fnode = tagfnode.get(head)
97 if fnode and fnode not in seen:
97 if fnode and fnode not in seen:
98 seen.add(fnode)
98 seen.add(fnode)
99 if not fctx:
99 if not fctx:
100 fctx = repo.filectx('.hgtags', fileid=fnode)
100 fctx = repo.filectx('.hgtags', fileid=fnode)
101 else:
101 else:
102 fctx = fctx.filectx(fnode)
102 fctx = fctx.filectx(fnode)
103
103
104 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
104 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
105 _updatetags(filetags, 'global', alltags, tagtypes)
105 _updatetags(filetags, 'global', alltags, tagtypes)
106
106
107 # and update the cache (if necessary)
107 # and update the cache (if necessary)
108 if shouldwrite:
108 if shouldwrite:
109 _writetagcache(ui, repo, heads, tagfnode, alltags)
109 _writetagcache(ui, repo, heads, tagfnode, alltags)
110
110
111 def readlocaltags(ui, repo, alltags, tagtypes):
111 def readlocaltags(ui, repo, alltags, tagtypes):
112 '''Read local tags in repo. Update alltags and tagtypes.'''
112 '''Read local tags in repo. Update alltags and tagtypes.'''
113 try:
113 try:
114 data = repo.vfs.read("localtags")
114 data = repo.vfs.read("localtags")
115 except IOError, inst:
115 except IOError, inst:
116 if inst.errno != errno.ENOENT:
116 if inst.errno != errno.ENOENT:
117 raise
117 raise
118 return
118 return
119
119
120 # localtags is in the local encoding; re-encode to UTF-8 on
120 # localtags is in the local encoding; re-encode to UTF-8 on
121 # input for consistency with the rest of this module.
121 # input for consistency with the rest of this module.
122 filetags = _readtags(
122 filetags = _readtags(
123 ui, repo, data.splitlines(), "localtags",
123 ui, repo, data.splitlines(), "localtags",
124 recode=encoding.fromlocal)
124 recode=encoding.fromlocal)
125
125
126 # remove tags pointing to invalid nodes
126 # remove tags pointing to invalid nodes
127 cl = repo.changelog
127 cl = repo.changelog
128 for t in filetags.keys():
128 for t in filetags.keys():
129 try:
129 try:
130 cl.rev(filetags[t][0])
130 cl.rev(filetags[t][0])
131 except (LookupError, ValueError):
131 except (LookupError, ValueError):
132 del filetags[t]
132 del filetags[t]
133
133
134 _updatetags(filetags, "local", alltags, tagtypes)
134 _updatetags(filetags, "local", alltags, tagtypes)
135
135
136 def _readtaghist(ui, repo, lines, fn, recode=None, calcnodelines=False):
136 def _readtaghist(ui, repo, lines, fn, recode=None, calcnodelines=False):
137 '''Read tag definitions from a file (or any source of lines).
137 '''Read tag definitions from a file (or any source of lines).
138
138
139 This function returns two sortdicts with similar information:
139 This function returns two sortdicts with similar information:
140
140
141 - the first dict, bintaghist, contains the tag information as expected by
141 - the first dict, bintaghist, contains the tag information as expected by
142 the _readtags function, i.e. a mapping from tag name to (node, hist):
142 the _readtags function, i.e. a mapping from tag name to (node, hist):
143 - node is the node id from the last line read for that name,
143 - node is the node id from the last line read for that name,
144 - hist is the list of node ids previously associated with it (in file
144 - hist is the list of node ids previously associated with it (in file
145 order). All node ids are binary, not hex.
145 order). All node ids are binary, not hex.
146
146
147 - the second dict, hextaglines, is a mapping from tag name to a list of
147 - the second dict, hextaglines, is a mapping from tag name to a list of
148 [hexnode, line number] pairs, ordered from the oldest to the newest node.
148 [hexnode, line number] pairs, ordered from the oldest to the newest node.
149
149
150 When calcnodelines is False the hextaglines dict is not calculated (an
150 When calcnodelines is False the hextaglines dict is not calculated (an
151 empty dict is returned). This is done to improve this function's
151 empty dict is returned). This is done to improve this function's
152 performance in cases where the line numbers are not needed.
152 performance in cases where the line numbers are not needed.
153 '''
153 '''
154
154
155 bintaghist = util.sortdict()
155 bintaghist = util.sortdict()
156 hextaglines = util.sortdict()
156 hextaglines = util.sortdict()
157 count = 0
157 count = 0
158
158
159 def warn(msg):
159 def warn(msg):
160 ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
160 ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
161
161
162 for nline, line in enumerate(lines):
162 for nline, line in enumerate(lines):
163 count += 1
163 count += 1
164 if not line:
164 if not line:
165 continue
165 continue
166 try:
166 try:
167 (nodehex, name) = line.split(" ", 1)
167 (nodehex, name) = line.split(" ", 1)
168 except ValueError:
168 except ValueError:
169 warn(_("cannot parse entry"))
169 warn(_("cannot parse entry"))
170 continue
170 continue
171 name = name.strip()
171 name = name.strip()
172 if recode:
172 if recode:
173 name = recode(name)
173 name = recode(name)
174 try:
174 try:
175 nodebin = bin(nodehex)
175 nodebin = bin(nodehex)
176 except TypeError:
176 except TypeError:
177 warn(_("node '%s' is not well formed") % nodehex)
177 warn(_("node '%s' is not well formed") % nodehex)
178 continue
178 continue
179
179
180 # update filetags
180 # update filetags
181 if calcnodelines:
181 if calcnodelines:
182 # map tag name to a list of line numbers
182 # map tag name to a list of line numbers
183 if name not in hextaglines:
183 if name not in hextaglines:
184 hextaglines[name] = []
184 hextaglines[name] = []
185 hextaglines[name].append([nodehex, nline])
185 hextaglines[name].append([nodehex, nline])
186 continue
186 continue
187 # map tag name to (node, hist)
187 # map tag name to (node, hist)
188 if name not in bintaghist:
188 if name not in bintaghist:
189 bintaghist[name] = []
189 bintaghist[name] = []
190 bintaghist[name].append(nodebin)
190 bintaghist[name].append(nodebin)
191 return bintaghist, hextaglines
191 return bintaghist, hextaglines
192
192
193 def _readtags(ui, repo, lines, fn, recode=None, calcnodelines=False):
193 def _readtags(ui, repo, lines, fn, recode=None, calcnodelines=False):
194 '''Read tag definitions from a file (or any source of lines).
194 '''Read tag definitions from a file (or any source of lines).
195
195
196 Returns a mapping from tag name to (node, hist).
196 Returns a mapping from tag name to (node, hist).
197
197
198 "node" is the node id from the last line read for that name. "hist"
198 "node" is the node id from the last line read for that name. "hist"
199 is the list of node ids previously associated with it (in file order).
199 is the list of node ids previously associated with it (in file order).
200 All node ids are binary, not hex.
200 All node ids are binary, not hex.
201 '''
201 '''
202 filetags, nodelines = _readtaghist(ui, repo, lines, fn, recode=recode,
202 filetags, nodelines = _readtaghist(ui, repo, lines, fn, recode=recode,
203 calcnodelines=calcnodelines)
203 calcnodelines=calcnodelines)
204 for tag, taghist in filetags.items():
204 for tag, taghist in filetags.items():
205 filetags[tag] = (taghist[-1], taghist[:-1])
205 filetags[tag] = (taghist[-1], taghist[:-1])
206 return filetags
206 return filetags
207
207
208 def _updatetags(filetags, tagtype, alltags, tagtypes):
208 def _updatetags(filetags, tagtype, alltags, tagtypes):
209 '''Incorporate the tag info read from one file into the two
209 '''Incorporate the tag info read from one file into the two
210 dictionaries, alltags and tagtypes, that contain all tag
210 dictionaries, alltags and tagtypes, that contain all tag
211 info (global across all heads plus local).'''
211 info (global across all heads plus local).'''
212
212
213 for name, nodehist in filetags.iteritems():
213 for name, nodehist in filetags.iteritems():
214 if name not in alltags:
214 if name not in alltags:
215 alltags[name] = nodehist
215 alltags[name] = nodehist
216 tagtypes[name] = tagtype
216 tagtypes[name] = tagtype
217 continue
217 continue
218
218
219 # we prefer alltags[name] if:
219 # we prefer alltags[name] if:
220 # it supersedes us OR
220 # it supersedes us OR
221 # mutual supersedes and it has a higher rank
221 # mutual supersedes and it has a higher rank
222 # otherwise we win because we're tip-most
222 # otherwise we win because we're tip-most
223 anode, ahist = nodehist
223 anode, ahist = nodehist
224 bnode, bhist = alltags[name]
224 bnode, bhist = alltags[name]
225 if (bnode != anode and anode in bhist and
225 if (bnode != anode and anode in bhist and
226 (bnode not in ahist or len(bhist) > len(ahist))):
226 (bnode not in ahist or len(bhist) > len(ahist))):
227 anode = bnode
227 anode = bnode
228 else:
228 else:
229 tagtypes[name] = tagtype
229 tagtypes[name] = tagtype
230 ahist.extend([n for n in bhist if n not in ahist])
230 ahist.extend([n for n in bhist if n not in ahist])
231 alltags[name] = anode, ahist
231 alltags[name] = anode, ahist
232
232
233 def _readtagcache(ui, repo):
233 def _readtagcache(ui, repo):
234 '''Read the tag cache.
234 '''Read the tag cache.
235
235
236 Returns a tuple (heads, fnodes, cachetags, shouldwrite).
236 Returns a tuple (heads, fnodes, cachetags, shouldwrite).
237
237
238 If the cache is completely up-to-date, "cachetags" is a dict of the
238 If the cache is completely up-to-date, "cachetags" is a dict of the
239 form returned by _readtags() and "heads" and "fnodes" are None and
239 form returned by _readtags() and "heads" and "fnodes" are None and
240 "shouldwrite" is False.
240 "shouldwrite" is False.
241
241
242 If the cache is not up to date, "cachetags" is None. "heads" is a list
242 If the cache is not up to date, "cachetags" is None. "heads" is a list
243 of all heads currently in the repository, ordered from tip to oldest.
243 of all heads currently in the repository, ordered from tip to oldest.
244 "fnodes" is a mapping from head to .hgtags filenode. "shouldwrite" is
244 "fnodes" is a mapping from head to .hgtags filenode. "shouldwrite" is
245 True.
245 True.
246
246
247 If the cache is not up to date, the caller is responsible for reading tag
247 If the cache is not up to date, the caller is responsible for reading tag
248 info from each returned head. (See findglobaltags().)
248 info from each returned head. (See findglobaltags().)
249 '''
249 '''
250
250
251 try:
251 try:
252 cachefile = repo.vfs('cache/tags', 'r')
252 cachefile = repo.vfs('cache/tags', 'r')
253 # force reading the file for static-http
253 # force reading the file for static-http
254 cachelines = iter(cachefile)
254 cachelines = iter(cachefile)
255 except IOError:
255 except IOError:
256 cachefile = None
256 cachefile = None
257
257
258 cacherevs = [] # list of headrev
258 cacherevs = [] # list of headrev
259 cacheheads = [] # list of headnode
259 cacheheads = [] # list of headnode
260 cachefnode = {} # map headnode to filenode
260 cachefnode = {} # map headnode to filenode
261 if cachefile:
261 if cachefile:
262 try:
262 try:
263 for line in cachelines:
263 for line in cachelines:
264 if line == "\n":
264 if line == "\n":
265 break
265 break
266 line = line.split()
266 line = line.split()
267 cacherevs.append(int(line[0]))
267 cacherevs.append(int(line[0]))
268 headnode = bin(line[1])
268 headnode = bin(line[1])
269 cacheheads.append(headnode)
269 cacheheads.append(headnode)
270 if len(line) == 3:
270 if len(line) == 3:
271 fnode = bin(line[2])
271 fnode = bin(line[2])
272 cachefnode[headnode] = fnode
272 cachefnode[headnode] = fnode
273 except Exception:
273 except Exception:
274 # corruption of the tags cache, just recompute it
274 # corruption of the tags cache, just recompute it
275 ui.warn(_('.hg/cache/tags is corrupt, rebuilding it\n'))
276 cacheheads = []
275 cacheheads = []
277 cacherevs = []
276 cacherevs = []
278 cachefnode = {}
277 cachefnode = {}
279
278
280 tipnode = repo.changelog.tip()
279 tipnode = repo.changelog.tip()
281 tiprev = len(repo.changelog) - 1
280 tiprev = len(repo.changelog) - 1
282
281
283 # Case 1 (common): tip is the same, so nothing has changed.
282 # Case 1 (common): tip is the same, so nothing has changed.
284 # (Unchanged tip trivially means no changesets have been added.
283 # (Unchanged tip trivially means no changesets have been added.
285 # But, thanks to localrepository.destroyed(), it also means none
284 # But, thanks to localrepository.destroyed(), it also means none
286 # have been destroyed by strip or rollback.)
285 # have been destroyed by strip or rollback.)
287 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
286 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
288 tags = _readtags(ui, repo, cachelines, cachefile.name)
287 tags = _readtags(ui, repo, cachelines, cachefile.name)
289 cachefile.close()
288 cachefile.close()
290 return (None, None, tags, False)
289 return (None, None, tags, False)
291 if cachefile:
290 if cachefile:
292 cachefile.close() # ignore rest of file
291 cachefile.close() # ignore rest of file
293
292
294 repoheads = repo.heads()
293 repoheads = repo.heads()
295 # Case 2 (uncommon): empty repo; get out quickly and don't bother
294 # Case 2 (uncommon): empty repo; get out quickly and don't bother
296 # writing an empty cache.
295 # writing an empty cache.
297 if repoheads == [nullid]:
296 if repoheads == [nullid]:
298 return ([], {}, {}, False)
297 return ([], {}, {}, False)
299
298
300 # Case 3 (uncommon): cache file missing or empty.
299 # Case 3 (uncommon): cache file missing or empty.
301
300
302 # Case 4 (uncommon): tip rev decreased. This should only happen
301 # Case 4 (uncommon): tip rev decreased. This should only happen
303 # when we're called from localrepository.destroyed(). Refresh the
302 # when we're called from localrepository.destroyed(). Refresh the
304 # cache so future invocations will not see disappeared heads in the
303 # cache so future invocations will not see disappeared heads in the
305 # cache.
304 # cache.
306
305
307 # Case 5 (common): tip has changed, so we've added/replaced heads.
306 # Case 5 (common): tip has changed, so we've added/replaced heads.
308
307
309 # As it happens, the code to handle cases 3, 4, 5 is the same.
308 # As it happens, the code to handle cases 3, 4, 5 is the same.
310
309
311 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
310 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
312 # exposed".
311 # exposed".
313 if not len(repo.file('.hgtags')):
312 if not len(repo.file('.hgtags')):
314 # No tags have ever been committed, so we can avoid a
313 # No tags have ever been committed, so we can avoid a
315 # potentially expensive search.
314 # potentially expensive search.
316 return (repoheads, cachefnode, None, True)
315 return (repoheads, cachefnode, None, True)
317
316
318 starttime = time.time()
317 starttime = time.time()
319
318
320 newheads = [head
319 newheads = [head
321 for head in repoheads
320 for head in repoheads
322 if head not in set(cacheheads)]
321 if head not in set(cacheheads)]
323
322
324 # Now we have to lookup the .hgtags filenode for every new head.
323 # Now we have to lookup the .hgtags filenode for every new head.
325 # This is the most expensive part of finding tags, so performance
324 # This is the most expensive part of finding tags, so performance
326 # depends primarily on the size of newheads. Worst case: no cache
325 # depends primarily on the size of newheads. Worst case: no cache
327 # file, so newheads == repoheads.
326 # file, so newheads == repoheads.
328 for head in reversed(newheads):
327 for head in reversed(newheads):
329 cctx = repo[head]
328 cctx = repo[head]
330 try:
329 try:
331 fnode = cctx.filenode('.hgtags')
330 fnode = cctx.filenode('.hgtags')
332 cachefnode[head] = fnode
331 cachefnode[head] = fnode
333 except error.LookupError:
332 except error.LookupError:
334 # no .hgtags file on this head
333 # no .hgtags file on this head
335 pass
334 pass
336
335
337 duration = time.time() - starttime
336 duration = time.time() - starttime
338 ui.log('tagscache',
337 ui.log('tagscache',
339 'resolved %d tags cache entries from %d manifests in %0.4f '
338 'resolved %d tags cache entries from %d manifests in %0.4f '
340 'seconds\n',
339 'seconds\n',
341 len(cachefnode), len(newheads), duration)
340 len(cachefnode), len(newheads), duration)
342
341
343 # Caller has to iterate over all heads, but can use the filenodes in
342 # Caller has to iterate over all heads, but can use the filenodes in
344 # cachefnode to get to each .hgtags revision quickly.
343 # cachefnode to get to each .hgtags revision quickly.
345 return (repoheads, cachefnode, None, True)
344 return (repoheads, cachefnode, None, True)
346
345
347 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
346 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
348 try:
347 try:
349 cachefile = repo.vfs('cache/tags', 'w', atomictemp=True)
348 cachefile = repo.vfs('cache/tags', 'w', atomictemp=True)
350 except (OSError, IOError):
349 except (OSError, IOError):
351 return
350 return
352
351
353 ui.log('tagscache', 'writing tags cache file with %d heads and %d tags\n',
352 ui.log('tagscache', 'writing tags cache file with %d heads and %d tags\n',
354 len(heads), len(cachetags))
353 len(heads), len(cachetags))
355
354
356 realheads = repo.heads() # for sanity checks below
355 realheads = repo.heads() # for sanity checks below
357 for head in heads:
356 for head in heads:
358 # temporary sanity checks; these can probably be removed
357 # temporary sanity checks; these can probably be removed
359 # once this code has been in crew for a few weeks
358 # once this code has been in crew for a few weeks
360 assert head in repo.changelog.nodemap, \
359 assert head in repo.changelog.nodemap, \
361 'trying to write non-existent node %s to tag cache' % short(head)
360 'trying to write non-existent node %s to tag cache' % short(head)
362 assert head in realheads, \
361 assert head in realheads, \
363 'trying to write non-head %s to tag cache' % short(head)
362 'trying to write non-head %s to tag cache' % short(head)
364 assert head != nullid, \
363 assert head != nullid, \
365 'trying to write nullid to tag cache'
364 'trying to write nullid to tag cache'
366
365
367 # This can't fail because of the first assert above. When/if we
366 # This can't fail because of the first assert above. When/if we
368 # remove that assert, we might want to catch LookupError here
367 # remove that assert, we might want to catch LookupError here
369 # and downgrade it to a warning.
368 # and downgrade it to a warning.
370 rev = repo.changelog.rev(head)
369 rev = repo.changelog.rev(head)
371
370
372 fnode = tagfnode.get(head)
371 fnode = tagfnode.get(head)
373 if fnode:
372 if fnode:
374 cachefile.write('%d %s %s\n' % (rev, hex(head), hex(fnode)))
373 cachefile.write('%d %s %s\n' % (rev, hex(head), hex(fnode)))
375 else:
374 else:
376 cachefile.write('%d %s\n' % (rev, hex(head)))
375 cachefile.write('%d %s\n' % (rev, hex(head)))
377
376
378 # Tag names in the cache are in UTF-8 -- which is the whole reason
377 # Tag names in the cache are in UTF-8 -- which is the whole reason
379 # we keep them in UTF-8 throughout this module. If we converted
378 # we keep them in UTF-8 throughout this module. If we converted
380 # them local encoding on input, we would lose info writing them to
379 # them local encoding on input, we would lose info writing them to
381 # the cache.
380 # the cache.
382 cachefile.write('\n')
381 cachefile.write('\n')
383 for (name, (node, hist)) in sorted(cachetags.iteritems()):
382 for (name, (node, hist)) in sorted(cachetags.iteritems()):
384 for n in hist:
383 for n in hist:
385 cachefile.write("%s %s\n" % (hex(n), name))
384 cachefile.write("%s %s\n" % (hex(n), name))
386 cachefile.write("%s %s\n" % (hex(node), name))
385 cachefile.write("%s %s\n" % (hex(node), name))
387
386
388 try:
387 try:
389 cachefile.close()
388 cachefile.close()
390 except (OSError, IOError):
389 except (OSError, IOError):
391 pass
390 pass
@@ -1,419 +1,418 b''
1 Helper functions:
1 Helper functions:
2
2
3 $ cacheexists() {
3 $ cacheexists() {
4 > [ -f .hg/cache/tags ] && echo "tag cache exists" || echo "no tag cache"
4 > [ -f .hg/cache/tags ] && echo "tag cache exists" || echo "no tag cache"
5 > }
5 > }
6
6
7 $ dumptags() {
7 $ dumptags() {
8 > rev=$1
8 > rev=$1
9 > echo "rev $rev: .hgtags:"
9 > echo "rev $rev: .hgtags:"
10 > hg cat -r$rev .hgtags
10 > hg cat -r$rev .hgtags
11 > }
11 > }
12
12
13 # XXX need to test that the tag cache works when we strip an old head
13 # XXX need to test that the tag cache works when we strip an old head
14 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
14 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
15 # same, but stuff has changed behind tip.
15 # same, but stuff has changed behind tip.
16
16
17 Setup:
17 Setup:
18
18
19 $ hg init t
19 $ hg init t
20 $ cd t
20 $ cd t
21 $ cacheexists
21 $ cacheexists
22 no tag cache
22 no tag cache
23 $ hg id
23 $ hg id
24 000000000000 tip
24 000000000000 tip
25 $ cacheexists
25 $ cacheexists
26 no tag cache
26 no tag cache
27 $ echo a > a
27 $ echo a > a
28 $ hg add a
28 $ hg add a
29 $ hg commit -m "test"
29 $ hg commit -m "test"
30 $ hg co
30 $ hg co
31 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ hg identify
32 $ hg identify
33 acb14030fe0a tip
33 acb14030fe0a tip
34 $ cacheexists
34 $ cacheexists
35 tag cache exists
35 tag cache exists
36
36
37 Try corrupting the cache
37 Try corrupting the cache
38
38
39 $ printf 'a b' > .hg/cache/tags
39 $ printf 'a b' > .hg/cache/tags
40 $ hg identify
40 $ hg identify
41 .hg/cache/tags is corrupt, rebuilding it
42 acb14030fe0a tip
41 acb14030fe0a tip
43 $ cacheexists
42 $ cacheexists
44 tag cache exists
43 tag cache exists
45 $ hg identify
44 $ hg identify
46 acb14030fe0a tip
45 acb14030fe0a tip
47
46
48 Create local tag with long name:
47 Create local tag with long name:
49
48
50 $ T=`hg identify --debug --id`
49 $ T=`hg identify --debug --id`
51 $ hg tag -l "This is a local tag with a really long name!"
50 $ hg tag -l "This is a local tag with a really long name!"
52 $ hg tags
51 $ hg tags
53 tip 0:acb14030fe0a
52 tip 0:acb14030fe0a
54 This is a local tag with a really long name! 0:acb14030fe0a
53 This is a local tag with a really long name! 0:acb14030fe0a
55 $ rm .hg/localtags
54 $ rm .hg/localtags
56
55
57 Create a tag behind hg's back:
56 Create a tag behind hg's back:
58
57
59 $ echo "$T first" > .hgtags
58 $ echo "$T first" > .hgtags
60 $ cat .hgtags
59 $ cat .hgtags
61 acb14030fe0a21b60322c440ad2d20cf7685a376 first
60 acb14030fe0a21b60322c440ad2d20cf7685a376 first
62 $ hg add .hgtags
61 $ hg add .hgtags
63 $ hg commit -m "add tags"
62 $ hg commit -m "add tags"
64 $ hg tags
63 $ hg tags
65 tip 1:b9154636be93
64 tip 1:b9154636be93
66 first 0:acb14030fe0a
65 first 0:acb14030fe0a
67 $ hg identify
66 $ hg identify
68 b9154636be93 tip
67 b9154636be93 tip
69
68
70 Repeat with cold tag cache:
69 Repeat with cold tag cache:
71
70
72 $ rm -f .hg/cache/tags
71 $ rm -f .hg/cache/tags
73 $ hg identify
72 $ hg identify
74 b9154636be93 tip
73 b9154636be93 tip
75
74
76 And again, but now unable to write tag cache:
75 And again, but now unable to write tag cache:
77
76
78 #if unix-permissions
77 #if unix-permissions
79 $ rm -f .hg/cache/tags
78 $ rm -f .hg/cache/tags
80 $ chmod 555 .hg
79 $ chmod 555 .hg
81 $ hg identify
80 $ hg identify
82 b9154636be93 tip
81 b9154636be93 tip
83 $ chmod 755 .hg
82 $ chmod 755 .hg
84 #endif
83 #endif
85
84
86 Create a branch:
85 Create a branch:
87
86
88 $ echo bb > a
87 $ echo bb > a
89 $ hg status
88 $ hg status
90 M a
89 M a
91 $ hg identify
90 $ hg identify
92 b9154636be93+ tip
91 b9154636be93+ tip
93 $ hg co first
92 $ hg co first
94 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
95 $ hg id
94 $ hg id
96 acb14030fe0a+ first
95 acb14030fe0a+ first
97 $ hg -v id
96 $ hg -v id
98 acb14030fe0a+ first
97 acb14030fe0a+ first
99 $ hg status
98 $ hg status
100 M a
99 M a
101 $ echo 1 > b
100 $ echo 1 > b
102 $ hg add b
101 $ hg add b
103 $ hg commit -m "branch"
102 $ hg commit -m "branch"
104 created new head
103 created new head
105 $ hg id
104 $ hg id
106 c8edf04160c7 tip
105 c8edf04160c7 tip
107
106
108 Merge the two heads:
107 Merge the two heads:
109
108
110 $ hg merge 1
109 $ hg merge 1
111 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
112 (branch merge, don't forget to commit)
111 (branch merge, don't forget to commit)
113 $ hg id
112 $ hg id
114 c8edf04160c7+b9154636be93+ tip
113 c8edf04160c7+b9154636be93+ tip
115 $ hg status
114 $ hg status
116 M .hgtags
115 M .hgtags
117 $ hg commit -m "merge"
116 $ hg commit -m "merge"
118
117
119 Create a fake head, make sure tag not visible afterwards:
118 Create a fake head, make sure tag not visible afterwards:
120
119
121 $ cp .hgtags tags
120 $ cp .hgtags tags
122 $ hg tag last
121 $ hg tag last
123 $ hg rm .hgtags
122 $ hg rm .hgtags
124 $ hg commit -m "remove"
123 $ hg commit -m "remove"
125
124
126 $ mv tags .hgtags
125 $ mv tags .hgtags
127 $ hg add .hgtags
126 $ hg add .hgtags
128 $ hg commit -m "readd"
127 $ hg commit -m "readd"
129 $
128 $
130 $ hg tags
129 $ hg tags
131 tip 6:35ff301afafe
130 tip 6:35ff301afafe
132 first 0:acb14030fe0a
131 first 0:acb14030fe0a
133
132
134 Add invalid tags:
133 Add invalid tags:
135
134
136 $ echo "spam" >> .hgtags
135 $ echo "spam" >> .hgtags
137 $ echo >> .hgtags
136 $ echo >> .hgtags
138 $ echo "foo bar" >> .hgtags
137 $ echo "foo bar" >> .hgtags
139 $ echo "a5a5 invalid" >> .hg/localtags
138 $ echo "a5a5 invalid" >> .hg/localtags
140 $ cat .hgtags
139 $ cat .hgtags
141 acb14030fe0a21b60322c440ad2d20cf7685a376 first
140 acb14030fe0a21b60322c440ad2d20cf7685a376 first
142 spam
141 spam
143
142
144 foo bar
143 foo bar
145 $ hg commit -m "tags"
144 $ hg commit -m "tags"
146
145
147 Report tag parse error on other head:
146 Report tag parse error on other head:
148
147
149 $ hg up 3
148 $ hg up 3
150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 $ echo 'x y' >> .hgtags
150 $ echo 'x y' >> .hgtags
152 $ hg commit -m "head"
151 $ hg commit -m "head"
153 created new head
152 created new head
154
153
155 $ hg tags
154 $ hg tags
156 .hgtags@75d9f02dfe28, line 2: cannot parse entry
155 .hgtags@75d9f02dfe28, line 2: cannot parse entry
157 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
156 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
158 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
157 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
159 tip 8:c4be69a18c11
158 tip 8:c4be69a18c11
160 first 0:acb14030fe0a
159 first 0:acb14030fe0a
161 $ hg tip
160 $ hg tip
162 changeset: 8:c4be69a18c11
161 changeset: 8:c4be69a18c11
163 tag: tip
162 tag: tip
164 parent: 3:ac5e980c4dc0
163 parent: 3:ac5e980c4dc0
165 user: test
164 user: test
166 date: Thu Jan 01 00:00:00 1970 +0000
165 date: Thu Jan 01 00:00:00 1970 +0000
167 summary: head
166 summary: head
168
167
169
168
170 Test tag precedence rules:
169 Test tag precedence rules:
171
170
172 $ cd ..
171 $ cd ..
173 $ hg init t2
172 $ hg init t2
174 $ cd t2
173 $ cd t2
175 $ echo foo > foo
174 $ echo foo > foo
176 $ hg add foo
175 $ hg add foo
177 $ hg ci -m 'add foo' # rev 0
176 $ hg ci -m 'add foo' # rev 0
178 $ hg tag bar # rev 1
177 $ hg tag bar # rev 1
179 $ echo >> foo
178 $ echo >> foo
180 $ hg ci -m 'change foo 1' # rev 2
179 $ hg ci -m 'change foo 1' # rev 2
181 $ hg up -C 1
180 $ hg up -C 1
182 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
183 $ hg tag -r 1 -f bar # rev 3
182 $ hg tag -r 1 -f bar # rev 3
184 $ hg up -C 1
183 $ hg up -C 1
185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 $ echo >> foo
185 $ echo >> foo
187 $ hg ci -m 'change foo 2' # rev 4
186 $ hg ci -m 'change foo 2' # rev 4
188 created new head
187 created new head
189 $ hg tags
188 $ hg tags
190 tip 4:0c192d7d5e6b
189 tip 4:0c192d7d5e6b
191 bar 1:78391a272241
190 bar 1:78391a272241
192
191
193 Repeat in case of cache effects:
192 Repeat in case of cache effects:
194
193
195 $ hg tags
194 $ hg tags
196 tip 4:0c192d7d5e6b
195 tip 4:0c192d7d5e6b
197 bar 1:78391a272241
196 bar 1:78391a272241
198
197
199 Detailed dump of tag info:
198 Detailed dump of tag info:
200
199
201 $ hg heads -q # expect 4, 3, 2
200 $ hg heads -q # expect 4, 3, 2
202 4:0c192d7d5e6b
201 4:0c192d7d5e6b
203 3:6fa450212aeb
202 3:6fa450212aeb
204 2:7a94127795a3
203 2:7a94127795a3
205 $ dumptags 2
204 $ dumptags 2
206 rev 2: .hgtags:
205 rev 2: .hgtags:
207 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
206 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
208 $ dumptags 3
207 $ dumptags 3
209 rev 3: .hgtags:
208 rev 3: .hgtags:
210 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
209 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
211 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
210 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
212 78391a272241d70354aa14c874552cad6b51bb42 bar
211 78391a272241d70354aa14c874552cad6b51bb42 bar
213 $ dumptags 4
212 $ dumptags 4
214 rev 4: .hgtags:
213 rev 4: .hgtags:
215 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
214 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
216
215
217 Dump cache:
216 Dump cache:
218
217
219 $ cat .hg/cache/tags
218 $ cat .hg/cache/tags
220 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
219 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
221 3 6fa450212aeb2a21ed616a54aea39a4a27894cd7 7d3b718c964ef37b89e550ebdafd5789e76ce1b0
220 3 6fa450212aeb2a21ed616a54aea39a4a27894cd7 7d3b718c964ef37b89e550ebdafd5789e76ce1b0
222 2 7a94127795a33c10a370c93f731fd9fea0b79af6 0c04f2a8af31de17fab7422878ee5a2dadbc943d
221 2 7a94127795a33c10a370c93f731fd9fea0b79af6 0c04f2a8af31de17fab7422878ee5a2dadbc943d
223
222
224 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
223 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
225 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
224 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
226 78391a272241d70354aa14c874552cad6b51bb42 bar
225 78391a272241d70354aa14c874552cad6b51bb42 bar
227
226
228 Test tag removal:
227 Test tag removal:
229
228
230 $ hg tag --remove bar # rev 5
229 $ hg tag --remove bar # rev 5
231 $ hg tip -vp
230 $ hg tip -vp
232 changeset: 5:5f6e8655b1c7
231 changeset: 5:5f6e8655b1c7
233 tag: tip
232 tag: tip
234 user: test
233 user: test
235 date: Thu Jan 01 00:00:00 1970 +0000
234 date: Thu Jan 01 00:00:00 1970 +0000
236 files: .hgtags
235 files: .hgtags
237 description:
236 description:
238 Removed tag bar
237 Removed tag bar
239
238
240
239
241 diff -r 0c192d7d5e6b -r 5f6e8655b1c7 .hgtags
240 diff -r 0c192d7d5e6b -r 5f6e8655b1c7 .hgtags
242 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
241 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
243 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
242 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
244 @@ -1,1 +1,3 @@
243 @@ -1,1 +1,3 @@
245 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
244 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
246 +78391a272241d70354aa14c874552cad6b51bb42 bar
245 +78391a272241d70354aa14c874552cad6b51bb42 bar
247 +0000000000000000000000000000000000000000 bar
246 +0000000000000000000000000000000000000000 bar
248
247
249 $ hg tags
248 $ hg tags
250 tip 5:5f6e8655b1c7
249 tip 5:5f6e8655b1c7
251 $ hg tags # again, try to expose cache bugs
250 $ hg tags # again, try to expose cache bugs
252 tip 5:5f6e8655b1c7
251 tip 5:5f6e8655b1c7
253
252
254 Remove nonexistent tag:
253 Remove nonexistent tag:
255
254
256 $ hg tag --remove foobar
255 $ hg tag --remove foobar
257 abort: tag 'foobar' does not exist
256 abort: tag 'foobar' does not exist
258 [255]
257 [255]
259 $ hg tip
258 $ hg tip
260 changeset: 5:5f6e8655b1c7
259 changeset: 5:5f6e8655b1c7
261 tag: tip
260 tag: tip
262 user: test
261 user: test
263 date: Thu Jan 01 00:00:00 1970 +0000
262 date: Thu Jan 01 00:00:00 1970 +0000
264 summary: Removed tag bar
263 summary: Removed tag bar
265
264
266
265
267 Undo a tag with rollback:
266 Undo a tag with rollback:
268
267
269 $ hg rollback # destroy rev 5 (restore bar)
268 $ hg rollback # destroy rev 5 (restore bar)
270 repository tip rolled back to revision 4 (undo commit)
269 repository tip rolled back to revision 4 (undo commit)
271 working directory now based on revision 4
270 working directory now based on revision 4
272 $ hg tags
271 $ hg tags
273 tip 4:0c192d7d5e6b
272 tip 4:0c192d7d5e6b
274 bar 1:78391a272241
273 bar 1:78391a272241
275 $ hg tags
274 $ hg tags
276 tip 4:0c192d7d5e6b
275 tip 4:0c192d7d5e6b
277 bar 1:78391a272241
276 bar 1:78391a272241
278
277
279 Test tag rank:
278 Test tag rank:
280
279
281 $ cd ..
280 $ cd ..
282 $ hg init t3
281 $ hg init t3
283 $ cd t3
282 $ cd t3
284 $ echo foo > foo
283 $ echo foo > foo
285 $ hg add foo
284 $ hg add foo
286 $ hg ci -m 'add foo' # rev 0
285 $ hg ci -m 'add foo' # rev 0
287 $ hg tag -f bar # rev 1 bar -> 0
286 $ hg tag -f bar # rev 1 bar -> 0
288 $ hg tag -f bar # rev 2 bar -> 1
287 $ hg tag -f bar # rev 2 bar -> 1
289 $ hg tag -fr 0 bar # rev 3 bar -> 0
288 $ hg tag -fr 0 bar # rev 3 bar -> 0
290 $ hg tag -fr 1 bar # rev 4 bar -> 1
289 $ hg tag -fr 1 bar # rev 4 bar -> 1
291 $ hg tag -fr 0 bar # rev 5 bar -> 0
290 $ hg tag -fr 0 bar # rev 5 bar -> 0
292 $ hg tags
291 $ hg tags
293 tip 5:85f05169d91d
292 tip 5:85f05169d91d
294 bar 0:bbd179dfa0a7
293 bar 0:bbd179dfa0a7
295 $ hg co 3
294 $ hg co 3
296 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 $ echo barbar > foo
296 $ echo barbar > foo
298 $ hg ci -m 'change foo' # rev 6
297 $ hg ci -m 'change foo' # rev 6
299 created new head
298 created new head
300 $ hg tags
299 $ hg tags
301 tip 6:735c3ca72986
300 tip 6:735c3ca72986
302 bar 0:bbd179dfa0a7
301 bar 0:bbd179dfa0a7
303
302
304 Don't allow moving tag without -f:
303 Don't allow moving tag without -f:
305
304
306 $ hg tag -r 3 bar
305 $ hg tag -r 3 bar
307 abort: tag 'bar' already exists (use -f to force)
306 abort: tag 'bar' already exists (use -f to force)
308 [255]
307 [255]
309 $ hg tags
308 $ hg tags
310 tip 6:735c3ca72986
309 tip 6:735c3ca72986
311 bar 0:bbd179dfa0a7
310 bar 0:bbd179dfa0a7
312
311
313 Strip 1: expose an old head:
312 Strip 1: expose an old head:
314
313
315 $ hg --config extensions.mq= strip 5
314 $ hg --config extensions.mq= strip 5
316 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
315 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
317 $ hg tags # partly stale cache
316 $ hg tags # partly stale cache
318 tip 5:735c3ca72986
317 tip 5:735c3ca72986
319 bar 1:78391a272241
318 bar 1:78391a272241
320 $ hg tags # up-to-date cache
319 $ hg tags # up-to-date cache
321 tip 5:735c3ca72986
320 tip 5:735c3ca72986
322 bar 1:78391a272241
321 bar 1:78391a272241
323
322
324 Strip 2: destroy whole branch, no old head exposed
323 Strip 2: destroy whole branch, no old head exposed
325
324
326 $ hg --config extensions.mq= strip 4
325 $ hg --config extensions.mq= strip 4
327 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
326 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
328 $ hg tags # partly stale
327 $ hg tags # partly stale
329 tip 4:735c3ca72986
328 tip 4:735c3ca72986
330 bar 0:bbd179dfa0a7
329 bar 0:bbd179dfa0a7
331 $ rm -f .hg/cache/tags
330 $ rm -f .hg/cache/tags
332 $ hg tags # cold cache
331 $ hg tags # cold cache
333 tip 4:735c3ca72986
332 tip 4:735c3ca72986
334 bar 0:bbd179dfa0a7
333 bar 0:bbd179dfa0a7
335
334
336 Test tag rank with 3 heads:
335 Test tag rank with 3 heads:
337
336
338 $ cd ..
337 $ cd ..
339 $ hg init t4
338 $ hg init t4
340 $ cd t4
339 $ cd t4
341 $ echo foo > foo
340 $ echo foo > foo
342 $ hg add
341 $ hg add
343 adding foo
342 adding foo
344 $ hg ci -m 'add foo' # rev 0
343 $ hg ci -m 'add foo' # rev 0
345 $ hg tag bar # rev 1 bar -> 0
344 $ hg tag bar # rev 1 bar -> 0
346 $ hg tag -f bar # rev 2 bar -> 1
345 $ hg tag -f bar # rev 2 bar -> 1
347 $ hg up -qC 0
346 $ hg up -qC 0
348 $ hg tag -fr 2 bar # rev 3 bar -> 2
347 $ hg tag -fr 2 bar # rev 3 bar -> 2
349 $ hg tags
348 $ hg tags
350 tip 3:197c21bbbf2c
349 tip 3:197c21bbbf2c
351 bar 2:6fa450212aeb
350 bar 2:6fa450212aeb
352 $ hg up -qC 0
351 $ hg up -qC 0
353 $ hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
352 $ hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
354
353
355 Bar should still point to rev 2:
354 Bar should still point to rev 2:
356
355
357 $ hg tags
356 $ hg tags
358 tip 4:3b4b14ed0202
357 tip 4:3b4b14ed0202
359 bar 2:6fa450212aeb
358 bar 2:6fa450212aeb
360
359
361 Test that removing global/local tags does not get confused when trying
360 Test that removing global/local tags does not get confused when trying
362 to remove a tag of type X which actually only exists as a type Y:
361 to remove a tag of type X which actually only exists as a type Y:
363
362
364 $ cd ..
363 $ cd ..
365 $ hg init t5
364 $ hg init t5
366 $ cd t5
365 $ cd t5
367 $ echo foo > foo
366 $ echo foo > foo
368 $ hg add
367 $ hg add
369 adding foo
368 adding foo
370 $ hg ci -m 'add foo' # rev 0
369 $ hg ci -m 'add foo' # rev 0
371
370
372 $ hg tag -r 0 -l localtag
371 $ hg tag -r 0 -l localtag
373 $ hg tag --remove localtag
372 $ hg tag --remove localtag
374 abort: tag 'localtag' is not a global tag
373 abort: tag 'localtag' is not a global tag
375 [255]
374 [255]
376 $
375 $
377 $ hg tag -r 0 globaltag
376 $ hg tag -r 0 globaltag
378 $ hg tag --remove -l globaltag
377 $ hg tag --remove -l globaltag
379 abort: tag 'globaltag' is not a local tag
378 abort: tag 'globaltag' is not a local tag
380 [255]
379 [255]
381 $ hg tags -v
380 $ hg tags -v
382 tip 1:a0b6fe111088
381 tip 1:a0b6fe111088
383 localtag 0:bbd179dfa0a7 local
382 localtag 0:bbd179dfa0a7 local
384 globaltag 0:bbd179dfa0a7
383 globaltag 0:bbd179dfa0a7
385
384
386 Test for issue3911
385 Test for issue3911
387
386
388 $ hg tag -r 0 -l localtag2
387 $ hg tag -r 0 -l localtag2
389 $ hg tag -l --remove localtag2
388 $ hg tag -l --remove localtag2
390 $ hg tags -v
389 $ hg tags -v
391 tip 1:a0b6fe111088
390 tip 1:a0b6fe111088
392 localtag 0:bbd179dfa0a7 local
391 localtag 0:bbd179dfa0a7 local
393 globaltag 0:bbd179dfa0a7
392 globaltag 0:bbd179dfa0a7
394
393
395 $ hg tag -r 1 -f localtag
394 $ hg tag -r 1 -f localtag
396 $ hg tags -v
395 $ hg tags -v
397 tip 2:5c70a037bb37
396 tip 2:5c70a037bb37
398 localtag 1:a0b6fe111088
397 localtag 1:a0b6fe111088
399 globaltag 0:bbd179dfa0a7
398 globaltag 0:bbd179dfa0a7
400
399
401 $ hg tags -v
400 $ hg tags -v
402 tip 2:5c70a037bb37
401 tip 2:5c70a037bb37
403 localtag 1:a0b6fe111088
402 localtag 1:a0b6fe111088
404 globaltag 0:bbd179dfa0a7
403 globaltag 0:bbd179dfa0a7
405
404
406 $ hg tag -r 1 localtag2
405 $ hg tag -r 1 localtag2
407 $ hg tags -v
406 $ hg tags -v
408 tip 3:bbfb8cd42be2
407 tip 3:bbfb8cd42be2
409 localtag2 1:a0b6fe111088
408 localtag2 1:a0b6fe111088
410 localtag 1:a0b6fe111088
409 localtag 1:a0b6fe111088
411 globaltag 0:bbd179dfa0a7
410 globaltag 0:bbd179dfa0a7
412
411
413 $ hg tags -v
412 $ hg tags -v
414 tip 3:bbfb8cd42be2
413 tip 3:bbfb8cd42be2
415 localtag2 1:a0b6fe111088
414 localtag2 1:a0b6fe111088
416 localtag 1:a0b6fe111088
415 localtag 1:a0b6fe111088
417 globaltag 0:bbd179dfa0a7
416 globaltag 0:bbd179dfa0a7
418
417
419 $ cd ..
418 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now