Patchwork [RFC] update: add an option to allow to merge local changes when crossing branches

login
register
mail settings
Submitter Gilles Moris
Date Feb. 22, 2013, 10:22 a.m.
Message ID <567106adefd309717d8f.1361528527@fc8>
Download mbox | patch
Permalink /patch/1044/
State Superseded
Commit 4085c9fafb8e6f1b68c8f9d4d549304d948e6e5a
Headers show

Comments

Gilles Moris - Feb. 22, 2013, 10:22 a.m.
# HG changeset patch
# User Gilles Moris <gilles.moris@free.fr>
# Date 1361528509 -3600
# Node ID 567106adefd309717d8f0538197dc7dde45d34f1
# Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
update: add an option to allow to merge local changes when crossing branches

*NOT READY FOR INCLUSION*

Normally, it is not possible to update to another branch with local changes.
One have to use "hg update --clean" to discard changes and force the update.
One workaround is to update to the common ancestor of the branches, and then
to the other branch. This workaround is not well known by users.

I introduce an update --merge option that enables to rebase the local changes
in one step.

However, I have still some questions about how to design it:
I/ option naming
* I used --merge as it seems pretty but though could be confused with
  changesets merge
* --rebase could be used, may be ,ore accurate, but IMO too tied to the rebase
  extension
* --crossbranches would not be a good idea, as we probably don't want to allow
  crossing branches if a revision is not explicitely provided, whether there
  are some local changes or not
* other possibilities: --reseat, --move, ...
* last possibility: no option, allow by default to merge local changes, we've
  got --check if we want to block. By the way, this would greatly simplify both
  the code and the help content. The help content is currently partially lying:
    "If the changeset is not a descendant or ancestor of the working
     directory's parent, the update is aborted."
  Was true before, but since many releases we allow to cross branch for clean
  working directory and an explicit release.
    "-c --check     update across branches if no uncommitted changes"
  Again wrong, this option aborts on any dirty working dir, whether we cross
  branches or not.
  So no option might be more consistent with the evolution of the CLI.

II/ options conflict handling
If we add a new option, we need it to have it mutually exclusive with --clean or
--check options. Do we want --merge be overridden by --check and --clean, so
that a user could habe --merge as an alias default?

III/ internal API changes
I choose the least impacting API change, since I am expecting many extensions to
use those API hg.updaterepo() and merge.update(), even if we are not bound to
keep internal APIs. subrepos and largefiles are clients for instance. Is the
approach OK?

IV/ help and tests will need to be updated once design choices are done.
Angel Ezquerra - Feb. 22, 2013, 12:26 p.m.
On Fri, Feb 22, 2013 at 11:22 AM, Gilles Moris <gilles.moris@free.fr> wrote:
> # HG changeset patch
> # User Gilles Moris <gilles.moris@free.fr>
> # Date 1361528509 -3600
> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
> update: add an option to allow to merge local changes when crossing branches
>
> *NOT READY FOR INCLUSION*
>
> Normally, it is not possible to update to another branch with local changes.
> One have to use "hg update --clean" to discard changes and force the update.
> One workaround is to update to the common ancestor of the branches, and then
> to the other branch. This workaround is not well known by users.
>
> I introduce an update --merge option that enables to rebase the local changes
> in one step.
>
> However, I have still some questions about how to design it:
> I/ option naming
> * I used --merge as it seems pretty but though could be confused with
>   changesets merge
> * --rebase could be used, may be ,ore accurate, but IMO too tied to the rebase
>   extension
> * --crossbranches would not be a good idea, as we probably don't want to allow
>   crossing branches if a revision is not explicitely provided, whether there
>   are some local changes or not
> * other possibilities: --reseat, --move, ...
> * last possibility: no option, allow by default to merge local changes, we've
>   got --check if we want to block. By the way, this would greatly simplify both
>   the code and the help content. The help content is currently partially lying:
>     "If the changeset is not a descendant or ancestor of the working
>      directory's parent, the update is aborted."
>   Was true before, but since many releases we allow to cross branch for clean
>   working directory and an explicit release.
>     "-c --check     update across branches if no uncommitted changes"
>   Again wrong, this option aborts on any dirty working dir, whether we cross
>   branches or not.
>   So no option might be more consistent with the evolution of the CLI.

What about --force? It would let you do an update where the regular
update would not let you...

Cheers,

Angel
Laurens Holst - Feb. 22, 2013, 1:24 p.m.
Op 22-02-13 13:26, Angel Ezquerra schreef:
> On Fri, Feb 22, 2013 at 11:22 AM, Gilles Moris <gilles.moris@free.fr> wrote:
>> # HG changeset patch
>> # User Gilles Moris <gilles.moris@free.fr>
>> # Date 1361528509 -3600
>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>> update: add an option to allow to merge local changes when crossing branches
>>
>> *NOT READY FOR INCLUSION*
>>
>> Normally, it is not possible to update to another branch with local changes.
>> One have to use "hg update --clean" to discard changes and force the update.
>> One workaround is to update to the common ancestor of the branches, and then
>> to the other branch. This workaround is not well known by users.
>>
>> I introduce an update --merge option that enables to rebase the local changes
>> in one step.
>>
>> However, I have still some questions about how to design it:
>> I/ option naming
>> * I used --merge as it seems pretty but though could be confused with
>>    changesets merge
>> * --rebase could be used, may be ,ore accurate, but IMO too tied to the rebase
>>    extension
>> * --crossbranches would not be a good idea, as we probably don't want to allow
>>    crossing branches if a revision is not explicitely provided, whether there
>>    are some local changes or not
>> * other possibilities: --reseat, --move, ...
>> * last possibility: no option, allow by default to merge local changes, we've
>>    got --check if we want to block. By the way, this would greatly simplify both
>>    the code and the help content. The help content is currently partially lying:
>>      "If the changeset is not a descendant or ancestor of the working
>>       directory's parent, the update is aborted."
>>    Was true before, but since many releases we allow to cross branch for clean
>>    working directory and an explicit release.
>>      "-c --check     update across branches if no uncommitted changes"
>>    Again wrong, this option aborts on any dirty working dir, whether we cross
>>    branches or not.
>>    So no option might be more consistent with the evolution of the CLI.
> What about --force? It would let you do an update where the regular
> update would not let you...

I’m not entirely clear why this isn’t the default? Any update with local 
changes is performing a merge anyway (with all its risks for conflicts), 
so why would updating across branches need to be prohibited? It’s just a 
matter of setting base to the common ancestor instead of working copy 
parent, right?

