Patchwork [3,of,3,V3] rebase: prevent creating divergence

login
register
mail settings
Submitter Laurent Charignon
Date Jan. 12, 2016, 9:44 p.m.
Message ID <8a5d4acc9d728eba0bdd.1452635065@lcharignon-mbp.dhcp.thefacebook.com>
Download mbox | patch
Permalink /patch/12708/
State Accepted
Delegated to: Martin von Zweigbergk
Headers show

Comments

Laurent Charignon - Jan. 12, 2016, 9:44 p.m.
# HG changeset patch
# User Laurent Charignon <lcharignon@fb.com>
# Date 1452635021 28800
#      Tue Jan 12 13:43:41 2016 -0800
# Node ID 8a5d4acc9d728eba0bdd009facc64056ad0f1922
# Parent  09b8308e5e443d73e81bb488bf13789b7ef11149
rebase: prevent creating divergence

Before this patch rebase would create divergence when you were rebasing obsolete
changesets on a destination not containing one of its successors.
This patch introduces rebase.allowdivergence to explicitly allow
divergence creation with rebase.
Martin von Zweigbergk - Jan. 13, 2016, 6:26 p.m.
On Tue, Jan 12, 2016 at 1:44 PM, Laurent Charignon <lcharignon@fb.com> wrote:
> diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
> --- a/mercurial/dirstate.py
> +++ b/mercurial/dirstate.py
> @@ -141,6 +141,9 @@ class dirstate(object):
>
>      @propertycache
>      def _nonnormalset(self):
> +        return self.nonnormalset()
> +
> +    def nonnormalset(self):
>          return nonnormalentries(self._map)
>
>      @propertycache

I'm assuming this ended up here by mistake. I'll drop it in flight.
Martin von Zweigbergk - Jan. 13, 2016, 6:40 p.m.
On Wed, Jan 13, 2016 at 10:26 AM, Martin von Zweigbergk
<martinvonz@google.com> wrote:
> On Tue, Jan 12, 2016 at 1:44 PM, Laurent Charignon <lcharignon@fb.com> wrote:
>> diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
>> --- a/mercurial/dirstate.py
>> +++ b/mercurial/dirstate.py
>> @@ -141,6 +141,9 @@ class dirstate(object):
>>
>>      @propertycache
>>      def _nonnormalset(self):
>> +        return self.nonnormalset()
>> +
>> +    def nonnormalset(self):
>>          return nonnormalentries(self._map)
>>
>>      @propertycache
>
> I'm assuming this ended up here by mistake. I'll drop it in flight.

Pushed to the clowncopter with that change, and with some test case
changes (e.g. "divergent: 2 changeset" is now in plural).
Laurent Charignon - Jan. 14, 2016, 11:41 p.m.
Indeed, sorry for that!

Thanks,

Laurent

> On Jan 13, 2016, at 10:26 AM, Martin von Zweigbergk <martinvonz@google.com> wrote:
> 
> On Tue, Jan 12, 2016 at 1:44 PM, Laurent Charignon <lcharignon@fb.com> wrote:
>> diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
>> --- a/mercurial/dirstate.py
>> +++ b/mercurial/dirstate.py
>> @@ -141,6 +141,9 @@ class dirstate(object):
>> 
>>     @propertycache
>>     def _nonnormalset(self):
>> +        return self.nonnormalset()
>> +
>> +    def nonnormalset(self):
>>         return nonnormalentries(self._map)
>> 
>>     @propertycache
> 
> I'm assuming this ended up here by mistake. I'll drop it in flight.

Patch

diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -350,6 +350,17 @@  def rebase(ui, repo, **opts):
                                                                 dest)
                 rebaseobsskipped = set(obsoletenotrebased)
 
+                # Obsolete node with successors not in dest leads to divergence
+                divergenceok = ui.configbool('rebase',
+                                             'allowdivergence')
+                divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
+
+                if divergencebasecandidates and not divergenceok:
+                    msg = _("this rebase will cause divergence")
+                    h = _("to force the rebase please set "
+                          "rebase.allowdivergence=True")
+                    raise error.Abort(msg, hint=h)
+
                 # - plain prune (no successor) changesets are rebased
                 # - split changesets are not rebased if at least one of the
                 # changeset resulting from the split is an ancestor of dest
diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -141,6 +141,9 @@  class dirstate(object):
 
     @propertycache
     def _nonnormalset(self):
+        return self.nonnormalset()
+
+    def nonnormalset(self):
         return nonnormalentries(self._map)
 
     @propertycache
diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -1306,6 +1306,13 @@  have a definite end point.
 ``assume-tty``
     If true, ALWAYS show a progress bar, unless disable is given.
 
+``rebase``
+----------
+
+``allowdivergence``
+    Default to False, when True allow creating divergence when performing
+    rebase of obsolete changesets.
+
 ``revsetalias``
 ---------------
 
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
@@ -712,3 +712,96 @@  should display a friendly error message
   abort: all requested changesets have equivalents or were marked as obsolete
   (to force the rebase, set the config experimental.rebaseskipobsolete to False)
   [255]
+
+If a rebase is going to create divergence, it should abort
+
+  $ hg log -G
+  @  11:f44da1f4954c nonrelevant
+  |
+  | o  10:121d9e3bc4c6 P
+  |/
+  o  9:4be60e099a77 C
+  |
+  o  6:9c48361117de D
+  |
+  o  2:261e70097290 B2
+  |
+  o  0:4a2df7238c3b A
+  
+
+  $ hg up 9
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "john" > doe
+  $ hg add doe
+  $ hg commit -m "john doe"
+  created new head
+  $ hg up 10
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "foo" > bar
+  $ hg add bar
+  $ hg commit --amend -m "10'"
+  $ hg up 10 --hidden
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "bar" > foo
+  $ hg add foo
+  $ hg commit -m "bar foo"
+  $ hg log -G
+  @  15:73568ab6879d bar foo
+  |
+  | o  14:77d874d096a2 10'
+  | |
+  | | o  12:3eb461388009 john doe
+  | |/
+  x |  10:121d9e3bc4c6 P
+  |/
+  o  9:4be60e099a77 C
+  |
+  o  6:9c48361117de D
+  |
+  o  2:261e70097290 B2
+  |
+  o  0:4a2df7238c3b A
+  
+  $ hg summary
+  parent: 15:73568ab6879d tip
+   bar foo
+  branch: default
+  commit: (clean)
+  update: 2 new changesets, 3 branch heads (merge)
+  phases: 8 draft
+  unstable: 1 changeset
+  $ hg rebase -s 10 -d 12
+  abort: this rebase will cause divergence
+  (to force the rebase please set rebase.allowdivergence=True)
+  [255]
+  $ hg log -G
+  @  15:73568ab6879d bar foo
+  |
+  | o  14:77d874d096a2 10'
+  | |
+  | | o  12:3eb461388009 john doe
+  | |/
+  x |  10:121d9e3bc4c6 P
+  |/
+  o  9:4be60e099a77 C
+  |
+  o  6:9c48361117de D
+  |
+  o  2:261e70097290 B2
+  |
+  o  0:4a2df7238c3b A
+  
+With rebase.allowdivergence=True, rebase can create divergence
+
+  $ hg rebase -s 10 -d 12 --config rebase.allowdivergence=True
+  rebasing 10:121d9e3bc4c6 "P"
+  rebasing 15:73568ab6879d "bar foo" (tip)
+  $ hg summary
+  parent: 17:61bd55f69bc4 tip
+   bar foo
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+  phases: 8 draft
+  divergent: 2 changeset
+