git-apply: guess correct -p<n> 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 <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2007-02-21 16:05:56 -08:00
Родитель 9987d7c58a
Коммит 3e8a5db966
2 изменённых файлов: 111 добавлений и 4 удалений

Просмотреть файл

@ -28,6 +28,7 @@ static int newfd = -1;
static int unidiff_zero; static int unidiff_zero;
static int p_value = 1; static int p_value = 1;
static int p_value_known;
static int check_index; static int check_index;
static int write_index; static int write_index;
static int cached; static int cached;
@ -312,11 +313,54 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
return name; 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 * 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 * 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 * 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. * 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 "--- " */ first += 4; /* skip "--- " */
second += 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)) { if (is_dev_null(first)) {
patch->is_new = 1; patch->is_new = 1;
patch->is_delete = 0; 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)) { if (!strncmp(arg, "-p", 2)) {
p_value = atoi(arg + 2); p_value = atoi(arg + 2);
p_value_known = 1;
continue; continue;
} }
if (!strcmp(arg, "--no-add")) { if (!strcmp(arg, "--no-add")) {

Просмотреть файл

@ -19,7 +19,7 @@ test_expect_success setup '
' '
# Also handcraft GNU diff output; note this has trailing whitespace. # 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:04:24.000000000 -0800
+++ file1+ 2007-02-21 01:07:44.000000000 -0800 +++ file1+ 2007-02-21 01:07:44.000000000 -0800
@@ -1 +1 @@ @@ -1 +1 @@
@ -27,6 +27,12 @@ cat >gpatch.file <<\EOF
+B +B
EOF 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' ' test_expect_success 'apply --whitespace=strip' '
rm -f sub/file1 && rm -f sub/file1 &&
@ -124,7 +130,53 @@ test_expect_success 'same in subdir but with traditional patch input' '
git update-index --refresh && git update-index --refresh &&
cd sub && 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 if grep " " file1
then then
echo "Eh?" echo "Eh?"