~Laurens
Laurens Holst - Feb. 22, 2013, 1:27 p.m.
Op 22-02-13 14:24, Laurens Holst schreef:
> Op 22-02-13 13:26, Angel Ezquerra schreef:
>> On Fri, Feb 22, 2013 at 11:22 AM, Gilles Moris <gilles.moris@free.fr> 
>> wrote:
>>> # HG changeset patch
>>> # User Gilles Moris <gilles.moris@free.fr>
>>> # Date 1361528509 -3600
>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>> update: add an option to allow to merge local changes when crossing 
>>> branches
>>>
>>> *NOT READY FOR INCLUSION*
>>>
>>> Normally, it is not possible to update to another branch with local 
>>> changes.
>>> One have to use "hg update --clean" to discard changes and force the 
>>> update.
>>> One workaround is to update to the common ancestor of the branches, 
>>> and then
>>> to the other branch. This workaround is not well known by users.
>>>
>>> I introduce an update --merge option that enables to rebase the 
>>> local changes
>>> in one step.
>>>
>>> However, I have still some questions about how to design it:
>>> I/ option naming
>>> * I used --merge as it seems pretty but though could be confused with
>>>    changesets merge
>>> * --rebase could be used, may be ,ore accurate, but IMO too tied to 
>>> the rebase
>>>    extension
>>> * --crossbranches would not be a good idea, as we probably don't 
>>> want to allow
>>>    crossing branches if a revision is not explicitely provided, 
>>> whether there
>>>    are some local changes or not
>>> * other possibilities: --reseat, --move, ...
>>> * last possibility: no option, allow by default to merge local 
>>> changes, we've
>>>    got --check if we want to block. By the way, this would greatly 
>>> simplify both
>>>    the code and the help content. The help content is currently 
>>> partially lying:
>>>      "If the changeset is not a descendant or ancestor of the working
>>>       directory's parent, the update is aborted."
>>>    Was true before, but since many releases we allow to cross branch 
>>> for clean
>>>    working directory and an explicit release.
>>>      "-c --check     update across branches if no uncommitted changes"
>>>    Again wrong, this option aborts on any dirty working dir, whether 
>>> we cross
>>>    branches or not.
>>>    So no option might be more consistent with the evolution of the CLI.
>> What about --force? It would let you do an update where the regular
>> update would not let you...
>
> I’m not entirely clear why this isn’t the default? Any update with 
> local changes is performing a merge anyway (with all its risks for 
> conflicts), so why would updating across branches need to be 
> prohibited? It’s just a matter of setting base to the common ancestor 
> instead of working copy parent, right?

Sorry, I said something stupid there, please ignore that last sentence :).

Anyway, my point is, why can’t it do it this by default.

~Laurens
Gilles Moris - Feb. 22, 2013, 2:54 p.m.
On Friday 22 February 2013 02:27:08 pm Laurens Holst wrote:
> Op 22-02-13 14:24, Laurens Holst schreef:
> > Op 22-02-13 13:26, Angel Ezquerra schreef:
> >> On Fri, Feb 22, 2013 at 11:22 AM, Gilles Moris <gilles.moris@free.fr>
> >>
> >> wrote:
> >>> # HG changeset patch
> >>> # User Gilles Moris <gilles.moris@free.fr>
> >>> # Date 1361528509 -3600
> >>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
> >>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
> >>> update: add an option to allow to merge local changes when crossing
> >>> branches
> >>>
> >>> *NOT READY FOR INCLUSION*
> >>>
> >>> Normally, it is not possible to update to another branch with local
> >>> changes.
> >>> One have to use "hg update --clean" to discard changes and force the
> >>> update.
> >>> One workaround is to update to the common ancestor of the branches,
> >>> and then
> >>> to the other branch. This workaround is not well known by users.
> >>>
> >>> I introduce an update --merge option that enables to rebase the
> >>> local changes
> >>> in one step.
> >>>
> >>> However, I have still some questions about how to design it:
> >>> I/ option naming
> >>> * I used --merge as it seems pretty but though could be confused with
> >>>    changesets merge
> >>> * --rebase could be used, may be ,ore accurate, but IMO too tied to
> >>> the rebase
> >>>    extension
> >>> * --crossbranches would not be a good idea, as we probably don't
> >>> want to allow
> >>>    crossing branches if a revision is not explicitely provided,
> >>> whether there
> >>>    are some local changes or not
> >>> * other possibilities: --reseat, --move, ...
> >>> * last possibility: no option, allow by default to merge local
> >>> changes, we've
> >>>    got --check if we want to block. By the way, this would greatly
> >>> simplify both
> >>>    the code and the help content. The help content is currently
> >>> partially lying:
> >>>      "If the changeset is not a descendant or ancestor of the working
> >>>       directory's parent, the update is aborted."
> >>>    Was true before, but since many releases we allow to cross branch
> >>> for clean
> >>>    working directory and an explicit release.
> >>>      "-c --check     update across branches if no uncommitted changes"
> >>>    Again wrong, this option aborts on any dirty working dir, whether
> >>> we cross
> >>>    branches or not.
> >>>    So no option might be more consistent with the evolution of the CLI.
> >>
> >> What about --force? It would let you do an update where the regular
> >> update would not let you...
> >
> > I’m not entirely clear why this isn’t the default? Any update with
> > local changes is performing a merge anyway (with all its risks for
> > conflicts), so why would updating across branches need to be
> > prohibited? It’s just a matter of setting base to the common ancestor
> > instead of working copy parent, right?
>
> Sorry, I said something stupid there, please ignore that last sentence :).
>
> Anyway, my point is, why can’t it do it this by default.
>
> ~Laurens
>

I think the rationale is that there are more probabilities to run into 
conflicts when jumping to another branch. This is questionable though, as if 
you are going far back in the past, the risk of conflicts is even higher, but 
you are allowed as this is linear.
And I am not aware of any way to abort the merge of a working directory once 
started. If you screwed your merge, you're done.

So I would rather be consistent and be able to always merge local changes by 
default as you said, and find a way to abort and revert a merge of the 
working directory. May be saving the current changes in an .hg/undo.update 
file before updating...

Regards.
Gilles.
Martin Geisler - Feb. 22, 2013, 3:03 p.m.
Laurens Holst <laurens.nospam@grauw.nl> writes:

> Anyway, my point is, why can’t it do it this by default.

I always do

  $ hg update 'ancestor(., $TARGET)'
  $ hg update $TARGET

