git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: Jeff King <peff@peff.net>
Cc: Pixel <pixel@mandriva.com>, git@vger.kernel.org
Subject: [PATCH] diff.c: output correct index lines for a split diff
Date: Mon, 26 Jan 2009 00:33:56 -0800	[thread overview]
Message-ID: <7vhc3m8o0b.fsf_-_@gitster.siamese.dyndns.org> (raw)
In-Reply-To: <7vy6wy8qmm.fsf@gitster.siamese.dyndns.org> (Junio C. Hamano's message of "Sun, 25 Jan 2009 23:37:21 -0800")

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

  reply	other threads:[~2009-01-26  8:35 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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     ` Junio C Hamano [this message]
2009-01-26  9:07       ` [PATCH] diff.c: output correct index lines for a split diff Jeff King
2009-01-28 21:32         ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7vhc3m8o0b.fsf_-_@gitster.siamese.dyndns.org \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    --cc=peff@peff.net \
    --cc=pixel@mandriva.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).