Re: [BUG] git-am silently applying patches incorrectly

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Junio C Hamano <gitster@xxxxxxxxx> writes:

> Looking at it closer, however, I noticed that the false hit (i.e. "two
> blocks closed, a blank line, return 0 and the end of function") in this
> particular case only appears because we applied the previous hunk.  In the
> version of the file in 0ce3017b, there is only one such place and there
> should be no ambiguity in the patch application.
>
> The problem we are seeing is caused only because we look at the result of
> application of the previous hunks in the patch and incrementally try to
> apply the remaining hunks.  So clearly "git apply" can and should be fixed
> for this case by teaching find_pos() not to report a match on a line that
> was touched by application of the previous hunk.

And here is a quick and dirty fix to do something like that.  It assumes
that the hunks for a single file being patched are already sorted in the
ascending order (which should be the case), and may regress cases where we
used to find a match even when the version you are patching has moved
functions around in the file by failing to notice a match.  And it does
get the same result as your GNU patch test.

-- >8 --
Subject: [PATCH] apply: do not look behind beyond what we already patched

When looking for a place to apply a hunk, we used to check lines that
match the preimage of it, starting from the line that the patch wants to
apply the hunk at, with increasing offsets in both ways until we find a
match.

Colin Guthrie found an interesting case where this misapplied a patch that
wanted to touch a preimage that consists of "two block closed '<indent>}<LF>',
a blank line, '<indent>return 0;<LF>', the function closed '}<LF>'".  The
target version of the file originally had only one such location, but the hunk
immediately before that created another such preimage, and find_pos() happily
reported that the preimage matched what the hunk wanted to modify.  Oops.

By recording where the last hunk is applied, and limiting the search done
by find_pos() to the rest of the file, we can reduce such an accident.
Ideally, we should not simply limit the upward search like this patch
does, but skip the regions that were touched by previous hunks, but this
approach was much simpler ;-)

I also considered to teach apply_one_fragment() to take the offset we have
found while applying the previous hunk into account when looking for a
match with find_pos(), but dismissed that approach, because it would
sometimes work better but sometimes worse, depending on the difference
between the version the patch was created against and the version the
patch is being applied.

Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx>
---
 builtin/apply.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin/apply.c b/builtin/apply.c
index 14951da..30857c6 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -211,6 +211,7 @@ struct line {
  */
 struct image {
 	char *buf;
+	size_t last_match;
 	size_t len;
 	size_t nr;
 	size_t alloc;
@@ -2323,11 +2324,11 @@ static int find_pos(struct image *img,
 			return try_lno;
 
 	again:
-		if (backwards_lno == 0 && forwards_lno == img->nr)
+		if (backwards_lno <= img->last_match && forwards_lno == img->nr)
 			break;
 
 		if (i & 1) {
-			if (backwards_lno == 0) {
+			if (backwards_lno <= img->last_match) {
 				i++;
 				goto again;
 			}
@@ -2648,6 +2649,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
 				" to apply fragment at %d\n",
 				leading, trailing, applied_pos+1);
 		update_image(img, applied_pos, &preimage, &postimage);
+		img->last_match = applied_pos;
 	} else {
 		if (apply_verbosely)
 			error("while searching for:\n%.*s",
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]