forked from pool/patch
294 lines
7.9 KiB
Diff
294 lines
7.9 KiB
Diff
From: Andreas Gruenbacher <agruen@suse.de>
|
|
Subject: diff3-style merges
|
|
|
|
Implement a diff3-style merge format: with the --merge option alone,
|
|
all hunks that apply without fuzz will be applied as usual, and
|
|
hunks that apply within the allowed fuzz limit will be bracketed as:
|
|
|
|
<<<<<<<
|
|
old lines from patch
|
|
|||||||
|
|
new lines from patch
|
|
=======
|
|
merge result
|
|
>>>>>>>
|
|
|
|
When the --show-all option is given in addition, hunks that apply without
|
|
fuzz will be bracketed as:
|
|
|
|
<<<<<<<
|
|
old lines from patch
|
|
=======
|
|
merge result
|
|
>>>>>>>
|
|
|
|
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|
|
|
---
|
|
patch.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
|
1 file changed, 170 insertions(+), 5 deletions(-)
|
|
|
|
Index: b/patch.c
|
|
===================================================================
|
|
--- a/patch.c
|
|
+++ b/patch.c
|
|
@@ -61,6 +61,8 @@ struct outstate
|
|
static FILE *create_output_file (char const *, int);
|
|
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 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);
|
|
@@ -77,6 +79,9 @@ static void reinitialize_almost_everythi
|
|
static void remove_if_needed (char const *, int volatile *);
|
|
static void usage (FILE *, int) __attribute__((noreturn));
|
|
|
|
+enum mergetype { SHOW_ALL = 1, SHOW_FUZZ = 2 };
|
|
+static enum mergetype mergetype;
|
|
+
|
|
static bool make_backups;
|
|
static bool backup_if_mismatch;
|
|
static char const *version_control;
|
|
@@ -117,7 +122,7 @@ int
|
|
main (int argc, char **argv)
|
|
{
|
|
char const *val;
|
|
- bool somefailed = false;
|
|
+ bool somemerged = false, somefailed = false;
|
|
struct outstate outstate;
|
|
char numbuf[LINENUM_LENGTH_BOUND + 1];
|
|
|
|
@@ -184,6 +189,7 @@ main (int argc, char **argv)
|
|
reinitialize_almost_everything()
|
|
) { /* for each patch in patch file */
|
|
int hunk = 0;
|
|
+ int merged = 0;
|
|
int failed = 0;
|
|
bool mismatch = false;
|
|
char *outname = outfile ? outfile : inname;
|
|
@@ -295,8 +301,17 @@ main (int argc, char **argv)
|
|
} else if (!where) {
|
|
goto skip_hunk;
|
|
} else {
|
|
- if (!apply_hunk (&outstate, where))
|
|
- goto skip_hunk;
|
|
+ if ((mergetype & SHOW_ALL) ||
|
|
+ (fuzz && (mergetype & SHOW_FUZZ))) {
|
|
+ if (merge_hunk(&outstate, where, fuzz)) {
|
|
+ merged++;
|
|
+ mismatch = true;
|
|
+ } else
|
|
+ goto skip_hunk;
|
|
+ } else {
|
|
+ if (!apply_hunk (&outstate, where))
|
|
+ goto skip_hunk;
|
|
+ }
|
|
}
|
|
|
|
if (verbosity == VERBOSE
|
|
@@ -325,6 +340,9 @@ skip_hunk:
|
|
format_linenum (numbuf, newwhere));
|
|
}
|
|
|
|
+ if (merged)
|
|
+ somemerged = true;
|
|
+
|
|
if (!skip_rest_of_patch)
|
|
{
|
|
if (got_hunk < 0 && using_plan_a)
|
|
@@ -374,7 +392,8 @@ skip_hunk:
|
|
else
|
|
{
|
|
if (! outstate.zero_output
|
|
- && pch_says_nonexistent (! reverse))
|
|
+ && pch_says_nonexistent (! reverse)
|
|
+ && !merged)
|
|
{
|
|
mismatch = true;
|
|
if (verbosity != SILENT)
|
|
@@ -468,7 +487,7 @@ skip_hunk:
|
|
if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
|
|
write_fatal ();
|
|
cleanup ();
|
|
- if (somefailed)
|
|
+ if (somemerged || somefailed)
|
|
exit (1);
|
|
return 0;
|
|
}
|
|
@@ -544,6 +563,7 @@ static struct option const longopts[] =
|
|
{"quoting-style", required_argument, NULL, CHAR_MAX + 8},
|
|
{"unified-reject-files", no_argument, NULL, CHAR_MAX + 9},
|
|
{"global-reject-file", required_argument, NULL, CHAR_MAX + 10},
|
|
+ {"merge", required_argument, NULL, CHAR_MAX + 11},
|
|
{NULL, no_argument, NULL, 0}
|
|
};
|
|
|
|
@@ -571,6 +591,7 @@ static char const *const option_help[] =
|
|
" -r FILE --reject-file=FILE Output rejects to FILE.",
|
|
"",
|
|
" -D NAME --ifdef=NAME Make merged if-then-else output using NAME.",
|
|
+" --merge={fuzz,all} Produce a diff3-style merge.",
|
|
" -E --remove-empty-files Remove output files that are empty after patching.",
|
|
"",
|
|
" -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).",
|
|
@@ -809,6 +830,23 @@ get_some_switches (void)
|
|
case CHAR_MAX + 10:
|
|
global_reject = savestr (optarg);
|
|
break;
|
|
+ case CHAR_MAX + 11:
|
|
+ {
|
|
+ char *tok = strtok(optarg, ",");
|
|
+
|
|
+ do {
|
|
+ if (!strcmp(tok, "fuzz"))
|
|
+ mergetype |= SHOW_FUZZ;
|
|
+ else if (!strcmp(tok, "all"))
|
|
+ mergetype |= SHOW_ALL;
|
|
+ else {
|
|
+ fprintf(stderr, "%s: invalid merge option %s\n",
|
|
+ program_name, quotearg(tok));
|
|
+ usage (stderr, 2);
|
|
+ }
|
|
+ } while ((tok = strtok(NULL, ",")));
|
|
+ }
|
|
+ break;
|
|
default:
|
|
usage (stderr, 2);
|
|
}
|
|
@@ -1264,6 +1302,133 @@ apply_hunk (struct outstate *outstate, L
|
|
return true;
|
|
}
|
|
|
|
+static bool common_context(LINENUM where, LINENUM old, LINENUM new)
|
|
+{
|
|
+ size_t size;
|
|
+ const char *line;
|
|
+
|
|
+ if (pch_char(old) != ' ' || pch_char(new) != ' ')
|
|
+ return false;
|
|
+
|
|
+ line = ifetch (where, false, &size);
|
|
+ return size &&
|
|
+ (canonicalize ?
|
|
+ (similar(pfetch(old), pch_line_len(old), line, size) &&
|
|
+ similar(pfetch(new), pch_line_len(new), line, size)) :
|
|
+ (size == pch_line_len(old) && size == pch_line_len(new) &&
|
|
+ memcmp(line, pfetch(old), size) == 0 &&
|
|
+ memcmp(line, pfetch(new), size) == 0));
|
|
+}
|
|
+
|
|
+/* A FUZZ value of -1 indicates that the hunk could not be applied. */
|
|
+
|
|
+static bool merge_hunk (struct outstate *outstate, LINENUM where, LINENUM fuzz)
|
|
+{
|
|
+ register LINENUM old = 1;
|
|
+ register LINENUM lastline = pch_ptrn_lines ();
|
|
+ register LINENUM new = lastline + 1;
|
|
+ register LINENUM pat_end = pch_end ();
|
|
+ register LINENUM merge, merge_end;
|
|
+ register FILE *fp = outstate->ofp;
|
|
+ bool succeeded = true;
|
|
+
|
|
+ while (pch_char(new) == '=' || pch_char(new) == '\n' /* ??? */)
|
|
+ new++;
|
|
+
|
|
+ /* Hide common prefix context */
|
|
+ merge = where;
|
|
+ while (old <= lastline && new <= pat_end) {
|
|
+ if (!common_context(merge, old, new))
|
|
+ break;
|
|
+ old++;
|
|
+ new++;
|
|
+ merge++;
|
|
+ }
|
|
+
|
|
+ if (fuzz != -1) {
|
|
+ /* Hide common suffix context */
|
|
+ merge_end = where + lastline - 1;
|
|
+ while (old <= lastline && new <= pat_end) {
|
|
+ if (!common_context(merge_end, lastline, pat_end))
|
|
+ break;
|
|
+ lastline--;
|
|
+ pat_end--;
|
|
+ merge_end--;
|
|
+ }
|
|
+ } else {
|
|
+ LINENUM overlap = pch_suffix_context ();
|
|
+
|
|
+ /* Hide common suffix context: check how much overlap we have! */
|
|
+ merge_end = merge - 1;
|
|
+ while (overlap) {
|
|
+ LINENUM n = 0;
|
|
+
|
|
+ merge_end += overlap;
|
|
+ while (n < overlap && old <= lastline && new <= pat_end) {
|
|
+ if (!common_context(merge_end, lastline, pat_end))
|
|
+ break;
|
|
+ lastline--;
|
|
+ pat_end--;
|
|
+ merge_end--;
|
|
+ n++;
|
|
+ }
|
|
+ merge_end -= overlap - n;
|
|
+ if (n == overlap)
|
|
+ break;
|
|
+ lastline += n;
|
|
+ pat_end += n;
|
|
+ overlap--;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ assert (outstate->after_newline);
|
|
+ if (last_frozen_line < merge) {
|
|
+ if (!copy_till(outstate, merge - 1))
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* "From" lines in the patch */
|
|
+ fprintf(fp, outstate->after_newline + "\n<<<<<<<\n");
|
|
+ if (ferror (fp))
|
|
+ write_fatal ();
|
|
+ outstate->after_newline = true;
|
|
+ while (old <= lastline) {
|
|
+ outstate->after_newline = pch_write_line(old, fp);
|
|
+ old++;
|
|
+ }
|
|
+
|
|
+ if (fuzz) {
|
|
+ /* "To" lines in the patch */
|
|
+ fprintf(fp, outstate->after_newline + "\n|||||||\n");
|
|
+ if (ferror (fp))
|
|
+ write_fatal ();
|
|
+ outstate->after_newline = true;
|
|
+ while (new <= pat_end) {
|
|
+ outstate->after_newline = pch_write_line(new, fp);
|
|
+ new++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (fuzz != -1) {
|
|
+ /* Merge result */
|
|
+ fprintf(fp, outstate->after_newline + "\n=======\n");
|
|
+ if (ferror (fp))
|
|
+ write_fatal ();
|
|
+ outstate->after_newline = true;
|
|
+
|
|
+ succeeded = apply_hunk(outstate, where) &&
|
|
+ copy_till(outstate, merge_end);
|
|
+ }
|
|
+
|
|
+ fprintf(fp, outstate->after_newline + "\n>>>>>>>\n");
|
|
+ if (ferror (fp))
|
|
+ write_fatal ();
|
|
+ outstate->after_newline = true;
|
|
+
|
|
+ outstate->zero_output = false;
|
|
+ return succeeded;
|
|
+}
|
|
+
|
|
/* Create an output file. */
|
|
|
|
static FILE *
|