threads / patch / 27213

patch, 2 partsmerge-one-file and GIT_WORK_TREE

Subject: [PATCH 0/2] merge-one-file and GIT_WORK_TREE

## tl;dr

6 messages between Apr 28, 2011 and Apr 29, 2011. Diffs are folded; open one to read it.

replies: 5people: 3as markdown or json

Jeff King· Apr 28, 2011, 22:38 UTC · lore
[argh, resend, actually remembering to cc the list this time]

At GitHub, we're using read-tree, merge-index, and merge-one-file to do trivial content-level merges from bare repositories (without having to check out the entire repository contents each time).

However, we noticed a bug in git-merge-one-file when used with GIT_WORK_TREE; it can silently create bogus results that ignore the "theirs" side of files needing content-level merging.

The problem is that merge-one-file simply assumes it is in the root of the working tree without any checking. The only two places we use merge-one-file in git itself are in the octopus and resolve strategies. I think normal use is fine, because "git merge" will have changed to the toplevel of the worktree already.

So I doubt anybody else is being affected by this. But we do expose the commands for general use, with no disclaimer or check on the working tree status or location. And the resulting bogus merge is a nasty surprise.

  [1/2]: add tests for merge-index / merge-one-file
  [2/2]: merge-one-file: fix broken merges with GIT_WORK_TREE
-Peff
Jeff King· Apr 28, 2011, 22:39 UTC · re: Jeff King · lore

[PATCH 1/2] add tests for merge-index / merge-one-file

There were no tests for either, except a brief use in t1200-tutorial.

These tools are not used much these days, as most people use the merge-recursive strategy, which handles everything internally. However, they are used by the "octopus" and "resolve" strategies, as well as any custom strategies or merge scripts people have built around them.

For example, together with read-tree, they are the simplest way to do a basic content-level merge without checking out the entire repository contents beforehand.

This script adds a basic test of the tools to perform one content-level merge. It also shows a failure of the tools to work properly in the face of GIT_WORK_TREE.

Signed-off-by: Jeff King <peff@peff.net>
---
We could also exercise the more exotic "both sides added" code paths of
merge-one-file. But anybody who cares about that these days is almost
certainly using "read-tree --aggressive" anyway.
 t/t6060-merge-index.sh |   70 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 70 insertions(+), 0 deletions(-)
 create mode 100755 t/t6060-merge-index.sh
Show changes to t/t6060-merge-index.sh +70 −0
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
new file mode 100755
index 0000000..8f8ec77
--- /dev/null
+++ b/t/t6060-merge-index.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='basic git merge-index / git-merge-one-file tests'
+. ./test-lib.sh
+
+test_expect_success 'setup diverging branches' '
+	for i in 1 2 3 4 5 6 7 8 9 10; do
+		echo $i
+	done >file &&
+	git add file &&
+	git commit -m base &&
+	git tag base &&
+	sed s/2/two/ <file >tmp &&
+	mv tmp file &&
+	git commit -a -m two &&
+	git tag two &&
+	git checkout -b other HEAD^ &&
+	sed s/10/ten/ <file >tmp &&
+	mv tmp file &&
+	git commit -a -m ten &&
+	git tag ten
+'
+
+cat >expect-merged <<'EOF'
+1
+two
+3
+4
+5
+6
+7
+8
+9
+ten
+EOF
+
+test_expect_success 'read-tree does not resolve content merge' '
+	git read-tree -i -m base ten two &&
+	echo file >expect &&
+	git diff-files --name-only --diff-filter=U >unmerged &&
+	test_cmp expect unmerged
+'
+
+test_expect_success 'git merge-index git-merge-one-file resolves' '
+	git merge-index git-merge-one-file -a &&
+	git diff-files --name-only --diff-filter=U >unmerged &&
+	>expect &&
+	test_cmp expect unmerged &&
+	test_cmp expect-merged file &&
+	git cat-file blob :file >file-index &&
+	test_cmp expect-merged file-index
+'
+
+test_expect_failure 'merge-one-file respects GIT_WORK_TREE' '
+	git clone . bare.git &&
+	(cd bare.git &&
+	 mkdir work &&
+	 GIT_WORK_TREE=$PWD/work &&
+	 export GIT_WORK_TREE &&
+	 GIT_INDEX_FILE=$PWD/merge.index &&
+	 export GIT_INDEX_FILE &&
+	 git read-tree -i -m base ten two &&
+	 git merge-index git-merge-one-file -a &&
+	 git cat-file blob :file >work/file-index
+	) &&
+	test_cmp expect-merged bare.git/work/file &&
+	test_cmp expect-merged bare.git/work/file-index
+'
+
+test_done
-- 
1.7.5.rc3.17.g5e09b
Johannes Sixt· Apr 29, 2011, 06:12 UTC · re: Jeff King · lore

