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.