static int newfd = -1;
static int p_value = 1;
-static int allow_binary_replacement;
static int check_index;
static int write_index;
static int cached;
static int check;
static int apply = 1;
static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
static int no_add;
static int show_index_info;
static int line_termination = '\n';
static unsigned long p_context = -1;
static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
static enum whitespace_eol {
nowarn_whitespace,
unsigned long newpos, newlines;
const char *patch;
int size;
+ int rejected;
struct fragment *next;
};
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
int is_rename, is_copy, is_new, is_delete, is_binary;
+ int rejected;
unsigned long deflate_origlen;
int lines_added, lines_deleted;
int score;
struct patch *next;
};
+static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+{
+ fputs(pre, output);
+ if (patch->old_name && patch->new_name &&
+ strcmp(patch->old_name, patch->new_name)) {
+ write_name_quoted(NULL, 0, patch->old_name, 1, output);
+ fputs(" => ", output);
+ write_name_quoted(NULL, 0, patch->new_name, 1, output);
+ }
+ else {
+ const char *n = patch->new_name;
+ if (!n)
+ n = patch->old_name;
+ write_name_quoted(NULL, 0, n, 1, output);
+ }
+ fputs(post, output);
+}
+
#define CHUNKSIZE (8192)
#define SLOP (16)
* form.
*/
for (len = 0 ; ; len++) {
- char c = name[len];
-
- switch (c) {
+ switch (name[len]) {
default:
continue;
case '\n':
llen = linelen(buffer, size);
used += llen;
linenr++;
- if (llen == 1)
+ if (llen == 1) {
+ /* consume the blank line */
+ buffer++;
+ size--;
break;
+ }
/* Minimum line is "A00000\n" which is 7-byte long,
* and the line length must be multiple of 5 plus 2.
*/
return frag;
corrupt:
- if (data)
- free(data);
+ free(data);
*status_p = -1;
error("corrupt binary patch at line %d: %.*s",
linenr-1, llen-1, buffer);
}
}
- /* Empty patch cannot be applied if:
- * - it is a binary patch and we do not do binary_replace, or
- * - text patch without metadata change
+ /* Empty patch cannot be applied if it is a text patch
+ * without metadata change. A binary patch appears
+ * empty to us here.
*/
if ((apply || check) &&
- (patch->is_binary
- ? !allow_binary_replacement
- : !metadata_changes(patch)))
+ (!patch->is_binary && !metadata_changes(patch)))
die("patch with only garbage at line %d", linenr);
}
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
len, name, patch->lines_added + patch->lines_deleted,
add, pluses, del, minuses);
- if (qname)
- free(qname);
+ free(qname);
}
static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
lines = 0;
pos = frag->newpos;
for (;;) {
- offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+ offset = find_offset(buf, desc->size,
+ oldlines, oldsize, pos, &lines);
if (match_end && offset + oldsize != desc->size)
offset = -1;
if (match_beginning && offset)
/* Warn if it was necessary to reduce the number
* of context lines.
*/
- if ((leading != frag->leading) || (trailing != frag->trailing))
- fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+ if ((leading != frag->leading) ||
+ (trailing != frag->trailing))
+ fprintf(stderr, "Context reduced to (%ld/%ld)"
+ " to apply fragment at %d\n",
leading, trailing, pos + lines);
if (size > alloc) {
desc->buffer = buf;
}
desc->size = size;
- memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+ memmove(buf + offset + newsize,
+ buf + offset + oldsize,
+ size - offset - newsize);
memcpy(buf + offset, newlines, newsize);
offset = 0;
"without the reverse hunk to '%s'",
patch->new_name
? patch->new_name : patch->old_name);
- fragment = fragment;
+ fragment = fragment->next;
}
data = (void*) fragment->patch;
switch (fragment->binary_patch_method) {
unsigned char hdr[50];
int hdrlen;
- if (!allow_binary_replacement)
- return error("cannot apply binary patch to '%s' "
- "without --allow-binary-replacement",
- name);
-
/* For safety, we require patch index line to contain
* full 40-byte textual SHA1 for old and new, at least for now.
*/
write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
sha1, hdr, &hdrlen);
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
- return error("binary patch to '%s' creates incorrect result", name);
+ return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
}
return 0;
return apply_binary(desc, patch);
while (frag) {
- if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
- return error("patch failed: %s:%ld",
- name, frag->oldpos);
+ if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+ error("patch failed: %s:%ld", name, frag->oldpos);
+ if (!apply_with_reject)
+ return -1;
+ frag->rejected = 1;
+ }
frag = frag->next;
}
return 0;
desc.size = size;
desc.alloc = alloc;
desc.buffer = buf;
+
if (apply_fragments(&desc, patch) < 0)
- return -1;
+ return -1; /* note with --reject this succeeds. */
/* NUL terminate the result */
if (desc.alloc <= desc.size)
struct cache_entry *ce = NULL;
int ok_if_exists;
+ patch->rejected = 1; /* we will drop this after we succeed */
if (old_name) {
int changed = 0;
int stat_ret = 0;
if (apply_data(patch, &st, ce) < 0)
return error("%s: patch does not apply", name);
+ patch->rejected = 0;
return 0;
}
static int check_patch_list(struct patch *patch)
{
struct patch *prev_patch = NULL;
- int error = 0;
+ int err = 0;
for (prev_patch = NULL; patch ; patch = patch->next) {
- error |= check_patch(patch, prev_patch);
+ if (apply_verbosely)
+ say_patch_name(stderr,
+ "Checking patch ", patch, "...\n");
+ err |= check_patch(patch, prev_patch);
prev_patch = patch;
}
- return error;
+ return err;
}
static void show_index_list(struct patch *list)
if (phase == 0)
remove_file(patch);
if (phase == 1)
- create_file(patch);
+ create_file(patch);
}
-static void write_out_results(struct patch *list, int skipped_patch)
+static int write_out_one_reject(struct patch *patch)
+{
+ FILE *rej;
+ char namebuf[PATH_MAX];
+ struct fragment *frag;
+ int cnt = 0;
+
+ for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+ if (!frag->rejected)
+ continue;
+ cnt++;
+ }
+
+ if (!cnt) {
+ if (apply_verbosely)
+ say_patch_name(stderr,
+ "Applied patch ", patch, " cleanly.\n");
+ return 0;
+ }
+
+ /* This should not happen, because a removal patch that leaves
+ * contents are marked "rejected" at the patch level.
+ */
+ if (!patch->new_name)
+ die("internal error");
+
+ /* Say this even without --verbose */
+ say_patch_name(stderr, "Applying patch ", patch, " with");
+ fprintf(stderr, " %d rejects...\n", cnt);
+
+ cnt = strlen(patch->new_name);
+ if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+ cnt = ARRAY_SIZE(namebuf) - 5;
+ fprintf(stderr,
+ "warning: truncating .rej filename to %.*s.rej",
+ cnt - 1, patch->new_name);
+ }
+ memcpy(namebuf, patch->new_name, cnt);
+ memcpy(namebuf + cnt, ".rej", 5);
+
+ rej = fopen(namebuf, "w");
+ if (!rej)
+ return error("cannot open %s: %s", namebuf, strerror(errno));
+
+ /* Normal git tools never deal with .rej, so do not pretend
+ * this is a git patch by saying --git nor give extended
+ * headers. While at it, maybe please "kompare" that wants
+ * the trailing TAB and some garbage at the end of line ;-).
+ */
+ fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+ patch->new_name, patch->new_name);
+ for (cnt = 1, frag = patch->fragments;
+ frag;
+ cnt++, frag = frag->next) {
+ if (!frag->rejected) {
+ fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+ continue;
+ }
+ fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+ fprintf(rej, "%.*s", frag->size, frag->patch);
+ if (frag->patch[frag->size-1] != '\n')
+ fputc('\n', rej);
+ }
+ fclose(rej);
+ return -1;
+}
+
+static int write_out_results(struct patch *list, int skipped_patch)
{
int phase;
+ int errs = 0;
+ struct patch *l;
if (!list && !skipped_patch)
- die("No changes");
+ return error("No changes");
for (phase = 0; phase < 2; phase++) {
- struct patch *l = list;
+ l = list;
while (l) {
- write_out_one_result(l, phase);
+ if (l->rejected)
+ errs = 1;
+ else {
+ write_out_one_result(l, phase);
+ if (phase == 1 && write_out_one_reject(l))
+ errs = 1;
+ }
l = l->next;
}
}
+ return errs;
}
static struct lock_file lock_file;
die("unable to read index file");
}
- if ((check || apply) && check_patch_list(list) < 0)
+ if ((check || apply) &&
+ check_patch_list(list) < 0 &&
+ !apply_with_reject)
exit(1);
- if (apply)
- write_out_results(list, skipped_patch);
+ if (apply && write_out_results(list, skipped_patch))
+ exit(1);
if (show_index_info)
show_index_list(list);
static int git_apply_config(const char *var, const char *value)
{
if (!strcmp(var, "apply.whitespace")) {
- apply_default_whitespace = strdup(value);
+ apply_default_whitespace = xstrdup(value);
return 0;
}
return git_default_config(var, value);
int i;
int read_stdin = 1;
int inaccurate_eof = 0;
+ int errs = 0;
const char *whitespace_option = NULL;
int fd;
if (!strcmp(arg, "-")) {
- apply_patch(0, "<stdin>", inaccurate_eof);
+ errs |= apply_patch(0, "<stdin>", inaccurate_eof);
read_stdin = 0;
continue;
}
}
if (!strcmp(arg, "--allow-binary-replacement") ||
!strcmp(arg, "--binary")) {
- allow_binary_replacement = 1;
- continue;
+ continue; /* now no-op */
}
if (!strcmp(arg, "--numstat")) {
apply = 0;
apply_in_reverse = 1;
continue;
}
+ if (!strcmp(arg, "--reject")) {
+ apply = apply_with_reject = apply_verbosely = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--verbose")) {
+ apply_verbosely = 1;
+ continue;
+ }
if (!strcmp(arg, "--inaccurate-eof")) {
inaccurate_eof = 1;
continue;
usage(apply_usage);
read_stdin = 0;
set_default_whitespace_mode(whitespace_option);
- apply_patch(fd, arg, inaccurate_eof);
+ errs |= apply_patch(fd, arg, inaccurate_eof);
close(fd);
}
set_default_whitespace_mode(whitespace_option);
if (read_stdin)
- apply_patch(0, "<stdin>", inaccurate_eof);
+ errs |= apply_patch(0, "<stdin>", inaccurate_eof);
if (whitespace_error) {
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error) {
int squelched =
whitespace_error - squelch_whitespace_errors;
- fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+ fprintf(stderr, "warning: squelched %d "
+ "whitespace error%s\n",
squelched,
squelched == 1 ? "" : "s");
}
die("Unable to write new index file");
}
- return 0;
+ return !!errs;
}