when I get the warning about crossing branches and so far it has worked
fine -- but maybe there is a subtle bug somewhere that makes that a bad
idea?
Matt Mackall - Feb. 22, 2013, 5:15 p.m.
On Fri, 2013-02-22 at 14:24 +0100, Laurens Holst wrote:
> Op 22-02-13 13:26, Angel Ezquerra schreef:
> > On Fri, Feb 22, 2013 at 11:22 AM, Gilles Moris <gilles.moris@free.fr> wrote:
> >> # HG changeset patch
> >> # User Gilles Moris <gilles.moris@free.fr>
> >> # Date 1361528509 -3600
> >> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
> >> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
> >> update: add an option to allow to merge local changes when crossing branches
> >>
> >> *NOT READY FOR INCLUSION*
> >>
> >> Normally, it is not possible to update to another branch with local changes.
> >> One have to use "hg update --clean" to discard changes and force the update.
> >> One workaround is to update to the common ancestor of the branches, and then
> >> to the other branch. This workaround is not well known by users.
> >>
> >> I introduce an update --merge option that enables to rebase the local changes
> >> in one step.
> >>
> >> However, I have still some questions about how to design it:
> >> I/ option naming
> >> * I used --merge as it seems pretty but though could be confused with
> >>    changesets merge
> >> * --rebase could be used, may be ,ore accurate, but IMO too tied to the rebase
> >>    extension
> >> * --crossbranches would not be a good idea, as we probably don't want to allow
> >>    crossing branches if a revision is not explicitely provided, whether there
> >>    are some local changes or not
> >> * other possibilities: --reseat, --move, ...
> >> * last possibility: no option, allow by default to merge local changes, we've
> >>    got --check if we want to block. By the way, this would greatly simplify both
> >>    the code and the help content. The help content is currently partially lying:
> >>      "If the changeset is not a descendant or ancestor of the working
> >>       directory's parent, the update is aborted."
> >>    Was true before, but since many releases we allow to cross branch for clean
> >>    working directory and an explicit release.
> >>      "-c --check     update across branches if no uncommitted changes"
> >>    Again wrong, this option aborts on any dirty working dir, whether we cross
> >>    branches or not.
> >>    So no option might be more consistent with the evolution of the CLI.
> > What about --force? It would let you do an update where the regular
> > update would not let you...
> 
> I’m not entirely clear why this isn’t the default?

Because it was once the default and many users shot themselves in the
foot (and blamed us) and now we know better.
Martin Geisler - Feb. 22, 2013, 8:30 p.m.
Kevin Bullock <kbullock+mercurial@ringworld.org> writes:

> On 22 Feb 2013, at 9:03 AM, Martin Geisler wrote:
>
>> Laurens Holst <laurens.nospam@grauw.nl> writes:
>> 
>>> Anyway, my point is, why can’t it do it this by default.
>> 
>> I always do
>> 
>>  $ hg update 'ancestor(., $TARGET)'
>>  $ hg update $TARGET
>> 
>> when I get the warning about crossing branches and so far it has worked
>> fine -- but maybe there is a subtle bug somewhere that makes that a bad
>> idea?
>
> Not really a bug, but it is one more merge than would strictly be
> necessary.

That is true with the work-around I use today, but if it was implemented
internally, then I think it could be done with a single merge as one
would expect.
Martin Geisler - Feb. 22, 2013, 8:35 p.m.
Kevin Bullock <kbullock+mercurial@ringworld.org> writes:

> On 22 Feb 2013, at 7:24 AM, Laurens Holst wrote:
>
>> I’m not entirely clear why this isn’t the default? Any update with
>> local changes is performing a merge anyway (with all its risks for
>> conflicts), so why would updating across branches need to be
>> prohibited?
>
> Much greater likelihood of conflicts, and no way to get your local
> changes back if you want to bail.

We do save the original files when merging a dirty working copy into a
target revisioon -- 'hg resolve --tool internal:local' will give them
back to you even though they were never committed anywhere.

I don't know why you say the risk of conflicts is greater here than with
any other update/merge?

> In a normal linear update with changes in the working copy, all
> Mercurial has to do is apply the changes in the working dir to the
> target head, and write the result back into the working copy.

It actually does a normal three-way merge as if you had temporarily
commited your changes and now rebase the temporary commit to the target
for your update.

> To update across branches, it would have to apply _all_ the changes
> between the common ancestor and the working copy, and write the result
> into the working copy. Thus the likelihood of clobbering uncommitted
> changes is much greater (and much more subject to operator error in
> your merge tool of choice).

I think you're saying that an update the crosses branches will tend to
"span" a greater range of revisions than an update that is linear.

That seems reasonable, but the underlying merge problem ought to be the
same as if you had done a linear update across a big span of revisions.
Martin Geisler - Feb. 23, 2013, 11:24 a.m.
Kevin Bullock <kbullock+mercurial@ringworld.org> writes:

> On 22 Feb 2013, at 2:35 PM, Martin Geisler wrote:
>
>> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
>> 
>>> On 22 Feb 2013, at 7:24 AM, Laurens Holst wrote:
>>> 
>>>> I’m not entirely clear why this isn’t the default? Any update with
>>>> local changes is performing a merge anyway (with all its risks for
>>>> conflicts), so why would updating across branches need to be
>>>> prohibited?
>>> 
>>> Much greater likelihood of conflicts, and no way to get your local
>>> changes back if you want to bail.
>> 
>> We do save the original files when merging a dirty working copy into a
>> target revisioon -- 'hg resolve --tool internal:local' will give them
>> back to you even though they were never committed anywhere.
>
> Huh, how long have we been doing that? And more importantly, if we
> allowed a cross-branch update, how would you get them back _after
> updating back to your original place_?

We've been doing it for ages, but despite my best efforts, it keeps
surprising even very experienced users and developers :)

As for getting the dirty files back after updating somewhere and back
again, then it is a bit tricky. You need to make sure that your working
copy has the dirty files after the first update.

So after

  hg update $SOMEWHERE
  hg resolve --all --tool internal:local

the files look like they did in your dirty working copy. If you do

  hg update $BACK
  hg resove --all --tool internal:local

you will be back to where you started. If you modified the files after
the first update, then it is the modified version you see after the
second resolve.

>> I don't know why you say the risk of conflicts is greater here than
>> with any other update/merge?
>
> Simply because you're merging a larger set of changes, as you get to
> below.

As you say, a merge because of a dirty working copy will use

* working copy parent (base)
* dirty working copy  (local)
* update target       (other)

That ought to be an easy merge regardless of the update target since the
difference between base and local is "small".

I say "small" because I expect the diff present in the dirty working
copy to be one commit. That is much smaller than most branch merges
where the distance from local/other to base is 10, 100 or more commits.

>>> To update across branches, it would have to apply _all_ the changes
>>> between the common ancestor and the working copy, and write the
>>> result into the working copy. Thus the likelihood of clobbering
>>> uncommitted changes is much greater (and much more subject to
>>> operator error in your merge tool of choice).
>> 
>> I think you're saying that an update the crosses branches will tend
>> to "span" a greater range of revisions than an update that is linear.
>> 
>> That seems reasonable, but the underlying merge problem ought to be
>> the same as if you had done a linear update across a big span of
>> revisions.
>
> The theoretical problem is the same, but the _usability_ problem is
> very different, both because of a likely larger set of conflicts
> (merge early and often!), and because you'd have to do something
> different to get back to your original state (and we'd likely have to
> track more state outside of history).

The risk of losing changes is certainly greater when they only live
inside .hg/merge instead of in permanent history -- 100% agreed.

