Skip to content

Commit

Permalink
Merge branch 'jc/apply'
Browse files Browse the repository at this point in the history
* jc/apply:
  git-apply --reject: finishing touches.
  apply --reject: count hunks starting from 1, not 0
  git-apply --verbose
  git-apply --reject: send rejects to .rej files.
  git-apply --reject
  apply --reverse: tie it all together.
  diff.c: make binary patch reversible.
  builtin-apply --reverse: two bugfixes.
  • Loading branch information
Junio C Hamano committed Aug 28, 2006
2 parents 2e61838 + 8938045 commit b32d37a
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Documentation/git-apply.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ OPTIONS
For atomicity, gitlink:git-apply[1] by default fails the whole patch and
does not touch the working tree when some of the hunks
do not apply. This option makes it apply
the parts of the patch that are applicable, and send the
rejected hunks to the standard output of the command.
the parts of the patch that are applicable, and leave the
rejected hunks in corresponding *.rej files.

-z::
When showing the index information, do not munge paths,
Expand Down
178 changes: 153 additions & 25 deletions builtin-apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ static int summary;
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,
Expand Down Expand Up @@ -122,6 +124,7 @@ struct fragment {
unsigned long newpos, newlines;
const char *patch;
int size;
int rejected;
struct fragment *next;
};

Expand All @@ -138,6 +141,7 @@ struct patch {
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;
Expand All @@ -150,6 +154,24 @@ struct patch {
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)

Expand Down Expand Up @@ -1061,8 +1083,12 @@ static struct fragment *parse_binary_hunk(char **buf_p,
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.
*/
Expand Down Expand Up @@ -1542,7 +1568,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
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)
Expand All @@ -1555,8 +1582,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
/* 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) {
Expand All @@ -1566,7 +1595,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
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;

Expand Down Expand Up @@ -1616,7 +1647,7 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
"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) {
Expand Down Expand Up @@ -1715,7 +1746,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
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;
Expand All @@ -1730,9 +1761,12 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
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;
Expand Down Expand Up @@ -1768,8 +1802,9 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
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)
Expand All @@ -1794,6 +1829,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
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;
Expand Down Expand Up @@ -1899,6 +1935,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)

if (apply_data(patch, &st, ce) < 0)
return error("%s: patch does not apply", name);
patch->rejected = 0;
return 0;
}

Expand All @@ -1908,6 +1945,9 @@ static int check_patch_list(struct patch *patch)
int err = 0;

for (prev_patch = NULL; patch ; patch = patch->next) {
if (apply_verbosely)
say_patch_name(stderr,
"Checking patch ", patch, "...\n");
err |= check_patch(patch, prev_patch);
prev_patch = patch;
}
Expand Down Expand Up @@ -2217,23 +2257,99 @@ static void write_out_one_result(struct patch *patch, int phase)
if (phase == 0)
remove_file(patch);
if (phase == 1)
create_file(patch);
create_file(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 void write_out_results(struct patch *list, int skipped_patch)
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;
Expand Down Expand Up @@ -2308,11 +2424,13 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
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);
Expand Down Expand Up @@ -2345,6 +2463,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
int i;
int read_stdin = 1;
int inaccurate_eof = 0;
int errs = 0;

const char *whitespace_option = NULL;

Expand All @@ -2354,7 +2473,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
int fd;

if (!strcmp(arg, "-")) {
apply_patch(0, "<stdin>", inaccurate_eof);
errs |= apply_patch(0, "<stdin>", inaccurate_eof);
read_stdin = 0;
continue;
}
Expand Down Expand Up @@ -2435,6 +2554,14 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
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;
Expand All @@ -2455,18 +2582,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
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");
}
Expand Down Expand Up @@ -2494,5 +2622,5 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
die("Unable to write new index file");
}

return 0;
return !!errs;
}
Loading

0 comments on commit b32d37a

Please sign in to comment.