{"thread":{"id":"3928","subject":"[PATCH] colored diff.","startedAt":"2006-04-20T10:18:31Z","lastAt":"2006-04-20T11:52:38Z","messageCount":2,"participants":["Junio C Hamano","Thomas Glanzmann"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"18914","messageId":"7vpsjczh1k.fsf@assigned-by-dhcp.cox.net","threadId":"3928","inReplyTo":null,"subject":"[PATCH] colored diff.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-20T10:18:31Z","receivedAt":"2006-04-20T10:18:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"With configuration option diff.usecolor, this colorizes the diff\noutput.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * I am not happy with the initialization, especially the part\n   that calls git_config(git_diff_config) from git.c, and I am\n   not that interested in this myself, but some people seem to\n   like colors, so this will be in \"pu\" for now.\n\n cache.h    |    3 +-\n diff-lib.c |  101 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------\n diff.h     |    2 +\n git.c      |    3 +-\n pager.c    |   17 ++++++----\n 5 files changed, 104 insertions(+), 22 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 69801b0..e6aa297 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -169,7 +169,6 @@ extern int trust_executable_bit;\n extern int assume_unchanged;\n extern int only_use_symrefs;\n extern int warn_ambiguous_refs;\n-extern int diff_rename_limit_default;\n extern int shared_repository;\n extern const char *apply_default_whitespace;\n \n@@ -359,6 +358,6 @@ extern int receive_unpack_pack(int fd[2]\n extern int receive_keep_pack(int fd[2], const char *me, int quiet);\n \n /* pager.c */\n-extern void setup_pager(void);\n+extern int setup_pager(void);\n \n #endif /* CACHE_H */\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0a832c3..86a2561 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -12,7 +12,8 @@ #include \"xdiff-interface.h\"\n \n static int use_size_cache;\n \n-int diff_rename_limit_default = -1;\n+static int diff_rename_limit_default = -1;\n+static int diff_use_color = 0;\n \n int git_diff_config(const char *var, const char *value)\n {\n@@ -20,10 +21,31 @@ int git_diff_config(const char *var, con\n \t\tdiff_rename_limit_default = git_config_int(var, value);\n \t\treturn 0;\n \t}\n-\n+\tif (!strcmp(var, \"diff.usecolor\")) {\n+\t\tdiff_use_color = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \treturn git_default_config(var, value);\n }\n \n+static const char *diff_colors[] = {\n+\t\"\\033[0;0m\",\n+\t\"\\033[1;35m\",\n+\t\"\\033[1;31m\",\n+\t\"\\033[1;34m\",\n+};\n+\n+void diff_setup_colors(void)\n+{\n+\tstatic int inited;\n+\tif (!inited) {\n+\t\tif (diff_use_color && !isatty(1))\n+\t\t\tdiff_use_color = 0;\n+\t\tinited++;\n+\t\tgit_config(git_diff_config);\n+\t}\n+}\n+\n static char *quote_one(const char *str)\n {\n \tint needlen;\n@@ -176,23 +198,61 @@ static int fill_mmfile(mmfile_t *mf, str\n }\n \n struct emit_callback {\n+\tstruct xdiff_emit_state xm;\n+\tint nparents;\n \tconst char **label_path;\n };\n \n-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)\n+enum color_diff {\n+\tDIFF_PLAIN = 0,\n+\tDIFF_METAINFO = 1,\n+\tDIFF_FILE_OLD = 2,\n+\tDIFF_FILE_NEW = 3,\n+};\n+\n+static void color_diff(enum color_diff ix)\n+{\n+\tdiff_setup_colors();\n+\tif (diff_use_color)\n+\t\tfputs(diff_colors[ix], stdout);\n+}\n+\n+static void fn_out_consume(void *priv, char *line, unsigned long len)\n {\n \tint i;\n \tstruct emit_callback *ecbdata = priv;\n-\n \tif (ecbdata->label_path[0]) {\n+\t\tcolor_diff(DIFF_METAINFO);\n \t\tprintf(\"--- %s\\n\", ecbdata->label_path[0]);\n+\t\tcolor_diff(DIFF_METAINFO);\n \t\tprintf(\"+++ %s\\n\", ecbdata->label_path[1]);\n \t\tecbdata->label_path[0] = ecbdata->label_path[1] = NULL;\n \t}\n-\tfor (i = 0; i < nbuf; i++)\n-\t\tif (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))\n-\t\t\treturn -1;\n-\treturn 0;\n+\n+\t/* This is not really necessary for now because\n+\t * this codepath only deals with two-way diffs.\n+\t */\n+\tfor (i = 0; i < len && line[i] == '@'; i++)\n+\t\t;\n+\tif (2 <= i && i < len && line[i] == ' ') {\n+\t\tecbdata->nparents = i - 1;\n+\t\tcolor_diff(DIFF_METAINFO);\n+\t}\n+\telse if (len < ecbdata->nparents)\n+\t\tcolor_diff(DIFF_PLAIN);\n+\telse {\n+\t\tint nparents = ecbdata->nparents;\n+\t\tint color = DIFF_PLAIN;\n+\t\tfor (i = 0; i < nparents && len; i++) {\n+\t\t\tif (line[i] == '-')\n+\t\t\t\tcolor = DIFF_FILE_OLD;\n+\t\t\telse if (line[i] == '+')\n+\t\t\t\tcolor = DIFF_FILE_NEW;\n+\t\t}\n+\t\tcolor_diff(color);\n+\t}\n+\tfwrite(line, len, 1, stdout);\n+\tcolor_diff(DIFF_PLAIN);\n }\n \n struct diffstat_t {\n@@ -359,25 +419,37 @@ static void builtin_diff(const char *nam\n \tb_two = quote_two(\"b/\", name_b);\n \tlbl[0] = DIFF_FILE_VALID(one) ? a_one : \"/dev/null\";\n \tlbl[1] = DIFF_FILE_VALID(two) ? b_two : \"/dev/null\";\n+\n+\tcolor_diff(DIFF_METAINFO);\n \tprintf(\"diff --git %s %s\\n\", a_one, b_two);\n \tif (lbl[0][0] == '/') {\n \t\t/* /dev/null */\n+\t\tcolor_diff(DIFF_METAINFO);\n \t\tprintf(\"new file mode %06o\\n\", two->mode);\n-\t\tif (xfrm_msg && xfrm_msg[0])\n+\t\tif (xfrm_msg && xfrm_msg[0]) {\n+\t\t\tcolor_diff(DIFF_METAINFO);\n \t\t\tputs(xfrm_msg);\n+\t\t}\n \t}\n \telse if (lbl[1][0] == '/') {\n+\t\tcolor_diff(DIFF_METAINFO);\n \t\tprintf(\"deleted file mode %06o\\n\", one->mode);\n-\t\tif (xfrm_msg && xfrm_msg[0])\n+\t\tif (xfrm_msg && xfrm_msg[0]) {\n+\t\t\tcolor_diff(DIFF_METAINFO);\n \t\t\tputs(xfrm_msg);\n+\t\t}\n \t}\n \telse {\n \t\tif (one->mode != two->mode) {\n+\t\t\tcolor_diff(DIFF_METAINFO);\n \t\t\tprintf(\"old mode %06o\\n\", one->mode);\n+\t\t\tcolor_diff(DIFF_METAINFO);\n \t\t\tprintf(\"new mode %06o\\n\", two->mode);\n \t\t}\n-\t\tif (xfrm_msg && xfrm_msg[0])\n+\t\tif (xfrm_msg && xfrm_msg[0]) {\n+\t\t\tcolor_diff(DIFF_METAINFO);\n \t\t\tputs(xfrm_msg);\n+\t\t}\n \t\t/*\n \t\t * we do not run diff between different kind\n \t\t * of objects.\n@@ -385,6 +457,7 @@ static void builtin_diff(const char *nam\n \t\tif ((one->mode ^ two->mode) & S_IFMT)\n \t\t\tgoto free_ab_and_return;\n \t\tif (complete_rewrite) {\n+\t\t\tcolor_diff(DIFF_PLAIN);\n \t\t\temit_rewrite_diff(name_a, name_b, one, two);\n \t\t\tgoto free_ab_and_return;\n \t\t}\n@@ -403,7 +476,9 @@ static void builtin_diff(const char *nam\n \t\txdemitcb_t ecb;\n \t\tstruct emit_callback ecbdata;\n \n+\t\tmemset(&ecbdata, 0, sizeof(ecbdata));\n \t\tecbdata.label_path = lbl;\n+\t\tecbdata.nparents = 0;\n \t\txpp.flags = XDF_NEED_MINIMAL;\n \t\txecfg.ctxlen = 3;\n \t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n@@ -413,12 +488,14 @@ static void builtin_diff(const char *nam\n \t\t\txecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);\n \t\telse if (!strncmp(diffopts, \"-u\", 2))\n \t\t\txecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);\n-\t\tecb.outf = fn_out;\n+\t\tecb.outf = xdiff_outf;\n \t\tecb.priv = &ecbdata;\n+\t\tecbdata.xm.consume = fn_out_consume;\n \t\txdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);\n \t}\n \n  free_ab_and_return:\n+\tcolor_diff(DIFF_PLAIN);\n \tfree(a_one);\n \tfree(b_two);\n \treturn;\ndiff --git a/diff.h b/diff.h\nindex 52fff66..7cb5708 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -168,4 +168,6 @@ #define DIFF_STATUS_FILTER_BROKEN\t'B'\n \n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n+extern void diff_setup_colors(void);\n+\n #endif /* DIFF_H */\ndiff --git a/git.c b/git.c\nindex 40b7e42..6742de7 100644\n--- a/git.c\n+++ b/git.c\n@@ -290,7 +290,8 @@ static int cmd_log_wc(int argc, const ch\n \t\tdie(\"unrecognized argument: %s\", argv[1]);\n \n \tprepare_revision_walk(rev);\n-\tsetup_pager();\n+\tif (!setup_pager())\n+\t\tdiff_setup_colors();\n \twhile ((commit = get_revision(rev)) != NULL) {\n \t\tlog_tree_commit(rev, commit);\n \t\tfree(commit->buffer);\ndiff --git a/pager.c b/pager.c\nindex b063353..1fb7103 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -10,26 +10,29 @@ static void run_pager(const char *pager)\n \texeclp(pager, pager, NULL);\n }\n \n-void setup_pager(void)\n+/* Returns 0 if the caller can assume the output stream\n+ * is like a terminal (either bare terminal or a pager.\n+ */\n+int setup_pager(void)\n {\n \tpid_t pid;\n \tint fd[2];\n \tconst char *pager = getenv(\"PAGER\");\n \n \tif (!isatty(1))\n-\t\treturn;\n+\t\treturn 1;\n \tif (!pager)\n \t\tpager = \"less\";\n \telse if (!*pager || !strcmp(pager, \"cat\"))\n-\t\treturn;\n+\t\treturn 0;\n \n \tif (pipe(fd) < 0)\n-\t\treturn;\n+\t\treturn -1;\n \tpid = fork();\n \tif (pid < 0) {\n \t\tclose(fd[0]);\n \t\tclose(fd[1]);\n-\t\treturn;\n+\t\treturn -1;\n \t}\n \n \t/* return in the child */\n@@ -37,7 +40,7 @@ void setup_pager(void)\n \t\tdup2(fd[1], 1);\n \t\tclose(fd[0]);\n \t\tclose(fd[1]);\n-\t\treturn;\n+\t\treturn 0;\n \t}\n \n \t/* The original process turns into the PAGER */\n@@ -45,7 +48,7 @@ void setup_pager(void)\n \tclose(fd[0]);\n \tclose(fd[1]);\n \n-\tsetenv(\"LESS\", \"-S\", 0);\n+\tsetenv(\"LESS\", \"-RS\", 0);\n \trun_pager(pager);\n \texit(255);\n }\n"},{"id":"18916","messageId":"20060420115238.GA4681@cip.informatik.uni-erlangen.de","threadId":"3928","inReplyTo":"7vpsjczh1k.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] colored diff.","fromName":"Thomas Glanzmann","fromEmail":"sithglan@stud.uni-erlangen.de","sentAt":"2006-04-20T11:52:38Z","receivedAt":"2006-04-20T11:52:38Z","isPatch":true,"sender":{"key":"sithglan@stud.uni-erlangen.de","avatar":null},"body":"Hello Junio,\n\n* Junio C Hamano <junkio@cox.net> [060420 12:22]:\n> With configuration option diff.usecolor, this colorizes the diff\n> output.\n\nI use a long time the following combination, which served me and a few\nothers very well:\n\n        bourne shell function:\n\n        BLACK=\"^[[0;30m\"\n        RED=\"^[[0;31m\"\n        GREEN=\"^[[0;32m\"\n        YELLO=\"^[[0;33m\"\n        BLUE=\"^[[0;34m\"\n        PURPLE=\"^[[0;35m\"\n        CYAN=\"^[[0;36m\"\n        WHITE=\"^[[0;37m\"\n        END=\"^[[0m\"\n        REVERSE=\"^[[7m\"\n\n        cdiff()\n        {\n                cat $1 | \\\n                sed -e \" \\\n                        s/^\\(diff-tree\\)\\(.*\\)/\\1${RED}\\2${END}/; \\\n                        s/^+.*/$RED&$END/;  \\\n                        s/^-.*/$BLUE&$END/;  \\\n                        s/^@.*/$GREEN&$END/;  \\\n                        s/^Date.*/$RED&$END/;  \\\n                        s/^Author.*/$RED&$END/; \\\n                        \" | less -R -i -p \"^diff-tree\"\n\n                        # This would highlight manpages but the last line clashes with cdiff\n                        # --tg 00:52 05-05-26\n                        #s/^[A-Z]\\+[A-Z ]\\+$/$RED&$END/; \\\n                        #s/[A-Z]\\+([0-9])/$RED&$END/g; \\\n                        # s/\\W--\\?[0-9a-zA-Z=-]\\+/$BLUE&$END/g; \\\n        }\n\n# with 'n' you simply jump to the next commit\n# with 'N' to the previous\n\ngit-whatchanged -p | cdiff\n\nscreenshot: http://wwwcip.informatik.uni-erlangen.de/~sithglan/shot.png\n\nThanks for the enegery you put in git,\n                                   Thomas\n"}]}