My starting point was only that every single time I've had the "sorry, I
wont help you update across branches" message I "fixed" it by updating
twice and could continue with my work.
Angel Ezquerra - Feb. 23, 2013, 6:24 p.m.
On Sat, Feb 23, 2013 at 12:24 PM, Martin Geisler <martin@geisler.net> wrote:
> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
>
>> On 22 Feb 2013, at 2:35 PM, Martin Geisler wrote:
>>
>>> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
>>>
>>>> On 22 Feb 2013, at 7:24 AM, Laurens Holst wrote:
>>>>
>>>>> I’m not entirely clear why this isn’t the default? Any update with
>>>>> local changes is performing a merge anyway (with all its risks for
>>>>> conflicts), so why would updating across branches need to be
>>>>> prohibited?
>>>>
>>>> Much greater likelihood of conflicts, and no way to get your local
>>>> changes back if you want to bail.
>>>
>>> We do save the original files when merging a dirty working copy into a
>>> target revisioon -- 'hg resolve --tool internal:local' will give them
>>> back to you even though they were never committed anywhere.
>>
>> Huh, how long have we been doing that? And more importantly, if we
>> allowed a cross-branch update, how would you get them back _after
>> updating back to your original place_?
>
> We've been doing it for ages, but despite my best efforts, it keeps
> surprising even very experienced users and developers :)
>
> As for getting the dirty files back after updating somewhere and back
> again, then it is a bit tricky. You need to make sure that your working
> copy has the dirty files after the first update.
>
> So after
>
>   hg update $SOMEWHERE
>   hg resolve --all --tool internal:local
>
> the files look like they did in your dirty working copy. If you do
>
>   hg update $BACK
>   hg resove --all --tool internal:local
>
> you will be back to where you started. If you modified the files after
> the first update, then it is the modified version you see after the
> second resolve.

That is a nice trick Martin!


>>> I don't know why you say the risk of conflicts is greater here than
>>> with any other update/merge?
>>
>> Simply because you're merging a larger set of changes, as you get to
>> below.
>
> As you say, a merge because of a dirty working copy will use
>
> * working copy parent (base)
> * dirty working copy  (local)
> * update target       (other)
>
> That ought to be an easy merge regardless of the update target since the
> difference between base and local is "small".
>
> I say "small" because I expect the diff present in the dirty working
> copy to be one commit. That is much smaller than most branch merges
> where the distance from local/other to base is 10, 100 or more commits.
>
>>>> To update across branches, it would have to apply _all_ the changes
>>>> between the common ancestor and the working copy, and write the
>>>> result into the working copy. Thus the likelihood of clobbering
>>>> uncommitted changes is much greater (and much more subject to
>>>> operator error in your merge tool of choice).
>>>
>>> I think you're saying that an update the crosses branches will tend
>>> to "span" a greater range of revisions than an update that is linear.
>>>
>>> That seems reasonable, but the underlying merge problem ought to be
>>> the same as if you had done a linear update across a big span of
>>> revisions.
>>
>> The theoretical problem is the same, but the _usability_ problem is
>> very different, both because of a likely larger set of conflicts
>> (merge early and often!), and because you'd have to do something
>> different to get back to your original state (and we'd likely have to
>> track more state outside of history).
>
> The risk of losing changes is certainly greater when they only live
> inside .hg/merge instead of in permanent history -- 100% agreed.
>
> My starting point was only that every single time I've had the "sorry, I
> wont help you update across branches" message I "fixed" it by updating
> twice and could continue with my work.

I also do this somethings although in other cases I either shelve my
changes or commit and rebase.
It seems that having to update back to an ancestor doubles the changes
of making an error while merging?

Angel
Martin Geisler - Feb. 27, 2013, 10:24 p.m.
Kevin Bullock <kbullock+mercurial@ringworld.org> writes:

> On 23 Feb 2013, at 5:24 AM, Martin Geisler wrote:
>
>> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
>> 
>>> On 22 Feb 2013, at 2:35 PM, Martin Geisler wrote:
>>> 
>>>> I don't know why you say the risk of conflicts is greater here than
>>>> with any other update/merge?
>>> 
>>> Simply because you're merging a larger set of changes, as you get to
>>> below.
>> 
>> As you say, a merge because of a dirty working copy will use
>> 
>> * working copy parent (base)
>> * dirty working copy  (local)
>> * update target       (other)
>> 
>> That ought to be an easy merge regardless of the update target since
>> the difference between base and local is "small".
>> 
>> I say "small" because I expect the diff present in the dirty working
>> copy to be one commit. That is much smaller than most branch merges
>> where the distance from local/other to base is 10, 100 or more
>> commits.
>
> I was assuming that a cross-branch update would use:
>
> * common ancestor of target and WC (base)
> * dirty working copy               (local)
> * update target on other branch    (other)
>
> Is this not what the patch does?

I don't know this code well, but I think you're suggesting the wrong
ancestor: using the common ancestor of target and WC for a three-way
merge will make you merge the entire *branch* from "base" into "other".

You only want to merge the changes since the working copy parent, and
the working copy. For that you need the base to be the working copy
parent.

This is really a matter of committing the working copy, grafting the
commit to the target revision and uncommitting it again. I once made an
ASCII art graph of how I believe graft works:

  http://stackoverflow.com/a/9605306/110204

I hope someone will correct me if I'm confused here :)
Martin Geisler - Feb. 27, 2013, 10:32 p.m.
Angel Ezquerra <angel.ezquerra@gmail.com> writes:

> On Sat, Feb 23, 2013 at 12:24 PM, Martin Geisler <martin@geisler.net> wrote:
>> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
>>
>>> The theoretical problem is the same, but the _usability_ problem is
>>> very different, both because of a likely larger set of conflicts
>>> (merge early and often!), and because you'd have to do something
>>> different to get back to your original state (and we'd likely have
>>> to track more state outside of history).
>>
>> The risk of losing changes is certainly greater when they only live
>> inside .hg/merge instead of in permanent history -- 100% agreed.
>>
>> My starting point was only that every single time I've had the
>> "sorry, I wont help you update across branches" message I "fixed" it
>> by updating twice and could continue with my work.
>
> I also do this somethings although in other cases I either shelve my
> changes or commit and rebase.
> It seems that having to update back to an ancestor doubles the changes
> of making an error while merging?

Yeah, there will be situations where you get to resolve conflicts when
updating to the ancestor and get the resolve the same conflicts again
when updating to your target revision.

If there two merges were done as a single merge, then you might get
fewer conflicts: Think of a case where you modify file A.txt in the
working copy. File A.txt exists in both the working copy parent and in
your target revision, but it doesn't exist in the common ancestor: two
merges will cause a conflict that you don't get if you merge directly.

