diff --git a/diff3-style-merges-add-file-labels.diff b/diff3-style-merges-add-file-labels.diff index 07add31..b676f72 100644 --- a/diff3-style-merges-add-file-labels.diff +++ b/diff3-style-merges-add-file-labels.diff @@ -1,29 +1,36 @@ -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(-) + common.h | 2 ++ + merge.c | 8 ++++---- + patch.c | 14 +++++++++++++- + 3 files changed, 19 insertions(+), 5 deletions(-) +Index: b/common.h +=================================================================== +--- a/common.h ++++ b/common.h +@@ -310,6 +310,8 @@ XTERN LINENUM last_offset; + /* how many input lines have been irretractably output */ + XTERN LINENUM last_frozen_line; + ++XTERN const char *file_label[2]; ++ + bool similar (char const *, size_t, char const *, size_t); + bool copy_till (struct outstate *, LINENUM); + 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; +@@ -70,6 +70,8 @@ static void usage (FILE *, int) __attrib + static void (*abort_hunk) (bool, bool) = abort_hunk_context; + static bool merge; +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) +@@ -506,7 +508,7 @@ reinitialize_almost_everything (void) skip_rest_of_patch = false; } @@ -32,25 +39,25 @@ Index: b/patch.c static struct option const longopts[] = { {"backup", no_argument, NULL, 'b'}, -@@ -553,6 +554,7 @@ static struct option const longopts[] = +@@ -521,6 +523,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'}, + {"merge", no_argument, NULL, 'M'}, {"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[] = +@@ -577,6 +580,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.", + " -M --merge Produce a diff3-style merge for rejects.", +" -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; +@@ -718,6 +722,14 @@ get_some_switches (void) + case 'M': + merge = true; break; + case 'L': + if (!file_label[0]) @@ -60,11 +67,15 @@ Index: b/patch.c + else + fatal ("too many file label options"); + break; - case 'M': - mergetype |= MERGE_REJECTS; + case 'n': + diff_type = NORMAL_DIFF; break; -@@ -1411,7 +1422,7 @@ static bool merge_hunk (struct outstate - } +Index: b/merge.c +=================================================================== +--- a/merge.c ++++ b/merge.c +@@ -50,7 +50,7 @@ merge_hunk (struct outstate *outstate) + return false; /* "From" lines in the patch */ - name = pch_name(OLD); @@ -72,21 +83,23 @@ Index: b/patch.c if (!name) name = ""; fprintf(fp, outstate->after_newline + "\n<<<<<<<%*s\n", -@@ -1426,7 +1437,7 @@ static bool merge_hunk (struct outstate - - if (fuzz) { +@@ -84,7 +84,7 @@ merge_hunk (struct outstate *outstate) + if (! same_result) + { /* "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 +@@ -112,8 +112,8 @@ merge_hunk (struct outstate *outstate) 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", + if (same_result) + { +- name = pch_name(NEW); +- if (!name) ++ name = file_label[NEW] ? file_label[NEW] : pch_name(NEW); ++ if (! name) + name = ""; + } + else diff --git a/diff3-style-merges-base.diff b/diff3-style-merges-base.diff deleted file mode 100644 index 19c6acf..0000000 --- a/diff3-style-merges-base.diff +++ /dev/null @@ -1,293 +0,0 @@ -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 deleted file mode 100644 index 25dd7c2..0000000 --- a/diff3-style-merges-include-filenames.diff +++ /dev/null @@ -1,73 +0,0 @@ -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 index c81b9a5..8bb65fe 100644 --- a/diff3-style-merges-locate-merge.diff +++ b/diff3-style-merges-locate-merge.diff @@ -1,70 +1,100 @@ --- - patch.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 72 insertions(+), 6 deletions(-) + Makefile.in | 3 - + bestmatch.h | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + common.h | 11 ++++ + merge.c | 95 ++++++++++++++++++++++++++++++++++++++++- + patch.c | 19 ++++++-- + 5 files changed, 256 insertions(+), 10 deletions(-) +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -85,7 +85,8 @@ HDRS = argmatch.h backupfile.h common.h + error.h getopt.h gettext.h \ + inp.h maketime.h partime.h pch.h \ + quote.h quotearg.h quotesys.h \ +- unlocked-io.h util.h version.h xalloc.h hash.h ++ unlocked-io.h util.h version.h xalloc.h hash.h \ ++ bestmatch.h + MISC = AUTHORS COPYING ChangeLog INSTALL Makefile.in NEWS README \ + aclocal.m4 \ + config.hin configure configure.ac \ +Index: b/common.h +=================================================================== +--- a/common.h ++++ b/common.h +@@ -296,6 +296,14 @@ void *realloc (); + #define TTY_DEVICE "/dev/tty" + #endif + ++#ifndef MIN ++# define MIN(a, b) ((a) <= (b) ? (a) : (b)) ++#endif ++ ++#ifndef MAX ++# define MAX(a, b) ((a) >= (b) ? (a) : (b)) ++#endif ++ + /* Output stream state. */ + struct outstate + { +@@ -316,4 +324,5 @@ bool similar (char const *, size_t, char + bool copy_till (struct outstate *, LINENUM); + + /* Defined in merge.c */ +-bool merge_hunk (struct outstate *); ++LINENUM locate_merge (LINENUM, LINENUM *); ++bool merge_hunk (struct outstate *, LINENUM, LINENUM); 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) +@@ -286,13 +286,22 @@ 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)) { + if (merge) { +- if (merge_hunk(&outstate)) { ++ LINENUM matched; ++ ++ where = locate_merge (maxfuzz, &matched); ++ if (! where) ++ { ++ where = pch_first () + last_offset; ++ matched = 0; ++ } ++ ++ if (merge_hunk (&outstate, where, matched)) ++ { merged++; mismatch = 1; - } else { -- /* FIXME: guess harder! */ -+ } else - goto skip_hunk; +- /* FIXME: try harder! */ +- goto skip_hunk; - } ++ } ++ 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)); - } +Index: b/merge.c +=================================================================== +--- a/merge.c ++++ b/merge.c +@@ -7,8 +7,97 @@ + static bool context_matches_file (LINENUM, LINENUM); + static bool common_context (LINENUM, LINENUM, LINENUM); -+static LINENUM min_mismatches(LINENUM where, LINENUM lowest) ++#define OFFSET LINENUM ++#define EQUAL(x, y) (context_matches_file (x, y)) ++ ++#include "bestmatch.h" ++ ++LINENUM ++locate_merge (LINENUM fuzz, LINENUM *matched) +{ -+ 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 first_guess = pch_first () + last_offset; + LINENUM pat_lines = pch_ptrn_lines(); + LINENUM suffix_context = pch_suffix_context (); + LINENUM max_where = input_lines - (pat_lines - suffix_context) + 1; @@ -73,38 +103,235 @@ Index: b/patch.c + LINENUM max_neg_offset = first_guess - min_where; + LINENUM max_offset = (max_pos_offset < max_neg_offset + ? max_neg_offset : max_pos_offset); ++ LINENUM prefix_fuzz = MIN (fuzz, pch_prefix_context()); ++ LINENUM suffix_fuzz = MIN (fuzz, pch_suffix_context()); ++ LINENUM where = 0, max_matched = 0; ++ LINENUM min, max; ++ LINENUM offset; ++ ++ /* The minimum number of matched lines and maximum number of changes ++ are mostly guesses. */ ++ min = pat_lines - (prefix_fuzz + suffix_fuzz); ++ max = 2 * (prefix_fuzz + suffix_fuzz); + + /* Do not try lines <= 0. */ + if (first_guess <= max_neg_offset) -+ max_neg_offset = first_guess - 1; ++ max_neg_offset = first_guess - 1; + -+ for (offset = 0; offset <= max_offset; offset++) { -+ if (offset <= max_pos_offset) { -+ register LINENUM mismatched; ++ for (offset = 0; offset <= max_offset; offset++) ++ { ++ if (offset <= max_pos_offset) ++ { ++ LINENUM guess = first_guess + offset; ++ LINENUM last; ++ LINENUM changes; + -+ 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; ++ changes = bestmatch(1, pat_lines + 1, guess, input_lines + 1, ++ min, max, &last); ++ if (changes <= max && max_matched < last - guess) ++ { ++ max_matched = last - guess; ++ where = guess; ++ if (changes == 0) ++ break; ++ min = last - guess; ++ max = changes - 1; ++ } ++ } ++ if (0 < offset && offset <= max_neg_offset) ++ { ++ LINENUM guess = first_guess - offset; ++ LINENUM last; ++ LINENUM changes; + -+ mismatched = min_mismatches(first_guess + offset, lowest); -+ if (mismatched < lowest) { -+ lowest = mismatched; -+ where = first_guess + offset; -+ if (lowest == 1) -+ break; -+ } -+ } -+ } ++ changes = bestmatch(1, pat_lines + 1, guess, input_lines + 1, ++ min, max, &last); ++ if (changes <= max && max_matched < last - guess) ++ { ++ max_matched = last - guess; ++ where = guess; ++ if (changes == 0) ++ break; ++ min = last - guess; ++ max = changes - 1; ++ } ++ } ++ } ++ if (debug & 1) ++ { ++ char numbuf0[LINENUM_LENGTH_BOUND + 1]; ++ char numbuf1[LINENUM_LENGTH_BOUND + 1]; ++ char numbuf2[LINENUM_LENGTH_BOUND + 1]; ++ char numbuf3[LINENUM_LENGTH_BOUND + 1]; ++ say ("locating merge: min=%s max=%s where=%s matched=%s\n", ++ format_linenum (numbuf0, min), ++ format_linenum (numbuf1, max), ++ format_linenum (numbuf2, where), ++ format_linenum (numbuf3, max_matched)); ++ } ++ ++ if (where) ++ *matched = max_matched; + return where; +} + - /* A FUZZ value of -1 indicates that the hunk could not be applied. */ + bool +-merge_hunk (struct outstate *outstate) ++merge_hunk (struct outstate *outstate, LINENUM where, LINENUM matched) + { + LINENUM old = 1; + LINENUM lastold = pch_ptrn_lines (); +@@ -22,8 +111,8 @@ merge_hunk (struct outstate *outstate) + while (pch_char(new) == '=' || pch_char(new) == '\n') + new++; - static bool merge_hunk (struct outstate *outstate, LINENUM where, LINENUM fuzz) +- merge = pch_first () + last_offset; +- lastmerge = merge + lastold - 1; ++ merge = where; ++ lastmerge = where + matched - 1; + if (! common_context(lastmerge, lastold, lastnew)) + lastmerge = merge - 1; + +Index: b/bestmatch.h +=================================================================== +--- /dev/null ++++ b/bestmatch.h +@@ -0,0 +1,138 @@ ++/* Before including this file, you need to define: ++ EQUAL(x, y) A two-argument macro that tests elements ++ at index x and y for equality. ++ OFFSET A signed integer type sufficient to hold the ++ difference between two indices. Usually ++ something like ssize_t. */ ++ ++/* ++ * Shortest Edit Sequence ++ * ++ * Based on the Greedy LCS/SES Algorithm (Figure 2) in: ++ * ++ * Eugene W. Myers, "An O(ND) Difference Algorithm and Its Variations", ++ * Algorithmica, Vol. 1, No. 1, pp. 251-266, March 1986. ++ * Available: http://dx.doi.org/10.1007/BF01840446 ++ * http://xmailserver.org/diff2.pdf ++ * ++ * Returns the number of changes (insertions and deletions) required to get ++ * from a[] to b[]. Returns MAX + 1 if a[] cannot be turned into b[] with ++ * MAX or fewer changes. ++ * ++ * MIN specifies the minimum number of elements in which a[] and b[] must ++ * match. This allows to prevent trivial matches in which a sequence is ++ * completely discarded, or completely made up. ++ * ++ * If PY is not NULL, matches a[] against a prefix of b[], and returns the ++ * number of elements in b[] that were matched in *PY. Otherwise, matches ++ * all elements of b[]. ++ * ++ * Note that the divide-and-conquer strategy discussed in section 4b of the ++ * paper is more efficient, but does not allow an open-ended prefix string ++ * search. ++ */ ++ ++OFFSET ++bestmatch(OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim, ++ OFFSET min, OFFSET max, OFFSET *py) ++{ ++ const OFFSET dmin = xoff - ylim; /* Minimum valid diagonal. */ ++ const OFFSET dmax = xlim - yoff; /* Maximum valid diagonal. */ ++ const OFFSET fmid = xoff - yoff; /* Center diagonal. */ ++ OFFSET fmin = fmid; ++ OFFSET fmax = fmid; ++ OFFSET V[2 * max + 3], *fd = V + max + 2 - fmid; ++ OFFSET fmid_plus_2_min, ymax = -1; ++ OFFSET c; ++ ++ /* ++ The number of elements that were matched in x and in y can be ++ computed as either (x - x_skipped) or (y - y_skipped), with: ++ ++ delta = (x - xoff) - (y - yoff) ++ x_skipped = (c + delta) / 2 ++ y_skipped = (c - delta) / 2 ++ ++ For searching for a minimum number of matching elements, we end up ++ with this check: ++ ++ (x - x_skipped) >= min ++ ... ++ x + y - c >= (xoff - yoff) + 2 * min ++ x + y - c >= fmid + 2 * min ++ */ ++ ++ if (min) ++ { ++ fmid_plus_2_min = fmid + 2 * min; ++ min += yoff; ++ if (min > ylim) ++ return max + 1; ++ } ++ else ++ fmid_plus_2_min = 0; /* disable this check */ ++ if (!py) ++ min = ylim; ++ ++ /* Handle the exact-match case. */ ++ while (xoff < xlim && yoff < ylim && EQUAL (xoff, yoff)) ++ { ++ xoff++; ++ yoff++; ++ } ++ if (xoff == xlim && yoff >= min ++ && xoff + yoff >= fmid_plus_2_min) ++ { ++ ymax = yoff; ++ c = 0; ++ } ++ else ++ { ++ fd[fmid] = xoff; ++ for (c = 1; c <= max; c++) ++ { ++ OFFSET d; ++ ++ if (fmin > dmin) ++ fd[--fmin - 1] = -1; ++ else ++ ++fmin; ++ if (fmax < dmax) ++ fd[++fmax + 1] = -1; ++ else ++ --fmax; ++ for (d = fmax; d >= fmin; d -= 2) ++ { ++ OFFSET x, y; ++ ++ if (fd[d - 1] < fd[d + 1]) ++ x = fd[d + 1]; ++ else ++ x = fd[d - 1] + 1; ++ for (y = x - d; ++ x < xlim && y < ylim && EQUAL (x, y); ++ x++, y++) ++ continue; ++ fd[d] = x; ++ if (x == xlim && y >= min ++ && x + y - c >= fmid_plus_2_min) ++ { ++ if (ymax < y) ++ ymax = y; ++ if (y == ylim) ++ goto done; ++ } ++ } ++ if (ymax != -1) ++ goto done; ++ } ++ } ++ ++ done: ++ if (py) ++ *py = ymax; ++ return c; ++} ++ ++#undef OFFSET ++#undef EQUAL diff --git a/diff3-style-merges-other-strategy.diff b/diff3-style-merges-other-strategy.diff deleted file mode 100644 index ae4cf35..0000000 --- a/diff3-style-merges-other-strategy.diff +++ /dev/null @@ -1,36 +0,0 @@ ---- - 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 deleted file mode 100644 index bf8ff43..0000000 --- a/diff3-style-merges-overlap.diff +++ /dev/null @@ -1,249 +0,0 @@ -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-refactoring-2.diff b/diff3-style-merges-refactoring-2.diff index c5a6d06..8b50c38 100644 --- a/diff3-style-merges-refactoring-2.diff +++ b/diff3-style-merges-refactoring-2.diff @@ -6,7 +6,7 @@ Index: b/patch.c =================================================================== --- a/patch.c +++ b/patch.c -@@ -286,32 +286,30 @@ main (int argc, char **argv) +@@ -288,32 +288,30 @@ main (int argc, char **argv) newwhere = pch_newfirst() + last_offset; if (skip_rest_of_patch) { goto skip_hunk; @@ -44,7 +44,7 @@ Index: b/patch.c + if (!apply_hunk (&outstate, where)) + goto skip_hunk; + } -+ ++ + if (verbosity == VERBOSE + || (verbosity != SILENT && (fuzz || last_offset))) { + say ("Hunk #%d succeeded at %s", hunk, diff --git a/diff3-style-merges-refactoring.diff b/diff3-style-merges-refactoring.diff index a5f5699..1df83aa 100644 --- a/diff3-style-merges-refactoring.diff +++ b/diff3-style-merges-refactoring.diff @@ -1,18 +1,16 @@ --- - patch.c | 36 +++++++++++++++--------------------- - 1 file changed, 15 insertions(+), 21 deletions(-) + patch.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) Index: b/patch.c =================================================================== --- a/patch.c +++ b/patch.c -@@ -285,13 +285,7 @@ main (int argc, char **argv) +@@ -287,11 +287,7 @@ main (int argc, char **argv) newwhere = pch_newfirst() + last_offset; if (skip_rest_of_patch) { -- if (!failed) -- reject_header(outname); -- abort_hunk(); +- abort_hunk(!failed, reverse); - failed++; - if (verbosity == VERBOSE) - say ("Hunk #%d ignored at %s.\n", hunk, @@ -21,13 +19,11 @@ Index: b/patch.c } else if (!where || (where == 1 && pch_says_nonexistent (reverse) == 2 -@@ -301,22 +295,10 @@ main (int argc, char **argv) +@@ -301,18 +297,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(); +- abort_hunk(!failed, reverse); - failed++; - if (verbosity != SILENT) - say ("Hunk #%d FAILED at %s.\n", hunk, @@ -35,9 +31,7 @@ Index: b/patch.c + goto skip_hunk; } else if (! apply_hunk (&outstate, where)) { -- if (!failed) -- reject_header(outname); -- abort_hunk (); +- abort_hunk (!failed, reverse); - failed++; - if (verbosity != SILENT) - say ("Hunk #%d FAILED at %s.\n", hunk, @@ -46,16 +40,14 @@ Index: b/patch.c } else { if (verbosity == VERBOSE || (verbosity != SILENT && (fuzz || last_offset))) { -@@ -331,6 +313,18 @@ main (int argc, char **argv) +@@ -327,6 +315,16 @@ main (int argc, char **argv) say (".\n"); } } + continue; + +skip_hunk: -+ if (!failed) -+ reject_header(outname); -+ abort_hunk (); ++ abort_hunk(!failed, reverse); + failed++; + if (verbosity == VERBOSE || + (!skip_rest_of_patch && verbosity != SILENT)) diff --git a/diff3-style-merges-rejects.diff b/diff3-style-merges-rejects.diff deleted file mode 100644 index b90b97f..0000000 --- a/diff3-style-merges-rejects.diff +++ /dev/null @@ -1,116 +0,0 @@ -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-simple-merge.diff b/diff3-style-merges-simple-merge.diff new file mode 100644 index 0000000..7c85546 --- /dev/null +++ b/diff3-style-merges-simple-merge.diff @@ -0,0 +1,442 @@ +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 + +--- + Makefile.in | 6 +- + common.h | 20 +++++++ + merge.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + patch.c | 62 +++++++++++++---------- + 4 files changed, 222 insertions(+), 27 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -48,22 +48,12 @@ struct utimbuf + }; + #endif + +-/* Output stream state. */ +-struct outstate +-{ +- FILE *ofp; +- bool after_newline; +- bool zero_output; +-}; +- + /* procedures */ + + static FILE *create_output_file (char const *, int); + static LINENUM locate_hunk (LINENUM); + static bool apply_hunk (struct outstate *, 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); + static bool spew_output (struct outstate *); + static char const *make_temp (char); + static void abort_hunk_context (bool, bool); +@@ -79,6 +69,7 @@ static void usage (FILE *, int) __attrib + + static void (*abort_hunk) (bool, bool) = abort_hunk_context; + ++static bool merge; + static bool make_backups; + static bool backup_if_mismatch; + static char const *version_control; +@@ -88,9 +79,6 @@ static bool remove_empty_files; + /* true if -R was specified on command line. */ + static bool reverse_flag_specified; + +-/* how many input lines have been irretractably output */ +-static LINENUM last_frozen_line; +- + static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ + static char const if_defined[] = "\n#ifdef %s\n"; + static char const not_defined[] = "\n#ifndef %s\n"; +@@ -108,7 +96,6 @@ static char *rejname; + static char const * volatile TMPREJNAME; + static int volatile TMPREJNAME_needs_removal; + +-static LINENUM last_offset; + static LINENUM maxfuzz = 2; + + static char serrbuf[BUFSIZ]; +@@ -119,7 +106,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]; + +@@ -186,6 +173,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,7 +283,16 @@ main (int argc, char **argv) + + goto skip_hunk; + } else if (!where) { +- goto skip_hunk; ++ if (merge) { ++ if (merge_hunk(&outstate)) { ++ merged++; ++ mismatch = 1; ++ } else { ++ /* FIXME: try harder! */ ++ goto skip_hunk; ++ } ++ } else ++ goto skip_hunk; + } else { + if (!apply_hunk (&outstate, where)) + goto skip_hunk; +@@ -303,10 +300,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 conflicts", 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), +@@ -325,6 +328,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 +380,8 @@ skip_hunk: + else + { + if (! outstate.zero_output +- && pch_says_nonexistent (! reverse)) ++ && pch_says_nonexistent (! reverse) ++ && !merged) + { + mismatch = true; + if (verbosity != SILENT) +@@ -465,7 +472,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; + } +@@ -499,7 +506,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'}, +@@ -514,6 +521,7 @@ static struct option const longopts[] = + {"get", no_argument, NULL, 'g'}, + {"input", required_argument, NULL, 'i'}, + {"ignore-whitespace", no_argument, NULL, 'l'}, ++ {"merge", no_argument, NULL, 'M'}, + {"normal", no_argument, NULL, 'n'}, + {"forward", no_argument, NULL, 'N'}, + {"output", required_argument, NULL, 'o'}, +@@ -568,6 +576,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.", ++" -M --merge Produce a diff3-style merge for rejects.", + " -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).", +@@ -706,6 +715,9 @@ get_some_switches (void) + case 'l': + canonicalize = true; + break; ++ case 'M': ++ merge = true; ++ break; + case 'n': + diff_type = NORMAL_DIFF; + break; +@@ -1289,7 +1301,7 @@ init_reject (void) + + /* Copy input file to output, up to wherever hunk is to be applied. */ + +-static bool ++bool + copy_till (register struct outstate *outstate, register LINENUM lastline) + { + register LINENUM R_last_frozen_line = last_frozen_line; +@@ -1375,7 +1387,7 @@ patch_match (LINENUM base, LINENUM offse + + /* Do two lines match with canonicalized white space? */ + +-static bool ++bool + similar (register char const *a, register size_t alen, + register char const *b, register size_t blen) + { +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -70,7 +70,8 @@ SRCS = $(LIBSRCS) \ + maketime.c partime.c \ + patch.c pch.c \ + quote.c quotearg.c quotesys.c \ +- util.c version.c xmalloc.c ++ util.c version.c xmalloc.c \ ++ merge.c + OBJS = $(LIBOBJS) \ + addext.$(OBJEXT) argmatch.$(OBJEXT) backupfile.$(OBJEXT) \ + basename.$(OBJEXT) dirname.$(OBJEXT) \ +@@ -78,7 +79,8 @@ OBJS = $(LIBOBJS) \ + maketime.$(OBJEXT) partime.$(OBJEXT) \ + patch.$(OBJEXT) pch.$(OBJEXT) \ + quote.$(OBJEXT) quotearg.$(OBJEXT) quotesys.$(OBJEXT) \ +- util.$(OBJEXT) version.$(OBJEXT) xmalloc.$(OBJEXT) hash.$(OBJEXT) ++ util.$(OBJEXT) version.$(OBJEXT) xmalloc.$(OBJEXT) hash.$(OBJEXT) \ ++ merge.$(OBJEXT) + HDRS = argmatch.h backupfile.h common.h dirname.h \ + error.h getopt.h gettext.h \ + inp.h maketime.h partime.h pch.h \ +Index: b/common.h +=================================================================== +--- a/common.h ++++ b/common.h +@@ -295,3 +295,23 @@ void *realloc (); + #ifndef TTY_DEVICE + #define TTY_DEVICE "/dev/tty" + #endif ++ ++/* Output stream state. */ ++struct outstate ++{ ++ FILE *ofp; ++ bool after_newline; ++ bool zero_output; ++}; ++ ++/* offset at which the previous hunk matched */ ++XTERN LINENUM last_offset; ++ ++/* how many input lines have been irretractably output */ ++XTERN LINENUM last_frozen_line; ++ ++bool similar (char const *, size_t, char const *, size_t); ++bool copy_till (struct outstate *, LINENUM); ++ ++/* Defined in merge.c */ ++bool merge_hunk (struct outstate *); +Index: b/merge.c +=================================================================== +--- /dev/null ++++ b/merge.c +@@ -0,0 +1,161 @@ ++#define XTERN extern ++#include ++#include ++#include ++#include ++ ++static bool context_matches_file (LINENUM, LINENUM); ++static bool common_context (LINENUM, LINENUM, LINENUM); ++ ++bool ++merge_hunk (struct outstate *outstate) ++{ ++ LINENUM old = 1; ++ LINENUM lastold = pch_ptrn_lines (); ++ LINENUM new = lastold + 1; ++ LINENUM lastnew = pch_end (); ++ LINENUM merge, lastmerge; ++ FILE *fp = outstate->ofp; ++ bool same_result, succeeded = true; ++ const char *name; ++ ++ while (pch_char(new) == '=' || pch_char(new) == '\n') ++ new++; ++ ++ merge = pch_first () + last_offset; ++ lastmerge = merge + lastold - 1; ++ if (! common_context(lastmerge, lastold, lastnew)) ++ lastmerge = merge - 1; ++ ++ /* Hide common prefix context */ ++ while (old <= lastold && new <= lastnew && merge <= lastmerge && ++ common_context(merge, old, new)) ++ { ++ old++; ++ new++; ++ merge++; ++ } ++ ++ /* Hide common suffix context */ ++ while (old <= lastold && new <= lastnew && merge <= lastmerge && ++ common_context(lastmerge, lastold, lastnew)) ++ { ++ lastold--; ++ lastnew--; ++ lastmerge--; ++ } ++ ++ assert (outstate->after_newline); ++ if (! copy_till(outstate, merge - 1)) ++ return false; ++ ++ /* "From" lines in the patch */ ++ 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; ++ while (old <= lastold) ++ { ++ outstate->after_newline = pch_write_line(old, fp); ++ old++; ++ } ++ ++ same_result = (lastmerge - merge == lastnew - new); ++ if (same_result) ++ { ++ LINENUM n = new, m = merge; ++ ++ while (n <= lastnew) ++ { ++ if (! context_matches_file (n, m)) ++ { ++ same_result = false; ++ break; ++ } ++ n++; ++ m++; ++ } ++ } ++ ++ if (! same_result) ++ { ++ /* "To" lines in the patch */ ++ 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; ++ while (new <= lastnew) ++ { ++ outstate->after_newline = pch_write_line(new, fp); ++ new++; ++ } ++ } ++ ++ /* Merge result */ ++ fprintf(fp, outstate->after_newline + "\n=======\n"); ++ if (ferror (fp)) ++ write_fatal (); ++ outstate->after_newline = true; ++ ++ if (! copy_till(outstate, lastmerge)) ++ succeeded = false; ++ ++ /* If the merge result and the new file are the same, label the merge ++ result with the new file's name. */ ++ if (same_result) ++ { ++ name = pch_name(NEW); ++ if (!name) ++ name = ""; ++ } ++ else ++ 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; ++ outstate->zero_output = false; ++ return succeeded; ++} ++ ++static bool ++context_matches_file (LINENUM old, LINENUM where) ++{ ++ size_t size; ++ const char *line; ++ ++ line = ifetch (where, false, &size); ++ return size && ++ (canonicalize ? ++ similar(pfetch(old), pch_line_len(old), line, size) : ++ (size == pch_line_len(old) && ++ memcmp(line, pfetch(old), size) == 0)); ++} ++ ++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)); ++} diff --git a/diff3-style-merges-tests.diff b/diff3-style-merges-tests.diff index 9fea168..659354a 100644 --- a/diff3-style-merges-tests.diff +++ b/diff3-style-merges-tests.diff @@ -1,16 +1,20 @@ --- - 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(+) + tests/NOTES | 23 ++ + tests/diff3-merge.shrun | 51 ++++ + tests/locate-rejects.shrun | 77 +++++++ + tests/modes.shrun | 401 ++++++++++++++++++++++++++++++++++++++ + tests/prefix-suffix.shrun | 42 +++ + tests/rejects.shrun | 133 ++++++++++++ + tests/wiggle-base.shrun | 157 ++++++++++++++ + tests/wiggle-bugs.shrun | 179 ++++++++++++++++ + tests/wiggle-changeafteradd.shrun | 69 ++++++ + 9 files changed, 1132 insertions(+) -Index: b/merge-tests/diff3-merge.test +Index: b/tests/diff3-merge.shrun =================================================================== --- /dev/null -+++ b/merge-tests/diff3-merge.test -@@ -0,0 +1,35 @@ ++++ b/tests/diff3-merge.shrun +@@ -0,0 +1,51 @@ +$ tmpdir=$(mktemp -d) +$ trap "cd /; rm -rf $tmpdir" EXIT +$ cd $tmpdir @@ -19,7 +23,8 @@ Index: b/merge-tests/diff3-merge.test + $ 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 will merge changes if there is a one-line gap between them. ++(Patch would merge this as well.) + + $ diff3 -m b a c + > 1 @@ -27,10 +32,10 @@ Index: b/merge-tests/diff3-merge.test + > 3 + > 4d + -+But it will not merge adjacent changes: ++But it will not merge adjacent but nonoverlapping changes. ++(Patch would merge this.) + + $ sed -e 's/3/3c/' a > d -+ + $ diff3 -m b a d + > 1 + > <<<<<<< b @@ -45,12 +50,30 @@ Index: b/merge-tests/diff3-merge.test + > >>>>>>> d + > 4 + -+Patch will merge the changes in either case. -Index: b/merge-tests/modes.test ++This is what you get when the first and third files are identical (i.e., ++a patch has already been applied). This is confusing because it is not ++immediately obvious which section refers to the first file. (It is the ++section between === and >>>, while it was the section between <<< and ||| ++above.) ++ ++ $ cp b e ++ $ diff3 -m e a b ++ > 1 ++ > <<<<<<< a ++ > 2 ++ > ======= ++ > 2b ++ > >>>>>>> b ++ > 3 ++ > 4 +Index: b/tests/modes.shrun =================================================================== --- /dev/null -+++ b/merge-tests/modes.test -@@ -0,0 +1,148 @@ ++++ b/tests/modes.shrun +@@ -0,0 +1,401 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ +$ tmpdir=$(mktemp -d) +$ trap "cd /; rm -rf $tmpdir" EXIT +$ cd $tmpdir @@ -70,27 +93,7 @@ Index: b/merge-tests/modes.test +> 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 ++$ seq 1 3 > c ; patch -M c < clean.diff +> patching file c + +$ cat c @@ -116,9 +119,9 @@ Index: b/merge-tests/modes.test +> 2fuzz +> 3 + -+$ seq 1 3 > c ; patch --merge=all c < fuzz.diff ++$ patch -f -M c < fuzz.diff +> patching file c -+> Hunk #1 succeeded at 1 with fuzz 1. ++> Hunk #1 merged at 1 with conflicts. + +$ cat c +> <<<<<<< a @@ -130,16 +133,25 @@ Index: b/merge-tests/modes.test +> 2fuzz +> x +> ======= ++> >>>>>>> +> 1 +> 2fuzz +> 3 -+> >>>>>>> + -+$ seq 1 3 > c ; patch --merge=fuzz c < fuzz.diff ++$ seq 1 3 > c ; patch -M c < fuzz.diff +> patching file c +> Hunk #1 succeeded at 1 with fuzz 1. + +$ cat c ++> 1 ++> 2fuzz ++> 3 ++ ++$ seq 1 3 > c ; patch --fuzz=0 -M c < fuzz.diff ++> patching file c ++> Hunk #1 merged at 1 with conflicts. ++ ++$ cat c +> <<<<<<< a +> x +> 2 @@ -149,18 +161,9 @@ Index: b/merge-tests/modes.test +> 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 ++> 2 +> 3 + +$ cat > reject.diff @@ -168,42 +171,315 @@ Index: b/merge-tests/modes.test +< +++ b +< @@ -2 +2 @@ +< -2reject -+< +2reject ++< +2tcejer + +$ 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 ++$ seq 1 3 > c ; patch -M 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. ++> Hunk #1 merged at 2 with conflicts. + +$ cat c +> 1 +> <<<<<<< a +> 2reject +> ||||||| b -+> 2reject ++> 2tcejer ++> ======= +> >>>>>>> +> 2 +> 3 + -Index: b/merge-tests/prefix-suffix.test ++$ cat > insert.diff ++< --- a ++< +++ b ++< @@ -2,4 +2,5 @@ ++< 2 ++< 3x ++< +ins ++< 4 ++< 5 ++ ++$ seq 1 6 > c ; patch -M c < insert.diff ++> patching file c ++> Hunk #1 succeeded at 2 with fuzz 2. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> ins ++> 4 ++> 5 ++> 6 ++ ++$ cat > insert.diff ++< --- a ++< +++ b ++< @@ -2,4 +2,5 @@ ++< 2 ++< 3 ++< +ins ++< 4x ++< 5 ++ ++$ seq 1 6 > c ; patch --fuzz=1 -M c < insert.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4x ++> ||||||| b ++> ins ++> 4x ++> ======= ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++ ++$ cat > insert.diff ++< --- a ++< +++ b ++< @@ -2,4 +2,5 @@ ++< 2 ++< 3x ++< +ins ++< 4x ++< 5 ++ ++$ seq 1 6 > c ; patch --fuzz=1 -M c < insert.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3x ++> 4x ++> ||||||| b ++> 3x ++> ins ++> 4x ++> ======= ++> 3 ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++ ++$ seq 1 6 > c ; patch -M c < insert.diff ++> patching file c ++> Hunk #1 succeeded at 2 with fuzz 2. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> ins ++> 4 ++> 5 ++> 6 ++ ++$ cat > delete.diff ++< --- a ++< +++ b ++< @@ -2,5 +2,4 @@ ++< 2 ++< 3x ++< -4 ++< 5 ++< 6 ++ ++$ seq 1 7 > c ; patch --fuzz=1 -M c < delete.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3x ++> 4 ++> ||||||| b ++> 3x ++> ======= ++> 3 ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++> 7 ++ ++$ cat > delete.diff ++< --- a ++< +++ b ++< @@ -2,5 +2,4 @@ ++< 2 ++< 3 ++< -4x ++< 5 ++< 6 ++ ++$ seq 1 7 > c ; patch --fuzz=1 -M c < delete.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4x ++> ||||||| b ++> ======= ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++> 7 ++ ++$ cat > delete.diff ++< --- a ++< +++ b ++< @@ -2,5 +2,4 @@ ++< 2 ++< 3 ++< -4 ++< 5x ++< 6 ++ ++$ seq 1 7 > c ; patch --fuzz=1 -M c < delete.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> 5x ++> ||||||| b ++> 5x ++> ======= ++> 4 ++> 5 ++> >>>>>>> ++> 6 ++> 7 ++ ++$ cat > change.diff ++< --- a ++< +++ b ++< @@ -2,5 +2,5 @@ ++< 2 ++< 3x ++< -4 ++< +4x ++< 5 ++< 6 ++ ++$ seq 1 7 > c ; patch --fuzz=1 -M c < change.diff ++> patching file c ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3x ++> 4 ++> ||||||| b ++> 3x ++> 4x ++> ======= ++> 3 ++> 4 ++> >>>>>>> ++> 5 ++> 6 ++> 7 ++ ++$ cat > delete-gone.diff ++< --- a ++< +++ b ++< @@ -1,7 +1,6 @@ ++< 1 ++< 2 ++< 3 ++< -4x ++< 5 ++< 6 ++< 7 ++ ++$ seq 1 7 | sed -e '3,5d' > c; patch --fuzz=2 -M c < delete-gone.diff ++> patching file c ++> Hunk #1 merged at 1 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3 ++> 4x ++> 5 ++> ||||||| b ++> 3 ++> 5 ++> ======= ++> >>>>>>> ++> 6 ++> 7 ++ ++ ++$ cat > insert-gone.diff ++< --- a ++< +++ b ++< @@ -1,6 +1,7 @@ ++< 1 ++< 2 ++< 3 ++< +4x ++< 5 ++< 6 ++< 7 ++ ++$ seq 1 7 | sed -e '3,5d' > c; patch --fuzz=2 -M c < insert-gone.diff ++> patching file c ++> Hunk #1 merged at 1 with conflicts. ++ ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3 ++> 5 ++> ||||||| b ++> 3 ++> 4x ++> 5 ++> ======= ++> >>>>>>> ++> 6 ++> 7 ++ +Index: b/tests/prefix-suffix.shrun =================================================================== --- /dev/null -+++ b/merge-tests/prefix-suffix.test -@@ -0,0 +1,105 @@ ++++ b/tests/prefix-suffix.shrun +@@ -0,0 +1,42 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ +$ tmpdir=$(mktemp -d) +$ trap "cd /; rm -rf $tmpdir" EXIT +$ cd $tmpdir @@ -213,77 +489,10 @@ Index: b/merge-tests/prefix-suffix.test +$ 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 ++$ patch -M c < ab.diff +> patching file c -+> Hunk #1 merged at 1 with errors. ++> Hunk #1 merged at 1 with conflicts. + +$ cat c +> <<<<<<< a @@ -301,6 +510,7 @@ Index: b/merge-tests/prefix-suffix.test +> 5 +> 6 +> 7 ++> ======= +> >>>>>>> +> 1c +> 2c @@ -309,110 +519,14 @@ Index: b/merge-tests/prefix-suffix.test +> 5c +> 6c +> 7c -Index: b/merge-tests/overlap-patch.test +Index: b/tests/locate-rejects.shrun =================================================================== --- /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 ++++ b/tests/locate-rejects.shrun @@ -0,0 +1,77 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ +$ tmpdir=$(mktemp -d) +$ trap "cd /; rm -rf $tmpdir" EXIT +$ cd $tmpdir @@ -420,7 +534,6 @@ Index: b/merge-tests/locate-rejects.test +$ 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 + @@ -429,9 +542,9 @@ Index: b/merge-tests/locate-rejects.test +> Hunk #1 FAILED at 1. +> 1 out of 1 hunk FAILED -- saving rejects to file c.rej + -+$ patch --merge=rejects c < ab.diff ++$ patch -M c < ab.diff +> patching file c -+> Hunk #1 merged at 1 with errors. ++> Hunk #1 merged at 1 with conflicts. + +$ cat c +> 1 @@ -439,24 +552,19 @@ Index: b/merge-tests/locate-rejects.test +> 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 ++$ patch -M c < ab.diff +> patching file c -+> Hunk #1 merged at 1 with errors. ++> Hunk #1 merged at 1 with conflicts. + +$ cat c +> 1 @@ -470,14 +578,16 @@ Index: b/merge-tests/locate-rejects.test +> 4b +> 5 +> 6 ++> ======= +> >>>>>>> +> 7 + -+$ patch --merge=rejects d < ab.diff -+> patching file d -+> Hunk #1 merged at 1 with errors. ++$ seq 1 7 | sed -e '4d' > c ++$ patch -M c < ab.diff ++> patching file c ++> Hunk #1 merged at 1 with conflicts. + -+$ cat d ++$ cat c +> 1 +> 2 +> 3 @@ -485,8 +595,595 @@ Index: b/merge-tests/locate-rejects.test +> 4 +> ||||||| b +> 4b ++> ======= +> >>>>>>> +> 5 +> 6 +> 7 + +Index: b/tests/rejects.shrun +=================================================================== +--- /dev/null ++++ b/tests/rejects.shrun +@@ -0,0 +1,133 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ set +o posix ++ ++$ x() { +++ local max=$1 b +++ shift +++ +++ while [ "$1" != -- ]; do +++ b[${#b[@]}]=$1 +++ shift +++ done +++ shift +++ local c=("$@") +++ +++ seq 1 $max > a +++ seq 1 $max | sed -f <(printf "%s\n" "${b[@]}") > b +++ seq 1 $max | sed -f <(printf "%s\n" "${c[@]}") > c +++ +++ diff -u a b | patch -s -f -M c +++ cat c +++ #diff3 -m c a b +++ } ++ ++$ x 9 -- ++> 1 ++> 2 ++> 3 ++> 4 ++> 5 ++> 6 ++> 7 ++> 8 ++> 9 ++ ++$ x 9 5d -- ++> 1 ++> 2 ++> 3 ++> 4 ++> 6 ++> 7 ++> 8 ++> 9 ++ ++$ x 9 5d -- 5d ++> 1 ++> 2 ++> 3 ++> 4 ++> <<<<<<< a ++> 5 ++> ======= ++> >>>>>>> b ++> 6 ++> 7 ++> 8 ++> 9 ++ ++$ x 9 5d -- 6d ++> 1 ++> 2 ++> 3 ++> 4 ++> <<<<<<< a ++> 5 ++> 6 ++> ||||||| b ++> 6 ++> ======= ++> 5 ++> >>>>>>> ++> 7 ++> 8 ++> 9 ++ ++$ x 9 5d -- 4d ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4 ++> 5 ++> ||||||| b ++> 4 ++> ======= ++> 5 ++> >>>>>>> ++> 6 ++> 7 ++> 8 ++> 9 ++ ++$ x 9 5aa -- 5aa ++> 1 ++> 2 ++> 3 ++> 4 ++> 5 ++> <<<<<<< a ++> ======= ++> a ++> >>>>>>> b ++> 6 ++> 7 ++> 8 ++> 9 ++ ++$ x 9 s/4/4b/ -- s/3/3c/ s/5/5c/ ++> 1 ++> 2 ++> <<<<<<< a ++> 3 ++> 4 ++> 5 ++> ||||||| b ++> 3 ++> 4b ++> 5 ++> ======= ++> 3c ++> 4 ++> 5c ++> >>>>>>> ++> 6 ++> 7 ++> 8 ++> 9 +Index: b/tests/wiggle-bugs.shrun +=================================================================== +--- /dev/null ++++ b/tests/wiggle-bugs.shrun +@@ -0,0 +1,179 @@ ++$ WIGGLE=wiggle ++ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++======================================== ++ ++$ echo a > a ++$ echo b > b ++$ diff -u a b > ab.diff ++$ echo c > c ++$ diff3 -m c a b ++> <<<<<<< c ++> c ++> ||||||| a ++> a ++> ======= ++> b ++> >>>>>>> b ++ ++In this case, patch has no way to know that c is part of the conflict. ++It could make a blind guess, though. ++ ++$ patch -s -M c < ab.diff ++$ cat c ++> <<<<<<< a ++> a ++> ||||||| b ++> b ++> ======= ++> >>>>>>> ++> c ++ ++======================================== ++ ++$ seq 1 7 | sed -e 's/4/4a/' > a ++$ seq 1 7 | sed -e 's/4/4b/' > b ++$ diff -u a b > ab.diff ++$ seq 1 7 | sed -e 's/4/4c/' > c ++$ diff3 -m c a b ++> 1 ++> 2 ++> 3 ++> <<<<<<< c ++> 4c ++> ||||||| a ++> 4a ++> ======= ++> 4b ++> >>>>>>> b ++> 5 ++> 6 ++> 7 ++ ++$ patch -s -M c < ab.diff ++$ cat c ++> 1 ++> 2 ++> 3 ++> <<<<<<< a ++> 4a ++> ||||||| b ++> 4b ++> ======= ++> 4c ++> >>>>>>> ++> 5 ++> 6 ++> 7 ++ ++======================================== ++ ++$ seq 1 7 | sed -e 's/4/4a/' > a ++$ seq 1 7 | sed -e 's/4/4b/' > b ++$ diff -u a b > ab.diff ++$ seq 1 7 | sed -e 's/[345]/&c/' > c ++$ diff3 -m c a b ++> 1 ++> 2 ++> <<<<<<< c ++> 3c ++> 4c ++> 5c ++> ||||||| a ++> 3 ++> 4a ++> 5 ++> ======= ++> 3 ++> 4b ++> 5 ++> >>>>>>> b ++> 6 ++> 7 ++ ++$ patch -s -M c < ab.diff ++$ cat c ++> 1 ++> 2 ++> <<<<<<< a ++> 3 ++> 4a ++> 5 ++> ||||||| b ++> 3 ++> 4b ++> 5 ++> ======= ++> 3c ++> 4c ++> 5c ++> >>>>>>> ++> 6 ++> 7 ++ ++======================================== ++ ++$ seq 1 7 | sed -e 's/4/4a/' > a ++$ seq 1 7 | sed -e 's/4/4b/' > b ++$ diff -u a b > ab.diff ++$ seq 1 7 | sed -e 's/./&c/' > c ++$ diff3 -m c a b ++> <<<<<<< c ++> 1c ++> 2c ++> 3c ++> 4c ++> 5c ++> 6c ++> 7c ++> ||||||| a ++> 1 ++> 2 ++> 3 ++> 4a ++> 5 ++> 6 ++> 7 ++> ======= ++> 1 ++> 2 ++> 3 ++> 4b ++> 5 ++> 6 ++> 7 ++> >>>>>>> b ++ ++$ patch -s -M c < ab.diff 2>/dev/null ++$ cat c ++> <<<<<<< a ++> 1 ++> 2 ++> 3 ++> 4a ++> 5 ++> 6 ++> 7 ++> ||||||| b ++> 1 ++> 2 ++> 3 ++> 4b ++> 5 ++> 6 ++> 7 ++> ======= ++> >>>>>>> ++> 1c ++> 2c ++> 3c ++> 4c ++> 5c ++> 6c ++> 7c +Index: b/tests/NOTES +=================================================================== +--- /dev/null ++++ b/tests/NOTES +@@ -0,0 +1,23 @@ ++Same change in b and c: ++ <<<<<<< a ++ old lines from patch ++ ======= ++ new version == new lines from patch ++ >>>>>>> b ++ ++Conflicting change (section order differs from diff3, otherwise equivalent): ++ <<<<<<< a ++ old lines from patch ++ ||||||| b ++ new lines from patch ++ ======= ++ new version ++ >>>>>>> ++ ++Clean change (diff3 doesn't have that, so maybe we don't need it either ++except for debugging): ++ <<<<<<< a ++ old lines ++ ======= ++ new version == new lines of patch ++ >>>>>>> +Index: b/tests/wiggle-changeafteradd.shrun +=================================================================== +--- /dev/null ++++ b/tests/wiggle-changeafteradd.shrun +@@ -0,0 +1,69 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ cat > orig ++< here ++< is ++< the ++< original ++< file ++ ++$ cat > new ++< here ++< is ++< the ++< new version of the ++< original ++< file ++ ++$ cat > new2 ++< here ++< is ++< the ++< new version of the ++< inaugural ++< file ++ ++$ diff3 -m new new2 orig ++> here ++> is ++> the ++> <<<<<<< new ++> new version of the ++> original ++> ||||||| new2 ++> new version of the ++> inaugural ++> ======= ++> original ++> >>>>>>> orig ++> file ++ ++In this case, merging without marking the conflict would be desirable: ++it should be possible to detect that this is only a minor change to a ++context line, there are context matches before and after, and the ++number of changes required is small. ++ ++$ diff -u new new2 > ab.diff ++$ patch orig -M < ab.diff ++> patching file orig ++> Hunk #1 merged at 2 with conflicts. ++ ++$ cat orig ++> here ++> is ++> the ++> <<<<<<< new ++> new version of the ++> original ++> ||||||| new2 ++> new version of the ++> inaugural ++> ======= ++> original ++> >>>>>>> ++> file +Index: b/tests/wiggle-base.shrun +=================================================================== +--- /dev/null ++++ b/tests/wiggle-base.shrun +@@ -0,0 +1,157 @@ ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++ ++$ cat > orig ++< ++< This is a base file ++< some changes are going to happen to it ++< but it has ++< several lines ++< so that alll ++< the changes ++< don't h... ++< I don't know waht I am saying. ++< This lion will have some changes made. ++< but this one wont ++< stuf stuf stuff ++< thing thing ++< xxxxx ++< that is all ++< except ++< for ++< this ++< last ++< bit ++ ++$ cat > new ++< s is a base file ++< some changes are going to happen to it ++< but it has ++< had ++< several lines ++< so that alll ++< the changes ++< don't h... ++< I don't know what I am saying. ++< This line will have some changes made. ++< but this one wont ++< stuf stuf stuff ++< thing thing ++< xxxxx ++< that is all ++< except ++< for ++< this ++< last ++< bit ++< x ++ ++$ cat > new2 ++< This is a base file ++< some changes are going to happen to it ++< but it has ++< had ++< several lines ++< so that alll ++< the changes ++< don't h... ++< I don't know what I am saying. ++< This line will have some modifications made. ++< but this one wont ++< stuf stuf stuff ++< thing thing ++< xxxxx ++< that is all ++< except ++< for ++< this ++< last ++< bit ++< x ++< ++ ++$ diff -u new new2 > ab.diff ++$ patch orig -f -M < ab.diff ++> patching file orig ++> Hunk #1 merged at 1 with conflicts. ++> Hunk #2 merged at 7 with conflicts. ++> Hunk #3 merged at 19 with conflicts. ++ ++$ cat orig ++> <<<<<<< new ++> s is a base file ++> some changes are going to happen to it ++> but it has ++> had ++> ||||||| new2 ++> This is a base file ++> some changes are going to happen to it ++> but it has ++> had ++> ======= ++> >>>>>>> ++> ++> This is a base file ++> some changes are going to happen to it ++> but it has ++> several lines ++> so that alll ++> the changes ++> don't h... ++> <<<<<<< new ++> I don't know what I am saying. ++> This line will have some changes made. ++> ||||||| new2 ++> I don't know what I am saying. ++> This line will have some modifications made. ++> ======= ++> I don't know waht I am saying. ++> This lion will have some changes made. ++> >>>>>>> ++> but this one wont ++> stuf stuf stuff ++> thing thing ++> xxxxx ++> that is all ++> except ++> for ++> <<<<<<< new ++> last ++> bit ++> x ++> ||||||| new2 ++> last ++> bit ++> x ++> ++> ======= ++> >>>>>>> ++> this ++> last ++> bit ++ ++#$ cat > merge ++#< ++#< This is a base file ++#< some changes are going to happen to it ++#< but it has ++#< several lines ++#< so that alll ++#< the changes ++#< don't h... ++#< I don't know waht I am saying. ++#< This lion will have some modifications made. ++#< but this one wont ++#< stuf stuf stuff ++#< thing thing ++#< xxxxx ++#< that is all ++#< except ++#< for ++#< this ++#< last ++#< bit diff --git a/explain-pch_char-oddity.diff b/explain-pch_char-oddity.diff new file mode 100644 index 0000000..b13f755 --- /dev/null +++ b/explain-pch_char-oddity.diff @@ -0,0 +1,19 @@ +--- + pch.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +Index: b/pch.c +=================================================================== +--- a/pch.c ++++ b/pch.c +@@ -1877,7 +1877,9 @@ pch_line_len (LINENUM line) + return p_len[line]; + } + +-/* Return the control character (+, -, *, !, etc) for a patch line. */ ++/* Return the control character (+, -, *, !, etc) for a patch line. A '\n' ++ indicates an empty line in a hunk that isn't part of the old or new ++ file's contents -- the context format allows that. */ + + char + pch_char (LINENUM line) diff --git a/fix-partial-context.diff b/fix-partial-context.diff index f543820..ad1ff50 100644 --- a/fix-partial-context.diff +++ b/fix-partial-context.diff @@ -13,6 +13,8 @@ are perfectly valid. patch.c | 43 ++++--------------------------------------- 2 files changed, 11 insertions(+), 39 deletions(-) +Index: b/ChangeLog +=================================================================== --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ @@ -26,9 +28,11 @@ are perfectly valid. 2003-05-20 Paul Eggert * NEWS, configure.ac (AC_INIT): Version 2.5.9 released. +Index: b/patch.c +=================================================================== --- a/patch.c +++ b/patch.c -@@ -849,10 +849,10 @@ locate_hunk (LINENUM fuzz) +@@ -880,10 +880,10 @@ locate_hunk (LINENUM fuzz) LINENUM pat_lines = pch_ptrn_lines(); LINENUM prefix_context = pch_prefix_context (); LINENUM suffix_context = pch_suffix_context (); @@ -43,7 +47,7 @@ are perfectly valid. LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1; LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz); LINENUM max_pos_offset = max_where - first_guess; -@@ -867,41 +867,6 @@ locate_hunk (LINENUM fuzz) +@@ -898,41 +898,6 @@ locate_hunk (LINENUM fuzz) if (first_guess <= max_neg_offset) max_neg_offset = first_guess - 1; diff --git a/fix-timestamp-parsing.diff b/fix-timestamp-parsing.diff new file mode 100644 index 0000000..fdf6a87 --- /dev/null +++ b/fix-timestamp-parsing.diff @@ -0,0 +1,58 @@ +* str2time() will happily parse "\n" and similar; the result will be + undistinguishable from a valid timestamp. Avoid this by skipping + whitespace before and checking if anything remains first. + +* Always parse timestamps in patch headers relative to the local timezone + or UTC so that pch_timestamp() will always contain reasonable values + if a timestamp is present: we may need the timestamps later. + +* Change the bizarre logic which checks whether a timestamp is close to + the epoch by checking for exactly that. The result is the same, and we + won't have to parse the timestamp again. + +--- + util.c | 26 ++++++++++++-------------- + 1 file changed, 12 insertions(+), 14 deletions(-) + +Index: b/util.c +=================================================================== +--- a/util.c ++++ b/util.c +@@ -996,25 +996,23 @@ fetchname (char *at, int strip_leading, + if (*u != '\t' && strchr (u + 1, '\t')) + continue; + +- if (set_time | set_utc) +- stamp = str2time (&u, initial_time, +- set_utc ? 0L : TM_LOCAL_ZONE); +- else +- { ++ while (ISSPACE((unsigned char) *u)) ++ u++; ++ if (*u) { ++ stamp = str2time (&u, initial_time, ++ set_utc ? 0L : TM_LOCAL_ZONE); ++ + /* The head says the file is nonexistent if the timestamp + is the epoch; but the listed time is local time, not UTC, + and POSIX.1 allows local time offset anywhere in the range +- -25:00 < offset < +26:00. Match any time in that +- range by assuming local time is -25:00 and then matching +- any ``local'' time T in the range 0 < T < 25+26 hours. */ +- stamp = str2time (&u, initial_time, -25L * 60 * 60); +- if (0 < stamp && stamp < (25 + 26) * 60L * 60) ++ -25:00 < offset < +26:00. Match any time in that range. */ ++ if (!(set_time || set_utc) ++ && 25 * 60L * 60 < stamp && stamp < 26 * 60L * 60) + stamp = 0; +- } +- +- if (*u && ! ISSPACE ((unsigned char) *u)) +- stamp = (time_t) -1; + ++ if (*u && ! ISSPACE ((unsigned char) *u)) ++ stamp = (time_t) -1; ++ } + *t = '\0'; + break; + } diff --git a/format_startcount.diff b/format_startcount.diff new file mode 100644 index 0000000..d0b4a9b --- /dev/null +++ b/format_startcount.diff @@ -0,0 +1,74 @@ +--- + util.c | 30 +++++++++++++++++++++++++++--- + util.h | 2 ++ + 2 files changed, 29 insertions(+), 3 deletions(-) + +Index: b/util.c +=================================================================== +--- a/util.c ++++ b/util.c +@@ -466,10 +466,9 @@ remove_prefix (char *p, size_t prefixlen + continue; + } + +-char * +-format_linenum (char numbuf[LINENUM_LENGTH_BOUND + 1], LINENUM n) ++static char * ++__format_linenum (char *p, LINENUM n) + { +- char *p = numbuf + LINENUM_LENGTH_BOUND; + *p = '\0'; + + if (n < 0) +@@ -490,6 +489,31 @@ format_linenum (char numbuf[LINENUM_LENG + return p; + } + ++char * ++format_linenum (char numbuf[LINENUM_LENGTH_BOUND + 1], LINENUM n) ++{ ++ return __format_linenum(numbuf + LINENUM_LENGTH_BOUND, n); ++} ++ ++char * ++format_startcount (char rangebuf[LINERANGE_LENGTH_BOUND + 1], ++ LINENUM first, LINENUM lines) ++{ ++ char *p = rangebuf + LINERANGE_LENGTH_BOUND; ++ ++ if (lines == 1) ++ rangebuf = __format_linenum (p, first); ++ else ++ { ++ if (lines == 0 && first == 1) ++ first = 0; /* what diff produces ... */ ++ p = __format_linenum(p, lines); ++ rangebuf = __format_linenum(--p, first); ++ *p = ','; ++ } ++ return rangebuf; ++} ++ + #if !HAVE_VPRINTF + #define vfprintf my_vfprintf + static int +Index: b/util.h +=================================================================== +--- a/util.h ++++ b/util.h +@@ -25,6 +25,7 @@ + /* An upper bound on the print length of a signed decimal line number. + Add one for the sign. */ + #define LINENUM_LENGTH_BOUND (sizeof (LINENUM) * CHAR_BIT / 3 + 1) ++#define LINERANGE_LENGTH_BOUND (LINENUM_LENGTH_BOUND * 2 + 1) + + XTERN enum backup_type backup_type; + +@@ -45,6 +46,7 @@ bool version_get (char const *, char con + int create_file (char const *, int, mode_t); + int systemic (char const *); + char *format_linenum (char[LINENUM_LENGTH_BOUND + 1], LINENUM); ++char *format_startcount (char[LINERANGE_LENGTH_BOUND + 1], LINENUM, LINENUM); + void Fseek (FILE *, file_offset, int); + void copy_file (char const *, char const *, int, mode_t); + void exit_with_signal (int) __attribute__ ((noreturn)); diff --git a/global-reject-file.diff b/global-reject-file.diff index 918f00c..b6d33be 100644 --- a/global-reject-file.diff +++ b/global-reject-file.diff @@ -1,44 +1,21 @@ -Index: patch-2.5.9/patch.man +--- + patch.c | 33 ++++++++++++++++++++++++++++----- + patch.man | 9 +++++++++ + 2 files changed, 37 insertions(+), 5 deletions(-) + +Index: b/patch.c =================================================================== ---- patch-2.5.9.orig/patch.man -+++ patch-2.5.9/patch.man -@@ -520,6 +520,15 @@ file. - \fB\*=reject\-unified\fP - Produce unified reject files. The default is to produce context type reject files. - .TP -+.BI \*=global\-reject\-file= rejectfile -+Put all rejects into -+.I rejectfile -+instead of creating separate reject files for all files that have rejects. The -+.I rejectfile -+will contain headers that identify which file each reject refers to. Note that -+the global reject file is created even if \-\-dry\-run is specified (while -+non-global reject files will only be created without \-\-dry\-run). -+.TP - \fB\-R\fP or \fB\*=reverse\fP - Assume that this patch was created with the old and new files swapped. - (Yes, I'm afraid that does happen occasionally, human nature being what it -Index: patch-2.5.9/patch.c -=================================================================== ---- patch-2.5.9.orig/patch.c -+++ patch-2.5.9/patch.c -@@ -67,6 +67,7 @@ static bool similar (char const *, size_ - static bool spew_output (struct outstate *); - static char const *make_temp (char); - static int numeric_string (char const *, bool, char const *); -+static void reject_header (const char *filename); - static void abort_hunk (void); - static void cleanup (void); - static void get_some_switches (void); -@@ -98,6 +99,7 @@ static int Argc; +--- a/patch.c ++++ b/patch.c +@@ -101,6 +101,7 @@ static int Argc; static char * const *Argv; static FILE *rejfp; /* reject file pointer */ -+static char *global_reject; ++char *global_reject; static char const *patchname; static char *rejname; -@@ -172,6 +174,10 @@ main (int argc, char **argv) +@@ -175,6 +176,10 @@ main (int argc, char **argv) /* Make sure we clean up in case of disaster. */ set_signals (false); @@ -49,7 +26,7 @@ Index: patch-2.5.9/patch.c for ( open_patch_file (patchname); there_is_another_patch(); -@@ -208,8 +214,9 @@ main (int argc, char **argv) +@@ -211,8 +216,9 @@ main (int argc, char **argv) init_output (TMPOUTNAME, exclusive, &outstate); } @@ -61,34 +38,7 @@ Index: patch-2.5.9/patch.c /* find out where all the lines are */ if (!skip_rest_of_patch) -@@ -278,6 +285,8 @@ 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) -@@ -292,6 +301,8 @@ 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) -@@ -299,6 +310,8 @@ main (int argc, char **argv) - format_linenum (numbuf, newwhere)); - } - else if (! apply_hunk (&outstate, where)) { -+ if (!failed) -+ reject_header(outname); - abort_hunk (); - failed++; - if (verbosity != SILENT) -@@ -332,7 +345,8 @@ main (int argc, char **argv) +@@ -335,7 +341,8 @@ main (int argc, char **argv) fclose (outstate.ofp); outstate.ofp = 0; } @@ -98,7 +48,7 @@ Index: patch-2.5.9/patch.c continue; } -@@ -412,13 +426,13 @@ main (int argc, char **argv) +@@ -415,13 +422,13 @@ main (int argc, char **argv) } } if (diff_type != ED_DIFF) { @@ -114,7 +64,7 @@ Index: patch-2.5.9/patch.c char *rej = rejname; if (!rejname) { rej = xmalloc (strlen (outname) + 5); -@@ -445,6 +459,20 @@ main (int argc, char **argv) +@@ -448,6 +455,17 @@ main (int argc, char **argv) } set_signals (true); } @@ -124,18 +74,15 @@ Index: patch-2.5.9/patch.c + write_fatal (); + if (somefailed) + { -+ say (" -- saving rejects to file %s\n", quotearg (global_reject)); -+ /*if (! dry_run) -+ {*/ -+ move_file (TMPREJNAME, &TMPREJNAME_needs_removal, -+ global_reject, 0644, false); -+ /*}*/ ++ say (" -- saving rejects to file %s\n", quotearg (global_reject)); ++ move_file (TMPREJNAME, &TMPREJNAME_needs_removal, ++ global_reject, 0644, false); + } + } if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) write_fatal (); cleanup (); -@@ -523,6 +551,7 @@ static struct option const longopts[] = +@@ -526,6 +544,7 @@ static struct option const longopts[] = {"posix", no_argument, NULL, CHAR_MAX + 7}, {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, {"unified-reject-files", no_argument, NULL, CHAR_MAX + 9}, @@ -143,7 +90,7 @@ Index: patch-2.5.9/patch.c {NULL, no_argument, NULL, 0} }; -@@ -582,6 +611,7 @@ static char const *const option_help[] = +@@ -585,6 +604,7 @@ static char const *const option_help[] = " --dry-run Do not actually change any files; just print what would happen.", " --posix Conform to the POSIX standard.", " --unified-reject-files Create unified reject files.", @@ -151,9 +98,9 @@ Index: patch-2.5.9/patch.c "", " -d DIR --directory=DIR Change the working directory to DIR first.", #if HAVE_SETMODE_DOS -@@ -784,6 +814,9 @@ get_some_switches (void) +@@ -787,6 +807,9 @@ get_some_switches (void) case CHAR_MAX + 9: - unified_reject_files = true; + abort_hunk = abort_hunk_unified; break; + case CHAR_MAX + 10: + global_reject = savestr (optarg); @@ -161,41 +108,23 @@ Index: patch-2.5.9/patch.c default: usage (stderr, 2); } -@@ -933,6 +966,37 @@ locate_hunk (LINENUM fuzz) - } - - static char * -+format_timestamp (char timebuf[37], bool which) -+{ -+ time_t ts = pch_timestamp(which); -+ if (ts != -1) -+ { -+ struct tm *tm = localtime(&ts); -+ strftime(timebuf, 37, "\t%Y-%m-%d %H:%M:%S.000000000 %z", tm); -+ } -+ else -+ timebuf[0] = 0; -+ return timebuf; -+} -+ -+/* Write a header in a reject file that combines multiple hunks. */ -+static void -+reject_header (const char *outname) -+{ -+ char timebuf0[37], timebuf1[37]; -+ if (!global_reject) -+ return; -+ if (diff_type == UNI_DIFF) -+ fprintf(rejfp, "--- %s.orig%s\n+++ %s%s\n", -+ outname, format_timestamp(timebuf0, reverse), -+ outname, format_timestamp(timebuf1, !reverse)); -+ else -+ fprintf(rejfp, "*** %s.orig%s\n--- %s%s\n", -+ outname, format_timestamp(timebuf0, reverse), -+ outname, format_timestamp(timebuf1, !reverse)); -+} -+ -+static char * - format_linerange (char rangebuf[LINENUM_LENGTH_BOUND*2 + 2], - LINENUM first, LINENUM lines) - { +Index: b/patch.man +=================================================================== +--- a/patch.man ++++ b/patch.man +@@ -520,6 +520,15 @@ file. + \fB\*=unified\-reject\-files\fP + Produce unified reject files. The default is to produce context type reject files. + .TP ++.BI \*=global\-reject\-file= rejectfile ++Put all rejects into ++.I rejectfile ++instead of creating separate reject files for all files that have rejects. The ++.I rejectfile ++will contain headers that identify which file each reject refers to. Note that ++the global reject file is created even if \-\-dry\-run is specified (while ++non-global reject files will only be created without \-\-dry\-run). ++.TP + \fB\-R\fP or \fB\*=reverse\fP + Assume that this patch was created with the old and new files swapped. + (Yes, I'm afraid that does happen occasionally, human nature being what it diff --git a/if_else_endif_comments.diff b/if_else_endif_comments.diff deleted file mode 100644 index 85f5e55..0000000 --- a/if_else_endif_comments.diff +++ /dev/null @@ -1,75 +0,0 @@ -Index: patch-2.5.9/patch.c -=================================================================== ---- patch-2.5.9.orig/patch.c 2003-06-05 00:22:56.000000000 +0200 -+++ patch-2.5.9/patch.c 2003-06-05 00:23:16.000000000 +0200 -@@ -91,8 +91,8 @@ static LINENUM last_frozen_line; - static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ - static char const if_defined[] = "\n#ifdef %s\n"; - static char const not_defined[] = "\n#ifndef %s\n"; --static char const else_defined[] = "\n#else\n"; --static char const end_defined[] = "\n#endif\n"; -+static char const else_defined[] = "\n#else /* %s */\n"; -+static char const end_defined[] = "\n#endif /* %s */\n"; - - static int Argc; - static char * const *Argv; -@@ -1097,7 +1097,8 @@ apply_hunk (struct outstate *outstate, L - def_state = IN_IFNDEF; - } - else if (def_state == IN_IFDEF) { -- fprintf (fp, outstate->after_newline + else_defined); -+ fprintf (fp, outstate->after_newline + else_defined, -+ R_do_defines); - def_state = IN_ELSE; - } - if (ferror (fp)) -@@ -1116,7 +1117,8 @@ apply_hunk (struct outstate *outstate, L - return false; - if (R_do_defines) { - if (def_state == IN_IFNDEF) { -- fprintf (fp, outstate->after_newline + else_defined); -+ fprintf (fp, outstate->after_newline + else_defined, -+ R_do_defines); - def_state = IN_ELSE; - } - else if (def_state == OUTSIDE) { -@@ -1164,7 +1166,8 @@ apply_hunk (struct outstate *outstate, L - while (pch_char (old) == '!'); - - if (R_do_defines) { -- fprintf (fp, outstate->after_newline + else_defined); -+ fprintf (fp, outstate->after_newline + else_defined, -+ R_do_defines); - if (ferror (fp)) - write_fatal (); - def_state = IN_ELSE; -@@ -1183,7 +1186,8 @@ apply_hunk (struct outstate *outstate, L - old++; - new++; - if (R_do_defines && def_state != OUTSIDE) { -- fprintf (fp, outstate->after_newline + end_defined); -+ fprintf (fp, outstate->after_newline + end_defined, -+ R_do_defines); - if (ferror (fp)) - write_fatal (); - outstate->after_newline = true; -@@ -1201,7 +1205,8 @@ apply_hunk (struct outstate *outstate, L - def_state = IN_IFDEF; - } - else if (def_state == IN_IFNDEF) { -- fprintf (fp, outstate->after_newline + else_defined); -+ fprintf (fp, outstate->after_newline + else_defined, -+ R_do_defines); - def_state = IN_ELSE; - } - if (ferror (fp)) -@@ -1220,7 +1225,8 @@ apply_hunk (struct outstate *outstate, L - while (new <= pat_end && pch_char (new) == '+'); - } - if (R_do_defines && def_state != OUTSIDE) { -- fprintf (fp, outstate->after_newline + end_defined); -+ fprintf (fp, outstate->after_newline + end_defined, -+ R_do_defines); - if (ferror (fp)) - write_fatal (); - outstate->after_newline = true; diff --git a/patch-2.5.9-cat_if_device.diff b/patch-2.5.9-cat_if_device.diff index 4bf8bfc..5efe1d8 100644 --- a/patch-2.5.9-cat_if_device.diff +++ b/patch-2.5.9-cat_if_device.diff @@ -1,8 +1,14 @@ -Index: patch-2.5.9/patch.c +--- + patch.c | 2 +- + util.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ + util.h | 1 + + 3 files changed, 52 insertions(+), 1 deletion(-) + +Index: b/patch.c =================================================================== ---- patch-2.5.9.orig/patch.c -+++ patch-2.5.9/patch.c -@@ -444,7 +444,7 @@ main (int argc, char **argv) +--- a/patch.c ++++ b/patch.c +@@ -440,7 +440,7 @@ main (int argc, char **argv) { move_file (TMPREJNAME, &TMPREJNAME_needs_removal, rej, instat.st_mode, false); @@ -11,10 +17,10 @@ Index: patch-2.5.9/patch.c && (chmod (rej, (instat.st_mode & ~(S_IXUSR|S_IXGRP|S_IXOTH))) != 0)) -Index: patch-2.5.9/util.c +Index: b/util.c =================================================================== ---- patch-2.5.9.orig/util.c -+++ patch-2.5.9/util.c +--- a/util.c ++++ b/util.c @@ -65,6 +65,49 @@ static bool fid_search (const char *, co FROM_NEEDS_REMOVAL must be nonnull if FROM is nonnull. Back up TO if BACKUP is true. */ @@ -79,11 +85,11 @@ Index: patch-2.5.9/util.c if (errno == EXDEV) { if (! backup) -Index: patch-2.5.9/util.h +Index: b/util.h =================================================================== ---- patch-2.5.9.orig/util.h -+++ patch-2.5.9/util.h -@@ -57,3 +57,4 @@ void remove_prefix (char *, size_t); +--- a/util.h ++++ b/util.h +@@ -59,3 +59,4 @@ void remove_prefix (char *, size_t); void removedirs (char *); void set_signals (bool); void write_fatal (void) __attribute__ ((noreturn)); diff --git a/patch-headers-in-reject-files.diff b/patch-headers-in-reject-files.diff new file mode 100644 index 0000000..f51ae04 --- /dev/null +++ b/patch-headers-in-reject-files.diff @@ -0,0 +1,221 @@ +--- + patch.c | 42 +++++++++++++++++++++++++++++++-------- + tests/unified-reject-files.shrun | 32 ++++++++++++++++++++++------- + 2 files changed, 58 insertions(+), 16 deletions(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -66,8 +66,8 @@ static bool patch_match (LINENUM, LINENU + static bool similar (char const *, size_t, char const *, size_t); + static bool spew_output (struct outstate *); + static char const *make_temp (char); +-static void abort_hunk_context (void); +-static void abort_hunk_unified (void); ++static void abort_hunk_context (bool, bool); ++static void abort_hunk_unified (bool, bool); + static int numeric_string (char const *, bool, char const *); + static void cleanup (void); + static void get_some_switches (void); +@@ -77,7 +77,7 @@ static void reinitialize_almost_everythi + static void remove_if_needed (char const *, int volatile *); + static void usage (FILE *, int) __attribute__((noreturn)); + +-static void (*abort_hunk) (void) = abort_hunk_context; ++static void (*abort_hunk) (bool, bool) = abort_hunk_context; + + static bool make_backups; + static bool backup_if_mismatch; +@@ -281,7 +281,7 @@ main (int argc, char **argv) + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { +- abort_hunk(); ++ abort_hunk(!failed, reverse); + failed++; + if (verbosity == VERBOSE) + say ("Hunk #%d ignored at %s.\n", hunk, +@@ -295,14 +295,14 @@ main (int argc, char **argv) + say ("Patch attempted to create file %s, which already exists.\n", + quotearg (inname)); + +- abort_hunk(); ++ abort_hunk(!failed, reverse); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %s.\n", hunk, + format_linenum (numbuf, newwhere)); + } + else if (! apply_hunk (&outstate, where)) { +- abort_hunk (); ++ abort_hunk (!failed, reverse); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %s.\n", hunk, +@@ -938,7 +938,24 @@ locate_hunk (LINENUM fuzz) + /* We did not find the pattern, dump out the hunk so they can handle it. */ + + static void +-abort_hunk_context (void) ++print_header_line(FILE *fp, const char *tag, bool reverse) ++{ ++ const char *name = pch_name(reverse); ++ time_t time = pch_timestamp(reverse); ++ char timebuf[37]; ++ ++ if (time != -1) { ++ timebuf[0] = '\t'; ++ strftime(timebuf + 1, sizeof(timebuf), ++ "%Y-%m-%d %H:%M:%S %z", ++ (set_utc ? gmtime : localtime)(&time)); ++ } else ++ *timebuf = '\0'; ++ fprintf(fp, "%s %s%s\n", tag, name ? name : "/dev/null", timebuf); ++} ++ ++static void ++abort_hunk_context (bool header, bool reverse) + { + register LINENUM i; + register LINENUM pat_end = pch_end (); +@@ -953,6 +970,10 @@ abort_hunk_context (void) + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; + char const *c_function = pch_c_function(); + ++ if (header) { ++ print_header_line(rejfp, "***", reverse); ++ print_header_line(rejfp, "---", ! reverse); ++ } + fprintf(rejfp, "***************%s\n", c_function ? c_function : ""); + for (i=0; i<=pat_end; i++) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; +@@ -1066,7 +1087,7 @@ print_unified(FILE *fp) + } + + static void +-abort_hunk_unified (void) ++abort_hunk_unified (bool header, bool reverse) + { + char rangebuf0[LINERANGE_LENGTH_BOUND + 1]; + char rangebuf1[LINERANGE_LENGTH_BOUND + 1]; +@@ -1074,6 +1095,11 @@ abort_hunk_unified (void) + LINENUM newfirst = pch_newfirst () + last_offset; + char const *c_function = pch_c_function (); + ++ if (header) ++ { ++ print_header_line (rejfp, "---", reverse); ++ print_header_line (rejfp, "+++", ! reverse); ++ } + fprintf (rejfp, "@@ -%s +%s @@%s\n", + format_startcount (rangebuf0, oldfirst, pch_ptrn_lines ()), + format_startcount (rangebuf1, newfirst, pch_repl_lines ()), +Index: b/tests/unified-reject-files.shrun +=================================================================== +--- a/tests/unified-reject-files.shrun ++++ b/tests/unified-reject-files.shrun +@@ -1,4 +1,6 @@ +-$ PATCH=$(pwd)/patch ++$ PATCH=$(PATH=.:$PATH which patch) ++$ patch() { $PATCH "$@"; } ++ + $ tmpdir=$(mktemp -d) + $ trap "cd /; rm -rf $tmpdir" EXIT + $ cd $tmpdir +@@ -17,12 +19,14 @@ $ diff -U0 -p \ + + -L "f 2009-02-07 14:49:03.000000000 +0100" \ + + f.orig f > f.diff + +-$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff ++$ patch -f -F0 -s --no-backup-if-mismatch f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + Note: patch cannot deal with nanosecond timestamps :-( + + $ cat f.rej ++> *** f.orig 2009-02-07 14:49:02 +0100 ++> --- f 2009-02-07 14:49:03 +0100 + > *************** a() { + > *** 5 **** + > - 5 +@@ -30,38 +34,46 @@ $ cat f.rej + > + 5a + + $ diff -U0 -p -L f.orig -L f f.orig f > f.diff +-$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff ++$ patch -f -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej ++> --- f.orig ++> +++ f + > @@ -5 +5 @@ a() { + > -5 + > +5a + +-$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff ++$ patch -f -F0 -s --no-backup-if-mismatch f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej ++> *** f.orig ++> --- f + > *************** a() { + > *** 5 **** + > - 5 + > --- 5 ---- + > + 5a + +-$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files -R f.orig < f.diff ++$ patch -f -F0 -s --no-backup-if-mismatch --unified-reject-files -R f.orig < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.orig.rej + + $ cat f.orig.rej ++> --- f ++> +++ f.orig + > @@ -5 +5 @@ a() { + > -5a + > +5 + + $ diff -U2 -p -L f.orig -L f f.orig f > f.diff + $ sed -e 's/5/5a/' -e 's/6/6x/' f.orig > f +-$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff ++$ patch -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej ++> --- f.orig ++> +++ f + > @@ -3,5 +3,5 @@ a() { + > 3 + > +@@ -70,10 +82,12 @@ $ cat f.rej + > 6 + > } + +-$ $PATCH -F0 -s --no-backup-if-mismatch f < f.diff ++$ patch -F0 -s --no-backup-if-mismatch f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej ++> *** f.orig ++> --- f + > *************** a() { + > *** 3,7 **** + > 3 +@@ -89,11 +103,13 @@ $ cat f.rej + > } + + $ diff -Nu -p -L /dev/null -L f.orig /dev/null f.orig > f2.diff +-$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f --set-utc < f2.diff ++$ patch -F0 -s --no-backup-if-mismatch --unified-reject-files f --set-utc < f2.diff + > Patch attempted to create file f, which already exists. + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej ++> --- /dev/null 1970-01-01 00:00:00 +0000 ++> +++ f.orig + > @@ -0,0 +1,7 @@ + > +a() { + > +2 diff --git a/patch-man-unified-reject.diff b/patch-man-unified-reject.diff deleted file mode 100644 index 5703e4a..0000000 --- a/patch-man-unified-reject.diff +++ /dev/null @@ -1,11 +0,0 @@ ---- patch-2.5.9/patch.man -+++ patch-2.5.9/patch.man -@@ -517,7 +517,7 @@ - .B \&.rej - file. - .TP --\fB\*=reject\-unified\fP -+\fB\*=unified\-reject\-files\fP - Produce unified reject files. The default is to produce context type reject files. - .TP - .BI \*=global\-reject\-file= rejectfile diff --git a/patch.changes b/patch.changes index 0e5fa1f..5afca9d 100644 --- a/patch.changes +++ b/patch.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Tue Feb 24 12:56:06 CET 2009 - agruen@suse.de + +- Include patch headers in reject files so that they form proper + patches themselves. +- Rewrite the unified reject files patch; this is much cleaner + now. +- Add an improved strategy for locating merges. + ------------------------------------------------------------------- Tue Feb 3 06:10:49 CET 2009 - agruen@suse.de @@ -137,14 +146,14 @@ Wed Apr 9 19:33:59 CEST 2003 - agruen@suse.de Wed Mar 26 14:00:55 CET 2003 - mmj@suse.de - Update to 2.5.8: - · Bugfixes - · patch -D now outputs preprocessor lines without comments, as + + Bugfixes + + patch -D now outputs preprocessor lines without comments, as required by POSIX 1003.1-2001 - · File names in context patches may now contain spaces, so long + + File names in context patches may now contain spaces, so long as the context patch headers use a tab to separate the file name from the time stamp - · Perforce is now supported - · Patch lines beginning with "#" are comments and are ignored + + Perforce is now supported + + Patch lines beginning with "#" are comments and are ignored ------------------------------------------------------------------- Wed Jan 15 16:50:57 CET 2003 - agruen@suse.de diff --git a/patch.spec b/patch.spec index 9d55898..c281b83 100644 --- a/patch.spec +++ b/patch.spec @@ -23,29 +23,29 @@ License: GPL v2 or later Group: Productivity/Text/Utilities AutoReqProv: on Version: 2.5.9 -Release: 287 +Release: 288 Summary: GNU patch Source: ftp://prep.ai.mit.edu/pub/gnu/patch/%{name}-%{version}.tar.bz2 Url: ftp://alpha.gnu.org/gnu/diffutils/ Patch: trailing-cr-fix.diff Patch1: remember-backup-files.diff -Patch2: unified-reject-files.diff -Patch3: global-reject-file.diff -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 +Patch2: pch_c_function.diff +Patch3: pch_name.diff +Patch4: preserve-c_function-in-reject-files.diff +Patch5: format_startcount.diff +Patch6: fix-timestamp-parsing.diff +Patch7: unified-reject-files.diff +Patch8: patch-headers-in-reject-files.diff +Patch9: global-reject-file.diff +Patch10: patch-2.5.9-cat_if_device.diff +Patch11: fix-partial-context.diff +Patch12: explain-pch_char-oddity.diff +Patch13: diff3-style-merges-tests.diff +Patch14: diff3-style-merges-refactoring.diff +Patch15: diff3-style-merges-refactoring-2.diff +Patch16: diff3-style-merges-simple-merge.diff +Patch17: diff3-style-merges-add-file-labels.diff +Patch18: diff3-style-merges-locate-merge.diff BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -66,6 +66,7 @@ Authors: %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 %patch5 -p1 %patch6 -p1 %patch7 -p1 @@ -105,6 +106,12 @@ make install \ %doc %{_mandir}/man1/patch.1.gz %changelog +* Tue Feb 24 2009 agruen@suse.de +- Include patch headers in reject files so that they form proper + patches themselves. +- Rewrite the unified reject files patch; this is much cleaner + now. +- Add an improved strategy for locating merges. * Tue Feb 03 2009 agruen@suse.de - Implement diff3-style merges (including several fixes and improvements). @@ -188,14 +195,14 @@ make install \ patch more than once. * Wed Mar 26 2003 mmj@suse.de - Update to 2.5.8: - · Bugfixes - · patch -D now outputs preprocessor lines without comments, as + + Bugfixes + + patch -D now outputs preprocessor lines without comments, as required by POSIX 1003.1-2001 - · File names in context patches may now contain spaces, so long + + File names in context patches may now contain spaces, so long as the context patch headers use a tab to separate the file name from the time stamp - · Perforce is now supported - · Patch lines beginning with "#" are comments and are ignored + + Perforce is now supported + + Patch lines beginning with "#" are comments and are ignored * Wed Jan 15 2003 agruen@suse.de - Fix a bug with hardlinks (see rename-same-file.patch) * Tue Sep 17 2002 ro@suse.de diff --git a/pch_c_function.diff b/pch_c_function.diff new file mode 100644 index 0000000..b225bf0 --- /dev/null +++ b/pch_c_function.diff @@ -0,0 +1,84 @@ +FIXME: free() + +--- + pch.c | 32 +++++++++++++++++++++++++++++++- + pch.h | 1 + + 2 files changed, 32 insertions(+), 1 deletion(-) + +Index: b/pch.c +=================================================================== +--- a/pch.c ++++ b/pch.c +@@ -68,6 +68,7 @@ static LINENUM p_sline; /* and the lin + static LINENUM p_hunk_beg; /* line number of current hunk */ + static LINENUM p_efake = -1; /* end of faked up lines--don't free */ + 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 }; + +@@ -888,6 +889,19 @@ another_hunk (enum diff difftype, bool r + next_intuit_at(line_beginning,p_input_line); + return chars_read == (size_t) -1 ? -1 : 0; + } ++ s = buf; ++ while (*s == '*') ++ s++; ++ if (*s == ' ') ++ { ++ p_c_function = s; ++ while (*s != '\n') ++ s++; ++ *s = '\0'; ++ p_c_function = savestr (p_c_function); ++ } ++ else ++ p_c_function = NULL; + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + chars_read = get_line (); +@@ -1277,8 +1291,18 @@ another_hunk (enum diff difftype, bool r + else + p_repl_lines = 1; + if (*s == ' ') s++; +- if (*s != '@') ++ if (*s++ != '@') + malformed (); ++ if (*s++ == '@' && *s == ' ' && *s != '\0') ++ { ++ p_c_function = s; ++ while (*s != '\n') ++ s++; ++ *s = '\0'; ++ p_c_function = savestr (p_c_function); ++ } ++ else ++ p_c_function = NULL; + if (!p_ptrn_lines) + p_first++; /* do append rather than insert */ + if (!p_repl_lines) +@@ -1884,6 +1908,12 @@ pch_hunk_beg (void) + return p_hunk_beg; + } + ++char const * ++pch_c_function (void) ++{ ++ return p_c_function; ++} ++ + /* Is the newline-terminated line a valid `ed' command for patch + input? If so, return the command character; if not, return 0. + This accepts accepts just a subset of the valid commands, but it's +Index: b/pch.h +=================================================================== +--- a/pch.h ++++ b/pch.h +@@ -25,6 +25,7 @@ + LINENUM pch_end (void); + LINENUM pch_first (void); + LINENUM pch_hunk_beg (void); ++char const *pch_c_function (void); + LINENUM pch_newfirst (void); + LINENUM pch_prefix_context (void); + LINENUM pch_ptrn_lines (void); diff --git a/diff3-style-merges-pch_name.diff b/pch_name.diff similarity index 98% rename from diff3-style-merges-pch_name.diff rename to pch_name.diff index 7c0db30..2852f10 100644 --- a/diff3-style-merges-pch_name.diff +++ b/pch_name.diff @@ -245,9 +245,9 @@ Index: b/pch.c 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. */ +@@ -1800,6 +1798,12 @@ pch_says_nonexistent (bool which) + return p_says_nonexistent[which]; + } +const char * +pch_name (enum nametype type) @@ -255,9 +255,9 @@ Index: b/pch.c + return type == NONE ? NULL : p_name[type]; +} + - time_t - pch_timestamp (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. */ + Index: b/pch.h =================================================================== --- a/pch.h diff --git a/preserve-c_function-in-reject-files.diff b/preserve-c_function-in-reject-files.diff new file mode 100644 index 0000000..4d63ef4 --- /dev/null +++ b/preserve-c_function-in-reject-files.diff @@ -0,0 +1,21 @@ +Include the C function names in reject files whenever possible. + +--- + patch.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +Index: b/patch.c +=================================================================== +--- a/patch.c ++++ b/patch.c +@@ -943,8 +943,9 @@ abort_hunk (void) + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; + char const *minuses = + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; ++ char const *c_function = pch_c_function(); + +- fprintf(rejfp, "***************\n"); ++ fprintf(rejfp, "***************%s\n", c_function ? c_function : ""); + for (i=0; i<=pat_end; i++) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; diff --git a/unified-reject-files.diff b/unified-reject-files.diff index a21ddf6..3fd31fc 100644 --- a/unified-reject-files.diff +++ b/unified-reject-files.diff @@ -1,173 +1,50 @@ -Generate unified diff style reject files. Also include the C function names -in reject files whenever possible. +Generate unified diff style reject files. - $ cat > f.orig - < a() { - < 2 - < 3 - < - < 5 - < 6 - < } +--- + patch.c | 104 +++++++++++++++++++++++++++++++++++++-- + patch.man | 3 + + tests/unified-reject-files.shrun | 104 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 208 insertions(+), 3 deletions(-) - $ sed -e 's/5/5a/' f.orig > f - $ diff -U2 -p f.orig f > f.diff - $ sed -e 's/5/5a/' -e 's/6/6x/' f.orig > f - $ ./patch -F0 -s --no-backup-if-mismatch f --reject-unified < f.diff - > 1 out of 1 hunk FAILED -- saving rejects to file f.rej - - $ cat f.rej - > @@ -3,5 +3,5 @@ a() { - > 3 - > - > -5 - > +5a - > 6 - > } - - $ ./patch -F0 -s --no-backup-if-mismatch f < f.diff - > 1 out of 1 hunk FAILED -- saving rejects to file f.rej - - $ cat f.rej - > *************** a() { - > *** 3,7 **** - > 3 - > - > - 5 - > 6 - > } - > --- 3,7 ---- - > 3 - > - > + 5a - > 6 - > } - - $ diff -Nu -p /dev/null f.orig > f2.diff - $ ./patch -F0 -s --no-backup-if-mismatch f --reject-unified < f2.diff - > Patch attempted to create file f, which already exists. - > 1 out of 1 hunk FAILED -- saving rejects to file f.rej - - $ cat f.rej - > @@ -0,0 +1,7 @@ - > +a() { - > +2 - > +3 - > + - > +5 - > +6 - > +} - - $ rm -f f f.orig f.rej f.diff f2.diff - -Index: patch-2.5.9/pch.c +Index: b/patch.man =================================================================== ---- patch-2.5.9.orig/pch.c -+++ patch-2.5.9/pch.c -@@ -68,6 +68,7 @@ static LINENUM p_sline; /* and the lin - static LINENUM p_hunk_beg; /* line number of current hunk */ - static LINENUM p_efake = -1; /* end of faked up lines--don't free */ - 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 }; - -@@ -888,6 +889,19 @@ another_hunk (enum diff difftype, bool r - next_intuit_at(line_beginning,p_input_line); - return chars_read == (size_t) -1 ? -1 : 0; - } -+ s = buf; -+ while (*s == '*') -+ s++; -+ if (*s == ' ') -+ { -+ p_c_function = s; -+ while (*s != '\n') -+ s++; -+ *s = '\0'; -+ p_c_function = savestr (p_c_function); -+ } -+ else -+ p_c_function = NULL; - p_hunk_beg = p_input_line + 1; - while (p_end < p_max) { - chars_read = get_line (); -@@ -1277,8 +1291,18 @@ another_hunk (enum diff difftype, bool r - else - p_repl_lines = 1; - if (*s == ' ') s++; -- if (*s != '@') -+ if (*s++ != '@') - malformed (); -+ if (*s++ == '@' && *s == ' ' && *s != '\0') -+ { -+ p_c_function = s; -+ while (*s != '\n') -+ s++; -+ *s = '\0'; -+ p_c_function = savestr (p_c_function); -+ } -+ else -+ p_c_function = NULL; - if (!p_ptrn_lines) - p_first++; /* do append rather than insert */ - if (!p_repl_lines) -@@ -1884,6 +1908,12 @@ pch_hunk_beg (void) - return p_hunk_beg; - } - -+char const * -+pch_c_function (void) -+{ -+ return p_c_function; -+} -+ - /* Is the newline-terminated line a valid `ed' command for patch - input? If so, return the command character; if not, return 0. - This accepts accepts just a subset of the valid commands, but it's -Index: patch-2.5.9/pch.h -=================================================================== ---- patch-2.5.9.orig/pch.h -+++ patch-2.5.9/pch.h -@@ -25,6 +25,7 @@ - LINENUM pch_end (void); - LINENUM pch_first (void); - LINENUM pch_hunk_beg (void); -+char const *pch_c_function (void); - LINENUM pch_newfirst (void); - LINENUM pch_prefix_context (void); - LINENUM pch_ptrn_lines (void); -Index: patch-2.5.9/patch.man -=================================================================== ---- patch-2.5.9.orig/patch.man -+++ patch-2.5.9/patch.man +--- a/patch.man ++++ b/patch.man @@ -517,6 +517,9 @@ instead of the default .B \&.rej file. .TP -+\fB\*=reject\-unified\fP ++\fB\*=unified\-reject\-files\fP +Produce unified reject files. The default is to produce context type reject files. +.TP \fB\-R\fP or \fB\*=reverse\fP Assume that this patch was created with the old and new files swapped. (Yes, I'm afraid that does happen occasionally, human nature being what it -Index: patch-2.5.9/common.h +Index: b/patch.c =================================================================== ---- patch-2.5.9.orig/common.h -+++ patch-2.5.9/common.h -@@ -146,6 +146,7 @@ XTERN int invc; - XTERN struct stat instat; - XTERN bool dry_run; - XTERN bool posixly_correct; -+XTERN bool unified_reject_files; +--- a/patch.c ++++ b/patch.c +@@ -66,8 +66,9 @@ static bool patch_match (LINENUM, LINENU + static bool similar (char const *, size_t, char const *, size_t); + static bool spew_output (struct outstate *); + static char const *make_temp (char); ++static void abort_hunk_context (void); ++static void abort_hunk_unified (void); + static int numeric_string (char const *, bool, char const *); +-static void abort_hunk (void); + static void cleanup (void); + static void get_some_switches (void); + static void init_output (char const *, int, struct outstate *); +@@ -76,6 +77,8 @@ static void reinitialize_almost_everythi + static void remove_if_needed (char const *, int volatile *); + static void usage (FILE *, int) __attribute__((noreturn)); - XTERN char const *origprae; - XTERN char const *origbase; -Index: patch-2.5.9/patch.c -=================================================================== ---- patch-2.5.9.orig/patch.c -+++ patch-2.5.9/patch.c -@@ -522,6 +522,7 @@ static struct option const longopts[] = ++static void (*abort_hunk) (void) = abort_hunk_context; ++ + static bool make_backups; + static bool backup_if_mismatch; + static char const *version_control; +@@ -522,6 +525,7 @@ static struct option const longopts[] = {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, {"posix", no_argument, NULL, CHAR_MAX + 7}, {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, @@ -175,7 +52,7 @@ Index: patch-2.5.9/patch.c {NULL, no_argument, NULL, 0} }; -@@ -580,6 +581,7 @@ static char const *const option_help[] = +@@ -580,6 +584,7 @@ static char const *const option_help[] = " --verbose Output extra information about the work being done.", " --dry-run Do not actually change any files; just print what would happen.", " --posix Conform to the POSIX standard.", @@ -183,123 +60,236 @@ Index: patch-2.5.9/patch.c "", " -d DIR --directory=DIR Change the working directory to DIR first.", #if HAVE_SETMODE_DOS -@@ -779,6 +781,9 @@ get_some_switches (void) +@@ -779,6 +784,9 @@ get_some_switches (void) (enum quoting_style) i); } break; + case CHAR_MAX + 9: -+ unified_reject_files = true; ++ abort_hunk = abort_hunk_unified; + break; default: usage (stderr, 2); } -@@ -927,6 +932,24 @@ locate_hunk (LINENUM fuzz) - return 0; - } - -+static char * -+format_linerange (char rangebuf[LINENUM_LENGTH_BOUND*2 + 2], -+ LINENUM first, LINENUM lines) -+{ -+ if (lines == 1) -+ rangebuf = format_linenum (rangebuf, first); -+ else -+ { -+ char *rb; -+ rangebuf = format_linenum (rangebuf + LINENUM_LENGTH_BOUND + 1, lines); -+ rb = rangebuf-1; -+ rangebuf = format_linenum (rangebuf - LINENUM_LENGTH_BOUND - 1, -+ (lines > 0) ? first : 0); -+ *rb = ','; -+ } -+ return rangebuf; -+} -+ +@@ -930,7 +938,7 @@ locate_hunk (LINENUM fuzz) /* We did not find the pattern, dump out the hunk so they can handle it. */ static void -@@ -943,8 +966,83 @@ abort_hunk (void) - (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; - char const *minuses = - (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; -+ char const *function = pch_c_function(); -+ if (function == NULL) -+ function = ""; -+ -+ if (unified_reject_files) -+ { -+ /* produce unified reject files */ -+ char rangebuf0[LINENUM_LENGTH_BOUND*2 + 2]; -+ char rangebuf1[LINENUM_LENGTH_BOUND*2 + 2]; -+ LINENUM j; -+ -+ /* Find the beginning of the remove and insert section. */ -+ for (j = 0; j <= pat_end; j++) -+ if (pch_char (j) == '=') -+ break; -+ for (i = j+1; i <= pat_end; i++) -+ if (pch_char (i) == '^') -+ break; -+ if (pch_char (0) != '*' || j > pat_end || i > pat_end+1) -+ fatal ("internal error in abort_hunk"); -+ i = 1; j++; -+ -+ /* @@ -from,lines +to,lines @@ */ -+ fprintf (rejfp, "@@ -%s +%s @@%s\n", -+ format_linerange (rangebuf0, oldfirst, pch_ptrn_lines()), -+ format_linerange (rangebuf1, newfirst, pch_repl_lines()), -+ function); -+ -+ while ( (i <= pat_end && pch_char (i) != '=') -+ || (j <= pat_end && pch_char (j) != '^')) -+ { -+ if (i <= pat_end -+ && (pch_char (i) == '-' || pch_char (i) == '!')) -+ { -+ fputc('-', rejfp); -+ pch_write_line (i++, rejfp); -+ } -+ else if (j <= pat_end -+ && (pch_char (j) == '+' || pch_char (j) == '!')) -+ { -+ fputc('+', rejfp); -+ pch_write_line (j++, rejfp); -+ } -+ else if ((i <= pat_end -+ && (pch_char (i) == ' ' || pch_char (i) == '\n')) && -+ (j > pat_end -+ || (pch_char (j) == ' ' || pch_char (j) == '\n'))) -+ { -+ /* Unless j is already past the end, lines i and j -+ must be equal here. */ -+ -+ if (pch_char (i) == ' ') -+ fputc(' ', rejfp); -+ pch_write_line (i++, rejfp); -+ if (j <= pat_end) -+ j++; -+ } -+ else if ((j <= pat_end && -+ (pch_char (j) == ' ' || pch_char (j) == '\n')) && -+ (pch_char (i) == '=')) -+ { -+ if (pch_char (j) == ' ') -+ fputc(' ', rejfp); -+ pch_write_line (j++, rejfp); -+ } -+ else -+ fatal ("internal error in abort_hunk"); -+ } -+ -+ if (ferror (rejfp)) -+ write_fatal (); -+ return; -+ } +-abort_hunk (void) ++abort_hunk_context (void) + { + register LINENUM i; + register LINENUM pat_end = pch_end (); +@@ -979,13 +987,103 @@ abort_hunk (void) + pch_write_line (i, rejfp); + break; + default: +- fatal ("fatal internal error in abort_hunk"); ++ fatal ("fatal internal error in abort_hunk_context"); + } + if (ferror (rejfp)) + write_fatal (); + } + } -- fprintf(rejfp, "***************\n"); -+ /* produce context type reject files */ -+ -+ fprintf(rejfp, "***************%s\n", function); - for (i=0; i<=pat_end; i++) { - char numbuf0[LINENUM_LENGTH_BOUND + 1]; - char numbuf1[LINENUM_LENGTH_BOUND + 1]; ++/* Produce unified reject files */ ++ ++static void ++print_unified(FILE *fp) ++{ ++ register LINENUM old = 1; ++ register LINENUM lastline = pch_ptrn_lines (); ++ register LINENUM new = lastline + 1; ++ register LINENUM pat_end = pch_end (); ++ ++ while (pch_char (new) == '=' || pch_char (new) == '\n') ++ new++; ++ ++ while (old <= lastline) ++ { ++ if (pch_char (old) == '-') ++ { ++ fputc ('-', fp); ++ pch_write_line (old++, fp); ++ } ++ else if (new > pat_end) ++ break; ++ else if (pch_char (new) == '+') ++ { ++ fputc ('+', fp); ++ pch_write_line (new++, fp); ++ } ++ else if (pch_char (new) != pch_char (old)) ++ { ++ char numbuf0[LINENUM_LENGTH_BOUND + 1]; ++ char numbuf1[LINENUM_LENGTH_BOUND + 1]; ++ if (debug & 1) ++ say ("oldchar = '%c', newchar = '%c'\n", ++ pch_char (old), pch_char (new)); ++ fatal ("Out-of-sync patch, lines %s,%s -- mangled text or " ++ "line numbers, maybe?", ++ format_linenum (numbuf0, pch_hunk_beg () + old), ++ format_linenum (numbuf1, pch_hunk_beg () + new)); ++ } ++ else if (pch_char (new) == '!') ++ { ++ do ++ { ++ fputc ('-', fp); ++ pch_write_line (old++, fp); ++ } ++ while (pch_char (old) == '!'); ++ do ++ { ++ fputc ('+', fp); ++ pch_write_line (new++, fp); ++ } ++ while (pch_char (new) == '!'); ++ } ++ else ++ { ++ assert (pch_char (new) == ' '); ++ fputc (' ', fp); ++ pch_write_line (new, fp); ++ old++; ++ new++; ++ } ++ } ++ while (pch_char (new) == '+' || pch_char (new) == ' ') ++ { ++ fputc ('+', fp); ++ pch_write_line (new++, fp); ++ } ++ assert (new > pat_end); ++} ++ ++static void ++abort_hunk_unified (void) ++{ ++ char rangebuf0[LINERANGE_LENGTH_BOUND + 1]; ++ char rangebuf1[LINERANGE_LENGTH_BOUND + 1]; ++ LINENUM oldfirst = pch_first () + last_offset; ++ LINENUM newfirst = pch_newfirst () + last_offset; ++ char const *c_function = pch_c_function (); ++ ++ fprintf (rejfp, "@@ -%s +%s @@%s\n", ++ format_startcount (rangebuf0, oldfirst, pch_ptrn_lines ()), ++ format_startcount (rangebuf1, newfirst, pch_repl_lines ()), ++ c_function ? c_function : ""); ++ print_unified (rejfp); ++ ++ if (ferror (rejfp)) ++ write_fatal (); ++} ++ + /* We found where to apply it (we hope), so do it. */ + + static bool +Index: b/tests/unified-reject-files.shrun +=================================================================== +--- /dev/null ++++ b/tests/unified-reject-files.shrun +@@ -0,0 +1,104 @@ ++$ PATCH=$(pwd)/patch ++$ tmpdir=$(mktemp -d) ++$ trap "cd /; rm -rf $tmpdir" EXIT ++$ cd $tmpdir ++$ cat > f.orig ++< a() { ++< 2 ++< 3 ++< ++< 5 ++< 6 ++< } ++ ++$ sed -e 's/5/5a/' f.orig > f ++$ diff -U0 -p \ +++ -L "f.orig 2009-02-07 14:49:02.000000000 +0100" \ +++ -L "f 2009-02-07 14:49:03.000000000 +0100" \ +++ f.orig f > f.diff ++ ++$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++Note: patch cannot deal with nanosecond timestamps :-( ++ ++$ cat f.rej ++> *************** a() { ++> *** 5 **** ++> - 5 ++> --- 5 ---- ++> + 5a ++ ++$ diff -U0 -p -L f.orig -L f f.orig f > f.diff ++$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++$ cat f.rej ++> @@ -5 +5 @@ a() { ++> -5 ++> +5a ++ ++$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++$ cat f.rej ++> *************** a() { ++> *** 5 **** ++> - 5 ++> --- 5 ---- ++> + 5a ++ ++$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files -R f.orig < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.orig.rej ++ ++$ cat f.orig.rej ++> @@ -5 +5 @@ a() { ++> -5a ++> +5 ++ ++$ diff -U2 -p -L f.orig -L f f.orig f > f.diff ++$ sed -e 's/5/5a/' -e 's/6/6x/' f.orig > f ++$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++$ cat f.rej ++> @@ -3,5 +3,5 @@ a() { ++> 3 ++> ++> -5 ++> +5a ++> 6 ++> } ++ ++$ $PATCH -F0 -s --no-backup-if-mismatch f < f.diff ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++$ cat f.rej ++> *************** a() { ++> *** 3,7 **** ++> 3 ++> ++> - 5 ++> 6 ++> } ++> --- 3,7 ---- ++> 3 ++> ++> + 5a ++> 6 ++> } ++ ++$ diff -Nu -p -L /dev/null -L f.orig /dev/null f.orig > f2.diff ++$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f --set-utc < f2.diff ++> Patch attempted to create file f, which already exists. ++> 1 out of 1 hunk FAILED -- saving rejects to file f.rej ++ ++$ cat f.rej ++> @@ -0,0 +1,7 @@ ++> +a() { ++> +2 ++> +3 ++> + ++> +5 ++> +6 ++> +}