From 4130b99571903fb93e4c6e0d6677be7b6b986426 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 May 2005 02:24:30 -0700 Subject: [PATCH] [PATCH] Diff updates to express type changes 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 Signed-off-by: Linus Torvalds --- diff.c | 45 ++++++++++++++++++++++++++++++++++++--------- diffcore.h | 6 ++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/diff.c b/diff.c index dd4b4fffe2..f745cdd6e8 100644 --- a/diff.c +++ b/diff.c @@ -171,8 +171,8 @@ struct diff_filespec *alloc_filespec(const char *path) 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(int signo) * 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 *name, 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 *name, 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_filepair *p) 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_filepair *p) } 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, int resolve_rename_copy) 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 index 092eecce23..ee1955bf38 100644 --- 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 {