258 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			258 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
| #!/bin/sh
 | |
| # Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>
 | |
| #
 | |
| # Perform a directory diff between commits in the repository using
 | |
| # the external diff or merge tool specified in the user's config.
 | |
| 
 | |
| USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*]
 | |
| 
 | |
|     --cached     Compare to the index rather than the working tree.
 | |
| 
 | |
|     --copy-back  Copy files back to the working tree when the diff
 | |
|                  tool exits (in case they were modified by the
 | |
|                  user).  This option is only valid if the diff
 | |
|                  compared with the working tree.
 | |
| 
 | |
|     -x=<command>
 | |
|     --extcmd=<command>  Specify a custom command for viewing diffs.
 | |
|                  git-diffall ignores the configured defaults and
 | |
|                  runs $command $LOCAL $REMOTE when this option is
 | |
|                  specified. Additionally, $BASE is set in the
 | |
|                  environment.
 | |
| '
 | |
| 
 | |
| SUBDIRECTORY_OK=1
 | |
| . "$(git --exec-path)/git-sh-setup"
 | |
| 
 | |
| TOOL_MODE=diff
 | |
| . "$(git --exec-path)/git-mergetool--lib"
 | |
| 
 | |
| merge_tool="$(get_merge_tool)"
 | |
| if test -z "$merge_tool"
 | |
| then
 | |
| 	echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
 | |
| 	usage
 | |
| fi
 | |
| 
 | |
| start_dir=$(pwd)
 | |
| 
 | |
| # All the file paths returned by the diff command are relative to the root
 | |
| # of the working copy. So if the script is called from a subdirectory, it
 | |
| # must switch to the root of working copy before trying to use those paths.
 | |
| cdup=$(git rev-parse --show-cdup) &&
 | |
| cd "$cdup" || {
 | |
| 	echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
 | |
| 	exit 1
 | |
| }
 | |
| 
 | |
| # set up temp dir
 | |
| tmp=$(perl -e 'use File::Temp qw(tempdir);
 | |
| 	$t=tempdir("/tmp/git-diffall.XXXXX") or exit(1);
 | |
| 	print $t') || exit 1
 | |
| trap 'rm -rf "$tmp"' EXIT
 | |
| 
 | |
| left=
 | |
| right=
 | |
| paths=
 | |
| dashdash_seen=
 | |
| compare_staged=
 | |
| merge_base=
 | |
| left_dir=
 | |
| right_dir=
 | |
| diff_tool=
 | |
| copy_back=
 | |
| 
 | |
| while test $# != 0
 | |
| do
 | |
| 	case "$1" in
 | |
| 	-h|--h|--he|--hel|--help)
 | |
| 		usage
 | |
| 		;;
 | |
| 	--cached)
 | |
| 		compare_staged=1
 | |
| 		;;
 | |
| 	--copy-back)
 | |
| 		copy_back=1
 | |
| 		;;
 | |
| 	-x|--e|--ex|--ext|--extc|--extcm|--extcmd)
 | |
| 		if test $# = 1
 | |
| 		then
 | |
| 			echo You must specify the tool for use with --extcmd
 | |
| 			usage
 | |
| 		else
 | |
| 			diff_tool=$2
 | |
| 			shift
 | |
| 		fi
 | |
| 		;;
 | |
| 	--)
 | |
| 		dashdash_seen=1
 | |
| 		;;
 | |
| 	-*)
 | |
| 		echo Invalid option: "$1"
 | |
| 		usage
 | |
| 		;;
 | |
| 	*)
 | |
| 		# could be commit, commit range or path limiter
 | |
| 		case "$1" in
 | |
| 		*...*)
 | |
| 			left=${1%...*}
 | |
| 			right=${1#*...}
 | |
| 			merge_base=1
 | |
| 			;;
 | |
| 		*..*)
 | |
| 			left=${1%..*}
 | |
| 			right=${1#*..}
 | |
| 			;;
 | |
| 		*)
 | |
| 			if test -n "$dashdash_seen"
 | |
| 			then
 | |
| 				paths="$paths$1 "
 | |
| 			elif test -z "$left"
 | |
| 			then
 | |
| 				left=$1
 | |
| 			elif test -z "$right"
 | |
| 			then
 | |
| 				right=$1
 | |
| 			else
 | |
| 				paths="$paths$1 "
 | |
| 			fi
 | |
| 			;;
 | |
| 		esac
 | |
| 		;;
 | |
| 	esac
 | |
| 	shift
 | |
| done
 | |
| 
 | |
| # Determine the set of files which changed
 | |
| if test -n "$left" && test -n "$right"
 | |
| then
 | |
| 	left_dir="cmt-$(git rev-parse --short $left)"
 | |
| 	right_dir="cmt-$(git rev-parse --short $right)"
 | |
| 
 | |
| 	if test -n "$compare_staged"
 | |
| 	then
 | |
| 		usage
 | |
| 	elif test -n "$merge_base"
 | |
