D2668: rebase: introduce support for automatically rebasing orphan changes

durin42 (Augie Fackler) phabricator at mercurial-scm.org
Wed Apr 18 15:30:50 EDT 2018


This revision was automatically updated to reflect the committed changes.
Closed by commit rHG92213f6745ed: rebase: introduce support for automatically rebasing orphan changes (authored by durin42, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2668?vs=8390&id=8392

REVISION DETAIL
  https://phab.mercurial-scm.org/D2668

AFFECTED FILES
  hgext/rebase.py
  mercurial/destutil.py
  tests/test-rebase-obsolete.t

CHANGE DETAILS

diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -482,7 +482,34 @@
   |/
   o  0:cd010b8cd998 A
   
+  $ cd ..
+  $ cp -R hidden stabilize
+  $ cd stabilize
+  $ hg rebase --auto-orphans '0::' -d 10
+  abort: --auto-orphans is incompatible with --dest
+  [255]
+  $ hg rebase --auto-orphans '0::'
+  rebasing 9:cf44d2f5a9f4 "D"
+  $ hg log -G
+  o  12:7e3935feaa68 D
+  |
+  o  11:0d8f238b634c C
+  |
+  o  10:7c6027df6a99 B
+  |
+  @  7:02de42196ebe H
+  |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
 
+  $ cd ../hidden
+  $ rm -r ../stabilize
 
 Test multiple root handling
 ------------------------------------
diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -16,6 +16,39 @@
     stack
 )
 
+def orphanpossibledestination(repo, rev):
+    """Return all changesets that may be a new parent for orphan `rev`.
+
+    This function works fine on non-orphan revisions, it's just silly
+    because there's no destination implied by obsolete markers, so
+    it'll return nothing.
+    """
+    tonode = repo.changelog.node
+    parents = repo.changelog.parentrevs
+    torev = repo.changelog.rev
+    dest = set()
+    tovisit = list(parents(rev))
+    while tovisit:
+        r = tovisit.pop()
+        succsets = obsutil.successorssets(repo, tonode(r))
+        if not succsets:
+            # if there are no successors for r, r was probably pruned
+            # and we should walk up to r's parents to try and find
+            # some successors.
+            tovisit.extend(parents(r))
+        else:
+            # We should probably pick only one destination from split
+            # (case where '1 < len(ss)'), This could be the currently
+            # tipmost, but the correct result is less clear when
+            # results of the split have been moved such that they
+            # reside on multiple branches.
+            for ss in succsets:
+                for n in ss:
+                    dr = torev(n)
+                    if dr != -1:
+                        dest.add(dr)
+    return dest
+
 def _destupdateobs(repo, clean):
     """decide of an update destination from obsolescence markers"""
     node = None
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -108,6 +108,25 @@
         sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
     return subset & smartset.baseset([_destrebase(repo, sourceset)])
 
+ at revsetpredicate('_destautoorphanrebase')
+def _revsetdestautoorphanrebase(repo, subset, x):
+    """automatic rebase destination for a single orphan revision"""
+    unfi = repo.unfiltered()
+    obsoleted = unfi.revs('obsolete()')
+
+    src = revset.getset(repo, subset, x).first()
+
+    # Empty src or already obsoleted - Do not return a destination
+    if not src or src in obsoleted:
+        return smartset.baseset()
+    dests = destutil.orphanpossibledestination(repo, src)
+    if len(dests) > 1:
+        raise error.Abort(
+            _("ambiguous automatic rebase: %r could end up on any of %r") % (
+                src, dests))
+    # We have zero or one destination, so we can just return here.
+    return smartset.baseset(dests)
+
 def _ctxdesc(ctx):
     """short description for a context"""
     desc = '%d:%s "%s"' % (ctx.rev(), ctx,
@@ -651,7 +670,10 @@
     ('i', 'interactive', False, _('(DEPRECATED)')),
     ('t', 'tool', '', _('specify merge tool')),
     ('c', 'continue', False, _('continue an interrupted rebase')),
-    ('a', 'abort', False, _('abort an interrupted rebase'))] +
+    ('a', 'abort', False, _('abort an interrupted rebase')),
+    ('', 'auto-orphans', '', _('automatically rebase orphan revisions '
+                               'in the specified revset (EXPERIMENTAL)')),
+     ] +
     cmdutil.formatteropts,
     _('[-s REV | -b REV] [-d REV] [OPTION]'))
 def rebase(ui, repo, **opts):
@@ -783,6 +805,15 @@
         # fail the entire transaction.)
         inmemory = False
 
+    if opts.get('auto_orphans'):
+        for key in opts:
+            if key != 'auto_orphans' and opts.get(key):
+                raise error.Abort(_('--auto-orphans is incompatible with %s') %
+                                  ('--' + key))
+        userrevs = list(repo.revs(opts.get('auto_orphans')))
+        opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)]
+        opts['dest'] = '_destautoorphanrebase(SRC)'
+
     if inmemory:
         try:
             # in-memory merge doesn't support conflicts, so if we hit any, abort



To: durin42, #hg-reviewers, indygreg, krbullock
Cc: krbullock, mbthomas, spectral, quark, indygreg, mercurial-devel


More information about the Mercurial-devel mailing list