#!/bin/bash
#
# Wrapper around git diff that accumulates changes,
# and then calls vim in diff mode on all the changes.
# Unlike gitdifftool, it invokes vim on all the files in
# one go, thereby enabling navigation across all pairs
# of files.
#
# Based on a hack by John Reese ([email protected]).
#
# See http://www.kernel.org/pub/software/scm/git/docs/git.html (GIT_EXTERNAL_DIFF)
set -e
# Writes vimdiff rc to temp directory. All in an effort to keep this shell
# script self-contained.
function writerc {
cat >$DIFFDIR/rc <<'ENDRC'
" Color scheme fix:
highlight DiffAdd ctermbg=White
highlight DiffChange ctermbg=White
highlight DiffText ctermbg=Yellow
" Given two open panes, turn on diff mode " and leave the cursor on the left.
" The left one is always the original, so it's set readonly
fun! Diffball()
" help |window-move-cursor| explain these
wincmd w
if expand("%") == "null"
wincmd w | diffthis | set nofoldenable foldcolumn=0
normal _
else
wincmd t | diffthis | set nofoldenable foldcolumn=0 readonly
wincmd w | diffthis | set nofoldenable foldcolumn=0
normal ]c " go to first change
endif
" wincmd t
" goto 1
endf
" Go back to the beginning of the list of file pairs.
fun! Diffrewind()
only | set nodiff | rewind | set equalalways | vsplit
wincmd w | next
call Diffball()
endf
" Step to the next pair of files and diff them.
fun! Diffnext()
wincmd t | set nodiff | 2next
wincmd w | set nodiff | 2next
call Diffball()
endf
" Step to the previous pair of files and diff them.
fun! Diffprev()
wincmd t | set nodiff | 2prev
wincmd w | set nodiff | 2prev
call Diffball()
endf
fun! Diffquit()
only | q!
endf
call Diffrewind()
" Bind control-n to next and control-a to rewind.
map :call Diffnext()
map :call Diffprev()
map :call Diffrewind()
map :call Diffquit()
ENDRC
}
function main {
export TMPDIR=${TMPDIR:-/tmp}
export DIFFDIR=$(mktemp -d $TMPDIR/git-vimdiff.XXXXXX)
export GIT_VIMDIFF_HELPER=true
GIT_EXTERNAL_DIFF="$0" git-diff $*
if [ -f $DIFFDIR/args ]; then
GIT_ROOT=$(git rev-parse --show-cdup)
if [ ! -z $GIT_ROOT ]; then
cd $GIT_ROOT
fi
writerc
vim -S $DIFFDIR/rc $(cat $DIFFDIR/args)
fi
}
# Invoked on every set of changed files. The
# "old" file is copied to a temp directory and
# made read-only. The new file is copied to
# a temp directory only if it's different
# than the current copy.
function helper {
GIT_PATH=${GIT_ROOT}${1}
OLD_FILE=$2
OLD_HEX=$3
OLD_MODE=$4
NEW_FILE=$5
NEW_MODE=$6
mkdir -p $DIFFDIR/files
# Make a temp dir so that all old file copies are unique.
OUT_DIR=$(mktemp -d $DIFFDIR/files.XXXXXX)
COPY_OF_OLD_FILE=$OUT_DIR/$(basename $GIT_PATH)
cp $OLD_FILE $COPY_OF_OLD_FILE
LEFT=$COPY_OF_OLD_FILE
chmod ugo-w $COPY_OF_OLD_FILE
if [ $GIT_PATH = $NEW_FILE ]; then
RIGHT=$NEW_FILE
else
if diff -q $GIT_PATH $NEW_FILE; then
RIGHT=$GIT_PATH
else
COPY_OF_NEW_FILE=$OUT_DIR/right-$(basename $GIT_PATH)
cp $NEW_FILE $COPY_OF_NEW_FILE
RIGHT=$COPY_OF_NEW_FILE
fi
fi
# Accumulate the arguments for diff.
echo $LEFT $RIGHT >> $DIFFDIR/args
}
if [ $GIT_VIMDIFF_HELPER ]; then
helper "$@"
else
main "$@"
fi