Subject: [PATCH v3 13/13] mergetool: fix running in subdir when rerere enabled

From: Richard Hansen <[email-masked]>
Date: Mon, 9 Jan 2017 00:42:38 -0500

If rerere is enabled and no pathnames are given, run cd_to_toplevel
before running 'git diff --name-only' so that 'git diff --name-only'
sees the files named by 'git rerere remaining', which outputs
pathnames relative to the top-level directory.

The cd_to_toplevel command could be run after 'git rerere remaining',
but it is run before just in case 'git rerere remaining' is ever
changed to print pathnames relative to the current working directory
rather than relative to the top-level directory.

An alternative approach would be to unconditionally convert all
relative pathnames (including the orderfile pathname) to be relative
to the top-level directory and then run cd_to_toplevel before 'git
diff --name-only', but unfortunately 'git rev-parse --prefix' requires
valid pathnames, which would break some valid use cases.

This fixes a regression introduced in
57937f70a09c12ef484c290865dac4066d207c9c (v2.11.0).

Signed-off-by: Richard Hansen <[email-masked]>
---
 git-mergetool.sh     | 32 ++++++++++++++++++++++++++++++++
 t/t7610-mergetool.sh |  2 +-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/git-mergetool.sh b/git-mergetool.sh
index b506896dc..22f56c25a 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -456,6 +456,28 @@ main () {
 
 	if test $# -eq 0 && test -e "$GIT_DIR/MERGE_RR"
 	then
+		# The pathnames output by the 'git rerere remaining'
+		# command below are relative to the top-level
+		# directory but the 'git diff --name-only' command
+		# further below expects the pathnames to be relative
+		# to the current working directory.  Thus, we cd to
+		# the top-level directory before running 'git diff
+		# --name-only'.  We change directories even earlier
+		# (before running 'git rerere remaining') in case 'git
+		# rerere remaining' is ever changed to output
+		# pathnames relative to the current working directory.
+		#
+		# Changing directories breaks a relative $orderfile
+		# pathname argument, so fix it up to be relative to
+		# the top-level directory.
+
+		prefix=$(git rev-parse --show-prefix) || exit 1
+		cd_to_toplevel
+		if test -n "$orderfile"
+		then
+			orderfile=$(git rev-parse --prefix "$prefix" "$orderfile") || exit 1
+		fi
+
 		set -- $(git rerere remaining)
 		if test $# -eq 0
 		then
@@ -463,6 +485,16 @@ main () {
 		fi
 	fi
 
+	# Note:  The pathnames output by 'git diff --name-only' are
+	# relative to the top-level directory, but it expects input
+	# pathnames to be relative to the current working directory.
+	# Thus:
+	#   * Either cd_to_toplevel must not be run before this or all
+	#     relative input pathnames must be converted to be
+	#     relative to the top-level directory (or absolute).
+	#   * Either cd_to_toplevel must be run after this or all
+	#     relative output pathnames must be converted to be
+	#     relative to the current working directory (or absolute).
 	files=$(git -c core.quotePath=false \
 		diff --name-only --diff-filter=U \
 		${orderfile:+"-O$orderfile"} -- "$@")
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index b36fde1c0..a55bf67e1 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -234,7 +234,7 @@ test_expect_success 'mergetool merges all from subdir (rerere disabled)' '
 	)
 '
 
-test_expect_failure 'mergetool merges all from subdir (rerere enabled)' '
+test_expect_success 'mergetool merges all from subdir (rerere enabled)' '
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
 	test_config rerere.enabled true &&
-- 
2.11.0.390.gc69c2f50cf-goog