bug: when applying binary diffs, -p0 is ignored

2012-08-24 Thread Colin McCabe
I found a bug in git's handling of binary diff segments.

When applying binary diffs using -p0, the prefix (or --strip) argument
is ignored.

For example, try this:
git apply -p0 --binary 'EOF'
diff --git a/init.tar.gz a/init.tar.gz
new file mode 100644
index ..
 386b94f511a17a8a3d62eb6cec14694cb9b9b51d
GIT binary patch
literal 118
zcmb2|=3qGT-8_JS`RzGFp(Y0bmIKvX{ySXzs`-OhSfm*K#a}SG%CY!eN_LjnjGuP-
zFHV7m)|X1{OzyyaqIde8O2kg^q0P?b0-p;muDS-IC{I=+*S-L~Mn{_^zwEo=Tu
UurRDep|-nAGgFaXfQAU0L+LoA^-pY

literal 0
HcmV?d1

EOF

It will create init.tar.gz, but in the root of the repository, not in a/

(Sorry if my mailer wraps the long index line)

cheers,
Colin McCabe
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: bug: when applying binary diffs, -p0 is ignored

2012-08-24 Thread Junio C Hamano
Colin McCabe cmcc...@alumni.cmu.edu writes:

 I found a bug in git's handling of binary diff segments.

 When applying binary diffs using -p0, the prefix (or --strip) argument
 is ignored.

Thanks for a report.  An ancient bug well spotted.

Perhaps this will help.

-- 8 --
Subject: apply: compute patch-def_name correctly under -p0

Back when git apply was written, we made sure that the user can
skip more than the default number of path components (i.e. 1) by
giving -pn, but the logic for doing so was built around the
notion of we skip N slashes and stop.  This obviously does not
work well when running under -p0 where we do not want to skip any,
but still want to skip SP/HT that separates the pathnames of
preimage and postimage and want to reject absolute pathnames.

Stop using stop_at_slash(), and instead introduce a new helper
skip_tree_prefix() with similar logic but works correctly even for
the -p0 case.

This is an ancient bug, but has been masked for a long time because
most of the patches are text and have other clues to tell us the
name of the preimage and the postimage.

Signed-off-by: Junio C Hamano gits...@pobox.com
---
 builtin/apply.c | 55 ++-
 1 file changed, 30 insertions(+), 25 deletions(-)

diff --git i/builtin/apply.c w/builtin/apply.c
index 3bf71dc..4a511b3 100644
--- i/builtin/apply.c
+++ w/builtin/apply.c
@@ -1095,15 +1095,23 @@ static int gitdiff_unrecognized(const char *line, 
struct patch *patch)
return -1;
 }
 
-static const char *stop_at_slash(const char *line, int llen)
+/*
+ * Skip p_value leading components from line; as we do not accept
+ * absolute paths, return NULL in that case.
+ */
+static const char *skip_tree_prefix(const char *line, int llen)
 {
-   int nslash = p_value;
+   int nslash;
int i;
 
+   if (!p_value)
+   return (llen  line[0] == '/') ? NULL : line;
+
+   nslash = p_value;
for (i = 0; i  llen; i++) {
int ch = line[i];
if (ch == '/'  --nslash = 0)
-   return line[i];
+   return (i == 0) ? NULL : line[i + 1];
}
return NULL;
 }
@@ -1133,12 +1141,11 @@ static char *git_header_name(const char *line, int llen)
if (unquote_c_style(first, line, second))
goto free_and_fail1;
 
-   /* advance to the first slash */
-   cp = stop_at_slash(first.buf, first.len);
-   /* we do not accept absolute paths */
-   if (!cp || cp == first.buf)
+   /* strip the a/b prefix including trailing slash */
+   cp = skip_tree_prefix(first.buf, first.len);
+   if (!cp)
goto free_and_fail1;
-   strbuf_remove(first, 0, cp + 1 - first.buf);
+   strbuf_remove(first, 0, cp - first.buf);
 
/*
 * second points at one past closing dq of name.
@@ -1152,22 +1159,21 @@ static char *git_header_name(const char *line, int llen)
if (*second == '') {
if (unquote_c_style(sp, second, NULL))
goto free_and_fail1;
-   cp = stop_at_slash(sp.buf, sp.len);
-   if (!cp || cp == sp.buf)
+   cp = skip_tree_prefix(sp.buf, sp.len);
+   if (!cp)
goto free_and_fail1;
/* They must match, otherwise ignore */
-   if (strcmp(cp + 1, first.buf))
+   if (strcmp(cp, first.buf))
goto free_and_fail1;
strbuf_release(sp);
return strbuf_detach(first, NULL);
}
 
/* unquoted second */
-   cp = stop_at_slash(second, line + llen - second);
-   if (!cp || cp == second)
+   cp = skip_tree_prefix(second, line + llen - second);
+   if (!cp)
goto free_and_fail1;
-   cp++;
-   if (line + llen - cp != first.len + 1 ||
+   if (line + llen - cp != first.len ||
memcmp(first.buf, cp, first.len))
goto free_and_fail1;
return strbuf_detach(first, NULL);
@@ -1179,10 +1185,9 @@ static char *git_header_name(const char *line, int llen)
}
 
/* unquoted first name */
-   name = stop_at_slash(line, llen);
-   if (!name || name == line)
+   name = skip_tree_prefix(line, llen);
+   if (!name)
return NULL;
-   name++;
 
/*
 * since the first name is unquoted, a dq if exists must be
@@ -1196,10 +1201,9 @@ static char *git_header_name(const char *line, int llen)
if (unquote_c_style(sp, second, NULL))
goto free_and_fail2;
 
-