I'm not sure if the reversion situation can occur.
Gilles Moris - Feb. 28, 2013, 3:24 p.m.
On Wednesday 27 February 2013 11:24:24 pm Martin Geisler wrote:
> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
> > On 23 Feb 2013, at 5:24 AM, Martin Geisler wrote:
> >> Kevin Bullock <kbullock+mercurial@ringworld.org> writes:
> >>> On 22 Feb 2013, at 2:35 PM, Martin Geisler wrote:
> >>>> I don't know why you say the risk of conflicts is greater here than
> >>>> with any other update/merge?
> >>>
> >>> Simply because you're merging a larger set of changes, as you get to
> >>> below.
> >>
> >> As you say, a merge because of a dirty working copy will use
> >>
> >> * working copy parent (base)
> >> * dirty working copy  (local)
> >> * update target       (other)
> >>
> >> That ought to be an easy merge regardless of the update target since
> >> the difference between base and local is "small".
> >>
> >> I say "small" because I expect the diff present in the dirty working
> >> copy to be one commit. That is much smaller than most branch merges
> >> where the distance from local/other to base is 10, 100 or more
> >> commits.
> >
> > I was assuming that a cross-branch update would use:
> >
> > * common ancestor of target and WC (base)
> > * dirty working copy               (local)
> > * update target on other branch    (other)
> >
> > Is this not what the patch does?
>
> I don't know this code well, but I think you're suggesting the wrong
> ancestor: using the common ancestor of target and WC for a three-way
> merge will make you merge the entire *branch* from "base" into "other".
>
> You only want to merge the changes since the working copy parent, and
> the working copy. For that you need the base to be the working copy
> parent.
>
> This is really a matter of committing the working copy, grafting the
> commit to the target revision and uncommitting it again. I once made an
> ASCII art graph of how I believe graft works:
>
>   http://stackoverflow.com/a/9605306/110204
>
> I hope someone will correct me if I'm confused here :)

Martin,

Yes you are right, all the patch does is pa = p1, where pa provides the 
ancestor to the merge engine and p1 is the parent of the working copy. It 
just extends what the linear case already does.

I think one of the tricky point is that there is currently no easy way to 
revert from a working copy merge, even in the linear case. Either we document 
your trick, or we try to provide a more intuitive way. I am going to look how 
to tackle that one.

Once we have that, we may rediscuss whether we can allow merge of the working 
copy by default when crossing branches.

Regards.
Gilles.
Pierre-Yves David - March 12, 2013, 6:02 p.m.
On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
> # HG changeset patch
> # User Gilles Moris <gilles.moris@free.fr>
> # Date 1361528509 -3600
> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
> update: add an option to allow to merge local changes when crossing branches

Let's sum up my opinion on that:

1) We cannot have this behavior by default. preventing such update by default
   frequently save my changes.

   The same as mercurial prevent multiple operation in the middle of merge or a
   rebase.

   We need more of such safety

2) Not being able to do this update at all is painful. Intermediate update to
   common ancestor have more chances to conflict. And explaining this gymnastic
   to new comers is nuts.

3) a --merge option for update make more sense that a --stash one.
   BUT we are going to want a --stash option in a couple of other places:
   - merge
   - rebase
   - histedit
   - probably others

   So we better have a single option for all. The --merge option does not make
   sense for other command. We should use --stash one for update too.
Angel Ezquerra - March 12, 2013, 7:03 p.m.
On Tue, Mar 12, 2013 at 7:10 PM, Kevin Bullock
<kbullock+mercurial@ringworld.org> wrote:
> On 12 Mar 2013, at 1:02 PM, Pierre-Yves David wrote:
>
>> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>>> # HG changeset patch
>>> # User Gilles Moris <gilles.moris@free.fr>
>>> # Date 1361528509 -3600
>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>> update: add an option to allow to merge local changes when crossing branches
>>
>> Let's sum up my opinion on that:
>>
>> 1) We cannot have this behavior by default. preventing such update by default
>>   frequently save my changes.
>>
>>   The same as mercurial prevent multiple operation in the middle of merge or a
>>   rebase.
>>
>>   We need more of such safety
>>
>> 2) Not being able to do this update at all is painful. Intermediate update to
>>   common ancestor have more chances to conflict. And explaining this gymnastic
>>   to new comers is nuts.
>>
>> 3) a --merge option for update make more sense that a --stash one.
>>   BUT we are going to want a --stash option in a couple of other places:
>>   - merge
>>   - rebase
>>   - histedit
>>   - probably others
>>
>>   So we better have a single option for all. The --merge option does not make
>>   sense for other command. We should use --stash one for update too.
>
> I'd much rather just have a stash _command_ that we can suggest in the abort hints for all those commands.
>

Personally I don't like "--stash" too much for a couple of reasons:

- I think there is a git stash which does not really do the same
thing. I think that would be confusing for git refugees.
- A lot of people have been trained by TortoiseHg to think of "shelve"
as the way to save your working directory changes. So why not use
"--shelve" rather than "--stash"?

Cheers,

Angel
Angel Ezquerra - March 12, 2013, 7:30 p.m.
On Tue, Mar 12, 2013 at 8:03 PM, Angel Ezquerra
<angel.ezquerra@gmail.com> wrote:
> On Tue, Mar 12, 2013 at 7:10 PM, Kevin Bullock
> <kbullock+mercurial@ringworld.org> wrote:
>> On 12 Mar 2013, at 1:02 PM, Pierre-Yves David wrote:
>>
>>> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>>>> # HG changeset patch
>>>> # User Gilles Moris <gilles.moris@free.fr>
>>>> # Date 1361528509 -3600
>>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>>> update: add an option to allow to merge local changes when crossing branches
>>>
>>> Let's sum up my opinion on that:
>>>
>>> 1) We cannot have this behavior by default. preventing such update by default
>>>   frequently save my changes.
>>>
>>>   The same as mercurial prevent multiple operation in the middle of merge or a
>>>   rebase.
>>>
>>>   We need more of such safety
>>>
>>> 2) Not being able to do this update at all is painful. Intermediate update to
>>>   common ancestor have more chances to conflict. And explaining this gymnastic
>>>   to new comers is nuts.
>>>
>>> 3) a --merge option for update make more sense that a --stash one.
>>>   BUT we are going to want a --stash option in a couple of other places:
>>>   - merge
>>>   - rebase
>>>   - histedit
>>>   - probably others
>>>
>>>   So we better have a single option for all. The --merge option does not make
>>>   sense for other command. We should use --stash one for update too.
>>
>> I'd much rather just have a stash _command_ that we can suggest in the abort hints for all those commands.
>>
>
> Personally I don't like "--stash" too much for a couple of reasons:
>
> - I think there is a git stash which does not really do the same
> thing. I think that would be confusing for git refugees.
> - A lot of people have been trained by TortoiseHg to think of "shelve"
> as the way to save your working directory changes. So why not use
> "--shelve" rather than "--stash"?
>
> Cheers,
>
> Angel

Kevin,

re-reading your email I think I misunderstood you. You were advocating
for a stash _command_ (you even underscored that yourself! :-) ), in
which case it is possible that mercurial's stash command may behave
very similarly to git's.

If we add a flag to the different mecurial commands I'd prefer
--shelve, but if it is a stand alone command... there is a lot to be
said to try to use the same command if we can get it to behave the
same.

Angel
Laurens Holst - March 13, 2013, 12:13 p.m.
Op 12-03-13 19:02, Pierre-Yves David schreef:
> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>> # HG changeset patch
>> # User Gilles Moris <gilles.moris@free.fr>
>> # Date 1361528509 -3600
>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>> update: add an option to allow to merge local changes when crossing branches
> Let's sum up my opinion on that:
>
> 1) We cannot have this behavior by default. preventing such update by default
>     frequently save my changes.
>
>     The same as mercurial prevent multiple operation in the middle of merge or a
>     rebase.
>
>     We need more of such safety
>

