There is not a really convenient way to do this, but you can build one from the tools Git provides. The main tool required is this git merge-file
, which performs a three-way merge on a single trio of file-versions, i.e., base + ours + theirs. It accepts --ours
and --theirs
options to resolve conflicts the same way that -X ours
and -X theirs
does for an overall merge, i.e., it doesn't just take our file or their file, it only takes ours or theirs at conflict-points.
That's all great, but, where you do get the three versions? Git has stopped with a merge conflict on, say, main.py
. In your work-tree, main.py
contains the mess that Git left behind, with the <<<<<<< ... >>>>>>>
markers around the conflicted lines. But git merge-file
needs three un-marked-up input files, for the merge base version, the "ours" version, and the "theirs" version. But those three files are in the index! If file F had a conflict, there is a :1:F
with the merge base version, :2:F
with ours, and :3:F
with theirs.
To get them, you can either use git show
or git checkout-index
. The latter is actually the right tool: git mergetool
uses git checkout-index
, with this little shell function:
checkout_staged_file () {
tmpfile=$(expr \
"$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
: '\([^ ]*\) ')
if test $? -eq 0 && test -n "$tmpfile"
then
mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
else
>"$3"
fi
}
Invoked as checkout_staged_file 1 main.py main.py.base
, for instance, it extracts the stage-1 (merge base) copy of main.py
to main.py.base
. Repeat with 2 and 3, and suitable variants on the third argument, to get all three files out. Then, run git merge-file
on the three files in the way described in the git merge-file
documentation.
(See the git mergetool
source code for more. It's just a big shell script, so it's pretty easy to read and modify for your own purposes.)