Skip to content

Commit

Permalink
apply: do not patch lines that were already patched
Browse files Browse the repository at this point in the history
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, looking forward and backward with increasing offsets
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

                        }
                }

                return 0;
        }

which is a rather unfortunately common pattern.

The target version of the file originally had only one such location, but
the hunk immediately before that created another instance of such block of
lines, and find_pos() happily reported that the preimage of the hunk
matched what it wanted to modify.

Oops.

By marking the lines application of earlier hunks touched and preventing
match_fragment() from considering them as a match with preimage of other
hunks, we can reduce such an accident.

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.

This does _not_ prevent misapplication of patches to a file that has many
similar looking blocks of lines and a preimage cannot identify which one
of them should be applied.  For that, we would need to scan beyond the
first match in find_pos(), and issue a warning (or error out).  That will
be a separate topic.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Junio C Hamano committed Mar 4, 2011
1 parent 964498e commit 9d15860
Showing 1 changed file with 6 additions and 1 deletion.
7 changes: 6 additions & 1 deletion builtin/apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ struct line {
unsigned hash : 24;
unsigned flag : 8;
#define LINE_COMMON 1
#define LINE_PATCHED 2
};

/*
Expand Down Expand Up @@ -2085,7 +2086,8 @@ static int match_fragment(struct image *img,

/* Quick hash check */
for (i = 0; i < preimage_limit; i++)
if (preimage->line[i].hash != img->line[try_lno + i].hash)
if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
(preimage->line[i].hash != img->line[try_lno + i].hash))
return 0;

if (preimage_limit == preimage->nr) {
Expand Down Expand Up @@ -2428,6 +2430,9 @@ static void update_image(struct image *img,
memcpy(img->line + applied_pos,
postimage->line,
postimage->nr * sizeof(*img->line));
for (i = 0; i < postimage->nr; i++)
img->line[applied_pos + i].flag |= LINE_PATCHED;

img->nr = nr;
}

Expand Down

0 comments on commit 9d15860

Please sign in to comment.