Can you elaborate on how this abort saves your changes? Because it is 
not clear to me.

~Laurens
Martin Geisler - March 13, 2013, 6:07 p.m.
Pierre-Yves David <pierre-yves.david@logilab.fr> writes:

> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>> # HG changeset patch
>> # User Gilles Moris <gilles.moris@free.fr>
>> # Date 1361528509 -3600
>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>> update: add an option to allow to merge local changes when crossing branches
>
> Let's sum up my opinion on that:
>
> 1) We cannot have this behavior by default. preventing such update by default
>    frequently save my changes.
>
>    The same as mercurial prevent multiple operation in the middle of
>    merge or a rebase.
>
>    We need more of such safety

Looking at this again, I realized that I have never understood the idea
behind the --check option. I looked at the code and saw that 'hg update'
takes me to the tip of the current *named* branch.

That's often, but not always what I want -- instead I want 'hg update'
to take me to 'max(.:: and branch(.))', which should be the tip of the
current *topological* branch.

Letting 'hg update' update me to some random head of my named branch
does indeed sound bad, so the --check option makes some kind of sense.
Not much sense, though: adding --check will let me update to some other
revision, but I probably don't know which revision without looking at
some log viewer first, and then I would use 'hg update REV' *anyway*.

To put it differently: if 'hg update' aborts because I should use 'hg
update --check' instead, then 'hg update <CUR-BRANCH>' would give the
same result. That's how I use update 98% of the time: to update to some
named branch. The other 2% I use an explicit revision that I've looked
up somewhere.


So when do you guys really use 'hg update' (without a branch name or
revision number argument)? When do you then use 'hg update --check'?
Greg Ward - March 13, 2013, 6:09 p.m.
On 13 March 2013, Laurens Holst said:
> Op 12-03-13 19:02, Pierre-Yves David schreef:
> >On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
> >># HG changeset patch
> >># User Gilles Moris <gilles.moris@free.fr>
> >># Date 1361528509 -3600
> >># Node ID 567106adefd309717d8f0538197dc7dde45d34f1
> >># Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
> >>update: add an option to allow to merge local changes when crossing branches
> >Let's sum up my opinion on that:
> >
> >1) We cannot have this behavior by default. preventing such update by default
> >    frequently save my changes.
> >
> >    The same as mercurial prevent multiple operation in the middle of merge or a
> >    rebase.
> >
> >    We need more of such safety
> >
> 
> Can you elaborate on how this abort saves your changes? Because it
> is not clear to me.

I think Pierre-Yves meant save as in "prevent unexpected/unwanted
merge" rather than "store a backup copy somewhere". 

Here's the scenario: you have uncommitted changes that are valuable
but that you forgot about. You pull and new changesets land, so you
"hg update". Mercurial says "sorry buddy, no cross-branch updates for
you", which reminds you of your uncommitted changes. You can ignore
the pulled changesets and keep working, or commit and merge, or commit
and rebase.

It would perhaps have been more accurate for Pierre-Yves to say

  preventing such update by default frequently saves my bacon

but expecting perfect idiomatic English from everyone is unrealistic. ;-)

       Greg
Isaac Jurado - March 13, 2013, 11:05 p.m.
Replying Martin Geisler:
> Pierre-Yves David <pierre-yves.david@logilab.fr> writes:
>
>> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>>> # HG changeset patch
>>> # User Gilles Moris <gilles.moris@free.fr>
>>> # Date 1361528509 -3600
>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>> update: add an option to allow to merge local changes when crossing branches
>>
>> Let's sum up my opinion on that:
>>
>> 1) We cannot have this behavior by default. preventing such update by default
>>    frequently save my changes.
>>
>>    The same as mercurial prevent multiple operation in the middle of
>>    merge or a rebase.
>>
>>    We need more of such safety
>
> Looking at this again, I realized that I have never understood the
> idea behind the --check option. I looked at the code and saw that 'hg
> update' takes me to the tip of the current *named* branch.
>
> That's often, but not always what I want -- instead I want 'hg update'
> to take me to 'max(.:: and branch(.))', which should be the tip of the
> current *topological* branch.

Even though I would also prefer such behaviour (if I understood
correctli: update to descendant by default), I don't think it is such a
simple change (not to even mention backwards compatibility).  Think of
"hg pull --update" or what "hg summary" says about update.

Cheers.
Laurens Holst - March 14, 2013, 9:42 a.m.
Op 13-03-13 19:09, Greg Ward schreef:
> On 13 March 2013, Laurens Holst said:
>> Op 12-03-13 19:02, Pierre-Yves David schreef:
>>> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>>>> # HG changeset patch
>>>> # User Gilles Moris <gilles.moris@free.fr>
>>>> # Date 1361528509 -3600
>>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>>> update: add an option to allow to merge local changes when crossing branches
>>> Let's sum up my opinion on that:
>>>
>>> 1) We cannot have this behavior by default. preventing such update by default
>>>     frequently save my changes.
>>>
>>>     The same as mercurial prevent multiple operation in the middle of merge or a
>>>     rebase.
>>>
>>>     We need more of such safety
>>>
>> Can you elaborate on how this abort saves your changes? Because it
>> is not clear to me.
> I think Pierre-Yves meant save as in "prevent unexpected/unwanted
> merge" rather than "store a backup copy somewhere".
>
> Here's the scenario: you have uncommitted changes that are valuable
> but that you forgot about. You pull and new changesets land, so you
> "hg update". Mercurial says "sorry buddy, no cross-branch updates for
> you", which reminds you of your uncommitted changes. You can ignore
> the pulled changesets and keep working, or commit and merge, or commit
> and rebase.
>
> It would perhaps have been more accurate for Pierre-Yves to say
>
>    preventing such update by default frequently saves my bacon
>
> but expecting perfect idiomatic English from everyone is unrealistic. ;-)

So, if there is an easy way to revert back to the state before the 
merge, there will be no need for an extra --merge / --stash flag. Right? 
From a UI perspective, that seems the way to go.

Prompting the user to type his command again but with a special flag 
seems just as suboptimal as the update-to-ancestor thing. After typing 
the flag a couple of times, users will start typing it always just to 
avoid the abort. Then, the value of such a flag is reduced to less than 
zero, because users will have to type more and their forgotten changes 
are still not saved.

When I was attempting to implement a "--guard" flag to only update when 
there are no conflicts [1], I think the final outcome was also that 
rather than adding such a flag, there should be a way to easily back out 
of a merge while preserving changes in the working copy.

~Laurens

[1] http://selenic.com/pipermail/mercurial-devel/2011-December/036520.html
Laurens Holst - March 14, 2013, 9:54 a.m.
Op 14-03-13 10:42, Laurens Holst schreef:
> When I was attempting to implement a "--guard" flag to only update 
> when there are no conflicts [1], I think the final outcome was also 
> that rather than adding such a flag, there should be a way to easily 
> back out of a merge while preserving changes in the working copy.
>
> [1] http://selenic.com/pipermail/mercurial-devel/2011-December/036520.html

