Patchwork [2,of,2] branch: make it possible to gently (without force) revive an ancestor branch

login
register
mail settings
Submitter Mads Kiilerich
Date Aug. 29, 2014, 10:22 a.m.
Message ID <07b14258740026ef01bc.1409307765@localhost.localdomain>
Download mbox | patch
Permalink /patch/5623/
State Rejected
Headers show

Comments

Mads Kiilerich - Aug. 29, 2014, 10:22 a.m.
# HG changeset patch
# User Mads Kiilerich <madski@unity3d.com>
# Date 1408124612 -7200
#      Fri Aug 15 19:43:32 2014 +0200
# Node ID 07b14258740026ef01bcf754da382c22e71b1d7c
# Parent  104cfce5efd2e557801dd45dba6e35b056b8f3d2
branch: make it possible to gently (without force) revive an ancestor branch

Sometimes, when using named branches, it is convenient to merge the wrong way
and then fix the branch name after the merge before the commit. One example is
when reviving an old release branch and you don't want to update to the old
branch head and recompile everything.

Forcible switching to another existing branch is normally not possible without
force. It has however "always" (b2873c587b1a) been possible to gently switch
to one of the parent branches.

Merging "the wrong way" is however not always possible - it has often already
been merged and doing it again will thus be a merge with a working directory
ancestor.

For symmetry, it should thus also be possible to gently switch to another
branch if the branch head is working directory ancestor.
Pierre-Yves David - Aug. 29, 2014, 4:09 p.m.
On 08/29/2014 12:22 PM, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@unity3d.com>
> # Date 1408124612 -7200
> #      Fri Aug 15 19:43:32 2014 +0200
> # Node ID 07b14258740026ef01bcf754da382c22e71b1d7c
> # Parent  104cfce5efd2e557801dd45dba6e35b056b8f3d2
> branch: make it possible to gently (without force) revive an ancestor branch
>
> Sometimes, when using named branches, it is convenient to merge the wrong way
> and then fix the branch name after the merge before the commit. One example is
> when reviving an old release branch and you don't want to update to the old
> branch head and recompile everything.
>
> Forcible switching to another existing branch is normally not possible without
> force. It has however "always" (b2873c587b1a) been possible to gently switch
> to one of the parent branches.
>
> Merging "the wrong way" is however not always possible - it has often already
> been merged and doing it again will thus be a merge with a working directory
> ancestor.
>
> For symmetry, it should thus also be possible to gently switch to another
> branch if the branch head is working directory ancestor.
>
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -1033,7 +1033,10 @@ def branch(ui, repo, label=None, **opts)
>               ui.status(_('reset working directory to branch %s\n') % label)
>           elif label:
>               if not opts.get('force') and label in repo.branchmap():
> -                if label not in [p.branch() for p in repo.parents()]:
> +                if (label not in [p.branch() for p in repo.parents()] and
> +                    not util.any(repo.changelog.isancestor(repo[label].node(),
> +                                                           p.node())
> +                                 for p in repo.parents())):

Can you make this four lines oneliner less horible ? (buy the use of 
temporary variable for exemple).

I love the change however, even If I believe mpm will want to say 
something about it.
Matt Mackall - Aug. 30, 2014, 1:50 p.m.
On Fri, 2014-08-29 at 12:22 +0200, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@unity3d.com>
> # Date 1408124612 -7200
> #      Fri Aug 15 19:43:32 2014 +0200
> # Node ID 07b14258740026ef01bcf754da382c22e71b1d7c
> # Parent  104cfce5efd2e557801dd45dba6e35b056b8f3d2
> branch: make it possible to gently (without force) revive an ancestor branch

>    $ hg branch default
> -  abort: a branch of the same name already exists
> -  (use 'hg update' to switch to it)

The above behavior exists because people who have been exposed to Git
think this is the command to switch branches. So they would get the
below...

> +  marked working directory as branch default
> +  (branches are permanent and global, did you want a bookmark?)

..and not realized they hadn't switched branches (because Git taught
them not to bother reading output) and then be confused/upset/blame the
tool when they had two heads.

So no, we can't switch this back.
Mads Kiilerich - Aug. 30, 2014, 3:40 p.m.
On 08/30/2014 03:50 PM, Matt Mackall wrote:
> On Fri, 2014-08-29 at 12:22 +0200, Mads Kiilerich wrote:
>> # HG changeset patch
>> # User Mads Kiilerich <madski@unity3d.com>
>> # Date 1408124612 -7200
>> #      Fri Aug 15 19:43:32 2014 +0200
>> # Node ID 07b14258740026ef01bcf754da382c22e71b1d7c
>> # Parent  104cfce5efd2e557801dd45dba6e35b056b8f3d2
>> branch: make it possible to gently (without force) revive an ancestor branch
>>     $ hg branch default
>> -  abort: a branch of the same name already exists
>> -  (use 'hg update' to switch to it)
> The above behavior exists because people who have been exposed to Git
> think this is the command to switch branches. So they would get the
> below...

