From e553963af35cf23ea956e9cc70ac100dd8e7574c63432c91e65766da28006154 Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Tue, 3 Feb 2009 23:18:23 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/patch?expand=0&rev=5 --- diff3-style-merges-add-file-labels.diff | 92 ++++ diff3-style-merges-base.diff | 293 +++++++++++++ diff3-style-merges-include-filenames.diff | 73 ++++ diff3-style-merges-locate-merge.diff | 110 +++++ diff3-style-merges-other-strategy.diff | 36 ++ diff3-style-merges-overlap.diff | 249 +++++++++++ diff3-style-merges-pch_name.diff | 281 ++++++++++++ diff3-style-merges-refactoring-2.diff | 61 +++ diff3-style-merges-refactoring.diff | 67 +++ diff3-style-merges-rejects.diff | 116 +++++ diff3-style-merges-tests.diff | 492 ++++++++++++++++++++++ fail.test | 19 - patch.changes | 6 + patch.spec | 40 +- 14 files changed, 1912 insertions(+), 23 deletions(-) create mode 100644 diff3-style-merges-add-file-labels.diff create mode 100644 diff3-style-merges-base.diff create mode 100644 diff3-style-merges-include-filenames.diff create mode 100644 diff3-style-merges-locate-merge.diff create mode 100644 diff3-style-merges-other-strategy.diff create mode 100644 diff3-style-merges-overlap.diff create mode 100644 diff3-style-merges-pch_name.diff create mode 100644 diff3-style-merges-refactoring-2.diff create mode 100644 diff3-style-merges-refactoring.diff create mode 100644 diff3-style-merges-rejects.diff create mode 100644 diff3-style-merges-tests.diff delete mode 100644 fail.test diff --git a/diff3-style-merges-add-file-labels.diff b/diff3-style-merges-add-file-labels.diff new file mode 100644 index 0000000..07add31 --- /dev/null +++ b/diff3-style-merges-add-file-labels.diff @@ -0,0 +1,92 @@ +From: Andreas Gruenbacher +Subject: diff3-style merges: add file labels + +Add a --label=LABEL option for overriding the labels in the diff3-style +format. The option can be given up to three times for overriding the +old, new, and output filename. + +Signed-off-by: Andreas Gruenbacher + +--- + patch.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -82,6 +82,7 @@ static void usage (FILE *, int) __attrib + enum mergetype { SHOW_ALL = 1, SHOW_FUZZ = 2, MERGE_REJECTS = 4 }; + static enum mergetype mergetype; + ++const char *file_label[2]; + static bool make_backups; + static bool backup_if_mismatch; + static char const *version_control; +@@ -538,7 +539,7 @@ reinitialize_almost_everything (void) + skip_rest_of_patch = false; + } + +-static char const shortopts[] = "bB:cd:D:eEfF:g:i:lMnNo:p:r:RstTuvV:x:Y:z:Z"; ++static char const shortopts[] = "bB:cd:D:eEfF:g:i:lL:MnNo:p:r:RstTuvV:x:Y:z:Z"; + static struct option const longopts[] = + { + {"backup", no_argument, NULL, 'b'}, +@@ -553,6 +554,7 @@ static struct option const longopts[] = + {"get", no_argument, NULL, 'g'}, + {"input", required_argument, NULL, 'i'}, + {"ignore-whitespace", no_argument, NULL, 'l'}, ++ {"label", required_argument, NULL, 'L'}, + {"normal", no_argument, NULL, 'n'}, + {"forward", no_argument, NULL, 'N'}, + {"output", required_argument, NULL, 'o'}, +@@ -609,6 +611,7 @@ static char const *const option_help[] = + "", + " -D NAME --ifdef=NAME Make merged if-then-else output using NAME.", + " --merge={rejects,fuzz,all} Produce a diff3-style merge.", ++" -L LABEL --label=LABEL Use LABEL instead of file name in 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).", +@@ -747,6 +750,14 @@ get_some_switches (void) + case 'l': + canonicalize = true; + break; ++ case 'L': ++ if (!file_label[0]) ++ file_label[0] = optarg; ++ else if (!file_label[1]) ++ file_label[1] = optarg; ++ else ++ fatal ("too many file label options"); ++ break; + case 'M': + mergetype |= MERGE_REJECTS; + break; +@@ -1411,7 +1422,7 @@ static bool merge_hunk (struct outstate + } + + /* "From" lines in the patch */ +- name = pch_name(OLD); ++ name = file_label[OLD] ? file_label[OLD] : pch_name(OLD); + if (!name) + name = ""; + fprintf(fp, outstate->after_newline + "\n<<<<<<<%*s\n", +@@ -1426,7 +1437,7 @@ static bool merge_hunk (struct outstate + + if (fuzz) { + /* "To" lines in the patch */ +- name = pch_name(NEW); ++ name = file_label[NEW] ? file_label[NEW] : pch_name(NEW); + if (!name) + name = ""; + fprintf(fp, outstate->after_newline + "\n|||||||%*s\n", +@@ -1453,7 +1464,7 @@ static bool merge_hunk (struct outstate + + /* If the merge result and the new file are the same, label the merge + result with the new file's name. */ +- name = fuzz ? NULL : pch_name(NEW); ++ name = fuzz ? NULL : (file_label[NEW] ? file_label[NEW] : pch_name(NEW)); + if (!name) + name = ""; + fprintf(fp, outstate->after_newline + "\n>>>>>>>%*s\n", diff --git a/diff3-style-merges-base.diff b/diff3-style-merges-base.diff new file mode 100644 index 0000000..19c6acf --- /dev/null +++ b/diff3-style-merges-base.diff @@ -0,0 +1,293 @@ +From: Andreas Gruenbacher +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 + +--- + 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 * diff --git a/diff3-style-merges-include-filenames.diff b/diff3-style-merges-include-filenames.diff new file mode 100644 index 0000000..25dd7c2 --- /dev/null +++ b/diff3-style-merges-include-filenames.diff @@ -0,0 +1,73 @@ +From: Andreas Gruenbacher +Subject: diff3-style merges: include filenames + +Include the filenames from the patch header in the diff3-style format. +Use the name of the output file as the third filename. + + <<<<<<< old-filename + old lines from patch + ||||||| new-filename + new lines from patch + ======= + merge result + >>>>>>> output-filename + +Signed-off-by: Andreas Gruenbacher + +--- + patch.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -1353,6 +1353,7 @@ static bool merge_hunk (struct outstate + register LINENUM merge, merge_end; + register FILE *fp = outstate->ofp; + bool succeeded = true; ++ const char *name; + + while (pch_char(new) == '=' || pch_char(new) == '\n' /* ??? */) + new++; +@@ -1410,7 +1411,11 @@ static bool merge_hunk (struct outstate + } + + /* "From" lines in the patch */ +- fprintf(fp, outstate->after_newline + "\n<<<<<<<\n"); ++ name = pch_name(OLD); ++ 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; +@@ -1421,7 +1426,11 @@ static bool merge_hunk (struct outstate + + if (fuzz) { + /* "To" lines in the patch */ +- fprintf(fp, outstate->after_newline + "\n|||||||\n"); ++ name = 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; +@@ -1442,7 +1451,13 @@ static bool merge_hunk (struct outstate + copy_till(outstate, merge_end); + } + +- fprintf(fp, outstate->after_newline + "\n>>>>>>>\n"); ++ /* If the merge result and the new file are the same, label the merge ++ result with the new file's name. */ ++ name = fuzz ? NULL : 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; diff --git a/diff3-style-merges-locate-merge.diff b/diff3-style-merges-locate-merge.diff new file mode 100644 index 0000000..c81b9a5 --- /dev/null +++ b/diff3-style-merges-locate-merge.diff @@ -0,0 +1,110 @@ +--- + patch.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 72 insertions(+), 6 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -62,6 +62,8 @@ static FILE *create_output_file (char co + static LINENUM locate_hunk (LINENUM); + static bool apply_hunk (struct outstate *, LINENUM); + static bool common_context(LINENUM, LINENUM, LINENUM); ++static LINENUM min_mismatches(LINENUM, LINENUM); ++static LINENUM locate_merge (void); + static bool merge_hunk (struct outstate *, LINENUM, LINENUM); + static void merge_ends_here(struct outstate *outstate); + static bool copy_till (struct outstate *, LINENUM); +@@ -304,15 +306,11 @@ main (int argc, char **argv) + goto skip_hunk; + } else if (!where) { + if (mergetype & MERGE_REJECTS) { +- LINENUM guess = pch_first () + last_offset; +- +- if (merge_hunk(&outstate, guess, -1)) { ++ if (merge_hunk(&outstate, locate_merge(), -1)) { + merged++; + mismatch = 1; +- } else { +- /* FIXME: guess harder! */ ++ } else + goto skip_hunk; +- } + } else + goto skip_hunk; + } else { +@@ -1356,6 +1354,74 @@ static bool common_context(LINENUM where + memcmp(line, pfetch(new), size) == 0)); + } + ++static LINENUM min_mismatches(LINENUM where, LINENUM lowest) ++{ ++ register LINENUM ptrn_lines = pch_ptrn_lines(); ++ register LINENUM old, mismatched = 0; ++ ++ for (old = 1; old <= ptrn_lines; old++, where++) { ++ size_t size; ++ const char *line; ++ ++ line = ifetch (where, false, &size); ++ if (!size || ++ !(canonicalize ? ++ similar(pfetch(old), pch_line_len(old), line, size) : ++ (size == pch_line_len(old) && ++ memcmp(line, pfetch(old), size) == 0))) { ++ mismatched++; ++ if (mismatched >= lowest) ++ break; ++ } ++ } ++ return mismatched; ++} ++ ++static LINENUM locate_merge () ++{ ++ register LINENUM first_guess = pch_first () + last_offset; ++ register LINENUM lowest = input_lines, where = first_guess; ++ register LINENUM offset; ++ LINENUM pat_lines = pch_ptrn_lines(); ++ LINENUM suffix_context = pch_suffix_context (); ++ LINENUM max_where = input_lines - (pat_lines - suffix_context) + 1; ++ LINENUM min_where = last_frozen_line + 1; ++ LINENUM max_pos_offset = max_where - first_guess; ++ LINENUM max_neg_offset = first_guess - min_where; ++ LINENUM max_offset = (max_pos_offset < max_neg_offset ++ ? max_neg_offset : max_pos_offset); ++ ++ /* Do not try lines <= 0. */ ++ if (first_guess <= max_neg_offset) ++ max_neg_offset = first_guess - 1; ++ ++ for (offset = 0; offset <= max_offset; offset++) { ++ if (offset <= max_pos_offset) { ++ register LINENUM mismatched; ++ ++ mismatched = min_mismatches(first_guess - offset, lowest); ++ if (mismatched < lowest) { ++ lowest = mismatched; ++ where = first_guess - offset; ++ if (lowest == 1) ++ break; ++ } ++ } ++ if (0 < offset && offset <= max_neg_offset) { ++ register LINENUM mismatched; ++ ++ mismatched = min_mismatches(first_guess + offset, lowest); ++ if (mismatched < lowest) { ++ lowest = mismatched; ++ where = first_guess + offset; ++ if (lowest == 1) ++ break; ++ } ++ } ++ } ++ return where; ++} ++ + /* A FUZZ value of -1 indicates that the hunk could not be applied. */ + + static bool merge_hunk (struct outstate *outstate, LINENUM where, LINENUM fuzz) diff --git a/diff3-style-merges-other-strategy.diff b/diff3-style-merges-other-strategy.diff new file mode 100644 index 0000000..ae4cf35 --- /dev/null +++ b/diff3-style-merges-other-strategy.diff @@ -0,0 +1,36 @@ +--- + patch.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -1459,6 +1459,18 @@ static bool merge_hunk (struct outstate + R_merge_end--; + } + } else { ++#if 1 ++ /* Hide common suffix context */ ++ R_merge_end = where + lastline - 1; ++ while (old <= lastline && new <= pat_end) { ++ if (!common_context(R_merge_end, lastline, pat_end)) ++ break; ++ lastline--; ++ pat_end--; ++ R_merge_end--; ++ } ++ R_merge_end = merge - 1; ++#else + LINENUM overlap = pch_suffix_context (); + + /* Hide common suffix context: check how much overlap we have! */ +@@ -1482,6 +1494,8 @@ static bool merge_hunk (struct outstate + pat_end += n; + overlap--; + } ++ /*assert(R_merge_end == merge - 1);*/ ++#endif + } + + assert (outstate->after_newline); diff --git a/diff3-style-merges-overlap.diff b/diff3-style-merges-overlap.diff new file mode 100644 index 0000000..bf8ff43 --- /dev/null +++ b/diff3-style-merges-overlap.diff @@ -0,0 +1,249 @@ +From: Andreas Gruenbacher +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 + +--- + 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) + { diff --git a/diff3-style-merges-pch_name.diff b/diff3-style-merges-pch_name.diff new file mode 100644 index 0000000..7c0db30 --- /dev/null +++ b/diff3-style-merges-pch_name.diff @@ -0,0 +1,281 @@ +From: Andreas Gruenbacher +Subject: diff3-style merges: pch_name() + +Make a pch_name() function available for accessing the old and the new +filename in the patch header. + +Signed-off-by: Andreas Gruenbacher + +--- + pch.c | 88 ++++++++++++++++++++++++++++++++++-------------------------------- + pch.h | 3 ++ + 2 files changed, 49 insertions(+), 42 deletions(-) + +Index: b/pch.c +=================================================================== +--- a/pch.c ++++ b/pch.c +@@ -43,6 +43,7 @@ static int p_says_nonexistent[2]; /* [0] + 1 for existent and probably (but not necessarily) empty, + 2 for nonexistent */ + static int p_rfc934_nesting; /* RFC 934 nesting level */ ++static char *p_name[3]; /* filenames in patch headers */ + static time_t p_timestamp[2]; /* timestamps in patch headers */ + static off_t p_filesize; /* size of the patch file */ + static LINENUM p_first; /* 1st line number */ +@@ -70,8 +71,6 @@ static LINENUM p_efake = -1; /* end of + static LINENUM p_bfake = -1; /* beg of faked up lines */ + static char *p_c_function; /* the C function a hunk is in */ + +-enum nametype { OLD, NEW, INDEX, NONE }; +- + static char *scan_linenum (char *, LINENUM *); + static enum diff intuit_diff_type (void); + static enum nametype best_name (char * const *, int const *); +@@ -300,13 +299,17 @@ intuit_diff_type (void) + register bool this_is_a_command = false; + register bool stars_this_line = false; + enum nametype i; +- char *name[3]; + struct stat st[3]; + int stat_errno[3]; + int version_controlled[3]; + register enum diff retval; + +- name[OLD] = name[NEW] = name[INDEX] = 0; ++ for (i = OLD; i <= INDEX; i++) ++ if (p_name[i]) { ++ free (p_name[i]); ++ p_name[i] = 0; ++ } ++ + version_controlled[OLD] = -1; + version_controlled[NEW] = -1; + version_controlled[INDEX] = -1; +@@ -365,16 +368,16 @@ intuit_diff_type (void) + p_strip_trailing_cr = strip_trailing_cr; + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) +- name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); ++ p_name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + else if (strnEQ(s, "+++ ", 4)) + { + /* Swap with NEW below. */ +- name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); ++ p_name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + p_strip_trailing_cr = strip_trailing_cr; + } + else if (strnEQ(s, "Index:", 6)) + { +- name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); ++ p_name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); + p_strip_trailing_cr = strip_trailing_cr; + } + else if (strnEQ(s, "Prereq:", 7)) { +@@ -410,7 +413,7 @@ intuit_diff_type (void) + if (strnEQ(t, "--- ", 4)) + { + time_t timestamp = (time_t) -1; +- name[NEW] = fetchname (t+4, strippath, ×tamp); ++ p_name[NEW] = fetchname (t+4, strippath, ×tamp); + if (timestamp != (time_t) -1) + { + p_timestamp[NEW] = timestamp; +@@ -430,13 +433,13 @@ intuit_diff_type (void) + if ((diff_type == NO_DIFF || diff_type == UNI_DIFF) + && strnEQ(s, "@@ -", 4)) { + +- /* `name' and `p_timestamp' are backwards; swap them. */ ++ /* `p_name' and `p_timestamp' are backwards; swap them. */ + time_t ti = p_timestamp[OLD]; + p_timestamp[OLD] = p_timestamp[NEW]; + p_timestamp[NEW] = ti; +- t = name[OLD]; +- name[OLD] = name[NEW]; +- name[NEW] = t; ++ t = p_name[OLD]; ++ p_name[OLD] = p_name[NEW]; ++ p_name[NEW] = t; + + s += 4; + if (s[0] == '0' && !ISDIGIT (s[1])) +@@ -451,9 +454,9 @@ intuit_diff_type (void) + p_start = this_line; + p_sline = p_input_line; + retval = UNI_DIFF; +- if (! ((name[OLD] || ! p_timestamp[OLD]) +- && (name[NEW] || ! p_timestamp[NEW])) +- && ! name[INDEX]) ++ if (! ((p_name[OLD] || ! p_timestamp[OLD]) ++ && (p_name[NEW] || ! p_timestamp[NEW])) ++ && ! p_name[INDEX]) + { + char numbuf[LINENUM_LENGTH_BOUND + 1]; + say ("missing header for unified diff at line %s of patch\n", +@@ -492,9 +495,9 @@ intuit_diff_type (void) + next_intuit_at (saved_p_base, saved_p_bline); + } + +- if (! ((name[OLD] || ! p_timestamp[OLD]) +- && (name[NEW] || ! p_timestamp[NEW])) +- && ! name[INDEX]) ++ if (! ((p_name[OLD] || ! p_timestamp[OLD]) ++ && (p_name[NEW] || ! p_timestamp[NEW])) ++ && ! p_name[INDEX]) + { + char numbuf[LINENUM_LENGTH_BOUND + 1]; + say ("missing header for context diff at line %s of patch\n", +@@ -543,23 +546,23 @@ intuit_diff_type (void) + { + enum nametype i0 = NONE; + +- if (! posixly_correct && (name[OLD] || name[NEW]) && name[INDEX]) ++ if (! posixly_correct && (p_name[OLD] || p_name[NEW]) && p_name[INDEX]) + { +- free (name[INDEX]); +- name[INDEX] = 0; ++ free (p_name[INDEX]); ++ p_name[INDEX] = 0; + } + + for (i = OLD; i <= INDEX; i++) +- if (name[i]) ++ if (p_name[i]) + { +- if (i0 != NONE && strcmp (name[i0], name[i]) == 0) ++ if (i0 != NONE && strcmp (p_name[i0], p_name[i]) == 0) + { + /* It's the same name as before; reuse stat results. */ + stat_errno[i] = stat_errno[i0]; + if (! stat_errno[i]) + st[i] = st[i0]; + } +- else if (stat (name[i], &st[i]) != 0) ++ else if (stat (p_name[i], &st[i]) != 0) + stat_errno[i] = errno; + else + { +@@ -574,30 +577,30 @@ intuit_diff_type (void) + { + bool is_empty; + +- i = best_name (name, stat_errno); ++ i = best_name (p_name, stat_errno); + + if (i == NONE && patch_get) + { + enum nametype nope = NONE; + + for (i = OLD; i <= INDEX; i++) +- if (name[i]) ++ if (p_name[i]) + { + char const *cs; + char *getbuf; + char *diffbuf; + bool readonly = (outfile +- && strcmp (outfile, name[i]) != 0); ++ && strcmp (outfile, p_name[i]) != 0); + +- if (nope == NONE || strcmp (name[nope], name[i]) != 0) ++ if (nope == NONE || strcmp (p_name[nope], p_name[i]) != 0) + { + cs = (version_controller +- (name[i], readonly, (struct stat *) 0, ++ (p_name[i], readonly, (struct stat *) 0, + &getbuf, &diffbuf)); + version_controlled[i] = !! cs; + if (cs) + { +- if (version_get (name[i], cs, false, readonly, ++ if (version_get (p_name[i], cs, false, readonly, + getbuf, &st[i])) + stat_errno[i] = 0; + else +@@ -627,7 +630,7 @@ intuit_diff_type (void) + (i == NONE ? "delete" + : st[i].st_size == 0 ? "empty out" + : "create"), +- quotearg (name[i == NONE || st[i].st_size == 0 ? i0 : i]), ++ quotearg (p_name[i == NONE || st[i].st_size == 0 ? i0 : i]), + (i == NONE ? "does not exist" + : st[i].st_size == 0 ? "is already empty" + : "already exists")); +@@ -640,19 +643,19 @@ intuit_diff_type (void) + int distance_from_minimum[3]; + + for (i = OLD; i <= INDEX; i++) +- if (name[i]) ++ if (p_name[i]) + { +- newdirs[i] = (prefix_components (name[i], false) +- - prefix_components (name[i], true)); ++ newdirs[i] = (prefix_components (p_name[i], false) ++ - prefix_components (p_name[i], true)); + if (newdirs[i] < newdirs_min) + newdirs_min = newdirs[i]; + } + + for (i = OLD; i <= INDEX; i++) +- if (name[i]) ++ if (p_name[i]) + distance_from_minimum[i] = newdirs[i] - newdirs_min; + +- i = best_name (name, distance_from_minimum); ++ i = best_name (p_name, distance_from_minimum); + } + } + } +@@ -661,17 +664,12 @@ intuit_diff_type (void) + inerrno = -1; + else + { +- inname = name[i]; +- name[i] = 0; ++ inname = savestr(p_name[i]); + inerrno = stat_errno[i]; + invc = version_controlled[i]; + instat = st[i]; + } + +- for (i = OLD; i <= INDEX; i++) +- if (name[i]) +- free (name[i]); +- + return retval; + } + +@@ -1803,6 +1801,12 @@ pch_says_nonexistent (bool which) + /* Return timestamp of patch header for file WHICH (false = old, true = new), + or -1 if there was no timestamp or an error in the timestamp. */ + ++const char * ++pch_name (enum nametype type) ++{ ++ return type == NONE ? NULL : p_name[type]; ++} ++ + time_t + pch_timestamp (bool which) + { +Index: b/pch.h +=================================================================== +--- a/pch.h ++++ b/pch.h +@@ -22,6 +22,8 @@ + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + ++enum nametype { OLD, NEW, INDEX, NONE }; ++ + LINENUM pch_end (void); + LINENUM pch_first (void); + LINENUM pch_hunk_beg (void); +@@ -39,6 +41,7 @@ char pch_char (LINENUM); + int another_hunk (enum diff, bool); + int pch_says_nonexistent (bool); + size_t pch_line_len (LINENUM); ++const char *pch_name(enum nametype); + time_t pch_timestamp (bool); + void do_ed_script (FILE *); + void open_patch_file (char const *); diff --git a/diff3-style-merges-refactoring-2.diff b/diff3-style-merges-refactoring-2.diff new file mode 100644 index 0000000..c5a6d06 --- /dev/null +++ b/diff3-style-merges-refactoring-2.diff @@ -0,0 +1,61 @@ +--- + patch.c | 42 ++++++++++++++++++++---------------------- + 1 file changed, 20 insertions(+), 22 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -286,32 +286,30 @@ main (int argc, char **argv) + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { + goto skip_hunk; +- } +- else if (!where +- || (where == 1 && pch_says_nonexistent (reverse) == 2 +- && instat.st_size)) { +- +- if (where) +- say ("Patch attempted to create file %s, which already exists.\n", +- quotearg (inname)); ++ } else if (where == 1 && pch_says_nonexistent (reverse) == 2 ++ && instat.st_size) { ++ say ("Patch attempted to create file %s, " ++ "which already exists.\n", quotearg (inname)); + + goto skip_hunk; +- } +- else if (! apply_hunk (&outstate, where)) { ++ } else if (!where) { + goto skip_hunk; + } else { +- if (verbosity == VERBOSE +- || (verbosity != SILENT && (fuzz || last_offset))) { +- say ("Hunk #%d succeeded at %s", hunk, +- format_linenum (numbuf, newwhere)); +- if (fuzz) +- say (" with fuzz %s", format_linenum (numbuf, fuzz)); +- if (last_offset) +- say (" (offset %s line%s)", +- format_linenum (numbuf, last_offset), +- "s" + (last_offset == 1)); +- say (".\n"); +- } ++ if (!apply_hunk (&outstate, where)) ++ goto skip_hunk; ++ } ++ ++ if (verbosity == VERBOSE ++ || (verbosity != SILENT && (fuzz || last_offset))) { ++ say ("Hunk #%d succeeded at %s", hunk, ++ format_linenum (numbuf, newwhere)); ++ if (fuzz) ++ say (" with fuzz %s", format_linenum (numbuf, fuzz)); ++ if (last_offset) ++ say (" (offset %s line%s)", ++ format_linenum (numbuf, last_offset), ++ "s" + (last_offset == 1)); ++ say (".\n"); + } + continue; + diff --git a/diff3-style-merges-refactoring.diff b/diff3-style-merges-refactoring.diff new file mode 100644 index 0000000..a5f5699 --- /dev/null +++ b/diff3-style-merges-refactoring.diff @@ -0,0 +1,67 @@ +--- + patch.c | 36 +++++++++++++++--------------------- + 1 file changed, 15 insertions(+), 21 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -285,13 +285,7 @@ main (int argc, char **argv) + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { +- if (!failed) +- reject_header(outname); +- abort_hunk(); +- failed++; +- if (verbosity == VERBOSE) +- say ("Hunk #%d ignored at %s.\n", hunk, +- format_linenum (numbuf, newwhere)); ++ goto skip_hunk; + } + else if (!where + || (where == 1 && pch_says_nonexistent (reverse) == 2 +@@ -301,22 +295,10 @@ main (int argc, char **argv) + say ("Patch attempted to create file %s, which already exists.\n", + quotearg (inname)); + +- if (!failed) +- reject_header(outname); +- abort_hunk(); +- failed++; +- if (verbosity != SILENT) +- say ("Hunk #%d FAILED at %s.\n", hunk, +- format_linenum (numbuf, newwhere)); ++ goto skip_hunk; + } + else if (! apply_hunk (&outstate, where)) { +- if (!failed) +- reject_header(outname); +- abort_hunk (); +- failed++; +- if (verbosity != SILENT) +- say ("Hunk #%d FAILED at %s.\n", hunk, +- format_linenum (numbuf, newwhere)); ++ goto skip_hunk; + } else { + if (verbosity == VERBOSE + || (verbosity != SILENT && (fuzz || last_offset))) { +@@ -331,6 +313,18 @@ main (int argc, char **argv) + say (".\n"); + } + } ++ continue; ++ ++skip_hunk: ++ if (!failed) ++ reject_header(outname); ++ abort_hunk (); ++ failed++; ++ if (verbosity == VERBOSE || ++ (!skip_rest_of_patch && verbosity != SILENT)) ++ say ("Hunk #%d %s at %s.\n", hunk, ++ skip_rest_of_patch ? "ignored" : "FAILED", ++ format_linenum (numbuf, newwhere)); + } + + if (!skip_rest_of_patch) diff --git a/diff3-style-merges-rejects.diff b/diff3-style-merges-rejects.diff new file mode 100644 index 0000000..b90b97f --- /dev/null +++ b/diff3-style-merges-rejects.diff @@ -0,0 +1,116 @@ +From: Andreas Gruenbacher +Subject: diff3-style merges: merge rejects + +With the --merge-all option, hunks which do not apply and which would +end up in a *.rej file will also get added to output files. They will +be bracketed like this: + + <<<<<<< + old lines from patch + ||||||| + new lines from patch + >>>>>>> + +With this change, all pieces of a patch can be applied, and all the +resulting merge conflicts can be fixed in the patched files instead +of looking at the reject files. + +Signed-off-by: Andreas Gruenbacher + +--- + patch.c | 40 +++++++++++++++++++++++++++++++--------- + 1 file changed, 31 insertions(+), 9 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -79,7 +79,7 @@ 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 }; ++enum mergetype { SHOW_ALL = 1, SHOW_FUZZ = 2, MERGE_REJECTS = 4 }; + static enum mergetype mergetype; + + static bool make_backups; +@@ -299,7 +299,18 @@ main (int argc, char **argv) + + goto skip_hunk; + } else if (!where) { +- goto skip_hunk; ++ if (mergetype & MERGE_REJECTS) { ++ LINENUM guess = pch_first () + last_offset; ++ ++ if (merge_hunk(&outstate, guess, -1)) { ++ merged++; ++ mismatch = 1; ++ } else { ++ /* FIXME: guess harder! */ ++ goto skip_hunk; ++ } ++ } else ++ goto skip_hunk; + } else { + if ((mergetype & SHOW_ALL) || + (fuzz && (mergetype & SHOW_FUZZ))) { +@@ -316,10 +327,16 @@ main (int argc, char **argv) + + if (verbosity == VERBOSE + || (verbosity != SILENT && (fuzz || last_offset))) { +- say ("Hunk #%d succeeded at %s", hunk, +- format_linenum (numbuf, newwhere)); +- if (fuzz) +- say (" with fuzz %s", format_linenum (numbuf, fuzz)); ++ if (fuzz > mymaxfuzz) { ++ say ("Hunk #%d merged at %s with errors", hunk, ++ format_linenum (numbuf, newwhere)); ++ somefailed = true; ++ } else { ++ say ("Hunk #%d succeeded at %s", hunk, ++ format_linenum (numbuf, newwhere)); ++ if (fuzz) ++ say (" with fuzz %s", format_linenum (numbuf, fuzz)); ++ } + if (last_offset) + say (" (offset %s line%s)", + format_linenum (numbuf, last_offset), +@@ -521,7 +538,7 @@ reinitialize_almost_everything (void) + skip_rest_of_patch = false; + } + +-static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z"; ++static char const shortopts[] = "bB:cd:D:eEfF:g:i:lMnNo:p:r:RstTuvV:x:Y:z:Z"; + static struct option const longopts[] = + { + {"backup", no_argument, NULL, 'b'}, +@@ -591,7 +608,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.", ++" --merge={rejects,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).", +@@ -730,6 +747,9 @@ get_some_switches (void) + case 'l': + canonicalize = true; + break; ++ case 'M': ++ mergetype |= MERGE_REJECTS; ++ break; + case 'n': + diff_type = NORMAL_DIFF; + break; +@@ -835,7 +855,9 @@ get_some_switches (void) + char *tok = strtok(optarg, ","); + + do { +- if (!strcmp(tok, "fuzz")) ++ if (!strcmp(tok, "rejects")) ++ mergetype |= MERGE_REJECTS; ++ else if (!strcmp(tok, "fuzz")) + mergetype |= SHOW_FUZZ; + else if (!strcmp(tok, "all")) + mergetype |= SHOW_ALL; diff --git a/diff3-style-merges-tests.diff b/diff3-style-merges-tests.diff new file mode 100644 index 0000000..9fea168 --- /dev/null +++ b/diff3-style-merges-tests.diff @@ -0,0 +1,492 @@ +--- + merge-tests/diff3-merge.test | 35 +++++++++ + merge-tests/locate-rejects.test | 77 ++++++++++++++++++++ + merge-tests/modes.test | 148 ++++++++++++++++++++++++++++++++++++++++ + merge-tests/overlap-patch.test | 94 +++++++++++++++++++++++++ + merge-tests/prefix-suffix.test | 105 ++++++++++++++++++++++++++++ + 5 files changed, 459 insertions(+) + +Index: b/merge-tests/diff3-merge.test +=================================================================== +--- /dev/null ++++ b/merge-tests/diff3-merge.test +@@ -0,0 +1,35 @@ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++ $ seq 1 4 > a ++ $ sed -e 's/2/2b/' a > b ++ $ sed -e 's/4/4d/' a > c ++ ++Diff3 will merge changes if they are one line apart: ++ ++ $ diff3 -m b a c ++ > 1 ++ > 2b ++ > 3 ++ > 4d ++ ++But it will not merge adjacent changes: ++ ++ $ sed -e 's/3/3c/' a > d ++ ++ $ diff3 -m b a d ++ > 1 ++ > <<<<<<< b ++ > 2b ++ > 3 ++ > ||||||| a ++ > 2 ++ > 3 ++ > ======= ++ > 2 ++ > 3c ++ > >>>>>>> d ++ > 4 ++ ++Patch will merge the changes in either case. +Index: b/merge-tests/modes.test +=================================================================== +--- /dev/null ++++ b/merge-tests/modes.test +@@ -0,0 +1,148 @@ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ cat > clean.diff ++< --- a ++< +++ b ++< @@ -2 +2 @@ ++< -2 ++< +2clean ++ ++$ seq 1 3 > c ; patch c < clean.diff ++> patching file c ++ ++$ cat c ++> 1 ++> 2clean ++> 3 ++ ++$ seq 1 3 > c ; patch --merge=all c < clean.diff ++> patching file c ++ ++$ cat c ++> 1 ++> <<<<<<< a ++> 2 ++> ======= ++> 2clean ++> >>>>>>> b ++> 3 ++ ++$ seq 1 3 > c ; patch --merge=fuzz c < clean.diff ++> patching file c ++ ++$ cat c ++> 1 ++> 2clean ++> 3 ++ ++$ seq 1 3 > c ; patch --merge=rejects c < clean.diff ++> patching file c ++ ++$ cat c ++> 1 ++> 2clean ++> 3 ++ ++$ cat > fuzz.diff ++< --- a ++< +++ b ++< @@ -1,3 +1,3 @@ ++< x ++< -2 ++< +2fuzz ++< x ++ ++$ seq 1 3 > c ; patch c < fuzz.diff ++> patching file c ++> Hunk #1 succeeded at 1 with fuzz 1. ++ ++$ cat c ++> 1 ++> 2fuzz ++> 3 ++ ++$ seq 1 3 > c ; patch --merge=all c < fuzz.diff ++> patching file c ++> Hunk #1 succeeded at 1 with fuzz 1. ++ ++$ cat c ++> <<<<<<< a ++> x ++> 2 ++> x ++> ||||||| b ++> x ++> 2fuzz ++> x ++> ======= ++> 1 ++> 2fuzz ++> 3 ++> >>>>>>> ++ ++$ seq 1 3 > c ; patch --merge=fuzz c < fuzz.diff ++> patching file c ++> Hunk #1 succeeded at 1 with fuzz 1. ++ ++$ cat c ++> <<<<<<< a ++> x ++> 2 ++> x ++> ||||||| b ++> x ++> 2fuzz ++> x ++> ======= ++> 1 ++> 2fuzz ++> 3 ++> >>>>>>> ++ ++$ seq 1 3 > c ; patch --merge=rejects c < fuzz.diff ++> patching file c ++> Hunk #1 succeeded at 1 with fuzz 1. ++ ++$ cat c ++> 1 ++> 2fuzz ++> 3 ++ ++$ cat > reject.diff ++< --- a ++< +++ b ++< @@ -2 +2 @@ ++< -2reject ++< +2reject ++ ++$ seq 1 3 > c ; patch c < reject.diff ++> patching file c ++> Hunk #1 FAILED at 2. ++> 1 out of 1 hunk FAILED -- saving rejects to file c.rej ++ ++$ seq 1 3 > c ; patch --merge=all c < reject.diff ++> patching file c ++> Hunk #1 FAILED at 2. ++> 1 out of 1 hunk FAILED -- saving rejects to file c.rej ++ ++$ seq 1 3 > c ; patch --merge=fuzz c < reject.diff ++> patching file c ++> Hunk #1 FAILED at 2. ++> 1 out of 1 hunk FAILED -- saving rejects to file c.rej ++ ++$ seq 1 3 > c ; patch --merge=rejects c < reject.diff ++> patching file c ++> Hunk #1 merged at 2 with errors. ++ ++$ cat c ++> 1 ++> <<<<<<< a ++> 2reject ++> ||||||| b ++> 2reject ++> >>>>>>> ++> 2 ++> 3 ++ +Index: b/merge-tests/prefix-suffix.test +=================================================================== +--- /dev/null ++++ b/merge-tests/prefix-suffix.test +@@ -0,0 +1,105 @@ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ seq 1 7 > a ++$ seq 1 7 | sed -e '4d' > b ++$ diff -u -L a -L b a b > ab.diff ++$ diff -u -L b -L a b a > ba.diff ++ ++$ cp a c ++$ patch --merge=all c < ab.diff ++> patching file c ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> ======= ++> >>>>>>> b ++> 5 ++> 6 ++> 7 ++ ++$ cp b c ++$ patch --merge=all c < ba.diff ++> patching file c ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< b ++> ======= ++> 4 ++> >>>>>>> a ++> 5 ++> 6 ++> 7 ++ ++$ diff -u /dev/null a > a.diff ++$ echo -n '' > c ++$ patch --merge=all c < a.diff ++> patching file c ++ ++$ cat c ++> <<<<<<< ++> ======= ++> 1 ++> 2 ++> 3 ++> 4 ++> 5 ++> 6 ++> 7 ++> >>>>>>> a ++ ++$ diff -u a /dev/null > a-.diff ++$ cp a c ++$ patch --merge=all c < a-.diff ++> patching file c ++ ++$ cat c ++# ++> <<<<<<< a ++> 1 ++> 2 ++> 3 ++> 4 ++> 5 ++> 6 ++> 7 ++> ======= ++> >>>>>>> ++ ++$ seq -f '%gc' 1 7 > c ++$ patch --merge=rejects c < ab.diff ++> patching file c ++> Hunk #1 merged at 1 with errors. ++ ++$ cat c ++> <<<<<<< a ++> 1 ++> 2 ++> 3 ++> 4 ++> 5 ++> 6 ++> 7 ++> ||||||| b ++> 1 ++> 2 ++> 3 ++> 5 ++> 6 ++> 7 ++> >>>>>>> ++> 1c ++> 2c ++> 3c ++> 4c ++> 5c ++> 6c ++> 7c +Index: b/merge-tests/overlap-patch.test +=================================================================== +--- /dev/null ++++ b/merge-tests/overlap-patch.test +@@ -0,0 +1,94 @@ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ cat > patch ++< --- a ++< +++ b ++< @@ -2,3 +2,3 @@ ++< x ++< -3 ++< +3b ++< x ++ ++$ seq 1 6 > file ++$ patch file < patch ++> patching file file ++> Hunk #1 succeeded at 2 with fuzz 1. ++ ++$ seq 1 6 > file ++$ patch --merge=all file < patch ++> patching file file ++> Hunk #1 succeeded at 2 with fuzz 1. ++ ++$ cat file ++> 1 ++> <<<<<<< a ++> x ++> 3 ++> x ++> ||||||| b ++> x ++> 3b ++> x ++> ======= ++> 2 ++> 3b ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++ ++$ cat > patch ++< --- a ++< +++ b ++< @@ -2,3 +2,3 @@ ++< x ++< -3 ++< +3b ++< x ++< @@ -3,3 +3,3 @@ ++< x ++< -4 ++< +4b ++< x ++ ++$ seq 1 6 > file ++$ patch file < patch ++> patching file file ++> Hunk #1 succeeded at 2 with fuzz 1. ++> Hunk #2 succeeded at 3 with fuzz 1. ++ ++$ seq 1 6 > file ++$ patch --merge=all file < patch ++> patching file file ++> Hunk #1 succeeded at 2 with fuzz 1. ++> Hunk #2 succeeded at 3 with fuzz 1. ++ ++$ cat file ++> 1 ++> <<<<<<< a ++> x ++> 3 ++> x ++> ||||||| b ++> x ++> 3b ++> x ++> ======= ++> 2 ++> 3b ++> >>>>>>> ++> <<<<<<< a ++> x ++> 4 ++> x ++> ||||||| b ++> x ++> 4b ++> x ++> ======= ++> 4b ++> 5 ++> >>>>>>> ++> 6 +Index: b/merge-tests/locate-rejects.test +=================================================================== +--- /dev/null ++++ b/merge-tests/locate-rejects.test +@@ -0,0 +1,77 @@ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ seq 1 7 > a ++$ seq 1 7 | sed -e 's/4/4b/' > b ++$ seq 1 7 | sed -e 's/4/4c/' > c ++$ seq 1 7 | sed -e '4d' > d ++ ++$ diff -u a b > ab.diff ++ ++$ patch c < ab.diff ++> patching file c ++> Hunk #1 FAILED at 1. ++> 1 out of 1 hunk FAILED -- saving rejects to file c.rej ++ ++$ patch --merge=rejects c < ab.diff ++> patching file c ++> Hunk #1 merged at 1 with errors. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> 5 ++> 6 ++> 7 ++> ||||||| b ++> 4b ++> 5 ++> 6 ++> 7 ++> >>>>>>> ++> 4c ++> 5 ++> 6 ++> 7 ++ ++$ (seq 1 3; echo 7) > c ++$ patch --merge=rejects c < ab.diff ++> patching file c ++> Hunk #1 merged at 1 with errors. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> 5 ++> 6 ++> ||||||| b ++> 4b ++> 5 ++> 6 ++> >>>>>>> ++> 7 ++ ++$ patch --merge=rejects d < ab.diff ++> patching file d ++> Hunk #1 merged at 1 with errors. ++ ++$ cat d ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> ||||||| b ++> 4b ++> >>>>>>> ++> 5 ++> 6 ++> 7 ++ diff --git a/fail.test b/fail.test deleted file mode 100644 index deb918d..0000000 --- a/fail.test +++ /dev/null @@ -1,19 +0,0 @@ - $ mkdir d - $ cd d - - $ mkdir sub - $ echo 1 > f - $ echo 2 > f.new - $ diff -Nu f f.new > f.diff - $ mv f.new f - $ echo 3 > f.new - $ diff -Nu f f.new >> f.diff - $ rm f.new - $ echo 1 > f - $ chmod a=r f - $ strace -o ../log patch -p0 --backup < f.diff - > patching file f - > patching file f - - $ cd .. - $ rm -rf d diff --git a/patch.changes b/patch.changes index 362ff4e..0e5fa1f 100644 --- a/patch.changes +++ b/patch.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Tue Feb 3 06:10:49 CET 2009 - agruen@suse.de + +- Implement diff3-style merges (including several fixes and + improvements). + ------------------------------------------------------------------- Wed Apr 23 10:53:19 CEST 2008 - agruen@suse.de diff --git a/patch.spec b/patch.spec index ed95a88..9d55898 100644 --- a/patch.spec +++ b/patch.spec @@ -1,10 +1,17 @@ # # spec file for package patch (Version 2.5.9) # -# Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. -# This file and all modifications and additions to the pristine -# package are under the same license as the package itself. +# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. # +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + # Please submit bugfixes or comments via http://bugs.opensuse.org/ # @@ -16,7 +23,7 @@ License: GPL v2 or later Group: Productivity/Text/Utilities AutoReqProv: on Version: 2.5.9 -Release: 252 +Release: 287 Summary: GNU patch Source: ftp://prep.ai.mit.edu/pub/gnu/patch/%{name}-%{version}.tar.bz2 Url: ftp://alpha.gnu.org/gnu/diffutils/ @@ -28,6 +35,17 @@ Patch4: if_else_endif_comments.diff Patch5: patch-2.5.9-cat_if_device.diff Patch6: patch-man-unified-reject.diff Patch7: fix-partial-context.diff +Patch8: diff3-style-merges-tests.diff +Patch9: diff3-style-merges-refactoring.diff +Patch10: diff3-style-merges-refactoring-2.diff +Patch11: diff3-style-merges-base.diff +Patch12: diff3-style-merges-rejects.diff +Patch13: diff3-style-merges-pch_name.diff +Patch14: diff3-style-merges-include-filenames.diff +Patch15: diff3-style-merges-add-file-labels.diff +Patch16: diff3-style-merges-overlap.diff +Patch17: diff3-style-merges-locate-merge.diff +Patch18: diff3-style-merges-other-strategy.diff BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -51,6 +69,17 @@ Authors: %patch5 -p1 %patch6 -p1 %patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 %build aclocal --acdir=m4 @@ -76,6 +105,9 @@ make install \ %doc %{_mandir}/man1/patch.1.gz %changelog +* Tue Feb 03 2009 agruen@suse.de +- Implement diff3-style merges (including several fixes and + improvements). * Wed Apr 23 2008 agruen@suse.de - remember-backup-files.diff: Fix bug when a file is touched by the same patch more than twice. Move the test cases from the