summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <junkio@cox.net>2005-05-26 02:24:30 -0700
committerLibravatar Linus Torvalds <torvalds@ppc970.osdl.org>2005-05-26 15:18:55 -0700
commit4130b99571903fb93e4c6e0d6677be7b6b986426 (patch)
tree4f330570b0cbf3edceaef05ca97e4d255afd48e4
parentgit-apply: add "--check" option to check that the diff makes sense (diff)
downloadtgif-4130b99571903fb93e4c6e0d6677be7b6b986426.tar.xz
[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 <junkio@cox.net> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--diff.c45
-rw-r--r--diffcore.h6
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 {