Git development
 help / color / mirror / Atom feed
From: Junio C Hamano <junkio@cox.net>
To: Linus Torvalds <torvalds@osdl.org>
Cc: Git Mailing List <git@vger.kernel.org>
Subject: [PATCH] Diff updates to express type changes
Date: Thu, 26 May 2005 02:24:30 -0700	[thread overview]
Message-ID: <7vu0kqcpbl.fsf@assigned-by-dhcp.cox.net> (raw)

With the introduction of type 'T' in the diff-raw output, and
the "apply-patch" program Linus has been quietly working on
without much advertisement, it started to make sense to emit
usable information in the "diff --git" patch output format as
well.  Earlier built-in diff driver punted and did not say
anything about a symbolic link changing into a file or vice
versa, but this version represents it as a pair of deletion
and creation.

It also fixes a minor problem dealing with old archive created
with ancient git.  The earlier code was reporting file mode
change between 100664 and 100644 (we shouldn't).  The linux-2.6
git tree has a good example that exposes this problem.  A good
test case is commit ce1dc02f76432a46db149241e015a4f782974623.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---

diff.c     |   45 ++++++++++++++++++++++++++++++++++++---------
diffcore.h |    6 ++++++
2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -171,8 +171,8 @@ struct diff_filespec *alloc_filespec(con
 void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
 		   unsigned short mode)
 {
-	if (mode) { /* just playing defensive */
-		spec->mode = mode;
+	if (mode) {
+		spec->mode = DIFF_FILE_CANON_MODE(mode);
 		memcpy(spec->sha1, sha1, 20);
 		spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
 	}
@@ -390,7 +390,8 @@ static void remove_tempfile_on_signal(in
  *               infile2 infile2-sha1 infile2-mode [ rename-to ]
  *
  */
-static void run_external_diff(const char *name,
+static void run_external_diff(const char *pgm,
+			      const char *name,
 			      const char *other,
 			      struct diff_filespec *one,
 			      struct diff_filespec *two,
@@ -418,7 +419,6 @@ static void run_external_diff(const char
 	if (pid < 0)
 		die("unable to fork");
 	if (!pid) {
-		const char *pgm = external_diff();
 		if (pgm) {
 			if (one && two) {
 				const char *exec_arg[10];
@@ -468,6 +468,30 @@ static void run_external_diff(const char
 	remove_tempfile();
 }
 
+static void run_diff(const char *name,
+		     const char *other,
+		     struct diff_filespec *one,
+		     struct diff_filespec *two,
+		     const char *xfrm_msg)
+{
+	const char *pgm = external_diff();
+	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
+		 * needs to be split into deletion and creation.
+		 */
+		struct diff_filespec *null = alloc_filespec(two->path);
+		run_external_diff(NULL, name, other, one, null, xfrm_msg);
+		free(null);
+		null = alloc_filespec(one->path);
+		run_external_diff(NULL, name, other, null, two, xfrm_msg);
+		free(null);
+	}
+	else
+		run_external_diff(pgm, name, other, one, two, xfrm_msg);
+}
+
 void diff_setup(int reverse_diff_)
 {
 	reverse_diff = reverse_diff_;
@@ -553,9 +577,11 @@ int diff_unmodified_pair(struct diff_fil
 	one = p->one;
 	two = p->two;
 
-	/* deletion, addition, mode change and renames are all interesting. */
+	/* deletion, addition, mode or type change
+	 * and rename are all interesting.
+	 */
 	if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
-	    (one->mode != two->mode) ||
+	    DIFF_PAIR_MODE_CHANGED(p) ||
 	    strcmp(one->path, two->path))
 		return 0;
 
@@ -608,9 +634,9 @@ static void diff_flush_patch(struct diff
 	}
 
 	if (DIFF_PAIR_UNMERGED(p))
-		run_external_diff(name, NULL, NULL, NULL, NULL);
+		run_diff(name, NULL, NULL, NULL, NULL);
 	else
-		run_external_diff(name, other, p->one, p->two, msg);
+		run_diff(name, other, p->one, p->two, msg);
 }
 
 int diff_needs_to_stay(struct diff_queue_struct *q, int i,
@@ -775,7 +801,8 @@ void diff_flush(int diff_output_style, i
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		if (p->status == 'X')
+		if ((diff_output_style == DIFF_FORMAT_NO_OUTPUT) ||
+		    (p->status == 'X'))
 			continue;
 		if (p->status == 0)
 			die("internal error in diff-resolve-rename-copy");
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -48,6 +48,12 @@ struct diff_filepair {
 #define DIFF_PAIR_TYPE_CHANGED(p) \
 	((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
 
+#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode)
+
+#define DIFF_FILE_CANON_MODE(mode) \
+	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
+	S_ISLNK(mode) ? S_IFLNK : S_IFDIR)
+
 extern int diff_unmodified_pair(struct diff_filepair *);
 
 struct diff_queue_struct {
------------------------------------------------


                 reply	other threads:[~2005-05-26  9:22 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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

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

  git send-email \
    --in-reply-to=7vu0kqcpbl.fsf@assigned-by-dhcp.cox.net \
    --to=junkio@cox.net \
    --cc=git@vger.kernel.org \
    --cc=torvalds@osdl.org \
    /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.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox