I’m checked out on feature/my-branch and running git merge dev. The conflict markers added to the file are:
<<<<<<< HEAD
let foo = "foo"
let bar = "bar"
||||||| merged common ancestors
let baz = "baz"
let bar = "bar"
=======
let baz = "baz"
let qux = "qux"
>>>>>>> dev
I then run git mergetool. I have p4mergetool set as my mergetool and it seems to be working. My .gitconfig:
[merge]
tool = p4mergetool
conflictstyle = diff3
[mergetool "p4mergetool"]
cmd = /Applications/p4merge.app/Contents/Resources/launchp4merge $PWD/$BASE $PWD/$REMOTE $PWD/$LOCAL $PWD/$MERGED
trustExitCode = true
The git mergetool auto resolves the above conflict (0 conflicts shown in the tool) as:
let foo = "foo"
let qux = "qux"
This sort of makes sense: even though HEAD and dev are in conflict, we can see that one branch updated one line and the other branch updated the other line. So we can probably assume what we want.
My questions are:
- Is there a way to run/configure
git-mergetoolorp4mergetoolspecifically to NOT make this assumption and still show a conflict? Do I need to run both commands:
git merge dev git mergetoolto have this conflict solved this automatically? I.e. produce the output:
let foo = "foo" let qux = "qux"
Said another way: is there a git merge strategy/arguments that I can use to simply run the merge command to produce:
let foo = "foo"
let qux = "qux"
That's exactly right.
git mergetooldoes not make the assumption, so we can conclude thatp4mergetoolmust be the one doing it. I don't havep4mergetool, though, so I don't know if it has a configuration knob to change this.Yes: Git's own merge is kind of dumb and merely notices that, gosh, this range of changes on the left abuts (touches) or overlaps this other range of changes on the right, so let's call it a conflict and make the user handle it. In this case, the left-side change (from baz to foo) came just before the right-side change (bar to qux), so the two ranges touched at the edges and Git itself declared a conflict. Had there been one line in between,
git mergewould have combined the changes on its own, without calling it a conflict.(Note that using
-X oursor-X theirswould resolve the conflict by discarding one side's change and using only the other's. Again you'd never even get to the point of havingp4mergetooldo its own thing.)What
git mergetooldoes is extract the three files—merge base, left or local or--ours, and right or remote or--theirsversions of some file—and run some other, non-Git program on the three files. When that program finishes,git mergetoolcan either trust its exit code to decide whether the tool itself merged the files correctly, or run some file comparisons, or just ask you directly: is the work-tree copy of the file now the correct merged copy?If the work-tree copy is now the correct final result (or at least
git mergetoolbelieves this),git mergetoolrunsgit add, which marks the conflict as being resolved.1 Otherwise, it leaves the conflict in place.(I tend to just resolve merge conflicts in my editor.)
1A file is unmerged if there are any copies of it in any nonzero index slots. A file is merged if there is one copy of it, in slot zero—the normal slot number. Except using some of Git's low-level diagnostic-ish commands (
git ls-index --stage, really), you can't actually see these staging slot numbers, butgit statuscalls the file unmerged and tells you whether it is in all three slots (UU) or in just the left or right one (UDandDUrespectively). I'm not sure off hand whatgit statussays about a file in slot 1, which would represent the merge base copy of a file with a rename/rename conflict.Normally we just use
git addor perhapsgit rmto overwrite the higher-numbered slots. In the rare case of wanting to restore a resolved file to conflicted state,git checkout -mcan do that.