rebase: choose default destination the same way as 'hg merge' (BC)

This changeset finally make 'hg rebase' choose its default destination using the
same logic as 'hg merge'. The previous default was "tipmost changeset on the
current branch", the new default is "the other head if there is only one".  This
change has multiple consequences:

- Multiple tests which were not rebasing anything (rebasing from tipmost head)
  are now rebasing on the other "lower" branch. This is the expected new
  behavior.

- A test is now explicitly aborting when there is too many heads on the branch.
  This is the expected behavior.

- We gained a better detection of the "nothing to rebase" case while performing
  'hg pull --rebase' so the message have been updated. Making clearer than an
  update was performed and why. This is beneficial side-effect.

- Rebasing from an active bookmark will behave the same as 'hg merge' from a
  bookmark.
This commit is contained in:
Pierre-Yves David 2016-02-14 13:25:59 +00:00
parent 2f0e2c8635
commit b305efc185
6 changed files with 91 additions and 39 deletions

View File

@ -69,11 +69,12 @@ def _makeextrafn(copiers):
c(ctx, extra)
return extrafn
def _destrebase(repo):
# Destination defaults to the latest revision in the
# current branch
branch = repo[None].branch()
return repo[branch].rev()
def _destrebase(repo, sourceset):
"""small wrapper around destmerge to pass the right extra args
Please wrap destutil.destmerge instead."""
return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
onheadcheck=False)
revsetpredicate = revset.extpredicate()
@ -83,12 +84,12 @@ def _revsetdestrebase(repo, subset, x):
# default destination for rebase.
# # XXX: Currently private because I expect the signature to change.
# # XXX: - taking rev as arguments,
# # XXX: - bailing out in case of ambiguity vs returning all data.
# # XXX: - probably merging with the merge destination.
# i18n: "_rebasedefaultdest" is a keyword
revset.getargs(x, 0, 0, _("_rebasedefaultdest takes no arguments"))
return subset & revset.baseset([_destrebase(repo)])
sourceset = None
if x is not None:
sourceset = revset.getset(repo, revset.fullreposet(repo), x)
return subset & revset.baseset([_destrebase(repo, sourceset)])
@command('rebase',
[('s', 'source', '',
@ -127,10 +128,13 @@ def rebase(ui, repo, **opts):
Published commits cannot be rebased (see :hg:`help phases`).
To copy commits, see :hg:`help graft`.
If you don't specify a destination changeset (``-d/--dest``),
rebase uses the current branch tip as the destination. (The
destination changeset is not modified by rebasing, but new
changesets are added as its descendants.)
If you don't specify a destination changeset (``-d/--dest``), rebase
will use the same logic as :hg:`merge` to pick a destination. if
the current branch contains exactly one other head, the other head
is merged with by default. Otherwise, an explicit revision with
which to merge with must be provided. (destination changeset is not
modified by rebasing, but new changesets are added as its
descendants.)
Here are the ways to select changesets:
@ -544,9 +548,6 @@ def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
if destf:
dest = scmutil.revsingle(repo, destf)
else:
dest = repo[_destrebase(repo)]
destf = str(dest)
if revf:
rebaseset = scmutil.revrange(repo, revf)
@ -566,6 +567,10 @@ def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
ui.status(_('empty "base" revision set - '
"can't compute rebase set\n"))
return None, None
if not destf:
dest = repo[_destrebase(repo, base)]
destf = str(dest)
commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
if commonanc is not None:
rebaseset = repo.revs('(%d::(%ld) - %d)::',
@ -599,6 +604,11 @@ def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
ui.status(_('nothing to rebase from %s to %s\n') %
('+'.join(str(repo[r]) for r in base), dest))
return None, None
if not destf:
dest = repo[_destrebase(repo, rebaseset)]
destf = str(dest)
return dest, rebaseset
def externalparent(repo, state, targetancestors):
@ -1198,9 +1208,15 @@ def pullrebase(orig, ui, repo, *args, **opts):
# --source.
if 'source' in opts:
del opts['source']
if rebase(ui, repo, **opts) == _nothingtorebase():
try:
rebase(ui, repo, **opts)
except error.NoMergeDestAbort:
# we can maybe update instead
rev, _a, _b = destutil.destupdate(repo)
if rev != repo['.'].rev(): # we could update
if rev == repo['.'].rev():
ui.status(_('nothing to rebase\n'))
else:
ui.status(_('nothing to rebase - updating instead\n'))
# not passing argument to get the bare update behavior
# with warning and trumpets
commands.update(ui, repo)

View File

@ -141,6 +141,10 @@ msgdestmerge = {
(_("multiple matching bookmarks to merge -"
" please merge with an explicit rev or bookmark"),
_("run 'hg heads' to see all heads")),
'rebase':
(_("multiple matching bookmarks to rebase -"
" please rebase to an explicit rev or bookmark"),
_("run 'hg heads' to see all heads")),
},
# no other matching divergent bookmark
'nootherbookmarks':
@ -148,52 +152,80 @@ msgdestmerge = {
(_("no matching bookmark to merge - "
"please merge with an explicit rev or bookmark"),
_("run 'hg heads' to see all heads")),
'rebase':
(_("no matching bookmark to rebase - "
"please rebase to an explicit rev or bookmark"),
_("run 'hg heads' to see all heads")),
},
# branch have too many unbookmarked heads, no obvious destination
'toomanyheads':
{'merge':
(_("branch '%s' has %d heads - please merge with an explicit rev"),
_("run 'hg heads .' to see heads")),
'rebase':
(_("branch '%s' has %d heads - please rebase to an explicit rev"),
_("run 'hg heads .' to see heads")),
},
# branch have no other unbookmarked heads
'bookmarkedheads':
{'merge':
(_("heads are bookmarked - please merge with an explicit rev"),
_("run 'hg heads' to see all heads")),
'rebase':
(_("heads are bookmarked - please rebase to an explicit rev"),
_("run 'hg heads' to see all heads")),
},
# branch have just a single heads, but there is other branches
'nootherbranchheads':
{'merge':
(_("branch '%s' has one head - please merge with an explicit rev"),
_("run 'hg heads' to see all heads")),
'rebase':
(_("branch '%s' has one head - please rebase to an explicit rev"),
_("run 'hg heads' to see all heads")),
},
# repository have a single head
'nootherheads':
{'merge':
(_('nothing to merge'),
None),
{'merge':
(_('nothing to merge'),
None),
'rebase':
(_('nothing to rebase'),
None),
},
# repository have a single head and we are not on it
'nootherheadsbehind':
{'merge':
(_('nothing to merge'),
_("use 'hg update' instead")),
'rebase':
(_('nothing to rebase'),
_("use 'hg update' instead")),
},
# We are not on a head
'notatheads':
{'merge':
(_('working directory not at a head revision'),
_("use 'hg update' or merge with an explicit revision"))
_("use 'hg update' or merge with an explicit revision")),
'rebase':
(_('working directory not at a head revision'),
_("use 'hg update' or rebase to an explicit revision"))
},
'emptysourceset':
{'merge':
(_('source set is empty'),
None)
None),
'rebase':
(_('source set is empty'),
None),
},
'multiplebranchessourceset':
{'merge':
(_('source set is rooted in multiple branches'),
None)
None),
'rebase':
(_('rebaseset is rooted in multiple named branches'),
_('specify an explicit destination with --dest')),
},
}

View File

@ -1095,7 +1095,7 @@ Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
nothing to rebase - updating instead
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ..

View File

@ -327,14 +327,13 @@ rebase 'b2' to another lower branch head
$ hg up -qr 2
$ hg rebase
nothing to rebase - working directory parent is also destination
[1]
rebasing 2:792845bb77ee "b2"
note: rebase of 2:792845bb77ee created no changes to commit
saved backup bundle to $TESTTMP/case1/.hg/strip-backup/792845bb77ee-627120ee-backup.hg (glob)
$ hg tglog
o 3: 'c1' c
o 2: 'c1' c
|
| @ 2: 'b2' b
|/
| o 1: 'b1' b
| @ 1: 'b1' b
|/
o 0: '0'
@ -373,8 +372,9 @@ rebase 'c1' to the branch head 'c2' that is closed
o 0: '0'
$ hg rebase
nothing to rebase - working directory parent is also destination
[1]
abort: branch 'c' has one head - please rebase to an explicit rev
(run 'hg heads' to see all heads)
[255]
$ hg tglog
_ 4: 'c2 closed' c
|

View File

@ -85,7 +85,7 @@ Invoke pull --rebase and nothing to rebase:
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
nothing to rebase - working directory parent is already an ancestor of destination 77ae9631bcca
nothing to rebase - updating instead
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
updating bookmark norebase
@ -285,7 +285,7 @@ pull --rebase update (no rebase) use proper update:
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
nothing to rebase - working directory parent is already an ancestor of destination 65bc164c1d9b
nothing to rebase - updating instead
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1 other heads for branch "default"
$ hg tglog

View File

@ -818,13 +818,17 @@ Testing from lower head
Testing from upper head
$ hg log -r '_destrebase(4)'
changeset: 3:1910d5ff34ea
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: second source with subdir
$ hg up 4
1 files updated, 0 files merged, 2 files removed, 0 files unresolved
$ hg log -r '_destrebase()'
changeset: 4:5f7bc9025ed2
tag: tip
parent: 1:58d79cc1cf43
changeset: 3:1910d5ff34ea
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: aaa
summary: second source with subdir