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:
git-mergetool
or p4mergetool
specifically to NOT make this assumption and still show a conflict?Do I need to run both commands:
git merge dev
git mergetool
to 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"
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.
That's exactly right.
- Is there a way to run/configure
git-mergetool
orp4mergetool
specifically to NOT make this assumption and still show a conflict?
git mergetool
does not make the assumption, so we can conclude that p4mergetool
must be the one doing it. I don't have p4mergetool
, though, so I don't know if it has a configuration knob to change this.
- Do I need to run both commands ...
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 merge
would have combined the changes on its own, without calling it a conflict.
(Note that using -X ours
or -X theirs
would 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 having p4mergetool
do its own thing.)
What git mergetool
does is extract the three files—merge base, left or local or --ours
, and right or remote or --theirs
versions of some file—and run some other, non-Git program on the three files. When that program finishes, git mergetool
can 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 mergetool
believes this), git mergetool
runs git 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, but git status
calls the file unmerged and tells you whether it is in all three slots (UU
) or in just the left or right one (UD
and DU
respectively). I'm not sure off hand what git status
says 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 add
or perhaps git rm
to overwrite the higher-numbered slots. In the rare case of wanting to restore a resolved file to conflicted state, git checkout -m
can do that.