OBS User unknown 2009-02-26 17:17:05 +00:00 committed by Git OBS Bridge
parent e553963af3
commit 4031722516
26 changed files with 2603 additions and 1663 deletions

View File

@ -1,29 +1,36 @@
From: Andreas Gruenbacher <agruen@suse.de>
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 <agruen@suse.de>
---
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

View File

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

View File

@ -1,73 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
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 <agruen@suse.de>
---
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;

View File

@ -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

View File

@ -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);

View File

@ -1,249 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: diff3-style merges: overlapping merges
In some situations, hunks in a patch may apply so that their context
lines overlap. Patch never commits context lines to the output until
their contents are fully determined.
The diff3-style merge format may include context lines. For example,
when he following patch:
# --- a
# +++ b
# @@ -2,3 +2,3 @@
# x
# -3
# +3b
# x
is applied to `seq 1 6` file with --merge=all, this is what you get (with
fuzz 1):
1
<<<<<<< a
x
3
x
||||||| b
x
3b
x
=======
2
3b
4
>>>>>>>
5
6
At this point, the first four lines of the output file are "frozen", and
the next hunk cannot modify the fourth line anymore. This can cause the diff3-
style merge format to reject hunks that would otherwise be accepted. To
avoid this regression, this patch adds delayed printing of the end marker,
with this effect on the merge format:
1
<<<<<<< a
x
3
x
||||||| b
x
3b
x
=======
2
3b
>>>>>>>
<<<<<<< a
x
4
x
||||||| b
x
4b
x
=======
4b
5
>>>>>>>
6
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
patch.c | 71 +++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 48 insertions(+), 23 deletions(-)
Index: b/patch.c
===================================================================
--- a/patch.c
+++ b/patch.c
@@ -63,6 +63,7 @@ static LINENUM locate_hunk (LINENUM);
static bool apply_hunk (struct outstate *, LINENUM);
static bool common_context(LINENUM, LINENUM, LINENUM);
static bool merge_hunk (struct outstate *, LINENUM, LINENUM);
+static void merge_ends_here(struct outstate *outstate);
static bool copy_till (struct outstate *, LINENUM);
static bool patch_match (LINENUM, LINENUM, LINENUM, LINENUM);
static bool similar (char const *, size_t, char const *, size_t);
@@ -81,6 +82,8 @@ static void usage (FILE *, int) __attrib
enum mergetype { SHOW_ALL = 1, SHOW_FUZZ = 2, MERGE_REJECTS = 4 };
static enum mergetype mergetype;
+static LINENUM merge_end = -1;
+static const char *merge_name;
const char *file_label[2];
static bool make_backups;
@@ -1361,7 +1364,7 @@ static bool merge_hunk (struct outstate
register LINENUM lastline = pch_ptrn_lines ();
register LINENUM new = lastline + 1;
register LINENUM pat_end = pch_end ();
- register LINENUM merge, merge_end;
+ register LINENUM merge, R_merge_end;
register FILE *fp = outstate->ofp;
bool succeeded = true;
const char *name;
@@ -1381,32 +1384,32 @@ static bool merge_hunk (struct outstate
if (fuzz != -1) {
/* Hide common suffix context */
- merge_end = where + lastline - 1;
+ R_merge_end = where + lastline - 1;
while (old <= lastline && new <= pat_end) {
- if (!common_context(merge_end, lastline, pat_end))
+ if (!common_context(R_merge_end, lastline, pat_end))
break;
lastline--;
pat_end--;
- merge_end--;
+ R_merge_end--;
}
} else {
LINENUM overlap = pch_suffix_context ();
/* Hide common suffix context: check how much overlap we have! */
- merge_end = merge - 1;
+ R_merge_end = merge - 1;
while (overlap) {
LINENUM n = 0;
- merge_end += overlap;
+ R_merge_end += overlap;
while (n < overlap && old <= lastline && new <= pat_end) {
- if (!common_context(merge_end, lastline, pat_end))
+ if (!common_context(R_merge_end, lastline, pat_end))
break;
lastline--;
pat_end--;
- merge_end--;
+ R_merge_end--;
n++;
}
- merge_end -= overlap - n;
+ R_merge_end -= overlap - n;
if (n == overlap)
break;
lastline += n;
@@ -1419,7 +1422,8 @@ static bool merge_hunk (struct outstate
if (last_frozen_line < merge) {
if (!copy_till(outstate, merge - 1))
return false;
- }
+ } else
+ merge_ends_here(outstate);
/* "From" lines in the patch */
name = file_label[OLD] ? file_label[OLD] : pch_name(OLD);
@@ -1458,25 +1462,41 @@ static bool merge_hunk (struct outstate
write_fatal ();
outstate->after_newline = true;
- succeeded = apply_hunk(outstate, where) &&
- copy_till(outstate, merge_end);
+ succeeded = apply_hunk(outstate, where);
}
+ assert(merge_end == -1);
+ merge_end = R_merge_end;
+
/* If the merge result and the new file are the same, label the merge
result with the new file's name. */
- name = fuzz ? NULL : (file_label[NEW] ? file_label[NEW] : pch_name(NEW));
- if (!name)
- name = "";
- fprintf(fp, outstate->after_newline + "\n>>>>>>>%*s\n",
- strlen(name) ? strlen(name) + 1 : 0, name);
- if (ferror (fp))
- write_fatal ();
- outstate->after_newline = true;
+ merge_name = fuzz ? NULL : (file_label[NEW] ? file_label[NEW] :
+ pch_name(NEW));
outstate->zero_output = false;
return succeeded;
}
+static void
+merge_ends_here(struct outstate *outstate)
+{
+ register FILE *fp = outstate->ofp;
+
+ if (merge_end != -1)
+ {
+ if (!merge_name)
+ merge_name = "";
+ fprintf(fp, outstate->after_newline + "\n>>>>>>>%*s\n",
+ strlen(merge_name) ? strlen(merge_name) + 1 : 0,
+ merge_name);
+ if (ferror (fp))
+ write_fatal ();
+ outstate->after_newline = true;
+ outstate->zero_output = false;
+ merge_end = -1;
+ }
+}
+
/* Create an output file. */
static FILE *
@@ -1516,6 +1536,7 @@ static bool
copy_till (register struct outstate *outstate, register LINENUM lastline)
{
register LINENUM R_last_frozen_line = last_frozen_line;
+ register LINENUM R_merge_end = (merge_end != -1) ? merge_end : lastline;
register FILE *fp = outstate->ofp;
register char const *s;
size_t size;
@@ -1527,6 +1548,9 @@ copy_till (register struct outstate *out
}
while (R_last_frozen_line < lastline)
{
+ if (R_merge_end == R_last_frozen_line)
+ merge_ends_here(outstate);
+
s = ifetch (++R_last_frozen_line, false, &size);
if (size)
{
@@ -1538,6 +1562,8 @@ copy_till (register struct outstate *out
}
}
last_frozen_line = R_last_frozen_line;
+ if (merge_end != -1)
+ merge_ends_here(outstate);
return true;
}
@@ -1555,9 +1581,8 @@ spew_output (struct outstate *outstate)
format_linenum (numbuf1, last_frozen_line));
}
- if (last_frozen_line < input_lines)
- if (! copy_till (outstate, input_lines))
- return false;
+ if (! copy_till (outstate, input_lines))
+ return false;
if (outstate->ofp && ! outfile)
{

View File

@ -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,

View File

@ -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))

View File

@ -1,116 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
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 <agruen@suse.de>
---
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;

View File

@ -0,0 +1,442 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: diff3-style merges
Implement a diff3-style merge format: with the --merge option alone,
all hunks that apply without fuzz will be applied as usual, and
hunks that apply within the allowed fuzz limit will be bracketed as:
<<<<<<<
old lines from patch
|||||||
new lines from patch
=======
merge result
>>>>>>>
When the --show-all option is given in addition, hunks that apply without
fuzz will be bracketed as:
<<<<<<<
old lines from patch
=======
merge result
>>>>>>>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
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 <common.h>
+#include <inp.h>
+#include <pch.h>
+#include <util.h>
+
+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));
+}

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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 <eggert@twinsun.com>
* 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;

View File

@ -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;
}

74
format_startcount.diff Normal file
View File

@ -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));

View File

@ -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

View File

@ -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;

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

84
pch_c_function.diff Normal file
View File

@ -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);

View File

@ -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

View File

@ -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];

View File

@ -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
+> +}