{"thread":{"id":"23855","subject":"[PATCH 0/6 v2] Make git log --graph looks better with -p and other diff options","startedAt":"2010-05-20T10:25:32Z","lastAt":"2010-05-21T01:49:20Z","messageCount":10,"participants":["Bo Yang","Jeff King"],"isPatch":true,"patchVersion":2,"patchTotal":6},"messages":[{"id":"141949","messageId":"1274351138-11813-1-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":null,"subject":"[PATCH 0/6 v2] Make git log --graph looks better with -p and other diff options","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:32Z","receivedAt":"2010-05-20T10:25:32Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"When we run git log --graph with any other diff output function, such as '-p','--check','--numstat' or other diff optoins, the diff output area have no graph lines ahead of it. And this make the text graph looks strange.\nThe following 7 patches try to deal with this, and put the text graph columns before all the diff output lines.\n\n\nBo Yang (6):\n  Add a prefix output callback to diff output.\n  Output the graph columns at the end of the commit message.\n  diff.c: Output the text graph padding before each diff line.\n  Emit a whole line once a time.\n  Register a callback for graph output.\n  Make --color-words work well with --graph.\n\n color.c    |   21 +++-\n color.h    |    3 +-\n diff.c     |  339 +++++++++++++++++++++++++++++++++++++++++++++---------------\n diff.h     |    5 +\n graph.c    |   19 ++++\n log-tree.c |    6 +\n 6 files changed, 303 insertions(+), 90 deletions(-)\n"},{"id":"141950","messageId":"1274351138-11813-2-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-1-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 1/6 v2] Add a prefix output callback to diff output.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:33Z","receivedAt":"2010-05-20T10:25:33Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"The callback allow to output some prefix string for\neach line of diff output.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n diff.c |   56 ++++++++++++++++++++++++++++++++------------------------\n diff.h |    5 +++++\n 2 files changed, 37 insertions(+), 24 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 43a313d..b2df2d5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -194,8 +194,8 @@ struct emit_callback {\n \tsane_truncate_fn truncate;\n \tconst char **label_path;\n \tstruct diff_words_data *diff_words;\n+\tstruct diff_options *opt;\n \tint *found_changesp;\n-\tFILE *file;\n \tstruct strbuf *header;\n };\n \n@@ -282,11 +282,19 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,\n \tecbdata->blank_at_eof_in_postimage = (at - l2) + 1;\n }\n \n-static void emit_line_0(FILE *file, const char *set, const char *reset,\n+static void emit_line_0(struct diff_options *o, const char *set, const char *reset,\n \t\t\tint first, const char *line, int len)\n {\n \tint has_trailing_newline, has_trailing_carriage_return;\n \tint nofirst;\n+\tFILE *file = o->file;\n+\n+\tif (o->output_prefix) {\n+\t\tstruct strbuf *msg = NULL;\n+\t\tmsg = o->output_prefix(file, o->output_prefix_data);\n+\t\tassert(msg);\n+\t\tfwrite(msg->buf, msg->len, 1, file);\n+\t}\n \n \tif (len == 0) {\n \t\thas_trailing_newline = (first == '\\n');\n@@ -316,10 +324,10 @@ static void emit_line_0(FILE *file, const char *set, const char *reset,\n \t\tfputc('\\n', file);\n }\n \n-static void emit_line(FILE *file, const char *set, const char *reset,\n+static void emit_line(struct diff_options *o, const char *set, const char *reset,\n \t\t      const char *line, int len)\n {\n-\temit_line_0(file, set, reset, line[0], line+1, len-1);\n+\temit_line_0(o, set, reset, line[0], line+1, len-1);\n }\n \n static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)\n@@ -341,15 +349,15 @@ static void emit_add_line(const char *reset,\n \tconst char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);\n \n \tif (!*ws)\n-\t\temit_line_0(ecbdata->file, set, reset, '+', line, len);\n+\t\temit_line_0(ecbdata->opt, set, reset, '+', line, len);\n \telse if (new_blank_line_at_eof(ecbdata, line, len))\n \t\t/* Blank line at EOF - paint '+' as well */\n-\t\temit_line_0(ecbdata->file, ws, reset, '+', line, len);\n+\t\temit_line_0(ecbdata->opt, ws, reset, '+', line, len);\n \telse {\n \t\t/* Emit just the prefix, then the rest. */\n-\t\temit_line_0(ecbdata->file, set, reset, '+', \"\", 0);\n+\t\temit_line_0(ecbdata->opt, set, reset, '+', \"\", 0);\n \t\tws_check_emit(line, len, ecbdata->ws_rule,\n-\t\t\t      ecbdata->file, set, reset, ws);\n+\t\t\t      ecbdata->opt->file, set, reset, ws);\n \t}\n }\n \n@@ -370,23 +378,23 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tif (len < 10 ||\n \t    memcmp(line, atat, 2) ||\n \t    !(ep = memmem(line + 2, len - 2, atat, 2))) {\n-\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\temit_line(ecbdata->opt, plain, reset, line, len);\n \t\treturn;\n \t}\n \tep += 2; /* skip over @@ */\n \n \t/* The hunk header in fraginfo color */\n-\temit_line(ecbdata->file, frag, reset, line, ep - line);\n+\temit_line(ecbdata->opt, frag, reset, line, ep - line);\n \n \t/* blank before the func header */\n \tfor (cp = ep; ep - line < len; ep++)\n \t\tif (*ep != ' ' && *ep != '\\t')\n \t\t\tbreak;\n \tif (ep != cp)\n-\t\temit_line(ecbdata->file, plain, reset, cp, ep - cp);\n+\t\temit_line(ecbdata->opt, plain, reset, cp, ep - cp);\n \n \tif (ep < line + len)\n-\t\temit_line(ecbdata->file, func, reset, ep, line + len - ep);\n+\t\temit_line(ecbdata->opt, func, reset, ep, line + len - ep);\n }\n \n static struct diff_tempfile *claim_diff_tempfile(void) {\n@@ -446,7 +454,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,\n \t\tlen = endp ? (endp - data + 1) : size;\n \t\tif (prefix != '+') {\n \t\t\tecb->lno_in_preimage++;\n-\t\t\temit_line_0(ecb->file, old, reset, '-',\n+\t\t\temit_line_0(ecb->opt, old, reset, '-',\n \t\t\t\t    data, len);\n \t\t} else {\n \t\t\tecb->lno_in_postimage++;\n@@ -458,7 +466,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,\n \tif (!endp) {\n \t\tconst char *plain = diff_get_color(ecb->color_diff,\n \t\t\t\t\t\t   DIFF_PLAIN);\n-\t\temit_line_0(ecb->file, plain, reset, '\\\\',\n+\t\temit_line_0(ecb->opt, plain, reset, '\\\\',\n \t\t\t    nneof, strlen(nneof));\n \t}\n }\n@@ -508,7 +516,7 @@ static void emit_rewrite_diff(const char *name_a,\n \tecbdata.color_diff = color_diff;\n \tecbdata.found_changesp = &o->found_changes;\n \tecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);\n-\tecbdata.file = o->file;\n+\tecbdata.opt = o;\n \tif (ecbdata.ws_rule & WS_BLANK_AT_EOF) {\n \t\tmmfile_t mf1, mf2;\n \t\tmf1.ptr = (char *)data_one;\n@@ -786,7 +794,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \tconst char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);\n \n \tif (ecbdata->header) {\n-\t\tfprintf(ecbdata->file, \"%s\", ecbdata->header->buf);\n+\t\tfprintf(ecbdata->opt->file, \"%s\", ecbdata->header->buf);\n \t\tstrbuf_reset(ecbdata->header);\n \t\tecbdata->header = NULL;\n \t}\n@@ -798,9 +806,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\tname_a_tab = strchr(ecbdata->label_path[0], ' ') ? \"\\t\" : \"\";\n \t\tname_b_tab = strchr(ecbdata->label_path[1], ' ') ? \"\\t\" : \"\";\n \n-\t\tfprintf(ecbdata->file, \"%s--- %s%s%s\\n\",\n+\t\tfprintf(ecbdata->opt->file, \"%s--- %s%s%s\\n\",\n \t\t\tmeta, ecbdata->label_path[0], reset, name_a_tab);\n-\t\tfprintf(ecbdata->file, \"%s+++ %s%s%s\\n\",\n+\t\tfprintf(ecbdata->opt->file, \"%s+++ %s%s%s\\n\",\n \t\t\tmeta, ecbdata->label_path[1], reset, name_b_tab);\n \t\tecbdata->label_path[0] = ecbdata->label_path[1] = NULL;\n \t}\n@@ -818,12 +826,12 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\tfind_lno(line, ecbdata);\n \t\temit_hunk_header(ecbdata, line, len);\n \t\tif (line[len-1] != '\\n')\n-\t\t\tputc('\\n', ecbdata->file);\n+\t\t\tputc('\\n', ecbdata->opt->file);\n \t\treturn;\n \t}\n \n \tif (len < 1) {\n-\t\temit_line(ecbdata->file, reset, reset, line, len);\n+\t\temit_line(ecbdata->opt, reset, reset, line, len);\n \t\treturn;\n \t}\n \n@@ -840,7 +848,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\tdiff_words_flush(ecbdata);\n \t\tline++;\n \t\tlen--;\n-\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\temit_line(ecbdata->opt, plain, reset, line, len);\n \t\treturn;\n \t}\n \n@@ -851,7 +859,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\tecbdata->lno_in_preimage++;\n \t\tif (line[0] == ' ')\n \t\t\tecbdata->lno_in_postimage++;\n-\t\temit_line(ecbdata->file, color, reset, line, len);\n+\t\temit_line(ecbdata->opt, color, reset, line, len);\n \t} else {\n \t\tecbdata->lno_in_postimage++;\n \t\temit_add_line(reset, ecbdata, line + 1, len - 1);\n@@ -1416,7 +1424,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)\n \t\tfprintf(data->o->file, \"%s:%d: %s.\\n\",\n \t\t\tdata->filename, data->lineno, err);\n \t\tfree(err);\n-\t\temit_line(data->o->file, set, reset, line, 1);\n+\t\temit_line(data->o, set, reset, line, 1);\n \t\tws_check_emit(line + 1, len - 1, data->ws_rule,\n \t\t\t      data->o->file, set, reset, ws);\n \t} else if (line[0] == ' ') {\n@@ -1726,7 +1734,7 @@ static void builtin_diff(const char *name_a,\n \t\tecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);\n \t\tif (ecbdata.ws_rule & WS_BLANK_AT_EOF)\n \t\t\tcheck_blank_at_eof(&mf1, &mf2, &ecbdata);\n-\t\tecbdata.file = o->file;\n+\t\tecbdata.opt = o;\n \t\tecbdata.header = header.len ? &header : NULL;\n \t\txpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;\n \t\txecfg.ctxlen = o->context;\ndiff --git a/diff.h b/diff.h\nindex 6a71013..d28c0fd 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -9,6 +9,7 @@\n struct rev_info;\n struct diff_options;\n struct diff_queue_struct;\n+struct strbuf;\n \n typedef void (*change_fn_t)(struct diff_options *options,\n \t\t unsigned old_mode, unsigned new_mode,\n@@ -25,6 +26,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,\n typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n \t\tstruct diff_options *options, void *data);\n \n+typedef struct strbuf *(*diff_prefix_fn_t)(FILE *file, void *data);\n+\n #define DIFF_FORMAT_RAW\t\t0x0001\n #define DIFF_FORMAT_DIFFSTAT\t0x0002\n #define DIFF_FORMAT_NUMSTAT\t0x0004\n@@ -122,6 +125,8 @@ struct diff_options {\n \tadd_remove_fn_t add_remove;\n \tdiff_format_fn_t format_callback;\n \tvoid *format_callback_data;\n+\tdiff_prefix_fn_t output_prefix;\n+\tvoid *output_prefix_data;\n };\n \n enum color_diff {\n-- \n1.6.0.4\n"},{"id":"141951","messageId":"1274351138-11813-3-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-2-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 2/6 v2] Output the graph columns at the end of the commit message.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:34Z","receivedAt":"2010-05-20T10:25:34Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"There is an empty line between the commit message and the\ndiff output. Add the graph columns as prefix of this line.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n log-tree.c |    6 ++++++\n 1 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex d3ae969..a17cde3 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -469,6 +469,12 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n \t\t\tif ((pch & opt->diffopt.output_format) == pch)\n \t\t\t\tprintf(\"---\");\n+\t\t\tif (opt->diffopt.output_prefix) {\n+\t\t\t\tstruct strbuf *msg = NULL;\n+\t\t\t\tmsg = opt->diffopt.output_prefix(opt->diffopt.file,\n+\t\t\t\t\topt->diffopt.output_prefix_data);\n+\t\t\t\tfwrite(msg->buf, msg->len, 1, stdout);\n+\t\t\t}\n \t\t\tputchar('\\n');\n \t\t}\n \t}\n-- \n1.6.0.4\n"},{"id":"141953","messageId":"1274351138-11813-4-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-3-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 3/6 v2] diff.c: Output the text graph padding before each diff line.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:35Z","receivedAt":"2010-05-20T10:25:35Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Change -p/--dirstat/--binary/--numstat/--stat/--shortstat/--check\nto align with graph paddings.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n diff.c |  161 +++++++++++++++++++++++++++++++++++++++++++++++----------------\n 1 files changed, 120 insertions(+), 41 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex b2df2d5..5d1b7b5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -490,6 +490,13 @@ static void emit_rewrite_diff(const char *name_a,\n \tchar *data_one, *data_two;\n \tsize_t size_one, size_two;\n \tstruct emit_callback ecbdata;\n+\tchar *line_prefix = \"\";\n+\tstruct strbuf *msgbuf;\n+\n+\tif (o && o->output_prefix) {\n+\t\tmsgbuf = o->output_prefix(o->file, o->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n \n \tif (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {\n \t\ta_prefix = o->b_prefix;\n@@ -531,9 +538,10 @@ static void emit_rewrite_diff(const char *name_a,\n \tlc_a = count_lines(data_one, size_one);\n \tlc_b = count_lines(data_two, size_two);\n \tfprintf(o->file,\n-\t\t\"%s--- %s%s%s\\n%s+++ %s%s%s\\n%s@@ -\",\n-\t\tmetainfo, a_name.buf, name_a_tab, reset,\n-\t\tmetainfo, b_name.buf, name_b_tab, reset, fraginfo);\n+\t\t\"%s%s--- %s%s%s\\n%s%s+++ %s%s%s\\n%s%s@@ -\",\n+\t\tline_prefix, metainfo, a_name.buf, name_a_tab, reset,\n+\t\tline_prefix, metainfo, b_name.buf, name_b_tab, reset,\n+\t\tline_prefix, fraginfo);\n \tprint_line_count(o->file, lc_a);\n \tfprintf(o->file, \" +\");\n \tprint_line_count(o->file, lc_b);\n@@ -792,6 +800,14 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \tconst char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);\n \tconst char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);\n \tconst char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);\n+\tstruct diff_options *o = ecbdata->opt;\n+\tchar *line_prefix = \"\";\n+\tstruct strbuf *msgbuf;\n+\n+\tif (o && o->output_prefix) {\n+\t\tmsgbuf = o->output_prefix(o->file, o->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n \n \tif (ecbdata->header) {\n \t\tfprintf(ecbdata->opt->file, \"%s\", ecbdata->header->buf);\n@@ -806,10 +822,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\tname_a_tab = strchr(ecbdata->label_path[0], ' ') ? \"\\t\" : \"\";\n \t\tname_b_tab = strchr(ecbdata->label_path[1], ' ') ? \"\\t\" : \"\";\n \n-\t\tfprintf(ecbdata->opt->file, \"%s--- %s%s%s\\n\",\n-\t\t\tmeta, ecbdata->label_path[0], reset, name_a_tab);\n-\t\tfprintf(ecbdata->opt->file, \"%s+++ %s%s%s\\n\",\n-\t\t\tmeta, ecbdata->label_path[1], reset, name_b_tab);\n+\t\tfprintf(ecbdata->opt->file, \"%s%s--- %s%s%s\\n\",\n+\t\t\tline_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);\n+\t\tfprintf(ecbdata->opt->file, \"%s%s+++ %s%s%s\\n\",\n+\t\t\tline_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);\n \t\tecbdata->label_path[0] = ecbdata->label_path[1] = NULL;\n \t}\n \n@@ -1039,10 +1055,17 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)\n \tint total_files = data->nr;\n \tint width, name_width;\n \tconst char *reset, *set, *add_c, *del_c;\n+\tconst char *line_prefix = \"\";\n+\tstruct strbuf *msg = NULL;\n \n \tif (data->nr == 0)\n \t\treturn;\n \n+\tif (options->output_prefix) {\n+\t\tmsg = options->output_prefix(options->file, options->output_prefix_data);\n+\t\tline_prefix = msg->buf;\n+\t}\n+\n \twidth = options->stat_width ? options->stat_width : 80;\n \tname_width = options->stat_name_width ? options->stat_name_width : 50;\n \n@@ -1112,6 +1135,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)\n \t\t}\n \n \t\tif (data->files[i]->is_binary) {\n+\t\t\tfprintf(options->file, \"%s\", line_prefix);\n \t\t\tshow_name(options->file, prefix, name, len);\n \t\t\tfprintf(options->file, \"  Bin \");\n \t\t\tfprintf(options->file, \"%s%\"PRIuMAX\"%s\",\n@@ -1124,6 +1148,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)\n \t\t\tcontinue;\n \t\t}\n \t\telse if (data->files[i]->is_unmerged) {\n+\t\t\tfprintf(options->file, \"%s\", line_prefix);\n \t\t\tshow_name(options->file, prefix, name, len);\n \t\t\tfprintf(options->file, \"  Unmerged\\n\");\n \t\t\tcontinue;\n@@ -1146,6 +1171,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)\n \t\t\tadd = scale_linear(add, width, max_change);\n \t\t\tdel = scale_linear(del, width, max_change);\n \t\t}\n+\t\tfprintf(options->file, \"%s\", line_prefix);\n \t\tshow_name(options->file, prefix, name, len);\n \t\tfprintf(options->file, \"%5\"PRIuMAX\"%s\", added + deleted,\n \t\t\t\tadded + deleted ? \" \" : \"\");\n@@ -1153,6 +1179,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)\n \t\tshow_graph(options->file, '-', del, del_c, reset);\n \t\tfprintf(options->file, \"\\n\");\n \t}\n+\tfprintf(options->file, \"%s\", line_prefix);\n \tfprintf(options->file,\n \t       \" %d files changed, %d insertions(+), %d deletions(-)\\n\",\n \t       total_files, adds, dels);\n@@ -1179,6 +1206,12 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option\n \t\t\t}\n \t\t}\n \t}\n+\tif (options->output_prefix) {\n+\t\tstruct strbuf *msg = NULL;\n+\t\tmsg = options->output_prefix(options->file,\n+\t\t\t\toptions->output_prefix_data);\n+\t\tfprintf(options->file, \"%s\", msg->buf);\n+\t}\n \tfprintf(options->file, \" %d files changed, %d insertions(+), %d deletions(-)\\n\",\n \t       total_files, adds, dels);\n }\n@@ -1193,6 +1226,13 @@ static void show_numstat(struct diffstat_t *data, struct diff_options *options)\n \tfor (i = 0; i < data->nr; i++) {\n \t\tstruct diffstat_file *file = data->files[i];\n \n+\t\tif (options->output_prefix) {\n+\t\t\tstruct strbuf *msg = NULL;\n+\t\t\tmsg = options->output_prefix(options->file,\n+\t\t\t\t\toptions->output_prefix_data);\n+\t\t\tfprintf(options->file, \"%s\", msg->buf);\n+\t\t}\n+\n \t\tif (file->is_binary)\n \t\t\tfprintf(options->file, \"-\\t-\\t\");\n \t\telse\n@@ -1228,10 +1268,19 @@ struct dirstat_dir {\n \tint alloc, nr, percent, cumulative;\n };\n \n-static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)\n+static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)\n {\n \tunsigned long this_dir = 0;\n \tunsigned int sources = 0;\n+\tassert(opt);\n+\tFILE *file = opt->file;\n+\tconst char *line_prefix = \"\";\n+\tstruct strbuf *msg = NULL;\n+\n+\tif (opt->output_prefix) {\n+\t\tmsg = opt->output_prefix(opt->file, opt->output_prefix_data);\n+\t\tline_prefix = msg->buf;\n+\t}\n \n \twhile (dir->nr) {\n \t\tstruct dirstat_file *f = dir->files;\n@@ -1246,7 +1295,7 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch\n \t\tslash = strchr(f->name + baselen, '/');\n \t\tif (slash) {\n \t\t\tint newbaselen = slash + 1 - f->name;\n-\t\t\tthis = gather_dirstat(file, dir, changed, f->name, newbaselen);\n+\t\t\tthis = gather_dirstat(opt, dir, changed, f->name, newbaselen);\n \t\t\tsources++;\n \t\t} else {\n \t\t\tthis = f->changed;\n@@ -1268,7 +1317,7 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch\n \t\tif (permille) {\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n-\t\t\t\tfprintf(file, \"%4d.%01d%% %.*s\\n\", percent, permille % 10, baselen, base);\n+\t\t\t\tfprintf(file, \"%s%4d.%01d%% %.*s\\n\", line_prefix, percent, permille % 10, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\n@@ -1348,7 +1397,7 @@ static void show_dirstat(struct diff_options *options)\n \n \t/* Show all directories with more than x% of the changes */\n \tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n-\tgather_dirstat(options->file, &dir, changed, \"\", 0);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n static void free_diffstat_info(struct diffstat_t *diffstat)\n@@ -1406,6 +1455,15 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)\n \tconst char *reset = diff_get_color(color_diff, DIFF_RESET);\n \tconst char *set = diff_get_color(color_diff, DIFF_FILE_NEW);\n \tchar *err;\n+\tchar *line_prefix = \"\";\n+\tstruct strbuf *msgbuf;\n+\n+\tassert(data->o);\n+\tif (data->o->output_prefix) {\n+\t\tmsgbuf = data->o->output_prefix(data->o->file,\n+\t\t\tdata->o->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n \n \tif (line[0] == '+') {\n \t\tunsigned bad;\n@@ -1413,16 +1471,16 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)\n \t\tif (is_conflict_marker(line + 1, marker_size, len - 1)) {\n \t\t\tdata->status |= 1;\n \t\t\tfprintf(data->o->file,\n-\t\t\t\t\"%s:%d: leftover conflict marker\\n\",\n-\t\t\t\tdata->filename, data->lineno);\n+\t\t\t\t\"%s%s:%d: leftover conflict marker\\n\",\n+\t\t\t\tline_prefix, data->filename, data->lineno);\n \t\t}\n \t\tbad = ws_check(line + 1, len - 1, data->ws_rule);\n \t\tif (!bad)\n \t\t\treturn;\n \t\tdata->status |= bad;\n \t\terr = whitespace_error_string(bad);\n-\t\tfprintf(data->o->file, \"%s:%d: %s.\\n\",\n-\t\t\tdata->filename, data->lineno, err);\n+\t\tfprintf(data->o->file, \"%s%s:%d: %s.\\n\",\n+\t\t\tline_prefix, data->filename, data->lineno, err);\n \t\tfree(err);\n \t\temit_line(data->o, set, reset, line, 1);\n \t\tws_check_emit(line + 1, len - 1, data->ws_rule,\n@@ -1462,7 +1520,7 @@ static unsigned char *deflate_it(char *data,\n \treturn deflated;\n }\n \n-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)\n+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)\n {\n \tvoid *cp;\n \tvoid *delta;\n@@ -1491,13 +1549,13 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)\n \t}\n \n \tif (delta && delta_size < deflate_size) {\n-\t\tfprintf(file, \"delta %lu\\n\", orig_size);\n+\t\tfprintf(file, \"%sdelta %lu\\n\", prefix, orig_size);\n \t\tfree(deflated);\n \t\tdata = delta;\n \t\tdata_size = delta_size;\n \t}\n \telse {\n-\t\tfprintf(file, \"literal %lu\\n\", two->size);\n+\t\tfprintf(file, \"%sliteral %lu\\n\", prefix, two->size);\n \t\tfree(delta);\n \t\tdata = deflated;\n \t\tdata_size = deflate_size;\n@@ -1515,18 +1573,19 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)\n \t\t\tline[0] = bytes - 26 + 'a' - 1;\n \t\tencode_85(line + 1, cp, bytes);\n \t\tcp = (char *) cp + bytes;\n+\t\tfprintf(file, \"%s\", prefix);\n \t\tfputs(line, file);\n \t\tfputc('\\n', file);\n \t}\n-\tfprintf(file, \"\\n\");\n+\tfprintf(file, \"%s\\n\", prefix);\n \tfree(data);\n }\n \n-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)\n+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)\n {\n-\tfprintf(file, \"GIT binary patch\\n\");\n-\temit_binary_diff_body(file, one, two);\n-\temit_binary_diff_body(file, two, one);\n+\tfprintf(file, \"%sGIT binary patch\\n\", prefix);\n+\temit_binary_diff_body(file, one, two, prefix);\n+\temit_binary_diff_body(file, two, one, prefix);\n }\n \n static void diff_filespec_load_driver(struct diff_filespec *one)\n@@ -1615,6 +1674,13 @@ static void builtin_diff(const char *name_a,\n \tstruct userdiff_driver *textconv_one = NULL;\n \tstruct userdiff_driver *textconv_two = NULL;\n \tstruct strbuf header = STRBUF_INIT;\n+\tstruct strbuf *msgbuf;\n+\tchar *line_prefix = \"\";\n+\n+\tif (o->output_prefix) {\n+\t\tmsgbuf = o->output_prefix(o->file, o->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n \n \tif (DIFF_OPT_TST(o, SUBMODULE_LOG) &&\n \t\t\t(!one->mode || S_ISGITLINK(one->mode)) &&\n@@ -1649,22 +1715,22 @@ static void builtin_diff(const char *name_a,\n \tb_two = quote_two(b_prefix, name_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-\tstrbuf_addf(&header, \"%sdiff --git %s %s%s\\n\", set, a_one, b_two, reset);\n+\tstrbuf_addf(&header, \"%s%sdiff --git %s %s%s\\n\", line_prefix, set, a_one, b_two, reset);\n \tif (lbl[0][0] == '/') {\n \t\t/* /dev/null */\n-\t\tstrbuf_addf(&header, \"%snew file mode %06o%s\\n\", set, two->mode, reset);\n+\t\tstrbuf_addf(&header, \"%s%snew file mode %06o%s\\n\", line_prefix, set, two->mode, reset);\n \t\tif (xfrm_msg && xfrm_msg[0])\n \t\t\tstrbuf_addf(&header, \"%s%s%s\\n\", set, xfrm_msg, reset);\n \t}\n \telse if (lbl[1][0] == '/') {\n-\t\tstrbuf_addf(&header, \"%sdeleted file mode %06o%s\\n\", set, one->mode, reset);\n+\t\tstrbuf_addf(&header, \"%s%sdeleted file mode %06o%s\\n\", line_prefix, set, one->mode, reset);\n \t\tif (xfrm_msg && xfrm_msg[0])\n \t\t\tstrbuf_addf(&header, \"%s%s%s\\n\", set, xfrm_msg, reset);\n \t}\n \telse {\n \t\tif (one->mode != two->mode) {\n-\t\t\tstrbuf_addf(&header, \"%sold mode %06o%s\\n\", set, one->mode, reset);\n-\t\t\tstrbuf_addf(&header, \"%snew mode %06o%s\\n\", set, two->mode, reset);\n+\t\t\tstrbuf_addf(&header, \"%s%sold mode %06o%s\\n\", line_prefix, set, one->mode, reset);\n+\t\t\tstrbuf_addf(&header, \"%s%snew mode %06o%s\\n\", line_prefix, set, two->mode, reset);\n \t\t}\n \t\tif (xfrm_msg && xfrm_msg[0])\n \t\t\tstrbuf_addf(&header, \"%s%s%s\\n\", set, xfrm_msg, reset);\n@@ -1699,10 +1765,10 @@ static void builtin_diff(const char *name_a,\n \t\tfprintf(o->file, \"%s\", header.buf);\n \t\tstrbuf_reset(&header);\n \t\tif (DIFF_OPT_TST(o, BINARY))\n-\t\t\temit_binary_diff(o->file, &mf1, &mf2);\n+\t\t\temit_binary_diff(o->file, &mf1, &mf2, line_prefix);\n \t\telse\n-\t\t\tfprintf(o->file, \"Binary files %s and %s differ\\n\",\n-\t\t\t\tlbl[0], lbl[1]);\n+\t\t\tfprintf(o->file, \"%sBinary files %s and %s differ\\n\",\n+\t\t\t\tline_prefix, lbl[0], lbl[1]);\n \t\to->found_changes = 1;\n \t}\n \telse {\n@@ -2312,28 +2378,36 @@ static void fill_metainfo(struct strbuf *msg,\n \t\t\t  struct diff_options *o,\n \t\t\t  struct diff_filepair *p)\n {\n+\tstruct strbuf *msgbuf;\n+\tchar *line_prefix = \"\";\n+\n+\tif (o->output_prefix) {\n+\t\tmsgbuf = o->output_prefix(o->file, o->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n+\n \tstrbuf_init(msg, PATH_MAX * 2 + 300);\n \tswitch (p->status) {\n \tcase DIFF_STATUS_COPIED:\n-\t\tstrbuf_addf(msg, \"similarity index %d%%\", similarity_index(p));\n-\t\tstrbuf_addstr(msg, \"\\ncopy from \");\n+\t\tstrbuf_addf(msg, \"%ssimilarity index %d%%\", line_prefix, similarity_index(p));\n+\t\tstrbuf_addf(msg, \"\\n%scopy from \", line_prefix);\n \t\tquote_c_style(name, msg, NULL, 0);\n-\t\tstrbuf_addstr(msg, \"\\ncopy to \");\n+\t\tstrbuf_addf(msg, \"\\n%scopy to \", line_prefix);\n \t\tquote_c_style(other, msg, NULL, 0);\n \t\tstrbuf_addch(msg, '\\n');\n \t\tbreak;\n \tcase DIFF_STATUS_RENAMED:\n-\t\tstrbuf_addf(msg, \"similarity index %d%%\", similarity_index(p));\n-\t\tstrbuf_addstr(msg, \"\\nrename from \");\n+\t\tstrbuf_addf(msg, \"%ssimilarity index %d%%\", line_prefix, similarity_index(p));\n+\t\tstrbuf_addf(msg, \"\\n%srename from \", line_prefix);\n \t\tquote_c_style(name, msg, NULL, 0);\n-\t\tstrbuf_addstr(msg, \"\\nrename to \");\n+\t\tstrbuf_addf(msg, \"\\n%srename to \", line_prefix);\n \t\tquote_c_style(other, msg, NULL, 0);\n \t\tstrbuf_addch(msg, '\\n');\n \t\tbreak;\n \tcase DIFF_STATUS_MODIFIED:\n \t\tif (p->score) {\n-\t\t\tstrbuf_addf(msg, \"dissimilarity index %d%%\\n\",\n-\t\t\t\t    similarity_index(p));\n+\t\t\tstrbuf_addf(msg, \"%sdissimilarity index %d%%\\n\",\n+\t\t\t\t    line_prefix, similarity_index(p));\n \t\t\tbreak;\n \t\t}\n \t\t/* fallthru */\n@@ -2350,8 +2424,8 @@ static void fill_metainfo(struct strbuf *msg,\n \t\t\t    (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))\n \t\t\t\tabbrev = 40;\n \t\t}\n-\t\tstrbuf_addf(msg, \"index %.*s..%.*s\",\n-\t\t\t    abbrev, sha1_to_hex(one->sha1),\n+\t\tstrbuf_addf(msg, \"%sindex %.*s..%.*s\",\n+\t\t\t    line_prefix, abbrev, sha1_to_hex(one->sha1),\n \t\t\t    abbrev, sha1_to_hex(two->sha1));\n \t\tif (one->mode == two->mode)\n \t\t\tstrbuf_addf(msg, \" %06o\", one->mode);\n@@ -3030,6 +3104,11 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)\n {\n \tint line_termination = opt->line_termination;\n \tint inter_name_termination = line_termination ? '\\t' : '\\0';\n+\tif (opt->output_prefix) {\n+\t\tstruct strbuf *msg = NULL;\n+\t\tmsg = opt->output_prefix(opt->file, opt->output_prefix_data);\n+\t\tfprintf(opt->file, \"%s\", msg->buf);\n+\t}\n \n \tif (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {\n \t\tfprintf(opt->file, \":%06o %06o %s \", p->one->mode, p->two->mode,\n-- \n1.6.0.4\n"},{"id":"141952","messageId":"1274351138-11813-5-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-4-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 4/6 v2] Emit a whole line once a time.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:36Z","receivedAt":"2010-05-20T10:25:36Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Use a strbuf to compose the whole line, and then\ncall emit_line to output it once. This make output_prefix\ncallback works well.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n diff.c |   34 +++++++++++++++++++++++++++++-----\n 1 files changed, 29 insertions(+), 5 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 5d1b7b5..69c15a6 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -370,6 +370,18 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tconst char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);\n \tstatic const char atat[2] = { '@', '@' };\n \tconst char *cp, *ep;\n+\tstruct strbuf msgbuf = STRBUF_INIT;\n+\tint org_len = len;\n+\n+\t/*\n+\t * trailing \\r\\n\n+\t */\n+\tint i = 1;\n+\tfor (; i < 3; i++) {\n+\t\tif (line[len - i] == '\\r' || line[len - i] == '\\n') {\n+\t\t\tlen --;\n+\t\t}\n+\t}\n \n \t/*\n \t * As a hunk header must begin with \"@@ -<old>, +<new> @@\",\n@@ -384,17 +396,29 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tep += 2; /* skip over @@ */\n \n \t/* The hunk header in fraginfo color */\n-\temit_line(ecbdata->opt, frag, reset, line, ep - line);\n+\tstrbuf_add(&msgbuf, frag, strlen(frag));\n+\tstrbuf_add(&msgbuf, line, ep - line);\n+\tstrbuf_add(&msgbuf, reset, strlen(reset));\n \n \t/* blank before the func header */\n \tfor (cp = ep; ep - line < len; ep++)\n \t\tif (*ep != ' ' && *ep != '\\t')\n \t\t\tbreak;\n-\tif (ep != cp)\n-\t\temit_line(ecbdata->opt, plain, reset, cp, ep - cp);\n+\tif (ep != cp) {\n+\t\tstrbuf_add(&msgbuf, plain, strlen(plain));\n+\t\tstrbuf_add(&msgbuf, cp, ep - cp);\n+\t\tstrbuf_add(&msgbuf, reset, strlen(reset));\n+\t}\n+\n+\tif (ep < line + len) {\n+\t\tstrbuf_add(&msgbuf, func, strlen(func));\n+\t\tstrbuf_add(&msgbuf, ep, line + len - ep);\n+\t\tstrbuf_add(&msgbuf, reset, strlen(reset));\n+\t}\n \n-\tif (ep < line + len)\n-\t\temit_line(ecbdata->opt, func, reset, ep, line + len - ep);\n+\tstrbuf_add(&msgbuf, line + len, org_len - len);\n+\temit_line(ecbdata->opt, \"\", \"\", msgbuf.buf, msgbuf.len);\n+\tstrbuf_release(&msgbuf);\n }\n \n static struct diff_tempfile *claim_diff_tempfile(void) {\n-- \n1.6.0.4\n"},{"id":"141956","messageId":"1274351138-11813-6-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-5-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 5/6 v2] Register a callback for graph output.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:37Z","receivedAt":"2010-05-20T10:25:37Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"It will look better if the 'git log --graph' print\nthe graph pading lines before the diff output just\nlike what it does for commit message.\nAnd this patch leverage the new diff prefix callback\nfunction to achieve this.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n graph.c |   19 +++++++++++++++++++\n 1 files changed, 19 insertions(+), 0 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex e6bbcaa..6982d7a 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -211,6 +211,18 @@ struct git_graph {\n \tunsigned short default_column_color;\n };\n \n+static struct strbuf *diff_output_prefix_callback(FILE *file, void *data)\n+{\n+\tstruct git_graph *graph = data;\n+\tstatic struct strbuf msgbuf = STRBUF_INIT;\n+\n+\tassert(graph);\n+\n+\tstrbuf_reset(&msgbuf);\n+\tgraph_padding_line(graph, &msgbuf);\n+\treturn &msgbuf;\n+}\n+\n struct git_graph *graph_init(struct rev_info *opt)\n {\n \tstruct git_graph *graph = xmalloc(sizeof(struct git_graph));\n@@ -244,6 +256,13 @@ struct git_graph *graph_init(struct rev_info *opt)\n \tgraph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);\n \tgraph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);\n \n+\t/*\n+\t * The diff output prefix callback, with this we can make\n+\t * all the diff output to align with the graph lines.\n+\t */\n+\topt->diffopt.output_prefix = diff_output_prefix_callback;\n+\topt->diffopt.output_prefix_data = graph;\n+\n \treturn graph;\n }\n \n-- \n1.6.0.4\n"},{"id":"141955","messageId":"1274351138-11813-7-git-send-email-struggleyb.nku@gmail.com","threadId":"23855","inReplyTo":"1274351138-11813-6-git-send-email-struggleyb.nku@gmail.com","subject":"[PATCH 6/6 v2] Make --color-words work well with --graph.","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-20T10:25:38Z","receivedAt":"2010-05-20T10:25:38Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"'--color-words' algorithm can be described as:\n\n1. collect a the minus/plus lines of a diff hunk, divided into minus-lines and plus-lines;\n2. break both minus-lines and plus-lines into words and place them into two\n   mmfile_t with one word for each line;\n3. use xdiff to run diff on the two mmfile_t to get the words level diff;\n\nAnd for the common parts of the both file, we output the plus side text.\ndiff_words->current_plus is used to trace the current position of the plus file\nwhich printed. diff_words->last_minus is used to trace the last minus word\nprinted.\n\nFor '--graph' to work with '--color-words', we need to output the graph prefix\non each line of color words output. Generally, there are two conditions on\nwhich we should output the prefix.\n1. diff_words->last_minus == 0 && diff_words->current_plus == diff_words->plus.text.ptr\n   that is: the plus text must start as a new line, and if there is no minus\n   word printed, a graph prefix must be printed.\n2. diff_words->current_plus > diff_words->plus.text.ptr && *(diff_words->current_plus - 1) == '\\n'\n   that is: a graph prefix must be printed following a '\\n'\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n color.c |   21 +++++++++----\n color.h |    3 +-\n diff.c  |   98 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------\n 3 files changed, 97 insertions(+), 25 deletions(-)\n\ndiff --git a/color.c b/color.c\nindex bcf4e2c..e034e3b 100644\n--- a/color.c\n+++ b/color.c\n@@ -218,22 +218,31 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)\n  * Returns 0 on success.\n  */\n int color_fwrite_lines(FILE *fp, const char *color,\n-\t\tsize_t count, const char *buf)\n+\t\tsize_t count, const char *buf, const char *prefix)\n {\n-\tif (!*color)\n-\t\treturn fwrite(buf, count, 1, fp) != 1;\n+\tint printing = 0;\n+\tconst char *reset = GIT_COLOR_RESET;\n+\n+\tif (strlen(color) == 0)\n+\t\treset = \"\";\n+\n \twhile (count) {\n \t\tchar *p = memchr(buf, '\\n', count);\n-\t\tif (p != buf && (fputs(color, fp) < 0 ||\n+\t\tif (printing)\n+\t\t\tfputs(prefix, fp);\n+\t\tif (p != buf) {\n+\t\t\tif (fputs(color, fp) < 0 ||\n \t\t\t\tfwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n-\t\t\t\tfputs(GIT_COLOR_RESET, fp) < 0))\n-\t\t\treturn -1;\n+\t\t\t\tfputs(reset, fp) < 0)\n+\t\t\t\treturn -1;\n+\t\t}\n \t\tif (!p)\n \t\t\treturn 0;\n \t\tif (fputc('\\n', fp) < 0)\n \t\t\treturn -1;\n \t\tcount -= p + 1 - buf;\n \t\tbuf = p + 1;\n+\t\tprinting = 1;\n \t}\n \treturn 0;\n }\ndiff --git a/color.h b/color.h\nindex 5c264b0..55fea4f 100644\n--- a/color.h\n+++ b/color.h\n@@ -61,6 +61,7 @@ __attribute__((format (printf, 3, 4)))\n int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);\n __attribute__((format (printf, 3, 4)))\n int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);\n-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);\n+int color_fwrite_lines(FILE *fp, const char *color, size_t count,\n+\tconst char *buf, const char *prefix);\n \n #endif /* COLOR_H */\ndiff --git a/diff.c b/diff.c\nindex 69c15a6..3a1e05a 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -603,7 +603,8 @@ static void diff_words_append(char *line, unsigned long len,\n struct diff_words_data {\n \tstruct diff_words_buffer minus, plus;\n \tconst char *current_plus;\n-\tFILE *file;\n+\tint last_minus;\n+\tstruct diff_options *opt;\n \tregex_t *word_regex;\n };\n \n@@ -612,11 +613,20 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n \tstruct diff_words_data *diff_words = priv;\n \tint minus_first, minus_len, plus_first, plus_len;\n \tconst char *minus_begin, *minus_end, *plus_begin, *plus_end;\n+\tstruct diff_options *opt = diff_words->opt;\n+\tstruct strbuf *msgbuf;\n+\tchar *line_prefix = \"\";\n \n \tif (line[0] != '@' || parse_hunk_header(line, len,\n \t\t\t&minus_first, &minus_len, &plus_first, &plus_len))\n \t\treturn;\n \n+\tassert(opt);\n+\tif (opt->output_prefix) {\n+\t\tmsgbuf = opt->output_prefix(opt->file, opt->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n+\n \t/* POSIX requires that first be decremented by one if len == 0... */\n \tif (minus_len) {\n \t\tminus_begin = diff_words->minus.orig[minus_first].begin;\n@@ -632,20 +642,54 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n \t} else\n \t\tplus_begin = plus_end = diff_words->plus.orig[plus_first].end;\n \n-\tif (diff_words->current_plus != plus_begin)\n-\t\tfwrite(diff_words->current_plus,\n-\t\t\t\tplus_begin - diff_words->current_plus, 1,\n-\t\t\t\tdiff_words->file);\n-\tif (minus_begin != minus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n+\t/*\n+\t * '--color-words' algorithm can be described as:\n+\t *\n+\t * 1. collect a the minus/plus lines of a diff hunk, divided into minus-lines and plus-lines;\n+\t * 2. break both minus-lines and plus-lines into words and place them into two\n+\t *    mmfile_t with one word for each line;\n+\t * 3. use xdiff to run diff on the two mmfile_t to get the words level diff;\n+\t *\n+\t * And for the common parts of the both file, we output the plus side text.\n+\t * diff_words->current_plus is used to trace the current position of the plus file\n+\t * which printed. diff_words->last_minus is used to trace the last minus word\n+\t * printed.\n+\t *\n+\t * For '--graph' to work with '--color-words', we need to output the graph prefix\n+\t * on each line of color words output. Generally, there are two conditions on\n+\t * which we should output the prefix.\n+\t * 1. diff_words->last_minus == 0 && diff_words->current_plus == diff_words->plus.text.ptr\n+\t *    that is: the plus text must start as a new line, and if there is no minus\n+\t *    word printed, a graph prefix must be printed.\n+\t * 2. diff_words->current_plus > diff_words->plus.text.ptr && *(diff_words->current_plus - 1) == '\\n'\n+\t *    that is: a graph prefix must be printed following a '\\n'\n+\t */\n+\tif ((diff_words->last_minus == 0 &&\n+\t\tdiff_words->current_plus == diff_words->plus.text.ptr) ||\n+\t\t(diff_words->current_plus > diff_words->plus.text.ptr &&\n+\t\t*(diff_words->current_plus - 1) == '\\n')) {\n+\t\tfputs(line_prefix, diff_words->opt->file);\n+\t}\n+\tif (diff_words->current_plus != plus_begin) {\n+\t\tcolor_fwrite_lines(diff_words->opt->file, \"\",\n+\t\t\t\tplus_begin - diff_words->current_plus,\n+\t\t\t\tdiff_words->current_plus, line_prefix);\n+\t\tif (*(plus_begin - 1) == '\\n')\n+\t\t\tfputs(line_prefix, diff_words->opt->file);\n+\t}\n+\tif (minus_begin != minus_end) {\n+\t\tcolor_fwrite_lines(diff_words->opt->file,\n \t\t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n-\t\t\t\tminus_end - minus_begin, minus_begin);\n-\tif (plus_begin != plus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n+\t\t\t\tminus_end - minus_begin, minus_begin, line_prefix);\n+\t}\n+\tif (plus_begin != plus_end) {\n+\t\tcolor_fwrite_lines(diff_words->opt->file,\n \t\t\t\tdiff_get_color(1, DIFF_FILE_NEW),\n-\t\t\t\tplus_end - plus_begin, plus_begin);\n+\t\t\t\tplus_end - plus_begin, plus_begin, line_prefix);\n+\t}\n \n \tdiff_words->current_plus = plus_end;\n+\tdiff_words->last_minus = minus_first;\n }\n \n /* This function starts looking at *begin, and returns 0 iff a word was found. */\n@@ -724,17 +768,29 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \txpparam_t xpp;\n \txdemitconf_t xecfg;\n \tmmfile_t minus, plus;\n+\tstruct diff_options *opt = diff_words->opt;\n+\tstruct strbuf *msgbuf;\n+\tchar *line_prefix = \"\";\n+\n+\tassert(opt);\n+\tif (opt->output_prefix) {\n+\t\tmsgbuf = opt->output_prefix(opt->file, opt->output_prefix_data);\n+\t\tline_prefix = msgbuf->buf;\n+\t}\n \n \t/* special case: only removal */\n \tif (!diff_words->plus.text.size) {\n-\t\tcolor_fwrite_lines(diff_words->file,\n+\t\tfputs(line_prefix, diff_words->opt->file);\n+\t\tcolor_fwrite_lines(diff_words->opt->file,\n \t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n-\t\t\tdiff_words->minus.text.size, diff_words->minus.text.ptr);\n+\t\t\tdiff_words->minus.text.size,\n+\t\t\tdiff_words->minus.text.ptr, line_prefix);\n \t\tdiff_words->minus.text.size = 0;\n \t\treturn;\n \t}\n \n \tdiff_words->current_plus = diff_words->plus.text.ptr;\n+\tdiff_words->last_minus = 0;\n \n \tmemset(&xpp, 0, sizeof(xpp));\n \tmemset(&xecfg, 0, sizeof(xecfg));\n@@ -748,11 +804,17 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \tfree(minus.ptr);\n \tfree(plus.ptr);\n \tif (diff_words->current_plus != diff_words->plus.text.ptr +\n-\t\t\tdiff_words->plus.text.size)\n-\t\tfwrite(diff_words->current_plus,\n+\t\t\tdiff_words->plus.text.size) {\n+\t\tif ((diff_words->current_plus == diff_words->plus.text.ptr &&\n+\t\t\tdiff_words->last_minus == 0) ||\n+\t\t\t(diff_words->current_plus > diff_words->plus.text.ptr &&\n+\t\t\t*(diff_words->current_plus - 1) == '\\n'))\n+\t\t\tfputs(line_prefix, diff_words->opt->file);\n+\t\tcolor_fwrite_lines(diff_words->opt->file, \"\",\n \t\t\tdiff_words->plus.text.ptr + diff_words->plus.text.size\n-\t\t\t- diff_words->current_plus, 1,\n-\t\t\tdiff_words->file);\n+\t\t\t- diff_words->current_plus, diff_words->current_plus,\n+\t\t\tline_prefix);\n+\t}\n \tdiff_words->minus.text.size = diff_words->plus.text.size = 0;\n }\n \n@@ -1841,7 +1903,7 @@ static void builtin_diff(const char *name_a,\n \t\tif (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {\n \t\t\tecbdata.diff_words =\n \t\t\t\txcalloc(1, sizeof(struct diff_words_data));\n-\t\t\tecbdata.diff_words->file = o->file;\n+\t\t\tecbdata.diff_words->opt = o;\n \t\t\tif (!o->word_regex)\n \t\t\t\to->word_regex = userdiff_word_regex(one);\n \t\t\tif (!o->word_regex)\n-- \n1.6.0.4\n"},{"id":"141961","messageId":"20100520123650.GA7665@sigill.intra.peff.net","threadId":"23855","inReplyTo":"1274351138-11813-1-git-send-email-struggleyb.nku@gmail.com","subject":"Re: [PATCH 0/6 v2] Make git log --graph looks better with -p and other diff options","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-05-20T12:36:50Z","receivedAt":"2010-05-20T12:36:50Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 20, 2010 at 03:25:32AM -0700, Bo Yang wrote:\n\n> When we run git log --graph with any other diff output function, such as '-p','--check','--numstat' or other diff optoins, the diff output area have no graph lines ahead of it. And this make the text graph looks strange.\n> The following 7 patches try to deal with this, and put the text graph\n> columns before all the diff output lines.\n\nThis is very cool. \"git log --graph --oneline --stat\" is interesting to\nsee. It seems to work fine with \"-p\", \"--stat\", and \"--raw\". But try:\n\n  git log --graph --oneline --summary 212f0ba\n\nSummary lines for some reason don't get properly indented or prefixed\nwith branch lines.\n\n-Peff\n"},{"id":"141962","messageId":"20100520132425.GA5504@coredump.intra.peff.net","threadId":"23855","inReplyTo":"20100520123650.GA7665@sigill.intra.peff.net","subject":"Re: [PATCH 0/6 v2] Make git log --graph looks better with -p and other diff options","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-05-20T13:24:25Z","receivedAt":"2010-05-20T13:24:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 20, 2010 at 08:36:50AM -0400, Jeff King wrote:\n\n>   git log --graph --oneline --summary 212f0ba\n> \n> Summary lines for some reason don't get properly indented or prefixed\n> with branch lines.\n\nThis is my naive attempt at fixing it just by copying your other\nchanges. However, it seems to sometimes put several copies of the prefix\nin front of summary lines (and sometimes the next commit line!?). So\nclearly there is something about the output prefix code that I don't\nquite understand.\n\nMaybe this is helpful, and maybe not. :)\n\ndiff --git a/diff.c b/diff.c\nindex 3a1e05a..656f40b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3460,27 +3460,41 @@ static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_fil\n \tshow_mode_change(file, p, 0);\n }\n \n-static void diff_summary(FILE *file, struct diff_filepair *p)\n+static void diff_summary(struct diff_options *opt, struct diff_filepair *p)\n {\n+\tFILE *file = opt->file;\n+\tchar *line_prefix = \"\";\n+\n+\tif (opt->output_prefix) {\n+\t\tstruct strbuf *buf = opt->output_prefix(file, opt->output_prefix_data);\n+\t\tline_prefix = buf->buf;\n+\t}\n+\n \tswitch(p->status) {\n \tcase DIFF_STATUS_DELETED:\n+\t\tfputs(line_prefix, file);\n \t\tshow_file_mode_name(file, \"delete\", p->one);\n \t\tbreak;\n \tcase DIFF_STATUS_ADDED:\n+\t\tfputs(line_prefix, file);\n \t\tshow_file_mode_name(file, \"create\", p->two);\n \t\tbreak;\n \tcase DIFF_STATUS_COPIED:\n+\t\tfputs(line_prefix, file);\n \t\tshow_rename_copy(file, \"copy\", p);\n \t\tbreak;\n \tcase DIFF_STATUS_RENAMED:\n+\t\tfputs(line_prefix, file);\n \t\tshow_rename_copy(file, \"rename\", p);\n \t\tbreak;\n \tdefault:\n \t\tif (p->score) {\n+\t\t\tfputs(line_prefix, file);\n \t\t\tfputs(\" rewrite \", file);\n \t\t\twrite_name_quoted(p->two->path, file, ' ');\n \t\t\tfprintf(file, \"(%d%%)\\n\", similarity_index(p));\n \t\t}\n+\t\tfputs(line_prefix, file);\n \t\tshow_mode_change(file, p, !p->score);\n \t\tbreak;\n \t}\n@@ -3692,7 +3706,7 @@ void diff_flush(struct diff_options *options)\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\n \t\tfor (i = 0; i < q->nr; i++)\n-\t\t\tdiff_summary(options->file, q->queue[i]);\n+\t\t\tdiff_summary(options, q->queue[i]);\n \t\tseparator++;\n \t}\n \n"},{"id":"141994","messageId":"AANLkTim5R94ZmIfujvhky5zFQYT5MIgJJ58tVnjvQkHF@mail.gmail.com","threadId":"23855","inReplyTo":"20100520132425.GA5504@coredump.intra.peff.net","subject":"Re: [PATCH 0/6 v2] Make git log --graph looks better with -p and other diff options","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-05-21T01:49:20Z","receivedAt":"2010-05-21T01:49:20Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Hi Jeff,\nOn Thu, May 20, 2010 at 6:24 AM, Jeff King <peff@peff.net> wrote:\n> On Thu, May 20, 2010 at 08:36:50AM -0400, Jeff King wrote:\n>\n>>   git log --graph --oneline --summary 212f0ba\n>>\n>> Summary lines for some reason don't get properly indented or prefixed\n>> with branch lines.\n>\n> This is my naive attempt at fixing it just by copying your other\n> changes. However, it seems to sometimes put several copies of the prefix\n> in front of summary lines (and sometimes the next commit line!?). So\n> clearly there is something about the output prefix code that I don't\n> quite understand.\n\nThanks a lot for reporting this bug. I would not to ignore the\n--summary option, :) Thanks again!\n\n> Maybe this is helpful, and maybe not. :)\n>\n> diff --git a/diff.c b/diff.c\n> index 3a1e05a..656f40b 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -3460,27 +3460,41 @@ static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_fil\n>        show_mode_change(file, p, 0);\n>  }\n>\n> -static void diff_summary(FILE *file, struct diff_filepair *p)\n> +static void diff_summary(struct diff_options *opt, struct diff_filepair *p)\n>  {\n> +       FILE *file = opt->file;\n> +       char *line_prefix = \"\";\n> +\n> +       if (opt->output_prefix) {\n> +               struct strbuf *buf = opt->output_prefix(file, opt->output_prefix_data);\n> +               line_prefix = buf->buf;\n> +       }\n> +\n>        switch(p->status) {\n>        case DIFF_STATUS_DELETED:\n> +               fputs(line_prefix, file);\n>                show_file_mode_name(file, \"delete\", p->one);\n>                break;\n>        case DIFF_STATUS_ADDED:\n> +               fputs(line_prefix, file);\n>                show_file_mode_name(file, \"create\", p->two);\n>                break;\n>        case DIFF_STATUS_COPIED:\n> +               fputs(line_prefix, file);\n>                show_rename_copy(file, \"copy\", p);\n>                break;\n>        case DIFF_STATUS_RENAMED:\n> +               fputs(line_prefix, file);\n>                show_rename_copy(file, \"rename\", p);\n>                break;\n>        default:\n>                if (p->score) {\n> +                       fputs(line_prefix, file);\n>                        fputs(\" rewrite \", file);\n>                        write_name_quoted(p->two->path, file, ' ');\n>                        fprintf(file, \"(%d%%)\\n\", similarity_index(p));\n>                }\n> +               fputs(line_prefix, file);\n>                show_mode_change(file, p, !p->score);\n>                break;\n>        }\n> @@ -3692,7 +3706,7 @@ void diff_flush(struct diff_options *options)\n>\n>        if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\n>                for (i = 0; i < q->nr; i++)\n> -                       diff_summary(options->file, q->queue[i]);\n> +                       diff_summary(options, q->queue[i]);\n>                separator++;\n>        }\n\nAh, thanks for patch and of course it helps! I have make --summary\nworks well and make some other changes for the output_prefix call, and\nI will resend the series later. Thanks!\n\nRegards!\nBo\n-- \nMy blog: http://blog.morebits.org\n"}]}