提交 b32d37a3 编写于 作者: J Junio C Hamano

Merge branch 'jc/apply'

* 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.
......@@ -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,
......
......@@ -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,
......@@ -122,6 +124,7 @@ struct fragment {
unsigned long newpos, newlines;
const char *patch;
int size;
int rejected;
struct fragment *next;
};
......@@ -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;
......@@ -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)
......@@ -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.
*/
......@@ -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)
......@@ -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) {
......@@ -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;
......@@ -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) {
......@@ -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;
......@@ -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;
......@@ -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)
......@@ -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;
......@@ -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;
}
......@@ -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;
}
......@@ -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;
......@@ -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);
......@@ -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;
......@@ -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;
}
......@@ -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;
......@@ -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");
}
......@@ -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;
}
......@@ -838,7 +838,7 @@ static unsigned char *deflate_it(char *data,
return deflated;
}
static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
{
void *cp;
void *delta;
......@@ -849,7 +849,6 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
unsigned long deflate_size;
unsigned long data_size;
printf("GIT binary patch\n");
/* We could do deflated delta, or we could do just deflated two,
* whichever is smaller.
*/
......@@ -898,6 +897,13 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
free(data);
}
static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
{
printf("GIT binary patch\n");
emit_binary_diff_body(one, two);
emit_binary_diff_body(two, one);
}
#define FIRST_FEW_BYTES 8000
static int mmfile_is_binary(mmfile_t *mf)
{
......
......@@ -22,25 +22,64 @@ test_expect_success setup '
tr "[mon]" '\''[\0\1\2]'\'' <file1 >file2 &&
git commit -a -m second &&
git tag second &&
git diff --binary -R initial >patch
git diff --binary initial second >patch
'
test_expect_success 'apply in forward' '
T0=`git rev-parse "second^{tree}"` &&
git reset --hard initial &&
git apply --index --binary patch &&
git diff initial >diff &&
diff -u /dev/null diff
T1=`git write-tree` &&
test "$T0" = "$T1"
'
test_expect_success 'apply in reverse' '
git reset --hard second &&
git apply --reverse --binary --index patch &&
git diff >diff &&
diff -u /dev/null diff
'
test_expect_success 'setup separate repository lacking postimage' '
git tar-tree initial initial | tar xf - &&
(
cd initial && git init-db && git add .
) &&
git tar-tree second second | tar xf - &&
(
cd second && git init-db && git add .
)
'
test_expect_success 'apply in forward without postimage' '
T0=`git rev-parse "second^{tree}"` &&
(
cd initial &&
git apply --index --binary ../patch &&
T1=`git write-tree` &&
test "$T0" = "$T1"
)
'
test_expect_success 'apply in reverse without postimage' '
T0=`git rev-parse "initial^{tree}"` &&
(
cd second &&
git apply --index --binary --reverse ../patch &&
T1=`git write-tree` &&
test "$T0" = "$T1"
)
'
test_done
#!/bin/sh
#
# Copyright (c) 2005 Junio C Hamano
#
test_description='git-apply with rejects
'
. ./test-lib.sh
test_expect_success setup '
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
do
echo $i
done >file1 &&
cat file1 >saved.file1 &&
git update-index --add file1 &&
git commit -m initial &&
for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
do
echo $i
done >file1 &&
git diff >patch.1 &&
cat file1 >clean &&
for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
do
echo $i
done >expected &&
mv file1 file2 &&
git update-index --add --remove file1 file2 &&
git diff -M HEAD >patch.2 &&
rm -f file1 file2 &&
mv saved.file1 file1 &&
git update-index --add --remove file1 file2 &&
for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
do
echo $i
done >file1 &&
cat file1 >saved.file1
'
test_expect_success 'apply without --reject should fail' '
if git apply patch.1
then
echo "Eh? Why?"
exit 1
fi
diff -u file1 saved.file1
'
test_expect_success 'apply without --reject should fail' '
if git apply --verbose patch.1
then
echo "Eh? Why?"
exit 1
fi
diff -u file1 saved.file1
'
test_expect_success 'apply with --reject should fail but update the file' '
cat saved.file1 >file1 &&
rm -f file1.rej file2.rej &&
if git apply --reject patch.1
then
echo "succeeds with --reject?"
exit 1
fi
diff -u file1 expected &&
cat file1.rej &&
if test -f file2.rej
then
echo "file2 should not have been touched"
exit 1
fi
'
test_expect_success 'apply with --reject should fail but update the file' '
cat saved.file1 >file1 &&
rm -f file1.rej file2.rej file2 &&
if git apply --reject patch.2 >rejects
then
echo "succeeds with --reject?"
exit 1
fi
test -f file1 && {
echo "file1 still exists?"
exit 1
}
diff -u file2 expected &&
cat file2.rej &&
if test -f file1.rej
then
echo "file2 should not have been touched"
exit 1
fi
'
test_expect_success 'the same test with --verbose' '
cat saved.file1 >file1 &&
rm -f file1.rej file2.rej file2 &&
if git apply --reject --verbose patch.2 >rejects
then
echo "succeeds with --reject?"
exit 1
fi
test -f file1 && {
echo "file1 still exists?"
exit 1
}
diff -u file2 expected &&
cat file2.rej &&
if test -f file1.rej
then
echo "file2 should not have been touched"
exit 1
fi
'
test_expect_success 'apply cleanly with --verbose' '
git cat-file -p HEAD:file1 >file1 &&
rm -f file?.rej file2 &&
git apply --verbose patch.1 &&
diff -u file1 clean
'
test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册