From 3e8a5db966c26a0a986161103d59683b909a6c78 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Feb 2007 16:05:56 -0800 Subject: [PATCH] git-apply: guess correct -p value for non-git patches. This enhances the third point in the previous commit. When applying a non-git patch that begins like this: --- 2.6.orig/mm/slab.c +++ 2.6/mm/slab.c @@ -N,M +L,K @@@ ... and if you are in 'mm' subdirectory, we notice that -p2 is the right option to use to apply the patch in file slab.c in the current directory (i.e. mm/slab.c) The guess function also knows about this pattern, where you would need to use -p0 if applying from the top-level: --- mm/slab.c +++ mm/slab.c @@ -N,M +L,K @@@ ... Signed-off-by: Junio C Hamano --- builtin-apply.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++-- t/t4119-apply-config.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 12f00e38db..c7d4bdd474 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -28,6 +28,7 @@ static int newfd = -1; static int unidiff_zero; static int p_value = 1; +static int p_value_known; static int check_index; static int write_index; static int cached; @@ -312,11 +313,54 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) return name; } +static int count_slashes(const char *cp) +{ + int cnt = 0; + char ch; + + while ((ch = *cp++)) + if (ch == '/') + cnt++; + return cnt; +} + +/* + * Given the string after "--- " or "+++ ", guess the appropriate + * p_value for the given patch. + */ +static int guess_p_value(const char *nameline) +{ + char *name, *cp; + int val = -1; + + if (is_dev_null(nameline)) + return -1; + name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB); + if (!name) + return -1; + cp = strchr(name, '/'); + if (!cp) + val = 0; + else if (prefix) { + /* + * Does it begin with "a/$our-prefix" and such? Then this is + * very likely to apply to our directory. + */ + if (!strncmp(name, prefix, prefix_length)) + val = count_slashes(prefix); + else { + cp++; + if (!strncmp(cp, prefix, prefix_length)) + val = count_slashes(prefix) + 1; + } + } + free(name); + return val; +} + /* * Get the name etc info from the --/+++ lines of a traditional patch header * - * NOTE! This hardcodes "-p1" behaviour in filename detection. - * * FIXME! The end-of-filename heuristics are kind of screwy. For existing * files, we can happily check the index for a match, but for creating a * new file we should try to match whatever "patch" does. I have no idea. @@ -327,6 +371,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc first += 4; /* skip "--- " */ second += 4; /* skip "+++ " */ + if (!p_value_known) { + int p, q; + p = guess_p_value(first); + q = guess_p_value(second); + if (p < 0) p = q; + if (0 <= p && p == q) { + p_value = p; + p_value_known = 1; + } + } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; @@ -2656,6 +2710,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) } if (!strncmp(arg, "-p", 2)) { p_value = atoi(arg + 2); + p_value_known = 1; continue; } if (!strcmp(arg, "--no-add")) { diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh index 32e0d7172e..55f46737c6 100755 --- a/t/t4119-apply-config.sh +++ b/t/t4119-apply-config.sh @@ -19,7 +19,7 @@ test_expect_success setup ' ' # Also handcraft GNU diff output; note this has trailing whitespace. -cat >gpatch.file <<\EOF +cat >gpatch.file <<\EOF && --- file1 2007-02-21 01:04:24.000000000 -0800 +++ file1+ 2007-02-21 01:07:44.000000000 -0800 @@ -1 +1 @@ @@ -27,6 +27,12 @@ cat >gpatch.file <<\EOF +B EOF +sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file && +sed -e ' + /^--- /s|file1|a/sub/&| + /^+++ /s|file1|b/sub/&| +' gpatch.file >gpatch-ab-sub.file && + test_expect_success 'apply --whitespace=strip' ' rm -f sub/file1 && @@ -124,7 +130,53 @@ test_expect_success 'same in subdir but with traditional patch input' ' git update-index --refresh && cd sub && - git apply -p0 ../gpatch.file && + git apply ../gpatch.file && + if grep " " file1 + then + echo "Eh?" + false + elif grep B file1 + then + echo Happy + else + echo "Huh?" + false + fi +' + +test_expect_success 'same but with traditional patch input of depth 1' ' + + cd "$D" && + git config apply.whitespace strip && + rm -f sub/file1 && + cp saved sub/file1 && + git update-index --refresh && + + cd sub && + git apply ../gpatch-sub.file && + if grep " " file1 + then + echo "Eh?" + false + elif grep B file1 + then + echo Happy + else + echo "Huh?" + false + fi +' + +test_expect_success 'same but with traditional patch input of depth 2' ' + + cd "$D" && + git config apply.whitespace strip && + rm -f sub/file1 && + cp saved sub/file1 && + git update-index --refresh && + + cd sub && + git apply ../gpatch-ab-sub.file && if grep " " file1 then echo "Eh?" -- 2.11.4.GIT