SHA256
3
0
forked from pool/patch
patch/diff3-style-merges-overlap.diff

250 lines
6.3 KiB
Diff

From: Andreas Gruenbacher <agruen@suse.de>
Subject: diff3-style merges: overlapping merges
In some situations, hunks in a patch may apply so that their context
lines overlap. Patch never commits context lines to the output until
their contents are fully determined.
The diff3-style merge format may include context lines. For example,
when he following patch:
# --- a
# +++ b
# @@ -2,3 +2,3 @@
# x
# -3
# +3b
# x
is applied to `seq 1 6` file with --merge=all, this is what you get (with
fuzz 1):
1
<<<<<<< a
x
3
x
||||||| b
x
3b
x
=======
2
3b
4
>>>>>>>
5
6
At this point, the first four lines of the output file are "frozen", and
the next hunk cannot modify the fourth line anymore. This can cause the diff3-
style merge format to reject hunks that would otherwise be accepted. To
avoid this regression, this patch adds delayed printing of the end marker,
with this effect on the merge format:
1
<<<<<<< a
x
3
x
||||||| b
x
3b
x
=======
2
3b
>>>>>>>
<<<<<<< a
x
4
x
||||||| b
x
4b
x
=======
4b
5
>>>>>>>
6
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
patch.c | 71 +++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 48 insertions(+), 23 deletions(-)
Index: b/patch.c
===================================================================
--- a/patch.c
+++ b/patch.c
@@ -63,6 +63,7 @@ static LINENUM locate_hunk (LINENUM);
static bool apply_hunk (struct outstate *, LINENUM);
static bool common_context(LINENUM, LINENUM, LINENUM);
static bool merge_hunk (struct outstate *, LINENUM, LINENUM);
+static void merge_ends_here(struct outstate *outstate);
static bool copy_till (struct outstate *, LINENUM);
static bool patch_match (LINENUM, LINENUM, LINENUM, LINENUM);
static bool similar (char const *, size_t, char const *, size_t);
@@ -81,6 +82,8 @@ static void usage (FILE *, int) __attrib
enum mergetype { SHOW_ALL = 1, SHOW_FUZZ = 2, MERGE_REJECTS = 4 };
static enum mergetype mergetype;
+static LINENUM merge_end = -1;
+static const char *merge_name;
const char *file_label[2];
static bool make_backups;
@@ -1361,7 +1364,7 @@ static bool merge_hunk (struct outstate
register LINENUM lastline = pch_ptrn_lines ();
register LINENUM new = lastline + 1;
register LINENUM pat_end = pch_end ();
- register LINENUM merge, merge_end;
+ register LINENUM merge, R_merge_end;
register FILE *fp = outstate->ofp;
bool succeeded = true;
const char *name;
@@ -1381,32 +1384,32 @@ static bool merge_hunk (struct outstate
if (fuzz != -1) {
/* Hide common suffix context */
- merge_end = where + lastline - 1;
+ R_merge_end = where + lastline - 1;
while (old <= lastline && new <= pat_end) {
- if (!common_context(merge_end, lastline, pat_end))
+ if (!common_context(R_merge_end, lastline, pat_end))
break;
lastline--;
pat_end--;
- merge_end--;
+ R_merge_end--;
}
} else {
LINENUM overlap = pch_suffix_context ();
/* Hide common suffix context: check how much overlap we have! */
- merge_end = merge - 1;
+ R_merge_end = merge - 1;
while (overlap) {
LINENUM n = 0;
- merge_end += overlap;
+ R_merge_end += overlap;
while (n < overlap && old <= lastline && new <= pat_end) {
- if (!common_context(merge_end, lastline, pat_end))
+ if (!common_context(R_merge_end, lastline, pat_end))
break;
lastline--;
pat_end--;
- merge_end--;
+ R_merge_end--;
n++;
}
- merge_end -= overlap - n;
+ R_merge_end -= overlap - n;
if (n == overlap)
break;
lastline += n;
@@ -1419,7 +1422,8 @@ static bool merge_hunk (struct outstate
if (last_frozen_line < merge) {
if (!copy_till(outstate, merge - 1))
return false;
- }
+ } else
+ merge_ends_here(outstate);
/* "From" lines in the patch */
name = file_label[OLD] ? file_label[OLD] : pch_name(OLD);
@@ -1458,25 +1462,41 @@ static bool merge_hunk (struct outstate
write_fatal ();
outstate->after_newline = true;
- succeeded = apply_hunk(outstate, where) &&
- copy_till(outstate, merge_end);
+ succeeded = apply_hunk(outstate, where);
}
+ assert(merge_end == -1);
+ merge_end = R_merge_end;
+
/* If the merge result and the new file are the same, label the merge
result with the new file's name. */
- name = fuzz ? NULL : (file_label[NEW] ? file_label[NEW] : pch_name(NEW));
- if (!name)
- name = "";
- fprintf(fp, outstate->after_newline + "\n>>>>>>>%*s\n",
- strlen(name) ? strlen(name) + 1 : 0, name);
- if (ferror (fp))
- write_fatal ();
- outstate->after_newline = true;
+ merge_name = fuzz ? NULL : (file_label[NEW] ? file_label[NEW] :
+ pch_name(NEW));
outstate->zero_output = false;
return succeeded;
}
+static void
+merge_ends_here(struct outstate *outstate)
+{
+ register FILE *fp = outstate->ofp;
+
+ if (merge_end != -1)
+ {
+ if (!merge_name)
+ merge_name = "";
+ fprintf(fp, outstate->after_newline + "\n>>>>>>>%*s\n",
+ strlen(merge_name) ? strlen(merge_name) + 1 : 0,
+ merge_name);
+ if (ferror (fp))
+ write_fatal ();
+ outstate->after_newline = true;
+ outstate->zero_output = false;
+ merge_end = -1;
+ }
+}
+
/* Create an output file. */
static FILE *
@@ -1516,6 +1536,7 @@ static bool
copy_till (register struct outstate *outstate, register LINENUM lastline)
{
register LINENUM R_last_frozen_line = last_frozen_line;
+ register LINENUM R_merge_end = (merge_end != -1) ? merge_end : lastline;
register FILE *fp = outstate->ofp;
register char const *s;
size_t size;
@@ -1527,6 +1548,9 @@ copy_till (register struct outstate *out
}
while (R_last_frozen_line < lastline)
{
+ if (R_merge_end == R_last_frozen_line)
+ merge_ends_here(outstate);
+
s = ifetch (++R_last_frozen_line, false, &size);
if (size)
{
@@ -1538,6 +1562,8 @@ copy_till (register struct outstate *out
}
}
last_frozen_line = R_last_frozen_line;
+ if (merge_end != -1)
+ merge_ends_here(outstate);
return true;
}
@@ -1555,9 +1581,8 @@ spew_output (struct outstate *outstate)
format_linenum (numbuf1, last_frozen_line));
}
- if (last_frozen_line < input_lines)
- if (! copy_till (outstate, input_lines))
- return false;
+ if (! copy_till (outstate, input_lines))
+ return false;
if (outstate->ofp && ! outfile)
{