SHA256
3
0
forked from pool/patch
patch/diff3-style-merges-base.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 *