I've been trying to set up a custom merge driver for merge conflicts in Git and have it mostly working, but can't figure out why it doesn't automatically stage successfully-resolved conflicts like the built-in text
driver does.
This is my custom driver definition, it's relatively simple:
[merge "auto-pro"]
name = Auto-merge as much as possible and don't add conflict markers
driver = git merge-file -p %A %O %B > %P || (git merge-file --ours -p %A %O %B > %P && false)
recursive = binary
The idea is that it tries to do a normal-ish merge using git merge-file
first. If that fails, it leaves conflict markers in the file and returns a non-zero exit code, which leads to the next statement getting executed and running git merge-file
again, but with the --ours
flag to suppress conflict markers for chunks that can't be auto-merged.
This all works pretty much as expected, except that in the "successful auto merge" case (ie, the case where git merge-file
only runs once and returns an exit code of 0), the successfully merged file doesn't end up staged for commit. Why? If I switch back to the default text
driver and redo the merge, it successfully merges and automatically stages the resolved file. How can I get my custom driver to behave like this?
Note: I already tried adding git add
the driver command, but it barfs an error because the merge process is already holding the lock on the Git index.
Finally figured out my problem here. I didn't read the docs for custom merge drivers closely enough and it turns out the driver is supposed to write out the results of the merge to the %A
file, not the %P
file.
However, simply changing the driver to direct the output of git merge-file
to %A
instead of %P
was not working for some reason. Best guess is that it's because I'm on Windows and the merge-file
command was holding open a lock on %A
and preventing it from being directly re-written. Storing the merge result in a temp variable before flushing it to disk solved the problem, which seems to support that theory.
Here is my final merge driver implementation, which works as desired:
[merge "auto-pro"]
name = Auto-merge as much as possible and don't add conflict markers
driver = ~/.git/pro-merge-driver.sh %A %O %B %P
recursive = binary
Contents of ~/.git/pro-merge-driver.sh
:
#!/bin/bash
if AUTO_MERGE_RESULT=`git merge-file -p $1 $2 $3`; then
echo "Auto-merge: $4"
echo "$AUTO_MERGE_RESULT" > $1
exit 0
else
echo "Conflict: $4"
OURS_MERGE_RESULT=`git merge-file --ours -p $1 $2 $3`
echo "$OURS_MERGE_RESULT" > $1
exit 1
fi