##// END OF EJS Templates
tags: don't crash if unable to write tag cache...
Greg Ward -
r9366:9ff178e7 default
parent child Browse files
Show More
@@ -1,338 +1,341 b''
1 1 # tags.py - read tag info from local repository
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2009 Greg Ward <greg@gerg.ca>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 # Currently this module only deals with reading and caching tags.
10 10 # Eventually, it could take care of updating (adding/removing/moving)
11 11 # tags too.
12 12
13 13 import os
14 14 from node import nullid, bin, hex, short
15 15 from i18n import _
16 16 import encoding
17 17 import error
18 18
19 19 def _debugalways(ui, *msg):
20 20 ui.write(*msg)
21 21
22 22 def _debugconditional(ui, *msg):
23 23 ui.debug(*msg)
24 24
25 25 def _debugnever(ui, *msg):
26 26 pass
27 27
28 28 _debug = _debugalways
29 29 _debug = _debugnever
30 30
31 31 def findglobaltags1(ui, repo, alltags, tagtypes):
32 32 '''Find global tags in repo by reading .hgtags from every head that
33 33 has a distinct version of it. Updates the dicts alltags, tagtypes
34 34 in place: alltags maps tag name to (node, hist) pair (see _readtags()
35 35 below), and tagtypes maps tag name to tag type ('global' in this
36 36 case).'''
37 37
38 38 seen = set()
39 39 fctx = None
40 40 ctxs = [] # list of filectx
41 41 for node in repo.heads():
42 42 try:
43 43 fnode = repo[node].filenode('.hgtags')
44 44 except error.LookupError:
45 45 continue
46 46 if fnode not in seen:
47 47 seen.add(fnode)
48 48 if not fctx:
49 49 fctx = repo.filectx('.hgtags', fileid=fnode)
50 50 else:
51 51 fctx = fctx.filectx(fnode)
52 52 ctxs.append(fctx)
53 53
54 54 # read the tags file from each head, ending with the tip
55 55 for fctx in reversed(ctxs):
56 56 filetags = _readtags(
57 57 ui, repo, fctx.data().splitlines(), fctx)
58 58 _updatetags(filetags, "global", alltags, tagtypes)
59 59
60 60 def findglobaltags2(ui, repo, alltags, tagtypes):
61 61 '''Same as findglobaltags1(), but with caching.'''
62 62 # This is so we can be lazy and assume alltags contains only global
63 63 # tags when we pass it to _writetagcache().
64 64 assert len(alltags) == len(tagtypes) == 0, \
65 65 "findglobaltags() should be called first"
66 66
67 67 (heads, tagfnode, cachetags, shouldwrite) = _readtagcache(ui, repo)
68 68 if cachetags is not None:
69 69 assert not shouldwrite
70 70 # XXX is this really 100% correct? are there oddball special
71 71 # cases where a global tag should outrank a local tag but won't,
72 72 # because cachetags does not contain rank info?
73 73 _updatetags(cachetags, 'global', alltags, tagtypes)
74 74 return
75 75
76 76 _debug(ui, "reading tags from %d head(s): %s\n"
77 77 % (len(heads), map(short, reversed(heads))))
78 78 seen = set() # set of fnode
79 79 fctx = None
80 80 for head in reversed(heads): # oldest to newest
81 81 assert head in repo.changelog.nodemap, \
82 82 "tag cache returned bogus head %s" % short(head)
83 83
84 84 fnode = tagfnode.get(head)
85 85 if fnode and fnode not in seen:
86 86 seen.add(fnode)
87 87 if not fctx:
88 88 fctx = repo.filectx('.hgtags', fileid=fnode)
89 89 else:
90 90 fctx = fctx.filectx(fnode)
91 91
92 92 filetags = _readtags(ui, repo, fctx.data().splitlines(), fctx)
93 93 _updatetags(filetags, 'global', alltags, tagtypes)
94 94
95 95 # and update the cache (if necessary)
96 96 if shouldwrite:
97 97 _writetagcache(ui, repo, heads, tagfnode, alltags)
98 98
99 99 # Set this to findglobaltags1 to disable tag caching.
100 100 findglobaltags = findglobaltags2
101 101
102 102 def readlocaltags(ui, repo, alltags, tagtypes):
103 103 '''Read local tags in repo. Update alltags and tagtypes.'''
104 104 try:
105 105 # localtags is in the local encoding; re-encode to UTF-8 on
106 106 # input for consistency with the rest of this module.
107 107 data = repo.opener("localtags").read()
108 108 filetags = _readtags(
109 109 ui, repo, data.splitlines(), "localtags",
110 110 recode=encoding.fromlocal)
111 111 _updatetags(filetags, "local", alltags, tagtypes)
112 112 except IOError:
113 113 pass
114 114
115 115 def _readtags(ui, repo, lines, fn, recode=None):
116 116 '''Read tag definitions from a file (or any source of lines).
117 117 Return a mapping from tag name to (node, hist): node is the node id
118 118 from the last line read for that name, and hist is the list of node
119 119 ids previously associated with it (in file order). All node ids are
120 120 binary, not hex.'''
121 121
122 122 filetags = {} # map tag name to (node, hist)
123 123 count = 0
124 124
125 125 def warn(msg):
126 126 ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
127 127
128 128 for line in lines:
129 129 count += 1
130 130 if not line:
131 131 continue
132 132 try:
133 133 (nodehex, name) = line.split(" ", 1)
134 134 except ValueError:
135 135 warn(_("cannot parse entry"))
136 136 continue
137 137 name = name.strip()
138 138 if recode:
139 139 name = recode(name)
140 140 try:
141 141 nodebin = bin(nodehex)
142 142 except TypeError:
143 143 warn(_("node '%s' is not well formed") % nodehex)
144 144 continue
145 145 if nodebin not in repo.changelog.nodemap:
146 146 # silently ignore as pull -r might cause this
147 147 continue
148 148
149 149 # update filetags
150 150 hist = []
151 151 if name in filetags:
152 152 n, hist = filetags[name]
153 153 hist.append(n)
154 154 filetags[name] = (nodebin, hist)
155 155 return filetags
156 156
157 157 def _updatetags(filetags, tagtype, alltags, tagtypes):
158 158 '''Incorporate the tag info read from one file into the two
159 159 dictionaries, alltags and tagtypes, that contain all tag
160 160 info (global across all heads plus local).'''
161 161
162 162 for name, nodehist in filetags.iteritems():
163 163 if name not in alltags:
164 164 alltags[name] = nodehist
165 165 tagtypes[name] = tagtype
166 166 continue
167 167
168 168 # we prefer alltags[name] if:
169 169 # it supercedes us OR
170 170 # mutual supercedes and it has a higher rank
171 171 # otherwise we win because we're tip-most
172 172 anode, ahist = nodehist
173 173 bnode, bhist = alltags[name]
174 174 if (bnode != anode and anode in bhist and
175 175 (bnode not in ahist or len(bhist) > len(ahist))):
176 176 anode = bnode
177 177 ahist.extend([n for n in bhist if n not in ahist])
178 178 alltags[name] = anode, ahist
179 179 tagtypes[name] = tagtype
180 180
181 181
182 182 # The tag cache only stores info about heads, not the tag contents
183 183 # from each head. I.e. it doesn't try to squeeze out the maximum
184 184 # performance, but is simpler has a better chance of actually
185 185 # working correctly. And this gives the biggest performance win: it
186 186 # avoids looking up .hgtags in the manifest for every head, and it
187 187 # can avoid calling heads() at all if there have been no changes to
188 188 # the repo.
189 189
190 190 def _readtagcache(ui, repo):
191 191 '''Read the tag cache and return a tuple (heads, fnodes, cachetags,
192 192 shouldwrite). If the cache is completely up-to-date, cachetags is a
193 193 dict of the form returned by _readtags(); otherwise, it is None and
194 194 heads and fnodes are set. In that case, heads is the list of all
195 195 heads currently in the repository (ordered from tip to oldest) and
196 196 fnodes is a mapping from head to .hgtags filenode. If those two are
197 197 set, caller is responsible for reading tag info from each head.'''
198 198
199 199 try:
200 200 cachefile = repo.opener('tags.cache', 'r')
201 201 _debug(ui, 'reading tag cache from %s\n' % cachefile.name)
202 202 except IOError:
203 203 cachefile = None
204 204
205 205 # The cache file consists of lines like
206 206 # <headrev> <headnode> [<tagnode>]
207 207 # where <headrev> and <headnode> redundantly identify a repository
208 208 # head from the time the cache was written, and <tagnode> is the
209 209 # filenode of .hgtags on that head. Heads with no .hgtags file will
210 210 # have no <tagnode>. The cache is ordered from tip to oldest (which
211 211 # is part of why <headrev> is there: a quick visual check is all
212 212 # that's required to ensure correct order).
213 213 #
214 214 # This information is enough to let us avoid the most expensive part
215 215 # of finding global tags, which is looking up <tagnode> in the
216 216 # manifest for each head.
217 217 cacherevs = [] # list of headrev
218 218 cacheheads = [] # list of headnode
219 219 cachefnode = {} # map headnode to filenode
220 220 if cachefile:
221 221 for line in cachefile:
222 222 if line == "\n":
223 223 break
224 224 line = line.rstrip().split()
225 225 cacherevs.append(int(line[0]))
226 226 headnode = bin(line[1])
227 227 cacheheads.append(headnode)
228 228 if len(line) == 3:
229 229 fnode = bin(line[2])
230 230 cachefnode[headnode] = fnode
231 231
232 232 tipnode = repo.changelog.tip()
233 233 tiprev = len(repo.changelog) - 1
234 234
235 235 # Case 1 (common): tip is the same, so nothing has changed.
236 236 # (Unchanged tip trivially means no changesets have been added.
237 237 # But, thanks to localrepository.destroyed(), it also means none
238 238 # have been destroyed by strip or rollback.)
239 239 if cacheheads and cacheheads[0] == tipnode and cacherevs[0] == tiprev:
240 240 _debug(ui, "tag cache: tip unchanged\n")
241 241 tags = _readtags(ui, repo, cachefile, cachefile.name)
242 242 cachefile.close()
243 243 return (None, None, tags, False)
244 244 if cachefile:
245 245 cachefile.close() # ignore rest of file
246 246
247 247 repoheads = repo.heads()
248 248 # Case 2 (uncommon): empty repo; get out quickly and don't bother
249 249 # writing an empty cache.
250 250 if repoheads == [nullid]:
251 251 return ([], {}, {}, False)
252 252
253 253 # Case 3 (uncommon): cache file missing or empty.
254 254 if not cacheheads:
255 255 _debug(ui, 'tag cache: cache file missing or empty\n')
256 256
257 257 # Case 4 (uncommon): tip rev decreased. This should only happen
258 258 # when we're called from localrepository.destroyed(). Refresh the
259 259 # cache so future invocations will not see disappeared heads in the
260 260 # cache.
261 261 elif cacheheads and tiprev < cacherevs[0]:
262 262 _debug(ui,
263 263 'tag cache: tip rev decremented (from %d to %d), '
264 264 'so we must be destroying nodes\n'
265 265 % (cacherevs[0], tiprev))
266 266
267 267 # Case 5 (common): tip has changed, so we've added/replaced heads.
268 268 else:
269 269 _debug(ui,
270 270 'tag cache: tip has changed (%d:%s); must find new heads\n'
271 271 % (tiprev, short(tipnode)))
272 272
273 273 # Luckily, the code to handle cases 3, 4, 5 is the same. So the
274 274 # above if/elif/else can disappear once we're confident this thing
275 275 # actually works and we don't need the debug output.
276 276
277 277 # N.B. in case 4 (nodes destroyed), "new head" really means "newly
278 278 # exposed".
279 279 newheads = [head
280 280 for head in repoheads
281 281 if head not in set(cacheheads)]
282 282 _debug(ui, 'tag cache: found %d head(s) not in cache: %s\n'
283 283 % (len(newheads), map(short, newheads)))
284 284
285 285 # Now we have to lookup the .hgtags filenode for every new head.
286 286 # This is the most expensive part of finding tags, so performance
287 287 # depends primarily on the size of newheads. Worst case: no cache
288 288 # file, so newheads == repoheads.
289 289 for head in newheads:
290 290 cctx = repo[head]
291 291 try:
292 292 fnode = cctx.filenode('.hgtags')
293 293 cachefnode[head] = fnode
294 294 except error.LookupError:
295 295 # no .hgtags file on this head
296 296 pass
297 297
298 298 # Caller has to iterate over all heads, but can use the filenodes in
299 299 # cachefnode to get to each .hgtags revision quickly.
300 300 return (repoheads, cachefnode, None, True)
301 301
302 302 def _writetagcache(ui, repo, heads, tagfnode, cachetags):
303 303
304 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
304 try:
305 cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
306 except (OSError, IOError):
307 return
305 308 _debug(ui, 'writing cache file %s\n' % cachefile.name)
306 309
307 310 realheads = repo.heads() # for sanity checks below
308 311 for head in heads:
309 312 # temporary sanity checks; these can probably be removed
310 313 # once this code has been in crew for a few weeks
311 314 assert head in repo.changelog.nodemap, \
312 315 'trying to write non-existent node %s to tag cache' % short(head)
313 316 assert head in realheads, \
314 317 'trying to write non-head %s to tag cache' % short(head)
315 318 assert head != nullid, \
316 319 'trying to write nullid to tag cache'
317 320
318 321 # This can't fail because of the first assert above. When/if we
319 322 # remove that assert, we might want to catch LookupError here
320 323 # and downgrade it to a warning.
321 324 rev = repo.changelog.rev(head)
322 325
323 326 fnode = tagfnode.get(head)
324 327 if fnode:
325 328 cachefile.write('%d %s %s\n' % (rev, hex(head), hex(fnode)))
326 329 else:
327 330 cachefile.write('%d %s\n' % (rev, hex(head)))
328 331
329 332 # Tag names in the cache are in UTF-8 -- which is the whole reason
330 333 # we keep them in UTF-8 throughout this module. If we converted
331 334 # them local encoding on input, we would lose info writing them to
332 335 # the cache.
333 336 cachefile.write('\n')
334 337 for (name, (node, hist)) in cachetags.iteritems():
335 338 cachefile.write("%s %s\n" % (hex(node), name))
336 339
337 340 cachefile.rename()
338 341 cachefile.close()
@@ -1,206 +1,214 b''
1 1 #!/bin/sh
2 2
3 3 cacheexists() {
4 4 [ -f .hg/tags.cache ] && echo "tag cache exists" || echo "no tag cache"
5 5 }
6 6
7 7 # XXX need to test that the tag cache works when we strip an old head
8 8 # and add a new one rooted off non-tip: i.e. node and rev of tip are the
9 9 # same, but stuff has changed behind tip.
10 10
11 11 echo "% setup"
12 12 mkdir t
13 13 cd t
14 14 hg init
15 15 cacheexists
16 16 hg id
17 17 cacheexists
18 18 echo a > a
19 19 hg add a
20 20 hg commit -m "test"
21 21 hg co
22 22 hg identify
23 23 cacheexists
24 24
25 25 echo "% create local tag with long name"
26 26 T=`hg identify --debug --id`
27 27 hg tag -l "This is a local tag with a really long name!"
28 28 hg tags
29 29 rm .hg/localtags
30 30
31 31 echo "% create a tag behind hg's back"
32 32 echo "$T first" > .hgtags
33 33 cat .hgtags
34 34 hg add .hgtags
35 35 hg commit -m "add tags"
36 36 hg tags
37 37 hg identify
38 38
39 39 # repeat with cold tag cache
40 echo "% identify with cold cache"
40 41 rm -f .hg/tags.cache
41 42 hg identify
42 43
44 # and again, but now unable to write tag cache
45 echo "% identify with unwritable cache"
46 rm -f .hg/tags.cache
47 chmod 555 .hg
48 hg identify
49 chmod 755 .hg
50
43 51 echo "% create a branch"
44 52 echo bb > a
45 53 hg status
46 54 hg identify
47 55 hg co first
48 56 hg id
49 57 hg -v id
50 58 hg status
51 59 echo 1 > b
52 60 hg add b
53 61 hg commit -m "branch"
54 62 hg id
55 63
56 64 echo "% merge the two heads"
57 65 hg merge 1
58 66 hg id
59 67 hg status
60 68
61 69 hg commit -m "merge"
62 70
63 71 echo "% create fake head, make sure tag not visible afterwards"
64 72 cp .hgtags tags
65 73 hg tag last
66 74 hg rm .hgtags
67 75 hg commit -m "remove"
68 76
69 77 mv tags .hgtags
70 78 hg add .hgtags
71 79 hg commit -m "readd"
72 80
73 81 hg tags
74 82
75 83 echo "% add invalid tags"
76 84 echo "spam" >> .hgtags
77 85 echo >> .hgtags
78 86 echo "foo bar" >> .hgtags
79 87 echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags
80 88 echo "committing .hgtags:"
81 89 cat .hgtags
82 90 hg commit -m "tags"
83 91
84 92 echo "% report tag parse error on other head"
85 93 hg up 3
86 94 echo 'x y' >> .hgtags
87 95 hg commit -m "head"
88 96
89 97 hg tags
90 98 hg tip
91 99
92 100 echo "% test tag precedence rules"
93 101 cd ..
94 102 hg init t2
95 103 cd t2
96 104 echo foo > foo
97 105 hg add foo
98 106 hg ci -m 'add foo' # rev 0
99 107 hg tag bar # rev 1
100 108 echo >> foo
101 109 hg ci -m 'change foo 1' # rev 2
102 110 hg up -C 1
103 111 hg tag -r 1 -f bar # rev 3
104 112 hg up -C 1
105 113 echo >> foo
106 114 hg ci -m 'change foo 2' # rev 4
107 115 hg tags
108 116 hg tags # repeat in case of cache effects
109 117
110 118 dumptags() {
111 119 rev=$1
112 120 echo "rev $rev: .hgtags:"
113 121 hg cat -r$rev .hgtags
114 122 }
115 123
116 124 echo "% detailed dump of tag info"
117 125 echo "heads:"
118 126 hg heads -q # expect 4, 3, 2
119 127 dumptags 2
120 128 dumptags 3
121 129 dumptags 4
122 130 echo ".hg/tags.cache:"
123 131 [ -f .hg/tags.cache ] && cat .hg/tags.cache || echo "no such file"
124 132
125 133 echo "% test tag removal"
126 134 hg tag --remove bar # rev 5
127 135 hg tip -vp
128 136 hg tags
129 137 hg tags # again, try to expose cache bugs
130 138
131 139 echo '% remove nonexistent tag'
132 140 hg tag --remove foobar
133 141 hg tip
134 142
135 143 echo "% rollback undoes tag operation"
136 144 hg rollback # destroy rev 5 (restore bar)
137 145 hg tags
138 146 hg tags
139 147
140 148 echo "% test tag rank"
141 149 cd ..
142 150 hg init t3
143 151 cd t3
144 152 echo foo > foo
145 153 hg add foo
146 154 hg ci -m 'add foo' # rev 0
147 155 hg tag -f bar # rev 1 bar -> 0
148 156 hg tag -f bar # rev 2 bar -> 1
149 157 hg tag -fr 0 bar # rev 3 bar -> 0
150 158 hg tag -fr 1 bar # rev 4 bar -> 1
151 159 hg tag -fr 0 bar # rev 5 bar -> 0
152 160 hg tags
153 161 hg co 3
154 162 echo barbar > foo
155 163 hg ci -m 'change foo' # rev 6
156 164 hg tags
157 165
158 166 echo "% don't allow moving tag without -f"
159 167 hg tag -r 3 bar
160 168 hg tags
161 169
162 170 echo "% strip 1: expose an old head"
163 171 hg --config extensions.mq= strip 5 > /dev/null 2>&1
164 172 hg tags # partly stale cache
165 173 hg tags # up-to-date cache
166 174 echo "% strip 2: destroy whole branch, no old head exposed"
167 175 hg --config extensions.mq= strip 4 > /dev/null 2>&1
168 176 hg tags # partly stale
169 177 rm -f .hg/tags.cache
170 178 hg tags # cold cache
171 179
172 180 echo "% test tag rank with 3 heads"
173 181 cd ..
174 182 hg init t4
175 183 cd t4
176 184 echo foo > foo
177 185 hg add
178 186 hg ci -m 'add foo' # rev 0
179 187 hg tag bar # rev 1 bar -> 0
180 188 hg tag -f bar # rev 2 bar -> 1
181 189 hg up -qC 0
182 190 hg tag -fr 2 bar # rev 3 bar -> 2
183 191 hg tags
184 192 hg up -qC 0
185 193 hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
186 194 echo "% bar should still point to rev 2"
187 195 hg tags
188 196
189 197
190 198 echo "% remove local as global and global as local"
191 199 # test that removing global/local tags does not get confused when trying
192 200 # to remove a tag of type X which actually only exists as a type Y
193 201 cd ..
194 202 hg init t5
195 203 cd t5
196 204 echo foo > foo
197 205 hg add
198 206 hg ci -m 'add foo' # rev 0
199 207
200 208 hg tag -r 0 -l localtag
201 209 hg tag --remove localtag
202 210
203 211 hg tag -r 0 globaltag
204 212 hg tag --remove -l globaltag
205 213 hg tags -v
206 214 exit 0
@@ -1,150 +1,153 b''
1 1 % setup
2 2 no tag cache
3 3 000000000000 tip
4 4 no tag cache
5 5 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
6 6 acb14030fe0a tip
7 7 tag cache exists
8 8 % create local tag with long name
9 9 tip 0:acb14030fe0a
10 10 This is a local tag with a really long name! 0:acb14030fe0a
11 11 % create a tag behind hg's back
12 12 acb14030fe0a21b60322c440ad2d20cf7685a376 first
13 13 tip 1:b9154636be93
14 14 first 0:acb14030fe0a
15 15 b9154636be93 tip
16 % identify with cold cache
17 b9154636be93 tip
18 % identify with unwritable cache
16 19 b9154636be93 tip
17 20 % create a branch
18 21 M a
19 22 b9154636be93+ tip
20 23 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 24 acb14030fe0a+ first
22 25 acb14030fe0a+ first
23 26 M a
24 27 created new head
25 28 c8edf04160c7 tip
26 29 % merge the two heads
27 30 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 31 (branch merge, don't forget to commit)
29 32 c8edf04160c7+b9154636be93+ tip
30 33 M .hgtags
31 34 % create fake head, make sure tag not visible afterwards
32 35 tip 6:35ff301afafe
33 36 first 0:acb14030fe0a
34 37 % add invalid tags
35 38 committing .hgtags:
36 39 acb14030fe0a21b60322c440ad2d20cf7685a376 first
37 40 spam
38 41
39 42 foo bar
40 43 % report tag parse error on other head
41 44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 45 created new head
43 46 .hgtags@75d9f02dfe28, line 2: cannot parse entry
44 47 .hgtags@75d9f02dfe28, line 4: node 'foo' is not well formed
45 48 .hgtags@c4be69a18c11, line 2: node 'x' is not well formed
46 49 tip 8:c4be69a18c11
47 50 first 0:acb14030fe0a
48 51 changeset: 8:c4be69a18c11
49 52 tag: tip
50 53 parent: 3:ac5e980c4dc0
51 54 user: test
52 55 date: Thu Jan 01 00:00:00 1970 +0000
53 56 summary: head
54 57
55 58 % test tag precedence rules
56 59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 61 created new head
59 62 tip 4:0c192d7d5e6b
60 63 bar 1:78391a272241
61 64 tip 4:0c192d7d5e6b
62 65 bar 1:78391a272241
63 66 % detailed dump of tag info
64 67 heads:
65 68 4:0c192d7d5e6b
66 69 3:6fa450212aeb
67 70 2:7a94127795a3
68 71 rev 2: .hgtags:
69 72 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
70 73 rev 3: .hgtags:
71 74 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
72 75 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
73 76 78391a272241d70354aa14c874552cad6b51bb42 bar
74 77 rev 4: .hgtags:
75 78 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
76 79 .hg/tags.cache:
77 80 4 0c192d7d5e6b78a714de54a2e9627952a877e25a 0c04f2a8af31de17fab7422878ee5a2dadbc943d
78 81 3 6fa450212aeb2a21ed616a54aea39a4a27894cd7 7d3b718c964ef37b89e550ebdafd5789e76ce1b0
79 82 2 7a94127795a33c10a370c93f731fd9fea0b79af6 0c04f2a8af31de17fab7422878ee5a2dadbc943d
80 83
81 84 78391a272241d70354aa14c874552cad6b51bb42 bar
82 85 % test tag removal
83 86 changeset: 5:5f6e8655b1c7
84 87 tag: tip
85 88 user: test
86 89 date: Thu Jan 01 00:00:00 1970 +0000
87 90 files: .hgtags
88 91 description:
89 92 Removed tag bar
90 93
91 94
92 95 diff -r 0c192d7d5e6b -r 5f6e8655b1c7 .hgtags
93 96 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
94 97 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
95 98 @@ -1,1 +1,3 @@
96 99 bbd179dfa0a71671c253b3ae0aa1513b60d199fa bar
97 100 +78391a272241d70354aa14c874552cad6b51bb42 bar
98 101 +0000000000000000000000000000000000000000 bar
99 102
100 103 tip 5:5f6e8655b1c7
101 104 tip 5:5f6e8655b1c7
102 105 % remove nonexistent tag
103 106 abort: tag 'foobar' does not exist
104 107 changeset: 5:5f6e8655b1c7
105 108 tag: tip
106 109 user: test
107 110 date: Thu Jan 01 00:00:00 1970 +0000
108 111 summary: Removed tag bar
109 112
110 113 % rollback undoes tag operation
111 114 rolling back last transaction
112 115 tip 4:0c192d7d5e6b
113 116 bar 1:78391a272241
114 117 tip 4:0c192d7d5e6b
115 118 bar 1:78391a272241
116 119 % test tag rank
117 120 tip 5:85f05169d91d
118 121 bar 0:bbd179dfa0a7
119 122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 123 created new head
121 124 tip 6:735c3ca72986
122 125 bar 0:bbd179dfa0a7
123 126 % don't allow moving tag without -f
124 127 abort: tag 'bar' already exists (use -f to force)
125 128 tip 6:735c3ca72986
126 129 bar 0:bbd179dfa0a7
127 130 % strip 1: expose an old head
128 131 tip 5:735c3ca72986
129 132 bar 1:78391a272241
130 133 tip 5:735c3ca72986
131 134 bar 1:78391a272241
132 135 % strip 2: destroy whole branch, no old head exposed
133 136 tip 4:735c3ca72986
134 137 bar 0:bbd179dfa0a7
135 138 tip 4:735c3ca72986
136 139 bar 0:bbd179dfa0a7
137 140 % test tag rank with 3 heads
138 141 adding foo
139 142 tip 3:197c21bbbf2c
140 143 bar 2:6fa450212aeb
141 144 % bar should still point to rev 2
142 145 tip 4:3b4b14ed0202
143 146 bar 2:6fa450212aeb
144 147 % remove local as global and global as local
145 148 adding foo
146 149 abort: tag 'localtag' is not a global tag
147 150 abort: tag 'globaltag' is not a local tag
148 151 tip 1:a0b6fe111088
149 152 localtag 0:bbd179dfa0a7 local
150 153 globaltag 0:bbd179dfa0a7
General Comments 0
You need to be logged in to leave comments. Login now