[git] Simple tool to 'accept theirs' or 'accept mine' on a whole file using git

The ideal situation for resolving conflicts is when you know ahead of time which way you want to resolve them and can pass the -Xours or -Xtheirs recursive merge strategy options. Outside of this I can see three scenarious:

  1. You want to just keep a single version of the file (this should probably only be used on unmergeable binary files, since otherwise conflicted and non-conflicted files may get out of sync with each other).
  2. You want to simply decide all of the conflicts in a particular direction.
  3. You need to resolve some conflicts manually and then resolve all of the rest in a particular direction.

To address these three scenarios you can add the following lines to your .gitconfig file (or equivalent):

[merge]
  conflictstyle = diff3
[mergetool.getours]
  cmd = git-checkout --ours ${MERGED}
  trustExitCode = true
[mergetool.mergeours]
  cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keepours]
  cmd = sed -i '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
  trustExitCode = true
[mergetool.gettheirs]
  cmd = git-checkout --theirs ${MERGED}
  trustExitCode = true
[mergetool.mergetheirs]
  cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keeptheirs]
  cmd = sed -i '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
  trustExitCode = true

The get(ours|theirs) tool just keeps the respective version of the file and throws away all of the changes from the other version (so no merging occurs).

The merge(ours|theirs) tool re-does the three way merge from the local, base, and remote versions of the file, choosing to resolve conflicts in the given direction. This has some caveats, specifically: it ignores the diff options that were passed to the merge command (such as algorithm and whitespace handling); does the merge cleanly from the original files (so any manual changes to the file are discarded, which could be good or bad); and has the advantage that it cannot be confused by diff markers that are supposed to be in the file.

The keep(ours|theirs) tool simply edits out the diff markers and enclosed sections, detecting them by regular expression. This has the advantage that it preserves the diff options from the merge command and allows you to resolve some conflicts by hand and then automatically resolve the rest. It has the disadvantage that if there are other conflict markers in the file it could get confused.

These are all used by running git mergetool -t (get|merge|keep)(ours|theirs) [<filename>] where if <filename> is not supplied it processes all conflicted files.

Generally speaking, assuming you know there are no diff markers to confuse the regular expression, the keep* variants of the command are the most powerful. If you leave the mergetool.keepBackup option unset or true then after the merge you can diff the *.orig file against the result of the merge to check that it makes sense. As an example, I run the following after the mergetool just to inspect the changes before committing:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

Note: If the merge.conflictstyle is not diff3 then the /^|||||||/ pattern in the sed rule needs to be /^=======/ instead.