diff options
-rwxr-xr-x | dotfiles/.local/bin/diffconflicts | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/dotfiles/.local/bin/diffconflicts b/dotfiles/.local/bin/diffconflicts new file mode 100755 index 0000000..ccd68f0 --- /dev/null +++ b/dotfiles/.local/bin/diffconflicts @@ -0,0 +1,108 @@ +#!/bin/bash +# A better vimdiff mergetool for Git +# +# NOTE: Now also an installable Vim plugin: +# https://github.com/whiteinge/diffconflicts +# +# Git does a great job resolving merge conflicts automatically but there are +# times when human intervention is still needed. Git resolves all the conflicts +# that it is able to and finally wraps each conflict it cannot resolve within +# special markers which must be resolved by a human. +# +# The vertical format and lack of syntax highlighting in the plain conflict +# marker layout makes it difficult to spot subtle conflicts such as +# single-character changes and this is where a two-way diff really shines! +# To see this explained using screenshots, see: +# http://vim.wikia.com/wiki/A_better_Vimdiff_Git_mergetool +# +# This script, when used as a Git mergetool, opens each "side" of the conflict +# markers in a two-way vimdiff window. This combines all the awesome of Git's +# automatic merging savvy with the awesome and simplicity of a simple two-way +# diff. +# +# Add this mergetool to your ~/.gitconfig (you can substitute gvim for vim): +# +# git config --global merge.tool diffconflicts +# git config --global mergetool.diffconflicts.cmd 'diffconflicts vim $BASE $LOCAL $REMOTE $MERGED' +# git config --global mergetool.diffconflicts.trustExitCode true +# git config --global mergetool.diffconflicts.keepBackup false +# +# The next time you perform a merge with conflicts, invoke this tool with the +# following command. (Of course you can set it as your default mergetool as +# well.) +# +# git mergetool --tool diffconflicts +# +# This tool can open three tabs in Vim that each provide a different way to +# view the conflicts. You can resolve the conflicts in the first tab and save +# and exit the file. This will also mark the conflict as resolved in Git. +# Only the first tab is opened by default so Vim loads more quickly and also +# because the other tabs are only occasionally useful for tough merges. To open +# Tab2 and Tab3 use the mapping <leader>D. +# +# Tab1 is a two-way diff of just the conflicts. Resolve the conflicts here +# and save the file. +# +--------------------------------+ +# | LCONFL | RCONFL | +# +--------------------------------+ +# Tab2 is a three-way diff of the original files and the merge base. This is +# the traditional three-way diff. Although noisy, it is occasionally useful +# to view the three original states of the conflicting file before the merge. +# +--------------------------------+ +# | LOCAL | BASE | REMOTE | +# +--------------------------------+ +# Tab3 is the in-progress merge that Git has written to the filesystem +# containing the conflict markers. I.e., the file you would normally edit by +# hand when not using a mergetool. +# +--------------------------------+ +# | <<<<<<< HEAD | +# | LCONFL | +# | ======= | +# | RCONFL | +# | >>>>>>> someref | +# +--------------------------------+ +# +# Workflow: +# +# 1. Save your changes to the LCONFL temporary file (the left window on the +# first tab; also the only file that isn't read-only). +# 2. The LOCAL, BASE, and REMOTE versions of the file are available in the +# second tabpage if you want to look at them. +# 3. When vimdiff exits cleanly, the file containing the conflict markers +# will be updated with the contents of your LCONFL file edits. +# +# NOTE: Use :cq to abort the merge and exit Vim with an error code. + +if [[ -z $@ || $# != "5" ]] ; then + echo -e "Usage: $0 \$EDITOR \$BASE \$LOCAL \$REMOTE \$MERGED" + exit 1 +fi + +cmd="$1" +BASE="$2" +LOCAL="$3" +REMOTE="$4" +MERGED="$5" +LCONFL="${MERGED}.$$.LCONFL" +RCONFL="${MERGED}.$$.RCONFL" + +# Always delete our temp files; Git will handle it's own temp files. +trap 'rm -f "'"${LCONFL}"'" "'"${RCONFL}"'"' SIGINT SIGTERM EXIT + +# Remove the conflict markers for each 'side' and put each into a temp file +sed -E -e '/^=======\r?$/,/^>>>>>>> /d' -e '/^<<<<<<< /d' "${MERGED}" > "${LCONFL}" +sed -E -e '/^<<<<<<< /,/^=======\r?$/d' -e '/^>>>>>>> /d' "${MERGED}" > "${RCONFL}" + +# Fire up vimdiff +$cmd -f -R -d "${LCONFL}" "${RCONFL}" \ + -c ":set noro" \ + -c ":nmap <leader>D :tabe $REMOTE<CR>:vert diffs $BASE<CR>:vert diffs $LOCAL<CR>:winc t<CR>:tabe $MERGED<CR>:tabfir<CR>" + +EC=$? + +# Overwrite $MERGED only if vimdiff exits cleanly. +if [[ $EC == "0" ]] ; then + cat "${LCONFL}" > "${MERGED}" +fi + +exit $EC |