rebase: filter out descendants of divergence-causing commits earlier
authorMartin von Zweigbergk <martinvonz@google.com>
Fri, 19 Mar 2021 22:52:59 -0700
changeset 46834 535de0e34a79
parent 46833 47c251a14525
child 46835 c2438f2f635c
rebase: filter out descendants of divergence-causing commits earlier `hg rebase` treats obsolete commits differently depending what has happened to the commit: 1) Obsolete commit without non-obsolete successors: Skipped, and a note is printed ("it has no successor"). 2) Obsolete commit with a successor in the destination (ancestor of it): Skipped, and a note is printed ("already in destination"). 3) Obsolete commit with a successor in the rebase set: The commit and its descendants are skipped, and a note is printed ("not rebasing <commit> and its descendants as this would cause divergence"), unless `allowdivergence` config set. 4) Obsolete commit with a successor elsewhere: Error ("this rebase will cause divergences"), unless `allowdivergence` config set. Before this patch, we did all those checks up front, except for (3), which was checked later. The later check consisted of two parts: 1) filtering out of descendants, and 2) conditionally printing message if the `allowdivergence` config was not set. This patch makes it so we do the filtering early. A consequence of filtering out divergence-causing commits earlier is that we rebase commits in slightly different order, which has some impact on tests. Differential Revision: https://phab.mercurial-scm.org/D10249
hgext/rebase.py
tests/test-rebase-obsolete3.t
--- a/hgext/rebase.py	Fri Mar 19 10:34:16 2021 -0700
+++ b/hgext/rebase.py	Fri Mar 19 22:52:59 2021 -0700
@@ -361,6 +361,19 @@
         skippedset = set(self.obsolete_with_successor_in_destination)
         skippedset.update(self.obsolete_with_successor_in_rebase_set)
         _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
+        allowdivergence = self.ui.configbool(
+            b'experimental', b'evolution.allowdivergence'
+        )
+        if allowdivergence:
+            self.obsolete_with_successor_in_rebase_set = set()
+        else:
+            for rev in self.repo.revs(
+                b'descendants(%ld) and not %ld',
+                self.obsolete_with_successor_in_rebase_set,
+                self.obsolete_with_successor_in_rebase_set,
+            ):
+                self.state.pop(rev, None)
+                self.destmap.pop(rev, None)
 
     def _prepareabortorcontinue(
         self, isabort, backup=True, suppwarns=False, dryrun=False, confirm=False
@@ -493,19 +506,10 @@
         def progress(ctx):
             p.increment(item=(b"%d:%s" % (ctx.rev(), ctx)))
 
-        allowdivergence = self.ui.configbool(
-            b'experimental', b'evolution.allowdivergence'
-        )
         for subset in sortsource(self.destmap):
             sortedrevs = self.repo.revs(b'sort(%ld, -topo)', subset)
-            if not allowdivergence:
-                sortedrevs -= self.repo.revs(
-                    b'descendants(%ld) and not %ld',
-                    self.obsolete_with_successor_in_rebase_set,
-                    self.obsolete_with_successor_in_rebase_set,
-                )
             for rev in sortedrevs:
-                self._rebasenode(tr, rev, allowdivergence, progress)
+                self._rebasenode(tr, rev, progress)
         p.complete()
         ui.note(_(b'rebase merging completed\n'))
 
@@ -567,16 +571,13 @@
 
             return newnode
 
-    def _rebasenode(self, tr, rev, allowdivergence, progressfn):
+    def _rebasenode(self, tr, rev, progressfn):
         repo, ui, opts = self.repo, self.ui, self.opts
         ctx = repo[rev]
         desc = _ctxdesc(ctx)
         if self.state[rev] == rev:
             ui.status(_(b'already rebased %s\n') % desc)
-        elif (
-            not allowdivergence
-            and rev in self.obsolete_with_successor_in_rebase_set
-        ):
+        elif rev in self.obsolete_with_successor_in_rebase_set:
             msg = (
                 _(
                     b'note: not rebasing %s and its descendants as '
--- a/tests/test-rebase-obsolete3.t	Fri Mar 19 10:34:16 2021 -0700
+++ b/tests/test-rebase-obsolete3.t	Fri Mar 19 22:52:59 2021 -0700
@@ -72,9 +72,9 @@
   $ hg rebase -b 'e' -d 'x'
   rebasing 1:488e1b7e7341 b "b"
   rebasing 3:a82ac2b38757 c "c"
+  note: not rebasing 4:76be324c128b d "d" and its descendants as this would cause divergence
   rebasing 5:027ad6c5830d d' "d'"
   rebasing 6:d60ebfa0f1cb e "e"
-  note: not rebasing 4:76be324c128b d "d" and its descendants as this would cause divergence
   $ hg log -G -r 'a'::
   o  11:eb6d63fc4ed5 e
   |
@@ -207,16 +207,16 @@
   $ hg rebase -b 'f' -d 'x'
   rebasing 1:488e1b7e7341 b "b"
   rebasing 3:a82ac2b38757 c "c"
-  rebasing 5:63324dc512ea e' "e'"
-  rebasing 7:3ffec603ab53 f "f"
   rebasing 4:76be324c128b d "d"
   note: not rebasing 6:e36fae928aec e "e" and its descendants as this would cause divergence
+  rebasing 5:63324dc512ea e' "e'"
+  rebasing 7:3ffec603ab53 f "f"
   $ hg log -G -r 'a':
-  o  13:a1707a5b7c2c d
+  o  13:ef6251596616 f
   |
-  | o  12:ef6251596616 f
-  | |
-  | o  11:b6f172e64af9 e'
+  o  12:b6f172e64af9 e'
+  |
+  | o  11:a1707a5b7c2c d
   |/
   o  10:d008e6b4d3fd c
   |
@@ -224,13 +224,13 @@
   |
   | *  8:2876ce66c6eb g
   | |
-  | | x  7:3ffec603ab53 f (rewritten using rebase as 12:ef6251596616)
+  | | x  7:3ffec603ab53 f (rewritten using rebase as 13:ef6251596616)
   | | |
   | x |  6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
   | | |
-  | | x  5:63324dc512ea e' (rewritten using rebase as 11:b6f172e64af9)
+  | | x  5:63324dc512ea e' (rewritten using rebase as 12:b6f172e64af9)
   | | |
-  | x |  4:76be324c128b d (rewritten using rebase as 13:a1707a5b7c2c)
+  | x |  4:76be324c128b d (rewritten using rebase as 11:a1707a5b7c2c)
   | |/
   | x  3:a82ac2b38757 c (rewritten using rebase as 10:d008e6b4d3fd)
   | |