git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

From
Junio C Hamano <gitster@pobox.com>
Date
Mar 4, 2011, 19:05 UTC
Message-ID
<7v39n27llq.fsf@alter.siamese.dyndns.org>
In-Reply-To
<7vei6m7muw.fsf@alter.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
Show 11 quoted lines
> 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@pobox.com>
---
 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",
Previous: Junio C HamanoNext: Linus Torvalds
Message 7 of 34 in “[BUG] git-am silently applying patches incorrectly”
  1. Colin GuthrieMar 4, 2011
  2. Drew NorthupMar 4, 2011
  3. Colin GuthrieMar 4, 2011
  4. Junio C HamanoMar 4, 2011
  5. Junio C HamanoMar 4, 2011
  6. Junio C HamanoMar 4, 2011
  7. Junio C HamanoMar 4, 2011
  8. Linus TorvaldsMar 4, 2011
  9. Junio C HamanoMar 4, 2011
  10. Alexander MiselerMar 4, 2011
  11. Junio C HamanoMar 4, 2011
  12. Colin GuthrieMar 4, 2011
  13. Junio C HamanoMar 4, 2011
  14. Junio C HamanoMar 4, 2011
  15. Colin GuthrieMar 5, 2011
  16. Junio C HamanoMar 6, 2011
  17. Junio C HamanoMar 6, 2011
  18. Jonathan NiederMar 6, 2011
  19. Junio C HamanoMar 6, 2011
  20. Colin GuthrieMar 7, 2011
  21. Alexander MiselerMar 4, 2011
  22. Junio C HamanoMar 5, 2011
  23. Junio C HamanoMar 4, 2011
  24. Drew NorthupMar 4, 2011
  25. 0/2 i18n: add ngettext stubJonathan Nieder, Mar 9, 2011
  26. 1/2 i18n: add stub ngettext implementationJonathan Nieder, Mar 9, 2011
  27. 2/2 i18n: avoid conflict with ngettext from libintlJonathan Nieder, Mar 9, 2011
  28. Junio C HamanoMar 9, 2011
  29. Jonathan NiederMar 9, 2011
  30. Junio C HamanoMar 9, 2011
  31. i18n: add stub Q_() wrapper for ngettextJonathan Nieder, Mar 10, 2011
  32. Junio C HamanoMar 10, 2011
  33. Ævar Arnfjörð BjarmasonMar 10, 2011
  34. Ævar Arnfjörð BjarmasonMar 10, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.