Re: [PATCH 1/2] add tests for merge-index / merge-one-file

Am 4/29/2011 0:39, schrieb Jeff King:
> +	sed s/2/two/ <file >tmp &&
You are not using -e; will all seds out there grok this?
-- Hannes
Junio C Hamano· Apr 29, 2011, 06:25 UTC · re: Johannes Sixt · lore

Re: [PATCH 1/2] add tests for merge-index / merge-one-file

Johannes Sixt <j.sixt@viscovery.net> writes:
> Am 4/29/2011 0:39, schrieb Jeff King:
>> +	sed s/2/two/ <file >tmp &&
>
> You are not using -e; will all seds out there grok this?
That should be Ok, as long as the script is not ambiguous.
Jeff King· Apr 28, 2011, 22:41 UTC · re: Jeff King · lore

[PATCH 2/2] merge-one-file: fix broken merges with GIT_WORK_TREE

The merge-one-file tool predates the invention of GIT_WORK_TREE. By the time GIT_WORK_TREE was invented, most people were using the merge-recursive strategy, which handles resolving internally. Therefore these features have had very little testing together.

For the most part, merge-one-file just works with GIT_WORK_TREE; most of its heavy lifting is done by plumbing commands which do respect GIT_WORK_TREE properly. The one exception is a shell redirection which touches the worktree directly, writing results to the wrong place in the presence of a GIT_WORK_TREE variable.

This means that merges won't even fail; they will silently produce incorrect results, throwing out the entire "theirs" side of files which need content-level merging!

This patch properly prefixes the path in the shell redirection. An alternative strategy would be to have merge-one-file use git-sh-setup, and cd_to_toplevel beforehand. I opted for the minimal fix here, as changing directories would impact where merge-one-file is unpacking and working with files.

While we're at it, we'll also error-check the call to cat. Merging a file in a subdirectory could in fact fail, as the redirection relies on the "checkout-index" call just prior to create leading directories. But we never noticed, since we ignored the error return from running cat.

Signed-off-by: Jeff King <peff@peff.net>
---
 git-merge-one-file.sh  |    4 +++-
 t/t6060-merge-index.sh |    2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)
Show changes to 2 files +4 −2

git-merge-one-file.sh, t/t6060-merge-index.sh

diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index b86402a..0b5e3f6 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -132,7 +132,9 @@ case "${1:-.}${2:-.}${3:-.}" in
 
 	# Create the working tree file, using "our tree" version from the
 	# index, and then store the result of the merge.
-	git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4"
+	git checkout-index -f --stage=2 -- "$4" &&
+		cat "$src1" >"${GIT_WORK_TREE:+$GIT_WORK_TREE/}$4" ||
+		exit 1
 	rm -f -- "$orig" "$src1" "$src2"
 
 	if [ "$6" != "$7" ]; then
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
index 8f8ec77..fd90e70 100755
--- a/t/t6060-merge-index.sh
+++ b/t/t6060-merge-index.sh
@@ -51,7 +51,7 @@ test_expect_success 'git merge-index git-merge-one-file resolves' '
 	test_cmp expect-merged file-index
 '
 
-test_expect_failure 'merge-one-file respects GIT_WORK_TREE' '
+test_expect_success 'merge-one-file respects GIT_WORK_TREE' '
 	git clone . bare.git &&
 	(cd bare.git &&
 	 mkdir work &&
-- 
1.7.5.rc3.17.g5e09b
Junio C Hamano· Apr 29, 2011, 16:27 UTC · re: Jeff King · lore

Re: [PATCH 0/2] merge-one-file and GIT_WORK_TREE

Jeff King <peff@peff.net> writes:
> The problem is that merge-one-file simply assumes it is in the root of
> the working tree without any checking.

It is kind of surprising that there was only one place that you needed to touch ;-).

Thanks.

← back to recent threads