Quoting for the lazy reader:

On 27-12-11 00:59, Matt Mackall wrote:
> > By adding --guard, we give the user a new option, which is to try out
> > the update and see if it works out. This wasn’t possible before. If it
> > doesn’t, the user faces the choice you mentioned, but at least the user
> > will know to expect a conflict (contrary to -c), so he can make a more
> > informed choice. This is the value that -g adds.
> >
> > For comparison, a rollback function for update would fulfill a similar
> > purpose and be an alternative to guard.
>
> So what if we stored every "conflict" that --guard would care about in
> the resolve "database", such that you could revisit/back out of the
> conflict? This has actually been a goal for a while.

~Laurens
Pierre-Yves David - March 14, 2013, 11:01 a.m.
On 14 mars 2013, at 10:42, Laurens Holst wrote:

> Op 13-03-13 19:09, Greg Ward schreef:
>> On 13 March 2013, Laurens Holst said:
>>> Op 12-03-13 19:02, Pierre-Yves David schreef:
>>>> On Fri, Feb 22, 2013 at 11:22:07AM +0100, Gilles Moris wrote:
>>>>> # HG changeset patch
>>>>> # User Gilles Moris <gilles.moris@free.fr>
>>>>> # Date 1361528509 -3600
>>>>> # Node ID 567106adefd309717d8f0538197dc7dde45d34f1
>>>>> # Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
>>>>> update: add an option to allow to merge local changes when crossing branches
>>>> Let's sum up my opinion on that:
>>>> 
>>>> 1) We cannot have this behavior by default. preventing such update by default
>>>>    frequently save my changes.
>>>> 
>>>>    The same as mercurial prevent multiple operation in the middle of merge or a
>>>>    rebase.
>>>> 
>>>>    We need more of such safety
>>>> 
>>> Can you elaborate on how this abort saves your changes? Because it
>>> is not clear to me.
>> I think Pierre-Yves meant save as in "prevent unexpected/unwanted
>> merge" rather than "store a backup copy somewhere".
>> 
>> Here's the scenario: you have uncommitted changes that are valuable
>> but that you forgot about. You pull and new changesets land, so you
>> "hg update". Mercurial says "sorry buddy, no cross-branch updates for
>> you", which reminds you of your uncommitted changes. You can ignore
>> the pulled changesets and keep working, or commit and merge, or commit
>> and rebase.
>> 
>> It would perhaps have been more accurate for Pierre-Yves to say
>> 
>>   preventing such update by default frequently saves my bacon
>> 
>> but expecting perfect idiomatic English from everyone is unrealistic. ;-)
> 
> So, if there is an easy way to revert back to the state before the merge, there will be no need for an extra --merge / --stash flag. Right? From a UI perspective, that seems the way to go.

No, no, no!

I forget to finish what I'm doing before moving to something else:
- I forget to commit my merge after conflict resolution
- I forget to amend my change in the relevant commit before moving to something else.

And I'm usually reminded by mercurial telling me:
- uncommited merge
- refuse to update with uncommited changes
So I can avoid the error early.

There is some situation were is doesn't do it:
- earlier version of amend that did not detected merge in progress.
- when I amend instead of commit.

And even if it easy to revert (rollback is my friend here :-/) it would really prefer to have a small warning.

Summary: Warning about suspicious situation is great. we just need a way to override that. 

> Prompting the user to type his command again but with a special flag seems just as suboptimal as the update-to-ancestor thing. After typing the flag a couple of times, users will start typing it always just to avoid the abort. Then, the value of such a flag is reduced to less than zero, because users will have to type more and their forgotten changes are still not saved.

Disagree. User will use it when he knows he want to move changes around. If he does it all the time it is the kind of user that use `hg push --force` all the time and complains later about troubles.

> When I was attempting to implement a "--guard" flag to only update when there are no conflicts [1], I think the final outcome was also that rather than adding such a flag, there should be a way to easily back out of a merge while preserving changes in the working copy.

easing the restoration process is a good idea on a general basis
Laurens Holst - March 14, 2013, 11:15 a.m.
Op 14-03-13 12:01, Pierre-Yves David schreef:
> I forget to finish what I'm doing before moving to something else:
> - I forget to commit my merge after conflict resolution

Updating while there is an uncommitted merge is I think a separate check 
that should remain in place, even when updating to an ancestor.

> - I forget to amend my change in the relevant commit before moving to something else.
>
> And I'm usually reminded by mercurial telling me:
> - uncommited merge
> - refuse to update with uncommited changes
> So I can avoid the error early.

But you *can* update with uncommitted changes. Just not to another 
topological branch. So if that’s the rationale, then you can’t 
consistently apply it unless you use the --check flag. And if you use 
that flag, then you won’t be affected by the change in default behaviour.

So I don’t think that is a good, or at least consistent argument to 
prevent updating across branches by default. It would be an argument for 
making --check default, however I think nobody wants that because it 
makes the tool more obnoxious and less convenient to use.

Similarly, the tool complaining just about cross branch updates is 
obnoxious and inconvenient, and if we have a means to roll back an 
update with merge conflict, this particular restriction by itself does 
not seem to serve any purpose in preventing the user from shooting 
himself in the foot.

~Laurens
Gilles Moris - March 17, 2013, 9:36 p.m.
On Thursday 14 March 2013 12:15:54 pm Laurens Holst wrote:
> Op 14-03-13 12:01, Pierre-Yves David schreef:
> > I forget to finish what I'm doing before moving to something else:
> > - I forget to commit my merge after conflict resolution
>
> Updating while there is an uncommitted merge is I think a separate check
> that should remain in place, even when updating to an ancestor.
>
> > - I forget to amend my change in the relevant commit before moving to
> > something else.
> >
> > And I'm usually reminded by mercurial telling me:
> > - uncommited merge
> > - refuse to update with uncommited changes
> > So I can avoid the error early.
>
> But you *can* update with uncommitted changes. Just not to another
> topological branch. So if that’s the rationale, then you can’t
> consistently apply it unless you use the --check flag. And if you use
> that flag, then you won’t be affected by the change in default behaviour.
>
> So I don’t think that is a good, or at least consistent argument to
> prevent updating across branches by default. It would be an argument for
> making --check default, however I think nobody wants that because it
> makes the tool more obnoxious and less convenient to use.
>
> Similarly, the tool complaining just about cross branch updates is
> obnoxious and inconvenient, and if we have a means to roll back an
> update with merge conflict, this particular restriction by itself does
> not seem to serve any purpose in preventing the user from shooting
> himself in the foot.
>
> ~Laurens

So it seems that this leaves us with 3 options:
1/ maximal safety: make --check the default to avoid any update with 
uncommitted files.
But then add a --merge option that merges local changes both linearly and 
across branches.

2/ Allow merging local changes by default both linearly and across branches.
But possibly:
  a/ provide a mechanism, like a --revert option to cancel the last update
  b/ warn the user when local changes have been merged/rebased

