git-apply: guess correct -p<n> value for non-git patches.
authorJunio C Hamano <junkio@cox.net>
Thu, 22 Feb 2007 00:05:56 +0000 (16:05 -0800)
committerJunio C Hamano <junkio@cox.net>
Thu, 22 Feb 2007 00:05:56 +0000 (16:05 -0800)
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 <junkio@cox.net>
builtin-apply.c
t/t4119-apply-config.sh

index 12f00e3..c7d4bdd 100644 (file)
@@ -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")) {
index 32e0d71..55f4673 100755 (executable)
@@ -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?"