Skip to content
This repository
branch: master
executable file 110 lines (100 sloc) 4.409 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#!/bin/bash
# A better vimdiff mergetool for Git
#
# 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 will 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.
#
# 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"
printf -v QBASE '%q' "${BASE}"
printf -v QLOCAL '%q' "${LOCAL}"
printf -v QREMOTE '%q' "${REMOTE}"
printf -v QMERGED '%q' "${MERGED}"

# Temporary files for left and right side
LCONFL="${MERGED}.REMOTE.$$.tmp"
RCONFL="${MERGED}.LOCAL.$$.tmp"

# 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 '/^<<<<<<< /,/^=======$/d' -e '/^>>>>>>> /d' "${MERGED}" > "${LCONFL}"
sed -e '/^=======$/,/^>>>>>>> /d' -e '/^<<<<<<< /d' "${MERGED}" > "${RCONFL}"

# Fire up vimdiff
$cmd -f -R -d "${LCONFL}" "${RCONFL}" \
    -c ":set noro" \
    -c ":tabe $QLOCAL" -c ":vert diffs $QBASE" -c ":vert diffs $QREMOTE" \
    -c ":winc t" -c ":tabe $QMERGED" -c ":tabfir"

EC=$?

# Overwrite $MERGED only if vimdiff exits cleanly.
if [[ $EC == "0" ]] ; then
cat "${LCONFL}" > "${MERGED}"
fi

exit $EC
Something went wrong with that request. Please try again.