forked from pool/patch
250 lines
6.3 KiB
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)
|
|
{
|