##// END OF EJS Templates
rebase: add --rev option to rebase...
Pierre-Yves David -
r15270:6cb6064f default
parent child Browse files
Show More
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,609 +1,634 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
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 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 http://mercurial.selenic.com/wiki/RebaseExtension
15 15 '''
16 16
17 17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch
18 from mercurial import extensions, patch, scmutil
19 19 from mercurial.commands import templateopts
20 20 from mercurial.node import nullrev
21 21 from mercurial.lock import release
22 22 from mercurial.i18n import _
23 23 import os, errno
24 24
25 25 nullmerge = -2
26 26
27 27 cmdtable = {}
28 28 command = cmdutil.command(cmdtable)
29 29
30 30 @command('rebase',
31 31 [('s', 'source', '',
32 32 _('rebase from the specified changeset'), _('REV')),
33 33 ('b', 'base', '',
34 34 _('rebase from the base of the specified changeset '
35 35 '(up to greatest common ancestor of base and dest)'),
36 36 _('REV')),
37 ('r', 'rev', [],
38 _('rebase these revisions'),
39 _('REV')),
37 40 ('d', 'dest', '',
38 41 _('rebase onto the specified changeset'), _('REV')),
39 42 ('', 'collapse', False, _('collapse the rebased changesets')),
40 43 ('m', 'message', '',
41 44 _('use text as collapse commit message'), _('TEXT')),
42 45 ('e', 'edit', False, _('invoke editor on commit messages')),
43 46 ('l', 'logfile', '',
44 47 _('read collapse commit message from file'), _('FILE')),
45 48 ('', 'keep', False, _('keep original changesets')),
46 49 ('', 'keepbranches', False, _('keep original branch names')),
47 50 ('', 'detach', False, _('force detaching of source from its original '
48 51 'branch')),
49 52 ('t', 'tool', '', _('specify merge tool')),
50 53 ('c', 'continue', False, _('continue an interrupted rebase')),
51 54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
52 55 templateopts,
53 56 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
54 57 'hg rebase {-a|-c}'))
55 58 def rebase(ui, repo, **opts):
56 59 """move changeset (and descendants) to a different branch
57 60
58 61 Rebase uses repeated merging to graft changesets from one part of
59 62 history (the source) onto another (the destination). This can be
60 63 useful for linearizing *local* changes relative to a master
61 64 development tree.
62 65
63 66 You should not rebase changesets that have already been shared
64 67 with others. Doing so will force everybody else to perform the
65 68 same rebase or they will end up with duplicated changesets after
66 69 pulling in your rebased changesets.
67 70
68 71 If you don't specify a destination changeset (``-d/--dest``),
69 72 rebase uses the tipmost head of the current named branch as the
70 73 destination. (The destination changeset is not modified by
71 74 rebasing, but new changesets are added as its descendants.)
72 75
73 76 You can specify which changesets to rebase in two ways: as a
74 77 "source" changeset or as a "base" changeset. Both are shorthand
75 78 for a topologically related set of changesets (the "source
76 79 branch"). If you specify source (``-s/--source``), rebase will
77 80 rebase that changeset and all of its descendants onto dest. If you
78 81 specify base (``-b/--base``), rebase will select ancestors of base
79 82 back to but not including the common ancestor with dest. Thus,
80 83 ``-b`` is less precise but more convenient than ``-s``: you can
81 84 specify any changeset in the source branch, and rebase will select
82 85 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
83 86 uses the parent of the working directory as the base.
84 87
85 88 By default, rebase recreates the changesets in the source branch
86 89 as descendants of dest and then destroys the originals. Use
87 90 ``--keep`` to preserve the original source changesets. Some
88 91 changesets in the source branch (e.g. merges from the destination
89 92 branch) may be dropped if they no longer contribute any change.
90 93
91 94 One result of the rules for selecting the destination changeset
92 95 and source branch is that, unlike ``merge``, rebase will do
93 96 nothing if you are at the latest (tipmost) head of a named branch
94 97 with two heads. You need to explicitly specify source and/or
95 98 destination (or ``update`` to the other head, if it's the head of
96 99 the intended source branch).
97 100
98 101 If a rebase is interrupted to manually resolve a merge, it can be
99 102 continued with --continue/-c or aborted with --abort/-a.
100 103
101 104 Returns 0 on success, 1 if nothing to rebase.
102 105 """
103 106 originalwd = target = None
104 107 external = nullrev
105 108 state = {}
106 109 skipped = set()
107 110 targetancestors = set()
108 111
109 112 editor = None
110 113 if opts.get('edit'):
111 114 editor = cmdutil.commitforceeditor
112 115
113 116 lock = wlock = None
114 117 try:
115 118 lock = repo.lock()
116 119 wlock = repo.wlock()
117 120
118 121 # Validate input and define rebasing points
119 122 destf = opts.get('dest', None)
120 123 srcf = opts.get('source', None)
121 124 basef = opts.get('base', None)
125 revf = opts.get('rev', [])
122 126 contf = opts.get('continue')
123 127 abortf = opts.get('abort')
124 128 collapsef = opts.get('collapse', False)
125 129 collapsemsg = cmdutil.logmessage(ui, opts)
126 130 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
127 131 keepf = opts.get('keep', False)
128 132 keepbranchesf = opts.get('keepbranches', False)
129 133 detachf = opts.get('detach', False)
130 134 # keepopen is not meant for use on the command line, but by
131 135 # other extensions
132 136 keepopen = opts.get('keepopen', False)
133 137
134 138 if collapsemsg and not collapsef:
135 139 raise util.Abort(
136 140 _('message can only be specified with collapse'))
137 141
138 142 if contf or abortf:
139 143 if contf and abortf:
140 144 raise util.Abort(_('cannot use both abort and continue'))
141 145 if collapsef:
142 146 raise util.Abort(
143 147 _('cannot use collapse with continue or abort'))
144 148 if detachf:
145 149 raise util.Abort(_('cannot use detach with continue or abort'))
146 150 if srcf or basef or destf:
147 151 raise util.Abort(
148 152 _('abort and continue do not allow specifying revisions'))
149 153 if opts.get('tool', False):
150 154 ui.warn(_('tool option will be ignored\n'))
151 155
152 156 (originalwd, target, state, skipped, collapsef, keepf,
153 157 keepbranchesf, external) = restorestatus(repo)
154 158 if abortf:
155 159 return abort(repo, originalwd, target, state)
156 160 else:
157 161 if srcf and basef:
158 162 raise util.Abort(_('cannot specify both a '
163 'source and a base'))
164 if revf and basef:
165 raise util.Abort(_('cannot specify both a'
159 166 'revision and a base'))
167 if revf and srcf:
168 raise util.Abort(_('cannot specify both a'
169 'revision and a source'))
160 170 if detachf:
161 171 if not srcf:
162 172 raise util.Abort(
163 173 _('detach requires a revision to be specified'))
164 174 if basef:
165 175 raise util.Abort(_('cannot specify a base with detach'))
166 176
167 177 cmdutil.bailifchanged(repo)
168 178
169 179 if not destf:
170 # Destination defaults to the latest revision in the current branch
180 # Destination defaults to the latest revision in the
181 # current branch
171 182 branch = repo[None].branch()
172 183 dest = repo[branch]
173 184 else:
174 185 dest = repo[destf]
175 186
187 rebaseset = None
176 188 if srcf:
177 189 revsetargs = ('(%r)::', srcf)
190 elif revf:
191 rebaseset = scmutil.revrange(repo, revf)
192 if not keepf and rebaseset:
193 try:
194 repo.set('children(%ld) - %ld',
195 rebaseset, rebaseset).next()
196 except StopIteration:
197 pass # empty revset is what we look for
198 else:
199 msg = _("can't remove original changesets with"
200 " unrebased descendants")
201 hint = _('use --keep to keep original changesets')
202 raise util.Abort(msg, hint=hint)
178 203 else:
179 204 base = basef or '.'
180 205 revsetargs = ('(children(ancestor(%r, %d)) and ::(%r))::',
181 206 base, dest, base)
182
183 rebaseset = [c.rev() for c in repo.set(*revsetargs)]
207 if rebaseset is None:
208 rebaseset = [c.rev() for c in repo.set(*revsetargs)]
184 209 if rebaseset:
185 210 result = buildstate(repo, dest, rebaseset, detachf)
186 211 else:
187 repo.ui.debug(_('base is ancestor of destination'))
212 repo.ui.debug('base is ancestor of destination')
188 213 result = None
189 214 if not result:
190 215 # Empty state built, nothing to rebase
191 216 ui.status(_('nothing to rebase\n'))
192 217 return 1
193 218 else:
194 219 originalwd, target, state = result
195 220 if collapsef:
196 221 targetancestors = set(repo.changelog.ancestors(target))
197 222 external = checkexternal(repo, state, targetancestors)
198 223
199 224 if keepbranchesf:
200 225 assert not extrafn, 'cannot use both keepbranches and extrafn'
201 226 def extrafn(ctx, extra):
202 227 extra['branch'] = ctx.branch()
203 228 if collapsef:
204 229 branches = set()
205 230 for rev in state:
206 231 branches.add(repo[rev].branch())
207 232 if len(branches) > 1:
208 233 raise util.Abort(_('cannot collapse multiple named '
209 234 'branches'))
210 235
211 236
212 237 # Rebase
213 238 if not targetancestors:
214 239 targetancestors = set(repo.changelog.ancestors(target))
215 240 targetancestors.add(target)
216 241
217 242 # Keep track of the current bookmarks in order to reset them later
218 243 currentbookmarks = repo._bookmarks.copy()
219 244
220 245 sortedstate = sorted(state)
221 246 total = len(sortedstate)
222 247 pos = 0
223 248 for rev in sortedstate:
224 249 pos += 1
225 250 if state[rev] == -1:
226 251 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
227 252 _('changesets'), total)
228 253 storestatus(repo, originalwd, target, state, collapsef, keepf,
229 254 keepbranchesf, external)
230 255 p1, p2 = defineparents(repo, rev, target, state,
231 256 targetancestors)
232 257 if len(repo.parents()) == 2:
233 258 repo.ui.debug('resuming interrupted rebase\n')
234 259 else:
235 260 try:
236 261 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
237 262 stats = rebasenode(repo, rev, p1, state)
238 263 if stats and stats[3] > 0:
239 264 raise util.Abort(_('unresolved conflicts (see hg '
240 265 'resolve, then hg rebase --continue)'))
241 266 finally:
242 267 ui.setconfig('ui', 'forcemerge', '')
243 268 cmdutil.duplicatecopies(repo, rev, target, p2)
244 269 if not collapsef:
245 270 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
246 271 editor=editor)
247 272 else:
248 273 # Skip commit if we are collapsing
249 274 repo.dirstate.setparents(repo[p1].node())
250 275 newrev = None
251 276 # Update the state
252 277 if newrev is not None:
253 278 state[rev] = repo[newrev].rev()
254 279 else:
255 280 if not collapsef:
256 281 ui.note(_('no changes, revision %d skipped\n') % rev)
257 282 ui.debug('next revision set to %s\n' % p1)
258 283 skipped.add(rev)
259 284 state[rev] = p1
260 285
261 286 ui.progress(_('rebasing'), None)
262 287 ui.note(_('rebase merging completed\n'))
263 288
264 289 if collapsef and not keepopen:
265 290 p1, p2 = defineparents(repo, min(state), target,
266 291 state, targetancestors)
267 292 if collapsemsg:
268 293 commitmsg = collapsemsg
269 294 else:
270 295 commitmsg = 'Collapsed revision'
271 296 for rebased in state:
272 297 if rebased not in skipped and state[rebased] != nullmerge:
273 298 commitmsg += '\n* %s' % repo[rebased].description()
274 299 commitmsg = ui.edit(commitmsg, repo.ui.username())
275 300 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
276 301 extrafn=extrafn, editor=editor)
277 302
278 303 if 'qtip' in repo.tags():
279 304 updatemq(repo, state, skipped, **opts)
280 305
281 306 if currentbookmarks:
282 307 # Nodeids are needed to reset bookmarks
283 308 nstate = {}
284 309 for k, v in state.iteritems():
285 310 if v != nullmerge:
286 311 nstate[repo[k].node()] = repo[v].node()
287 312
288 313 if not keepf:
289 314 # Remove no more useful revisions
290 315 rebased = [rev for rev in state if state[rev] != nullmerge]
291 316 if rebased:
292 317 if set(repo.changelog.descendants(min(rebased))) - set(state):
293 318 ui.warn(_("warning: new changesets detected "
294 319 "on source branch, not stripping\n"))
295 320 else:
296 321 # backup the old csets by default
297 322 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
298 323
299 324 if currentbookmarks:
300 325 updatebookmarks(repo, nstate, currentbookmarks, **opts)
301 326
302 327 clearstatus(repo)
303 328 ui.note(_("rebase completed\n"))
304 329 if os.path.exists(repo.sjoin('undo')):
305 330 util.unlinkpath(repo.sjoin('undo'))
306 331 if skipped:
307 332 ui.note(_("%d revisions have been skipped\n") % len(skipped))
308 333 finally:
309 334 release(lock, wlock)
310 335
311 336 def checkexternal(repo, state, targetancestors):
312 337 """Check whether one or more external revisions need to be taken in
313 338 consideration. In the latter case, abort.
314 339 """
315 340 external = nullrev
316 341 source = min(state)
317 342 for rev in state:
318 343 if rev == source:
319 344 continue
320 345 # Check externals and fail if there are more than one
321 346 for p in repo[rev].parents():
322 347 if (p.rev() not in state
323 348 and p.rev() not in targetancestors):
324 349 if external != nullrev:
325 350 raise util.Abort(_('unable to collapse, there is more '
326 351 'than one external parent'))
327 352 external = p.rev()
328 353 return external
329 354
330 355 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
331 356 'Commit the changes and store useful information in extra'
332 357 try:
333 358 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
334 359 ctx = repo[rev]
335 360 if commitmsg is None:
336 361 commitmsg = ctx.description()
337 362 extra = {'rebase_source': ctx.hex()}
338 363 if extrafn:
339 364 extrafn(ctx, extra)
340 365 # Commit might fail if unresolved files exist
341 366 newrev = repo.commit(text=commitmsg, user=ctx.user(),
342 367 date=ctx.date(), extra=extra, editor=editor)
343 368 repo.dirstate.setbranch(repo[newrev].branch())
344 369 return newrev
345 370 except util.Abort:
346 371 # Invalidate the previous setparents
347 372 repo.dirstate.invalidate()
348 373 raise
349 374
350 375 def rebasenode(repo, rev, p1, state):
351 376 'Rebase a single revision'
352 377 # Merge phase
353 378 # Update to target and merge it with local
354 379 if repo['.'].rev() != repo[p1].rev():
355 380 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
356 381 merge.update(repo, p1, False, True, False)
357 382 else:
358 383 repo.ui.debug(" already in target\n")
359 384 repo.dirstate.write()
360 385 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
361 386 base = None
362 387 if repo[rev].rev() != repo[min(state)].rev():
363 388 base = repo[rev].p1().node()
364 389 return merge.update(repo, rev, True, True, False, base)
365 390
366 391 def defineparents(repo, rev, target, state, targetancestors):
367 392 'Return the new parent relationship of the revision that will be rebased'
368 393 parents = repo[rev].parents()
369 394 p1 = p2 = nullrev
370 395
371 396 P1n = parents[0].rev()
372 397 if P1n in targetancestors:
373 398 p1 = target
374 399 elif P1n in state:
375 400 if state[P1n] == nullmerge:
376 401 p1 = target
377 402 else:
378 403 p1 = state[P1n]
379 404 else: # P1n external
380 405 p1 = target
381 406 p2 = P1n
382 407
383 408 if len(parents) == 2 and parents[1].rev() not in targetancestors:
384 409 P2n = parents[1].rev()
385 410 # interesting second parent
386 411 if P2n in state:
387 412 if p1 == target: # P1n in targetancestors or external
388 413 p1 = state[P2n]
389 414 else:
390 415 p2 = state[P2n]
391 416 else: # P2n external
392 417 if p2 != nullrev: # P1n external too => rev is a merged revision
393 418 raise util.Abort(_('cannot use revision %d as base, result '
394 419 'would have 3 parents') % rev)
395 420 p2 = P2n
396 421 repo.ui.debug(" future parents are %d and %d\n" %
397 422 (repo[p1].rev(), repo[p2].rev()))
398 423 return p1, p2
399 424
400 425 def isagitpatch(repo, patchname):
401 426 'Return true if the given patch is in git format'
402 427 mqpatch = os.path.join(repo.mq.path, patchname)
403 428 for line in patch.linereader(file(mqpatch, 'rb')):
404 429 if line.startswith('diff --git'):
405 430 return True
406 431 return False
407 432
408 433 def updatemq(repo, state, skipped, **opts):
409 434 'Update rebased mq patches - finalize and then import them'
410 435 mqrebase = {}
411 436 mq = repo.mq
412 437 original_series = mq.fullseries[:]
413 438
414 439 for p in mq.applied:
415 440 rev = repo[p.node].rev()
416 441 if rev in state:
417 442 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
418 443 (rev, p.name))
419 444 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
420 445
421 446 if mqrebase:
422 447 mq.finish(repo, mqrebase.keys())
423 448
424 449 # We must start import from the newest revision
425 450 for rev in sorted(mqrebase, reverse=True):
426 451 if rev not in skipped:
427 452 name, isgit = mqrebase[rev]
428 453 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
429 454 mq.qimport(repo, (), patchname=name, git=isgit,
430 455 rev=[str(state[rev])])
431 456
432 457 # restore old series to preserve guards
433 458 mq.fullseries = original_series
434 459 mq.series_dirty = True
435 460 mq.savedirty()
436 461
437 462 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
438 463 'Move bookmarks to their correct changesets'
439 464 current = repo._bookmarkcurrent
440 465 for k, v in originalbookmarks.iteritems():
441 466 if v in nstate:
442 467 if nstate[v] != nullmerge:
443 468 # reset the pointer if the bookmark was moved incorrectly
444 469 if k != current:
445 470 repo._bookmarks[k] = nstate[v]
446 471
447 472 bookmarks.write(repo)
448 473
449 474 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
450 475 external):
451 476 'Store the current status to allow recovery'
452 477 f = repo.opener("rebasestate", "w")
453 478 f.write(repo[originalwd].hex() + '\n')
454 479 f.write(repo[target].hex() + '\n')
455 480 f.write(repo[external].hex() + '\n')
456 481 f.write('%d\n' % int(collapse))
457 482 f.write('%d\n' % int(keep))
458 483 f.write('%d\n' % int(keepbranches))
459 484 for d, v in state.iteritems():
460 485 oldrev = repo[d].hex()
461 486 newrev = repo[v].hex()
462 487 f.write("%s:%s\n" % (oldrev, newrev))
463 488 f.close()
464 489 repo.ui.debug('rebase status stored\n')
465 490
466 491 def clearstatus(repo):
467 492 'Remove the status files'
468 493 if os.path.exists(repo.join("rebasestate")):
469 494 util.unlinkpath(repo.join("rebasestate"))
470 495
471 496 def restorestatus(repo):
472 497 'Restore a previously stored status'
473 498 try:
474 499 target = None
475 500 collapse = False
476 501 external = nullrev
477 502 state = {}
478 503 f = repo.opener("rebasestate")
479 504 for i, l in enumerate(f.read().splitlines()):
480 505 if i == 0:
481 506 originalwd = repo[l].rev()
482 507 elif i == 1:
483 508 target = repo[l].rev()
484 509 elif i == 2:
485 510 external = repo[l].rev()
486 511 elif i == 3:
487 512 collapse = bool(int(l))
488 513 elif i == 4:
489 514 keep = bool(int(l))
490 515 elif i == 5:
491 516 keepbranches = bool(int(l))
492 517 else:
493 518 oldrev, newrev = l.split(':')
494 519 state[repo[oldrev].rev()] = repo[newrev].rev()
495 520 skipped = set()
496 521 # recompute the set of skipped revs
497 522 if not collapse:
498 523 seen = set([target])
499 524 for old, new in sorted(state.items()):
500 525 if new != nullrev and new in seen:
501 526 skipped.add(old)
502 527 seen.add(new)
503 528 repo.ui.debug('computed skipped revs: %s\n' % skipped)
504 529 repo.ui.debug('rebase status resumed\n')
505 530 return (originalwd, target, state, skipped,
506 531 collapse, keep, keepbranches, external)
507 532 except IOError, err:
508 533 if err.errno != errno.ENOENT:
509 534 raise
510 535 raise util.Abort(_('no rebase in progress'))
511 536
512 537 def abort(repo, originalwd, target, state):
513 538 'Restore the repository to its original state'
514 539 if set(repo.changelog.descendants(target)) - set(state.values()):
515 540 repo.ui.warn(_("warning: new changesets detected on target branch, "
516 541 "can't abort\n"))
517 542 return -1
518 543 else:
519 544 # Strip from the first rebased revision
520 545 merge.update(repo, repo[originalwd].rev(), False, True, False)
521 546 rebased = filter(lambda x: x > -1 and x != target, state.values())
522 547 if rebased:
523 548 strippoint = min(rebased)
524 549 # no backup of rebased cset versions needed
525 550 repair.strip(repo.ui, repo, repo[strippoint].node())
526 551 clearstatus(repo)
527 552 repo.ui.warn(_('rebase aborted\n'))
528 553 return 0
529 554
530 555 def buildstate(repo, dest, rebaseset, detach):
531 556 '''Define which revisions are going to be rebased and where
532 557
533 558 repo: repo
534 559 dest: context
535 560 rebaseset: set of rev
536 561 detach: boolean'''
537 562
538 563 # This check isn't strictly necessary, since mq detects commits over an
539 564 # applied patch. But it prevents messing up the working directory when
540 565 # a partially completed rebase is blocked by mq.
541 566 if 'qtip' in repo.tags() and (dest.node() in
542 567 [s.node for s in repo.mq.applied]):
543 568 raise util.Abort(_('cannot rebase onto an applied mq patch'))
544 569
545 570 detachset = set()
546 571 roots = list(repo.set('roots(%ld)', rebaseset))
547 572 if not roots:
548 raise util.Abort( _('no matching revisions'))
573 raise util.Abort(_('no matching revisions'))
549 574 if len(roots) > 1:
550 raise util.Abort( _("can't rebase multiple roots"))
575 raise util.Abort(_("can't rebase multiple roots"))
551 576 root = roots[0]
552 577
553 578 commonbase = root.ancestor(dest)
554 579 if commonbase == root:
555 580 raise util.Abort(_('source is ancestor of destination'))
556 581 if commonbase == dest:
557 582 samebranch = root.branch() == dest.branch()
558 583 if samebranch and root in dest.children():
559 584 repo.ui.debug(_('source is a child of destination'))
560 585 return None
561 586 # rebase on ancestor, force detach
562 587 detach = True
563 588 if detach:
564 589 detachset = [c.rev() for c in repo.set('::%d - ::%d - %d',
565 590 root, commonbase, root)]
566 591
567 592 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
568 593 state = dict.fromkeys(rebaseset, nullrev)
569 594 state.update(dict.fromkeys(detachset, nullmerge))
570 595 return repo['.'].rev(), dest.rev(), state
571 596
572 597 def pullrebase(orig, ui, repo, *args, **opts):
573 598 'Call rebase after pull if the latter has been invoked with --rebase'
574 599 if opts.get('rebase'):
575 600 if opts.get('update'):
576 601 del opts['update']
577 602 ui.debug('--update and --rebase are not compatible, ignoring '
578 603 'the update flag\n')
579 604
580 605 cmdutil.bailifchanged(repo)
581 606 revsprepull = len(repo)
582 607 origpostincoming = commands.postincoming
583 608 def _dummy(*args, **kwargs):
584 609 pass
585 610 commands.postincoming = _dummy
586 611 try:
587 612 orig(ui, repo, *args, **opts)
588 613 finally:
589 614 commands.postincoming = origpostincoming
590 615 revspostpull = len(repo)
591 616 if revspostpull > revsprepull:
592 617 rebase(ui, repo, **opts)
593 618 branch = repo[None].branch()
594 619 dest = repo[branch].rev()
595 620 if dest != repo['.'].rev():
596 621 # there was nothing to rebase we force an update
597 622 hg.update(repo, dest)
598 623 else:
599 624 if opts.get('tool'):
600 625 raise util.Abort(_('--tool can only be used with --rebase'))
601 626 orig(ui, repo, *args, **opts)
602 627
603 628 def uisetup(ui):
604 629 'Replace pull with a decorator to provide --rebase option'
605 630 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
606 631 entry[1].append(('', 'rebase', None,
607 632 _("rebase working directory to branch head")))
608 633 entry[1].append(('t', 'tool', '',
609 634 _("specify merge tool for rebase")))
@@ -1,389 +1,389 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > graphlog=
4 4 > rebase=
5 5 >
6 6 > [alias]
7 7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 8 > EOF
9 9
10 10
11 11 $ hg init a
12 12 $ cd a
13 13 $ hg unbundle $TESTDIR/bundles/rebase.hg
14 14 adding changesets
15 15 adding manifests
16 16 adding file changes
17 17 added 8 changesets with 7 changes to 7 files (+2 heads)
18 18 (run 'hg heads' to see heads, 'hg merge' to merge)
19 19 $ hg up tip
20 20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 21
22 22 $ echo I > I
23 23 $ hg ci -AmI
24 24 adding I
25 25
26 26 $ hg tglog
27 27 @ 8: 'I'
28 28 |
29 29 o 7: 'H'
30 30 |
31 31 | o 6: 'G'
32 32 |/|
33 33 o | 5: 'F'
34 34 | |
35 35 | o 4: 'E'
36 36 |/
37 37 | o 3: 'D'
38 38 | |
39 39 | o 2: 'C'
40 40 | |
41 41 | o 1: 'B'
42 42 |/
43 43 o 0: 'A'
44 44
45 45 $ cd ..
46 46
47 47
48 48 These fail:
49 49
50 50 $ hg clone -q -u . a a1
51 51 $ cd a1
52 52
53 53 $ hg rebase -s 8 -d 7
54 54 nothing to rebase
55 55 [1]
56 56
57 57 $ hg rebase --continue --abort
58 58 abort: cannot use both abort and continue
59 59 [255]
60 60
61 61 $ hg rebase --continue --collapse
62 62 abort: cannot use collapse with continue or abort
63 63 [255]
64 64
65 65 $ hg rebase --continue --dest 4
66 66 abort: abort and continue do not allow specifying revisions
67 67 [255]
68 68
69 69 $ hg rebase --base 5 --source 4
70 abort: cannot specify both a revision and a base
70 abort: cannot specify both a source and a base
71 71 [255]
72 72
73 73 $ hg rebase
74 74 nothing to rebase
75 75 [1]
76 76
77 77 $ hg up -q 7
78 78
79 79 $ hg rebase --traceback
80 80 nothing to rebase
81 81 [1]
82 82
83 83
84 84 These work:
85 85
86 86 Rebase with no arguments (from 3 onto 8):
87 87
88 88 $ hg up -q -C 3
89 89
90 90 $ hg rebase
91 91 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
92 92
93 93 $ hg tglog
94 94 @ 8: 'D'
95 95 |
96 96 o 7: 'C'
97 97 |
98 98 o 6: 'B'
99 99 |
100 100 o 5: 'I'
101 101 |
102 102 o 4: 'H'
103 103 |
104 104 | o 3: 'G'
105 105 |/|
106 106 o | 2: 'F'
107 107 | |
108 108 | o 1: 'E'
109 109 |/
110 110 o 0: 'A'
111 111
112 112 Try to rollback after a rebase (fail):
113 113
114 114 $ hg rollback
115 115 no rollback information available
116 116 [1]
117 117
118 118 $ cd ..
119 119
120 120
121 121 Rebase with base == '.' => same as no arguments (from 3 onto 8):
122 122
123 123 $ hg clone -q -u 3 a a2
124 124 $ cd a2
125 125
126 126 $ hg rebase --base .
127 127 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
128 128
129 129 $ hg tglog
130 130 @ 8: 'D'
131 131 |
132 132 o 7: 'C'
133 133 |
134 134 o 6: 'B'
135 135 |
136 136 o 5: 'I'
137 137 |
138 138 o 4: 'H'
139 139 |
140 140 | o 3: 'G'
141 141 |/|
142 142 o | 2: 'F'
143 143 | |
144 144 | o 1: 'E'
145 145 |/
146 146 o 0: 'A'
147 147
148 148 $ cd ..
149 149
150 150
151 151 Rebase with dest == `hg branch` => same as no arguments (from 3 onto 8):
152 152
153 153 $ hg clone -q -u 3 a a3
154 154 $ cd a3
155 155
156 156 $ hg rebase --dest `hg branch`
157 157 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
158 158
159 159 $ hg tglog
160 160 @ 8: 'D'
161 161 |
162 162 o 7: 'C'
163 163 |
164 164 o 6: 'B'
165 165 |
166 166 o 5: 'I'
167 167 |
168 168 o 4: 'H'
169 169 |
170 170 | o 3: 'G'
171 171 |/|
172 172 o | 2: 'F'
173 173 | |
174 174 | o 1: 'E'
175 175 |/
176 176 o 0: 'A'
177 177
178 178 $ cd ..
179 179
180 180
181 181 Specify only source (from 2 onto 8):
182 182
183 183 $ hg clone -q -u . a a4
184 184 $ cd a4
185 185
186 186 $ hg rebase --source 2
187 187 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
188 188
189 189 $ hg tglog
190 190 @ 8: 'D'
191 191 |
192 192 o 7: 'C'
193 193 |\
194 194 | o 6: 'I'
195 195 | |
196 196 | o 5: 'H'
197 197 | |
198 198 | | o 4: 'G'
199 199 | |/|
200 200 | o | 3: 'F'
201 201 | | |
202 202 | | o 2: 'E'
203 203 | |/
204 204 o | 1: 'B'
205 205 |/
206 206 o 0: 'A'
207 207
208 208 $ cd ..
209 209
210 210
211 211 Specify only dest (from 3 onto 6):
212 212
213 213 $ hg clone -q -u 3 a a5
214 214 $ cd a5
215 215
216 216 $ hg rebase --dest 6
217 217 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
218 218
219 219 $ hg tglog
220 220 @ 8: 'D'
221 221 |
222 222 o 7: 'C'
223 223 |
224 224 o 6: 'B'
225 225 |
226 226 | o 5: 'I'
227 227 | |
228 228 | o 4: 'H'
229 229 | |
230 230 o | 3: 'G'
231 231 |\|
232 232 | o 2: 'F'
233 233 | |
234 234 o | 1: 'E'
235 235 |/
236 236 o 0: 'A'
237 237
238 238 $ cd ..
239 239
240 240
241 241 Specify only base (from 1 onto 8):
242 242
243 243 $ hg clone -q -u . a a6
244 244 $ cd a6
245 245
246 246 $ hg rebase --base 3
247 247 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
248 248
249 249 $ hg tglog
250 250 @ 8: 'D'
251 251 |
252 252 o 7: 'C'
253 253 |
254 254 o 6: 'B'
255 255 |
256 256 o 5: 'I'
257 257 |
258 258 o 4: 'H'
259 259 |
260 260 | o 3: 'G'
261 261 |/|
262 262 o | 2: 'F'
263 263 | |
264 264 | o 1: 'E'
265 265 |/
266 266 o 0: 'A'
267 267
268 268 $ cd ..
269 269
270 270
271 271 Specify source and dest (from 2 onto 7):
272 272
273 273 $ hg clone -q -u . a a7
274 274 $ cd a7
275 275
276 276 $ hg rebase --detach --source 2 --dest 7
277 277 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
278 278
279 279 $ hg tglog
280 280 @ 8: 'D'
281 281 |
282 282 o 7: 'C'
283 283 |
284 284 | o 6: 'I'
285 285 |/
286 286 o 5: 'H'
287 287 |
288 288 | o 4: 'G'
289 289 |/|
290 290 o | 3: 'F'
291 291 | |
292 292 | o 2: 'E'
293 293 |/
294 294 | o 1: 'B'
295 295 |/
296 296 o 0: 'A'
297 297
298 298 $ cd ..
299 299
300 300
301 301 Specify base and dest (from 1 onto 7):
302 302
303 303 $ hg clone -q -u . a a8
304 304 $ cd a8
305 305
306 306 $ hg rebase --base 3 --dest 7
307 307 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/*-backup.hg (glob)
308 308
309 309 $ hg tglog
310 310 @ 8: 'D'
311 311 |
312 312 o 7: 'C'
313 313 |
314 314 o 6: 'B'
315 315 |
316 316 | o 5: 'I'
317 317 |/
318 318 o 4: 'H'
319 319 |
320 320 | o 3: 'G'
321 321 |/|
322 322 o | 2: 'F'
323 323 | |
324 324 | o 1: 'E'
325 325 |/
326 326 o 0: 'A'
327 327
328 328 $ cd ..
329 329
330 330 Test --tool parameter:
331 331
332 332 $ hg init b
333 333 $ cd b
334 334
335 335 $ echo c1 > c1
336 336 $ hg ci -Am c1
337 337 adding c1
338 338
339 339 $ echo c2 > c2
340 340 $ hg ci -Am c2
341 341 adding c2
342 342
343 343 $ hg up -q 0
344 344 $ echo c2b > c2
345 345 $ hg ci -Am c2b
346 346 adding c2
347 347 created new head
348 348
349 349 $ cd ..
350 350
351 351 $ hg clone -q -u . b b1
352 352 $ cd b1
353 353
354 354 $ hg rebase -s 2 -d 1 --tool internal:local
355 355 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
356 356
357 357 $ hg cat c2
358 358 c2
359 359
360 360 $ cd ..
361 361
362 362
363 363 $ hg clone -q -u . b b2
364 364 $ cd b2
365 365
366 366 $ hg rebase -s 2 -d 1 --tool internal:other
367 367 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
368 368
369 369 $ hg cat c2
370 370 c2b
371 371
372 372 $ cd ..
373 373
374 374
375 375 $ hg clone -q -u . b b3
376 376 $ cd b3
377 377
378 378 $ hg rebase -s 2 -d 1 --tool internal:fail
379 379 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
380 380 [255]
381 381
382 382 $ hg resolve -l
383 383 U c2
384 384
385 385 $ hg resolve -m c2
386 386 $ hg rebase -c --tool internal:fail
387 387 tool option will be ignored
388 388 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
389 389
@@ -1,272 +1,508 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > graphlog=
4 4 > rebase=
5 5 >
6 6 > [alias]
7 7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 8 > EOF
9 9
10 10
11 11 $ hg init a
12 12 $ cd a
13 13 $ hg unbundle $TESTDIR/bundles/rebase.hg
14 14 adding changesets
15 15 adding manifests
16 16 adding file changes
17 17 added 8 changesets with 7 changes to 7 files (+2 heads)
18 18 (run 'hg heads' to see heads, 'hg merge' to merge)
19 19 $ hg up tip
20 20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 21 $ cd ..
22 22
23 23
24 24 Rebasing
25 25 D onto H - simple rebase:
26 26
27 27 $ hg clone -q -u . a a1
28 28 $ cd a1
29 29
30 30 $ hg tglog
31 31 @ 7: 'H'
32 32 |
33 33 | o 6: 'G'
34 34 |/|
35 35 o | 5: 'F'
36 36 | |
37 37 | o 4: 'E'
38 38 |/
39 39 | o 3: 'D'
40 40 | |
41 41 | o 2: 'C'
42 42 | |
43 43 | o 1: 'B'
44 44 |/
45 45 o 0: 'A'
46 46
47 47
48 48 $ hg rebase -s 3 -d 7
49 49 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
50 50
51 51 $ hg tglog
52 52 @ 7: 'D'
53 53 |\
54 54 | o 6: 'H'
55 55 | |
56 56 | | o 5: 'G'
57 57 | |/|
58 58 | o | 4: 'F'
59 59 | | |
60 60 | | o 3: 'E'
61 61 | |/
62 62 o | 2: 'C'
63 63 | |
64 64 o | 1: 'B'
65 65 |/
66 66 o 0: 'A'
67 67
68 68 $ cd ..
69 69
70 70
71 71 D onto F - intermediate point:
72 72
73 73 $ hg clone -q -u . a a2
74 74 $ cd a2
75 75
76 76 $ hg rebase -s 3 -d 5
77 77 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
78 78
79 79 $ hg tglog
80 80 @ 7: 'D'
81 81 |\
82 82 | | o 6: 'H'
83 83 | |/
84 84 | | o 5: 'G'
85 85 | |/|
86 86 | o | 4: 'F'
87 87 | | |
88 88 | | o 3: 'E'
89 89 | |/
90 90 o | 2: 'C'
91 91 | |
92 92 o | 1: 'B'
93 93 |/
94 94 o 0: 'A'
95 95
96 96 $ cd ..
97 97
98 98
99 99 E onto H - skip of G:
100 100
101 101 $ hg clone -q -u . a a3
102 102 $ cd a3
103 103
104 104 $ hg rebase -s 4 -d 7
105 105 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
106 106
107 107 $ hg tglog
108 108 @ 6: 'E'
109 109 |
110 110 o 5: 'H'
111 111 |
112 112 o 4: 'F'
113 113 |
114 114 | o 3: 'D'
115 115 | |
116 116 | o 2: 'C'
117 117 | |
118 118 | o 1: 'B'
119 119 |/
120 120 o 0: 'A'
121 121
122 122 $ cd ..
123 123
124 124
125 125 F onto E - rebase of a branching point (skip G):
126 126
127 127 $ hg clone -q -u . a a4
128 128 $ cd a4
129 129
130 130 $ hg rebase -s 5 -d 4
131 131 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
132 132
133 133 $ hg tglog
134 134 @ 6: 'H'
135 135 |
136 136 o 5: 'F'
137 137 |
138 138 o 4: 'E'
139 139 |
140 140 | o 3: 'D'
141 141 | |
142 142 | o 2: 'C'
143 143 | |
144 144 | o 1: 'B'
145 145 |/
146 146 o 0: 'A'
147 147
148 148 $ cd ..
149 149
150 150
151 151 G onto H - merged revision having a parent in ancestors of target:
152 152
153 153 $ hg clone -q -u . a a5
154 154 $ cd a5
155 155
156 156 $ hg rebase -s 6 -d 7
157 157 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
158 158
159 159 $ hg tglog
160 160 @ 7: 'G'
161 161 |\
162 162 | o 6: 'H'
163 163 | |
164 164 | o 5: 'F'
165 165 | |
166 166 o | 4: 'E'
167 167 |/
168 168 | o 3: 'D'
169 169 | |
170 170 | o 2: 'C'
171 171 | |
172 172 | o 1: 'B'
173 173 |/
174 174 o 0: 'A'
175 175
176 176 $ cd ..
177 177
178 178
179 179 F onto B - G maintains E as parent:
180 180
181 181 $ hg clone -q -u . a a6
182 182 $ cd a6
183 183
184 184 $ hg rebase -s 5 -d 1
185 185 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
186 186
187 187 $ hg tglog
188 188 @ 7: 'H'
189 189 |
190 190 | o 6: 'G'
191 191 |/|
192 192 o | 5: 'F'
193 193 | |
194 194 | o 4: 'E'
195 195 | |
196 196 | | o 3: 'D'
197 197 | | |
198 198 +---o 2: 'C'
199 199 | |
200 200 o | 1: 'B'
201 201 |/
202 202 o 0: 'A'
203 203
204 204 $ cd ..
205 205
206 206
207 207 These will fail (using --source):
208 208
209 209 G onto F - rebase onto an ancestor:
210 210
211 211 $ hg clone -q -u . a a7
212 212 $ cd a7
213 213
214 214 $ hg rebase -s 6 -d 5
215 215 nothing to rebase
216 216 [1]
217 217
218 218 F onto G - rebase onto a descendant:
219 219
220 220 $ hg rebase -s 5 -d 6
221 221 abort: source is ancestor of destination
222 222 [255]
223 223
224 224 G onto B - merge revision with both parents not in ancestors of target:
225 225
226 226 $ hg rebase -s 6 -d 1
227 227 abort: cannot use revision 6 as base, result would have 3 parents
228 228 [255]
229 229
230 230
231 231 These will abort gracefully (using --base):
232 232
233 233 G onto G - rebase onto same changeset:
234 234
235 235 $ hg rebase -b 6 -d 6
236 236 nothing to rebase
237 237 [1]
238 238
239 239 G onto F - rebase onto an ancestor:
240 240
241 241 $ hg rebase -b 6 -d 5
242 242 nothing to rebase
243 243 [1]
244 244
245 245 F onto G - rebase onto a descendant:
246 246
247 247 $ hg rebase -b 5 -d 6
248 248 nothing to rebase
249 249 [1]
250 250
251 251 C onto A - rebase onto an ancestor:
252 252
253 253 $ hg rebase -d 0 -s 2
254 254 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg
255 255 $ hg tglog
256 256 @ 7: 'D'
257 257 |
258 258 o 6: 'C'
259 259 |
260 260 | o 5: 'H'
261 261 | |
262 262 | | o 4: 'G'
263 263 | |/|
264 264 | o | 3: 'F'
265 265 |/ /
266 266 | o 2: 'E'
267 267 |/
268 268 | o 1: 'B'
269 269 |/
270 270 o 0: 'A'
271 271
272 $ cd ..
272 273
274 Test for revset
275
276 We need a bit different graph
277 All destination are B
278
279 $ hg init ah
280 $ cd ah
281 $ hg unbundle $TESTDIR/bundles/rebase-revset.hg
282 adding changesets
283 adding manifests
284 adding file changes
285 added 9 changesets with 9 changes to 9 files (+2 heads)
286 (run 'hg heads' to see heads, 'hg merge' to merge)
287 $ hg tglog
288 o 8: 'I'
289 |
290 o 7: 'H'
291 |
292 o 6: 'G'
293 |
294 | o 5: 'F'
295 | |
296 | o 4: 'E'
297 |/
298 o 3: 'D'
299 |
300 o 2: 'C'
301 |
302 | o 1: 'B'
303 |/
304 o 0: 'A'
305
306 $ cd ..
307
308
309 Simple case with keep:
310
311 Source on have two descendant heads but ask for one
312
313 $ hg clone -q -u . ah ah1
314 $ cd ah1
315 $ hg rebase -r '2::8' -d 1
316 abort: can't remove original changesets with unrebased descendants
317 (use --keep to keep original changesets)
318 [255]
319 $ hg rebase -r '2::8' -d 1 --keep
320 $ hg tglog
321 @ 13: 'I'
322 |
323 o 12: 'H'
324 |
325 o 11: 'G'
326 |
327 o 10: 'D'
328 |
329 o 9: 'C'
330 |
331 | o 8: 'I'
332 | |
333 | o 7: 'H'
334 | |
335 | o 6: 'G'
336 | |
337 | | o 5: 'F'
338 | | |
339 | | o 4: 'E'
340 | |/
341 | o 3: 'D'
342 | |
343 | o 2: 'C'
344 | |
345 o | 1: 'B'
346 |/
347 o 0: 'A'
348
349
350 $ cd ..
351
352 Base on have one descendant heads we ask for but common ancestor have two
353
354 $ hg clone -q -u . ah ah2
355 $ cd ah2
356 $ hg rebase -r '3::8' -d 1
357 abort: can't remove original changesets with unrebased descendants
358 (use --keep to keep original changesets)
359 [255]
360 $ hg rebase -r '3::8' -d 1 --keep
361 $ hg tglog
362 @ 12: 'I'
363 |
364 o 11: 'H'
365 |
366 o 10: 'G'
367 |
368 o 9: 'D'
369 |\
370 | | o 8: 'I'
371 | | |
372 | | o 7: 'H'
373 | | |
374 | | o 6: 'G'
375 | | |
376 | | | o 5: 'F'
377 | | | |
378 | | | o 4: 'E'
379 | | |/
380 | | o 3: 'D'
381 | |/
382 | o 2: 'C'
383 | |
384 o | 1: 'B'
385 |/
386 o 0: 'A'
387
388
389 $ cd ..
390
391 rebase subset
392
393 $ hg clone -q -u . ah ah3
394 $ cd ah3
395 $ hg rebase -r '3::7' -d 1
396 abort: can't remove original changesets with unrebased descendants
397 (use --keep to keep original changesets)
398 [255]
399 $ hg rebase -r '3::7' -d 1 --keep
400 $ hg tglog
401 @ 11: 'H'
402 |
403 o 10: 'G'
404 |
405 o 9: 'D'
406 |\
407 | | o 8: 'I'
408 | | |
409 | | o 7: 'H'
410 | | |
411 | | o 6: 'G'
412 | | |
413 | | | o 5: 'F'
414 | | | |
415 | | | o 4: 'E'
416 | | |/
417 | | o 3: 'D'
418 | |/
419 | o 2: 'C'
420 | |
421 o | 1: 'B'
422 |/
423 o 0: 'A'
424
425
426 $ cd ..
427
428 rebase subset with multiple head
429
430 $ hg clone -q -u . ah ah4
431 $ cd ah4
432 $ hg rebase -r '3::(7+5)' -d 1
433 abort: can't remove original changesets with unrebased descendants
434 (use --keep to keep original changesets)
435 [255]
436 $ hg rebase -r '3::(7+5)' -d 1 --keep
437 $ hg tglog
438 @ 13: 'H'
439 |
440 o 12: 'G'
441 |
442 | o 11: 'F'
443 | |
444 | o 10: 'E'
445 |/
446 o 9: 'D'
447 |\
448 | | o 8: 'I'
449 | | |
450 | | o 7: 'H'
451 | | |
452 | | o 6: 'G'
453 | | |
454 | | | o 5: 'F'
455 | | | |
456 | | | o 4: 'E'
457 | | |/
458 | | o 3: 'D'
459 | |/
460 | o 2: 'C'
461 | |
462 o | 1: 'B'
463 |/
464 o 0: 'A'
465
466
467 $ cd ..
468
469 More advanced tests
470
471 rebase on ancestor with revset
472
473 $ hg clone -q -u . ah ah5
474 $ cd ah5
475 $ hg rebase -r '6::' -d 2
476 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg
477 $ hg tglog
478 @ 8: 'I'
479 |
480 o 7: 'H'
481 |
482 o 6: 'G'
483 |
484 | o 5: 'F'
485 | |
486 | o 4: 'E'
487 | |
488 | o 3: 'D'
489 |/
490 o 2: 'C'
491 |
492 | o 1: 'B'
493 |/
494 o 0: 'A'
495
496 $ cd ..
497
498
499 rebase with multiple root.
500 We rebase E and G on B
501 We would expect heads are I, F if it was supported
502
503 $ hg clone -q -u . ah ah6
504 $ cd ah6
505 $ hg rebase -r '(4+6)::' -d 1
506 abort: can't rebase multiple roots
507 [255]
508 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now