One good reason for "abort: a branch of the same name already exists" is 
that it in the general case prevents these users towards not creating an 
extra head on branch default. I agree with that.

>
>> +  marked working directory as branch default
>> +  (branches are permanent and global, did you want a bookmark?)
> ..and not realized they hadn't switched branches (because Git taught
> them not to bother reading output) and then be confused/upset/blame the
> tool when they had two heads.

In this case, assuming they were on tip that was the only branch head 
and on another branch, the next commit would give _one_ topo head on 
default (with a parent not on default).

But ok, they could be disappointed if they wanted to update to the 
parent revision that also is the branch head on default, and expected 
the next commit to give them a new head on default.

> So no, we can't switch this back.

Do we really want to let Mercurial functionality be controlled that much 
by what git and its users is doing?

/Mads
Augie Fackler - Aug. 31, 2014, 8:47 a.m.
On Aug 30, 2014, at 5:40 PM, Mads Kiilerich <mads@kiilerich.com> wrote:

> On 08/30/2014 03:50 PM, Matt Mackall wrote:
>> On Fri, 2014-08-29 at 12:22 +0200, Mads Kiilerich wrote:
>>> # HG changeset patch
>>> # User Mads Kiilerich <madski@unity3d.com>
>>> # Date 1408124612 -7200
>>> #      Fri Aug 15 19:43:32 2014 +0200
>>> # Node ID 07b14258740026ef01bcf754da382c22e71b1d7c
>>> # Parent  104cfce5efd2e557801dd45dba6e35b056b8f3d2
>>> branch: make it possible to gently (without force) revive an ancestor branch
>>>    $ hg branch default
>>> -  abort: a branch of the same name already exists
>>> -  (use 'hg update' to switch to it)
>> The above behavior exists because people who have been exposed to Git
>> think this is the command to switch branches. So they would get the
>> below...
> 
> One good reason for "abort: a branch of the same name already exists" is that it in the general case prevents these users towards not creating an extra head on branch default. I agree with that.
> 
>> 
>>> +  marked working directory as branch default
>>> +  (branches are permanent and global, did you want a bookmark?)
>> ..and not realized they hadn't switched branches (because Git taught
>> them not to bother reading output) and then be confused/upset/blame the
>> tool when they had two heads.
> 
> In this case, assuming they were on tip that was the only branch head and on another branch, the next commit would give _one_ topo head on default (with a parent not on default).
> 
> But ok, they could be disappointed if they wanted to update to the parent revision that also is the branch head on default, and expected the next commit to give them a new head on default.
> 
>> So no, we can't switch this back.
> 
> Do we really want to let Mercurial functionality be controlled that much by what git and its users is doing?

It does seem prudent to avoid semi-permanent foot gun behavior.

> 
> /Mads
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Matt Mackall - Aug. 31, 2014, 10:58 a.m.
On Sat, 2014-08-30 at 17:40 +0200, Mads Kiilerich wrote:

> > So no, we can't switch this back.
> 
> Do we really want to let Mercurial functionality be controlled that much 
> by what git and its users is doing?

You can safely assume that any decision I make that is to make Git users
happy (aka "the bane of my existence") is done extremely grudgingly.
Pierre-Yves David - Sept. 2, 2014, 7:12 p.m.
On 08/30/2014 05:40 PM, Mads Kiilerich wrote:
>> So no, we can't switch this back.
>
> Do we really want to let Mercurial functionality be controlled that much
> by what git and its users is doing?

I sounds reasonable to shape the Mercurial UI after what our users are 
doing. As a non-negligible share of them will have some kind of git 
crust, it makes sense to protect the most obvious source of confusion 
when moving from one to another.

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1033,7 +1033,10 @@  def branch(ui, repo, label=None, **opts)
             ui.status(_('reset working directory to branch %s\n') % label)
         elif label:
             if not opts.get('force') and label in repo.branchmap():
-                if label not in [p.branch() for p in repo.parents()]:
+                if (label not in [p.branch() for p in repo.parents()] and
+                    not util.any(repo.changelog.isancestor(repo[label].node(),
+                                                           p.node())
+                                 for p in repo.parents())):
                     raise util.Abort(_('a branch of the same name already'
                                        ' exists'),
                                      # i18n: "it" refers to an existing branch
diff --git a/tests/test-newbranch.t b/tests/test-newbranch.t
--- a/tests/test-newbranch.t
+++ b/tests/test-newbranch.t
@@ -31,9 +31,8 @@ 
 Branch shadowing:
 
   $ hg branch default
-  abort: a branch of the same name already exists
-  (use 'hg update' to switch to it)
-  [255]
+  marked working directory as branch default
+  (branches are permanent and global, did you want a bookmark?)
 
   $ hg branch -f default
   marked working directory as branch default
@@ -86,9 +85,8 @@  Merging and branches
  set existing branch name where branch head is ancestor:
 
   $ hg branch bar
-  abort: a branch of the same name already exists
-  (use 'hg update' to switch to it)
-  [255]
+  marked working directory as branch bar
+  (branches are permanent and global, did you want a bookmark?)
 
  set (other) parent branch as branch name