##// END OF EJS Templates
convert: properly pass null ids through .hgtags (issue4678)...
Matt Mackall -
r25305:884ef09c stable
parent child Browse files
Show More
@@ -1,493 +1,496 b''
1 1 # hg.py - hg backend for convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # Notes for hg->hg conversion:
9 9 #
10 10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 11 # of commit messages, but new versions do. Changesets created by
12 12 # those older versions, then converted, may thus have different
13 13 # hashes for changesets that are otherwise identical.
14 14 #
15 15 # * Using "--config convert.hg.saverev=true" will make the source
16 16 # identifier to be stored in the converted revision. This will cause
17 17 # the converted revision to have a different identity than the
18 18 # source.
19 19
20 20
21 21 import os, time, cStringIO
22 22 from mercurial.i18n import _
23 23 from mercurial.node import bin, hex, nullid
24 24 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
25 25
26 26 from common import NoRepo, commit, converter_source, converter_sink
27 27
28 28 import re
29 29 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
30 30
31 31 class mercurial_sink(converter_sink):
32 32 def __init__(self, ui, path):
33 33 converter_sink.__init__(self, ui, path)
34 34 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
35 35 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
36 36 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
37 37 self.lastbranch = None
38 38 if os.path.isdir(path) and len(os.listdir(path)) > 0:
39 39 try:
40 40 self.repo = hg.repository(self.ui, path)
41 41 if not self.repo.local():
42 42 raise NoRepo(_('%s is not a local Mercurial repository')
43 43 % path)
44 44 except error.RepoError, err:
45 45 ui.traceback()
46 46 raise NoRepo(err.args[0])
47 47 else:
48 48 try:
49 49 ui.status(_('initializing destination %s repository\n') % path)
50 50 self.repo = hg.repository(self.ui, path, create=True)
51 51 if not self.repo.local():
52 52 raise NoRepo(_('%s is not a local Mercurial repository')
53 53 % path)
54 54 self.created.append(path)
55 55 except error.RepoError:
56 56 ui.traceback()
57 57 raise NoRepo(_("could not create hg repository %s as sink")
58 58 % path)
59 59 self.lock = None
60 60 self.wlock = None
61 61 self.filemapmode = False
62 62
63 63 def before(self):
64 64 self.ui.debug('run hg sink pre-conversion action\n')
65 65 self.wlock = self.repo.wlock()
66 66 self.lock = self.repo.lock()
67 67
68 68 def after(self):
69 69 self.ui.debug('run hg sink post-conversion action\n')
70 70 if self.lock:
71 71 self.lock.release()
72 72 if self.wlock:
73 73 self.wlock.release()
74 74
75 75 def revmapfile(self):
76 76 return self.repo.join("shamap")
77 77
78 78 def authorfile(self):
79 79 return self.repo.join("authormap")
80 80
81 81 def setbranch(self, branch, pbranches):
82 82 if not self.clonebranches:
83 83 return
84 84
85 85 setbranch = (branch != self.lastbranch)
86 86 self.lastbranch = branch
87 87 if not branch:
88 88 branch = 'default'
89 89 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
90 90 if pbranches:
91 91 pbranch = pbranches[0][1]
92 92 else:
93 93 pbranch = 'default'
94 94
95 95 branchpath = os.path.join(self.path, branch)
96 96 if setbranch:
97 97 self.after()
98 98 try:
99 99 self.repo = hg.repository(self.ui, branchpath)
100 100 except Exception:
101 101 self.repo = hg.repository(self.ui, branchpath, create=True)
102 102 self.before()
103 103
104 104 # pbranches may bring revisions from other branches (merge parents)
105 105 # Make sure we have them, or pull them.
106 106 missings = {}
107 107 for b in pbranches:
108 108 try:
109 109 self.repo.lookup(b[0])
110 110 except Exception:
111 111 missings.setdefault(b[1], []).append(b[0])
112 112
113 113 if missings:
114 114 self.after()
115 115 for pbranch, heads in sorted(missings.iteritems()):
116 116 pbranchpath = os.path.join(self.path, pbranch)
117 117 prepo = hg.peer(self.ui, {}, pbranchpath)
118 118 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
119 119 exchange.pull(self.repo, prepo,
120 120 [prepo.lookup(h) for h in heads])
121 121 self.before()
122 122
123 123 def _rewritetags(self, source, revmap, data):
124 124 fp = cStringIO.StringIO()
125 125 for line in data.splitlines():
126 126 s = line.split(' ', 1)
127 127 if len(s) != 2:
128 128 continue
129 129 revid = revmap.get(source.lookuprev(s[0]))
130 130 if not revid:
131 continue
131 if s[0] == hex(nullid):
132 revid = s[0]
133 else:
134 continue
132 135 fp.write('%s %s\n' % (revid, s[1]))
133 136 return fp.getvalue()
134 137
135 138 def putcommit(self, files, copies, parents, commit, source, revmap, full,
136 139 cleanp2):
137 140 files = dict(files)
138 141
139 142 def getfilectx(repo, memctx, f):
140 143 if p2ctx and f in cleanp2 and f not in copies:
141 144 self.ui.debug('reusing %s from p2\n' % f)
142 145 return p2ctx[f]
143 146 try:
144 147 v = files[f]
145 148 except KeyError:
146 149 return None
147 150 data, mode = source.getfile(f, v)
148 151 if data is None:
149 152 return None
150 153 if f == '.hgtags':
151 154 data = self._rewritetags(source, revmap, data)
152 155 return context.memfilectx(self.repo, f, data, 'l' in mode,
153 156 'x' in mode, copies.get(f))
154 157
155 158 pl = []
156 159 for p in parents:
157 160 if p not in pl:
158 161 pl.append(p)
159 162 parents = pl
160 163 nparents = len(parents)
161 164 if self.filemapmode and nparents == 1:
162 165 m1node = self.repo.changelog.read(bin(parents[0]))[0]
163 166 parent = parents[0]
164 167
165 168 if len(parents) < 2:
166 169 parents.append(nullid)
167 170 if len(parents) < 2:
168 171 parents.append(nullid)
169 172 p2 = parents.pop(0)
170 173
171 174 text = commit.desc
172 175
173 176 sha1s = re.findall(sha1re, text)
174 177 for sha1 in sha1s:
175 178 oldrev = source.lookuprev(sha1)
176 179 newrev = revmap.get(oldrev)
177 180 if newrev is not None:
178 181 text = text.replace(sha1, newrev[:len(sha1)])
179 182
180 183 extra = commit.extra.copy()
181 184
182 185 for label in ('source', 'transplant_source', 'rebase_source'):
183 186 node = extra.get(label)
184 187
185 188 if node is None:
186 189 continue
187 190
188 191 # Only transplant stores its reference in binary
189 192 if label == 'transplant_source':
190 193 node = hex(node)
191 194
192 195 newrev = revmap.get(node)
193 196 if newrev is not None:
194 197 if label == 'transplant_source':
195 198 newrev = bin(newrev)
196 199
197 200 extra[label] = newrev
198 201
199 202 if self.branchnames and commit.branch:
200 203 extra['branch'] = commit.branch
201 204 if commit.rev:
202 205 extra['convert_revision'] = commit.rev
203 206
204 207 while parents:
205 208 p1 = p2
206 209 p2 = parents.pop(0)
207 210 p2ctx = None
208 211 if p2 != nullid:
209 212 p2ctx = self.repo[p2]
210 213 fileset = set(files)
211 214 if full:
212 215 fileset.update(self.repo[p1])
213 216 fileset.update(self.repo[p2])
214 217 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
215 218 getfilectx, commit.author, commit.date, extra)
216 219 self.repo.commitctx(ctx)
217 220 text = "(octopus merge fixup)\n"
218 221 p2 = hex(self.repo.changelog.tip())
219 222
220 223 if self.filemapmode and nparents == 1:
221 224 man = self.repo.manifest
222 225 mnode = self.repo.changelog.read(bin(p2))[0]
223 226 closed = 'close' in commit.extra
224 227 if not closed and not man.cmp(m1node, man.revision(mnode)):
225 228 self.ui.status(_("filtering out empty revision\n"))
226 229 self.repo.rollback(force=True)
227 230 return parent
228 231 return p2
229 232
230 233 def puttags(self, tags):
231 234 try:
232 235 parentctx = self.repo[self.tagsbranch]
233 236 tagparent = parentctx.node()
234 237 except error.RepoError:
235 238 parentctx = None
236 239 tagparent = nullid
237 240
238 241 oldlines = set()
239 242 for branch, heads in self.repo.branchmap().iteritems():
240 243 for h in heads:
241 244 if '.hgtags' in self.repo[h]:
242 245 oldlines.update(
243 246 set(self.repo[h]['.hgtags'].data().splitlines(True)))
244 247 oldlines = sorted(list(oldlines))
245 248
246 249 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
247 250 if newlines == oldlines:
248 251 return None, None
249 252
250 253 # if the old and new tags match, then there is nothing to update
251 254 oldtags = set()
252 255 newtags = set()
253 256 for line in oldlines:
254 257 s = line.strip().split(' ', 1)
255 258 if len(s) != 2:
256 259 continue
257 260 oldtags.add(s[1])
258 261 for line in newlines:
259 262 s = line.strip().split(' ', 1)
260 263 if len(s) != 2:
261 264 continue
262 265 if s[1] not in oldtags:
263 266 newtags.add(s[1].strip())
264 267
265 268 if not newtags:
266 269 return None, None
267 270
268 271 data = "".join(newlines)
269 272 def getfilectx(repo, memctx, f):
270 273 return context.memfilectx(repo, f, data, False, False, None)
271 274
272 275 self.ui.status(_("updating tags\n"))
273 276 date = "%s 0" % int(time.mktime(time.gmtime()))
274 277 extra = {'branch': self.tagsbranch}
275 278 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
276 279 [".hgtags"], getfilectx, "convert-repo", date,
277 280 extra)
278 281 self.repo.commitctx(ctx)
279 282 return hex(self.repo.changelog.tip()), hex(tagparent)
280 283
281 284 def setfilemapmode(self, active):
282 285 self.filemapmode = active
283 286
284 287 def putbookmarks(self, updatedbookmark):
285 288 if not len(updatedbookmark):
286 289 return
287 290
288 291 self.ui.status(_("updating bookmarks\n"))
289 292 destmarks = self.repo._bookmarks
290 293 for bookmark in updatedbookmark:
291 294 destmarks[bookmark] = bin(updatedbookmark[bookmark])
292 295 destmarks.write()
293 296
294 297 def hascommitfrommap(self, rev):
295 298 # the exact semantics of clonebranches is unclear so we can't say no
296 299 return rev in self.repo or self.clonebranches
297 300
298 301 def hascommitforsplicemap(self, rev):
299 302 if rev not in self.repo and self.clonebranches:
300 303 raise util.Abort(_('revision %s not found in destination '
301 304 'repository (lookups with clonebranches=true '
302 305 'are not implemented)') % rev)
303 306 return rev in self.repo
304 307
305 308 class mercurial_source(converter_source):
306 309 def __init__(self, ui, path, rev=None):
307 310 converter_source.__init__(self, ui, path, rev)
308 311 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
309 312 self.ignored = set()
310 313 self.saverev = ui.configbool('convert', 'hg.saverev', False)
311 314 try:
312 315 self.repo = hg.repository(self.ui, path)
313 316 # try to provoke an exception if this isn't really a hg
314 317 # repo, but some other bogus compatible-looking url
315 318 if not self.repo.local():
316 319 raise error.RepoError
317 320 except error.RepoError:
318 321 ui.traceback()
319 322 raise NoRepo(_("%s is not a local Mercurial repository") % path)
320 323 self.lastrev = None
321 324 self.lastctx = None
322 325 self._changescache = None, None
323 326 self.convertfp = None
324 327 # Restrict converted revisions to startrev descendants
325 328 startnode = ui.config('convert', 'hg.startrev')
326 329 hgrevs = ui.config('convert', 'hg.revs')
327 330 if hgrevs is None:
328 331 if startnode is not None:
329 332 try:
330 333 startnode = self.repo.lookup(startnode)
331 334 except error.RepoError:
332 335 raise util.Abort(_('%s is not a valid start revision')
333 336 % startnode)
334 337 startrev = self.repo.changelog.rev(startnode)
335 338 children = {startnode: 1}
336 339 for r in self.repo.changelog.descendants([startrev]):
337 340 children[self.repo.changelog.node(r)] = 1
338 341 self.keep = children.__contains__
339 342 else:
340 343 self.keep = util.always
341 344 if rev:
342 345 self._heads = [self.repo[rev].node()]
343 346 else:
344 347 self._heads = self.repo.heads()
345 348 else:
346 349 if rev or startnode is not None:
347 350 raise util.Abort(_('hg.revs cannot be combined with '
348 351 'hg.startrev or --rev'))
349 352 nodes = set()
350 353 parents = set()
351 354 for r in scmutil.revrange(self.repo, [hgrevs]):
352 355 ctx = self.repo[r]
353 356 nodes.add(ctx.node())
354 357 parents.update(p.node() for p in ctx.parents())
355 358 self.keep = nodes.__contains__
356 359 self._heads = nodes - parents
357 360
358 361 def changectx(self, rev):
359 362 if self.lastrev != rev:
360 363 self.lastctx = self.repo[rev]
361 364 self.lastrev = rev
362 365 return self.lastctx
363 366
364 367 def parents(self, ctx):
365 368 return [p for p in ctx.parents() if p and self.keep(p.node())]
366 369
367 370 def getheads(self):
368 371 return [hex(h) for h in self._heads if self.keep(h)]
369 372
370 373 def getfile(self, name, rev):
371 374 try:
372 375 fctx = self.changectx(rev)[name]
373 376 return fctx.data(), fctx.flags()
374 377 except error.LookupError:
375 378 return None, None
376 379
377 380 def getchanges(self, rev, full):
378 381 ctx = self.changectx(rev)
379 382 parents = self.parents(ctx)
380 383 if full or not parents:
381 384 files = copyfiles = ctx.manifest()
382 385 if parents:
383 386 if self._changescache[0] == rev:
384 387 m, a, r = self._changescache[1]
385 388 else:
386 389 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
387 390 if not full:
388 391 files = m + a + r
389 392 copyfiles = m + a
390 393 # getcopies() is also run for roots and before filtering so missing
391 394 # revlogs are detected early
392 395 copies = self.getcopies(ctx, parents, copyfiles)
393 396 cleanp2 = set()
394 397 if len(parents) == 2:
395 398 cleanp2.update(self.repo.status(parents[1].node(), ctx.node(),
396 399 clean=True).clean)
397 400 changes = [(f, rev) for f in files if f not in self.ignored]
398 401 changes.sort()
399 402 return changes, copies, cleanp2
400 403
401 404 def getcopies(self, ctx, parents, files):
402 405 copies = {}
403 406 for name in files:
404 407 if name in self.ignored:
405 408 continue
406 409 try:
407 410 copysource, _copynode = ctx.filectx(name).renamed()
408 411 if copysource in self.ignored:
409 412 continue
410 413 # Ignore copy sources not in parent revisions
411 414 found = False
412 415 for p in parents:
413 416 if copysource in p:
414 417 found = True
415 418 break
416 419 if not found:
417 420 continue
418 421 copies[name] = copysource
419 422 except TypeError:
420 423 pass
421 424 except error.LookupError, e:
422 425 if not self.ignoreerrors:
423 426 raise
424 427 self.ignored.add(name)
425 428 self.ui.warn(_('ignoring: %s\n') % e)
426 429 return copies
427 430
428 431 def getcommit(self, rev):
429 432 ctx = self.changectx(rev)
430 433 parents = [p.hex() for p in self.parents(ctx)]
431 434 if self.saverev:
432 435 crev = rev
433 436 else:
434 437 crev = None
435 438 return commit(author=ctx.user(),
436 439 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
437 440 desc=ctx.description(), rev=crev, parents=parents,
438 441 branch=ctx.branch(), extra=ctx.extra(),
439 442 sortkey=ctx.rev())
440 443
441 444 def gettags(self):
442 445 # This will get written to .hgtags, filter non global tags out.
443 446 tags = [t for t in self.repo.tagslist()
444 447 if self.repo.tagtype(t[0]) == 'global']
445 448 return dict([(name, hex(node)) for name, node in tags
446 449 if self.keep(node)])
447 450
448 451 def getchangedfiles(self, rev, i):
449 452 ctx = self.changectx(rev)
450 453 parents = self.parents(ctx)
451 454 if not parents and i is None:
452 455 i = 0
453 456 changes = [], ctx.manifest().keys(), []
454 457 else:
455 458 i = i or 0
456 459 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
457 460 changes = [[f for f in l if f not in self.ignored] for l in changes]
458 461
459 462 if i == 0:
460 463 self._changescache = (rev, changes)
461 464
462 465 return changes[0] + changes[1] + changes[2]
463 466
464 467 def converted(self, rev, destrev):
465 468 if self.convertfp is None:
466 469 self.convertfp = open(self.repo.join('shamap'), 'a')
467 470 self.convertfp.write('%s %s\n' % (destrev, rev))
468 471 self.convertfp.flush()
469 472
470 473 def before(self):
471 474 self.ui.debug('run hg source pre-conversion action\n')
472 475
473 476 def after(self):
474 477 self.ui.debug('run hg source post-conversion action\n')
475 478
476 479 def hasnativeorder(self):
477 480 return True
478 481
479 482 def hasnativeclose(self):
480 483 return True
481 484
482 485 def lookuprev(self, rev):
483 486 try:
484 487 return hex(self.repo.lookup(rev))
485 488 except (error.RepoError, error.LookupError):
486 489 return None
487 490
488 491 def getbookmarks(self):
489 492 return bookmarks.listbookmarks(self.repo)
490 493
491 494 def checkrevformat(self, revstr, mapname='splicemap'):
492 495 """ Mercurial, revision string is a 40 byte hex """
493 496 self.checkhexformat(revstr, mapname)
@@ -1,156 +1,166 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > convert=
4 4 > [convert]
5 5 > hg.saverev=False
6 6 > EOF
7 7 $ hg init orig
8 8 $ cd orig
9 9 $ echo foo > foo
10 10 $ echo bar > bar
11 11 $ hg ci -qAm 'add foo bar' -d '0 0'
12 12 $ echo >> foo
13 13 $ hg ci -m 'change foo' -d '1 0'
14 14 $ hg up -qC 0
15 15 $ hg copy --after --force foo bar
16 16 $ hg copy foo baz
17 17 $ hg ci -m 'make bar and baz copies of foo' -d '2 0'
18 18 created new head
19 19
20 20 Test that template can print all file copies (issue4362)
21 21 $ hg log -r . --template "{file_copies % ' File: {file_copy}\n'}"
22 22 File: bar (foo)
23 23 File: baz (foo)
24 24
25 25 $ hg bookmark premerge1
26 26 $ hg merge -r 1
27 27 merging baz and foo to baz
28 28 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
29 29 (branch merge, don't forget to commit)
30 30 $ hg ci -m 'merge local copy' -d '3 0'
31 31 $ hg up -C 1
32 32 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 33 (leaving bookmark premerge1)
34 34 $ hg bookmark premerge2
35 35 $ hg merge 2
36 36 merging foo and baz to baz
37 37 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
38 38 (branch merge, don't forget to commit)
39 39 $ hg ci -m 'merge remote copy' -d '4 0'
40 40 created new head
41
42 Make and delete some tags
43
44 $ hg tag that
45 $ hg tag --remove that
46 $ hg tag this
47
41 48 #if execbit
42 49 $ chmod +x baz
43 50 #else
44 51 $ echo some other change to make sure we get a rev 5 > baz
45 52 #endif
46 53 $ hg ci -m 'mark baz executable' -d '5 0'
47 54 $ cd ..
48 55 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
49 56 initializing destination new repository
50 57 scanning source...
51 58 sorting...
52 59 converting...
53 5 add foo bar
54 4 change foo
55 3 make bar and baz copies of foo
56 2 merge local copy
57 1 merge remote copy
60 8 add foo bar
61 7 change foo
62 6 make bar and baz copies of foo
63 5 merge local copy
64 4 merge remote copy
65 3 Added tag that for changeset 88586c4e9f02
66 2 Removed tag that
67 1 Added tag this for changeset c56a7f387039
58 68 0 mark baz executable
59 69 updating bookmarks
60 70 $ cd new
61 71 $ hg out ../orig
62 72 comparing with ../orig
63 73 searching for changes
64 74 no changes found
65 75 [1]
66 76 #if execbit
67 77 $ hg bookmarks
68 78 premerge1 3:973ef48a98a4
69 premerge2 5:13d9b87cf8f8
79 premerge2 8:91d107c423ba
70 80 #else
71 81 Different hash because no x bit
72 82 $ hg bookmarks
73 83 premerge1 3:973ef48a98a4
74 premerge2 5:df0779bcf33c
84 premerge2 8:3537b15eaaca
75 85 #endif
76 86 $ cd ..
77 87
78 88 check shamap LF and CRLF handling
79 89
80 90 $ cat > rewrite.py <<EOF
81 91 > import sys
82 92 > # Interlace LF and CRLF
83 93 > lines = [(l.rstrip() + ((i % 2) and '\n' or '\r\n'))
84 94 > for i, l in enumerate(file(sys.argv[1]))]
85 95 > file(sys.argv[1], 'wb').write(''.join(lines))
86 96 > EOF
87 97 $ python rewrite.py new/.hg/shamap
88 98 $ cd orig
89 99 $ hg up -qC 1
90 100 $ echo foo >> foo
91 101 $ hg ci -qm 'change foo again'
92 102 $ hg up -qC 2
93 103 $ echo foo >> foo
94 104 $ hg ci -qm 'change foo again again'
95 105 $ cd ..
96 106 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
97 107 scanning source...
98 108 sorting...
99 109 converting...
100 110 1 change foo again again
101 111 0 change foo again
102 112 updating bookmarks
103 113
104 114 init broken repository
105 115
106 116 $ hg init broken
107 117 $ cd broken
108 118 $ echo a >> a
109 119 $ echo b >> b
110 120 $ hg ci -qAm init
111 121 $ echo a >> a
112 122 $ echo b >> b
113 123 $ hg copy b c
114 124 $ hg ci -qAm changeall
115 125 $ hg up -qC 0
116 126 $ echo bc >> b
117 127 $ hg ci -m changebagain
118 128 created new head
119 129 $ HGMERGE=internal:local hg -q merge
120 130 $ hg ci -m merge
121 131 $ hg mv b d
122 132 $ hg ci -m moveb
123 133
124 134 break it
125 135
126 136 $ rm .hg/store/data/b.*
127 137 $ cd ..
128 138 $ hg --config convert.hg.ignoreerrors=True convert broken fixed
129 139 initializing destination fixed repository
130 140 scanning source...
131 141 sorting...
132 142 converting...
133 143 4 init
134 144 ignoring: data/b.i@1e88685f5dde: no match found
135 145 3 changeall
136 146 2 changebagain
137 147 1 merge
138 148 0 moveb
139 149 $ hg -R fixed verify
140 150 checking changesets
141 151 checking manifests
142 152 crosschecking files in changesets and manifests
143 153 checking files
144 154 3 files, 5 changesets, 5 total revisions
145 155
146 156 manifest -r 0
147 157
148 158 $ hg -R fixed manifest -r 0
149 159 a
150 160
151 161 manifest -r tip
152 162
153 163 $ hg -R fixed manifest -r tip
154 164 a
155 165 c
156 166 d
General Comments 0
You need to be logged in to leave comments. Login now