comparison hgext/rebase.py @ 28189:fac3a24be50e

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.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Sun, 14 Feb 2016 13:25:59 +0000
parents 6411140aeda9
children dc6032a1d888
comparison
equal deleted inserted replaced
28188:6411140aeda9 28189:fac3a24be50e
67 def extrafn(ctx, extra): 67 def extrafn(ctx, extra):
68 for c in copiers: 68 for c in copiers:
69 c(ctx, extra) 69 c(ctx, extra)
70 return extrafn 70 return extrafn
71 71
72 def _destrebase(repo): 72 def _destrebase(repo, sourceset):
73 # Destination defaults to the latest revision in the 73 """small wrapper around destmerge to pass the right extra args
74 # current branch 74
75 branch = repo[None].branch() 75 Please wrap destutil.destmerge instead."""
76 return repo[branch].rev() 76 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
77 onheadcheck=False)
77 78
78 revsetpredicate = revset.extpredicate() 79 revsetpredicate = revset.extpredicate()
79 80
80 @revsetpredicate('_destrebase') 81 @revsetpredicate('_destrebase')
81 def _revsetdestrebase(repo, subset, x): 82 def _revsetdestrebase(repo, subset, x):
82 # ``_rebasedefaultdest()`` 83 # ``_rebasedefaultdest()``
83 84
84 # default destination for rebase. 85 # default destination for rebase.
85 # # XXX: Currently private because I expect the signature to change. 86 # # XXX: Currently private because I expect the signature to change.
86 # # XXX: - taking rev as arguments,
87 # # XXX: - bailing out in case of ambiguity vs returning all data. 87 # # XXX: - bailing out in case of ambiguity vs returning all data.
88 # # XXX: - probably merging with the merge destination.
89 # i18n: "_rebasedefaultdest" is a keyword 88 # i18n: "_rebasedefaultdest" is a keyword
90 revset.getargs(x, 0, 0, _("_rebasedefaultdest takes no arguments")) 89 sourceset = None
91 return subset & revset.baseset([_destrebase(repo)]) 90 if x is not None:
91 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
92 return subset & revset.baseset([_destrebase(repo, sourceset)])
92 93
93 @command('rebase', 94 @command('rebase',
94 [('s', 'source', '', 95 [('s', 'source', '',
95 _('rebase the specified changeset and descendants'), _('REV')), 96 _('rebase the specified changeset and descendants'), _('REV')),
96 ('b', 'base', '', 97 ('b', 'base', '',
125 development tree. 126 development tree.
126 127
127 Published commits cannot be rebased (see :hg:`help phases`). 128 Published commits cannot be rebased (see :hg:`help phases`).
128 To copy commits, see :hg:`help graft`. 129 To copy commits, see :hg:`help graft`.
129 130
130 If you don't specify a destination changeset (``-d/--dest``), 131 If you don't specify a destination changeset (``-d/--dest``), rebase
131 rebase uses the current branch tip as the destination. (The 132 will use the same logic as :hg:`merge` to pick a destination. if
132 destination changeset is not modified by rebasing, but new 133 the current branch contains exactly one other head, the other head
133 changesets are added as its descendants.) 134 is merged with by default. Otherwise, an explicit revision with
135 which to merge with must be provided. (destination changeset is not
136 modified by rebasing, but new changesets are added as its
137 descendants.)
134 138
135 Here are the ways to select changesets: 139 Here are the ways to select changesets:
136 140
137 1. Explicitly select them using ``--rev``. 141 1. Explicitly select them using ``--rev``.
138 142
542 cmdutil.checkunfinished(repo) 546 cmdutil.checkunfinished(repo)
543 cmdutil.bailifchanged(repo) 547 cmdutil.bailifchanged(repo)
544 548
545 if destf: 549 if destf:
546 dest = scmutil.revsingle(repo, destf) 550 dest = scmutil.revsingle(repo, destf)
547 else:
548 dest = repo[_destrebase(repo)]
549 destf = str(dest)
550 551
551 if revf: 552 if revf:
552 rebaseset = scmutil.revrange(repo, revf) 553 rebaseset = scmutil.revrange(repo, revf)
553 if not rebaseset: 554 if not rebaseset:
554 ui.status(_('empty "rev" revision set - nothing to rebase\n')) 555 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
564 base = scmutil.revrange(repo, [basef or '.']) 565 base = scmutil.revrange(repo, [basef or '.'])
565 if not base: 566 if not base:
566 ui.status(_('empty "base" revision set - ' 567 ui.status(_('empty "base" revision set - '
567 "can't compute rebase set\n")) 568 "can't compute rebase set\n"))
568 return None, None 569 return None, None
570 if not destf:
571 dest = repo[_destrebase(repo, base)]
572 destf = str(dest)
573
569 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first() 574 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
570 if commonanc is not None: 575 if commonanc is not None:
571 rebaseset = repo.revs('(%d::(%ld) - %d)::', 576 rebaseset = repo.revs('(%d::(%ld) - %d)::',
572 commonanc, base, commonanc) 577 commonanc, base, commonanc)
573 else: 578 else:
597 'ancestor of destination %s\n') % dest) 602 'ancestor of destination %s\n') % dest)
598 else: # can it happen? 603 else: # can it happen?
599 ui.status(_('nothing to rebase from %s to %s\n') % 604 ui.status(_('nothing to rebase from %s to %s\n') %
600 ('+'.join(str(repo[r]) for r in base), dest)) 605 ('+'.join(str(repo[r]) for r in base), dest))
601 return None, None 606 return None, None
607
608 if not destf:
609 dest = repo[_destrebase(repo, rebaseset)]
610 destf = str(dest)
611
602 return dest, rebaseset 612 return dest, rebaseset
603 613
604 def externalparent(repo, state, targetancestors): 614 def externalparent(repo, state, targetancestors):
605 """Return the revision that should be used as the second parent 615 """Return the revision that should be used as the second parent
606 when the revisions in state is collapsed on top of targetancestors. 616 when the revisions in state is collapsed on top of targetancestors.
1196 del opts['rev'] 1206 del opts['rev']
1197 # positional argument from pull conflicts with rebase's own 1207 # positional argument from pull conflicts with rebase's own
1198 # --source. 1208 # --source.
1199 if 'source' in opts: 1209 if 'source' in opts:
1200 del opts['source'] 1210 del opts['source']
1201 if rebase(ui, repo, **opts) == _nothingtorebase(): 1211 try:
1212 rebase(ui, repo, **opts)
1213 except error.NoMergeDestAbort:
1214 # we can maybe update instead
1202 rev, _a, _b = destutil.destupdate(repo) 1215 rev, _a, _b = destutil.destupdate(repo)
1203 if rev != repo['.'].rev(): # we could update 1216 if rev == repo['.'].rev():
1217 ui.status(_('nothing to rebase\n'))
1218 else:
1219 ui.status(_('nothing to rebase - updating instead\n'))
1204 # not passing argument to get the bare update behavior 1220 # not passing argument to get the bare update behavior
1205 # with warning and trumpets 1221 # with warning and trumpets
1206 commands.update(ui, repo) 1222 commands.update(ui, repo)
1207 finally: 1223 finally:
1208 release(lock, wlock) 1224 release(lock, wlock)