Mercurial > public > mercurial-scm > hg-stable
diff hgext/rebase.py @ 34023:5e83a8fe6bc4
rebase: initial support for multiple destinations
This patch defines `SRC` (a single source revision) and `ALLSRC` (all source
revisions) to be valid names in `--dest` revset if `--src` or `--rev` is
used. So destination could be defined differently according to source
revisions. The names are capitalized to make it clear they are "dynamically
defined", distinguishable from normal revsets (Thanks Augie for the
suggestion).
This is useful, for example, `-r 'orphan()' -d 'calc-dest(SRC)'` to solve
instability, which seems to be a highly wanted feature.
The feature is not completed, namely if `-d` overlaps with `-r`, things
could go wrong. A later patch will handle that case.
The feature is also gated by `experimental.rebase.multidest` config option
which is default off.
Differential Revision: https://phab.mercurial-scm.org/D469
author | Jun Wu <quark@fb.com> |
---|---|
date | Tue, 29 Aug 2017 17:27:37 -0700 |
parents | af609bb3487f |
children | 32528419db64 |
line wrap: on
line diff
--- a/hgext/rebase.py Fri Aug 11 00:32:19 2017 -0700 +++ b/hgext/rebase.py Tue Aug 29 17:27:37 2017 -0700 @@ -46,6 +46,7 @@ repair, repoview, revset, + revsetlang, scmutil, smartset, util, @@ -736,8 +737,7 @@ raise error.Abort(_('you must specify a destination'), hint=_('use: hg rebase -d REV')) - if destf: - dest = scmutil.revsingle(repo, destf) + dest = None if revf: rebaseset = scmutil.revrange(repo, revf) @@ -757,7 +757,10 @@ ui.status(_('empty "base" revision set - ' "can't compute rebase set\n")) return None - if not destf: + if destf: + # --base does not support multiple destinations + dest = scmutil.revsingle(repo, destf) + else: dest = repo[_destrebase(repo, base, destspace=destspace)] destf = str(dest) @@ -806,9 +809,40 @@ dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] destf = str(dest) - # assign dest to each rev in rebaseset - destrev = dest.rev() - destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} + allsrc = revsetlang.formatspec('%ld', rebaseset) + alias = {'ALLSRC': allsrc} + + if dest is None: + try: + # fast path: try to resolve dest without SRC alias + dest = scmutil.revsingle(repo, destf, localalias=alias) + except error.RepoLookupError: + if not ui.configbool('experimental', 'rebase.multidest'): + raise + # multi-dest path: resolve dest for each SRC separately + destmap = {} + for r in rebaseset: + alias['SRC'] = revsetlang.formatspec('%d', r) + # use repo.anyrevs instead of scmutil.revsingle because we + # don't want to abort if destset is empty. + destset = repo.anyrevs([destf], user=True, localalias=alias) + size = len(destset) + if size == 1: + destmap[r] = destset.first() + elif size == 0: + ui.note(_('skipping %s - empty destination\n') % repo[r]) + else: + raise error.Abort(_('rebase destination for %s is not ' + 'unique') % repo[r]) + + if dest is not None: + # single-dest case: assign dest to each rev in rebaseset + destrev = dest.rev() + destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} + + if not destmap: + ui.status(_('nothing to rebase - empty destination\n')) + return None return destmap @@ -903,8 +937,8 @@ adjusted destinations for rev's p1 and p2, respectively. If a parent is nullrev, return dest without adjustment for it. - For example, when doing rebase -r B+E -d F, rebase will first move B to B1, - and E's destination will be adjusted from F to B1. + For example, when doing rebasing B+E to F, C to G, rebase will first move B + to B1, and E's destination will be adjusted from F to B1. B1 <- written during rebasing B | @@ -916,11 +950,11 @@ | | | x <- skipped, ex. no successor or successor in (::dest) | | - | C + | C <- rebased as C', different destination | | - | B <- rebased as B1 - |/ - A + | B <- rebased as B1 C' + |/ | + A G <- destination of C, different Another example about merge changeset, rebase -r C+G+H -d K, rebase will first move C to C1, G to G1, and when it's checking H, the adjusted