diff --git a/hgext/rebase.py b/hgext/rebase.py --- a/hgext/rebase.py +++ b/hgext/rebase.py @@ -48,8 +48,7 @@ testedwith = 'internal' _('read collapse commit message from file'), _('FILE')), ('', 'keep', False, _('keep original changesets')), ('', 'keepbranches', False, _('keep original branch names')), - ('D', 'detach', False, _('force detaching of source from its original ' - 'branch')), + ('D', 'detach', False, _('(DEPRECATED)')), ('t', 'tool', '', _('specify merge tool')), ('c', 'continue', False, _('continue an interrupted rebase')), ('a', 'abort', False, _('abort an interrupted rebase'))] + @@ -131,7 +130,6 @@ def rebase(ui, repo, **opts): extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion keepf = opts.get('keep', False) keepbranchesf = opts.get('keepbranches', False) - detachf = opts.get('detach', False) # keepopen is not meant for use on the command line, but by # other extensions keepopen = opts.get('keepopen', False) @@ -146,8 +144,6 @@ def rebase(ui, repo, **opts): if collapsef: raise util.Abort( _('cannot use collapse with continue or abort')) - if detachf: - raise util.Abort(_('cannot use detach with continue or abort')) if srcf or basef or destf: raise util.Abort( _('abort and continue do not allow specifying revisions')) @@ -168,12 +164,6 @@ def rebase(ui, repo, **opts): if revf and srcf: raise util.Abort(_('cannot specify both a ' 'revision and a source')) - if detachf: - if not (srcf or revf): - raise util.Abort( - _('detach requires a revision to be specified')) - if basef: - raise util.Abort(_('cannot specify a base with detach')) cmdutil.bailifchanged(repo) @@ -215,7 +205,7 @@ def rebase(ui, repo, **opts): % repo[root], hint=_('see hg help phases for details')) else: - result = buildstate(repo, dest, rebaseset, detachf, collapsef) + result = buildstate(repo, dest, rebaseset, collapsef) if not result: # Empty state built, nothing to rebase @@ -592,13 +582,13 @@ def abort(repo, originalwd, target, stat repo.ui.warn(_('rebase aborted\n')) return 0 -def buildstate(repo, dest, rebaseset, detach, collapse): +def buildstate(repo, dest, rebaseset, collapse): '''Define which revisions are going to be rebased and where repo: repo dest: context rebaseset: set of rev - detach: boolean''' + ''' # This check isn't strictly necessary, since mq detects commits over an # applied patch. But it prevents messing up the working directory when @@ -607,7 +597,6 @@ def buildstate(repo, dest, rebaseset, de [s.node for s in repo.mq.applied]): raise util.Abort(_('cannot rebase onto an applied mq patch')) - detachset = set() roots = list(repo.set('roots(%ld)', rebaseset)) if not roots: raise util.Abort(_('no matching revisions')) @@ -623,14 +612,50 @@ def buildstate(repo, dest, rebaseset, de if not collapse and samebranch and root in dest.children(): repo.ui.debug('source is a child of destination\n') return None - # rebase on ancestor, force detach - detach = True - if detach: - detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root)) state = dict.fromkeys(rebaseset, nullrev) - state.update(dict.fromkeys(detachset, nullmerge)) + # Rebase tries to turn into a parent of while + # preserving the number of parents of rebased changesets: + # + # - A changeset with a single parent will always be rebased as a + # changeset with a single parent. + # + # - A merge will be rebased as merge unless its parents are both + # ancestors of or are themselves in the rebased set and + # pruned while rebased. + # + # If one parent of is an ancestor of , the rebased + # version of this parent will be . This is always true with + # --base option. + # + # Otherwise, we need to *replace* the original parents with + # . This "detaches" the rebased set from its former location + # and rebases it onto . Changes introduced by ancestors of + # not common with (the detachset, marked as + # nullmerge) are "removed" from the rebased changesets. + # + # - If has a single parent, set it to . + # + # - If is a merge, we cannot decide which parent to + # replace, the rebase operation is not clearly defined. + # + # The table below sums up this behavior: + # + # +--------------------+----------------------+-------------------------+ + # | | one parent | merge | + # +--------------------+----------------------+-------------------------+ + # | parent in :: | new parent is | parents in :: are | + # | | | remapped to | + # +--------------------+----------------------+-------------------------+ + # | unrelated source | new parent is | ambiguous, abort | + # +--------------------+----------------------+-------------------------+ + # + # The actual abort is handled by `defineparents` + if len(root.parents()) <= 1: + # (strict) ancestors of not ancestors of + detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) + state.update(dict.fromkeys(detachset, nullmerge)) return repo['.'].rev(), dest.rev(), state def pullrebase(orig, ui, repo, *args, **opts): diff --git a/tests/test-bookmarks-rebase.t b/tests/test-bookmarks-rebase.t --- a/tests/test-bookmarks-rebase.t +++ b/tests/test-bookmarks-rebase.t @@ -39,11 +39,10 @@ rebase saved backup bundle to $TESTTMP/.hg/strip-backup/*-backup.hg (glob) $ hg log - changeset: 3:9163974d1cb5 + changeset: 3:42e5ed2cdcf4 bookmark: two tag: tip parent: 1:925d80f479bb - parent: 2:db815d6d32e6 user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3 diff --git a/tests/test-rebase-bookmarks.t b/tests/test-rebase-bookmarks.t --- a/tests/test-rebase-bookmarks.t +++ b/tests/test-rebase-bookmarks.t @@ -54,7 +54,7 @@ Move only rebased bookmarks $ cd a1 $ hg up -q Z - $ hg rebase --detach -s Y -d 3 + $ hg rebase -s Y -d 3 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob) $ hg tglog diff --git a/tests/test-rebase-cache.t b/tests/test-rebase-cache.t --- a/tests/test-rebase-cache.t +++ b/tests/test-rebase-cache.t @@ -104,7 +104,7 @@ Rebase part of branch2 (5-6) onto branch 2: 'B' branch1 0: 'A' - $ hg rebase --detach -s 5 -d 8 + $ hg rebase -s 5 -d 8 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob) $ hg branches @@ -165,7 +165,7 @@ Rebase head of branch3 (8) onto branch2 |/ o 0: 'A' - $ hg rebase --detach -s 8 -d 6 + $ hg rebase -s 8 -d 6 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob) $ hg branches @@ -229,7 +229,7 @@ Rebase entire branch3 (7-8) onto branch2 |/ o 0: 'A' - $ hg rebase --detach -s 7 -d 6 + $ hg rebase -s 7 -d 6 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob) $ hg branches diff --git a/tests/test-rebase-collapse.t b/tests/test-rebase-collapse.t --- a/tests/test-rebase-collapse.t +++ b/tests/test-rebase-collapse.t @@ -230,7 +230,7 @@ Rebase and collapse - more than one exte Rebase and collapse - E onto H: - $ hg rebase -s 4 --collapse + $ hg rebase -s 4 --collapse # root (4) is not a merge saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -250,7 +250,6 @@ Rebase and collapse - E onto H: $ hg manifest A - B C D E @@ -340,7 +339,7 @@ Rebase and collapse - E onto I: $ hg clone -q -u . c c1 $ cd c1 - $ hg rebase -s 4 --collapse + $ hg rebase -s 4 --collapse # root (4) is not a merge merging E saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob) @@ -362,7 +361,6 @@ Rebase and collapse - E onto I: $ hg manifest A - B C D E diff --git a/tests/test-rebase-detach.t b/tests/test-rebase-detach.t --- a/tests/test-rebase-detach.t +++ b/tests/test-rebase-detach.t @@ -48,7 +48,7 @@ Rebasing D onto H detaching from C: o 0: 'A' $ hg phase --force --secret 3 - $ hg rebase --detach -s 3 -d 7 + $ hg rebase -s 3 -d 7 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob) $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n" @@ -99,7 +99,7 @@ Rebasing C onto H detaching from B: |/ o 0: 'A' - $ hg rebase --detach -s 2 -d 7 + $ hg rebase -s 2 -d 7 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -151,7 +151,7 @@ Rebasing B onto H using detach (same as |/ o 0: 'A' - $ hg rebase --detach -s 1 -d 7 + $ hg rebase -s 1 -d 7 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -205,7 +205,7 @@ Rebasing C onto H detaching from B and c |/ o 0: 'A' - $ hg rebase --detach --collapse -s 2 -d 7 + $ hg rebase --collapse -s 2 -d 7 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob) $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n" @@ -264,7 +264,7 @@ Rebasing across null as ancestor |/ o 0: 'A' - $ hg rebase --detach -s 1 -d tip + $ hg rebase -s 1 -d tip saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -325,7 +325,7 @@ Verify that target is not selected as ex $ echo "J" >> F $ hg ci -m "J" - $ hg rebase -s 8 -d 7 --collapse --detach --config ui.merge=internal:other + $ hg rebase -s 8 -d 7 --collapse --config ui.merge=internal:other remote changed E which local deleted use (c)hanged version or leave (d)eleted? c saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob) @@ -370,7 +370,7 @@ Ensure --continue restores a correct sta $ hg ci -A -m 'H2' adding H $ hg phase --force --secret 8 - $ hg rebase -s 8 -d 7 --detach --config ui.merge=internal:fail + $ hg rebase -s 8 -d 7 --config ui.merge=internal:fail merging H warning: conflicts during merge. merging H incomplete! (edit conflicts, then use 'hg resolve --mark') diff --git a/tests/test-rebase-parameters.t b/tests/test-rebase-parameters.t --- a/tests/test-rebase-parameters.t +++ b/tests/test-rebase-parameters.t @@ -199,19 +199,19 @@ Specify only source (from 2 onto 8): $ hg tglog @ 8: 'D' | - o 7: 'C' - |\ - | o 6: 'I' - | | - | o 5: 'H' + o 7: 'C' + | + o 6: 'I' + | + o 5: 'H' + | + | o 4: 'G' + |/| + o | 3: 'F' | | - | | o 4: 'G' - | |/| - | o | 3: 'F' - | | | - | | o 2: 'E' - | |/ - o | 1: 'B' + | o 2: 'E' + |/ + | o 1: 'B' |/ o 0: 'A' @@ -283,7 +283,7 @@ Specify source and dest (from 2 onto 7): $ hg clone -q -u . a a7 $ cd a7 - $ hg rebase --detach --source 2 --dest 7 + $ hg rebase --source 2 --dest 7 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -349,19 +349,19 @@ Specify only revs (from 2 onto 8) $ hg tglog @ 8: 'D' | - o 7: 'C' - |\ - | o 6: 'I' - | | - | o 5: 'H' + o 7: 'C' + | + o 6: 'I' + | + o 5: 'H' + | + | o 4: 'G' + |/| + o | 3: 'F' | | - | | o 4: 'G' - | |/| - | o | 3: 'F' - | | | - | | o 2: 'E' - | |/ - o | 1: 'B' + | o 2: 'E' + |/ + | o 1: 'B' |/ o 0: 'A' diff --git a/tests/test-rebase-scenario-global.t b/tests/test-rebase-scenario-global.t --- a/tests/test-rebase-scenario-global.t +++ b/tests/test-rebase-scenario-global.t @@ -52,19 +52,19 @@ D onto H - simple rebase: saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob) $ hg tglog - @ 7: 'D' - |\ - | o 6: 'H' + @ 7: 'D' + | + o 6: 'H' + | + | o 5: 'G' + |/| + o | 4: 'F' | | - | | o 5: 'G' - | |/| - | o | 4: 'F' - | | | - | | o 3: 'E' - | |/ - o | 2: 'C' + | o 3: 'E' + |/ + | o 2: 'C' | | - o | 1: 'B' + | o 1: 'B' |/ o 0: 'A' @@ -80,19 +80,19 @@ D onto F - intermediate point: saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob) $ hg tglog - @ 7: 'D' - |\ - | | o 6: 'H' - | |/ - | | o 5: 'G' - | |/| - | o | 4: 'F' - | | | - | | o 3: 'E' - | |/ - o | 2: 'C' + @ 7: 'D' + | + | o 6: 'H' + |/ + | o 5: 'G' + |/| + o | 4: 'F' | | - o | 1: 'B' + | o 3: 'E' + |/ + | o 2: 'C' + | | + | o 1: 'B' |/ o 0: 'A' @@ -303,9 +303,9 @@ Source phase greater or equal to destina $ hg log --template "{phase}\n" -r 9 secret Source phase lower than destination phase: new changeset get the phase of destination: - $ hg rebase -s7 -d9 - saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c9659aac0000-backup.hg (glob) - $ hg log --template "{phase}\n" -r 9 + $ hg rebase -s8 -d9 + saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg + $ hg log --template "{phase}\n" -r 'rev(9)' secret $ cd .. @@ -404,20 +404,20 @@ Base on have one descendant heads we ask | o 10: 'G' | - o 9: 'D' - |\ - | | o 8: 'I' - | | | - | | o 7: 'H' + o 9: 'D' + | + | o 8: 'I' + | | + | o 7: 'H' + | | + | o 6: 'G' + | | + | | o 5: 'F' | | | - | | o 6: 'G' - | | | - | | | o 5: 'F' - | | | | - | | | o 4: 'E' - | | |/ - | | o 3: 'D' + | | o 4: 'E' | |/ + | o 3: 'D' + | | | o 2: 'C' | | o | 1: 'B' @@ -441,20 +441,20 @@ rebase subset | o 10: 'G' | - o 9: 'D' - |\ - | | o 8: 'I' - | | | - | | o 7: 'H' + o 9: 'D' + | + | o 8: 'I' + | | + | o 7: 'H' + | | + | o 6: 'G' + | | + | | o 5: 'F' | | | - | | o 6: 'G' - | | | - | | | o 5: 'F' - | | | | - | | | o 4: 'E' - | | |/ - | | o 3: 'D' + | | o 4: 'E' | |/ + | o 3: 'D' + | | | o 2: 'C' | | o | 1: 'B' @@ -482,20 +482,20 @@ rebase subset with multiple head | | | o 10: 'E' |/ - o 9: 'D' - |\ - | | o 8: 'I' - | | | - | | o 7: 'H' + o 9: 'D' + | + | o 8: 'I' + | | + | o 7: 'H' + | | + | o 6: 'G' + | | + | | o 5: 'F' | | | - | | o 6: 'G' - | | | - | | | o 5: 'F' - | | | | - | | | o 4: 'E' - | | |/ - | | o 3: 'D' + | | o 4: 'E' | |/ + | o 3: 'D' + | | | o 2: 'C' | | o | 1: 'B'