@matheusbsbs / matheus-bagliano / commits / c14b9d1e33
Fork 0

Allow git-apply to recount the lines in a hunk (AKA recountdiff)

Sometimes, the easiest way to fix up a patch is to edit it directly, even adding or deleting lines. Now, many people are not as divine as certain benevolent dictators as to update the hunk headers correctly at the first try. So teach the tool to do it for us. [jc: with tests] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jun 27, 2008 at 18:43 UTC c14b9d1e330a7f68ffd0ad7e22d6148c6097c122
3 files changed +101 -43
Documentation/git-apply.txt
+6 -1
@@ -12,7 +12,7 @@ SYNOPSIS
12 'git-apply' [--stat] [--numstat] [--summary] [--check] [--index]
13 [--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
14 [--allow-binary-replacement | --binary] [--reject] [-z]
15 - [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
15 + [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
16 [--whitespace=<nowarn|warn|fix|error|error-all>]
17 [--exclude=PATH] [--verbose] [<patch>...]
18
@@ -177,6 +177,11 @@ behavior:
177 current patch being applied will be printed. This option will cause
178 additional information to be reported.
179
180 +--recount::
181 + Do not trust the line counts in the hunk headers, but infer them
182 + by inspecting the patch (e.g. after editing the patch without
183 + adjusting the hunk headers appropriately).
184 +
185 Configuration
186 -------------
187
builtin-apply.c
+68 -7
@@ -153,6 +153,7 @@ struct patch {
153 unsigned int is_binary:1;
154 unsigned int is_copy:1;
155 unsigned int is_rename:1;
156 + unsigned int recount:1;
157 struct fragment *fragments;
158 char *result;
159 size_t resultsize;
@@ -882,6 +883,56 @@ static int parse_range(const char *line, int len, int offset, const char *expect
883 return offset + ex;
884 }
885
886 +static void recount_diff(char *line, int size, struct fragment *fragment)
887 +{
888 + int oldlines = 0, newlines = 0, ret = 0;
889 +
890 + if (size < 1) {
891 + warning("recount: ignore empty hunk");
892 + return;
893 + }
894 +
895 + for (;;) {
896 + int len = linelen(line, size);
897 + size -= len;
898 + line += len;
899 +
900 + if (size < 1)
901 + break;
902 +
903 + switch (*line) {
904 + case ' ': case '\n':
905 + newlines++;
906 + /* fall through */
907 + case '-':
908 + oldlines++;
909 + continue;
910 + case '+':
911 + newlines++;
912 + continue;
913 + case '\\':
914 + break;
915 + case '@':
916 + ret = size < 3 || prefixcmp(line, "@@ ");
917 + break;
918 + case 'd':
919 + ret = size < 5 || prefixcmp(line, "diff ");
920 + break;
921 + default:
922 + ret = -1;
923 + break;
924 + }
925 + if (ret) {
926 + warning("recount: unexpected line: %.*s",
927 + (int)linelen(line, size), line);
928 + return;
929 + }
930 + break;
931 + }
932 + fragment->oldlines = oldlines;
933 + fragment->newlines = newlines;
934 +}
935 +
936 /*
937 * Parse a unified diff fragment header of the
938 * form "@@ -a,b +c,d @@"
@@ -1013,6 +1064,8 @@ static int parse_fragment(char *line, unsigned long size,
1064 offset = parse_fragment_header(line, len, fragment);
1065 if (offset < 0)
1066 return -1;
1067 + if (offset > 0 && patch->recount)
1068 + recount_diff(line + offset, size - offset, fragment);
1069 oldlines = fragment->oldlines;
1070 newlines = fragment->newlines;
1071 leading = 0;
@@ -2912,7 +2965,10 @@ static void prefix_patches(struct patch *p)
2965 }
2966 }
2967
2915 -static int apply_patch(int fd, const char *filename, int inaccurate_eof)
2968 +#define INACCURATE_EOF (1<<0)
2969 +#define RECOUNT (1<<1)
2970 +
2971 +static int apply_patch(int fd, const char *filename, int options)
2972 {
2973 size_t offset;
2974 struct strbuf buf;
@@ -2928,7 +2984,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
2984 int nr;
2985
2986 patch = xcalloc(1, sizeof(*patch));
2931 - patch->inaccurate_eof = inaccurate_eof;
2987 + patch->inaccurate_eof = !!(options & INACCURATE_EOF);
2988 + patch->recount = !!(options & RECOUNT);
2989 nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
2990 if (nr < 0)
2991 break;
@@ -2997,7 +3054,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3054 {
3055 int i;
3056 int read_stdin = 1;
3000 - int inaccurate_eof = 0;
3057 + int options = 0;
3058 int errs = 0;
3059 int is_not_gitdir;
3060
@@ -3015,7 +3072,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3072 int fd;
3073
3074 if (!strcmp(arg, "-")) {
3018 - errs |= apply_patch(0, "<stdin>", inaccurate_eof);
3075 + errs |= apply_patch(0, "<stdin>", options);
3076 read_stdin = 0;
3077 continue;
3078 }
@@ -3115,7 +3172,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3172 continue;
3173 }
3174 if (!strcmp(arg, "--inaccurate-eof")) {
3118 - inaccurate_eof = 1;
3175 + options |= INACCURATE_EOF;
3176 + continue;
3177 + }
3178 + if (!strcmp(arg, "--recount")) {
3179 + options |= RECOUNT;
3180 continue;
3181 }
3182 if (0 < prefix_length)
@@ -3126,12 +3187,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3187 die("can't open patch '%s': %s", arg, strerror(errno));
3188 read_stdin = 0;
3189 set_default_whitespace_mode(whitespace_option);
3129 - errs |= apply_patch(fd, arg, inaccurate_eof);
3190 + errs |= apply_patch(fd, arg, options);
3191 close(fd);
3192 }
3193 set_default_whitespace_mode(whitespace_option);
3194 if (read_stdin)
3134 - errs |= apply_patch(0, "<stdin>", inaccurate_eof);
3195 + errs |= apply_patch(0, "<stdin>", options);
3196 if (whitespace_error) {
3197 if (squelch_whitespace_errors &&
3198 squelch_whitespace_errors < whitespace_error) {
t/t4100-apply-stat.sh
+27 -35
@@ -3,44 +3,36 @@
3 # Copyright (c) 2005 Junio C Hamano
4 #
5
6 -test_description='git apply --stat --summary test.
6 +test_description='git apply --stat --summary test, with --recount
7
8 '
9 . ./test-lib.sh
10
11 -test_expect_success \
12 - 'rename' \
13 - 'git apply --stat --summary <../t4100/t-apply-1.patch >current &&
14 - test_cmp ../t4100/t-apply-1.expect current'
15 -
16 -test_expect_success \
17 - 'copy' \
18 - 'git apply --stat --summary <../t4100/t-apply-2.patch >current &&
19 - test_cmp ../t4100/t-apply-2.expect current'
20 -
21 -test_expect_success \
22 - 'rewrite' \
23 - 'git apply --stat --summary <../t4100/t-apply-3.patch >current &&
24 - test_cmp ../t4100/t-apply-3.expect current'
25 -
26 -test_expect_success \
27 - 'mode' \
28 - 'git apply --stat --summary <../t4100/t-apply-4.patch >current &&
29 - test_cmp ../t4100/t-apply-4.expect current'
30 -
31 -test_expect_success \
32 - 'non git' \
33 - 'git apply --stat --summary <../t4100/t-apply-5.patch >current &&
34 - test_cmp ../t4100/t-apply-5.expect current'
35 -
36 -test_expect_success \
37 - 'non git' \
38 - 'git apply --stat --summary <../t4100/t-apply-6.patch >current &&
39 - test_cmp ../t4100/t-apply-6.expect current'
40 -
41 -test_expect_success \
42 - 'non git' \
43 - 'git apply --stat --summary <../t4100/t-apply-7.patch >current &&
44 - test_cmp ../t4100/t-apply-7.expect current'
11 +UNC='s/^\(@@ -[1-9][0-9]*\),[0-9]* \(+[1-9][0-9]*\),[0-9]* @@/\1,999 \2,999 @@/'
12 +
13 +num=0
14 +while read title
15 +do
16 + num=$(( $num + 1 ))
17 + test_expect_success "$title" '
18 + git apply --stat --summary \
19 + <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
20 + test_cmp ../t4100/t-apply-$num.expect current
21 + '
22 +
23 + test_expect_success "$title with recount" '
24 + sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
25 + git apply --recount --stat --summary >current &&
26 + test_cmp ../t4100/t-apply-$num.expect current
27 + '
28 +done <<\EOF
29 +rename
30 +copy
31 +rewrite
32 +mode
33 +non git (1)
34 +non git (2)
35 +non git (3)
36 +EOF
37
38 test_done