| 	then
 | |
| 		git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
 | |
| 	else
 | |
| 		git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
 | |
| 	fi
 | |
| elif test -n "$left"
 | |
| then
 | |
| 	left_dir="cmt-$(git rev-parse --short $left)"
 | |
| 
 | |
| 	if test -n "$compare_staged"
 | |
| 	then
 | |
| 		right_dir="staged"
 | |
| 		git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
 | |
| 	else
 | |
| 		right_dir="working_tree"
 | |
| 		git diff --name-only "$left" -- $paths >"$tmp/filelist"
 | |
| 	fi
 | |
| else
 | |
| 	left_dir="HEAD"
 | |
| 
 | |
| 	if test -n "$compare_staged"
 | |
| 	then
 | |
| 		right_dir="staged"
 | |
| 		git diff --name-only --cached -- $paths >"$tmp/filelist"
 | |
| 	else
 | |
| 		right_dir="working_tree"
 | |
| 		git diff --name-only -- $paths >"$tmp/filelist"
 | |
| 	fi
 | |
| fi
 | |
| 
 | |
| # Exit immediately if there are no diffs
 | |
| if test ! -s "$tmp/filelist"
 | |
| then
 | |
| 	exit 0
 | |
| fi
 | |
| 
 | |
| if test -n "$copy_back" && test "$right_dir" != "working_tree"
 | |
| then
 | |
| 	echo "--copy-back is only valid when diff includes the working tree."
 | |
| 	exit 1
 | |
| fi
 | |
| 
 | |
| # Create the named tmp directories that will hold the files to be compared
 | |
| mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
 | |
| 
 | |
| # Populate the tmp/right_dir directory with the files to be compared
 | |
| while read name
 | |
| do
 | |
| 	if test -n "$right"
 | |
| 	then
 | |
| 		ls_list=$(git ls-tree $right "$name")
 | |
| 		if test -n "$ls_list"
 | |
| 		then
 | |
| 			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
 | |
| 			git show "$right":"$name" >"$tmp/$right_dir/$name" || true
 | |
| 		fi
 | |
| 	elif test -n "$compare_staged"
 | |
| 	then
 | |
| 		ls_list=$(git ls-files -- "$name")
 | |
| 		if test -n "$ls_list"
 | |
| 		then
 | |
| 			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
 | |
| 			git show :"$name" >"$tmp/$right_dir/$name"
 | |
| 		fi
 | |
| 	else
 | |
| 		if test -e "$name"
 | |
| 		then
 | |
| 			mkdir -p "$tmp/$right_dir/$(dirname "$name")"
 | |
| 			cp "$name" "$tmp/$right_dir/$name"
 | |
| 		fi
 | |
| 	fi
 | |
| done < "$tmp/filelist"
 | |
| 
 | |
| # Populate the tmp/left_dir directory with the files to be compared
 | |
| while read name
 | |
| do
 | |
| 	if test -n "$left"
 | |
| 	then
 | |
| 		ls_list=$(git ls-tree $left "$name")
 | |
| 		if test -n "$ls_list"
 | |
| 		then
 | |
| 			mkdir -p "$tmp/$left_dir/$(dirname "$name")"
 | |
| 			git show "$left":"$name" >"$tmp/$left_dir/$name" || true
 | |
| 		fi
 | |
| 	else
 | |
| 		if test -n "$compare_staged"
 | |
| 		then
 | |
| 			ls_list=$(git ls-tree HEAD "$name")
 | |
| 			if test -n "$ls_list"
 | |
| 			then
 | |
| 				mkdir -p "$tmp/$left_dir/$(dirname "$name")"
 | |
| 				git show HEAD:"$name" >"$tmp/$left_dir/$name"
 | |
| 			fi
 | |
| 		else
 | |
| 			mkdir -p "$tmp/$left_dir/$(dirname "$name")"
 | |
| 			git show :"$name" >"$tmp/$left_dir/$name"
 | |
| 		fi
 | |
| 	fi
 | |
| done < "$tmp/filelist"
 | |
| 
 | |
| LOCAL="$tmp/$left_dir"
 | |
| REMOTE="$tmp/$right_dir"
 | |
| 
 | |
| if test -n "$diff_tool"
 | |
| then
 | |
| 	export BASE
 | |
| 	eval $diff_tool '"$LOCAL"' '"$REMOTE"'
 | |
| else
 | |
| 	run_merge_tool "$merge_tool" false
 | |
| fi
 | |
| 
 | |
| # Copy files back to the working dir, if requested
 | |
| if test -n "$copy_back" && test "$right_dir" = "working_tree"
 | |
| then
 | |
| 	cd "$start_dir"
 | |
| 	git_top_dir=$(git rev-parse --show-toplevel)
 | |
| 	find "$tmp/$right_dir" -type f |
 | |
| 	while read file
 | |
| 	do
 | |
| 		cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
 | |
| 	done
 | |
| fi
 |