* bug: transform a binary file into a symlink in one commit => invalid binary patch @ 2009-01-23 12:25 Pixel 2009-01-23 13:13 ` Michael J Gruber 2009-01-26 0:35 ` Jeff King 0 siblings, 2 replies; 7+ messages in thread From: Pixel @ 2009-01-23 12:25 UTC (permalink / raw) To: git hi, i hit a bug (git 1.6.1): when you transform a binary file into a symlink in one commit, the binary patch can't be used in "git apply". Is it a known issue? reproducer: ---------- #!/bin/sh -e # the bug: if you transform a binary file into a symlink in one commit, # the binary patch can't be used in "git apply" # # nb: if you uncomment the "##" lines, the problem disappears # since the first patch will empty the binary file then the non-binary file # will be transformed into a symlink rm -rf t t2 mkdir t dd if=/dev/urandom of=t/a count=10 cp -r t t2 cd t git init git add . git commit -m initial ##echo -n > a ##git commit -a -m foo ln -sf zzz a git commit -a -m foo2 git format-patch :/initial cd ../t2 git apply ../t/*.patch cd .. rm -rf t t2 ---------- ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: bug: transform a binary file into a symlink in one commit => invalid binary patch 2009-01-23 12:25 bug: transform a binary file into a symlink in one commit => invalid binary patch Pixel @ 2009-01-23 13:13 ` Michael J Gruber 2009-01-26 0:35 ` Jeff King 1 sibling, 0 replies; 7+ messages in thread From: Michael J Gruber @ 2009-01-23 13:13 UTC (permalink / raw) To: Pixel; +Cc: git Pixel venit, vidit, dixit 23.01.2009 13:25: > hi, > > i hit a bug (git 1.6.1): when you transform a binary file into a > symlink in one commit, the binary patch can't be used in "git apply". > Is it a known issue? > > > reproducer: > > ---------- > #!/bin/sh -e > > # the bug: if you transform a binary file into a symlink in one commit, > # the binary patch can't be used in "git apply" > # > # nb: if you uncomment the "##" lines, the problem disappears > # since the first patch will empty the binary file then the non-binary file > # will be transformed into a symlink > > rm -rf t t2 > mkdir t > dd if=/dev/urandom of=t/a count=10 > cp -r t t2 > > cd t > git init > git add . > git commit -m initial > > ##echo -n > a > ##git commit -a -m foo > ln -sf zzz a > git commit -a -m foo2 > git format-patch :/initial > > cd ../t2 > git apply ../t/*.patch > > cd .. > rm -rf t t2 > ---------- You're not even initialising a repo in t2. I assume you want to "cp -a t t2" after the "initial" commit, don't you? Doing that I get error: binary patch to 'a' creates incorrect result (expecting e132db255c0e0e3e9309c3c58a0a9c9eb97dd942, got e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) error: a: patch does not apply error: a: already exists in working directory which is not that magnificient either. If that's the error you're after your test script needs to be modified. Michael ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: bug: transform a binary file into a symlink in one commit => invalid binary patch 2009-01-23 12:25 bug: transform a binary file into a symlink in one commit => invalid binary patch Pixel 2009-01-23 13:13 ` Michael J Gruber @ 2009-01-26 0:35 ` Jeff King 2009-01-26 7:37 ` Junio C Hamano 1 sibling, 1 reply; 7+ messages in thread From: Jeff King @ 2009-01-26 0:35 UTC (permalink / raw) To: Pixel; +Cc: git On Fri, Jan 23, 2009 at 01:25:30PM +0100, Pixel wrote: > i hit a bug (git 1.6.1): when you transform a binary file into a > symlink in one commit, the binary patch can't be used in "git apply". > Is it a known issue? Not that I know of. Below is a patch against the test suite that fairly neatly displays the problem. I didn't get a chance to look into actually fixing it, though (I'm not even sure the problem is in apply, and not in the generated patch). --- diff --git a/t/t4130-apply-symlink-binary.sh b/t/t4130-apply-symlink-binary.sh new file mode 100755 index 0000000..0ee2ba1 --- /dev/null +++ b/t/t4130-apply-symlink-binary.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='apply handles binary to symlink conversion' +. ./test-lib.sh + +test_expect_success 'create commit with binary' ' + echo content >file && git add file && + printf "\0" > binary && git add binary && + git commit -m one +' + +test_expect_success 'convert binary to symlink' ' + rm binary && + ln -s file binary && + git add binary && + git commit -m two +' + +test_expect_success 'create patch' ' + git diff-tree --binary HEAD^ HEAD >patch +' + +test_expect_success 'apply patch' ' + git reset --hard HEAD^ && + git apply patch && + test -h binary && + test_cmp binary file +' + +test_done ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: bug: transform a binary file into a symlink in one commit => invalid binary patch 2009-01-26 0:35 ` Jeff King @ 2009-01-26 7:37 ` Junio C Hamano 2009-01-26 8:33 ` [PATCH] diff.c: output correct index lines for a split diff Junio C Hamano 0 siblings, 1 reply; 7+ messages in thread From: Junio C Hamano @ 2009-01-26 7:37 UTC (permalink / raw) To: Jeff King; +Cc: Pixel, git Jeff King <peff@peff.net> writes: > On Fri, Jan 23, 2009 at 01:25:30PM +0100, Pixel wrote: > >> i hit a bug (git 1.6.1): when you transform a binary file into a >> symlink in one commit, the binary patch can't be used in "git apply". >> Is it a known issue? > > Not that I know of. > > Below is a patch against the test suite that fairly neatly displays the > problem. I didn't get a chance to look into actually fixing it, though > (I'm not even sure the problem is in apply, and not in the generated > patch). The generated diff is wrong. A filepair that changes type must be split into deletion followed by creation, which means the "index" line should say 0{40} on the right hand side for the first half and then 0{40} on the left hand side for the second half. The patch generated by this step: > +test_expect_success 'create patch' ' > + git diff-tree --binary HEAD^ HEAD >patch > +' However says the blob contents change from "\0" to "file" on both. This is because diff.c::run_diff() computes "index" only once and reuses it for both halves. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH] diff.c: output correct index lines for a split diff 2009-01-26 7:37 ` Junio C Hamano @ 2009-01-26 8:33 ` Junio C Hamano 2009-01-26 9:07 ` Jeff King 0 siblings, 1 reply; 7+ messages in thread From: Junio C Hamano @ 2009-01-26 8:33 UTC (permalink / raw) To: Jeff King; +Cc: Pixel, git A patch that changes the filetype (e.g. regular file to symlink) of a path must be split into a deletion event followed by a creation event, which means that we need to have two independent metainfo lines for each. However, the code reused the single set of metainfo lines. As the blob object names recorded on the index lines are usually not used nor validated on the receiving end, this is not an issue with normal use of the resulting patch. However, when accepting a binary patch to delete a blob, git-apply verified that the postimage blob object name on the index line is 0{40}, hence a patch that deletes a regular file blob that records binary contents to create a blob with different filetype (e.g. a symbolic link) failed to apply. "git am -3" also uses the blob object names recorded on the index line, so it would also misbehave when synthesizing a preimage tree. This moves the code to generate metainfo lines around, so that two independent sets of metainfo lines are used for the split halves. The fix revealed that one test expected the incorrect behaviour, which is also fixed here. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Junio C Hamano <gitster@pobox.com> writes: > Jeff King <peff@peff.net> writes: > ... >> Below is a patch against the test suite that fairly neatly displays the >> problem. I didn't get a chance to look into actually fixing it, though >> (I'm not even sure the problem is in apply, and not in the generated >> patch). > > The generated diff is wrong. > > A filepair that changes type must be split into deletion followed by > creation, which means the "index" line should say 0{40} on the right hand > side for the first half and then 0{40} on the left hand side for the > second half. The patch generated by this step: > >> +test_expect_success 'create patch' ' >> + git diff-tree --binary HEAD^ HEAD >patch >> +' > > However says the blob contents change from "\0" to "file" on both. > > This is because diff.c::run_diff() computes "index" only once and reuses > it for both halves. I did not include your new test script here; perhaps we can add it to an existing typechange diff/apply test, like t4114? diff.c | 146 +++++++++++++++++++++++++--------------------- t/t4030-diff-textconv.sh | 2 +- 2 files changed, 81 insertions(+), 67 deletions(-) diff --git a/diff.c b/diff.c index 972b3da..7e18364 100644 --- a/diff.c +++ b/diff.c @@ -2081,16 +2081,86 @@ static void run_external_diff(const char *pgm, } } +static int similarity_index(struct diff_filepair *p) +{ + return p->score * 100 / MAX_SCORE; +} + +static void fill_metainfo(struct strbuf *msg, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + struct diff_options *o, + struct diff_filepair *p) +{ + strbuf_init(msg, PATH_MAX * 2 + 300); + switch (p->status) { + case DIFF_STATUS_COPIED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\ncopy from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\ncopy to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_RENAMED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\nrename from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\nrename to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_MODIFIED: + if (p->score) { + strbuf_addf(msg, "dissimilarity index %d%%\n", + similarity_index(p)); + break; + } + /* fallthru */ + default: + /* nothing */ + ; + } + if (one && two && hashcmp(one->sha1, two->sha1)) { + int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; + + if (DIFF_OPT_TST(o, BINARY)) { + mmfile_t mf; + if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || + (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) + abbrev = 40; + } + strbuf_addf(msg, "index %.*s..%.*s", + abbrev, sha1_to_hex(one->sha1), + abbrev, sha1_to_hex(two->sha1)); + if (one->mode == two->mode) + strbuf_addf(msg, " %06o", one->mode); + strbuf_addch(msg, '\n'); + } + if (msg->len) + strbuf_setlen(msg, msg->len - 1); +} + static void run_diff_cmd(const char *pgm, const char *name, const char *other, const char *attr_path, struct diff_filespec *one, struct diff_filespec *two, - const char *xfrm_msg, + struct strbuf *msg, struct diff_options *o, - int complete_rewrite) + struct diff_filepair *p) { + const char *xfrm_msg = NULL; + int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score; + + if (msg) { + fill_metainfo(msg, name, other, one, two, o, p); + xfrm_msg = msg->len ? msg->buf : NULL; + } + if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { @@ -2130,11 +2200,6 @@ static void diff_fill_sha1_info(struct diff_filespec *one) hashclr(one->sha1); } -static int similarity_index(struct diff_filepair *p) -{ - return p->score * 100 / MAX_SCORE; -} - static void strip_prefix(int prefix_length, const char **namep, const char **otherp) { /* Strip the prefix but do not molest /dev/null and absolute paths */ @@ -2148,13 +2213,11 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); struct strbuf msg; - char *xfrm_msg; struct diff_filespec *one = p->one; struct diff_filespec *two = p->two; const char *name; const char *other; const char *attr_path; - int complete_rewrite = 0; name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); @@ -2164,83 +2227,34 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (DIFF_PAIR_UNMERGED(p)) { run_diff_cmd(pgm, name, NULL, attr_path, - NULL, NULL, NULL, o, 0); + NULL, NULL, NULL, o, p); return; } diff_fill_sha1_info(one); diff_fill_sha1_info(two); - strbuf_init(&msg, PATH_MAX * 2 + 300); - switch (p->status) { - case DIFF_STATUS_COPIED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\ncopy from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\ncopy to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_RENAMED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\nrename from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\nrename to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_MODIFIED: - if (p->score) { - strbuf_addf(&msg, "dissimilarity index %d%%\n", - similarity_index(p)); - complete_rewrite = 1; - break; - } - /* fallthru */ - default: - /* nothing */ - ; - } - - if (hashcmp(one->sha1, two->sha1)) { - int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; - - if (DIFF_OPT_TST(o, BINARY)) { - mmfile_t mf; - if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || - (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) - abbrev = 40; - } - strbuf_addf(&msg, "index %.*s..%.*s", - abbrev, sha1_to_hex(one->sha1), - abbrev, sha1_to_hex(two->sha1)); - if (one->mode == two->mode) - strbuf_addf(&msg, " %06o", one->mode); - strbuf_addch(&msg, '\n'); - } - - if (msg.len) - strbuf_setlen(&msg, msg.len - 1); - xfrm_msg = msg.len ? msg.buf : NULL; - if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && (S_IFMT & one->mode) != (S_IFMT & two->mode)) { - /* a filepair that changes between file and symlink + /* + * a filepair that changes between file and symlink * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); run_diff_cmd(NULL, name, other, attr_path, - one, null, xfrm_msg, o, 0); + one, null, &msg, o, p); free(null); + strbuf_release(&msg); + null = alloc_filespec(one->path); run_diff_cmd(NULL, name, other, attr_path, - null, two, xfrm_msg, o, 0); + null, two, &msg, o, p); free(null); } else run_diff_cmd(pgm, name, other, attr_path, - one, two, xfrm_msg, o, complete_rewrite); + one, two, &msg, o, p); strbuf_release(&msg); } diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 2f27a0b..a3f0897 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -104,7 +104,7 @@ cat >expect.typechange <<'EOF' -1 diff --git a/file b/file new file mode 120000 -index ad8b3d2..67be421 +index 0000000..67be421 --- /dev/null +++ b/file @@ -0,0 +1 @@ -- 1.6.1.1.248.g7f6d2 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] diff.c: output correct index lines for a split diff 2009-01-26 8:33 ` [PATCH] diff.c: output correct index lines for a split diff Junio C Hamano @ 2009-01-26 9:07 ` Jeff King 2009-01-28 21:32 ` Junio C Hamano 0 siblings, 1 reply; 7+ messages in thread From: Jeff King @ 2009-01-26 9:07 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pixel, git On Mon, Jan 26, 2009 at 12:33:56AM -0800, Junio C Hamano wrote: > This moves the code to generate metainfo lines around, so that two > independent sets of metainfo lines are used for the split halves. The patch and the generated output look correct to me, so Acked-by: Jeff King <peff@peff.net> > I did not include your new test script here; perhaps we can add it to an > existing typechange diff/apply test, like t4114? I think it makes sense to add to t4114. Please squash in the test below. One think to note, though: test 8 (binary -> symlink) shows breakage with the current master, but test 9 (symlink -> binary) does not. However, if you run the test under "-d" you can see that the diff has bogus metainfo. It's just that "apply" doesn't care. Your test fixes the test failure, and I verified manually that the diff output is sensible. I don't think an additional test is worth it, but we could add one that explicitly checks the metainfo if you want to be extra paranoid. --- diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh index 5533492..0f185ca 100755 --- a/t/t4114-apply-typechange.sh +++ b/t/t4114-apply-typechange.sh @@ -25,6 +25,10 @@ test_expect_success 'setup repository and commits' ' git update-index foo && git commit -m "foo back to file" && git branch foo-back-to-file && + printf "\0" > foo && + git update-index foo && + git commit -m "foo becomes binary" && + git branch foo-becomes-binary && rm -f foo && git update-index --remove foo && mkdir foo && @@ -85,6 +89,20 @@ test_expect_success 'symlink becomes file' ' ' test_debug 'cat patch' +test_expect_success 'binary file becomes symlink' ' + git checkout -f foo-becomes-binary && + git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch && + git apply --index < patch + ' +test_debug 'cat patch' + +test_expect_success 'symlink becomes binary file' ' + git checkout -f foo-symlinked-to-bar && + git diff-tree -p --binary HEAD foo-becomes-binary > patch && + git apply --index < patch + ' +test_debug 'cat patch' + test_expect_success 'symlink becomes directory' ' git checkout -f foo-symlinked-to-bar && ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] diff.c: output correct index lines for a split diff 2009-01-26 9:07 ` Jeff King @ 2009-01-28 21:32 ` Junio C Hamano 0 siblings, 0 replies; 7+ messages in thread From: Junio C Hamano @ 2009-01-28 21:32 UTC (permalink / raw) To: Jeff King; +Cc: Pixel, git Jeff King <peff@peff.net> writes: > On Mon, Jan 26, 2009 at 12:33:56AM -0800, Junio C Hamano wrote: > >> This moves the code to generate metainfo lines around, so that two >> independent sets of metainfo lines are used for the split halves. > > The patch and the generated output look correct to me, so > > Acked-by: Jeff King <peff@peff.net> > >> I did not include your new test script here; perhaps we can add it to an >> existing typechange diff/apply test, like t4114? > > I think it makes sense to add to t4114. Please squash in the test below. Thanks. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-01-28 21:34 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-01-23 12:25 bug: transform a binary file into a symlink in one commit => invalid binary patch Pixel 2009-01-23 13:13 ` Michael J Gruber 2009-01-26 0:35 ` Jeff King 2009-01-26 7:37 ` Junio C Hamano 2009-01-26 8:33 ` [PATCH] diff.c: output correct index lines for a split diff Junio C Hamano 2009-01-26 9:07 ` Jeff King 2009-01-28 21:32 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).