{"thread":{"id":"721","subject":"[PATCH] Diff updates to express type changes","startedAt":"2005-05-26T09:24:30Z","lastAt":"2005-05-26T09:24:30Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"3981","messageId":"7vu0kqcpbl.fsf@assigned-by-dhcp.cox.net","threadId":"721","inReplyTo":null,"subject":"[PATCH] Diff updates to express type changes","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-26T09:24:30Z","receivedAt":"2005-05-26T09:24:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"With the introduction of type 'T' in the diff-raw output, and\nthe \"apply-patch\" program Linus has been quietly working on\nwithout much advertisement, it started to make sense to emit\nusable information in the \"diff --git\" patch output format as\nwell.  Earlier built-in diff driver punted and did not say\nanything about a symbolic link changing into a file or vice\nversa, but this version represents it as a pair of deletion\nand creation.\n\nIt also fixes a minor problem dealing with old archive created\nwith ancient git.  The earlier code was reporting file mode\nchange between 100664 and 100644 (we shouldn't).  The linux-2.6\ngit tree has a good example that exposes this problem.  A good\ntest case is commit ce1dc02f76432a46db149241e015a4f782974623.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c     |   45 ++++++++++++++++++++++++++++++++++++---------\ndiffcore.h |    6 ++++++\n2 files changed, 42 insertions(+), 9 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -171,8 +171,8 @@ struct diff_filespec *alloc_filespec(con\n void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,\n \t\t   unsigned short mode)\n {\n-\tif (mode) { /* just playing defensive */\n-\t\tspec->mode = mode;\n+\tif (mode) {\n+\t\tspec->mode = DIFF_FILE_CANON_MODE(mode);\n \t\tmemcpy(spec->sha1, sha1, 20);\n \t\tspec->sha1_valid = !!memcmp(sha1, null_sha1, 20);\n \t}\n@@ -390,7 +390,8 @@ static void remove_tempfile_on_signal(in\n  *               infile2 infile2-sha1 infile2-mode [ rename-to ]\n  *\n  */\n-static void run_external_diff(const char *name,\n+static void run_external_diff(const char *pgm,\n+\t\t\t      const char *name,\n \t\t\t      const char *other,\n \t\t\t      struct diff_filespec *one,\n \t\t\t      struct diff_filespec *two,\n@@ -418,7 +419,6 @@ static void run_external_diff(const char\n \tif (pid < 0)\n \t\tdie(\"unable to fork\");\n \tif (!pid) {\n-\t\tconst char *pgm = external_diff();\n \t\tif (pgm) {\n \t\t\tif (one && two) {\n \t\t\t\tconst char *exec_arg[10];\n@@ -468,6 +468,30 @@ static void run_external_diff(const char\n \tremove_tempfile();\n }\n \n+static void run_diff(const char *name,\n+\t\t     const char *other,\n+\t\t     struct diff_filespec *one,\n+\t\t     struct diff_filespec *two,\n+\t\t     const char *xfrm_msg)\n+{\n+\tconst char *pgm = external_diff();\n+\tif (!pgm &&\n+\t    DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&\n+\t    (S_IFMT & one->mode) != (S_IFMT & two->mode)) {\n+\t\t/* a filepair that changes between file and symlink\n+\t\t * needs to be split into deletion and creation.\n+\t\t */\n+\t\tstruct diff_filespec *null = alloc_filespec(two->path);\n+\t\trun_external_diff(NULL, name, other, one, null, xfrm_msg);\n+\t\tfree(null);\n+\t\tnull = alloc_filespec(one->path);\n+\t\trun_external_diff(NULL, name, other, null, two, xfrm_msg);\n+\t\tfree(null);\n+\t}\n+\telse\n+\t\trun_external_diff(pgm, name, other, one, two, xfrm_msg);\n+}\n+\n void diff_setup(int reverse_diff_)\n {\n \treverse_diff = reverse_diff_;\n@@ -553,9 +577,11 @@ int diff_unmodified_pair(struct diff_fil\n \tone = p->one;\n \ttwo = p->two;\n \n-\t/* deletion, addition, mode change and renames are all interesting. */\n+\t/* deletion, addition, mode or type change\n+\t * and rename are all interesting.\n+\t */\n \tif (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||\n-\t    (one->mode != two->mode) ||\n+\t    DIFF_PAIR_MODE_CHANGED(p) ||\n \t    strcmp(one->path, two->path))\n \t\treturn 0;\n \n@@ -608,9 +634,9 @@ static void diff_flush_patch(struct diff\n \t}\n \n \tif (DIFF_PAIR_UNMERGED(p))\n-\t\trun_external_diff(name, NULL, NULL, NULL, NULL);\n+\t\trun_diff(name, NULL, NULL, NULL, NULL);\n \telse\n-\t\trun_external_diff(name, other, p->one, p->two, msg);\n+\t\trun_diff(name, other, p->one, p->two, msg);\n }\n \n int diff_needs_to_stay(struct diff_queue_struct *q, int i,\n@@ -775,7 +801,8 @@ void diff_flush(int diff_output_style, i\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (p->status == 'X')\n+\t\tif ((diff_output_style == DIFF_FORMAT_NO_OUTPUT) ||\n+\t\t    (p->status == 'X'))\n \t\t\tcontinue;\n \t\tif (p->status == 0)\n \t\t\tdie(\"internal error in diff-resolve-rename-copy\");\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -48,6 +48,12 @@ struct diff_filepair {\n #define DIFF_PAIR_TYPE_CHANGED(p) \\\n \t((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))\n \n+#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode)\n+\n+#define DIFF_FILE_CANON_MODE(mode) \\\n+\t(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \\\n+\tS_ISLNK(mode) ? S_IFLNK : S_IFDIR)\n+\n extern int diff_unmodified_pair(struct diff_filepair *);\n \n struct diff_queue_struct {\n------------------------------------------------\n\n"}]}