##// END OF EJS Templates
tags: take lock instead of wlock before writing hgtagsfnodes1 cache...
Pulkit Goyal -
r46005:64de86fd default
parent child Browse files
Show More
@@ -1,876 +1,876 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 __future__ import absolute_import
13 from __future__ import absolute_import
14
14
15 import errno
15 import errno
16 import io
16 import io
17
17
18 from .node import (
18 from .node import (
19 bin,
19 bin,
20 hex,
20 hex,
21 nullid,
21 nullid,
22 nullrev,
22 nullrev,
23 short,
23 short,
24 )
24 )
25 from .i18n import _
25 from .i18n import _
26 from . import (
26 from . import (
27 encoding,
27 encoding,
28 error,
28 error,
29 match as matchmod,
29 match as matchmod,
30 pycompat,
30 pycompat,
31 scmutil,
31 scmutil,
32 util,
32 util,
33 )
33 )
34 from .utils import stringutil
34 from .utils import stringutil
35
35
36 # Tags computation can be expensive and caches exist to make it fast in
36 # Tags computation can be expensive and caches exist to make it fast in
37 # the common case.
37 # the common case.
38 #
38 #
39 # The "hgtagsfnodes1" cache file caches the .hgtags filenode values for
39 # The "hgtagsfnodes1" cache file caches the .hgtags filenode values for
40 # each revision in the repository. The file is effectively an array of
40 # each revision in the repository. The file is effectively an array of
41 # fixed length records. Read the docs for "hgtagsfnodescache" for technical
41 # fixed length records. Read the docs for "hgtagsfnodescache" for technical
42 # details.
42 # details.
43 #
43 #
44 # The .hgtags filenode cache grows in proportion to the length of the
44 # The .hgtags filenode cache grows in proportion to the length of the
45 # changelog. The file is truncated when the # changelog is stripped.
45 # changelog. The file is truncated when the # changelog is stripped.
46 #
46 #
47 # The purpose of the filenode cache is to avoid the most expensive part
47 # The purpose of the filenode cache is to avoid the most expensive part
48 # of finding global tags, which is looking up the .hgtags filenode in the
48 # of finding global tags, which is looking up the .hgtags filenode in the
49 # manifest for each head. This can take dozens or over 100ms for
49 # manifest for each head. This can take dozens or over 100ms for
50 # repositories with very large manifests. Multiplied by dozens or even
50 # repositories with very large manifests. Multiplied by dozens or even
51 # hundreds of heads and there is a significant performance concern.
51 # hundreds of heads and there is a significant performance concern.
52 #
52 #
53 # There also exist a separate cache file for each repository filter.
53 # There also exist a separate cache file for each repository filter.
54 # These "tags-*" files store information about the history of tags.
54 # These "tags-*" files store information about the history of tags.
55 #
55 #
56 # The tags cache files consists of a cache validation line followed by
56 # The tags cache files consists of a cache validation line followed by
57 # a history of tags.
57 # a history of tags.
58 #
58 #
59 # The cache validation line has the format:
59 # The cache validation line has the format:
60 #
60 #
61 # <tiprev> <tipnode> [<filteredhash>]
61 # <tiprev> <tipnode> [<filteredhash>]
62 #
62 #
63 # <tiprev> is an integer revision and <tipnode> is a 40 character hex
63 # <tiprev> is an integer revision and <tipnode> is a 40 character hex
64 # node for that changeset. These redundantly identify the repository
64 # node for that changeset. These redundantly identify the repository
65 # tip from the time the cache was written. In addition, <filteredhash>,
65 # tip from the time the cache was written. In addition, <filteredhash>,
66 # if present, is a 40 character hex hash of the contents of the filtered
66 # if present, is a 40 character hex hash of the contents of the filtered
67 # revisions for this filter. If the set of filtered revs changes, the
67 # revisions for this filter. If the set of filtered revs changes, the
68 # hash will change and invalidate the cache.
68 # hash will change and invalidate the cache.
69 #
69 #
70 # The history part of the tags cache consists of lines of the form:
70 # The history part of the tags cache consists of lines of the form:
71 #
71 #
72 # <node> <tag>
72 # <node> <tag>
73 #
73 #
74 # (This format is identical to that of .hgtags files.)
74 # (This format is identical to that of .hgtags files.)
75 #
75 #
76 # <tag> is the tag name and <node> is the 40 character hex changeset
76 # <tag> is the tag name and <node> is the 40 character hex changeset
77 # the tag is associated with.
77 # the tag is associated with.
78 #
78 #
79 # Tags are written sorted by tag name.
79 # Tags are written sorted by tag name.
80 #
80 #
81 # Tags associated with multiple changesets have an entry for each changeset.
81 # Tags associated with multiple changesets have an entry for each changeset.
82 # The most recent changeset (in terms of revlog ordering for the head
82 # The most recent changeset (in terms of revlog ordering for the head
83 # setting it) for each tag is last.
83 # setting it) for each tag is last.
84
84
85
85
86 def fnoderevs(ui, repo, revs):
86 def fnoderevs(ui, repo, revs):
87 """return the list of '.hgtags' fnodes used in a set revisions
87 """return the list of '.hgtags' fnodes used in a set revisions
88
88
89 This is returned as list of unique fnodes. We use a list instead of a set
89 This is returned as list of unique fnodes. We use a list instead of a set
90 because order matters when it comes to tags."""
90 because order matters when it comes to tags."""
91 unfi = repo.unfiltered()
91 unfi = repo.unfiltered()
92 tonode = unfi.changelog.node
92 tonode = unfi.changelog.node
93 nodes = [tonode(r) for r in revs]
93 nodes = [tonode(r) for r in revs]
94 fnodes = _getfnodes(ui, repo, nodes)
94 fnodes = _getfnodes(ui, repo, nodes)
95 fnodes = _filterfnodes(fnodes, nodes)
95 fnodes = _filterfnodes(fnodes, nodes)
96 return fnodes
96 return fnodes
97
97
98
98
99 def _nulltonone(value):
99 def _nulltonone(value):
100 """convert nullid to None
100 """convert nullid to None
101
101
102 For tag value, nullid means "deleted". This small utility function helps
102 For tag value, nullid means "deleted". This small utility function helps
103 translating that to None."""
103 translating that to None."""
104 if value == nullid:
104 if value == nullid:
105 return None
105 return None
106 return value
106 return value
107
107
108
108
109 def difftags(ui, repo, oldfnodes, newfnodes):
109 def difftags(ui, repo, oldfnodes, newfnodes):
110 """list differences between tags expressed in two set of file-nodes
110 """list differences between tags expressed in two set of file-nodes
111
111
112 The list contains entries in the form: (tagname, oldvalue, new value).
112 The list contains entries in the form: (tagname, oldvalue, new value).
113 None is used to expressed missing value:
113 None is used to expressed missing value:
114 ('foo', None, 'abcd') is a new tag,
114 ('foo', None, 'abcd') is a new tag,
115 ('bar', 'ef01', None) is a deletion,
115 ('bar', 'ef01', None) is a deletion,
116 ('baz', 'abcd', 'ef01') is a tag movement.
116 ('baz', 'abcd', 'ef01') is a tag movement.
117 """
117 """
118 if oldfnodes == newfnodes:
118 if oldfnodes == newfnodes:
119 return []
119 return []
120 oldtags = _tagsfromfnodes(ui, repo, oldfnodes)
120 oldtags = _tagsfromfnodes(ui, repo, oldfnodes)
121 newtags = _tagsfromfnodes(ui, repo, newfnodes)
121 newtags = _tagsfromfnodes(ui, repo, newfnodes)
122
122
123 # list of (tag, old, new): None means missing
123 # list of (tag, old, new): None means missing
124 entries = []
124 entries = []
125 for tag, (new, __) in newtags.items():
125 for tag, (new, __) in newtags.items():
126 new = _nulltonone(new)
126 new = _nulltonone(new)
127 old, __ = oldtags.pop(tag, (None, None))
127 old, __ = oldtags.pop(tag, (None, None))
128 old = _nulltonone(old)
128 old = _nulltonone(old)
129 if old != new:
129 if old != new:
130 entries.append((tag, old, new))
130 entries.append((tag, old, new))
131 # handle deleted tags
131 # handle deleted tags
132 for tag, (old, __) in oldtags.items():
132 for tag, (old, __) in oldtags.items():
133 old = _nulltonone(old)
133 old = _nulltonone(old)
134 if old is not None:
134 if old is not None:
135 entries.append((tag, old, None))
135 entries.append((tag, old, None))
136 entries.sort()
136 entries.sort()
137 return entries
137 return entries
138
138
139
139
140 def writediff(fp, difflist):
140 def writediff(fp, difflist):
141 """write tags diff information to a file.
141 """write tags diff information to a file.
142
142
143 Data are stored with a line based format:
143 Data are stored with a line based format:
144
144
145 <action> <hex-node> <tag-name>\n
145 <action> <hex-node> <tag-name>\n
146
146
147 Action are defined as follow:
147 Action are defined as follow:
148 -R tag is removed,
148 -R tag is removed,
149 +A tag is added,
149 +A tag is added,
150 -M tag is moved (old value),
150 -M tag is moved (old value),
151 +M tag is moved (new value),
151 +M tag is moved (new value),
152
152
153 Example:
153 Example:
154
154
155 +A 875517b4806a848f942811a315a5bce30804ae85 t5
155 +A 875517b4806a848f942811a315a5bce30804ae85 t5
156
156
157 See documentation of difftags output for details about the input.
157 See documentation of difftags output for details about the input.
158 """
158 """
159 add = b'+A %s %s\n'
159 add = b'+A %s %s\n'
160 remove = b'-R %s %s\n'
160 remove = b'-R %s %s\n'
161 updateold = b'-M %s %s\n'
161 updateold = b'-M %s %s\n'
162 updatenew = b'+M %s %s\n'
162 updatenew = b'+M %s %s\n'
163 for tag, old, new in difflist:
163 for tag, old, new in difflist:
164 # translate to hex
164 # translate to hex
165 if old is not None:
165 if old is not None:
166 old = hex(old)
166 old = hex(old)
167 if new is not None:
167 if new is not None:
168 new = hex(new)
168 new = hex(new)
169 # write to file
169 # write to file
170 if old is None:
170 if old is None:
171 fp.write(add % (new, tag))
171 fp.write(add % (new, tag))
172 elif new is None:
172 elif new is None:
173 fp.write(remove % (old, tag))
173 fp.write(remove % (old, tag))
174 else:
174 else:
175 fp.write(updateold % (old, tag))
175 fp.write(updateold % (old, tag))
176 fp.write(updatenew % (new, tag))
176 fp.write(updatenew % (new, tag))
177
177
178
178
179 def findglobaltags(ui, repo):
179 def findglobaltags(ui, repo):
180 '''Find global tags in a repo: return a tagsmap
180 '''Find global tags in a repo: return a tagsmap
181
181
182 tagsmap: tag name to (node, hist) 2-tuples.
182 tagsmap: tag name to (node, hist) 2-tuples.
183
183
184 The tags cache is read and updated as a side-effect of calling.
184 The tags cache is read and updated as a side-effect of calling.
185 '''
185 '''
186 (heads, tagfnode, valid, cachetags, shouldwrite) = _readtagcache(ui, repo)
186 (heads, tagfnode, valid, cachetags, shouldwrite) = _readtagcache(ui, repo)
187 if cachetags is not None:
187 if cachetags is not None:
188 assert not shouldwrite
188 assert not shouldwrite
189 # XXX is this really 100% correct? are there oddball special
189 # XXX is this really 100% correct? are there oddball special
190 # cases where a global tag should outrank a local tag but won't,
190 # cases where a global tag should outrank a local tag but won't,
191 # because cachetags does not contain rank info?
191 # because cachetags does not contain rank info?
192 alltags = {}
192 alltags = {}
193 _updatetags(cachetags, alltags)
193 _updatetags(cachetags, alltags)
194 return alltags
194 return alltags
195
195
196 for head in reversed(heads): # oldest to newest
196 for head in reversed(heads): # oldest to newest
197 assert repo.changelog.index.has_node(
197 assert repo.changelog.index.has_node(
198 head
198 head
199 ), b"tag cache returned bogus head %s" % short(head)
199 ), b"tag cache returned bogus head %s" % short(head)
200 fnodes = _filterfnodes(tagfnode, reversed(heads))
200 fnodes = _filterfnodes(tagfnode, reversed(heads))
201 alltags = _tagsfromfnodes(ui, repo, fnodes)
201 alltags = _tagsfromfnodes(ui, repo, fnodes)
202
202
203 # and update the cache (if necessary)
203 # and update the cache (if necessary)
204 if shouldwrite:
204 if shouldwrite:
205 _writetagcache(ui, repo, valid, alltags)
205 _writetagcache(ui, repo, valid, alltags)
206 return alltags
206 return alltags
207
207
208
208
209 def _filterfnodes(tagfnode, nodes):
209 def _filterfnodes(tagfnode, nodes):
210 """return a list of unique fnodes
210 """return a list of unique fnodes
211
211
212 The order of this list matches the order of "nodes". Preserving this order
212 The order of this list matches the order of "nodes". Preserving this order
213 is important as reading tags in different order provides different
213 is important as reading tags in different order provides different
214 results."""
214 results."""
215 seen = set() # set of fnode
215 seen = set() # set of fnode
216 fnodes = []
216 fnodes = []
217 for no in nodes: # oldest to newest
217 for no in nodes: # oldest to newest
218 fnode = tagfnode.get(no)
218 fnode = tagfnode.get(no)
219 if fnode and fnode not in seen:
219 if fnode and fnode not in seen:
220 seen.add(fnode)
220 seen.add(fnode)
221 fnodes.append(fnode)
221 fnodes.append(fnode)
222 return fnodes
222 return fnodes
223
223
224
224
225 def _tagsfromfnodes(ui, repo, fnodes):
225 def _tagsfromfnodes(ui, repo, fnodes):
226 """return a tagsmap from a list of file-node
226 """return a tagsmap from a list of file-node
227
227
228 tagsmap: tag name to (node, hist) 2-tuples.
228 tagsmap: tag name to (node, hist) 2-tuples.
229
229
230 The order of the list matters."""
230 The order of the list matters."""
231 alltags = {}
231 alltags = {}
232 fctx = None
232 fctx = None
233 for fnode in fnodes:
233 for fnode in fnodes:
234 if fctx is None:
234 if fctx is None:
235 fctx = repo.filectx(b'.hgtags', fileid=fnode)
235 fctx = repo.filectx(b'.hgtags', fileid=fnode)
236 else:
236 else:
237 fctx = fctx.filectx(fnode)
237 fctx = fctx.filectx(fnode)
238 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
238 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
239 _updatetags(filetags, alltags)
239 _updatetags(filetags, alltags)
240 return alltags
240 return alltags
241
241
242
242
243 def readlocaltags(ui, repo, alltags, tagtypes):
243 def readlocaltags(ui, repo, alltags, tagtypes):
244 '''Read local tags in repo. Update alltags and tagtypes.'''
244 '''Read local tags in repo. Update alltags and tagtypes.'''
245 try:
245 try:
246 data = repo.vfs.read(b"localtags")
246 data = repo.vfs.read(b"localtags")
247 except IOError as inst:
247 except IOError as inst:
248 if inst.errno != errno.ENOENT:
248 if inst.errno != errno.ENOENT:
249 raise
249 raise
250 return
250 return
251
251
252 # localtags is in the local encoding; re-encode to UTF-8 on
252 # localtags is in the local encoding; re-encode to UTF-8 on
253 # input for consistency with the rest of this module.
253 # input for consistency with the rest of this module.
254 filetags = _readtags(
254 filetags = _readtags(
255 ui, repo, data.splitlines(), b"localtags", recode=encoding.fromlocal
255 ui, repo, data.splitlines(), b"localtags", recode=encoding.fromlocal
256 )
256 )
257
257
258 # remove tags pointing to invalid nodes
258 # remove tags pointing to invalid nodes
259 cl = repo.changelog
259 cl = repo.changelog
260 for t in list(filetags):
260 for t in list(filetags):
261 try:
261 try:
262 cl.rev(filetags[t][0])
262 cl.rev(filetags[t][0])
263 except (LookupError, ValueError):
263 except (LookupError, ValueError):
264 del filetags[t]
264 del filetags[t]
265
265
266 _updatetags(filetags, alltags, b'local', tagtypes)
266 _updatetags(filetags, alltags, b'local', tagtypes)
267
267
268
268
269 def _readtaghist(ui, repo, lines, fn, recode=None, calcnodelines=False):
269 def _readtaghist(ui, repo, lines, fn, recode=None, calcnodelines=False):
270 '''Read tag definitions from a file (or any source of lines).
270 '''Read tag definitions from a file (or any source of lines).
271
271
272 This function returns two sortdicts with similar information:
272 This function returns two sortdicts with similar information:
273
273
274 - the first dict, bintaghist, contains the tag information as expected by
274 - the first dict, bintaghist, contains the tag information as expected by
275 the _readtags function, i.e. a mapping from tag name to (node, hist):
275 the _readtags function, i.e. a mapping from tag name to (node, hist):
276 - node is the node id from the last line read for that name,
276 - node is the node id from the last line read for that name,
277 - hist is the list of node ids previously associated with it (in file
277 - hist is the list of node ids previously associated with it (in file
278 order). All node ids are binary, not hex.
278 order). All node ids are binary, not hex.
279
279
280 - the second dict, hextaglines, is a mapping from tag name to a list of
280 - the second dict, hextaglines, is a mapping from tag name to a list of
281 [hexnode, line number] pairs, ordered from the oldest to the newest node.
281 [hexnode, line number] pairs, ordered from the oldest to the newest node.
282
282
283 When calcnodelines is False the hextaglines dict is not calculated (an
283 When calcnodelines is False the hextaglines dict is not calculated (an
284 empty dict is returned). This is done to improve this function's
284 empty dict is returned). This is done to improve this function's
285 performance in cases where the line numbers are not needed.
285 performance in cases where the line numbers are not needed.
286 '''
286 '''
287
287
288 bintaghist = util.sortdict()
288 bintaghist = util.sortdict()
289 hextaglines = util.sortdict()
289 hextaglines = util.sortdict()
290 count = 0
290 count = 0
291
291
292 def dbg(msg):
292 def dbg(msg):
293 ui.debug(b"%s, line %d: %s\n" % (fn, count, msg))
293 ui.debug(b"%s, line %d: %s\n" % (fn, count, msg))
294
294
295 for nline, line in enumerate(lines):
295 for nline, line in enumerate(lines):
296 count += 1
296 count += 1
297 if not line:
297 if not line:
298 continue
298 continue
299 try:
299 try:
300 (nodehex, name) = line.split(b" ", 1)
300 (nodehex, name) = line.split(b" ", 1)
301 except ValueError:
301 except ValueError:
302 dbg(b"cannot parse entry")
302 dbg(b"cannot parse entry")
303 continue
303 continue
304 name = name.strip()
304 name = name.strip()
305 if recode:
305 if recode:
306 name = recode(name)
306 name = recode(name)
307 try:
307 try:
308 nodebin = bin(nodehex)
308 nodebin = bin(nodehex)
309 except TypeError:
309 except TypeError:
310 dbg(b"node '%s' is not well formed" % nodehex)
310 dbg(b"node '%s' is not well formed" % nodehex)
311 continue
311 continue
312
312
313 # update filetags
313 # update filetags
314 if calcnodelines:
314 if calcnodelines:
315 # map tag name to a list of line numbers
315 # map tag name to a list of line numbers
316 if name not in hextaglines:
316 if name not in hextaglines:
317 hextaglines[name] = []
317 hextaglines[name] = []
318 hextaglines[name].append([nodehex, nline])
318 hextaglines[name].append([nodehex, nline])
319 continue
319 continue
320 # map tag name to (node, hist)
320 # map tag name to (node, hist)
321 if name not in bintaghist:
321 if name not in bintaghist:
322 bintaghist[name] = []
322 bintaghist[name] = []
323 bintaghist[name].append(nodebin)
323 bintaghist[name].append(nodebin)
324 return bintaghist, hextaglines
324 return bintaghist, hextaglines
325
325
326
326
327 def _readtags(ui, repo, lines, fn, recode=None, calcnodelines=False):
327 def _readtags(ui, repo, lines, fn, recode=None, calcnodelines=False):
328 '''Read tag definitions from a file (or any source of lines).
328 '''Read tag definitions from a file (or any source of lines).
329
329
330 Returns a mapping from tag name to (node, hist).
330 Returns a mapping from tag name to (node, hist).
331
331
332 "node" is the node id from the last line read for that name. "hist"
332 "node" is the node id from the last line read for that name. "hist"
333 is the list of node ids previously associated with it (in file order).
333 is the list of node ids previously associated with it (in file order).
334 All node ids are binary, not hex.
334 All node ids are binary, not hex.
335 '''
335 '''
336 filetags, nodelines = _readtaghist(
336 filetags, nodelines = _readtaghist(
337 ui, repo, lines, fn, recode=recode, calcnodelines=calcnodelines
337 ui, repo, lines, fn, recode=recode, calcnodelines=calcnodelines
338 )
338 )
339 # util.sortdict().__setitem__ is much slower at replacing then inserting
339 # util.sortdict().__setitem__ is much slower at replacing then inserting
340 # new entries. The difference can matter if there are thousands of tags.
340 # new entries. The difference can matter if there are thousands of tags.
341 # Create a new sortdict to avoid the performance penalty.
341 # Create a new sortdict to avoid the performance penalty.
342 newtags = util.sortdict()
342 newtags = util.sortdict()
343 for tag, taghist in filetags.items():
343 for tag, taghist in filetags.items():
344 newtags[tag] = (taghist[-1], taghist[:-1])
344 newtags[tag] = (taghist[-1], taghist[:-1])
345 return newtags
345 return newtags
346
346
347
347
348 def _updatetags(filetags, alltags, tagtype=None, tagtypes=None):
348 def _updatetags(filetags, alltags, tagtype=None, tagtypes=None):
349 """Incorporate the tag info read from one file into dictionnaries
349 """Incorporate the tag info read from one file into dictionnaries
350
350
351 The first one, 'alltags', is a "tagmaps" (see 'findglobaltags' for details).
351 The first one, 'alltags', is a "tagmaps" (see 'findglobaltags' for details).
352
352
353 The second one, 'tagtypes', is optional and will be updated to track the
353 The second one, 'tagtypes', is optional and will be updated to track the
354 "tagtype" of entries in the tagmaps. When set, the 'tagtype' argument also
354 "tagtype" of entries in the tagmaps. When set, the 'tagtype' argument also
355 needs to be set."""
355 needs to be set."""
356 if tagtype is None:
356 if tagtype is None:
357 assert tagtypes is None
357 assert tagtypes is None
358
358
359 for name, nodehist in pycompat.iteritems(filetags):
359 for name, nodehist in pycompat.iteritems(filetags):
360 if name not in alltags:
360 if name not in alltags:
361 alltags[name] = nodehist
361 alltags[name] = nodehist
362 if tagtype is not None:
362 if tagtype is not None:
363 tagtypes[name] = tagtype
363 tagtypes[name] = tagtype
364 continue
364 continue
365
365
366 # we prefer alltags[name] if:
366 # we prefer alltags[name] if:
367 # it supersedes us OR
367 # it supersedes us OR
368 # mutual supersedes and it has a higher rank
368 # mutual supersedes and it has a higher rank
369 # otherwise we win because we're tip-most
369 # otherwise we win because we're tip-most
370 anode, ahist = nodehist
370 anode, ahist = nodehist
371 bnode, bhist = alltags[name]
371 bnode, bhist = alltags[name]
372 if (
372 if (
373 bnode != anode
373 bnode != anode
374 and anode in bhist
374 and anode in bhist
375 and (bnode not in ahist or len(bhist) > len(ahist))
375 and (bnode not in ahist or len(bhist) > len(ahist))
376 ):
376 ):
377 anode = bnode
377 anode = bnode
378 elif tagtype is not None:
378 elif tagtype is not None:
379 tagtypes[name] = tagtype
379 tagtypes[name] = tagtype
380 ahist.extend([n for n in bhist if n not in ahist])
380 ahist.extend([n for n in bhist if n not in ahist])
381 alltags[name] = anode, ahist
381 alltags[name] = anode, ahist
382
382
383
383
384 def _filename(repo):
384 def _filename(repo):
385 """name of a tagcache file for a given repo or repoview"""
385 """name of a tagcache file for a given repo or repoview"""
386 filename = b'tags2'
386 filename = b'tags2'
387 if repo.filtername:
387 if repo.filtername:
388 filename = b'%s-%s' % (filename, repo.filtername)
388 filename = b'%s-%s' % (filename, repo.filtername)
389 return filename
389 return filename
390
390
391
391
392 def _readtagcache(ui, repo):
392 def _readtagcache(ui, repo):
393 '''Read the tag cache.
393 '''Read the tag cache.
394
394
395 Returns a tuple (heads, fnodes, validinfo, cachetags, shouldwrite).
395 Returns a tuple (heads, fnodes, validinfo, cachetags, shouldwrite).
396
396
397 If the cache is completely up-to-date, "cachetags" is a dict of the
397 If the cache is completely up-to-date, "cachetags" is a dict of the
398 form returned by _readtags() and "heads", "fnodes", and "validinfo" are
398 form returned by _readtags() and "heads", "fnodes", and "validinfo" are
399 None and "shouldwrite" is False.
399 None and "shouldwrite" is False.
400
400
401 If the cache is not up to date, "cachetags" is None. "heads" is a list
401 If the cache is not up to date, "cachetags" is None. "heads" is a list
402 of all heads currently in the repository, ordered from tip to oldest.
402 of all heads currently in the repository, ordered from tip to oldest.
403 "validinfo" is a tuple describing cache validation info. This is used
403 "validinfo" is a tuple describing cache validation info. This is used
404 when writing the tags cache. "fnodes" is a mapping from head to .hgtags
404 when writing the tags cache. "fnodes" is a mapping from head to .hgtags
405 filenode. "shouldwrite" is True.
405 filenode. "shouldwrite" is True.
406
406
407 If the cache is not up to date, the caller is responsible for reading tag
407 If the cache is not up to date, the caller is responsible for reading tag
408 info from each returned head. (See findglobaltags().)
408 info from each returned head. (See findglobaltags().)
409 '''
409 '''
410 try:
410 try:
411 cachefile = repo.cachevfs(_filename(repo), b'r')
411 cachefile = repo.cachevfs(_filename(repo), b'r')
412 # force reading the file for static-http
412 # force reading the file for static-http
413 cachelines = iter(cachefile)
413 cachelines = iter(cachefile)
414 except IOError:
414 except IOError:
415 cachefile = None
415 cachefile = None
416
416
417 cacherev = None
417 cacherev = None
418 cachenode = None
418 cachenode = None
419 cachehash = None
419 cachehash = None
420 if cachefile:
420 if cachefile:
421 try:
421 try:
422 validline = next(cachelines)
422 validline = next(cachelines)
423 validline = validline.split()
423 validline = validline.split()
424 cacherev = int(validline[0])
424 cacherev = int(validline[0])
425 cachenode = bin(validline[1])
425 cachenode = bin(validline[1])
426 if len(validline) > 2:
426 if len(validline) > 2:
427 cachehash = bin(validline[2])
427 cachehash = bin(validline[2])
428 except Exception:
428 except Exception:
429 # corruption of the cache, just recompute it.
429 # corruption of the cache, just recompute it.
430 pass
430 pass
431
431
432 tipnode = repo.changelog.tip()
432 tipnode = repo.changelog.tip()
433 tiprev = len(repo.changelog) - 1
433 tiprev = len(repo.changelog) - 1
434
434
435 # Case 1 (common): tip is the same, so nothing has changed.
435 # Case 1 (common): tip is the same, so nothing has changed.
436 # (Unchanged tip trivially means no changesets have been added.
436 # (Unchanged tip trivially means no changesets have been added.
437 # But, thanks to localrepository.destroyed(), it also means none
437 # But, thanks to localrepository.destroyed(), it also means none
438 # have been destroyed by strip or rollback.)
438 # have been destroyed by strip or rollback.)
439 if (
439 if (
440 cacherev == tiprev
440 cacherev == tiprev
441 and cachenode == tipnode
441 and cachenode == tipnode
442 and cachehash == scmutil.filteredhash(repo, tiprev)
442 and cachehash == scmutil.filteredhash(repo, tiprev)
443 ):
443 ):
444 tags = _readtags(ui, repo, cachelines, cachefile.name)
444 tags = _readtags(ui, repo, cachelines, cachefile.name)
445 cachefile.close()
445 cachefile.close()
446 return (None, None, None, tags, False)
446 return (None, None, None, tags, False)
447 if cachefile:
447 if cachefile:
448 cachefile.close() # ignore rest of file
448 cachefile.close() # ignore rest of file
449
449
450 valid = (tiprev, tipnode, scmutil.filteredhash(repo, tiprev))
450 valid = (tiprev, tipnode, scmutil.filteredhash(repo, tiprev))
451
451
452 repoheads = repo.heads()
452 repoheads = repo.heads()
453 # Case 2 (uncommon): empty repo; get out quickly and don't bother
453 # Case 2 (uncommon): empty repo; get out quickly and don't bother
454 # writing an empty cache.
454 # writing an empty cache.
455 if repoheads == [nullid]:
455 if repoheads == [nullid]:
456 return ([], {}, valid, {}, False)
456 return ([], {}, valid, {}, False)
457
457
458 # Case 3 (uncommon): cache file missing or empty.
458 # Case 3 (uncommon): cache file missing or empty.
459
459
460 # Case 4 (uncommon): tip rev decreased. This should only happen
460 # Case 4 (uncommon): tip rev decreased. This should only happen
461 # when we're called from localrepository.destroyed(). Refresh the
461 # when we're called from localrepository.destroyed(). Refresh the
462 # cache so future invocations will not see disappeared heads in the
462 # cache so future invocations will not see disappeared heads in the
463 # cache.
463 # cache.
464
464
465 # Case 5 (common): tip has changed, so we've added/replaced heads.
465 # Case 5 (common): tip has changed, so we've added/replaced heads.
466
466
467 # As it happens, the code to handle cases 3, 4, 5 is the same.
467 # As it happens, the code to handle cases 3, 4, 5 is the same.
468
468
469 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
469 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
470 # exposed".
470 # exposed".
471 if not len(repo.file(b'.hgtags')):
471 if not len(repo.file(b'.hgtags')):
472 # No tags have ever been committed, so we can avoid a
472 # No tags have ever been committed, so we can avoid a
473 # potentially expensive search.
473 # potentially expensive search.
474 return ([], {}, valid, None, True)
474 return ([], {}, valid, None, True)
475
475
476 # Now we have to lookup the .hgtags filenode for every new head.
476 # Now we have to lookup the .hgtags filenode for every new head.
477 # This is the most expensive part of finding tags, so performance
477 # This is the most expensive part of finding tags, so performance
478 # depends primarily on the size of newheads. Worst case: no cache
478 # depends primarily on the size of newheads. Worst case: no cache
479 # file, so newheads == repoheads.
479 # file, so newheads == repoheads.
480 # Reversed order helps the cache ('repoheads' is in descending order)
480 # Reversed order helps the cache ('repoheads' is in descending order)
481 cachefnode = _getfnodes(ui, repo, reversed(repoheads))
481 cachefnode = _getfnodes(ui, repo, reversed(repoheads))
482
482
483 # Caller has to iterate over all heads, but can use the filenodes in
483 # Caller has to iterate over all heads, but can use the filenodes in
484 # cachefnode to get to each .hgtags revision quickly.
484 # cachefnode to get to each .hgtags revision quickly.
485 return (repoheads, cachefnode, valid, None, True)
485 return (repoheads, cachefnode, valid, None, True)
486
486
487
487
488 def _getfnodes(ui, repo, nodes):
488 def _getfnodes(ui, repo, nodes):
489 """return .hgtags fnodes for a list of changeset nodes
489 """return .hgtags fnodes for a list of changeset nodes
490
490
491 Return value is a {node: fnode} mapping. There will be no entry for nodes
491 Return value is a {node: fnode} mapping. There will be no entry for nodes
492 without a '.hgtags' file.
492 without a '.hgtags' file.
493 """
493 """
494 starttime = util.timer()
494 starttime = util.timer()
495 fnodescache = hgtagsfnodescache(repo.unfiltered())
495 fnodescache = hgtagsfnodescache(repo.unfiltered())
496 cachefnode = {}
496 cachefnode = {}
497 for node in nodes:
497 for node in nodes:
498 fnode = fnodescache.getfnode(node)
498 fnode = fnodescache.getfnode(node)
499 if fnode != nullid:
499 if fnode != nullid:
500 cachefnode[node] = fnode
500 cachefnode[node] = fnode
501
501
502 fnodescache.write()
502 fnodescache.write()
503
503
504 duration = util.timer() - starttime
504 duration = util.timer() - starttime
505 ui.log(
505 ui.log(
506 b'tagscache',
506 b'tagscache',
507 b'%d/%d cache hits/lookups in %0.4f seconds\n',
507 b'%d/%d cache hits/lookups in %0.4f seconds\n',
508 fnodescache.hitcount,
508 fnodescache.hitcount,
509 fnodescache.lookupcount,
509 fnodescache.lookupcount,
510 duration,
510 duration,
511 )
511 )
512 return cachefnode
512 return cachefnode
513
513
514
514
515 def _writetagcache(ui, repo, valid, cachetags):
515 def _writetagcache(ui, repo, valid, cachetags):
516 filename = _filename(repo)
516 filename = _filename(repo)
517 try:
517 try:
518 cachefile = repo.cachevfs(filename, b'w', atomictemp=True)
518 cachefile = repo.cachevfs(filename, b'w', atomictemp=True)
519 except (OSError, IOError):
519 except (OSError, IOError):
520 return
520 return
521
521
522 ui.log(
522 ui.log(
523 b'tagscache',
523 b'tagscache',
524 b'writing .hg/cache/%s with %d tags\n',
524 b'writing .hg/cache/%s with %d tags\n',
525 filename,
525 filename,
526 len(cachetags),
526 len(cachetags),
527 )
527 )
528
528
529 if valid[2]:
529 if valid[2]:
530 cachefile.write(
530 cachefile.write(
531 b'%d %s %s\n' % (valid[0], hex(valid[1]), hex(valid[2]))
531 b'%d %s %s\n' % (valid[0], hex(valid[1]), hex(valid[2]))
532 )
532 )
533 else:
533 else:
534 cachefile.write(b'%d %s\n' % (valid[0], hex(valid[1])))
534 cachefile.write(b'%d %s\n' % (valid[0], hex(valid[1])))
535
535
536 # Tag names in the cache are in UTF-8 -- which is the whole reason
536 # Tag names in the cache are in UTF-8 -- which is the whole reason
537 # we keep them in UTF-8 throughout this module. If we converted
537 # we keep them in UTF-8 throughout this module. If we converted
538 # them local encoding on input, we would lose info writing them to
538 # them local encoding on input, we would lose info writing them to
539 # the cache.
539 # the cache.
540 for (name, (node, hist)) in sorted(pycompat.iteritems(cachetags)):
540 for (name, (node, hist)) in sorted(pycompat.iteritems(cachetags)):
541 for n in hist:
541 for n in hist:
542 cachefile.write(b"%s %s\n" % (hex(n), name))
542 cachefile.write(b"%s %s\n" % (hex(n), name))
543 cachefile.write(b"%s %s\n" % (hex(node), name))
543 cachefile.write(b"%s %s\n" % (hex(node), name))
544
544
545 try:
545 try:
546 cachefile.close()
546 cachefile.close()
547 except (OSError, IOError):
547 except (OSError, IOError):
548 pass
548 pass
549
549
550
550
551 def tag(repo, names, node, message, local, user, date, editor=False):
551 def tag(repo, names, node, message, local, user, date, editor=False):
552 '''tag a revision with one or more symbolic names.
552 '''tag a revision with one or more symbolic names.
553
553
554 names is a list of strings or, when adding a single tag, names may be a
554 names is a list of strings or, when adding a single tag, names may be a
555 string.
555 string.
556
556
557 if local is True, the tags are stored in a per-repository file.
557 if local is True, the tags are stored in a per-repository file.
558 otherwise, they are stored in the .hgtags file, and a new
558 otherwise, they are stored in the .hgtags file, and a new
559 changeset is committed with the change.
559 changeset is committed with the change.
560
560
561 keyword arguments:
561 keyword arguments:
562
562
563 local: whether to store tags in non-version-controlled file
563 local: whether to store tags in non-version-controlled file
564 (default False)
564 (default False)
565
565
566 message: commit message to use if committing
566 message: commit message to use if committing
567
567
568 user: name of user to use if committing
568 user: name of user to use if committing
569
569
570 date: date tuple to use if committing'''
570 date: date tuple to use if committing'''
571
571
572 if not local:
572 if not local:
573 m = matchmod.exact([b'.hgtags'])
573 m = matchmod.exact([b'.hgtags'])
574 st = repo.status(match=m, unknown=True, ignored=True)
574 st = repo.status(match=m, unknown=True, ignored=True)
575 if any(
575 if any(
576 (
576 (
577 st.modified,
577 st.modified,
578 st.added,
578 st.added,
579 st.removed,
579 st.removed,
580 st.deleted,
580 st.deleted,
581 st.unknown,
581 st.unknown,
582 st.ignored,
582 st.ignored,
583 )
583 )
584 ):
584 ):
585 raise error.Abort(
585 raise error.Abort(
586 _(b'working copy of .hgtags is changed'),
586 _(b'working copy of .hgtags is changed'),
587 hint=_(b'please commit .hgtags manually'),
587 hint=_(b'please commit .hgtags manually'),
588 )
588 )
589
589
590 with repo.wlock():
590 with repo.wlock():
591 repo.tags() # instantiate the cache
591 repo.tags() # instantiate the cache
592 _tag(repo, names, node, message, local, user, date, editor=editor)
592 _tag(repo, names, node, message, local, user, date, editor=editor)
593
593
594
594
595 def _tag(
595 def _tag(
596 repo, names, node, message, local, user, date, extra=None, editor=False
596 repo, names, node, message, local, user, date, extra=None, editor=False
597 ):
597 ):
598 if isinstance(names, bytes):
598 if isinstance(names, bytes):
599 names = (names,)
599 names = (names,)
600
600
601 branches = repo.branchmap()
601 branches = repo.branchmap()
602 for name in names:
602 for name in names:
603 repo.hook(b'pretag', throw=True, node=hex(node), tag=name, local=local)
603 repo.hook(b'pretag', throw=True, node=hex(node), tag=name, local=local)
604 if name in branches:
604 if name in branches:
605 repo.ui.warn(
605 repo.ui.warn(
606 _(b"warning: tag %s conflicts with existing branch name\n")
606 _(b"warning: tag %s conflicts with existing branch name\n")
607 % name
607 % name
608 )
608 )
609
609
610 def writetags(fp, names, munge, prevtags):
610 def writetags(fp, names, munge, prevtags):
611 fp.seek(0, io.SEEK_END)
611 fp.seek(0, io.SEEK_END)
612 if prevtags and not prevtags.endswith(b'\n'):
612 if prevtags and not prevtags.endswith(b'\n'):
613 fp.write(b'\n')
613 fp.write(b'\n')
614 for name in names:
614 for name in names:
615 if munge:
615 if munge:
616 m = munge(name)
616 m = munge(name)
617 else:
617 else:
618 m = name
618 m = name
619
619
620 if repo._tagscache.tagtypes and name in repo._tagscache.tagtypes:
620 if repo._tagscache.tagtypes and name in repo._tagscache.tagtypes:
621 old = repo.tags().get(name, nullid)
621 old = repo.tags().get(name, nullid)
622 fp.write(b'%s %s\n' % (hex(old), m))
622 fp.write(b'%s %s\n' % (hex(old), m))
623 fp.write(b'%s %s\n' % (hex(node), m))
623 fp.write(b'%s %s\n' % (hex(node), m))
624 fp.close()
624 fp.close()
625
625
626 prevtags = b''
626 prevtags = b''
627 if local:
627 if local:
628 try:
628 try:
629 fp = repo.vfs(b'localtags', b'r+')
629 fp = repo.vfs(b'localtags', b'r+')
630 except IOError:
630 except IOError:
631 fp = repo.vfs(b'localtags', b'a')
631 fp = repo.vfs(b'localtags', b'a')
632 else:
632 else:
633 prevtags = fp.read()
633 prevtags = fp.read()
634
634
635 # local tags are stored in the current charset
635 # local tags are stored in the current charset
636 writetags(fp, names, None, prevtags)
636 writetags(fp, names, None, prevtags)
637 for name in names:
637 for name in names:
638 repo.hook(b'tag', node=hex(node), tag=name, local=local)
638 repo.hook(b'tag', node=hex(node), tag=name, local=local)
639 return
639 return
640
640
641 try:
641 try:
642 fp = repo.wvfs(b'.hgtags', b'rb+')
642 fp = repo.wvfs(b'.hgtags', b'rb+')
643 except IOError as e:
643 except IOError as e:
644 if e.errno != errno.ENOENT:
644 if e.errno != errno.ENOENT:
645 raise
645 raise
646 fp = repo.wvfs(b'.hgtags', b'ab')
646 fp = repo.wvfs(b'.hgtags', b'ab')
647 else:
647 else:
648 prevtags = fp.read()
648 prevtags = fp.read()
649
649
650 # committed tags are stored in UTF-8
650 # committed tags are stored in UTF-8
651 writetags(fp, names, encoding.fromlocal, prevtags)
651 writetags(fp, names, encoding.fromlocal, prevtags)
652
652
653 fp.close()
653 fp.close()
654
654
655 repo.invalidatecaches()
655 repo.invalidatecaches()
656
656
657 if b'.hgtags' not in repo.dirstate:
657 if b'.hgtags' not in repo.dirstate:
658 repo[None].add([b'.hgtags'])
658 repo[None].add([b'.hgtags'])
659
659
660 m = matchmod.exact([b'.hgtags'])
660 m = matchmod.exact([b'.hgtags'])
661 tagnode = repo.commit(
661 tagnode = repo.commit(
662 message, user, date, extra=extra, match=m, editor=editor
662 message, user, date, extra=extra, match=m, editor=editor
663 )
663 )
664
664
665 for name in names:
665 for name in names:
666 repo.hook(b'tag', node=hex(node), tag=name, local=local)
666 repo.hook(b'tag', node=hex(node), tag=name, local=local)
667
667
668 return tagnode
668 return tagnode
669
669
670
670
671 _fnodescachefile = b'hgtagsfnodes1'
671 _fnodescachefile = b'hgtagsfnodes1'
672 _fnodesrecsize = 4 + 20 # changeset fragment + filenode
672 _fnodesrecsize = 4 + 20 # changeset fragment + filenode
673 _fnodesmissingrec = b'\xff' * 24
673 _fnodesmissingrec = b'\xff' * 24
674
674
675
675
676 class hgtagsfnodescache(object):
676 class hgtagsfnodescache(object):
677 """Persistent cache mapping revisions to .hgtags filenodes.
677 """Persistent cache mapping revisions to .hgtags filenodes.
678
678
679 The cache is an array of records. Each item in the array corresponds to
679 The cache is an array of records. Each item in the array corresponds to
680 a changelog revision. Values in the array contain the first 4 bytes of
680 a changelog revision. Values in the array contain the first 4 bytes of
681 the node hash and the 20 bytes .hgtags filenode for that revision.
681 the node hash and the 20 bytes .hgtags filenode for that revision.
682
682
683 The first 4 bytes are present as a form of verification. Repository
683 The first 4 bytes are present as a form of verification. Repository
684 stripping and rewriting may change the node at a numeric revision in the
684 stripping and rewriting may change the node at a numeric revision in the
685 changelog. The changeset fragment serves as a verifier to detect
685 changelog. The changeset fragment serves as a verifier to detect
686 rewriting. This logic is shared with the rev branch cache (see
686 rewriting. This logic is shared with the rev branch cache (see
687 branchmap.py).
687 branchmap.py).
688
688
689 The instance holds in memory the full cache content but entries are
689 The instance holds in memory the full cache content but entries are
690 only parsed on read.
690 only parsed on read.
691
691
692 Instances behave like lists. ``c[i]`` works where i is a rev or
692 Instances behave like lists. ``c[i]`` works where i is a rev or
693 changeset node. Missing indexes are populated automatically on access.
693 changeset node. Missing indexes are populated automatically on access.
694 """
694 """
695
695
696 def __init__(self, repo):
696 def __init__(self, repo):
697 assert repo.filtername is None
697 assert repo.filtername is None
698
698
699 self._repo = repo
699 self._repo = repo
700
700
701 # Only for reporting purposes.
701 # Only for reporting purposes.
702 self.lookupcount = 0
702 self.lookupcount = 0
703 self.hitcount = 0
703 self.hitcount = 0
704
704
705 try:
705 try:
706 data = repo.cachevfs.read(_fnodescachefile)
706 data = repo.cachevfs.read(_fnodescachefile)
707 except (OSError, IOError):
707 except (OSError, IOError):
708 data = b""
708 data = b""
709 self._raw = bytearray(data)
709 self._raw = bytearray(data)
710
710
711 # The end state of self._raw is an array that is of the exact length
711 # The end state of self._raw is an array that is of the exact length
712 # required to hold a record for every revision in the repository.
712 # required to hold a record for every revision in the repository.
713 # We truncate or extend the array as necessary. self._dirtyoffset is
713 # We truncate or extend the array as necessary. self._dirtyoffset is
714 # defined to be the start offset at which we need to write the output
714 # defined to be the start offset at which we need to write the output
715 # file. This offset is also adjusted when new entries are calculated
715 # file. This offset is also adjusted when new entries are calculated
716 # for array members.
716 # for array members.
717 cllen = len(repo.changelog)
717 cllen = len(repo.changelog)
718 wantedlen = cllen * _fnodesrecsize
718 wantedlen = cllen * _fnodesrecsize
719 rawlen = len(self._raw)
719 rawlen = len(self._raw)
720
720
721 self._dirtyoffset = None
721 self._dirtyoffset = None
722
722
723 rawlentokeep = min(
723 rawlentokeep = min(
724 wantedlen, (rawlen // _fnodesrecsize) * _fnodesrecsize
724 wantedlen, (rawlen // _fnodesrecsize) * _fnodesrecsize
725 )
725 )
726 if rawlen > rawlentokeep:
726 if rawlen > rawlentokeep:
727 # There's no easy way to truncate array instances. This seems
727 # There's no easy way to truncate array instances. This seems
728 # slightly less evil than copying a potentially large array slice.
728 # slightly less evil than copying a potentially large array slice.
729 for i in range(rawlen - rawlentokeep):
729 for i in range(rawlen - rawlentokeep):
730 self._raw.pop()
730 self._raw.pop()
731 rawlen = len(self._raw)
731 rawlen = len(self._raw)
732 self._dirtyoffset = rawlen
732 self._dirtyoffset = rawlen
733 if rawlen < wantedlen:
733 if rawlen < wantedlen:
734 if self._dirtyoffset is None:
734 if self._dirtyoffset is None:
735 self._dirtyoffset = rawlen
735 self._dirtyoffset = rawlen
736 self._raw.extend(b'\xff' * (wantedlen - rawlen))
736 self._raw.extend(b'\xff' * (wantedlen - rawlen))
737
737
738 def getfnode(self, node, computemissing=True):
738 def getfnode(self, node, computemissing=True):
739 """Obtain the filenode of the .hgtags file at a specified revision.
739 """Obtain the filenode of the .hgtags file at a specified revision.
740
740
741 If the value is in the cache, the entry will be validated and returned.
741 If the value is in the cache, the entry will be validated and returned.
742 Otherwise, the filenode will be computed and returned unless
742 Otherwise, the filenode will be computed and returned unless
743 "computemissing" is False, in which case None will be returned without
743 "computemissing" is False, in which case None will be returned without
744 any potentially expensive computation being performed.
744 any potentially expensive computation being performed.
745
745
746 If an .hgtags does not exist at the specified revision, nullid is
746 If an .hgtags does not exist at the specified revision, nullid is
747 returned.
747 returned.
748 """
748 """
749 if node == nullid:
749 if node == nullid:
750 return nullid
750 return nullid
751
751
752 ctx = self._repo[node]
752 ctx = self._repo[node]
753 rev = ctx.rev()
753 rev = ctx.rev()
754
754
755 self.lookupcount += 1
755 self.lookupcount += 1
756
756
757 offset = rev * _fnodesrecsize
757 offset = rev * _fnodesrecsize
758 record = b'%s' % self._raw[offset : offset + _fnodesrecsize]
758 record = b'%s' % self._raw[offset : offset + _fnodesrecsize]
759 properprefix = node[0:4]
759 properprefix = node[0:4]
760
760
761 # Validate and return existing entry.
761 # Validate and return existing entry.
762 if record != _fnodesmissingrec:
762 if record != _fnodesmissingrec:
763 fileprefix = record[0:4]
763 fileprefix = record[0:4]
764
764
765 if fileprefix == properprefix:
765 if fileprefix == properprefix:
766 self.hitcount += 1
766 self.hitcount += 1
767 return record[4:]
767 return record[4:]
768
768
769 # Fall through.
769 # Fall through.
770
770
771 # If we get here, the entry is either missing or invalid.
771 # If we get here, the entry is either missing or invalid.
772
772
773 if not computemissing:
773 if not computemissing:
774 return None
774 return None
775
775
776 fnode = None
776 fnode = None
777 cl = self._repo.changelog
777 cl = self._repo.changelog
778 p1rev, p2rev = cl._uncheckedparentrevs(rev)
778 p1rev, p2rev = cl._uncheckedparentrevs(rev)
779 p1node = cl.node(p1rev)
779 p1node = cl.node(p1rev)
780 p1fnode = self.getfnode(p1node, computemissing=False)
780 p1fnode = self.getfnode(p1node, computemissing=False)
781 if p2rev != nullrev:
781 if p2rev != nullrev:
782 # There is some no-merge changeset where p1 is null and p2 is set
782 # There is some no-merge changeset where p1 is null and p2 is set
783 # Processing them as merge is just slower, but still gives a good
783 # Processing them as merge is just slower, but still gives a good
784 # result.
784 # result.
785 p2node = cl.node(p1rev)
785 p2node = cl.node(p1rev)
786 p2fnode = self.getfnode(p2node, computemissing=False)
786 p2fnode = self.getfnode(p2node, computemissing=False)
787 if p1fnode != p2fnode:
787 if p1fnode != p2fnode:
788 # we cannot rely on readfast because we don't know against what
788 # we cannot rely on readfast because we don't know against what
789 # parent the readfast delta is computed
789 # parent the readfast delta is computed
790 p1fnode = None
790 p1fnode = None
791 if p1fnode is not None:
791 if p1fnode is not None:
792 mctx = ctx.manifestctx()
792 mctx = ctx.manifestctx()
793 fnode = mctx.readfast().get(b'.hgtags')
793 fnode = mctx.readfast().get(b'.hgtags')
794 if fnode is None:
794 if fnode is None:
795 fnode = p1fnode
795 fnode = p1fnode
796 if fnode is None:
796 if fnode is None:
797 # Populate missing entry.
797 # Populate missing entry.
798 try:
798 try:
799 fnode = ctx.filenode(b'.hgtags')
799 fnode = ctx.filenode(b'.hgtags')
800 except error.LookupError:
800 except error.LookupError:
801 # No .hgtags file on this revision.
801 # No .hgtags file on this revision.
802 fnode = nullid
802 fnode = nullid
803
803
804 self._writeentry(offset, properprefix, fnode)
804 self._writeentry(offset, properprefix, fnode)
805 return fnode
805 return fnode
806
806
807 def setfnode(self, node, fnode):
807 def setfnode(self, node, fnode):
808 """Set the .hgtags filenode for a given changeset."""
808 """Set the .hgtags filenode for a given changeset."""
809 assert len(fnode) == 20
809 assert len(fnode) == 20
810 ctx = self._repo[node]
810 ctx = self._repo[node]
811
811
812 # Do a lookup first to avoid writing if nothing has changed.
812 # Do a lookup first to avoid writing if nothing has changed.
813 if self.getfnode(ctx.node(), computemissing=False) == fnode:
813 if self.getfnode(ctx.node(), computemissing=False) == fnode:
814 return
814 return
815
815
816 self._writeentry(ctx.rev() * _fnodesrecsize, node[0:4], fnode)
816 self._writeentry(ctx.rev() * _fnodesrecsize, node[0:4], fnode)
817
817
818 def _writeentry(self, offset, prefix, fnode):
818 def _writeentry(self, offset, prefix, fnode):
819 # Slices on array instances only accept other array.
819 # Slices on array instances only accept other array.
820 entry = bytearray(prefix + fnode)
820 entry = bytearray(prefix + fnode)
821 self._raw[offset : offset + _fnodesrecsize] = entry
821 self._raw[offset : offset + _fnodesrecsize] = entry
822 # self._dirtyoffset could be None.
822 # self._dirtyoffset could be None.
823 self._dirtyoffset = min(self._dirtyoffset or 0, offset or 0)
823 self._dirtyoffset = min(self._dirtyoffset or 0, offset or 0)
824
824
825 def write(self):
825 def write(self):
826 """Perform all necessary writes to cache file.
826 """Perform all necessary writes to cache file.
827
827
828 This may no-op if no writes are needed or if a write lock could
828 This may no-op if no writes are needed or if a write lock could
829 not be obtained.
829 not be obtained.
830 """
830 """
831 if self._dirtyoffset is None:
831 if self._dirtyoffset is None:
832 return
832 return
833
833
834 data = self._raw[self._dirtyoffset :]
834 data = self._raw[self._dirtyoffset :]
835 if not data:
835 if not data:
836 return
836 return
837
837
838 repo = self._repo
838 repo = self._repo
839
839
840 try:
840 try:
841 lock = repo.wlock(wait=False)
841 lock = repo.lock(wait=False)
842 except error.LockError:
842 except error.LockError:
843 repo.ui.log(
843 repo.ui.log(
844 b'tagscache',
844 b'tagscache',
845 b'not writing .hg/cache/%s because '
845 b'not writing .hg/cache/%s because '
846 b'lock cannot be acquired\n' % _fnodescachefile,
846 b'lock cannot be acquired\n' % _fnodescachefile,
847 )
847 )
848 return
848 return
849
849
850 try:
850 try:
851 f = repo.cachevfs.open(_fnodescachefile, b'ab')
851 f = repo.cachevfs.open(_fnodescachefile, b'ab')
852 try:
852 try:
853 # if the file has been truncated
853 # if the file has been truncated
854 actualoffset = f.tell()
854 actualoffset = f.tell()
855 if actualoffset < self._dirtyoffset:
855 if actualoffset < self._dirtyoffset:
856 self._dirtyoffset = actualoffset
856 self._dirtyoffset = actualoffset
857 data = self._raw[self._dirtyoffset :]
857 data = self._raw[self._dirtyoffset :]
858 f.seek(self._dirtyoffset)
858 f.seek(self._dirtyoffset)
859 f.truncate()
859 f.truncate()
860 repo.ui.log(
860 repo.ui.log(
861 b'tagscache',
861 b'tagscache',
862 b'writing %d bytes to cache/%s\n'
862 b'writing %d bytes to cache/%s\n'
863 % (len(data), _fnodescachefile),
863 % (len(data), _fnodescachefile),
864 )
864 )
865 f.write(data)
865 f.write(data)
866 self._dirtyoffset = None
866 self._dirtyoffset = None
867 finally:
867 finally:
868 f.close()
868 f.close()
869 except (IOError, OSError) as inst:
869 except (IOError, OSError) as inst:
870 repo.ui.log(
870 repo.ui.log(
871 b'tagscache',
871 b'tagscache',
872 b"couldn't write cache/%s: %s\n"
872 b"couldn't write cache/%s: %s\n"
873 % (_fnodescachefile, stringutil.forcebytestr(inst)),
873 % (_fnodescachefile, stringutil.forcebytestr(inst)),
874 )
874 )
875 finally:
875 finally:
876 lock.release()
876 lock.release()
@@ -1,848 +1,848 b''
1 setup
1 setup
2
2
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > blackbox=
5 > blackbox=
6 > mock=$TESTDIR/mockblackbox.py
6 > mock=$TESTDIR/mockblackbox.py
7 > [blackbox]
7 > [blackbox]
8 > track = command, commandfinish, tagscache
8 > track = command, commandfinish, tagscache
9 > EOF
9 > EOF
10
10
11 Helper functions:
11 Helper functions:
12
12
13 $ cacheexists() {
13 $ cacheexists() {
14 > [ -f .hg/cache/tags2-visible ] && echo "tag cache exists" || echo "no tag cache"
14 > [ -f .hg/cache/tags2-visible ] && echo "tag cache exists" || echo "no tag cache"
15 > }
15 > }
16
16
17 $ fnodescacheexists() {
17 $ fnodescacheexists() {
18 > [ -f .hg/cache/hgtagsfnodes1 ] && echo "fnodes cache exists" || echo "no fnodes cache"
18 > [ -f .hg/cache/hgtagsfnodes1 ] && echo "fnodes cache exists" || echo "no fnodes cache"
19 > }
19 > }
20
20
21 $ dumptags() {
21 $ dumptags() {
22 > rev=$1
22 > rev=$1
23 > echo "rev $rev: .hgtags:"
23 > echo "rev $rev: .hgtags:"
24 > hg cat -r$rev .hgtags
24 > hg cat -r$rev .hgtags
25 > }
25 > }
26
26
27 # XXX need to test that the tag cache works when we strip an old head
27 # XXX need to test that the tag cache works when we strip an old head
28 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
28 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
29 # same, but stuff has changed behind tip.
29 # same, but stuff has changed behind tip.
30
30
31 Setup:
31 Setup:
32
32
33 $ hg init t
33 $ hg init t
34 $ cd t
34 $ cd t
35 $ cacheexists
35 $ cacheexists
36 no tag cache
36 no tag cache
37 $ fnodescacheexists
37 $ fnodescacheexists
38 no fnodes cache
38 no fnodes cache
39 $ hg id
39 $ hg id
40 000000000000 tip
40 000000000000 tip
41 $ cacheexists
41 $ cacheexists
42 no tag cache
42 no tag cache
43 $ fnodescacheexists
43 $ fnodescacheexists
44 no fnodes cache
44 no fnodes cache
45 $ echo a > a
45 $ echo a > a
46 $ hg add a
46 $ hg add a
47 $ hg commit -m "test"
47 $ hg commit -m "test"
48 $ hg co
48 $ hg co
49 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 $ hg identify
50 $ hg identify
51 acb14030fe0a tip
51 acb14030fe0a tip
52 $ hg identify -r 'wdir()'
52 $ hg identify -r 'wdir()'
53 acb14030fe0a tip
53 acb14030fe0a tip
54 $ cacheexists
54 $ cacheexists
55 tag cache exists
55 tag cache exists
56 No fnodes cache because .hgtags file doesn't exist
56 No fnodes cache because .hgtags file doesn't exist
57 (this is an implementation detail)
57 (this is an implementation detail)
58 $ fnodescacheexists
58 $ fnodescacheexists
59 no fnodes cache
59 no fnodes cache
60
60
61 Try corrupting the cache
61 Try corrupting the cache
62
62
63 $ printf 'a b' > .hg/cache/tags2-visible
63 $ printf 'a b' > .hg/cache/tags2-visible
64 $ hg identify
64 $ hg identify
65 acb14030fe0a tip
65 acb14030fe0a tip
66 $ cacheexists
66 $ cacheexists
67 tag cache exists
67 tag cache exists
68 $ fnodescacheexists
68 $ fnodescacheexists
69 no fnodes cache
69 no fnodes cache
70 $ hg identify
70 $ hg identify
71 acb14030fe0a tip
71 acb14030fe0a tip
72
72
73 Create local tag with long name:
73 Create local tag with long name:
74
74
75 $ T=`hg identify --debug --id`
75 $ T=`hg identify --debug --id`
76 $ hg tag -l "This is a local tag with a really long name!"
76 $ hg tag -l "This is a local tag with a really long name!"
77 $ hg tags
77 $ hg tags
78 tip 0:acb14030fe0a
78 tip 0:acb14030fe0a
79 This is a local tag with a really long name! 0:acb14030fe0a
79 This is a local tag with a really long name! 0:acb14030fe0a
80 $ rm .hg/localtags
80 $ rm .hg/localtags
81
81
82 Create a tag behind hg's back:
82 Create a tag behind hg's back:
83
83
84 $ echo "$T first" > .hgtags
84 $ echo "$T first" > .hgtags
85 $ cat .hgtags
85 $ cat .hgtags
86 acb14030fe0a21b60322c440ad2d20cf7685a376 first
86 acb14030fe0a21b60322c440ad2d20cf7685a376 first
87 $ hg add .hgtags
87 $ hg add .hgtags
88 $ hg commit -m "add tags"
88 $ hg commit -m "add tags"
89 $ hg tags
89 $ hg tags
90 tip 1:b9154636be93
90 tip 1:b9154636be93
91 first 0:acb14030fe0a
91 first 0:acb14030fe0a
92 $ hg identify
92 $ hg identify
93 b9154636be93 tip
93 b9154636be93 tip
94
94
95 We should have a fnodes cache now that we have a real tag
95 We should have a fnodes cache now that we have a real tag
96 The cache should have an empty entry for rev 0 and a valid entry for rev 1.
96 The cache should have an empty entry for rev 0 and a valid entry for rev 1.
97
97
98
98
99 $ fnodescacheexists
99 $ fnodescacheexists
100 fnodes cache exists
100 fnodes cache exists
101 $ f --size --hexdump .hg/cache/hgtagsfnodes1
101 $ f --size --hexdump .hg/cache/hgtagsfnodes1
102 .hg/cache/hgtagsfnodes1: size=48
102 .hg/cache/hgtagsfnodes1: size=48
103 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
103 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
104 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
104 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
105 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
105 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
106 $ hg debugtagscache
106 $ hg debugtagscache
107 0 acb14030fe0a21b60322c440ad2d20cf7685a376 missing/invalid
107 0 acb14030fe0a21b60322c440ad2d20cf7685a376 missing/invalid
108 1 b9154636be938d3d431e75a7c906504a079bfe07 26b7b4a773e09ee3c52f510e19e05e1ff966d859
108 1 b9154636be938d3d431e75a7c906504a079bfe07 26b7b4a773e09ee3c52f510e19e05e1ff966d859
109
109
110 Repeat with cold tag cache:
110 Repeat with cold tag cache:
111
111
112 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
112 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
113 $ hg identify
113 $ hg identify
114 b9154636be93 tip
114 b9154636be93 tip
115
115
116 $ fnodescacheexists
116 $ fnodescacheexists
117 fnodes cache exists
117 fnodes cache exists
118 $ f --size --hexdump .hg/cache/hgtagsfnodes1
118 $ f --size --hexdump .hg/cache/hgtagsfnodes1
119 .hg/cache/hgtagsfnodes1: size=48
119 .hg/cache/hgtagsfnodes1: size=48
120 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
120 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
121 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
121 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
122 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
122 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
123
123
124 And again, but now unable to write tag cache or lock file:
124 And again, but now unable to write tag cache or lock file:
125
125
126 #if unix-permissions no-fsmonitor
126 #if unix-permissions no-fsmonitor
127
127
128 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
128 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
129 $ chmod 555 .hg/cache
129 $ chmod 555 .hg/cache
130 $ hg identify
130 $ hg identify
131 b9154636be93 tip
131 b9154636be93 tip
132 $ chmod 755 .hg/cache
132 $ chmod 755 .hg/cache
133
133
134 (this block should be protected by no-fsmonitor, because "chmod 555 .hg"
134 (this block should be protected by no-fsmonitor, because "chmod 555 .hg"
135 makes watchman fail at accessing to files under .hg)
135 makes watchman fail at accessing to files under .hg)
136
136
137 $ chmod 555 .hg
137 $ chmod 555 .hg
138 $ hg identify
138 $ hg identify
139 b9154636be93 tip
139 b9154636be93 tip
140 $ chmod 755 .hg
140 $ chmod 755 .hg
141 #endif
141 #endif
142
142
143 Tag cache debug info written to blackbox log
143 Tag cache debug info written to blackbox log
144
144
145 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
145 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
146 $ hg identify
146 $ hg identify
147 b9154636be93 tip
147 b9154636be93 tip
148 $ hg blackbox -l 6
148 $ hg blackbox -l 6
149 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
149 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
150 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing 48 bytes to cache/hgtagsfnodes1
150 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing 48 bytes to cache/hgtagsfnodes1
151 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
151 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
152 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
152 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
153 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
153 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
154 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
154 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
155
155
156 Failure to acquire lock results in no write
156 Failure to acquire lock results in no write
157
157
158 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
158 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
159 $ echo 'foo:1' > .hg/wlock
159 $ echo 'foo:1' > .hg/store/lock
160 $ hg identify
160 $ hg identify
161 b9154636be93 tip
161 b9154636be93 tip
162 $ hg blackbox -l 6
162 $ hg blackbox -l 6
163 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
163 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
164 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> not writing .hg/cache/hgtagsfnodes1 because lock cannot be acquired
164 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> not writing .hg/cache/hgtagsfnodes1 because lock cannot be acquired
165 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
165 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
166 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
166 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
167 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
167 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
168 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
168 1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
169
169
170 $ fnodescacheexists
170 $ fnodescacheexists
171 no fnodes cache
171 no fnodes cache
172
172
173 $ rm .hg/wlock
173 $ rm .hg/store/lock
174
174
175 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
175 $ rm -f .hg/cache/tags2-visible .hg/cache/hgtagsfnodes1
176 $ hg identify
176 $ hg identify
177 b9154636be93 tip
177 b9154636be93 tip
178
178
179 Create a branch:
179 Create a branch:
180
180
181 $ echo bb > a
181 $ echo bb > a
182 $ hg status
182 $ hg status
183 M a
183 M a
184 $ hg identify
184 $ hg identify
185 b9154636be93+ tip
185 b9154636be93+ tip
186 $ hg co first
186 $ hg co first
187 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
187 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
188 $ hg id
188 $ hg id
189 acb14030fe0a+ first
189 acb14030fe0a+ first
190 $ hg id -r 'wdir()'
190 $ hg id -r 'wdir()'
191 acb14030fe0a+ first
191 acb14030fe0a+ first
192 $ hg -v id
192 $ hg -v id
193 acb14030fe0a+ first
193 acb14030fe0a+ first
194 $ hg status
194 $ hg status
195 M a
195 M a
196 $ echo 1 > b
196 $ echo 1 > b
197 $ hg add b
197 $ hg add b
198 $ hg commit -m "branch"
198 $ hg commit -m "branch"
199 created new head
199 created new head
200
200
201 Creating a new commit shouldn't append the .hgtags fnodes cache until
201 Creating a new commit shouldn't append the .hgtags fnodes cache until
202 tags info is accessed
202 tags info is accessed
203
203
204 $ f --size --hexdump .hg/cache/hgtagsfnodes1
204 $ f --size --hexdump .hg/cache/hgtagsfnodes1
205 .hg/cache/hgtagsfnodes1: size=48
205 .hg/cache/hgtagsfnodes1: size=48
206 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
206 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
207 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
207 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
208 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
208 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
209
209
210 $ hg id
210 $ hg id
211 c8edf04160c7 tip
211 c8edf04160c7 tip
212
212
213 First 4 bytes of record 3 are changeset fragment
213 First 4 bytes of record 3 are changeset fragment
214
214
215 $ f --size --hexdump .hg/cache/hgtagsfnodes1
215 $ f --size --hexdump .hg/cache/hgtagsfnodes1
216 .hg/cache/hgtagsfnodes1: size=72
216 .hg/cache/hgtagsfnodes1: size=72
217 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
217 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
218 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
218 0010: ff ff ff ff ff ff ff ff b9 15 46 36 26 b7 b4 a7 |..........F6&...|
219 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
219 0020: 73 e0 9e e3 c5 2f 51 0e 19 e0 5e 1f f9 66 d8 59 |s..../Q...^..f.Y|
220 0030: c8 ed f0 41 00 00 00 00 00 00 00 00 00 00 00 00 |...A............|
220 0030: c8 ed f0 41 00 00 00 00 00 00 00 00 00 00 00 00 |...A............|
221 0040: 00 00 00 00 00 00 00 00 |........|
221 0040: 00 00 00 00 00 00 00 00 |........|
222
222
223 Merge the two heads:
223 Merge the two heads:
224
224
225 $ hg merge 1
225 $ hg merge 1
226 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 (branch merge, don't forget to commit)
227 (branch merge, don't forget to commit)
228 $ hg blackbox -l3
228 $ hg blackbox -l3
229 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28 (5000)> merge 1
229 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28 (5000)> merge 1
230 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28+b9154636be938d3d431e75a7c906504a079bfe07 (5000)> merge 1 exited 0 after * seconds (glob)
230 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28+b9154636be938d3d431e75a7c906504a079bfe07 (5000)> merge 1 exited 0 after * seconds (glob)
231 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28+b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l3
231 1970/01/01 00:00:00 bob @c8edf04160c7f731e4589d66ab3ab3486a64ac28+b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l3
232 $ hg id
232 $ hg id
233 c8edf04160c7+b9154636be93+ tip
233 c8edf04160c7+b9154636be93+ tip
234 $ hg status
234 $ hg status
235 M .hgtags
235 M .hgtags
236 $ hg commit -m "merge"
236 $ hg commit -m "merge"
237
237
238 Create a fake head, make sure tag not visible afterwards:
238 Create a fake head, make sure tag not visible afterwards:
239
239
240 $ cp .hgtags tags
240 $ cp .hgtags tags
241 $ hg tag last
241 $ hg tag last
242 $ hg rm .hgtags
242 $ hg rm .hgtags
243 $ hg commit -m "remove"
243 $ hg commit -m "remove"
244
244
245 $ mv tags .hgtags
245 $ mv tags .hgtags
246 $ hg add .hgtags
246 $ hg add .hgtags
247 $ hg commit -m "readd"
247 $ hg commit -m "readd"
248 $
248 $
249 $ hg tags
249 $ hg tags
250 tip 6:35ff301afafe
250 tip 6:35ff301afafe
251 first 0:acb14030fe0a
251 first 0:acb14030fe0a
252
252
253 Add invalid tags:
253 Add invalid tags:
254
254
255 $ echo "spam" >> .hgtags
255 $ echo "spam" >> .hgtags
256 $ echo >> .hgtags
256 $ echo >> .hgtags
257 $ echo "foo bar" >> .hgtags
257 $ echo "foo bar" >> .hgtags
258 $ echo "a5a5 invalid" >> .hg/localtags
258 $ echo "a5a5 invalid" >> .hg/localtags
259 $ cat .hgtags
259 $ cat .hgtags
260 acb14030fe0a21b60322c440ad2d20cf7685a376 first
260 acb14030fe0a21b60322c440ad2d20cf7685a376 first
261 spam
261 spam
262
262
263 foo bar
263 foo bar
264 $ hg commit -m "tags"
264 $ hg commit -m "tags"
265
265
266 Report tag parse error on other head:
266 Report tag parse error on other head:
267
267
268 $ hg up 3
268 $ hg up 3
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 $ echo 'x y' >> .hgtags
270 $ echo 'x y' >> .hgtags
271 $ hg commit -m "head"
271 $ hg commit -m "head"
272 created new head
272 created new head
273
273
274 $ hg tags --debug
274 $ hg tags --debug
275 .hgtags@75d9f02dfe28, line 2: cannot parse entry
275 .hgtags@75d9f02dfe28, line 2: cannot parse entry
276 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
276 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
277 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
277 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
278 tip 8:c4be69a18c11e8bc3a5fdbb576017c25f7d84663
278 tip 8:c4be69a18c11e8bc3a5fdbb576017c25f7d84663
279 first 0:acb14030fe0a21b60322c440ad2d20cf7685a376
279 first 0:acb14030fe0a21b60322c440ad2d20cf7685a376
280 $ hg tip
280 $ hg tip
281 changeset: 8:c4be69a18c11
281 changeset: 8:c4be69a18c11
282 tag: tip
282 tag: tip
283 parent: 3:ac5e980c4dc0
283 parent: 3:ac5e980c4dc0
284 user: test
284 user: test
285 date: Thu Jan 01 00:00:00 1970 +0000
285 date: Thu Jan 01 00:00:00 1970 +0000
286 summary: head
286 summary: head
287
287
288
288
289 Test tag precedence rules:
289 Test tag precedence rules:
290
290
291 $ cd ..
291 $ cd ..
292 $ hg init t2
292 $ hg init t2
293 $ cd t2
293 $ cd t2
294 $ echo foo > foo
294 $ echo foo > foo
295 $ hg add foo
295 $ hg add foo
296 $ hg ci -m 'add foo' # rev 0
296 $ hg ci -m 'add foo' # rev 0
297 $ hg tag bar # rev 1
297 $ hg tag bar # rev 1
298 $ echo >> foo
298 $ echo >> foo
299 $ hg ci -m 'change foo 1' # rev 2
299 $ hg ci -m 'change foo 1' # rev 2
300 $ hg up -C 1
300 $ hg up -C 1
301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 $ hg tag -r 1 -f bar # rev 3
302 $ hg tag -r 1 -f bar # rev 3
303 $ hg up -C 1
303 $ hg up -C 1
304 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
305 $ echo >> foo
305 $ echo >> foo
306 $ hg ci -m 'change foo 2' # rev 4
306 $ hg ci -m 'change foo 2' # rev 4
307 created new head
307 created new head
308 $ hg tags
308 $ hg tags
309 tip 4:0c192d7d5e6b
309 tip 4:0c192d7d5e6b
310 bar 1:78391a272241
310 bar 1:78391a272241
311
311
312 Repeat in case of cache effects:
312 Repeat in case of cache effects:
313
313
314 $ hg tags
314 $ hg tags
315 tip 4:0c192d7d5e6b
315 tip 4:0c192d7d5e6b
316 bar 1:78391a272241
316 bar 1:78391a272241
317
317
318 Detailed dump of tag info:
318 Detailed dump of tag info:
319
319
320 $ hg heads -q # expect 4, 3, 2
320 $ hg heads -q # expect 4, 3, 2
321 4:0c192d7d5e6b
321 4:0c192d7d5e6b
322 3:6fa450212aeb
322 3:6fa450212aeb
323 2:7a94127795a3
323 2:7a94127795a3
324 $ dumptags 2
324 $ dumptags 2
325 rev 2: .hgtags:
325 rev 2: .hgtags:
326 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
326 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
327 $ dumptags 3
327 $ dumptags 3
328 rev 3: .hgtags:
328 rev 3: .hgtags:
329 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
329 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
330 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
330 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
331 78391a272241d70354aa14c874552cad6b51bb42 bar
331 78391a272241d70354aa14c874552cad6b51bb42 bar
332 $ dumptags 4
332 $ dumptags 4
333 rev 4: .hgtags:
333 rev 4: .hgtags:
334 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
334 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
335
335
336 Dump cache:
336 Dump cache:
337
337
338 $ cat .hg/cache/tags2-visible
338 $ cat .hg/cache/tags2-visible
339 4 0c192d7d5e6b78a714de54a2e9627952a877e25a
339 4 0c192d7d5e6b78a714de54a2e9627952a877e25a
340 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
340 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
341 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
341 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
342 78391a272241d70354aa14c874552cad6b51bb42 bar
342 78391a272241d70354aa14c874552cad6b51bb42 bar
343
343
344 $ f --size --hexdump .hg/cache/hgtagsfnodes1
344 $ f --size --hexdump .hg/cache/hgtagsfnodes1
345 .hg/cache/hgtagsfnodes1: size=120
345 .hg/cache/hgtagsfnodes1: size=120
346 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
346 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
347 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
347 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
348 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
348 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
349 0030: 7a 94 12 77 0c 04 f2 a8 af 31 de 17 fa b7 42 28 |z..w.....1....B(|
349 0030: 7a 94 12 77 0c 04 f2 a8 af 31 de 17 fa b7 42 28 |z..w.....1....B(|
350 0040: 78 ee 5a 2d ad bc 94 3d 6f a4 50 21 7d 3b 71 8c |x.Z-...=o.P!};q.|
350 0040: 78 ee 5a 2d ad bc 94 3d 6f a4 50 21 7d 3b 71 8c |x.Z-...=o.P!};q.|
351 0050: 96 4e f3 7b 89 e5 50 eb da fd 57 89 e7 6c e1 b0 |.N.{..P...W..l..|
351 0050: 96 4e f3 7b 89 e5 50 eb da fd 57 89 e7 6c e1 b0 |.N.{..P...W..l..|
352 0060: 0c 19 2d 7d 0c 04 f2 a8 af 31 de 17 fa b7 42 28 |..-}.....1....B(|
352 0060: 0c 19 2d 7d 0c 04 f2 a8 af 31 de 17 fa b7 42 28 |..-}.....1....B(|
353 0070: 78 ee 5a 2d ad bc 94 3d |x.Z-...=|
353 0070: 78 ee 5a 2d ad bc 94 3d |x.Z-...=|
354
354
355 Corrupt the .hgtags fnodes cache
355 Corrupt the .hgtags fnodes cache
356 Extra junk data at the end should get overwritten on next cache update
356 Extra junk data at the end should get overwritten on next cache update
357
357
358 $ echo extra >> .hg/cache/hgtagsfnodes1
358 $ echo extra >> .hg/cache/hgtagsfnodes1
359 $ echo dummy1 > foo
359 $ echo dummy1 > foo
360 $ hg commit -m throwaway1
360 $ hg commit -m throwaway1
361
361
362 $ hg tags
362 $ hg tags
363 tip 5:8dbfe60eff30
363 tip 5:8dbfe60eff30
364 bar 1:78391a272241
364 bar 1:78391a272241
365
365
366 $ hg blackbox -l 6
366 $ hg blackbox -l 6
367 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags
367 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags
368 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing 24 bytes to cache/hgtagsfnodes1
368 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing 24 bytes to cache/hgtagsfnodes1
369 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> 3/4 cache hits/lookups in * seconds (glob)
369 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> 3/4 cache hits/lookups in * seconds (glob)
370 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing .hg/cache/tags2-visible with 1 tags
370 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing .hg/cache/tags2-visible with 1 tags
371 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags exited 0 after * seconds (glob)
371 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags exited 0 after * seconds (glob)
372 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> blackbox -l 6
372 1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> blackbox -l 6
373
373
374 On junk data + missing cache entries, hg also overwrites the junk.
374 On junk data + missing cache entries, hg also overwrites the junk.
375
375
376 $ rm -f .hg/cache/tags2-visible
376 $ rm -f .hg/cache/tags2-visible
377 >>> import os
377 >>> import os
378 >>> with open(".hg/cache/hgtagsfnodes1", "ab+") as fp:
378 >>> with open(".hg/cache/hgtagsfnodes1", "ab+") as fp:
379 ... fp.seek(-10, os.SEEK_END) and None
379 ... fp.seek(-10, os.SEEK_END) and None
380 ... fp.truncate() and None
380 ... fp.truncate() and None
381
381
382 $ hg debugtagscache | tail -2
382 $ hg debugtagscache | tail -2
383 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
383 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
384 5 8dbfe60eff306a54259cfe007db9e330e7ecf866 missing/invalid
384 5 8dbfe60eff306a54259cfe007db9e330e7ecf866 missing/invalid
385 $ hg tags
385 $ hg tags
386 tip 5:8dbfe60eff30
386 tip 5:8dbfe60eff30
387 bar 1:78391a272241
387 bar 1:78391a272241
388 $ hg debugtagscache | tail -2
388 $ hg debugtagscache | tail -2
389 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
389 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
390 5 8dbfe60eff306a54259cfe007db9e330e7ecf866 0c04f2a8af31de17fab7422878ee5a2dadbc943d
390 5 8dbfe60eff306a54259cfe007db9e330e7ecf866 0c04f2a8af31de17fab7422878ee5a2dadbc943d
391
391
392 #if unix-permissions no-root
392 #if unix-permissions no-root
393 Errors writing to .hgtags fnodes cache are silently ignored
393 Errors writing to .hgtags fnodes cache are silently ignored
394
394
395 $ echo dummy2 > foo
395 $ echo dummy2 > foo
396 $ hg commit -m throwaway2
396 $ hg commit -m throwaway2
397
397
398 $ chmod a-w .hg/cache/hgtagsfnodes1
398 $ chmod a-w .hg/cache/hgtagsfnodes1
399 $ rm -f .hg/cache/tags2-visible
399 $ rm -f .hg/cache/tags2-visible
400
400
401 $ hg tags
401 $ hg tags
402 tip 6:b968051b5cf3
402 tip 6:b968051b5cf3
403 bar 1:78391a272241
403 bar 1:78391a272241
404
404
405 $ hg blackbox -l 6
405 $ hg blackbox -l 6
406 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
406 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
407 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> couldn't write cache/hgtagsfnodes1: [Errno *] * (glob)
407 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> couldn't write cache/hgtagsfnodes1: [Errno *] * (glob)
408 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
408 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
409 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
409 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
410 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
410 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
411 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
411 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
412
412
413 $ chmod a+w .hg/cache/hgtagsfnodes1
413 $ chmod a+w .hg/cache/hgtagsfnodes1
414
414
415 $ rm -f .hg/cache/tags2-visible
415 $ rm -f .hg/cache/tags2-visible
416 $ hg tags
416 $ hg tags
417 tip 6:b968051b5cf3
417 tip 6:b968051b5cf3
418 bar 1:78391a272241
418 bar 1:78391a272241
419
419
420 $ hg blackbox -l 6
420 $ hg blackbox -l 6
421 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
421 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
422 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing 24 bytes to cache/hgtagsfnodes1
422 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing 24 bytes to cache/hgtagsfnodes1
423 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
423 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
424 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
424 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
425 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
425 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
426 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
426 1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
427
427
428 $ f --size .hg/cache/hgtagsfnodes1
428 $ f --size .hg/cache/hgtagsfnodes1
429 .hg/cache/hgtagsfnodes1: size=168
429 .hg/cache/hgtagsfnodes1: size=168
430
430
431 $ hg -q --config extensions.strip= strip -r 6 --no-backup
431 $ hg -q --config extensions.strip= strip -r 6 --no-backup
432 #endif
432 #endif
433
433
434 Stripping doesn't truncate the tags cache until new data is available
434 Stripping doesn't truncate the tags cache until new data is available
435
435
436 $ rm -f .hg/cache/hgtagsfnodes1 .hg/cache/tags2-visible
436 $ rm -f .hg/cache/hgtagsfnodes1 .hg/cache/tags2-visible
437 $ hg tags
437 $ hg tags
438 tip 5:8dbfe60eff30
438 tip 5:8dbfe60eff30
439 bar 1:78391a272241
439 bar 1:78391a272241
440
440
441 $ f --size .hg/cache/hgtagsfnodes1
441 $ f --size .hg/cache/hgtagsfnodes1
442 .hg/cache/hgtagsfnodes1: size=144
442 .hg/cache/hgtagsfnodes1: size=144
443
443
444 $ hg -q --config extensions.strip= strip -r 5 --no-backup
444 $ hg -q --config extensions.strip= strip -r 5 --no-backup
445 $ hg tags
445 $ hg tags
446 tip 4:0c192d7d5e6b
446 tip 4:0c192d7d5e6b
447 bar 1:78391a272241
447 bar 1:78391a272241
448
448
449 $ hg blackbox -l 5
449 $ hg blackbox -l 5
450 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing 24 bytes to cache/hgtagsfnodes1
450 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing 24 bytes to cache/hgtagsfnodes1
451 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> 2/4 cache hits/lookups in * seconds (glob)
451 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> 2/4 cache hits/lookups in * seconds (glob)
452 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing .hg/cache/tags2-visible with 1 tags
452 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing .hg/cache/tags2-visible with 1 tags
453 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> tags exited 0 after * seconds (glob)
453 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> tags exited 0 after * seconds (glob)
454 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> blackbox -l 5
454 1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> blackbox -l 5
455
455
456 $ f --size .hg/cache/hgtagsfnodes1
456 $ f --size .hg/cache/hgtagsfnodes1
457 .hg/cache/hgtagsfnodes1: size=120
457 .hg/cache/hgtagsfnodes1: size=120
458
458
459 $ echo dummy > foo
459 $ echo dummy > foo
460 $ hg commit -m throwaway3
460 $ hg commit -m throwaway3
461
461
462 $ hg tags
462 $ hg tags
463 tip 5:035f65efb448
463 tip 5:035f65efb448
464 bar 1:78391a272241
464 bar 1:78391a272241
465
465
466 $ hg blackbox -l 6
466 $ hg blackbox -l 6
467 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags
467 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags
468 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing 24 bytes to cache/hgtagsfnodes1
468 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing 24 bytes to cache/hgtagsfnodes1
469 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> 3/4 cache hits/lookups in * seconds (glob)
469 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> 3/4 cache hits/lookups in * seconds (glob)
470 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing .hg/cache/tags2-visible with 1 tags
470 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing .hg/cache/tags2-visible with 1 tags
471 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags exited 0 after * seconds (glob)
471 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags exited 0 after * seconds (glob)
472 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> blackbox -l 6
472 1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> blackbox -l 6
473 $ f --size .hg/cache/hgtagsfnodes1
473 $ f --size .hg/cache/hgtagsfnodes1
474 .hg/cache/hgtagsfnodes1: size=144
474 .hg/cache/hgtagsfnodes1: size=144
475
475
476 $ hg -q --config extensions.strip= strip -r 5 --no-backup
476 $ hg -q --config extensions.strip= strip -r 5 --no-backup
477
477
478 Test tag removal:
478 Test tag removal:
479
479
480 $ hg tag --remove bar # rev 5
480 $ hg tag --remove bar # rev 5
481 $ hg tip -vp
481 $ hg tip -vp
482 changeset: 5:5f6e8655b1c7
482 changeset: 5:5f6e8655b1c7
483 tag: tip
483 tag: tip
484 user: test
484 user: test
485 date: Thu Jan 01 00:00:00 1970 +0000
485 date: Thu Jan 01 00:00:00 1970 +0000
486 files: .hgtags
486 files: .hgtags
487 description:
487 description:
488 Removed tag bar
488 Removed tag bar
489
489
490
490
491 diff -r 0c192d7d5e6b -r 5f6e8655b1c7 .hgtags
491 diff -r 0c192d7d5e6b -r 5f6e8655b1c7 .hgtags
492 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
492 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
493 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
493 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
494 @@ -1,1 +1,3 @@
494 @@ -1,1 +1,3 @@
495 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
495 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
496 +78391a272241d70354aa14c874552cad6b51bb42 bar
496 +78391a272241d70354aa14c874552cad6b51bb42 bar
497 +0000000000000000000000000000000000000000 bar
497 +0000000000000000000000000000000000000000 bar
498
498
499 $ hg tags
499 $ hg tags
500 tip 5:5f6e8655b1c7
500 tip 5:5f6e8655b1c7
501 $ hg tags # again, try to expose cache bugs
501 $ hg tags # again, try to expose cache bugs
502 tip 5:5f6e8655b1c7
502 tip 5:5f6e8655b1c7
503
503
504 Remove nonexistent tag:
504 Remove nonexistent tag:
505
505
506 $ hg tag --remove foobar
506 $ hg tag --remove foobar
507 abort: tag 'foobar' does not exist
507 abort: tag 'foobar' does not exist
508 [255]
508 [255]
509 $ hg tip
509 $ hg tip
510 changeset: 5:5f6e8655b1c7
510 changeset: 5:5f6e8655b1c7
511 tag: tip
511 tag: tip
512 user: test
512 user: test
513 date: Thu Jan 01 00:00:00 1970 +0000
513 date: Thu Jan 01 00:00:00 1970 +0000
514 summary: Removed tag bar
514 summary: Removed tag bar
515
515
516
516
517 Undo a tag with rollback:
517 Undo a tag with rollback:
518
518
519 $ hg rollback # destroy rev 5 (restore bar)
519 $ hg rollback # destroy rev 5 (restore bar)
520 repository tip rolled back to revision 4 (undo commit)
520 repository tip rolled back to revision 4 (undo commit)
521 working directory now based on revision 4
521 working directory now based on revision 4
522 $ hg tags
522 $ hg tags
523 tip 4:0c192d7d5e6b
523 tip 4:0c192d7d5e6b
524 bar 1:78391a272241
524 bar 1:78391a272241
525 $ hg tags
525 $ hg tags
526 tip 4:0c192d7d5e6b
526 tip 4:0c192d7d5e6b
527 bar 1:78391a272241
527 bar 1:78391a272241
528
528
529 Test tag rank:
529 Test tag rank:
530
530
531 $ cd ..
531 $ cd ..
532 $ hg init t3
532 $ hg init t3
533 $ cd t3
533 $ cd t3
534 $ echo foo > foo
534 $ echo foo > foo
535 $ hg add foo
535 $ hg add foo
536 $ hg ci -m 'add foo' # rev 0
536 $ hg ci -m 'add foo' # rev 0
537 $ hg tag -f bar # rev 1 bar -> 0
537 $ hg tag -f bar # rev 1 bar -> 0
538 $ hg tag -f bar # rev 2 bar -> 1
538 $ hg tag -f bar # rev 2 bar -> 1
539 $ hg tag -fr 0 bar # rev 3 bar -> 0
539 $ hg tag -fr 0 bar # rev 3 bar -> 0
540 $ hg tag -fr 1 bar # rev 4 bar -> 1
540 $ hg tag -fr 1 bar # rev 4 bar -> 1
541 $ hg tag -fr 0 bar # rev 5 bar -> 0
541 $ hg tag -fr 0 bar # rev 5 bar -> 0
542 $ hg tags
542 $ hg tags
543 tip 5:85f05169d91d
543 tip 5:85f05169d91d
544 bar 0:bbd179dfa0a7
544 bar 0:bbd179dfa0a7
545 $ hg co 3
545 $ hg co 3
546 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
546 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
547 $ echo barbar > foo
547 $ echo barbar > foo
548 $ hg ci -m 'change foo' # rev 6
548 $ hg ci -m 'change foo' # rev 6
549 created new head
549 created new head
550 $ hg tags
550 $ hg tags
551 tip 6:735c3ca72986
551 tip 6:735c3ca72986
552 bar 0:bbd179dfa0a7
552 bar 0:bbd179dfa0a7
553
553
554 Don't allow moving tag without -f:
554 Don't allow moving tag without -f:
555
555
556 $ hg tag -r 3 bar
556 $ hg tag -r 3 bar
557 abort: tag 'bar' already exists (use -f to force)
557 abort: tag 'bar' already exists (use -f to force)
558 [255]
558 [255]
559 $ hg tags
559 $ hg tags
560 tip 6:735c3ca72986
560 tip 6:735c3ca72986
561 bar 0:bbd179dfa0a7
561 bar 0:bbd179dfa0a7
562
562
563 Strip 1: expose an old head:
563 Strip 1: expose an old head:
564
564
565 $ hg --config extensions.mq= strip 5
565 $ hg --config extensions.mq= strip 5
566 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
566 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
567 $ hg tags # partly stale cache
567 $ hg tags # partly stale cache
568 tip 5:735c3ca72986
568 tip 5:735c3ca72986
569 bar 1:78391a272241
569 bar 1:78391a272241
570 $ hg tags # up-to-date cache
570 $ hg tags # up-to-date cache
571 tip 5:735c3ca72986
571 tip 5:735c3ca72986
572 bar 1:78391a272241
572 bar 1:78391a272241
573
573
574 Strip 2: destroy whole branch, no old head exposed
574 Strip 2: destroy whole branch, no old head exposed
575
575
576 $ hg --config extensions.mq= strip 4
576 $ hg --config extensions.mq= strip 4
577 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
577 saved backup bundle to $TESTTMP/t3/.hg/strip-backup/*-backup.hg (glob)
578 $ hg tags # partly stale
578 $ hg tags # partly stale
579 tip 4:735c3ca72986
579 tip 4:735c3ca72986
580 bar 0:bbd179dfa0a7
580 bar 0:bbd179dfa0a7
581 $ rm -f .hg/cache/tags2-visible
581 $ rm -f .hg/cache/tags2-visible
582 $ hg tags # cold cache
582 $ hg tags # cold cache
583 tip 4:735c3ca72986
583 tip 4:735c3ca72986
584 bar 0:bbd179dfa0a7
584 bar 0:bbd179dfa0a7
585
585
586 Test tag rank with 3 heads:
586 Test tag rank with 3 heads:
587
587
588 $ cd ..
588 $ cd ..
589 $ hg init t4
589 $ hg init t4
590 $ cd t4
590 $ cd t4
591 $ echo foo > foo
591 $ echo foo > foo
592 $ hg add
592 $ hg add
593 adding foo
593 adding foo
594 $ hg ci -m 'add foo' # rev 0
594 $ hg ci -m 'add foo' # rev 0
595 $ hg tag bar # rev 1 bar -> 0
595 $ hg tag bar # rev 1 bar -> 0
596 $ hg tag -f bar # rev 2 bar -> 1
596 $ hg tag -f bar # rev 2 bar -> 1
597 $ hg up -qC 0
597 $ hg up -qC 0
598 $ hg tag -fr 2 bar # rev 3 bar -> 2
598 $ hg tag -fr 2 bar # rev 3 bar -> 2
599 $ hg tags
599 $ hg tags
600 tip 3:197c21bbbf2c
600 tip 3:197c21bbbf2c
601 bar 2:6fa450212aeb
601 bar 2:6fa450212aeb
602 $ hg up -qC 0
602 $ hg up -qC 0
603 $ hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
603 $ hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
604
604
605 Bar should still point to rev 2:
605 Bar should still point to rev 2:
606
606
607 $ hg tags
607 $ hg tags
608 tip 4:3b4b14ed0202
608 tip 4:3b4b14ed0202
609 bar 2:6fa450212aeb
609 bar 2:6fa450212aeb
610
610
611 Test that removing global/local tags does not get confused when trying
611 Test that removing global/local tags does not get confused when trying
612 to remove a tag of type X which actually only exists as a type Y:
612 to remove a tag of type X which actually only exists as a type Y:
613
613
614 $ cd ..
614 $ cd ..
615 $ hg init t5
615 $ hg init t5
616 $ cd t5
616 $ cd t5
617 $ echo foo > foo
617 $ echo foo > foo
618 $ hg add
618 $ hg add
619 adding foo
619 adding foo
620 $ hg ci -m 'add foo' # rev 0
620 $ hg ci -m 'add foo' # rev 0
621
621
622 $ hg tag -r 0 -l localtag
622 $ hg tag -r 0 -l localtag
623 $ hg tag --remove localtag
623 $ hg tag --remove localtag
624 abort: tag 'localtag' is not a global tag
624 abort: tag 'localtag' is not a global tag
625 [255]
625 [255]
626 $
626 $
627 $ hg tag -r 0 globaltag
627 $ hg tag -r 0 globaltag
628 $ hg tag --remove -l globaltag
628 $ hg tag --remove -l globaltag
629 abort: tag 'globaltag' is not a local tag
629 abort: tag 'globaltag' is not a local tag
630 [255]
630 [255]
631 $ hg tags -v
631 $ hg tags -v
632 tip 1:a0b6fe111088
632 tip 1:a0b6fe111088
633 localtag 0:bbd179dfa0a7 local
633 localtag 0:bbd179dfa0a7 local
634 globaltag 0:bbd179dfa0a7
634 globaltag 0:bbd179dfa0a7
635
635
636 Templated output:
636 Templated output:
637
637
638 (immediate values)
638 (immediate values)
639
639
640 $ hg tags -T '{pad(tag, 9)} {rev}:{node} ({type})\n'
640 $ hg tags -T '{pad(tag, 9)} {rev}:{node} ({type})\n'
641 tip 1:a0b6fe111088c8c29567d3876cc466aa02927cae ()
641 tip 1:a0b6fe111088c8c29567d3876cc466aa02927cae ()
642 localtag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa (local)
642 localtag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa (local)
643 globaltag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa ()
643 globaltag 0:bbd179dfa0a71671c253b3ae0aa1513b60d199fa ()
644
644
645 (ctx/revcache dependent)
645 (ctx/revcache dependent)
646
646
647 $ hg tags -T '{pad(tag, 9)} {rev} {file_adds}\n'
647 $ hg tags -T '{pad(tag, 9)} {rev} {file_adds}\n'
648 tip 1 .hgtags
648 tip 1 .hgtags
649 localtag 0 foo
649 localtag 0 foo
650 globaltag 0 foo
650 globaltag 0 foo
651
651
652 $ hg tags -T '{pad(tag, 9)} {rev}:{node|shortest}\n'
652 $ hg tags -T '{pad(tag, 9)} {rev}:{node|shortest}\n'
653 tip 1:a0b6
653 tip 1:a0b6
654 localtag 0:bbd1
654 localtag 0:bbd1
655 globaltag 0:bbd1
655 globaltag 0:bbd1
656
656
657 Test for issue3911
657 Test for issue3911
658
658
659 $ hg tag -r 0 -l localtag2
659 $ hg tag -r 0 -l localtag2
660 $ hg tag -l --remove localtag2
660 $ hg tag -l --remove localtag2
661 $ hg tags -v
661 $ hg tags -v
662 tip 1:a0b6fe111088
662 tip 1:a0b6fe111088
663 localtag 0:bbd179dfa0a7 local
663 localtag 0:bbd179dfa0a7 local
664 globaltag 0:bbd179dfa0a7
664 globaltag 0:bbd179dfa0a7
665
665
666 $ hg tag -r 1 -f localtag
666 $ hg tag -r 1 -f localtag
667 $ hg tags -v
667 $ hg tags -v
668 tip 2:5c70a037bb37
668 tip 2:5c70a037bb37
669 localtag 1:a0b6fe111088
669 localtag 1:a0b6fe111088
670 globaltag 0:bbd179dfa0a7
670 globaltag 0:bbd179dfa0a7
671
671
672 $ hg tags -v
672 $ hg tags -v
673 tip 2:5c70a037bb37
673 tip 2:5c70a037bb37
674 localtag 1:a0b6fe111088
674 localtag 1:a0b6fe111088
675 globaltag 0:bbd179dfa0a7
675 globaltag 0:bbd179dfa0a7
676
676
677 $ hg tag -r 1 localtag2
677 $ hg tag -r 1 localtag2
678 $ hg tags -v
678 $ hg tags -v
679 tip 3:bbfb8cd42be2
679 tip 3:bbfb8cd42be2
680 localtag2 1:a0b6fe111088
680 localtag2 1:a0b6fe111088
681 localtag 1:a0b6fe111088
681 localtag 1:a0b6fe111088
682 globaltag 0:bbd179dfa0a7
682 globaltag 0:bbd179dfa0a7
683
683
684 $ hg tags -v
684 $ hg tags -v
685 tip 3:bbfb8cd42be2
685 tip 3:bbfb8cd42be2
686 localtag2 1:a0b6fe111088
686 localtag2 1:a0b6fe111088
687 localtag 1:a0b6fe111088
687 localtag 1:a0b6fe111088
688 globaltag 0:bbd179dfa0a7
688 globaltag 0:bbd179dfa0a7
689
689
690 $ cd ..
690 $ cd ..
691
691
692 Create a repository with tags data to test .hgtags fnodes transfer
692 Create a repository with tags data to test .hgtags fnodes transfer
693
693
694 $ hg init tagsserver
694 $ hg init tagsserver
695 $ cd tagsserver
695 $ cd tagsserver
696 $ touch foo
696 $ touch foo
697 $ hg -q commit -A -m initial
697 $ hg -q commit -A -m initial
698 $ hg tag -m 'tag 0.1' 0.1
698 $ hg tag -m 'tag 0.1' 0.1
699 $ echo second > foo
699 $ echo second > foo
700 $ hg commit -m second
700 $ hg commit -m second
701 $ hg tag -m 'tag 0.2' 0.2
701 $ hg tag -m 'tag 0.2' 0.2
702 $ hg tags
702 $ hg tags
703 tip 3:40f0358cb314
703 tip 3:40f0358cb314
704 0.2 2:f63cc8fe54e4
704 0.2 2:f63cc8fe54e4
705 0.1 0:96ee1d7354c4
705 0.1 0:96ee1d7354c4
706 $ cd ..
706 $ cd ..
707
707
708 Cloning should pull down hgtags fnodes mappings and write the cache file
708 Cloning should pull down hgtags fnodes mappings and write the cache file
709
709
710 $ hg clone --pull tagsserver tagsclient
710 $ hg clone --pull tagsserver tagsclient
711 requesting all changes
711 requesting all changes
712 adding changesets
712 adding changesets
713 adding manifests
713 adding manifests
714 adding file changes
714 adding file changes
715 added 4 changesets with 4 changes to 2 files
715 added 4 changesets with 4 changes to 2 files
716 new changesets 96ee1d7354c4:40f0358cb314
716 new changesets 96ee1d7354c4:40f0358cb314
717 updating to branch default
717 updating to branch default
718 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
718 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
719
719
720 Missing tags2* files means the cache wasn't written through the normal mechanism.
720 Missing tags2* files means the cache wasn't written through the normal mechanism.
721
721
722 $ ls tagsclient/.hg/cache
722 $ ls tagsclient/.hg/cache
723 branch2-base
723 branch2-base
724 hgtagsfnodes1
724 hgtagsfnodes1
725 rbc-names-v1
725 rbc-names-v1
726 rbc-revs-v1
726 rbc-revs-v1
727
727
728 Cache should contain the head only, even though other nodes have tags data
728 Cache should contain the head only, even though other nodes have tags data
729
729
730 $ f --size --hexdump tagsclient/.hg/cache/hgtagsfnodes1
730 $ f --size --hexdump tagsclient/.hg/cache/hgtagsfnodes1
731 tagsclient/.hg/cache/hgtagsfnodes1: size=96
731 tagsclient/.hg/cache/hgtagsfnodes1: size=96
732 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
732 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
733 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
733 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
734 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
734 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
735 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
735 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
736 0040: ff ff ff ff ff ff ff ff 40 f0 35 8c 19 e0 a7 d3 |........@.5.....|
736 0040: ff ff ff ff ff ff ff ff 40 f0 35 8c 19 e0 a7 d3 |........@.5.....|
737 0050: 8a 5c 6a 82 4d cf fb a5 87 d0 2f a3 1e 4f 2f 8a |.\j.M...../..O/.|
737 0050: 8a 5c 6a 82 4d cf fb a5 87 d0 2f a3 1e 4f 2f 8a |.\j.M...../..O/.|
738
738
739 Running hg tags should produce tags2* file and not change cache
739 Running hg tags should produce tags2* file and not change cache
740
740
741 $ hg -R tagsclient tags
741 $ hg -R tagsclient tags
742 tip 3:40f0358cb314
742 tip 3:40f0358cb314
743 0.2 2:f63cc8fe54e4
743 0.2 2:f63cc8fe54e4
744 0.1 0:96ee1d7354c4
744 0.1 0:96ee1d7354c4
745
745
746 $ ls tagsclient/.hg/cache
746 $ ls tagsclient/.hg/cache
747 branch2-base
747 branch2-base
748 hgtagsfnodes1
748 hgtagsfnodes1
749 rbc-names-v1
749 rbc-names-v1
750 rbc-revs-v1
750 rbc-revs-v1
751 tags2-visible
751 tags2-visible
752
752
753 $ f --size --hexdump tagsclient/.hg/cache/hgtagsfnodes1
753 $ f --size --hexdump tagsclient/.hg/cache/hgtagsfnodes1
754 tagsclient/.hg/cache/hgtagsfnodes1: size=96
754 tagsclient/.hg/cache/hgtagsfnodes1: size=96
755 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
755 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
756 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
756 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
757 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
757 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
758 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
758 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
759 0040: ff ff ff ff ff ff ff ff 40 f0 35 8c 19 e0 a7 d3 |........@.5.....|
759 0040: ff ff ff ff ff ff ff ff 40 f0 35 8c 19 e0 a7 d3 |........@.5.....|
760 0050: 8a 5c 6a 82 4d cf fb a5 87 d0 2f a3 1e 4f 2f 8a |.\j.M...../..O/.|
760 0050: 8a 5c 6a 82 4d cf fb a5 87 d0 2f a3 1e 4f 2f 8a |.\j.M...../..O/.|
761
761
762 Check that the bundle includes cache data
762 Check that the bundle includes cache data
763
763
764 $ hg -R tagsclient bundle --all ./test-cache-in-bundle-all-rev.hg
764 $ hg -R tagsclient bundle --all ./test-cache-in-bundle-all-rev.hg
765 4 changesets found
765 4 changesets found
766 $ hg debugbundle ./test-cache-in-bundle-all-rev.hg
766 $ hg debugbundle ./test-cache-in-bundle-all-rev.hg
767 Stream params: {Compression: BZ}
767 Stream params: {Compression: BZ}
768 changegroup -- {nbchanges: 4, version: 02} (mandatory: True)
768 changegroup -- {nbchanges: 4, version: 02} (mandatory: True)
769 96ee1d7354c4ad7372047672c36a1f561e3a6a4c
769 96ee1d7354c4ad7372047672c36a1f561e3a6a4c
770 c4dab0c2fd337eb9191f80c3024830a4889a8f34
770 c4dab0c2fd337eb9191f80c3024830a4889a8f34
771 f63cc8fe54e4d326f8d692805d70e092f851ddb1
771 f63cc8fe54e4d326f8d692805d70e092f851ddb1
772 40f0358cb314c824a5929ee527308d90e023bc10
772 40f0358cb314c824a5929ee527308d90e023bc10
773 hgtagsfnodes -- {} (mandatory: True)
773 hgtagsfnodes -- {} (mandatory: True)
774 cache:rev-branch-cache -- {} (mandatory: False)
774 cache:rev-branch-cache -- {} (mandatory: False)
775
775
776 Check that local clone includes cache data
776 Check that local clone includes cache data
777
777
778 $ hg clone tagsclient tags-local-clone
778 $ hg clone tagsclient tags-local-clone
779 updating to branch default
779 updating to branch default
780 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
780 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
781 $ (cd tags-local-clone/.hg/cache/; ls -1 tag*)
781 $ (cd tags-local-clone/.hg/cache/; ls -1 tag*)
782 tags2-visible
782 tags2-visible
783
783
784 Avoid writing logs on trying to delete an already deleted tag
784 Avoid writing logs on trying to delete an already deleted tag
785 $ hg init issue5752
785 $ hg init issue5752
786 $ cd issue5752
786 $ cd issue5752
787 $ echo > a
787 $ echo > a
788 $ hg commit -Am 'add a'
788 $ hg commit -Am 'add a'
789 adding a
789 adding a
790 $ hg tag a
790 $ hg tag a
791 $ hg tags
791 $ hg tags
792 tip 1:bd7ee4f3939b
792 tip 1:bd7ee4f3939b
793 a 0:a8a82d372bb3
793 a 0:a8a82d372bb3
794 $ hg log
794 $ hg log
795 changeset: 1:bd7ee4f3939b
795 changeset: 1:bd7ee4f3939b
796 tag: tip
796 tag: tip
797 user: test
797 user: test
798 date: Thu Jan 01 00:00:00 1970 +0000
798 date: Thu Jan 01 00:00:00 1970 +0000
799 summary: Added tag a for changeset a8a82d372bb3
799 summary: Added tag a for changeset a8a82d372bb3
800
800
801 changeset: 0:a8a82d372bb3
801 changeset: 0:a8a82d372bb3
802 tag: a
802 tag: a
803 user: test
803 user: test
804 date: Thu Jan 01 00:00:00 1970 +0000
804 date: Thu Jan 01 00:00:00 1970 +0000
805 summary: add a
805 summary: add a
806
806
807 $ hg tag --remove a
807 $ hg tag --remove a
808 $ hg log
808 $ hg log
809 changeset: 2:e7feacc7ec9e
809 changeset: 2:e7feacc7ec9e
810 tag: tip
810 tag: tip
811 user: test
811 user: test
812 date: Thu Jan 01 00:00:00 1970 +0000
812 date: Thu Jan 01 00:00:00 1970 +0000
813 summary: Removed tag a
813 summary: Removed tag a
814
814
815 changeset: 1:bd7ee4f3939b
815 changeset: 1:bd7ee4f3939b
816 user: test
816 user: test
817 date: Thu Jan 01 00:00:00 1970 +0000
817 date: Thu Jan 01 00:00:00 1970 +0000
818 summary: Added tag a for changeset a8a82d372bb3
818 summary: Added tag a for changeset a8a82d372bb3
819
819
820 changeset: 0:a8a82d372bb3
820 changeset: 0:a8a82d372bb3
821 user: test
821 user: test
822 date: Thu Jan 01 00:00:00 1970 +0000
822 date: Thu Jan 01 00:00:00 1970 +0000
823 summary: add a
823 summary: add a
824
824
825 $ hg tag --remove a
825 $ hg tag --remove a
826 abort: tag 'a' is already removed
826 abort: tag 'a' is already removed
827 [255]
827 [255]
828 $ hg log
828 $ hg log
829 changeset: 2:e7feacc7ec9e
829 changeset: 2:e7feacc7ec9e
830 tag: tip
830 tag: tip
831 user: test
831 user: test
832 date: Thu Jan 01 00:00:00 1970 +0000
832 date: Thu Jan 01 00:00:00 1970 +0000
833 summary: Removed tag a
833 summary: Removed tag a
834
834
835 changeset: 1:bd7ee4f3939b
835 changeset: 1:bd7ee4f3939b
836 user: test
836 user: test
837 date: Thu Jan 01 00:00:00 1970 +0000
837 date: Thu Jan 01 00:00:00 1970 +0000
838 summary: Added tag a for changeset a8a82d372bb3
838 summary: Added tag a for changeset a8a82d372bb3
839
839
840 changeset: 0:a8a82d372bb3
840 changeset: 0:a8a82d372bb3
841 user: test
841 user: test
842 date: Thu Jan 01 00:00:00 1970 +0000
842 date: Thu Jan 01 00:00:00 1970 +0000
843 summary: add a
843 summary: add a
844
844
845 $ cat .hgtags
845 $ cat .hgtags
846 a8a82d372bb35b42ff736e74f07c23bcd99c371f a
846 a8a82d372bb35b42ff736e74f07c23bcd99c371f a
847 a8a82d372bb35b42ff736e74f07c23bcd99c371f a
847 a8a82d372bb35b42ff736e74f07c23bcd99c371f a
848 0000000000000000000000000000000000000000 a
848 0000000000000000000000000000000000000000 a
General Comments 0
You need to be logged in to leave comments. Login now