3/ still prevent bare update across branches but provide an option to allow it 
(this patch).

I do not like solution 3/. I have made some training to my team and it has 
always been a pain to explain all the different cases where we can or not 
update to another branch, and which option to use. As Mercurial developers, 
we even need a table to understand them [1].

So it would be preferable to go to 1/ or 2/ to simplify this area, and I would 
prefer 2/ if 2a and 2b are fulfilled. Can we reach a concensus on that?

Regards.
Gilles.
[1] http://www.selenic.com/repo/hg/file/a07be8953733/mercurial/merge.py#l618
Pierre-Yves David - March 20, 2013, 12:52 p.m.
On Sun, Mar 17, 2013 at 10:36:22PM +0100, Gilles Moris wrote:
> On Thursday 14 March 2013 12:15:54 pm Laurens Holst wrote:
> > Op 14-03-13 12:01, Pierre-Yves David schreef:
> > > I forget to finish what I'm doing before moving to something else:
> > > - I forget to commit my merge after conflict resolution
> >
> > Updating while there is an uncommitted merge is I think a separate check
> > that should remain in place, even when updating to an ancestor.
> >
> > > - I forget to amend my change in the relevant commit before moving to
> > > something else.
> > >
> > > And I'm usually reminded by mercurial telling me:
> > > - uncommited merge
> > > - refuse to update with uncommited changes
> > > So I can avoid the error early.
> >
> > But you *can* update with uncommitted changes. Just not to another
> > topological branch. So if that’s the rationale, then you can’t
> > consistently apply it unless you use the --check flag. And if you use
> > that flag, then you won’t be affected by the change in default behaviour.
> >
> > So I don’t think that is a good, or at least consistent argument to
> > prevent updating across branches by default. It would be an argument for
> > making --check default, however I think nobody wants that because it
> > makes the tool more obnoxious and less convenient to use.
> >
> > Similarly, the tool complaining just about cross branch updates is
> > obnoxious and inconvenient, and if we have a means to roll back an
> > update with merge conflict, this particular restriction by itself does
> > not seem to serve any purpose in preventing the user from shooting
> > himself in the foot.
> >
> > ~Laurens
> 
> So it seems that this leaves us with 3 options:
> 1/ maximal safety: make --check the default to avoid any update with 
> uncommitted files.
> But then add a --merge option that merges local changes both linearly and 
> across branches.

Would make sense, but will break backward compatibility

> 2/ Allow merging local changes by default both linearly and across branches.
> But possibly:

The odds of doing a cross branch update by mistake is much higher than
the odds of mistake for linear update.

>   a/ provide a mechanism, like a --revert option to cancel the last update

--revert is very confusing. an "hg undo" our "hg update --undo" that
pbring you back in the state prior update would make sense.

>   b/ warn the user when local changes have been merged/rebased

We already do that a bit. We could a bit clearer.

> 
> 3/ still prevent bare update across branches but provide an option to allow it 
> (this patch).
> 
> I do not like solution 3/. I have made some training to my team and it has 
> always been a pain to explain all the different cases where we can or not 
> update to another branch, and which option to use. As Mercurial developers, 
> we even need a table to understand them [1].

For my part I can see prefectly valid reason to be stricter in cross
branch update. They are much more likely to move you to an unrelated
part of the code.

However this must be possible to do this update one way or another.

> So it would be preferable to go to 1/ or 2/ to simplify this area, and I would 
> prefer 2/ if 2a and 2b are fulfilled. Can we reach a concensus on that?

1 and 2 two seems non-backward compatible :-( I'm not sure which one of the
two I would prefer otherwise. Maybe (1) for safety reason (in doubt, you
user is a drunk otter)

Patch

diff -r 013fcd112f13 -r 567106adefd3 mercurial/commands.py
--- a/mercurial/commands.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/commands.py	Fri Feb 22 11:21:49 2013 +0100
@@ -5906,10 +5906,13 @@ 
     [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
     ('c', 'check', None,
      _('update across branches if no uncommitted changes')),
+    ('m', 'merge', None,
+     _('merge local changes when updating to another branch')),
     ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
     ('r', 'rev', '', _('revision'), _('REV'))],
     _('[-c] [-C] [-d DATE] [[-r] REV]'))
-def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
+def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
+           merge=False):
     """update working directory (or switch revisions)
 
     Update the repository's working directory to the specified
@@ -5998,7 +6001,7 @@ 
     if clean:
         ret = hg.clean(repo, rev)
     else:
-        ret = hg.update(repo, rev)
+        ret = hg.update(repo, rev, merge)
 
     if not ret and movemarkfrom:
         if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
diff -r 013fcd112f13 -r 567106adefd3 mercurial/hg.py
--- a/mercurial/hg.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/hg.py	Fri Feb 22 11:21:49 2013 +0100
@@ -449,17 +449,17 @@ 
     repo.ui.status(_("%d files updated, %d files merged, "
                      "%d files removed, %d files unresolved\n") % stats)
 
-def updaterepo(repo, node, overwrite):
+def updaterepo(repo, node, overwrite, merge=False):
     """Update the working directory to node.
 
     When overwrite is set, changes are clobbered, merged else
 
     returns stats (see pydoc mercurial.merge.applyupdates)"""
-    return mergemod.update(repo, node, False, overwrite, None)
+    return mergemod.update(repo, node, False, overwrite, None, wdmerge=merge)
 
-def update(repo, node):
+def update(repo, node, merge=False):
     """update the working directory to node, merging linear changes"""
-    stats = updaterepo(repo, node, False)
+    stats = updaterepo(repo, node, False, merge)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
diff -r 013fcd112f13 -r 567106adefd3 mercurial/merge.py
--- a/mercurial/merge.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/merge.py	Fri Feb 22 11:21:49 2013 +0100
@@ -545,7 +545,7 @@ 
                     repo.dirstate.drop(f)
 
 def update(repo, node, branchmerge, force, partial, ancestor=None,
-           mergeancestor=False):
+           mergeancestor=False, wdmerge=False):
     """
     Perform a merge between the working directory and the given node
 
@@ -557,6 +557,7 @@ 
       is only allowed between different named branches. This flag
       is used by rebase extension as a temporary fix and should be
       avoided in general.
+    wdmerge = allow update to rebase local changes across branches
 
     The table below shows all the behaviors of the update command
     given the -c and -C or no options, whether the working directory
@@ -633,11 +634,13 @@ 
             if pa == p1 or pa == p2: # linear
                 pass # all good
             elif wc.dirty(missing=True):
-                raise util.Abort(_("crosses branches (merge branches or use"
-                                   " --clean to discard changes)"))
+                if not wdmerge:
+                    raise util.Abort(_("crosses branches (merge branches or "
+                                       "use --merge to rebase your changes)"))
+                pa = p1
             elif onode is None:
                 raise util.Abort(_("crosses branches (merge branches or update"
-                                   " --check to force update)"))
+                                   " --merge to force update)"))
             else:
                 # Allow jumping branches if clean and specific rev given
                 pa = p1