Re: [PATCH v3 10/21] checkout: split part of it to new command 'switch'

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Mar 14, 2019 at 12:23 AM Junio C Hamano <gitster@xxxxxxxxx> wrote:
>
> Elijah Newren <newren@xxxxxxxxx> writes:
>
> >> I think sometimes a 3-way merge creates marker conflicts in file, and
> >> this does not look easy to reverse when switching back. If it's true
> >> and we can detect it, we can still abort this case (i.e. requiring -m
>
> Paths with actual conflicts are much easier to recover from than
> paths that cleanly merge.   You have your original in stage #2, so
> you should be able to "restore-path --stage=2 --from-stage path..."
> them.  Once the contents are auto-resolved cleanly, however, the
> cached contents are automaticaly updated to the auto-resolved result,
> and it needs more work to reverse the effect of the merge (it is
> doable, of course, as you know exactly the contents of the
> switched-to branch and the contents of the switching-from branch, so
> it is just the matter of running 3-way merge in the right direction
> to recover what used to be in the working tree).

I agree that mixtures of conflicts and clean merges would be the most
difficult to reverse.  However, recovering from conflict cases is
actually harder than you mention, at least when renames are involved.
If foo is modified on both sides of history in conflicting ways AND
renamed to bar in the other branch, then all three staged will be
stored under the path bar.  Using your method to restore would cause
you to get a file named bar (though it would have the contents of the
original foo).

It can unfortunately get even worse.  With rename/rename(2to1)
conflicts; there are up to six values that needed to be shoved into
the normal three slots in the index, which merge-recursive achieves by
first content merging three at a time and shoving the (possibly
conflict-marker containing) results from that into two slots of the
index and then two-way merging from there to get the worktree contents
(possibly resulting in nested conflict markers).  Checking something
out of stage 2 thus not only might get the file path wrong but can
also get you contents with conflict markers.


However, this whole exercise gave me an idea that answers Duy's
original question definitively: you cannot necessarily reverse a
successful (i.e. no conflicts present) three-way merge with another
three-way merge.  Here's an example to demonstrate that...let's say
you are on branch A, and you have two identical files:
  A: foo, bar
and you want to switch to branch B which renamed foo but didn't modify it:
  B: baz, bar
locally on A you had renamed bar but didn't modify it:
  C: foo, baz
(C isn't an actual commit; I just wanted a label to refer to it)

doing a "git switch -m B" will result in merge-recursive noting that
both foo and bar were renamed to baz but that both versions of baz
where identical, so we'd end up with:
  D: baz
(D isn't an actual commit; I just wanted a label to refer to it)

Now, if we were to run "git switch -m A" to go back to A,
merge-recursive would need to do a three-way merge of D & A using B as
the base.  merge-recursive would note that A renamed baz->foo, and
that D deleted bar.  So, you'd end up with:
  E: foo

Unfortunately, C != E, so our reversing was unsuccessful.


In summary, this is yet another reason that making --merge the default
for either checkout or switch would be unsafe.



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux