threads / bug / 13010

bug in git-apply

Subject: bug in git-apply

## tl;dr

4 messages between Apr 6, 2008 and Apr 7, 2008.

replies: 3people: 2as markdown or json

Karl Hasselström· Apr 6, 2008, 23:53 UTC · lore

StGit's test suite has started failing, and it's due to a change in git that I'm pretty sure is a bug. I built a small test case using just git for easier testing:

  mkdir foo
  cd foo
  git init
  touch xx
  git add xx
  git commit -m 'first'
  git checkout -b branch
  echo aaa >> xx
  git add xx
  git commit -m 'second'
  git checkout master
  echo bbb >> xx
  git add xx
  git commit -m 'third'
  git diff branch^ branch > p
  git apply < p && echo "this should not succeed!"

Basically, we have a file containing "aaa\n", and a patch that tries to take the file from "" to "bbb\n". A recent enough git-apply will accept this patch without complaining, and put "aaa\nbbb\n" in the file.

I bisected, and the culprit was this commit:
commit dc41976a3eed1d9c93fdc08c448bab969db4e0ec
Author: Junio C Hamano <gitster@pobox.com>
Date:   Sat Jan 19 01:58:34 2008 -0800
    builtin-apply.c: push match-beginning/end logic down
    This moves the logic to force match at the beginning and/or at
    the end of the buffer to the actual function that finds the
    match from its caller.  This is a necessary preparation for the
    next step to allow matching disregarding certain differences,
    such as whitespace changes.
    We probably could optimize this even more by taking advantage of
    the fact that match_beginning and match_end forces the match to
    be at an exact location (anchored at the beginning and/or the
    end), but that's for another commit.
    Signed-off-by: Junio C Hamano <gitster@pobox.com>
-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle
Junio C Hamano· Apr 7, 2008, 01:11 UTC · re: Karl Hasselström · lore

Re: bug in git-apply

Karl Hasselström <kha@treskal.com> writes:
> I bisected, and the culprit was this commit:
>
> commit dc41976a3eed1d9c93fdc08c448bab969db4e0ec

I think the issue is much older than that. The faulty one is 4be6096 (apply --unidiff-zero: loosen sanity checks for --unidiff=0 patches, 2006-09-17), which incorrectly loosened the match_beginning/match_end computation too much, whose botch may have been hidden by what the surrounding code did.

And 65aadb9 (apply: force matching at the beginning., 2006-05-24) is equally wrong in that it tried to take hints from leading context lines, to decide if the hunk must match at the beginning, but in this case we can just look at the line number in the hunk to decide.

How about doing this?
 builtin-apply.c           |   29 ++++++++++++++++-------------
 t/t4104-apply-boundary.sh |   13 +++++++++++++
 2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/builtin-apply.c b/builtin-apply.c
index b5f78ac..abe73a0 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1937,21 +1937,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
 	trailing = frag->trailing;
 
 	/*
-	 * If we don't have any leading/trailing data in the patch,
-	 * we want it to match at the beginning/end of the file.
+	 * A hunk to change lines at the beginning would begin with
+	 * @@ -1,L +N,M @@
 	 *
-	 * But that would break if the patch is generated with
-	 * --unified=0; sane people wouldn't do that to cause us
-	 * trouble, but we try to please not so sane ones as well.
+	 * And a hunk to add to an empty file would begin with
+	 * @@ -0,0 +N,M @@
+	 *
+	 * In other words, a hunk that is (frag->oldpos <= 1) with or
+	 * without leading context must match at the beginning.
 	 */
-	if (unidiff_zero) {
-		match_beginning = (!leading && !frag->oldpos);
-		match_end = 0;
-	}
-	else {
-		match_beginning = !leading && (frag->oldpos == 1);
-		match_end = !trailing;
-	}
+	match_beginning = frag->oldpos <= 1;
+
+	/*
+	 * A hunk without trailing lines must match at the end.
+	 * However, we simply cannot tell if a hunk must match end
+	 * from the lack of trailing lines if the patch was generated
+	 * with unidiff without any context.
+	 */
+	match_end = !unidiff_zero && !trailing;
 
 	pos = frag->newpos ? (frag->newpos - 1) : 0;
 	preimage.buf = oldlines;
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index 64f34e3..0007535 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -112,4 +112,17 @@ do
 	'
 done
 
+test_expect_success 'two lines' '
+
+	>file &&
+	git add file &&
+	echo aaa >file &&
+	git diff >patch &&
+	git add file &&
+	echo bbb >file &&
+	git add file &&
+	test_must_fail git apply patch
+
+'
+
 test_done
Karl Hasselström· Apr 7, 2008, 07:04 UTC · re: Junio C Hamano · lore

Re: bug in git-apply

On 2008-04-06 18:11:13 -0700, Junio C Hamano wrote:
> How about doing this?

Well, it does fix the stgit test suite failure, so I'm all for it! (Though I see you've already pushed it to master.) Thanks.

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle
Junio C Hamano· Apr 7, 2008, 07:12 UTC · re: Karl Hasselström · lore

Re: bug in git-apply

Karl Hasselström <kha@treskal.com> writes:
Show 6 quoted lines
> On 2008-04-06 18:11:13 -0700, Junio C Hamano wrote:
>
>> How about doing this?
>
> Well, it does fix the stgit test suite failure, so I'm all for it!
> (Though I see you've already pushed it to master.) Thanks.

This actually is a fix for maint; even though your test case does not let you observe the breakage directly, only because must-match-at-end check prevents the single-liner test hunk from applying cleanly.

← back to recent threads