{"thread":{"id":"42648","subject":"[PATCH 0/5] Let log-tree and friends respect diffopt's `file` field","startedAt":"2016-06-18T10:03:57Z","lastAt":"2016-06-26T06:56:19Z","messageCount":67,"participants":["Johannes Schindelin","Eric Sunshine","Junio C Hamano","Paul Tan"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"289563","messageId":"cover.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":null,"subject":"[PATCH 0/5] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:03:44Z","receivedAt":"2016-06-18T10:03:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The idea is to allow callers to redirect log-tree's output to a file\nwithout having to freopen() stdout (which would modify global state,\na big no-no-no for library functions).\n\nI reviewed log-tree.c, graph.c, line-log.c, builtin/shortlog.c and\nbuiltin/log.c line by line to ensure that all calls that assumed stdout\npreviously now use the `file` field instead, of course. I would\nwelcome additional eyes to go over the code to confirm that I did not\nmiss anything.\n\nThis patch series ends up removing the freopen() call in the\nformat-patch command, but that is only a by-product. The ulterior motive\nbehind this series is to allow the sequencer to write a `patch` file as\npart of my endeavor to move large chunks of rebase -i into a builtin.\n\nSpeaking of said endeavor: it is going a lot slower than I would like,\nbut I really need rebase -i to be robust. To that end, I not only review\nand re-review my patches frequently, I also use a cross-validation\ntechnique inspired by my original ll-merge validation as well as\nGitHub's Scientist library: I taught rebase -i to run every interactive\nrebase twice, once with the original shell script's code, and once with\nthe builtin rebase--helper, and then to verify that the end result is as\nsimilar as can be expected (the commit dates will differ most of the\ntime, of course).\n\nIt is a bit tedious, of course, because I have to resolve every merge\nconflict twice, I have to reword everything twice (identically!), and I\nhave to wait longer for the rebase to finish. It is worth it, though,\nbecause I really need rebase -i to be robust, as it is the center piece\nof almost all of my workflows.\n\nI organized the patch series into a thicket of branches, to make it\neasier to review them; In addition to this here patch series, I plan to\nsubmit 11 separate, partially interdependent series, resubmit another\none, and I already submitted a couple of patches earlier that were\nintegrated or replaced by superior solutions (thanks, Peff!). Skipping\nthe temporary patches (e.g. for cross-validation), that leaves me with\n99 patches to submit in total (sing with me: \"99 patches of code to\nsubmit, 99 patches of code, ...\").\n\nI was curious just how much these patches could accelerate rebase -i, so\nI disabled the cross-validation temprarily and let Travis measure the\nperformance improvements via the t/perf test that was already merged to\n`master`. Look for \"3404.2\" in this build's logs:\nhttps://travis-ci.org/git/git/builds/138277774\n\nIt looks like a 3x speedup on Linux, a 4x speedup on MacOSX and my local\nmeasurements suggest a >5x speed-up on Windows.\n\n\nJohannes Schindelin (5):\n  log-tree: respect diffopt's configured output file stream\n  line-log: respect diffopt's configured output file stream\n  graph: respect the diffopt.file setting\n  shortlog: support outputting to streams other than stdout\n  format-patch: avoid freopen()\n\n builtin/log.c      | 71 +++++++++++++++++++++++++++---------------------------\n builtin/shortlog.c | 13 ++++++----\n graph.c            | 30 +++++++++++++----------\n line-log.c         | 34 +++++++++++++-------------\n log-tree.c         | 65 +++++++++++++++++++++++++------------------------\n shortlog.h         |  1 +\n 6 files changed, 111 insertions(+), 103 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/diffopt.file-v1\n-- \n2.9.0.118.gce770ba.dirty\n\nbase-commit: 05219a1276341e72d8082d76b7f5ed394b7437a4\n"},{"id":"289564","messageId":"babf95df5f610feb6c2ae7f2ed3cff98bab47fe2.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH 1/5] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:03:49Z","receivedAt":"2016-06-18T10:04:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff options already know how to print the output anywhere else\nthan stdout. The same is needed for log output in general, e.g.\nwhen writing patches to files in `git format-patch`. Let's allow\nusers to use log_tree_commit() *without* changing global state via\nfreopen().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n log-tree.c | 65 +++++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 33 insertions(+), 32 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 78a5381..968428a 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -159,12 +159,12 @@ void load_ref_decorations(int flags)\n \t}\n }\n \n-static void show_parents(struct commit *commit, int abbrev)\n+static void show_parents(struct commit *commit, int abbrev, FILE *file)\n {\n \tstruct commit_list *p;\n \tfor (p = commit->parents; p ; p = p->next) {\n \t\tstruct commit *parent = p->item;\n-\t\tprintf(\" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n+\t\tfprintf(file, \" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -172,7 +172,7 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n {\n \tstruct commit_list *p = lookup_decoration(&opt->children, &commit->object);\n \tfor ( ; p; p = p->next) {\n-\t\tprintf(\" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n+\t\tfprintf(opt->diffopt.file, \" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -286,11 +286,11 @@ void show_decorations(struct rev_info *opt, struct commit *commit)\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\t\tfprintf(opt->diffopt.file, \"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n \tformat_decorations(&sb, commit, opt->diffopt.use_color);\n-\tfputs(sb.buf, stdout);\n+\tfputs(sb.buf, opt->diffopt.file);\n \tstrbuf_release(&sb);\n }\n \n@@ -364,18 +364,18 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\tsubject = \"Subject: \";\n \t}\n \n-\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n+\tfprintf(opt->diffopt.file, \"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n \tgraph_show_oneline(opt->graph);\n \tif (opt->message_id) {\n-\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n+\t\tfprintf(opt->diffopt.file, \"Message-Id: <%s>\\n\", opt->message_id);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \tif (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {\n \t\tint i, n;\n \t\tn = opt->ref_message_ids->nr;\n-\t\tprintf(\"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n+\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n \t\tfor (i = 0; i < n; i++)\n-\t\t\tprintf(\"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n+\t\t\tfprintf(opt->diffopt.file, \"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n \t\t\t       opt->ref_message_ids->items[i].string);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n@@ -432,7 +432,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)\n \treset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);\n \twhile (*bol) {\n \t\teol = strchrnul(bol, '\\n');\n-\t\tprintf(\"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n+\t\tfprintf(opt->diffopt.file, \"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n \t\t       *eol ? \"\\n\" : \"\");\n \t\tgraph_show_oneline(opt->graph);\n \t\tbol = (*eol) ? (eol + 1) : eol;\n@@ -553,17 +553,17 @@ void show_log(struct rev_info *opt)\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n-\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);\n+\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_remainder(opt->graph);\n \t\t}\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -589,7 +589,7 @@ void show_log(struct rev_info *opt)\n \t\tif (opt->diffopt.line_termination == '\\n' &&\n \t\t    !opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \topt->shown_one = 1;\n \n@@ -607,28 +607,28 @@ void show_log(struct rev_info *opt)\n \t\tlog_write_email_headers(opt, commit, &ctx.subject, &extra_headers,\n \t\t\t\t\t&ctx.need_8bit_cte);\n \t} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);\n \t\tif (opt->commit_format != CMIT_FMT_ONELINE)\n-\t\t\tfputs(\"commit \", stdout);\n+\t\t\tfputs(\"commit \", opt->diffopt.file);\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n \t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),\n-\t\t      stdout);\n+\t\t      opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tif (parent)\n-\t\t\tprintf(\" (from %s)\",\n+\t\t\tfprintf(opt->diffopt.file, \" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.oid.hash,\n \t\t\t\t\t\t  abbrev_commit));\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n-\t\t\tputchar(' ');\n+\t\t\tfputc(' ', opt->diffopt.file);\n \t\t} else {\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_oneline(opt->graph);\n \t\t}\n \t\tif (opt->reflog_info) {\n@@ -702,7 +702,7 @@ void show_log(struct rev_info *opt)\n \t}\n \n \tif (opt->show_log_size) {\n-\t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n+\t\tfprintf(opt->diffopt.file, \"log size %i\\n\", (int)msgbuf.len);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \n@@ -718,11 +718,11 @@ void show_log(struct rev_info *opt)\n \tif (opt->graph)\n \t\tgraph_show_commit_msg(opt->graph, &msgbuf);\n \telse\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, opt->diffopt.file);\n \tif (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {\n \t\tif (!opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \n \tstrbuf_release(&msgbuf);\n@@ -759,7 +759,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t\tstruct strbuf *msg = NULL;\n \t\t\t\tmsg = opt->diffopt.output_prefix(&opt->diffopt,\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\tfwrite(msg->buf, msg->len, 1, opt->diffopt.file);\n \t\t\t}\n \n \t\t\t/*\n@@ -774,8 +774,8 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t */\n \t\t\tif (!opt->shown_dashes &&\n \t\t\t    (pch & opt->diffopt.output_format) == pch)\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n+\t\t\t\tfprintf(opt->diffopt.file, \"---\");\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n@@ -872,7 +872,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\treturn line_log_print(opt, commit);\n \n \tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n@@ -880,8 +880,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\tshown = 1;\n \t}\n \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n-\tmaybe_flush_or_die(stdout, \"stdout\");\n+\tif (opt->diffopt.file == stdout)\n+\t\tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\n }\n-- \n2.9.0.118.gce770ba.dirty\n\n\n"},{"id":"289565","messageId":"f2b1795827bcb055028332cfdf04d996d8b6e0e6.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH 3/5] graph: respect the diffopt.file setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:04:01Z","receivedAt":"2016-06-18T10:04:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the caller overrides diffopt.file (which defaults to stdout),\nthe diff machinery already redirects its output, and the graph display\nshould also write to that file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n graph.c | 30 +++++++++++++++++-------------\n 1 file changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex 1350bdd..8ae56bc 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -17,8 +17,8 @@\n static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);\n \n /*\n- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n- * first will be prefixed with the graph output.\n+ * Print a strbuf.  If the graph is non-NULL, all lines but the first will be\n+ * prefixed with the graph output.\n  *\n  * If the strbuf ends with a newline, the output will end after this\n  * newline.  A new graph line will not be printed after the final newline.\n@@ -1193,9 +1193,10 @@ void graph_show_commit(struct git_graph *graph)\n \n \twhile (!shown_commit_line && !graph_is_commit_finished(graph)) {\n \t\tshown_commit_line = graph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tif (!shown_commit_line)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t}\n \n@@ -1210,7 +1211,7 @@ void graph_show_oneline(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_next_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1222,7 +1223,7 @@ void graph_show_padding(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_padding_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1239,12 +1240,13 @@ int graph_show_remainder(struct git_graph *graph)\n \n \tfor (;;) {\n \t\tgraph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t\tshown = 1;\n \n \t\tif (!graph_is_commit_finished(graph))\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t\telse\n \t\t\tbreak;\n \t}\n@@ -1259,7 +1261,8 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \tchar *p;\n \n \tif (!graph) {\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1277,7 +1280,7 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \t\t} else {\n \t\t\tlen = (sb->buf + sb->len) - p;\n \t\t}\n-\t\tfwrite(p, sizeof(char), len, stdout);\n+\t\tfwrite(p, sizeof(char), len, graph->revs->diffopt.file);\n \t\tif (next_p && *next_p != '\\0')\n \t\t\tgraph_show_oneline(graph);\n \t\tp = next_p;\n@@ -1297,7 +1300,8 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * CMIT_FMT_USERFORMAT are already missing a terminating\n \t\t * newline.  All of the other formats should have it.\n \t\t */\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1318,7 +1322,7 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * new line.\n \t\t */\n \t\tif (!newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \n \t\tgraph_show_remainder(graph);\n \n@@ -1326,6 +1330,6 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * If sb ends with a newline, our output should too.\n \t\t */\n \t\tif (newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t}\n }\n-- \n2.9.0.118.gce770ba.dirty\n\n\n"},{"id":"289566","messageId":"c9990c2ac84f43e76afc52e5cca5d8876715e004.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH 4/5] shortlog: support outputting to streams other than stdout","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:04:06Z","receivedAt":"2016-06-18T10:04:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to avoid freopen() in `git format-patch`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/shortlog.c | 13 ++++++++-----\n shortlog.h         |  1 +\n 2 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex bfc082e..4c68ba7 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -229,6 +229,7 @@ void shortlog_init(struct shortlog *log)\n \tlog->wrap = DEFAULT_WRAPLEN;\n \tlog->in1 = DEFAULT_INDENT1;\n \tlog->in2 = DEFAULT_INDENT2;\n+\tlog->file = stdout;\n }\n \n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n@@ -310,22 +311,24 @@ void shortlog_output(struct shortlog *log)\n \tfor (i = 0; i < log->list.nr; i++) {\n \t\tconst struct string_list_item *item = &log->list.items[i];\n \t\tif (log->summary) {\n-\t\t\tprintf(\"%6d\\t%s\\n\", (int)UTIL_TO_INT(item), item->string);\n+\t\t\tfprintf(log->file, \"%6d\\t%s\\n\",\n+\t\t\t\t(int)UTIL_TO_INT(item), item->string);\n \t\t} else {\n \t\t\tstruct string_list *onelines = item->util;\n-\t\t\tprintf(\"%s (%d):\\n\", item->string, onelines->nr);\n+\t\t\tfprintf(log->file, \"%s (%d):\\n\",\n+\t\t\t\titem->string, onelines->nr);\n \t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n \t\t\t\tconst char *msg = onelines->items[j].string;\n \n \t\t\t\tif (log->wrap_lines) {\n \t\t\t\t\tstrbuf_reset(&sb);\n \t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n-\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n+\t\t\t\t\tfwrite(sb.buf, sb.len, 1, log->file);\n \t\t\t\t}\n \t\t\t\telse\n-\t\t\t\t\tprintf(\"      %s\\n\", msg);\n+\t\t\t\t\tfprintf(log->file, \"      %s\\n\", msg);\n \t\t\t}\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', log->file);\n \t\t\tonelines->strdup_strings = 1;\n \t\t\tstring_list_clear(onelines, 0);\n \t\t\tfree(onelines);\ndiff --git a/shortlog.h b/shortlog.h\nindex de4f86f..5a326c6 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -17,6 +17,7 @@ struct shortlog {\n \tchar *common_repo_prefix;\n \tint email;\n \tstruct string_list mailmap;\n+\tFILE *file;\n };\n \n void shortlog_init(struct shortlog *log);\n-- \n2.9.0.118.gce770ba.dirty\n\n\n"},{"id":"289567","messageId":"de218a6cc529b3f5c33dc4b8282f16fd8a5329a8.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:04:12Z","receivedAt":"2016-06-18T10:04:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We just taught the relevant functions to respect the diffopt.file field,\nto allow writing somewhere else than stdout. Let's make use of it.\n\nTechnically, we do not need to avoid that call in a builtin: we assume\nthat builtins (as opposed to library functions) are stand-alone programs\nthat may do with their (global) state. Yet, we want to be able to reuse\nthat code in properly lib-ified code, e.g. when converting scripts into\nbuiltins.\n\nFurther, while we did not *have* to touch the cmd_show() and cmd_cherry()\ncode paths (because they do not want to write anywhere but stdout as of\nyet), it just makes sense to be consistent, making it easier and safer to\nmove the code later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 71 +++++++++++++++++++++++++++++------------------------------\n 1 file changed, 35 insertions(+), 36 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 099f4f7..5a889d5 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -236,7 +236,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)\n \t\tif (rev->commit_format != CMIT_FMT_ONELINE)\n \t\t\tputchar(rev->diffopt.line_termination);\n \t}\n-\tprintf(_(\"Final output: %d %s\\n\"), nr, stage);\n+\tfprintf(rev->diffopt.file, _(\"Final output: %d %s\\n\"), nr, stage);\n }\n \n static struct itimerval early_output_timer;\n@@ -445,7 +445,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)\n \tpp.fmt = rev->commit_format;\n \tpp.date_mode = rev->date_mode;\n \tpp_user_info(&pp, \"Tagger\", &out, buf, get_log_output_encoding());\n-\tprintf(\"%s\", out.buf);\n+\tfprintf(rev->diffopt.file, \"%s\", out.buf);\n \tstrbuf_release(&out);\n }\n \n@@ -456,7 +456,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con\n \tchar *buf;\n \tunsigned long size;\n \n-\tfflush(stdout);\n+\tfflush(rev->diffopt.file);\n \tif (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||\n \t    !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))\n \t\treturn stream_blob_to_fd(1, sha1, NULL, 0);\n@@ -496,7 +496,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)\n \t}\n \n \tif (offset < size)\n-\t\tfwrite(buf + offset, size - offset, 1, stdout);\n+\t\tfwrite(buf + offset, size - offset, 1, rev->diffopt.file);\n \tfree(buf);\n \treturn 0;\n }\n@@ -505,7 +505,8 @@ static int show_tree_object(const unsigned char *sha1,\n \t\tstruct strbuf *base,\n \t\tconst char *pathname, unsigned mode, int stage, void *context)\n {\n-\tprintf(\"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tFILE *file = context;\n+\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n \treturn 0;\n }\n \n@@ -565,7 +566,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stag %s%s\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stag %s%s\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tt->tag,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n@@ -584,12 +585,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\tcase OBJ_TREE:\n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stree %s%s\\n\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stree %s%s\\n\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive((struct tree *)o, \"\", 0, 0, &match_all,\n-\t\t\t\t\tshow_tree_object, NULL);\n+\t\t\t\t\tshow_tree_object, rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\n@@ -795,11 +796,10 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \treturn git_log_config(var, value, cb);\n }\n \n-static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n-static int reopen_stdout(struct commit *commit, const char *subject,\n+static int open_next_file(struct commit *commit, const char *subject,\n \t\t\t struct rev_info *rev, int quiet)\n {\n \tstruct strbuf filename = STRBUF_INIT;\n@@ -821,9 +821,9 @@ static int reopen_stdout(struct commit *commit, const char *subject,\n \t\tfmt_output_subject(&filename, subject, rev);\n \n \tif (!quiet)\n-\t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n+\t\tprintf(\"%s\\n\", filename.buf + outdir_offset);\n \n-\tif (freopen(filename.buf, \"w\", stdout) == NULL)\n+\tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n \n \tstrbuf_release(&filename);\n@@ -882,15 +882,15 @@ static void gen_message_id(struct rev_info *info, char *base)\n \tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n-static void print_signature(void)\n+static void print_signature(FILE *file)\n {\n \tif (!signature || !*signature)\n \t\treturn;\n \n-\tprintf(\"-- \\n%s\", signature);\n+\tfprintf(file, \"-- \\n%s\", signature);\n \tif (signature[strlen(signature)-1] != '\\n')\n-\t\tputchar('\\n');\n-\tputchar('\\n');\n+\t\tfputc('\\n', file);\n+\tfputc('\\n', file);\n }\n \n static void add_branch_description(struct strbuf *buf, const char *branch_name)\n@@ -959,7 +959,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tcommitter = git_committer_info(0);\n \n \tif (!use_stdout &&\n-\t    reopen_stdout(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n+\t    open_next_file(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n \t\treturn;\n \n \tlog_write_email_headers(rev, head, &pp.subject, &pp.after_subject,\n@@ -982,7 +982,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n \tpp_remainder(&pp, &msg, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n-\tprintf(\"%s\\n\", sb.buf);\n+\tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n \tstrbuf_release(&sb);\n \n@@ -991,6 +991,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tlog.wrap = 72;\n \tlog.in1 = 2;\n \tlog.in2 = 4;\n+\tlog.file = rev->diffopt.file;\n \tfor (i = 0; i < nr; i++)\n \t\tshortlog_add_commit(&log, list[i]);\n \n@@ -1013,8 +1014,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tdiffcore_std(&opts);\n \tdiff_flush(&opts);\n \n-\tprintf(\"\\n\");\n-\tprint_signature();\n+\tfprintf(rev->diffopt.file, \"\\n\");\n+\tprint_signature(rev->diffopt.file);\n }\n \n static const char *clean_message_id(const char *msg_id)\n@@ -1324,7 +1325,7 @@ static void prepare_bases(struct base_tree_info *bases,\n \t}\n }\n \n-static void print_bases(struct base_tree_info *bases)\n+static void print_bases(struct base_tree_info *bases, FILE *file)\n {\n \tint i;\n \n@@ -1333,11 +1334,11 @@ static void print_bases(struct base_tree_info *bases)\n \t\treturn;\n \n \t/* Show the base commit */\n-\tprintf(\"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n+\tfprintf(file, \"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n \n \t/* Show the prerequisite patches */\n \tfor (i = bases->nr_patch_id - 1; i >= 0; i--)\n-\t\tprintf(\"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n+\t\tfprintf(file, \"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n \n \tfree(bases->patch_id);\n \tbases->nr_patch_id = 0;\n@@ -1569,6 +1570,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (output_directory) {\n+\t\trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n@@ -1626,9 +1628,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tget_patch_ids(&rev, &ids);\n \t}\n \n-\tif (!use_stdout)\n-\t\trealstdout = xfdopen(xdup(1), \"w\");\n-\n \tif (prepare_revision_walk(&rev))\n \t\tdie(_(\"revision walk setup failed\"));\n \trev.boundary = 1;\n@@ -1693,7 +1692,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n \t\t\t\t  origin, nr, list, branch_name, quiet);\n-\t\tprint_bases(&bases);\n+\t\tprint_bases(&bases, rev.diffopt.file);\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n@@ -1739,7 +1738,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \n \t\tif (!use_stdout &&\n-\t\t    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n+\t\t    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n \t\t\tdie(_(\"Failed to create output files\"));\n \t\tshown = log_tree_commit(&rev, commit);\n \t\tfree_commit_buffer(commit);\n@@ -1754,15 +1753,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\trev.shown_one = 0;\n \t\tif (shown) {\n \t\t\tif (rev.mime_boundary)\n-\t\t\t\tprintf(\"\\n--%s%s--\\n\\n\\n\",\n+\t\t\t\tfprintf(rev.diffopt.file, \"\\n--%s%s--\\n\\n\\n\",\n \t\t\t\t       mime_boundary_leader,\n \t\t\t\t       rev.mime_boundary);\n \t\t\telse\n-\t\t\t\tprint_signature();\n-\t\t\tprint_bases(&bases);\n+\t\t\t\tprint_signature(rev.diffopt.file);\n+\t\t\tprint_bases(&bases, rev.diffopt.file);\n \t\t}\n \t\tif (!use_stdout)\n-\t\t\tfclose(stdout);\n+\t\t\tfclose(rev.diffopt.file);\n \t}\n \tfree(list);\n \tfree(branch_name);\n@@ -1794,15 +1793,15 @@ static const char * const cherry_usage[] = {\n };\n \n static void print_commit(char sign, struct commit *commit, int verbose,\n-\t\t\t int abbrev)\n+\t\t\t int abbrev, FILE *file)\n {\n \tif (!verbose) {\n-\t\tprintf(\"%c %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev));\n \t} else {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);\n-\t\tprintf(\"%c %s %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev),\n \t\t       buf.buf);\n \t\tstrbuf_release(&buf);\n@@ -1883,7 +1882,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\tcommit = list->item;\n \t\tif (has_commit_patch_id(commit, &ids))\n \t\t\tsign = '-';\n-\t\tprint_commit(sign, commit, verbose, abbrev);\n+\t\tprint_commit(sign, commit, verbose, abbrev, revs.diffopt.file);\n \t\tlist = list->next;\n \t}\n \n-- \n2.9.0.118.gce770ba.dirty\n"},{"id":"289568","messageId":"30c24f0e22085a4ba2fbfd16f8ba176e31420d5d.1466244194.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH 2/5] line-log: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-18T10:03:56Z","receivedAt":"2016-06-18T10:05:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff machinery can optionally output to a file stream other than\nstdout, by overriding diffopt.file. In such a case, the rest of the\nlog tree machinery should also write to that stream.\n\nCurrently, there is no user of the line level log that wants to\nredirect output to a file. Therefore, one might argue that it is\nsuperfluous to support that now. However, it is better to be\nconsistent now, rather than to face hard-to-debug problems later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n line-log.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/line-log.c b/line-log.c\nindex bbe31ed..c3b8563 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -841,7 +841,7 @@ static char *get_nth_line(long line, unsigned long *ends, void *data)\n \n static void print_line(const char *prefix, char first,\n \t\t       long line, unsigned long *ends, void *data,\n-\t\t       const char *color, const char *reset)\n+\t\t       const char *color, const char *reset, FILE *file)\n {\n \tchar *begin = get_nth_line(line, ends, data);\n \tchar *end = get_nth_line(line+1, ends, data);\n@@ -852,14 +852,14 @@ static void print_line(const char *prefix, char first,\n \t\thad_nl = 1;\n \t}\n \n-\tfputs(prefix, stdout);\n-\tfputs(color, stdout);\n-\tputchar(first);\n-\tfwrite(begin, 1, end-begin, stdout);\n-\tfputs(reset, stdout);\n-\tputchar('\\n');\n+\tfputs(prefix, file);\n+\tfputs(color, file);\n+\tfputc(first, file);\n+\tfwrite(begin, 1, end-begin, file);\n+\tfputs(reset, file);\n+\tfputc('\\n', file);\n \tif (!had_nl)\n-\t\tfputs(\"\\\\ No newline at end of file\\n\", stdout);\n+\t\tfputs(\"\\\\ No newline at end of file\\n\", file);\n }\n \n static char *output_prefix(struct diff_options *opt)\n@@ -898,12 +898,12 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\tfill_line_ends(pair->one, &p_lines, &p_ends);\n \tfill_line_ends(pair->two, &t_lines, &t_ends);\n \n-\tprintf(\"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n-\tprintf(\"%s%s--- %s%s%s\\n\", prefix, c_meta,\n+\tfprintf(opt->file, \"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s--- %s%s%s\\n\", prefix, c_meta,\n \t       pair->one->sha1_valid ? \"a/\" : \"\",\n \t       pair->one->sha1_valid ? pair->one->path : \"/dev/null\",\n \t       c_reset);\n-\tprintf(\"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n \tfor (i = 0; i < range->ranges.nr; i++) {\n \t\tlong p_start, p_end;\n \t\tlong t_start = range->ranges.ranges[i].start;\n@@ -945,7 +945,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t}\n \n \t\t/* Now output a diff hunk for this range */\n-\t\tprintf(\"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n+\t\tfprintf(opt->file, \"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n \t\t       prefix, c_frag,\n \t\t       p_start+1, p_end-p_start, t_start+1, t_end-t_start,\n \t\t       c_reset);\n@@ -953,18 +953,18 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t\tint k;\n \t\t\tfor (; t_cur < diff->target.ranges[j].start; t_cur++)\n \t\t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_context, c_reset);\n+\t\t\t\t\t   c_context, c_reset, opt->file);\n \t\t\tfor (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)\n \t\t\t\tprint_line(prefix, '-', k, p_ends, pair->one->data,\n-\t\t\t\t\t   c_old, c_reset);\n+\t\t\t\t\t   c_old, c_reset, opt->file);\n \t\t\tfor (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)\n \t\t\t\tprint_line(prefix, '+', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_new, c_reset);\n+\t\t\t\t\t   c_new, c_reset, opt->file);\n \t\t\tj++;\n \t\t}\n \t\tfor (; t_cur < t_end; t_cur++)\n \t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t   c_context, c_reset);\n+\t\t\t\t   c_context, c_reset, opt->file);\n \t}\n \n \tfree(p_ends);\n@@ -977,7 +977,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n  */\n static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)\n {\n-\tputs(output_prefix(&rev->diffopt));\n+\tfprintf(rev->diffopt.file, \"%s\\n\", output_prefix(&rev->diffopt));\n \twhile (range) {\n \t\tdump_diff_hacky_one(rev, range);\n \t\trange = range->next;\n-- \n2.9.0.118.gce770ba.dirty\n\n\n"},{"id":"289640","messageId":"CAPig+cTiexRhzS3MwMEntGYxKms-XQvtoc7HOnUGJvDaBSK7JA@mail.gmail.com","threadId":"42648","inReplyTo":"de218a6cc529b3f5c33dc4b8282f16fd8a5329a8.1466244194.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-19T20:01:59Z","receivedAt":"2016-06-19T20:02:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Jun 18, 2016 at 6:04 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> We just taught the relevant functions to respect the diffopt.file field,\n> to allow writing somewhere else than stdout. Let's make use of it.\n> [...]\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> diff --git a/builtin/log.c b/builtin/log.c\n> @@ -1569,6 +1570,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>                 setup_pager();\n>\n>         if (output_directory) {\n> +               rev.diffopt.use_color = 0;\n\nWhat is this change about? It doesn't seem to be related to anything\nelse in the patch.\n\n>                 if (use_stdout)\n>                         die(_(\"standard output, or directory, which one?\"));\n>                 if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n"},{"id":"289654","messageId":"alpine.DEB.2.20.1606200814510.22630@virtualbox","threadId":"42648","inReplyTo":"CAPig+cTiexRhzS3MwMEntGYxKms-XQvtoc7HOnUGJvDaBSK7JA@mail.gmail.com","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T06:26:01Z","receivedAt":"2016-06-20T06:26:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Sun, 19 Jun 2016, Eric Sunshine wrote:\n\n> On Sat, Jun 18, 2016 at 6:04 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > We just taught the relevant functions to respect the diffopt.file field,\n> > to allow writing somewhere else than stdout. Let's make use of it.\n> > [...]\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> > diff --git a/builtin/log.c b/builtin/log.c\n> > @@ -1569,6 +1570,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n> >                 setup_pager();\n> >\n> >         if (output_directory) {\n> > +               rev.diffopt.use_color = 0;\n> \n> What is this change about? It doesn't seem to be related to anything\n> else in the patch.\n\nGood point, I completely forgot to mention this is the commit message.\n\nWhen format-patch calls the diff machinery, want_color() is used to\ndetermine whether to use ANSI color sequences or not. If use_color is not\nset explicitly, isatty(1) is used to determine whether or not the user\nwants color. When stdout was freopen()ed, this isatty(1) call naturally\nlooked at the file descriptor that was reopened, and determined correctly\nthat no color was desired.\n\nWith the freopen() call gone, stdout may very well be the terminal. But we\nstill do not want color because the output is intended to go to a file (at\nleast if output_directory is set).\n\nSo how about this commit message (I inserted the \"Note: ...\" paragraph)?\n\n-- snipsnap --\nformat-patch: avoid freopen()\n\nWe just taught the relevant functions to respect the diffopt.file field,\nto allow writing somewhere else than stdout. Let's make use of it.\n\nTechnically, we do not need to avoid that call in a builtin: we assume\nthat builtins (as opposed to library functions) are stand-alone programs\nthat may do with their (global) state. Yet, we want to be able to reuse\nthat code in properly lib-ified code, e.g. when converting scripts into\nbuiltins.\n\nNote: we now have to set use_color = 0 explicitly when writing to files,\nas the auto-detection whether to colorify the output *still* looks at\nstdout (which is no longer freopen()ed, and therefore might still be\nprinting to the terminal).\n\nFurther, while we did not *have* to touch the cmd_show() and cmd_cherry()\ncode paths (because they do not want to write anywhere but stdout as of\nyet), it just makes sense to be consistent, making it easier and safer to\nmove the code later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n"},{"id":"289655","messageId":"CAPig+cQqworFpRvd-U9sgnyitQEzy6zAKc_091b9fzjuzsFnpA@mail.gmail.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606200814510.22630@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-20T06:32:37Z","receivedAt":"2016-06-20T06:32:41Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jun 20, 2016 at 2:26 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> On Sun, 19 Jun 2016, Eric Sunshine wrote:\n>> On Sat, Jun 18, 2016 at 6:04 AM, Johannes Schindelin\n>> <johannes.schindelin@gmx.de> wrote:\n>> >         if (output_directory) {\n>> > +               rev.diffopt.use_color = 0;\n>>\n>> What is this change about? It doesn't seem to be related to anything\n>> else in the patch.\n>\n> Good point, I completely forgot to mention this is the commit message.\n>\n> When format-patch calls the diff machinery, want_color() is used to\n> determine whether to use ANSI color sequences or not. If use_color is not\n> set explicitly, isatty(1) is used to determine whether or not the user\n> wants color. When stdout was freopen()ed, this isatty(1) call naturally\n> looked at the file descriptor that was reopened, and determined correctly\n> that no color was desired.\n>\n> With the freopen() call gone, stdout may very well be the terminal. But we\n> still do not want color because the output is intended to go to a file (at\n> least if output_directory is set).\n\nWould it make sense to do this as a separate preparatory patch, or is\nit just too minor?\n\n> So how about this commit message (I inserted the \"Note: ...\" paragraph)?\n>\n> -- snipsnap --\n> format-patch: avoid freopen()\n> [...]\n> Note: we now have to set use_color = 0 explicitly when writing to files,\n> as the auto-detection whether to colorify the output *still* looks at\n> stdout (which is no longer freopen()ed, and therefore might still be\n> printing to the terminal).\n\nThanks.\n"},{"id":"289666","messageId":"alpine.DEB.2.20.1606201208260.22630@virtualbox","threadId":"42648","inReplyTo":"CAPig+cQqworFpRvd-U9sgnyitQEzy6zAKc_091b9fzjuzsFnpA@mail.gmail.com","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:09:04Z","receivedAt":"2016-06-20T10:12:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Mon, 20 Jun 2016, Eric Sunshine wrote:\n\n> On Mon, Jun 20, 2016 at 2:26 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> > On Sun, 19 Jun 2016, Eric Sunshine wrote:\n> >> On Sat, Jun 18, 2016 at 6:04 AM, Johannes Schindelin\n> >> <johannes.schindelin@gmx.de> wrote:\n> >> >         if (output_directory) {\n> >> > +               rev.diffopt.use_color = 0;\n> >>\n> >> What is this change about? It doesn't seem to be related to anything\n> >> else in the patch.\n> >\n> > Good point, I completely forgot to mention this is the commit message.\n> >\n> > When format-patch calls the diff machinery, want_color() is used to\n> > determine whether to use ANSI color sequences or not. If use_color is not\n> > set explicitly, isatty(1) is used to determine whether or not the user\n> > wants color. When stdout was freopen()ed, this isatty(1) call naturally\n> > looked at the file descriptor that was reopened, and determined correctly\n> > that no color was desired.\n> >\n> > With the freopen() call gone, stdout may very well be the terminal. But we\n> > still do not want color because the output is intended to go to a file (at\n> > least if output_directory is set).\n> \n> Would it make sense to do this as a separate preparatory patch, or is\n> it just too minor?\n\nThat's a good point. It *is* a change in its own right. Will reroll.\n\nThanks,\nDscho\n"},{"id":"289667","messageId":"cover.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466244194.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 0/7] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:02Z","receivedAt":"2016-06-20T10:58:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The idea is to allow callers to redirect log-tree's output to a file\nwithout having to freopen() stdout (which would modify global state,\na big no-no-no for library functions).\n\nI reviewed log-tree.c, graph.c, line-log.c, builtin/shortlog.c and\nbuiltin/log.c line by line to ensure that all calls that assumed stdout\npreviously now use the `file` field instead, of course. I would\nwelcome additional eyes to go over the code to confirm that I did not\nmiss anything.\n\nThis patch series ends up removing the freopen() call in the\nformat-patch command, but that is only a by-product. The ulterior motive\nbehind this series is to allow the sequencer to write a `patch` file as\npart of my endeavor to move large chunks of rebase -i into a builtin.\n\nSpeaking of said endeavor: it is going a lot slower than I would like,\nbut I really need rebase -i to be robust. To that end, I not only review\nand re-review my patches frequently, I also use a cross-validation\ntechnique inspired by my original ll-merge validation as well as\nGitHub's Scientist library: I taught rebase -i to run every interactive\nrebase twice, once with the original shell script's code, and once with\nthe builtin rebase--helper, and then to verify that the end result is as\nsimilar as can be expected (the commit dates will differ most of the\ntime, of course).\n\nIt is a bit tedious, of course, because I have to resolve every merge\nconflict twice, I have to reword everything twice (identically!), and I\nhave to wait longer for the rebase to finish. It is worth it, though,\nbecause I really need rebase -i to be robust, as it is the center piece\nof almost all of my workflows.\n\nI organized the patch series into a thicket of branches, to make it\neasier to review them; In addition to this here patch series, I plan to\nsubmit 11 separate, partially interdependent series, resubmit another\none, and I already submitted a couple of patches earlier that were\nintegrated or replaced by superior solutions (thanks, Peff!). Skipping\nthe temporary patches (e.g. for cross-validation), that leaves me with\n99 patches to submit in total (sing with me: \"99 patches of code to\nsubmit, 99 patches of code, ...\").\n\nI was curious just how much these patches could accelerate rebase -i, so\nI disabled the cross-validation temprarily and let Travis measure the\nperformance improvements via the t/perf test that was already merged to\n`master`. Look for \"3404.2\" in this build's logs:\nhttps://travis-ci.org/git/git/builds/138277774\n\nIt looks like a 3x speedup on Linux, a 4x speedup on MacOSX and my local\nmeasurements suggest a >5x speed-up on Windows.\n\n\nJohannes Schindelin (7):\n  log-tree: respect diffopt's configured output file stream\n  line-log: respect diffopt's configured output file stream\n  graph: respect the diffopt.file setting\n  shortlog: support outputting to streams other than stdout\n  format-patch: explicitly switch off color when writing to files\n  format-patch: avoid freopen()\n  format-patch: use stdout directly\n\n builtin/log.c      | 71 +++++++++++++++++++++++++++---------------------------\n builtin/shortlog.c | 13 ++++++----\n graph.c            | 30 +++++++++++++----------\n line-log.c         | 34 +++++++++++++-------------\n log-tree.c         | 65 +++++++++++++++++++++++++------------------------\n shortlog.h         |  1 +\n 6 files changed, 111 insertions(+), 103 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/diffopt.file-v2\n-- \n2.9.0.119.g370c5a9\n\nbase-commit: 05219a1276341e72d8082d76b7f5ed394b7437a4\n"},{"id":"289668","messageId":"30c24f0e22085a4ba2fbfd16f8ba176e31420d5d.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 2/7] line-log: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:26Z","receivedAt":"2016-06-20T10:59:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff machinery can optionally output to a file stream other than\nstdout, by overriding diffopt.file. In such a case, the rest of the\nlog tree machinery should also write to that stream.\n\nCurrently, there is no user of the line level log that wants to\nredirect output to a file. Therefore, one might argue that it is\nsuperfluous to support that now. However, it is better to be\nconsistent now, rather than to face hard-to-debug problems later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n line-log.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/line-log.c b/line-log.c\nindex bbe31ed..c3b8563 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -841,7 +841,7 @@ static char *get_nth_line(long line, unsigned long *ends, void *data)\n \n static void print_line(const char *prefix, char first,\n \t\t       long line, unsigned long *ends, void *data,\n-\t\t       const char *color, const char *reset)\n+\t\t       const char *color, const char *reset, FILE *file)\n {\n \tchar *begin = get_nth_line(line, ends, data);\n \tchar *end = get_nth_line(line+1, ends, data);\n@@ -852,14 +852,14 @@ static void print_line(const char *prefix, char first,\n \t\thad_nl = 1;\n \t}\n \n-\tfputs(prefix, stdout);\n-\tfputs(color, stdout);\n-\tputchar(first);\n-\tfwrite(begin, 1, end-begin, stdout);\n-\tfputs(reset, stdout);\n-\tputchar('\\n');\n+\tfputs(prefix, file);\n+\tfputs(color, file);\n+\tfputc(first, file);\n+\tfwrite(begin, 1, end-begin, file);\n+\tfputs(reset, file);\n+\tfputc('\\n', file);\n \tif (!had_nl)\n-\t\tfputs(\"\\\\ No newline at end of file\\n\", stdout);\n+\t\tfputs(\"\\\\ No newline at end of file\\n\", file);\n }\n \n static char *output_prefix(struct diff_options *opt)\n@@ -898,12 +898,12 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\tfill_line_ends(pair->one, &p_lines, &p_ends);\n \tfill_line_ends(pair->two, &t_lines, &t_ends);\n \n-\tprintf(\"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n-\tprintf(\"%s%s--- %s%s%s\\n\", prefix, c_meta,\n+\tfprintf(opt->file, \"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s--- %s%s%s\\n\", prefix, c_meta,\n \t       pair->one->sha1_valid ? \"a/\" : \"\",\n \t       pair->one->sha1_valid ? pair->one->path : \"/dev/null\",\n \t       c_reset);\n-\tprintf(\"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n \tfor (i = 0; i < range->ranges.nr; i++) {\n \t\tlong p_start, p_end;\n \t\tlong t_start = range->ranges.ranges[i].start;\n@@ -945,7 +945,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t}\n \n \t\t/* Now output a diff hunk for this range */\n-\t\tprintf(\"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n+\t\tfprintf(opt->file, \"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n \t\t       prefix, c_frag,\n \t\t       p_start+1, p_end-p_start, t_start+1, t_end-t_start,\n \t\t       c_reset);\n@@ -953,18 +953,18 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t\tint k;\n \t\t\tfor (; t_cur < diff->target.ranges[j].start; t_cur++)\n \t\t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_context, c_reset);\n+\t\t\t\t\t   c_context, c_reset, opt->file);\n \t\t\tfor (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)\n \t\t\t\tprint_line(prefix, '-', k, p_ends, pair->one->data,\n-\t\t\t\t\t   c_old, c_reset);\n+\t\t\t\t\t   c_old, c_reset, opt->file);\n \t\t\tfor (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)\n \t\t\t\tprint_line(prefix, '+', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_new, c_reset);\n+\t\t\t\t\t   c_new, c_reset, opt->file);\n \t\t\tj++;\n \t\t}\n \t\tfor (; t_cur < t_end; t_cur++)\n \t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t   c_context, c_reset);\n+\t\t\t\t   c_context, c_reset, opt->file);\n \t}\n \n \tfree(p_ends);\n@@ -977,7 +977,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n  */\n static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)\n {\n-\tputs(output_prefix(&rev->diffopt));\n+\tfprintf(rev->diffopt.file, \"%s\\n\", output_prefix(&rev->diffopt));\n \twhile (range) {\n \t\tdump_diff_hacky_one(rev, range);\n \t\trange = range->next;\n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289669","messageId":"babf95df5f610feb6c2ae7f2ed3cff98bab47fe2.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 1/7] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:13Z","receivedAt":"2016-06-20T10:59:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff options already know how to print the output anywhere else\nthan stdout. The same is needed for log output in general, e.g.\nwhen writing patches to files in `git format-patch`. Let's allow\nusers to use log_tree_commit() *without* changing global state via\nfreopen().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n log-tree.c | 65 +++++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 33 insertions(+), 32 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 78a5381..968428a 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -159,12 +159,12 @@ void load_ref_decorations(int flags)\n \t}\n }\n \n-static void show_parents(struct commit *commit, int abbrev)\n+static void show_parents(struct commit *commit, int abbrev, FILE *file)\n {\n \tstruct commit_list *p;\n \tfor (p = commit->parents; p ; p = p->next) {\n \t\tstruct commit *parent = p->item;\n-\t\tprintf(\" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n+\t\tfprintf(file, \" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -172,7 +172,7 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n {\n \tstruct commit_list *p = lookup_decoration(&opt->children, &commit->object);\n \tfor ( ; p; p = p->next) {\n-\t\tprintf(\" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n+\t\tfprintf(opt->diffopt.file, \" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -286,11 +286,11 @@ void show_decorations(struct rev_info *opt, struct commit *commit)\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\t\tfprintf(opt->diffopt.file, \"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n \tformat_decorations(&sb, commit, opt->diffopt.use_color);\n-\tfputs(sb.buf, stdout);\n+\tfputs(sb.buf, opt->diffopt.file);\n \tstrbuf_release(&sb);\n }\n \n@@ -364,18 +364,18 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\tsubject = \"Subject: \";\n \t}\n \n-\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n+\tfprintf(opt->diffopt.file, \"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n \tgraph_show_oneline(opt->graph);\n \tif (opt->message_id) {\n-\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n+\t\tfprintf(opt->diffopt.file, \"Message-Id: <%s>\\n\", opt->message_id);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \tif (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {\n \t\tint i, n;\n \t\tn = opt->ref_message_ids->nr;\n-\t\tprintf(\"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n+\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n \t\tfor (i = 0; i < n; i++)\n-\t\t\tprintf(\"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n+\t\t\tfprintf(opt->diffopt.file, \"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n \t\t\t       opt->ref_message_ids->items[i].string);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n@@ -432,7 +432,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)\n \treset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);\n \twhile (*bol) {\n \t\teol = strchrnul(bol, '\\n');\n-\t\tprintf(\"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n+\t\tfprintf(opt->diffopt.file, \"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n \t\t       *eol ? \"\\n\" : \"\");\n \t\tgraph_show_oneline(opt->graph);\n \t\tbol = (*eol) ? (eol + 1) : eol;\n@@ -553,17 +553,17 @@ void show_log(struct rev_info *opt)\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n-\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);\n+\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_remainder(opt->graph);\n \t\t}\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -589,7 +589,7 @@ void show_log(struct rev_info *opt)\n \t\tif (opt->diffopt.line_termination == '\\n' &&\n \t\t    !opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \topt->shown_one = 1;\n \n@@ -607,28 +607,28 @@ void show_log(struct rev_info *opt)\n \t\tlog_write_email_headers(opt, commit, &ctx.subject, &extra_headers,\n \t\t\t\t\t&ctx.need_8bit_cte);\n \t} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);\n \t\tif (opt->commit_format != CMIT_FMT_ONELINE)\n-\t\t\tfputs(\"commit \", stdout);\n+\t\t\tfputs(\"commit \", opt->diffopt.file);\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n \t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),\n-\t\t      stdout);\n+\t\t      opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tif (parent)\n-\t\t\tprintf(\" (from %s)\",\n+\t\t\tfprintf(opt->diffopt.file, \" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.oid.hash,\n \t\t\t\t\t\t  abbrev_commit));\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n-\t\t\tputchar(' ');\n+\t\t\tfputc(' ', opt->diffopt.file);\n \t\t} else {\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_oneline(opt->graph);\n \t\t}\n \t\tif (opt->reflog_info) {\n@@ -702,7 +702,7 @@ void show_log(struct rev_info *opt)\n \t}\n \n \tif (opt->show_log_size) {\n-\t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n+\t\tfprintf(opt->diffopt.file, \"log size %i\\n\", (int)msgbuf.len);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \n@@ -718,11 +718,11 @@ void show_log(struct rev_info *opt)\n \tif (opt->graph)\n \t\tgraph_show_commit_msg(opt->graph, &msgbuf);\n \telse\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, opt->diffopt.file);\n \tif (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {\n \t\tif (!opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \n \tstrbuf_release(&msgbuf);\n@@ -759,7 +759,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t\tstruct strbuf *msg = NULL;\n \t\t\t\tmsg = opt->diffopt.output_prefix(&opt->diffopt,\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\tfwrite(msg->buf, msg->len, 1, opt->diffopt.file);\n \t\t\t}\n \n \t\t\t/*\n@@ -774,8 +774,8 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t */\n \t\t\tif (!opt->shown_dashes &&\n \t\t\t    (pch & opt->diffopt.output_format) == pch)\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n+\t\t\t\tfprintf(opt->diffopt.file, \"---\");\n+\t\t\tfputc('\\n', opt->diffopt.file);\n \t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n@@ -872,7 +872,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\treturn line_log_print(opt, commit);\n \n \tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n@@ -880,8 +880,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\tshown = 1;\n \t}\n \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n-\tmaybe_flush_or_die(stdout, \"stdout\");\n+\tif (opt->diffopt.file == stdout)\n+\t\tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\n }\n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289670","messageId":"f2b1795827bcb055028332cfdf04d996d8b6e0e6.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 3/7] graph: respect the diffopt.file setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:34Z","receivedAt":"2016-06-20T10:59:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the caller overrides diffopt.file (which defaults to stdout),\nthe diff machinery already redirects its output, and the graph display\nshould also write to that file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n graph.c | 30 +++++++++++++++++-------------\n 1 file changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex 1350bdd..8ae56bc 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -17,8 +17,8 @@\n static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);\n \n /*\n- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n- * first will be prefixed with the graph output.\n+ * Print a strbuf.  If the graph is non-NULL, all lines but the first will be\n+ * prefixed with the graph output.\n  *\n  * If the strbuf ends with a newline, the output will end after this\n  * newline.  A new graph line will not be printed after the final newline.\n@@ -1193,9 +1193,10 @@ void graph_show_commit(struct git_graph *graph)\n \n \twhile (!shown_commit_line && !graph_is_commit_finished(graph)) {\n \t\tshown_commit_line = graph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tif (!shown_commit_line)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t}\n \n@@ -1210,7 +1211,7 @@ void graph_show_oneline(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_next_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1222,7 +1223,7 @@ void graph_show_padding(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_padding_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1239,12 +1240,13 @@ int graph_show_remainder(struct git_graph *graph)\n \n \tfor (;;) {\n \t\tgraph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t\tshown = 1;\n \n \t\tif (!graph_is_commit_finished(graph))\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t\telse\n \t\t\tbreak;\n \t}\n@@ -1259,7 +1261,8 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \tchar *p;\n \n \tif (!graph) {\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1277,7 +1280,7 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \t\t} else {\n \t\t\tlen = (sb->buf + sb->len) - p;\n \t\t}\n-\t\tfwrite(p, sizeof(char), len, stdout);\n+\t\tfwrite(p, sizeof(char), len, graph->revs->diffopt.file);\n \t\tif (next_p && *next_p != '\\0')\n \t\t\tgraph_show_oneline(graph);\n \t\tp = next_p;\n@@ -1297,7 +1300,8 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * CMIT_FMT_USERFORMAT are already missing a terminating\n \t\t * newline.  All of the other formats should have it.\n \t\t */\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1318,7 +1322,7 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * new line.\n \t\t */\n \t\tif (!newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \n \t\tgraph_show_remainder(graph);\n \n@@ -1326,6 +1330,6 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * If sb ends with a newline, our output should too.\n \t\t */\n \t\tif (newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', graph->revs->diffopt.file);\n \t}\n }\n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289671","messageId":"8b1f13155c7ba6dde7994c920fbff7019be51054.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 6/7] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:55Z","receivedAt":"2016-06-20T10:59:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We just taught the relevant functions to respect the diffopt.file field,\nto allow writing somewhere else than stdout. Let's make use of it.\n\nTechnically, we do not need to avoid that call in a builtin: we assume\nthat builtins (as opposed to library functions) are stand-alone programs\nthat may do with their (global) state. Yet, we want to be able to reuse\nthat code in properly lib-ified code, e.g. when converting scripts into\nbuiltins.\n\nFurther, while we did not *have* to touch the cmd_show() and cmd_cherry()\ncode paths (because they do not want to write anywhere but stdout as of\nyet), it just makes sense to be consistent, making it easier and safer to\nmove the code later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 64 ++++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 33 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex abd889b..db034a8 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -236,7 +236,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)\n \t\tif (rev->commit_format != CMIT_FMT_ONELINE)\n \t\t\tputchar(rev->diffopt.line_termination);\n \t}\n-\tprintf(_(\"Final output: %d %s\\n\"), nr, stage);\n+\tfprintf(rev->diffopt.file, _(\"Final output: %d %s\\n\"), nr, stage);\n }\n \n static struct itimerval early_output_timer;\n@@ -445,7 +445,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)\n \tpp.fmt = rev->commit_format;\n \tpp.date_mode = rev->date_mode;\n \tpp_user_info(&pp, \"Tagger\", &out, buf, get_log_output_encoding());\n-\tprintf(\"%s\", out.buf);\n+\tfprintf(rev->diffopt.file, \"%s\", out.buf);\n \tstrbuf_release(&out);\n }\n \n@@ -456,7 +456,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con\n \tchar *buf;\n \tunsigned long size;\n \n-\tfflush(stdout);\n+\tfflush(rev->diffopt.file);\n \tif (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||\n \t    !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))\n \t\treturn stream_blob_to_fd(1, sha1, NULL, 0);\n@@ -496,7 +496,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)\n \t}\n \n \tif (offset < size)\n-\t\tfwrite(buf + offset, size - offset, 1, stdout);\n+\t\tfwrite(buf + offset, size - offset, 1, rev->diffopt.file);\n \tfree(buf);\n \treturn 0;\n }\n@@ -505,7 +505,8 @@ static int show_tree_object(const unsigned char *sha1,\n \t\tstruct strbuf *base,\n \t\tconst char *pathname, unsigned mode, int stage, void *context)\n {\n-\tprintf(\"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tFILE *file = context;\n+\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n \treturn 0;\n }\n \n@@ -565,7 +566,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stag %s%s\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stag %s%s\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tt->tag,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n@@ -584,12 +585,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\tcase OBJ_TREE:\n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stree %s%s\\n\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stree %s%s\\n\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive((struct tree *)o, \"\", 0, 0, &match_all,\n-\t\t\t\t\tshow_tree_object, NULL);\n+\t\t\t\t\tshow_tree_object, rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\n@@ -799,7 +800,7 @@ static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n-static int reopen_stdout(struct commit *commit, const char *subject,\n+static int open_next_file(struct commit *commit, const char *subject,\n \t\t\t struct rev_info *rev, int quiet)\n {\n \tstruct strbuf filename = STRBUF_INIT;\n@@ -823,7 +824,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,\n \tif (!quiet)\n \t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n \n-\tif (freopen(filename.buf, \"w\", stdout) == NULL)\n+\tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n \n \tstrbuf_release(&filename);\n@@ -882,15 +883,15 @@ static void gen_message_id(struct rev_info *info, char *base)\n \tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n-static void print_signature(void)\n+static void print_signature(FILE *file)\n {\n \tif (!signature || !*signature)\n \t\treturn;\n \n-\tprintf(\"-- \\n%s\", signature);\n+\tfprintf(file, \"-- \\n%s\", signature);\n \tif (signature[strlen(signature)-1] != '\\n')\n-\t\tputchar('\\n');\n-\tputchar('\\n');\n+\t\tfputc('\\n', file);\n+\tfputc('\\n', file);\n }\n \n static void add_branch_description(struct strbuf *buf, const char *branch_name)\n@@ -959,7 +960,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tcommitter = git_committer_info(0);\n \n \tif (!use_stdout &&\n-\t    reopen_stdout(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n+\t    open_next_file(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n \t\treturn;\n \n \tlog_write_email_headers(rev, head, &pp.subject, &pp.after_subject,\n@@ -982,7 +983,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n \tpp_remainder(&pp, &msg, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n-\tprintf(\"%s\\n\", sb.buf);\n+\tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n \tstrbuf_release(&sb);\n \n@@ -991,6 +992,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tlog.wrap = 72;\n \tlog.in1 = 2;\n \tlog.in2 = 4;\n+\tlog.file = rev->diffopt.file;\n \tfor (i = 0; i < nr; i++)\n \t\tshortlog_add_commit(&log, list[i]);\n \n@@ -1013,8 +1015,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tdiffcore_std(&opts);\n \tdiff_flush(&opts);\n \n-\tprintf(\"\\n\");\n-\tprint_signature();\n+\tfprintf(rev->diffopt.file, \"\\n\");\n+\tprint_signature(rev->diffopt.file);\n }\n \n static const char *clean_message_id(const char *msg_id)\n@@ -1324,7 +1326,7 @@ static void prepare_bases(struct base_tree_info *bases,\n \t}\n }\n \n-static void print_bases(struct base_tree_info *bases)\n+static void print_bases(struct base_tree_info *bases, FILE *file)\n {\n \tint i;\n \n@@ -1333,11 +1335,11 @@ static void print_bases(struct base_tree_info *bases)\n \t\treturn;\n \n \t/* Show the base commit */\n-\tprintf(\"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n+\tfprintf(file, \"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n \n \t/* Show the prerequisite patches */\n \tfor (i = bases->nr_patch_id - 1; i >= 0; i--)\n-\t\tprintf(\"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n+\t\tfprintf(file, \"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n \n \tfree(bases->patch_id);\n \tbases->nr_patch_id = 0;\n@@ -1694,7 +1696,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n \t\t\t\t  origin, nr, list, branch_name, quiet);\n-\t\tprint_bases(&bases);\n+\t\tprint_bases(&bases, rev.diffopt.file);\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n@@ -1740,7 +1742,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \n \t\tif (!use_stdout &&\n-\t\t    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n+\t\t    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n \t\t\tdie(_(\"Failed to create output files\"));\n \t\tshown = log_tree_commit(&rev, commit);\n \t\tfree_commit_buffer(commit);\n@@ -1755,15 +1757,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\trev.shown_one = 0;\n \t\tif (shown) {\n \t\t\tif (rev.mime_boundary)\n-\t\t\t\tprintf(\"\\n--%s%s--\\n\\n\\n\",\n+\t\t\t\tfprintf(rev.diffopt.file, \"\\n--%s%s--\\n\\n\\n\",\n \t\t\t\t       mime_boundary_leader,\n \t\t\t\t       rev.mime_boundary);\n \t\t\telse\n-\t\t\t\tprint_signature();\n-\t\t\tprint_bases(&bases);\n+\t\t\t\tprint_signature(rev.diffopt.file);\n+\t\t\tprint_bases(&bases, rev.diffopt.file);\n \t\t}\n \t\tif (!use_stdout)\n-\t\t\tfclose(stdout);\n+\t\t\tfclose(rev.diffopt.file);\n \t}\n \tfree(list);\n \tfree(branch_name);\n@@ -1795,15 +1797,15 @@ static const char * const cherry_usage[] = {\n };\n \n static void print_commit(char sign, struct commit *commit, int verbose,\n-\t\t\t int abbrev)\n+\t\t\t int abbrev, FILE *file)\n {\n \tif (!verbose) {\n-\t\tprintf(\"%c %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev));\n \t} else {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);\n-\t\tprintf(\"%c %s %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev),\n \t\t       buf.buf);\n \t\tstrbuf_release(&buf);\n@@ -1884,7 +1886,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\tcommit = list->item;\n \t\tif (has_commit_patch_id(commit, &ids))\n \t\t\tsign = '-';\n-\t\tprint_commit(sign, commit, verbose, abbrev);\n+\t\tprint_commit(sign, commit, verbose, abbrev, revs.diffopt.file);\n \t\tlist = list->next;\n \t}\n \n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289672","messageId":"2abbf428b42795f930c2338233b71f0f299e1d41.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 5/7] format-patch: explicitly switch off color when writing to files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:49Z","receivedAt":"2016-06-20T10:59:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We rely on the auto-detection (\"is stdout a terminal?\") to determine\nwhether to use color in the output of format-patch or not. That happens\nto work because we freopen() stdout when redirecting the output to files.\n\nHowever, we are about to fix that work-around, in which case the\nauto-detection has no chance to guess whether to use color or not.\n\nBut then, we do not need to guess to begin with. We know that we do not\nwant to use ANSI color sequences in the output files. So let's just be\nexplicit about our wishes.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 099f4f7..abd889b 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1569,6 +1569,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (output_directory) {\n+\t\trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289673","messageId":"c9990c2ac84f43e76afc52e5cca5d8876715e004.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 4/7] shortlog: support outputting to streams other than stdout","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:55:43Z","receivedAt":"2016-06-20T10:59:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to avoid freopen() in `git format-patch`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/shortlog.c | 13 ++++++++-----\n shortlog.h         |  1 +\n 2 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex bfc082e..4c68ba7 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -229,6 +229,7 @@ void shortlog_init(struct shortlog *log)\n \tlog->wrap = DEFAULT_WRAPLEN;\n \tlog->in1 = DEFAULT_INDENT1;\n \tlog->in2 = DEFAULT_INDENT2;\n+\tlog->file = stdout;\n }\n \n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n@@ -310,22 +311,24 @@ void shortlog_output(struct shortlog *log)\n \tfor (i = 0; i < log->list.nr; i++) {\n \t\tconst struct string_list_item *item = &log->list.items[i];\n \t\tif (log->summary) {\n-\t\t\tprintf(\"%6d\\t%s\\n\", (int)UTIL_TO_INT(item), item->string);\n+\t\t\tfprintf(log->file, \"%6d\\t%s\\n\",\n+\t\t\t\t(int)UTIL_TO_INT(item), item->string);\n \t\t} else {\n \t\t\tstruct string_list *onelines = item->util;\n-\t\t\tprintf(\"%s (%d):\\n\", item->string, onelines->nr);\n+\t\t\tfprintf(log->file, \"%s (%d):\\n\",\n+\t\t\t\titem->string, onelines->nr);\n \t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n \t\t\t\tconst char *msg = onelines->items[j].string;\n \n \t\t\t\tif (log->wrap_lines) {\n \t\t\t\t\tstrbuf_reset(&sb);\n \t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n-\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n+\t\t\t\t\tfwrite(sb.buf, sb.len, 1, log->file);\n \t\t\t\t}\n \t\t\t\telse\n-\t\t\t\t\tprintf(\"      %s\\n\", msg);\n+\t\t\t\t\tfprintf(log->file, \"      %s\\n\", msg);\n \t\t\t}\n-\t\t\tputchar('\\n');\n+\t\t\tfputc('\\n', log->file);\n \t\t\tonelines->strdup_strings = 1;\n \t\t\tstring_list_clear(onelines, 0);\n \t\t\tfree(onelines);\ndiff --git a/shortlog.h b/shortlog.h\nindex de4f86f..5a326c6 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -17,6 +17,7 @@ struct shortlog {\n \tchar *common_repo_prefix;\n \tint email;\n \tstruct string_list mailmap;\n+\tFILE *file;\n };\n \n void shortlog_init(struct shortlog *log);\n-- \n2.9.0.119.g370c5a9\n\n\n"},{"id":"289674","messageId":"d486373d9eb2bd248a80b6be7061b56db2f148aa.1466420060.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 7/7] format-patch: use stdout directly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T10:56:01Z","receivedAt":"2016-06-20T10:59:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Earlier, we freopen()ed stdout in order to write patches to files.\nThat forced us to duplicate stdout (naming it \"realstdout\") because we\n*still* wanted to be able to report the file names.\n\nAs we do not abuse stdout that way anymore, we no longer need to\nduplicate stdout, either.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 6 +-----\n 1 file changed, 1 insertion(+), 5 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex db034a8..5a889d5 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -796,7 +796,6 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \treturn git_log_config(var, value, cb);\n }\n \n-static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n@@ -822,7 +821,7 @@ static int open_next_file(struct commit *commit, const char *subject,\n \t\tfmt_output_subject(&filename, subject, rev);\n \n \tif (!quiet)\n-\t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n+\t\tprintf(\"%s\\n\", filename.buf + outdir_offset);\n \n \tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n@@ -1629,9 +1628,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tget_patch_ids(&rev, &ids);\n \t}\n \n-\tif (!use_stdout)\n-\t\trealstdout = xfdopen(xdup(1), \"w\");\n-\n \tif (prepare_revision_walk(&rev))\n \t\tdie(_(\"revision walk setup failed\"));\n \trev.boundary = 1;\n-- \n2.9.0.119.g370c5a9\n"},{"id":"289676","messageId":"alpine.DEB.2.20.1606201349060.22630@virtualbox","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 0/7] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-20T11:50:27Z","receivedAt":"2016-06-20T11:54:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\n[sorry, Eric, forgot to Cc: you on the cover letter...]\n\nOn Mon, 20 Jun 2016, Johannes Schindelin wrote:\n\n> Johannes Schindelin (7):\n>   log-tree: respect diffopt's configured output file stream\n>   line-log: respect diffopt's configured output file stream\n>   graph: respect the diffopt.file setting\n>   shortlog: support outputting to streams other than stdout\n>   format-patch: explicitly switch off color when writing to files\n>   format-patch: avoid freopen()\n>   format-patch: use stdout directly\n\nThe interdiff is empty: all I did was to split out the first and the third\n`format-patch:` commit, to leave really only the relevant part in the\n\"avoid freopen()\" commit.\n\nCiao,\nDscho\n"},{"id":"289680","messageId":"xmqq4m8nvodo.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606200814510.22630@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-20T16:03:31Z","receivedAt":"2016-06-20T16:12:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> When format-patch calls the diff machinery, want_color() is used to\n> determine whether to use ANSI color sequences or not. If use_color is not\n> set explicitly, isatty(1) is used to determine whether or not the user\n> wants color. When stdout was freopen()ed, this isatty(1) call naturally\n> looked at the file descriptor that was reopened, and determined correctly\n> that no color was desired.\n>\n> With the freopen() call gone, stdout may very well be the terminal. But we\n> still do not want color because the output is intended to go to a file (at\n> least if output_directory is set).\n\nHow does this interact with --color=... that is given from the\ncommand line at the same time?  Currently --color=always would\ngive you a coloured output.\n\nI personally do not think of a sensible reason why anybody wants a\ncolored format-patch output, but Git's userbase has grown large\nenough and you can no longer expect that only sensible people use it\n;-)\n\nYou can probably sell \"when giving out put to file, we will never\ncolor the output\" as an improved new world order, but if that is\nwhat this change wants to do, it probably deserves a separate patch.\n\nI however think you can avoid breaking expectations by people who\nare not so sensible by overriding only when use_color is set to\nGIT_COLOR_AUTO, perhaps?\n"},{"id":"289684","messageId":"xmqqwplju74a.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"babf95df5f610feb6c2ae7f2ed3cff98bab47fe2.1466420060.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 1/7] log-tree: respect diffopt's configured output file stream","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-20T17:01:41Z","receivedAt":"2016-06-20T17:09:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The diff options already know how to print the output anywhere else\n> than stdout. The same is needed for log output in general, e.g.\n> when writing patches to files in `git format-patch`. Let's allow\n> users to use log_tree_commit() *without* changing global state via\n> freopen().\n\nI wonder if this change is actually fixing existing bugs.  Are there\ncases where diffopt.file is set, i.e. the user expects the output to\nbe sent elsewhere, but the code here unconditionally emits to the\nstandard output?  I suspect that such a bug can be demonstratable in\na test or two, if that were the case.\n\nI am sort-of surprised that we didn't do this already even though we\nhad diffopt.file for a long time since c0c77734 (Write diff output\nto a file in struct diff_options, 2008-03-09).\n\nUse of freopen() to always write patches through stdout may have\nbeen done as a lazy workaround of the issue this patch fixes, but\nwhat is surprising to me is that doing it the right way like this\npatch does is not that much of work.  Perhaps that was done long\nbefore c0c77734 was done, which would mean doing it the right way\nback then when we started using freopen() it would have been a lot\nmore work and we thought taking a short-cut was warranted.\n\nIn any case, this is a change in the good direction.  Thanks for\ncleaning things up.\n\n>  \t\tif (opt->children.name)\n>  \t\t\tshow_children(opt, commit, abbrev_commit);\n>  \t\tshow_decorations(opt, commit);\n>  \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n> -\t\t\tputchar('\\n');\n> +\t\t\tfputc('\\n', opt->diffopt.file);\n\nHmph.  putc() is the \"to the given stream\" equivalent of putchar()\nin the \"send to stdout\" world, not fputc().  I do not see a reason\nto force the call to go to a function avoiding a possible macro here.\n\nLikewise for all the new fputc() calls in this series that were\noriginally putchar().\n\n> @@ -880,8 +880,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n>  \t\tshown = 1;\n>  \t}\n>  \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n> -\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n> +\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n>  \topt->loginfo = NULL;\n> -\tmaybe_flush_or_die(stdout, \"stdout\");\n> +\tif (opt->diffopt.file == stdout)\n> +\t\tmaybe_flush_or_die(stdout, \"stdout\");\n>  \treturn shown;\n>  }\n\nThis one looks fishy.\n\nBack when we freopen()'ed to write patches only through stdout, we\nalways called maybe_flush_or_die() to make sure that the output is\nflushed correctly after processing each commit.  This change makes\nit not to care, which I doubt was what you intended.  Instead, my\nsuspicion is that you didn't want to say \"stdout\" when writing into\na file.\n\nBut even when writing to on-disk files, the code before your series\nwould have said \"stdout\" when it had trouble flushing, so I do not\nthink this new \"if()\" condition is making things better.  If \"it\nsaid stdout when having trouble flushing to a file\" were a problem\nto be fixed, \"let's not say stdout by not even attempting to flush\nand catch errors when writing to a file\" would not be the right\nsolution, no?\n\nPersonally, I do not think it hurts if we kept saying 'stdout' here,\neven when we flush opt->diffopt.file and found a problem.\n\nThanks.\n\n\n"},{"id":"289695","messageId":"xmqqshw7u1s2.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"d486373d9eb2bd248a80b6be7061b56db2f148aa.1466420060.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 7/7] format-patch: use stdout directly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-20T18:57:01Z","receivedAt":"2016-06-20T19:31:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Earlier, we freopen()ed stdout in order to write patches to files.\n> That forced us to duplicate stdout (naming it \"realstdout\") because we\n> *still* wanted to be able to report the file names.\n>\n> As we do not abuse stdout that way anymore, we no longer need to\n> duplicate stdout, either.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/log.c | 6 +-----\n>  1 file changed, 1 insertion(+), 5 deletions(-)\n\nOverall, it was a pleasant read, modulo some details that I\nmentioned separately elsewhere, i.e.\n\n - we do not need to lose flush-or-die\n - we do not need to lose --color=always\n - we do not need to lose macro-ness of putchar()\n\nThanks.\n\n>\n> diff --git a/builtin/log.c b/builtin/log.c\n> index db034a8..5a889d5 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -796,7 +796,6 @@ static int git_format_config(const char *var, const char *value, void *cb)\n>  \treturn git_log_config(var, value, cb);\n>  }\n>  \n> -static FILE *realstdout = NULL;\n>  static const char *output_directory = NULL;\n>  static int outdir_offset;\n>  \n> @@ -822,7 +821,7 @@ static int open_next_file(struct commit *commit, const char *subject,\n>  \t\tfmt_output_subject(&filename, subject, rev);\n>  \n>  \tif (!quiet)\n> -\t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n> +\t\tprintf(\"%s\\n\", filename.buf + outdir_offset);\n>  \n>  \tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n>  \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n> @@ -1629,9 +1628,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \t\tget_patch_ids(&rev, &ids);\n>  \t}\n>  \n> -\tif (!use_stdout)\n> -\t\trealstdout = xfdopen(xdup(1), \"w\");\n> -\n>  \tif (prepare_revision_walk(&rev))\n>  \t\tdie(_(\"revision walk setup failed\"));\n>  \trev.boundary = 1;\n"},{"id":"289728","messageId":"alpine.DEB.2.20.1606210906190.22630@virtualbox","threadId":"42648","inReplyTo":"xmqq4m8nvodo.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T07:15:05Z","receivedAt":"2016-06-21T07:15:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 20 Jun 2016, Junio C Hamano wrote:\n\n> You can probably sell \"when giving out put to file, we will never color\n> the output\" as an improved new world order, but if that is what this\n> change wants to do, it probably deserves a separate patch.\n> \n> I however think you can avoid breaking expectations by people who are\n> not so sensible by overriding only when use_color is set to\n> GIT_COLOR_AUTO, perhaps?\n\nThat is a very convincing argument. So convincing that I wanted to change\nthe patch to guard behind `diff_use_color_default == GIT_COLOR_AUTO`. But\nthat is the wrong variable: the variable that *has* that default value is\ngit_use_color_default, and is private to color.c.\n\nBut then I dug further to determine under which circumstances that\nvariable can be reset to any different value. It turns out that in\nformat-patch's case, it cannot:\n\n\t787570c (format-patch: ignore ui.color, 2011-09-13)\n\nI hope you agree that it will be enough to augment the commit message with\nthis analysis and keep the patch as-is (as per v2, that is)?\n\nCiao,\nDscho\n"},{"id":"289729","messageId":"alpine.DEB.2.20.1606210920510.22630@virtualbox","threadId":"42648","inReplyTo":"xmqqwplju74a.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 1/7] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T07:31:45Z","receivedAt":"2016-06-21T07:32:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 20 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The diff options already know how to print the output anywhere else\n> > than stdout. The same is needed for log output in general, e.g.  when\n> > writing patches to files in `git format-patch`. Let's allow users to\n> > use log_tree_commit() *without* changing global state via freopen().\n> \n> I wonder if this change is actually fixing existing bugs.  Are there\n> cases where diffopt.file is set, i.e. the user expects the output to be\n> sent elsewhere, but the code here unconditionally emits to the standard\n> output?  I suspect that such a bug can be demonstratable in a test or\n> two, if that were the case.\n\nIt is conceivable, but I did not have time to chase those cases down yet.\n\n> I am sort-of surprised that we didn't do this already even though we\n> had diffopt.file for a long time since c0c77734 (Write diff output\n> to a file in struct diff_options, 2008-03-09).\n> \n> Use of freopen() to always write patches through stdout may have\n> been done as a lazy workaround of the issue this patch fixes, but\n> what is surprising to me is that doing it the right way like this\n> patch does is not that much of work.  Perhaps that was done long\n> before c0c77734 was done, which would mean doing it the right way\n> back then when we started using freopen() it would have been a lot\n> more work and we thought taking a short-cut was warranted.\n\nBack when I implemented the feature to write to individual files, I indeed\nused freopen() out of laziness: 0377db7 (Teach fmt-patch to write\nindividual files., 2006-05-05). I could not have used diffopt.file at that\nstage, anyway: that field still was almost two years in the future.\n\n> >  \t\tif (opt->children.name)\n> >  \t\t\tshow_children(opt, commit, abbrev_commit);\n> >  \t\tshow_decorations(opt, commit);\n> >  \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n> > -\t\t\tputchar('\\n');\n> > +\t\t\tfputc('\\n', opt->diffopt.file);\n> \n> Hmph.  putc() is the \"to the given stream\" equivalent of putchar()\n> in the \"send to stdout\" world, not fputc().  I do not see a reason\n> to force the call to go to a function avoiding a possible macro here.\n\nTBH I did not even *know* putc(). It is amazing how you can learn new\nthings about the POSIX API after decades of working with it.\n\n> Likewise for all the new fputc() calls in this series that were\n> originally putchar().\n\nGoes without saying.\n\n> > @@ -880,8 +880,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n> >  \t\tshown = 1;\n> >  \t}\n> >  \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n> > -\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n> > +\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n> >  \topt->loginfo = NULL;\n> > -\tmaybe_flush_or_die(stdout, \"stdout\");\n> > +\tif (opt->diffopt.file == stdout)\n> > +\t\tmaybe_flush_or_die(stdout, \"stdout\");\n> >  \treturn shown;\n> >  }\n> \n> This one looks fishy.\n> \n> Back when we freopen()'ed to write patches only through stdout, we\n> always called maybe_flush_or_die() to make sure that the output is\n> flushed correctly after processing each commit.  This change makes\n> it not to care, which I doubt was what you intended.  Instead, my\n> suspicion is that you didn't want to say \"stdout\" when writing into\n> a file.\n> \n> But even when writing to on-disk files, the code before your series\n> would have said \"stdout\" when it had trouble flushing, so I do not\n> think this new \"if()\" condition is making things better.  If \"it\n> said stdout when having trouble flushing to a file\" were a problem\n> to be fixed, \"let's not say stdout by not even attempting to flush\n> and catch errors when writing to a file\" would not be the right\n> solution, no?\n> \n> Personally, I do not think it hurts if we kept saying 'stdout' here,\n> even when we flush opt->diffopt.file and found a problem.\n\nOkay, I changed it back to be unconditional.\n\nMy original thinking was that we will fclose() the file (if it is not\nstdout) anyway, which implies flushing. But the more I think about it,\nthe more I come to the conclusion that this is more of a side effect,\nbased on deep knowledge of the current code. So I now agree with you that\nit would be \"too clever\".\n\nExpect v3 in a moment.\n\nCiao,\nDscho\n"},{"id":"289731","messageId":"alpine.DEB.2.20.1606210937440.22630@virtualbox","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606210920510.22630@virtualbox","subject":"Re: [PATCH v2 1/7] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T07:38:25Z","receivedAt":"2016-06-21T07:47:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 21 Jun 2016, Johannes Schindelin wrote:\n\n> Expect v3 in a moment.\n\nI am sorry. The regression test suite just sounded red alert. So: do not\nexpect v3 in a moment, but later today.\n\nCiao,\nDscho\n"},{"id":"289737","messageId":"b923b096901728b2f82313a083285ed31df52942.1466505223.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 8/9] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:20Z","receivedAt":"2016-06-21T10:39:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We just taught the relevant functions to respect the diffopt.file field,\nto allow writing somewhere else than stdout. Let's make use of it.\n\nTechnically, we do not need to avoid that call in a builtin: we assume\nthat builtins (as opposed to library functions) are stand-alone programs\nthat may do with their (global) state. Yet, we want to be able to reuse\nthat code in properly lib-ified code, e.g. when converting scripts into\nbuiltins.\n\nFurther, while we did not *have* to touch the cmd_show() and cmd_cherry()\ncode paths (because they do not want to write anywhere but stdout as of\nyet), it just makes sense to be consistent, making it easier and safer to\nmove the code later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 64 ++++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 33 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex abd889b..8dcf205 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -236,7 +236,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)\n \t\tif (rev->commit_format != CMIT_FMT_ONELINE)\n \t\t\tputchar(rev->diffopt.line_termination);\n \t}\n-\tprintf(_(\"Final output: %d %s\\n\"), nr, stage);\n+\tfprintf(rev->diffopt.file, _(\"Final output: %d %s\\n\"), nr, stage);\n }\n \n static struct itimerval early_output_timer;\n@@ -445,7 +445,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)\n \tpp.fmt = rev->commit_format;\n \tpp.date_mode = rev->date_mode;\n \tpp_user_info(&pp, \"Tagger\", &out, buf, get_log_output_encoding());\n-\tprintf(\"%s\", out.buf);\n+\tfprintf(rev->diffopt.file, \"%s\", out.buf);\n \tstrbuf_release(&out);\n }\n \n@@ -456,7 +456,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con\n \tchar *buf;\n \tunsigned long size;\n \n-\tfflush(stdout);\n+\tfflush(rev->diffopt.file);\n \tif (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||\n \t    !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))\n \t\treturn stream_blob_to_fd(1, sha1, NULL, 0);\n@@ -496,7 +496,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)\n \t}\n \n \tif (offset < size)\n-\t\tfwrite(buf + offset, size - offset, 1, stdout);\n+\t\tfwrite(buf + offset, size - offset, 1, rev->diffopt.file);\n \tfree(buf);\n \treturn 0;\n }\n@@ -505,7 +505,8 @@ static int show_tree_object(const unsigned char *sha1,\n \t\tstruct strbuf *base,\n \t\tconst char *pathname, unsigned mode, int stage, void *context)\n {\n-\tprintf(\"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tFILE *file = context;\n+\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n \treturn 0;\n }\n \n@@ -565,7 +566,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stag %s%s\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stag %s%s\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tt->tag,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n@@ -584,12 +585,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\tcase OBJ_TREE:\n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stree %s%s\\n\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stree %s%s\\n\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive((struct tree *)o, \"\", 0, 0, &match_all,\n-\t\t\t\t\tshow_tree_object, NULL);\n+\t\t\t\t\tshow_tree_object, rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\n@@ -799,7 +800,7 @@ static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n-static int reopen_stdout(struct commit *commit, const char *subject,\n+static int open_next_file(struct commit *commit, const char *subject,\n \t\t\t struct rev_info *rev, int quiet)\n {\n \tstruct strbuf filename = STRBUF_INIT;\n@@ -823,7 +824,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,\n \tif (!quiet)\n \t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n \n-\tif (freopen(filename.buf, \"w\", stdout) == NULL)\n+\tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n \n \tstrbuf_release(&filename);\n@@ -882,15 +883,15 @@ static void gen_message_id(struct rev_info *info, char *base)\n \tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n-static void print_signature(void)\n+static void print_signature(FILE *file)\n {\n \tif (!signature || !*signature)\n \t\treturn;\n \n-\tprintf(\"-- \\n%s\", signature);\n+\tfprintf(file, \"-- \\n%s\", signature);\n \tif (signature[strlen(signature)-1] != '\\n')\n-\t\tputchar('\\n');\n-\tputchar('\\n');\n+\t\tputc('\\n', file);\n+\tputc('\\n', file);\n }\n \n static void add_branch_description(struct strbuf *buf, const char *branch_name)\n@@ -959,7 +960,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tcommitter = git_committer_info(0);\n \n \tif (!use_stdout &&\n-\t    reopen_stdout(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n+\t    open_next_file(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n \t\treturn;\n \n \tlog_write_email_headers(rev, head, &pp.subject, &pp.after_subject,\n@@ -982,7 +983,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n \tpp_remainder(&pp, &msg, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n-\tprintf(\"%s\\n\", sb.buf);\n+\tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n \tstrbuf_release(&sb);\n \n@@ -991,6 +992,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tlog.wrap = 72;\n \tlog.in1 = 2;\n \tlog.in2 = 4;\n+\tlog.file = rev->diffopt.file;\n \tfor (i = 0; i < nr; i++)\n \t\tshortlog_add_commit(&log, list[i]);\n \n@@ -1013,8 +1015,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tdiffcore_std(&opts);\n \tdiff_flush(&opts);\n \n-\tprintf(\"\\n\");\n-\tprint_signature();\n+\tfprintf(rev->diffopt.file, \"\\n\");\n+\tprint_signature(rev->diffopt.file);\n }\n \n static const char *clean_message_id(const char *msg_id)\n@@ -1324,7 +1326,7 @@ static void prepare_bases(struct base_tree_info *bases,\n \t}\n }\n \n-static void print_bases(struct base_tree_info *bases)\n+static void print_bases(struct base_tree_info *bases, FILE *file)\n {\n \tint i;\n \n@@ -1333,11 +1335,11 @@ static void print_bases(struct base_tree_info *bases)\n \t\treturn;\n \n \t/* Show the base commit */\n-\tprintf(\"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n+\tfprintf(file, \"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n \n \t/* Show the prerequisite patches */\n \tfor (i = bases->nr_patch_id - 1; i >= 0; i--)\n-\t\tprintf(\"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n+\t\tfprintf(file, \"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n \n \tfree(bases->patch_id);\n \tbases->nr_patch_id = 0;\n@@ -1694,7 +1696,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n \t\t\t\t  origin, nr, list, branch_name, quiet);\n-\t\tprint_bases(&bases);\n+\t\tprint_bases(&bases, rev.diffopt.file);\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n@@ -1740,7 +1742,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \n \t\tif (!use_stdout &&\n-\t\t    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n+\t\t    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n \t\t\tdie(_(\"Failed to create output files\"));\n \t\tshown = log_tree_commit(&rev, commit);\n \t\tfree_commit_buffer(commit);\n@@ -1755,15 +1757,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\trev.shown_one = 0;\n \t\tif (shown) {\n \t\t\tif (rev.mime_boundary)\n-\t\t\t\tprintf(\"\\n--%s%s--\\n\\n\\n\",\n+\t\t\t\tfprintf(rev.diffopt.file, \"\\n--%s%s--\\n\\n\\n\",\n \t\t\t\t       mime_boundary_leader,\n \t\t\t\t       rev.mime_boundary);\n \t\t\telse\n-\t\t\t\tprint_signature();\n-\t\t\tprint_bases(&bases);\n+\t\t\t\tprint_signature(rev.diffopt.file);\n+\t\t\tprint_bases(&bases, rev.diffopt.file);\n \t\t}\n \t\tif (!use_stdout)\n-\t\t\tfclose(stdout);\n+\t\t\tfclose(rev.diffopt.file);\n \t}\n \tfree(list);\n \tfree(branch_name);\n@@ -1795,15 +1797,15 @@ static const char * const cherry_usage[] = {\n };\n \n static void print_commit(char sign, struct commit *commit, int verbose,\n-\t\t\t int abbrev)\n+\t\t\t int abbrev, FILE *file)\n {\n \tif (!verbose) {\n-\t\tprintf(\"%c %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev));\n \t} else {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);\n-\t\tprintf(\"%c %s %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev),\n \t\t       buf.buf);\n \t\tstrbuf_release(&buf);\n@@ -1884,7 +1886,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\tcommit = list->item;\n \t\tif (has_commit_patch_id(commit, &ids))\n \t\t\tsign = '-';\n-\t\tprint_commit(sign, commit, verbose, abbrev);\n+\t\tprint_commit(sign, commit, verbose, abbrev, revs.diffopt.file);\n \t\tlist = list->next;\n \t}\n \n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289738","messageId":"16e3e24e6f81fa334cfe5535c7a21ec50114bd97.1466505223.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 7/9] format-patch: explicitly switch off color when writing to files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:16Z","receivedAt":"2016-06-21T10:39:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We rely on the auto-detection (\"is stdout a terminal?\") to determine\nwhether to use color in the output of format-patch or not. That happens\nto work because we freopen() stdout when redirecting the output to files.\n\nHowever, we are about to fix that work-around, in which case the\nauto-detection has no chance to guess whether to use color or not.\n\nBut then, we do not need to guess to begin with. We know that we do not\nwant to use ANSI color sequences in the output files. So let's just be\nexplicit about our wishes.\n\nIt might be argued that we should only do this when the variable\ngit_use_color_default still has its default value, GIT_COLOR_AUTO.\nAs of 7787570c (format-patch: ignore ui.color, 2011-09-13) we\ndo not allow the ui.color setting to affect format-patch's output,\ntherefore this will always be the case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 099f4f7..abd889b 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1569,6 +1569,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (output_directory) {\n+\t\trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289739","messageId":"alpine.DEB.2.20.1606211236040.22630@virtualbox","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606210937440.22630@virtualbox","subject":"Re: [PATCH v2 1/7] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:39:37Z","receivedAt":"2016-06-21T10:40:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 21 Jun 2016, Johannes Schindelin wrote:\n\n> On Tue, 21 Jun 2016, Johannes Schindelin wrote:\n> \n> > Expect v3 in a moment.\n> \n> I am sorry. The regression test suite just sounded red alert. So: do not\n> expect v3 in a moment, but later today.\n\nSo making maybe_flush_or_die() uncovered a problem with the builtin am: it\ncalled log_tree_commit() after setting diffopt.file *and*\ndiffopt.close_file. The latter forced diff_flush() to close the file, and\nlog_tree_commit() happily tried to flush() the same file stream. In my\nsetup, this triggered a segmentation fault which was really hard to debug\nbecause it happened in a Git subprocess that expected input from stdin\n(and therefore my go-to debugging method to fire up gdb was a bit\nuseless).\n\nIn the end, it not only added two new patches to the patch series, but\nalso opened a big rabbit hole: builtin/am.c could use quite some error\nchecking, I guess... Now I wish I hadn't seen it...\n\nWell, I just sent v3 of the series.\n\nCiao,\nDscho\n"},{"id":"289740","messageId":"2b9fb9fcc8aa1159e4386b99940185019cba3e4a.1466505223.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 9/9] format-patch: use stdout directly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:26Z","receivedAt":"2016-06-21T10:40:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Earlier, we freopen()ed stdout in order to write patches to files.\nThat forced us to duplicate stdout (naming it \"realstdout\") because we\n*still* wanted to be able to report the file names.\n\nAs we do not abuse stdout that way anymore, we no longer need to\nduplicate stdout, either.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 6 +-----\n 1 file changed, 1 insertion(+), 5 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 8dcf205..2bfcc43 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -796,7 +796,6 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \treturn git_log_config(var, value, cb);\n }\n \n-static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n@@ -822,7 +821,7 @@ static int open_next_file(struct commit *commit, const char *subject,\n \t\tfmt_output_subject(&filename, subject, rev);\n \n \tif (!quiet)\n-\t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n+\t\tprintf(\"%s\\n\", filename.buf + outdir_offset);\n \n \tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n@@ -1629,9 +1628,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tget_patch_ids(&rev, &ids);\n \t}\n \n-\tif (!use_stdout)\n-\t\trealstdout = xfdopen(xdup(1), \"w\");\n-\n \tif (prepare_revision_walk(&rev))\n \t\tdie(_(\"revision walk setup failed\"));\n \trev.boundary = 1;\n-- \n2.9.0.118.g0e1a633\n"},{"id":"289741","messageId":"973f9f676225aa98377f607ced1ff474f39b863f.1466505222.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 2/9] Disallow diffopt.close_file when using the log_tree machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:34:54Z","receivedAt":"2016-06-21T10:41:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to teach the log_tree machinery to reuse the diffopt.file\nsetting to output to a file stream different from stdout.\n\nThis means that builtin am can no longer ask the diff machinery to\nclose the file when actually calling the log_tree machinery (which\nwants to flush the very same file stream that would then already be\nclosed).\n\nTo stave off similar problems in the future, report it as a bug if\nlog_tree_commit() is called with a non-zero diffopt.close_file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 6 ++++--\n log-tree.c   | 3 +++\n 2 files changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0e28a62..47d78aa 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1437,6 +1437,7 @@ static int write_commit_patch(const struct am_state *state, struct commit *commi\n {\n \tstruct rev_info rev_info;\n \tFILE *fp;\n+\tint res;\n \n \tfp = fopen(am_path(state, \"patch\"), \"w\");\n \tif (!fp)\n@@ -1453,10 +1454,11 @@ static int write_commit_patch(const struct am_state *state, struct commit *commi\n \tDIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);\n \trev_info.diffopt.use_color = 0;\n \trev_info.diffopt.file = fp;\n-\trev_info.diffopt.close_file = 1;\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n-\treturn log_tree_commit(&rev_info, commit);\n+\tres = log_tree_commit(&rev_info, commit);\n+\tfclose(fp);\n+\treturn res;\n }\n \n /**\ndiff --git a/log-tree.c b/log-tree.c\nindex 78a5381..dc0180d 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -864,6 +864,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tstruct log_info log;\n \tint shown;\n \n+\tif (opt->diffopt.close_file)\n+\t\tdie(\"BUG: close_file is incompatible with log_tree_commit()\");\n+\n \tlog.commit = commit;\n \tlog.parent = NULL;\n \topt->loginfo = &log;\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289742","messageId":"cover.1466505222.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466420060.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:34:44Z","receivedAt":"2016-06-21T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The idea is to allow callers to redirect log-tree's output to a file\nwithout having to freopen() stdout (which would modify global state,\na big no-no-no for library functions).\n\nI reviewed log-tree.c, graph.c, line-log.c, builtin/shortlog.c and\nbuiltin/log.c line by line to ensure that all calls that assumed stdout\npreviously now use the `file` field instead, of course. I would\nwelcome additional eyes to go over the code to confirm that I did not\nmiss anything.\n\nThis patch series ends up removing the freopen() call in the\nformat-patch command, but that is only a by-product. The ulterior motive\nbehind this series is to allow the sequencer to write a `patch` file as\npart of my endeavor to move large chunks of rebase -i into a builtin.\n\nAs mentioned earlier, the `use_color = 0` setting is still kept\nunconditional because format-patch explicitly ignores the ui.color\nsetting.\n\nThis iteration has the following changes with regards to the previous\none:\n\n- putchar() is replaced with putc(), not fputc()\n\n- the maybe_flush_or_die() function is now called by log_tree_commit()\n  even if outputting to a file stream different from stdout\n\n- this uncovered a problem with builtin am, where it asked the diff\n  machinery to close the file stream, but actually called the log_tree\n  machinery (which might mean that this patch series inadvertently fixes\n  a bug where `git am --rebasing` would write the commit message to\n  stdout instead of the `patch` file when erroring out)\n\nThis last point is a bigger issue, actually. There seem to be quite a\nfew function calls in builtin/am.c whose return values that might\nindicate errors are flatly ignored. I see two calls to run_diff_index()\nwhose return value then goes poof unchecked, and several calls to\nwrite_state_text() and write_state_bool() with the same issue. And I did\nnot even try to review the code to that end, all I wanted was to verify\nthat builtin am only has the close_file issue once (it does use it a\nsecond time, but that one is okay because it then calls\nrun_diff_index(), i.e. the diff machinery).\n\nI am embarrassed to admit that these builtin am problems demonstrate\nthat I, as a mentor of the builtin am project, failed to help make the\npatches as good as I expected myself to do.\n\n\nJohannes Schindelin (9):\n  am: stop ignoring errors reported by log_tree_diff()\n  Disallow diffopt.close_file when using the log_tree machinery\n  log-tree: respect diffopt's configured output file stream\n  line-log: respect diffopt's configured output file stream\n  graph: respect the diffopt.file setting\n  shortlog: support outputting to streams other than stdout\n  format-patch: explicitly switch off color when writing to files\n  format-patch: avoid freopen()\n  format-patch: use stdout directly\n\n builtin/am.c       | 18 +++++++++-----\n builtin/log.c      | 71 +++++++++++++++++++++++++++---------------------------\n builtin/shortlog.c | 13 ++++++----\n graph.c            | 30 +++++++++++++----------\n line-log.c         | 34 +++++++++++++-------------\n log-tree.c         | 67 +++++++++++++++++++++++++++------------------------\n shortlog.h         |  1 +\n 7 files changed, 125 insertions(+), 109 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/diffopt.file-v3\nInterdiff vs v2:\n\n diff --git a/builtin/am.c b/builtin/am.c\n index 3dfe70b..47d78aa 100644\n --- a/builtin/am.c\n +++ b/builtin/am.c\n @@ -1433,12 +1433,16 @@ static void get_commit_info(struct am_state *state, struct commit *commit)\n  /**\n   * Writes `commit` as a patch to the state directory's \"patch\" file.\n   */\n -static void write_commit_patch(const struct am_state *state, struct commit *commit)\n +static int write_commit_patch(const struct am_state *state, struct commit *commit)\n  {\n  \tstruct rev_info rev_info;\n  \tFILE *fp;\n +\tint res;\n  \n -\tfp = xfopen(am_path(state, \"patch\"), \"w\");\n +\tfp = fopen(am_path(state, \"patch\"), \"w\");\n +\tif (!fp)\n +\t\treturn error(_(\"Could not open %s for writing\"),\n +\t\t\tam_path(state, \"patch\"));\n  \tinit_revisions(&rev_info, NULL);\n  \trev_info.diff = 1;\n  \trev_info.abbrev = 0;\n @@ -1450,10 +1454,11 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n  \tDIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);\n  \trev_info.diffopt.use_color = 0;\n  \trev_info.diffopt.file = fp;\n -\trev_info.diffopt.close_file = 1;\n  \tadd_pending_object(&rev_info, &commit->object, \"\");\n  \tdiff_setup_done(&rev_info.diffopt);\n -\tlog_tree_commit(&rev_info, commit);\n +\tres = log_tree_commit(&rev_info, commit);\n +\tfclose(fp);\n +\treturn res;\n  }\n  \n  /**\n @@ -1501,13 +1506,14 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)\n  \tunsigned char commit_sha1[GIT_SHA1_RAWSZ];\n  \n  \tif (get_mail_commit_sha1(commit_sha1, mail) < 0)\n -\t\tdie(_(\"could not parse %s\"), mail);\n +\t\treturn error(_(\"could not parse %s\"), mail);\n  \n  \tcommit = lookup_commit_or_die(commit_sha1, mail);\n  \n  \tget_commit_info(state, commit);\n  \n -\twrite_commit_patch(state, commit);\n +\tif (write_commit_patch(state, commit) < 0)\n +\t\treturn -1;\n  \n  \thashcpy(state->orig_commit, commit_sha1);\n  \twrite_state_text(state, \"original-commit\", sha1_to_hex(commit_sha1));\n diff --git a/builtin/log.c b/builtin/log.c\n index 5a889d5..2bfcc43 100644\n --- a/builtin/log.c\n +++ b/builtin/log.c\n @@ -889,8 +889,8 @@ static void print_signature(FILE *file)\n  \n  \tfprintf(file, \"-- \\n%s\", signature);\n  \tif (signature[strlen(signature)-1] != '\\n')\n -\t\tfputc('\\n', file);\n -\tfputc('\\n', file);\n +\t\tputc('\\n', file);\n +\tputc('\\n', file);\n  }\n  \n  static void add_branch_description(struct strbuf *buf, const char *branch_name)\n diff --git a/builtin/shortlog.c b/builtin/shortlog.c\n index 4c68ba7..39d74fe 100644\n --- a/builtin/shortlog.c\n +++ b/builtin/shortlog.c\n @@ -328,7 +328,7 @@ void shortlog_output(struct shortlog *log)\n  \t\t\t\telse\n  \t\t\t\t\tfprintf(log->file, \"      %s\\n\", msg);\n  \t\t\t}\n -\t\t\tfputc('\\n', log->file);\n +\t\t\tputc('\\n', log->file);\n  \t\t\tonelines->strdup_strings = 1;\n  \t\t\tstring_list_clear(onelines, 0);\n  \t\t\tfree(onelines);\n diff --git a/graph.c b/graph.c\n index 8ae56bc..8ad8ba3 100644\n --- a/graph.c\n +++ b/graph.c\n @@ -1196,7 +1196,7 @@ void graph_show_commit(struct git_graph *graph)\n  \t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n  \t\t\tgraph->revs->diffopt.file);\n  \t\tif (!shown_commit_line)\n -\t\t\tfputc('\\n', graph->revs->diffopt.file);\n +\t\t\tputc('\\n', graph->revs->diffopt.file);\n  \t\tstrbuf_setlen(&msgbuf, 0);\n  \t}\n  \n @@ -1246,7 +1246,7 @@ int graph_show_remainder(struct git_graph *graph)\n  \t\tshown = 1;\n  \n  \t\tif (!graph_is_commit_finished(graph))\n -\t\t\tfputc('\\n', graph->revs->diffopt.file);\n +\t\t\tputc('\\n', graph->revs->diffopt.file);\n  \t\telse\n  \t\t\tbreak;\n  \t}\n @@ -1322,7 +1322,7 @@ void graph_show_commit_msg(struct git_graph *graph,\n  \t\t * new line.\n  \t\t */\n  \t\tif (!newline_terminated)\n -\t\t\tfputc('\\n', graph->revs->diffopt.file);\n +\t\t\tputc('\\n', graph->revs->diffopt.file);\n  \n  \t\tgraph_show_remainder(graph);\n  \n @@ -1330,6 +1330,6 @@ void graph_show_commit_msg(struct git_graph *graph,\n  \t\t * If sb ends with a newline, our output should too.\n  \t\t */\n  \t\tif (newline_terminated)\n -\t\t\tfputc('\\n', graph->revs->diffopt.file);\n +\t\t\tputc('\\n', graph->revs->diffopt.file);\n  \t}\n  }\n diff --git a/line-log.c b/line-log.c\n index c3b8563..e62a7f4 100644\n --- a/line-log.c\n +++ b/line-log.c\n @@ -854,10 +854,10 @@ static void print_line(const char *prefix, char first,\n  \n  \tfputs(prefix, file);\n  \tfputs(color, file);\n -\tfputc(first, file);\n +\tputc(first, file);\n  \tfwrite(begin, 1, end-begin, file);\n  \tfputs(reset, file);\n -\tfputc('\\n', file);\n +\tputc('\\n', file);\n  \tif (!had_nl)\n  \t\tfputs(\"\\\\ No newline at end of file\\n\", file);\n  }\n diff --git a/log-tree.c b/log-tree.c\n index 968428a..530297d 100644\n --- a/log-tree.c\n +++ b/log-tree.c\n @@ -560,10 +560,10 @@ void show_log(struct rev_info *opt)\n  \t\t\tshow_children(opt, commit, abbrev_commit);\n  \t\tshow_decorations(opt, commit);\n  \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n -\t\t\tfputc('\\n', opt->diffopt.file);\n +\t\t\tputc('\\n', opt->diffopt.file);\n  \t\t\tgraph_show_remainder(opt->graph);\n  \t\t}\n -\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n +\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n  \t\treturn;\n  \t}\n  \n @@ -589,7 +589,7 @@ void show_log(struct rev_info *opt)\n  \t\tif (opt->diffopt.line_termination == '\\n' &&\n  \t\t    !opt->missing_newline)\n  \t\t\tgraph_show_padding(opt->graph);\n -\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n +\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n  \t}\n  \topt->shown_one = 1;\n  \n @@ -626,9 +626,9 @@ void show_log(struct rev_info *opt)\n  \t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);\n  \t\tshow_decorations(opt, commit);\n  \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n -\t\t\tfputc(' ', opt->diffopt.file);\n +\t\t\tputc(' ', opt->diffopt.file);\n  \t\t} else {\n -\t\t\tfputc('\\n', opt->diffopt.file);\n +\t\t\tputc('\\n', opt->diffopt.file);\n  \t\t\tgraph_show_oneline(opt->graph);\n  \t\t}\n  \t\tif (opt->reflog_info) {\n @@ -722,7 +722,7 @@ void show_log(struct rev_info *opt)\n  \tif (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {\n  \t\tif (!opt->missing_newline)\n  \t\t\tgraph_show_padding(opt->graph);\n -\t\tfputc(opt->diffopt.line_termination, opt->diffopt.file);\n +\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n  \t}\n  \n  \tstrbuf_release(&msgbuf);\n @@ -775,7 +775,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n  \t\t\tif (!opt->shown_dashes &&\n  \t\t\t    (pch & opt->diffopt.output_format) == pch)\n  \t\t\t\tfprintf(opt->diffopt.file, \"---\");\n -\t\t\tfputc('\\n', opt->diffopt.file);\n +\t\t\tputc('\\n', opt->diffopt.file);\n  \t\t}\n  \t}\n  \tdiff_flush(&opt->diffopt);\n @@ -864,6 +864,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n  \tstruct log_info log;\n  \tint shown;\n  \n +\tif (opt->diffopt.close_file)\n +\t\tdie(\"BUG: close_file is incompatible with log_tree_commit()\");\n +\n  \tlog.commit = commit;\n  \tlog.parent = NULL;\n  \topt->loginfo = &log;\n @@ -882,7 +885,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n  \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n  \t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n  \topt->loginfo = NULL;\n -\tif (opt->diffopt.file == stdout)\n -\t\tmaybe_flush_or_die(stdout, \"stdout\");\n +\tmaybe_flush_or_die(opt->diffopt.file, \"stdout\");\n  \treturn shown;\n  }\n\n-- \n2.9.0.118.g0e1a633\n\nbase-commit: ab7797dbe95fff38d9265869ea367020046db118\n"},{"id":"289743","messageId":"228f28864245f89a0a641202472bf78fdcd8a3aa.1466505222.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 4/9] line-log: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:03Z","receivedAt":"2016-06-21T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff machinery can optionally output to a file stream other than\nstdout, by overriding diffopt.file. In such a case, the rest of the\nlog tree machinery should also write to that stream.\n\nCurrently, there is no user of the line level log that wants to\nredirect output to a file. Therefore, one might argue that it is\nsuperfluous to support that now. However, it is better to be\nconsistent now, rather than to face hard-to-debug problems later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n line-log.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/line-log.c b/line-log.c\nindex bbe31ed..e62a7f4 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -841,7 +841,7 @@ static char *get_nth_line(long line, unsigned long *ends, void *data)\n \n static void print_line(const char *prefix, char first,\n \t\t       long line, unsigned long *ends, void *data,\n-\t\t       const char *color, const char *reset)\n+\t\t       const char *color, const char *reset, FILE *file)\n {\n \tchar *begin = get_nth_line(line, ends, data);\n \tchar *end = get_nth_line(line+1, ends, data);\n@@ -852,14 +852,14 @@ static void print_line(const char *prefix, char first,\n \t\thad_nl = 1;\n \t}\n \n-\tfputs(prefix, stdout);\n-\tfputs(color, stdout);\n-\tputchar(first);\n-\tfwrite(begin, 1, end-begin, stdout);\n-\tfputs(reset, stdout);\n-\tputchar('\\n');\n+\tfputs(prefix, file);\n+\tfputs(color, file);\n+\tputc(first, file);\n+\tfwrite(begin, 1, end-begin, file);\n+\tfputs(reset, file);\n+\tputc('\\n', file);\n \tif (!had_nl)\n-\t\tfputs(\"\\\\ No newline at end of file\\n\", stdout);\n+\t\tfputs(\"\\\\ No newline at end of file\\n\", file);\n }\n \n static char *output_prefix(struct diff_options *opt)\n@@ -898,12 +898,12 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\tfill_line_ends(pair->one, &p_lines, &p_ends);\n \tfill_line_ends(pair->two, &t_lines, &t_ends);\n \n-\tprintf(\"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n-\tprintf(\"%s%s--- %s%s%s\\n\", prefix, c_meta,\n+\tfprintf(opt->file, \"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s--- %s%s%s\\n\", prefix, c_meta,\n \t       pair->one->sha1_valid ? \"a/\" : \"\",\n \t       pair->one->sha1_valid ? pair->one->path : \"/dev/null\",\n \t       c_reset);\n-\tprintf(\"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n \tfor (i = 0; i < range->ranges.nr; i++) {\n \t\tlong p_start, p_end;\n \t\tlong t_start = range->ranges.ranges[i].start;\n@@ -945,7 +945,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t}\n \n \t\t/* Now output a diff hunk for this range */\n-\t\tprintf(\"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n+\t\tfprintf(opt->file, \"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n \t\t       prefix, c_frag,\n \t\t       p_start+1, p_end-p_start, t_start+1, t_end-t_start,\n \t\t       c_reset);\n@@ -953,18 +953,18 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t\tint k;\n \t\t\tfor (; t_cur < diff->target.ranges[j].start; t_cur++)\n \t\t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_context, c_reset);\n+\t\t\t\t\t   c_context, c_reset, opt->file);\n \t\t\tfor (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)\n \t\t\t\tprint_line(prefix, '-', k, p_ends, pair->one->data,\n-\t\t\t\t\t   c_old, c_reset);\n+\t\t\t\t\t   c_old, c_reset, opt->file);\n \t\t\tfor (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)\n \t\t\t\tprint_line(prefix, '+', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_new, c_reset);\n+\t\t\t\t\t   c_new, c_reset, opt->file);\n \t\t\tj++;\n \t\t}\n \t\tfor (; t_cur < t_end; t_cur++)\n \t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t   c_context, c_reset);\n+\t\t\t\t   c_context, c_reset, opt->file);\n \t}\n \n \tfree(p_ends);\n@@ -977,7 +977,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n  */\n static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)\n {\n-\tputs(output_prefix(&rev->diffopt));\n+\tfprintf(rev->diffopt.file, \"%s\\n\", output_prefix(&rev->diffopt));\n \twhile (range) {\n \t\tdump_diff_hacky_one(rev, range);\n \t\trange = range->next;\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289744","messageId":"eb881db815d04bc48b4bb86161890f52d9ba14fa.1466505222.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 3/9] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:34:59Z","receivedAt":"2016-06-21T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff options already know how to print the output anywhere else\nthan stdout. The same is needed for log output in general, e.g.\nwhen writing patches to files in `git format-patch`. Let's allow\nusers to use log_tree_commit() *without* changing global state via\nfreopen().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n log-tree.c | 64 +++++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 32 insertions(+), 32 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex dc0180d..530297d 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -159,12 +159,12 @@ void load_ref_decorations(int flags)\n \t}\n }\n \n-static void show_parents(struct commit *commit, int abbrev)\n+static void show_parents(struct commit *commit, int abbrev, FILE *file)\n {\n \tstruct commit_list *p;\n \tfor (p = commit->parents; p ; p = p->next) {\n \t\tstruct commit *parent = p->item;\n-\t\tprintf(\" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n+\t\tfprintf(file, \" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -172,7 +172,7 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n {\n \tstruct commit_list *p = lookup_decoration(&opt->children, &commit->object);\n \tfor ( ; p; p = p->next) {\n-\t\tprintf(\" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n+\t\tfprintf(opt->diffopt.file, \" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -286,11 +286,11 @@ void show_decorations(struct rev_info *opt, struct commit *commit)\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\t\tfprintf(opt->diffopt.file, \"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n \tformat_decorations(&sb, commit, opt->diffopt.use_color);\n-\tfputs(sb.buf, stdout);\n+\tfputs(sb.buf, opt->diffopt.file);\n \tstrbuf_release(&sb);\n }\n \n@@ -364,18 +364,18 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\tsubject = \"Subject: \";\n \t}\n \n-\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n+\tfprintf(opt->diffopt.file, \"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n \tgraph_show_oneline(opt->graph);\n \tif (opt->message_id) {\n-\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n+\t\tfprintf(opt->diffopt.file, \"Message-Id: <%s>\\n\", opt->message_id);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \tif (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {\n \t\tint i, n;\n \t\tn = opt->ref_message_ids->nr;\n-\t\tprintf(\"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n+\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n \t\tfor (i = 0; i < n; i++)\n-\t\t\tprintf(\"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n+\t\t\tfprintf(opt->diffopt.file, \"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n \t\t\t       opt->ref_message_ids->items[i].string);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n@@ -432,7 +432,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)\n \treset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);\n \twhile (*bol) {\n \t\teol = strchrnul(bol, '\\n');\n-\t\tprintf(\"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n+\t\tfprintf(opt->diffopt.file, \"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n \t\t       *eol ? \"\\n\" : \"\");\n \t\tgraph_show_oneline(opt->graph);\n \t\tbol = (*eol) ? (eol + 1) : eol;\n@@ -553,17 +553,17 @@ void show_log(struct rev_info *opt)\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n-\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);\n+\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_remainder(opt->graph);\n \t\t}\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -589,7 +589,7 @@ void show_log(struct rev_info *opt)\n \t\tif (opt->diffopt.line_termination == '\\n' &&\n \t\t    !opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \topt->shown_one = 1;\n \n@@ -607,28 +607,28 @@ void show_log(struct rev_info *opt)\n \t\tlog_write_email_headers(opt, commit, &ctx.subject, &extra_headers,\n \t\t\t\t\t&ctx.need_8bit_cte);\n \t} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);\n \t\tif (opt->commit_format != CMIT_FMT_ONELINE)\n-\t\t\tfputs(\"commit \", stdout);\n+\t\t\tfputs(\"commit \", opt->diffopt.file);\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n \t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),\n-\t\t      stdout);\n+\t\t      opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tif (parent)\n-\t\t\tprintf(\" (from %s)\",\n+\t\t\tfprintf(opt->diffopt.file, \" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.oid.hash,\n \t\t\t\t\t\t  abbrev_commit));\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n-\t\t\tputchar(' ');\n+\t\t\tputc(' ', opt->diffopt.file);\n \t\t} else {\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_oneline(opt->graph);\n \t\t}\n \t\tif (opt->reflog_info) {\n@@ -702,7 +702,7 @@ void show_log(struct rev_info *opt)\n \t}\n \n \tif (opt->show_log_size) {\n-\t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n+\t\tfprintf(opt->diffopt.file, \"log size %i\\n\", (int)msgbuf.len);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \n@@ -718,11 +718,11 @@ void show_log(struct rev_info *opt)\n \tif (opt->graph)\n \t\tgraph_show_commit_msg(opt->graph, &msgbuf);\n \telse\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, opt->diffopt.file);\n \tif (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {\n \t\tif (!opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \n \tstrbuf_release(&msgbuf);\n@@ -759,7 +759,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t\tstruct strbuf *msg = NULL;\n \t\t\t\tmsg = opt->diffopt.output_prefix(&opt->diffopt,\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\tfwrite(msg->buf, msg->len, 1, opt->diffopt.file);\n \t\t\t}\n \n \t\t\t/*\n@@ -774,8 +774,8 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t */\n \t\t\tif (!opt->shown_dashes &&\n \t\t\t    (pch & opt->diffopt.output_format) == pch)\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n+\t\t\t\tfprintf(opt->diffopt.file, \"---\");\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n@@ -875,7 +875,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\treturn line_log_print(opt, commit);\n \n \tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n@@ -883,8 +883,8 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\tshown = 1;\n \t}\n \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n-\tmaybe_flush_or_die(stdout, \"stdout\");\n+\tmaybe_flush_or_die(opt->diffopt.file, \"stdout\");\n \treturn shown;\n }\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289745","messageId":"e1d68fa14e7938f7e3a07bb85db28aa84017710d.1466505222.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 1/9] am: stop ignoring errors reported by log_tree_diff()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:34:50Z","receivedAt":"2016-06-21T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is never a good idea to ignore errors, so let's handle them.\n\nWhile at it, handle fopen() errors more gently (i.e. give the caller a\nchance to do something about it rather than die()ing).\n\nNote: there are more places in the builtin am code that ignore\nerrors returned from library functions. Fixing those is outside the\npurview of the current patch series, though.\n\nCc: Paul Tan <pyokagan@gmail.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 3dfe70b..0e28a62 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1433,12 +1433,15 @@ static void get_commit_info(struct am_state *state, struct commit *commit)\n /**\n  * Writes `commit` as a patch to the state directory's \"patch\" file.\n  */\n-static void write_commit_patch(const struct am_state *state, struct commit *commit)\n+static int write_commit_patch(const struct am_state *state, struct commit *commit)\n {\n \tstruct rev_info rev_info;\n \tFILE *fp;\n \n-\tfp = xfopen(am_path(state, \"patch\"), \"w\");\n+\tfp = fopen(am_path(state, \"patch\"), \"w\");\n+\tif (!fp)\n+\t\treturn error(_(\"Could not open %s for writing\"),\n+\t\t\tam_path(state, \"patch\"));\n \tinit_revisions(&rev_info, NULL);\n \trev_info.diff = 1;\n \trev_info.abbrev = 0;\n@@ -1453,7 +1456,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \trev_info.diffopt.close_file = 1;\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n-\tlog_tree_commit(&rev_info, commit);\n+\treturn log_tree_commit(&rev_info, commit);\n }\n \n /**\n@@ -1501,13 +1504,14 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)\n \tunsigned char commit_sha1[GIT_SHA1_RAWSZ];\n \n \tif (get_mail_commit_sha1(commit_sha1, mail) < 0)\n-\t\tdie(_(\"could not parse %s\"), mail);\n+\t\treturn error(_(\"could not parse %s\"), mail);\n \n \tcommit = lookup_commit_or_die(commit_sha1, mail);\n \n \tget_commit_info(state, commit);\n \n-\twrite_commit_patch(state, commit);\n+\tif (write_commit_patch(state, commit) < 0)\n+\t\treturn -1;\n \n \thashcpy(state->orig_commit, commit_sha1);\n \twrite_state_text(state, \"original-commit\", sha1_to_hex(commit_sha1));\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289746","messageId":"e568e34c23eaf9a40a2d17be7df08426b74a27dc.1466505223.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 6/9] shortlog: support outputting to streams other than stdout","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:12Z","receivedAt":"2016-06-21T10:41:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to avoid freopen() in `git format-patch`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/shortlog.c | 13 ++++++++-----\n shortlog.h         |  1 +\n 2 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex bfc082e..39d74fe 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -229,6 +229,7 @@ void shortlog_init(struct shortlog *log)\n \tlog->wrap = DEFAULT_WRAPLEN;\n \tlog->in1 = DEFAULT_INDENT1;\n \tlog->in2 = DEFAULT_INDENT2;\n+\tlog->file = stdout;\n }\n \n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n@@ -310,22 +311,24 @@ void shortlog_output(struct shortlog *log)\n \tfor (i = 0; i < log->list.nr; i++) {\n \t\tconst struct string_list_item *item = &log->list.items[i];\n \t\tif (log->summary) {\n-\t\t\tprintf(\"%6d\\t%s\\n\", (int)UTIL_TO_INT(item), item->string);\n+\t\t\tfprintf(log->file, \"%6d\\t%s\\n\",\n+\t\t\t\t(int)UTIL_TO_INT(item), item->string);\n \t\t} else {\n \t\t\tstruct string_list *onelines = item->util;\n-\t\t\tprintf(\"%s (%d):\\n\", item->string, onelines->nr);\n+\t\t\tfprintf(log->file, \"%s (%d):\\n\",\n+\t\t\t\titem->string, onelines->nr);\n \t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n \t\t\t\tconst char *msg = onelines->items[j].string;\n \n \t\t\t\tif (log->wrap_lines) {\n \t\t\t\t\tstrbuf_reset(&sb);\n \t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n-\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n+\t\t\t\t\tfwrite(sb.buf, sb.len, 1, log->file);\n \t\t\t\t}\n \t\t\t\telse\n-\t\t\t\t\tprintf(\"      %s\\n\", msg);\n+\t\t\t\t\tfprintf(log->file, \"      %s\\n\", msg);\n \t\t\t}\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', log->file);\n \t\t\tonelines->strdup_strings = 1;\n \t\t\tstring_list_clear(onelines, 0);\n \t\t\tfree(onelines);\ndiff --git a/shortlog.h b/shortlog.h\nindex de4f86f..5a326c6 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -17,6 +17,7 @@ struct shortlog {\n \tchar *common_repo_prefix;\n \tint email;\n \tstruct string_list mailmap;\n+\tFILE *file;\n };\n \n void shortlog_init(struct shortlog *log);\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289747","messageId":"761d203a775a437832a5178ae4e716a7a34c7e67.1466505223.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 5/9] graph: respect the diffopt.file setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T10:35:08Z","receivedAt":"2016-06-21T10:41:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the caller overrides diffopt.file (which defaults to stdout),\nthe diff machinery already redirects its output, and the graph display\nshould also write to that file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n graph.c | 30 +++++++++++++++++-------------\n 1 file changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex 1350bdd..8ad8ba3 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -17,8 +17,8 @@\n static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);\n \n /*\n- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n- * first will be prefixed with the graph output.\n+ * Print a strbuf.  If the graph is non-NULL, all lines but the first will be\n+ * prefixed with the graph output.\n  *\n  * If the strbuf ends with a newline, the output will end after this\n  * newline.  A new graph line will not be printed after the final newline.\n@@ -1193,9 +1193,10 @@ void graph_show_commit(struct git_graph *graph)\n \n \twhile (!shown_commit_line && !graph_is_commit_finished(graph)) {\n \t\tshown_commit_line = graph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tif (!shown_commit_line)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t}\n \n@@ -1210,7 +1211,7 @@ void graph_show_oneline(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_next_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1222,7 +1223,7 @@ void graph_show_padding(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_padding_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1239,12 +1240,13 @@ int graph_show_remainder(struct git_graph *graph)\n \n \tfor (;;) {\n \t\tgraph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t\tshown = 1;\n \n \t\tif (!graph_is_commit_finished(graph))\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t\telse\n \t\t\tbreak;\n \t}\n@@ -1259,7 +1261,8 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \tchar *p;\n \n \tif (!graph) {\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1277,7 +1280,7 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \t\t} else {\n \t\t\tlen = (sb->buf + sb->len) - p;\n \t\t}\n-\t\tfwrite(p, sizeof(char), len, stdout);\n+\t\tfwrite(p, sizeof(char), len, graph->revs->diffopt.file);\n \t\tif (next_p && *next_p != '\\0')\n \t\t\tgraph_show_oneline(graph);\n \t\tp = next_p;\n@@ -1297,7 +1300,8 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * CMIT_FMT_USERFORMAT are already missing a terminating\n \t\t * newline.  All of the other formats should have it.\n \t\t */\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1318,7 +1322,7 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * new line.\n \t\t */\n \t\tif (!newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \n \t\tgraph_show_remainder(graph);\n \n@@ -1326,6 +1330,6 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * If sb ends with a newline, our output should too.\n \t\t */\n \t\tif (newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t}\n }\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289771","messageId":"alpine.DEB.2.20.1606211555380.22630@virtualbox","threadId":"42648","inReplyTo":"CACRoPnRvp7oguE2w2mcsEZfaX_fni8UhFCdsGQ3ZaijQprSHog@mail.gmail.com","subject":"Re: [PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-21T14:12:12Z","receivedAt":"2016-06-21T14:13:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn Tue, 21 Jun 2016, Paul Tan wrote:\n\n> On Tue, Jun 21, 2016 at 6:34 PM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > - this uncovered a problem with builtin am, where it asked the diff\n> >   machinery to close the file stream, but actually called the log_tree\n> >   machinery (which might mean that this patch series inadvertently fixes\n> >   a bug where `git am --rebasing` would write the commit message to\n> >   stdout instead of the `patch` file when erroring out)\n> \n> Please correct me if I'm wrong: looking at log-tree.c, the commit\n> message will not be printed when no_commit_id = 1, isn't it?\n> This is because we do not hit the code paths that write to stdout since\n> show_log() is not called.\n\nWhy does builtin/am.c use log_tree_commit(), then? Why not simply run\nthings through the diff machinery?\n\n> Also, the return value of log_tree_commit() is actually a boolean\n> value, not an error status value, isn't it?\n\nIt is not really a boolean, no. Sure, at the moment, it happens to return\neither 0 or 1. You can figure that out by following the call paths all the\nway to do_diff_combined() or line_log_print().\n\nThe key words are: at the moment.\n\nWe do find more and more places where library functions call die() in case\nof errors, and it hurts us. Badly. That is why I, among others, try to\nremedy the situation by converting these calls to \"return error()\"\nstatements.\n\nThe log_tree functions are prepared for that: they return non-negative\nvalues in case of success.\n\nThe callers are not really prepared for that, hence my complaints.\n\n> > This last point is a bigger issue, actually. There seem to be quite a\n> > few function calls in builtin/am.c whose return values that might\n> > indicate errors are flatly ignored. I see two calls to run_diff_index()\n> > whose return value then goes poof unchecked,\n> \n> Thanks, future-proofing the builtin/am.c code is good, in case\n> run_diff_index() is updated to not call exit(128) on error in the\n> future.\n\nAnd run_diff_cache(). And read_ref_at(). And rerere(). And\nsetup_revisions(). And get_sha1().\n\n> > and several calls to write_state_text() and write_state_bool() with\n> > the same issue.\n> \n> These functions will die() on error\n\nIndeed. And I do not think that is a good practice.\n\n> > And I did not even try to review the code to that end, all I wanted\n> > was to verify that builtin am only has the close_file issue once (it\n> > does use it a second time, but that one is okay because it then calls\n> > run_diff_index(), i.e. the diff machinery).\n> >\n> > I am embarrassed to admit that these builtin am problems demonstrate\n> > that I, as a mentor of the builtin am project, failed to help make the\n> > patches as good as I expected myself to do.\n> \n> Sorry to disappoint you :-(\n\nYou misunderstood. I am not disappointed in you. *I* did a lousy job. Not\nonly mentoring, but I also obviously failed to make things fun enough for\nyou.\n\nMy apologies,\nDscho\n"},{"id":"289772","messageId":"CACRoPnRvp7oguE2w2mcsEZfaX_fni8UhFCdsGQ3ZaijQprSHog@mail.gmail.com","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2016-06-21T13:47:11Z","receivedAt":"2016-06-21T14:13:39Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi Johannes,\n\nOn Tue, Jun 21, 2016 at 6:34 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> - this uncovered a problem with builtin am, where it asked the diff\n>   machinery to close the file stream, but actually called the log_tree\n>   machinery (which might mean that this patch series inadvertently fixes\n>   a bug where `git am --rebasing` would write the commit message to\n>   stdout instead of the `patch` file when erroring out)\n\nPlease correct me if I'm wrong: looking at log-tree.c, the commit\nmessage will not be printed when no_commit_id = 1, isn't it? This is\nbecause we do not hit the code paths that write to stdout since\nshow_log() is not called.\n\nAlso, the return value of log_tree_commit() is actually a boolean\nvalue, not an error status value, isn't it?\n\n> This last point is a bigger issue, actually. There seem to be quite a\n> few function calls in builtin/am.c whose return values that might\n> indicate errors are flatly ignored. I see two calls to run_diff_index()\n> whose return value then goes poof unchecked,\n\nThanks, future-proofing the builtin/am.c code is good, in case\nrun_diff_index() is updated to not call exit(128) on error in the\nfuture.\n\n> and several calls to\n> write_state_text() and write_state_bool() with the same issue.\n\nThese functions will die() on error, so checking their error code is\nnot really useful. I'm not opposed to changing them to use the\nwrite_file_gently() version though, although I don't see a need to\nunless builtin/am.c is being libified.\n\n> And I did\n> not even try to review the code to that end, all I wanted was to verify\n> that builtin am only has the close_file issue once (it does use it a\n> second time, but that one is okay because it then calls\n> run_diff_index(), i.e. the diff machinery).\n>\n> I am embarrassed to admit that these builtin am problems demonstrate\n> that I, as a mentor of the builtin am project, failed to help make the\n> patches as good as I expected myself to do.\n\nSorry to disappoint you :-(\n\nRegards,\nPaul\n"},{"id":"289784","messageId":"xmqqvb12qyeu.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606210906190.22630@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-21T16:50:17Z","receivedAt":"2016-06-21T16:50:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> That is a very convincing argument. So convincing that I wanted to change\n> the patch to guard behind `diff_use_color_default == GIT_COLOR_AUTO`.\n\nI actually was expecting, instead of your:\n\n \tif (output_directory) {\n+\t\trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n\nan update would say\n\n \tif (output_directory) {\n\t\tif (rev.diffopt.use_color == GIT_COLOR_AUTO)\n                \trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n\nI didn't expect you to check diff_use_color_default exactly for the\nreason why you say \"But that is the wrong variable\".\n"},{"id":"289790","messageId":"xmqqfus6quii.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"973f9f676225aa98377f607ced1ff474f39b863f.1466505222.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 2/9] Disallow diffopt.close_file when using the log_tree machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-21T18:14:29Z","receivedAt":"2016-06-21T18:29:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> We are about to teach the log_tree machinery to reuse the diffopt.file\n> setting to output to a file stream different from stdout.\n>\n> This means that builtin am can no longer ask the diff machinery to\n> close the file when actually calling the log_tree machinery (which\n> wants to flush the very same file stream that would then already be\n> closed).\n\nSorry for being slow, but I am not sure why the first paragraph has\nto mean the second paragraph.  This existing caller opens a new\nstream, sets .fp to it, and expects that the log_tree_commit() to\nclose it if told by setting .close_file to true, all of which sounds\nsensible.\n\nIf a codepath wants to use the same stream for two or more calls to\nlog_tree by pointing the stream with .fp, it would be of course a\nproblem for the caller to set .close_file to true in its first call,\nas .fp will be closed and no longer usable for second and subsequent\ncall, and that would be a bug, but for a single-shot call it feels\nentirely a sensible request to make, no?\n\nObviously you have looked at the codepaths involved a lot longer\nthan I did, and I do not doubt your conclusion, but I cannot quite\nconvince myself with the above explanation.\n\nThe option parser of \"git diff\" family sets ->close_file to true\nwhen the --output option is given.\n\nWouldn't this patch break \"git log --output=foo -3\"?\n\n> To stave off similar problems in the future, report it as a bug if\n> log_tree_commit() is called with a non-zero diffopt.close_file.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/am.c | 6 ++++--\n>  log-tree.c   | 3 +++\n>  2 files changed, 7 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 0e28a62..47d78aa 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -1437,6 +1437,7 @@ static int write_commit_patch(const struct am_state *state, struct commit *commi\n>  {\n>  \tstruct rev_info rev_info;\n>  \tFILE *fp;\n> +\tint res;\n>  \n>  \tfp = fopen(am_path(state, \"patch\"), \"w\");\n>  \tif (!fp)\n> @@ -1453,10 +1454,11 @@ static int write_commit_patch(const struct am_state *state, struct commit *commi\n>  \tDIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);\n>  \trev_info.diffopt.use_color = 0;\n>  \trev_info.diffopt.file = fp;\n> -\trev_info.diffopt.close_file = 1;\n>  \tadd_pending_object(&rev_info, &commit->object, \"\");\n>  \tdiff_setup_done(&rev_info.diffopt);\n> -\treturn log_tree_commit(&rev_info, commit);\n> +\tres = log_tree_commit(&rev_info, commit);\n> +\tfclose(fp);\n> +\treturn res;\n>  }\n>  \n>  /**\n> diff --git a/log-tree.c b/log-tree.c\n> index 78a5381..dc0180d 100644\n> --- a/log-tree.c\n> +++ b/log-tree.c\n> @@ -864,6 +864,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n>  \tstruct log_info log;\n>  \tint shown;\n>  \n> +\tif (opt->diffopt.close_file)\n> +\t\tdie(\"BUG: close_file is incompatible with log_tree_commit()\");\n> +\n>  \tlog.commit = commit;\n>  \tlog.parent = NULL;\n>  \topt->loginfo = &log;\n"},{"id":"289796","messageId":"xmqqporapdu8.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"e1d68fa14e7938f7e3a07bb85db28aa84017710d.1466505222.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 1/9] am: stop ignoring errors reported by log_tree_diff()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-21T18:59:59Z","receivedAt":"2016-06-21T19:01:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Note: there are more places in the builtin am code that ignore\n> errors returned from library functions. Fixing those is outside the\n> purview of the current patch series, though.\n\nThe caller of parse_mail() and parse_mail_rebase() is not prepared\nto see an error code in the returned value from these function,\nwhich are to return a boolean telling the caller to skip or use the\npatch file.  At least the caller needs to notice negative return and\nmade to die/exit(128), instead of silently skipping a corrupt or\nunopenable patch, no?  Otherwise this will change the behaviour.\n\n> Cc: Paul Tan <pyokagan@gmail.com>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/am.c | 14 +++++++++-----\n>  1 file changed, 9 insertions(+), 5 deletions(-)\n>\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 3dfe70b..0e28a62 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -1433,12 +1433,15 @@ static void get_commit_info(struct am_state *state, struct commit *commit)\n>  /**\n>   * Writes `commit` as a patch to the state directory's \"patch\" file.\n>   */\n> -static void write_commit_patch(const struct am_state *state, struct commit *commit)\n> +static int write_commit_patch(const struct am_state *state, struct commit *commit)\n>  {\n>  \tstruct rev_info rev_info;\n>  \tFILE *fp;\n>  \n> -\tfp = xfopen(am_path(state, \"patch\"), \"w\");\n> +\tfp = fopen(am_path(state, \"patch\"), \"w\");\n> +\tif (!fp)\n> +\t\treturn error(_(\"Could not open %s for writing\"),\n> +\t\t\tam_path(state, \"patch\"));\n>  \tinit_revisions(&rev_info, NULL);\n>  \trev_info.diff = 1;\n>  \trev_info.abbrev = 0;\n> @@ -1453,7 +1456,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n>  \trev_info.diffopt.close_file = 1;\n>  \tadd_pending_object(&rev_info, &commit->object, \"\");\n>  \tdiff_setup_done(&rev_info.diffopt);\n> -\tlog_tree_commit(&rev_info, commit);\n> +\treturn log_tree_commit(&rev_info, commit);\n>  }\n>  \n>  /**\n> @@ -1501,13 +1504,14 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)\n>  \tunsigned char commit_sha1[GIT_SHA1_RAWSZ];\n>  \n>  \tif (get_mail_commit_sha1(commit_sha1, mail) < 0)\n> -\t\tdie(_(\"could not parse %s\"), mail);\n> +\t\treturn error(_(\"could not parse %s\"), mail);\n>  \n>  \tcommit = lookup_commit_or_die(commit_sha1, mail);\n>  \n>  \tget_commit_info(state, commit);\n>  \n> -\twrite_commit_patch(state, commit);\n> +\tif (write_commit_patch(state, commit) < 0)\n> +\t\treturn -1;\n>  \n>  \thashcpy(state->orig_commit, commit_sha1);\n>  \twrite_state_text(state, \"original-commit\", sha1_to_hex(commit_sha1));\n"},{"id":"289798","messageId":"xmqqlh1ypdk8.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"xmqqfus6quii.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 2/9] Disallow diffopt.close_file when using the log_tree machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-21T19:05:59Z","receivedAt":"2016-06-21T19:19:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>\n>> We are about to teach the log_tree machinery to reuse the diffopt.file\n>> setting to output to a file stream different from stdout.\n>>\n>> This means that builtin am can no longer ask the diff machinery to\n>> close the file when actually calling the log_tree machinery (which\n>> wants to flush the very same file stream that would then already be\n>> closed).\n>\n> Sorry for being slow, but I am not sure why the first paragraph has\n> to mean the second paragraph.  This existing caller opens a new\n> stream, sets .fp to it, and expects that the log_tree_commit() to\n> close it if told by setting .close_file to true, all of which sounds\n> sensible.\n>\n> If a codepath wants to use the same stream for two or more calls to\n> log_tree by pointing the stream with .fp, it would be of course a\n> problem for the caller to set .close_file to true in its first call,\n> as .fp will be closed and no longer usable for second and subsequent\n> call, and that would be a bug, but for a single-shot call it feels\n> entirely a sensible request to make, no?\n>\n> Obviously you have looked at the codepaths involved a lot longer\n> than I did, and I do not doubt your conclusion, but I cannot quite\n> convince myself with the above explanation.\n>\n> The option parser of \"git diff\" family sets ->close_file to true\n> when the --output option is given.\n>\n> Wouldn't this patch break \"git log --output=foo -3\"?\n\nI wonder if the right approach is to stop using .close_file\neverywhere.\n\nWith this \"do not set .close_file if you use log_tree_commit()\",\n\"git log --output=/dev/stdout -3\" gets broken, but removing that\ncheck is not sufficient to correct the same command with \"-p\", as\nletting .close_file to close the output file after finishing a\nsingle diff would mean that subsequent write to the same file\ndescriptor will trigger a failure.\n"},{"id":"289800","messageId":"xmqqa8iepcbx.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"xmqqlh1ypdk8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 2/9] Disallow diffopt.close_file when using the log_tree machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-21T19:32:34Z","receivedAt":"2016-06-21T19:32:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>\n>>> We are about to teach the log_tree machinery to reuse the diffopt.file\n>>> setting to output to a file stream different from stdout.\n>>>\n>>> This means that builtin am can no longer ask the diff machinery to\n>>> close the file when actually calling the log_tree machinery (which\n>>> wants to flush the very same file stream that would then already be\n>>> closed).\n>>\n>> Sorry for being slow, but I am not sure why the first paragraph has\n>> to mean the second paragraph.  This existing caller opens a new\n>> stream, sets .fp to it, and expects that the log_tree_commit() to\n>> close it if told by setting .close_file to true, all of which sounds\n>> sensible.\n>>\n>> If a codepath wants to use the same stream for two or more calls to\n>> log_tree by pointing the stream with .fp, it would be of course a\n>> problem for the caller to set .close_file to true in its first call,\n>> as .fp will be closed and no longer usable for second and subsequent\n>> call, and that would be a bug, but for a single-shot call it feels\n>> entirely a sensible request to make, no?\n>>\n>> Obviously you have looked at the codepaths involved a lot longer\n>> than I did, and I do not doubt your conclusion, but I cannot quite\n>> convince myself with the above explanation.\n>>\n>> The option parser of \"git diff\" family sets ->close_file to true\n>> when the --output option is given.\n>>\n>> Wouldn't this patch break \"git log --output=foo -3\"?\n>\n> I wonder if the right approach is to stop using .close_file\n> everywhere.\n>\n> With this \"do not set .close_file if you use log_tree_commit()\",\n> \"git log --output=/dev/stdout -3\" gets broken, but removing that\n> check is not sufficient to correct the same command with \"-p\", as\n> letting .close_file to close the output file after finishing a\n> single diff would mean that subsequent write to the same file\n> descriptor will trigger a failure.\n\nWe could say \"git log --output=foo -3 [-p]\" without any of your\npatches is already broken, and it is a valid excuse to take this\nchange that we are not making things worse with it.\n\nIt is just 3/9 is a logical first step to correct that exact\nproblem, i.e. some codepaths, even though there is a place that\nholds the output stream and command line parser does prepare one for\n\"foo\" when --output=foo is given, ignore it and send thigns to the\nstandard output stream.  You might not have written 3/9 in order to\nfix that \"git log --output=foo\" problem, but a fix for it should\nlook exactly like your 3/9, I would think.\n\nAnd it is sad that this step makes that fix impossible.\n"},{"id":"289831","messageId":"alpine.DEB.2.20.1606220849480.10382@virtualbox","threadId":"42648","inReplyTo":"xmqqvb12qyeu.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T07:24:07Z","receivedAt":"2016-06-22T07:25:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 21 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > That is a very convincing argument. So convincing that I wanted to change\n> > the patch to guard behind `diff_use_color_default == GIT_COLOR_AUTO`.\n> \n> I actually was expecting, instead of your:\n> \n>  \tif (output_directory) {\n> +\t\trev.diffopt.use_color = 0;\n>  \t\tif (use_stdout)\n>  \t\t\tdie(_(\"standard output, or directory, which one?\"));\n> \n> an update would say\n> \n>  \tif (output_directory) {\n> \t\tif (rev.diffopt.use_color == GIT_COLOR_AUTO)\n>                 \trev.diffopt.use_color = 0;\n>  \t\tif (use_stdout)\n>  \t\t\tdie(_(\"standard output, or directory, which one?\"));\n> \n> I didn't expect you to check diff_use_color_default exactly for the\n> reason why you say \"But that is the wrong variable\".\n\nIn diff_setup() (which is called by init_revisions() which in turn is\ncalled by format-patch's cmd_format_patch()), the use_color field is\ninitialized with the value of diff_use_color_default, though:\n\n\thttps://github.com/git/git/blob/v2.9.0/diff.c#L3283\n\nPlease note that diff_use_color_default is initialized to -1:\n\n\thttps://github.com/git/git/blob/v2.9.0/diff.c#L32\n\nand set to a different value here:\n\n\thttps://github.com/git/git/blob/v2.9.0/diff.c#L180\n\nwhen parsing the diff.color or color.diff config settings, which\nhowever are *not* parsed in format-patch, thanks to:\n\n\thttps://github.com/git/git/blob/v2.9.0/builtin/log.c#L740-L743\n\nTherefore, use_color is initialized to -1, and in format-patch's case it\nremains like this. I was a bit surprised to see that GIT_COLOR_AUTO's\nnumerical value is *not* -1 (which I would have chosen for the \"undecided\"\ncase, but I guess that -1 was assumed to mean the \"unspecified\" case), but\nthe numerical value of 2 instead:\n\n\thttps://github.com/git/git/blob/v2.9.0/color.h#L59\n\nHence \" rev.diffopt.use_color == GIT_COLOR_AUTO\" would\nevaluate to \"-1 == 2\" in this context.\n\nFurther, I think that the commit message of 7787570c (format-patch: ignore\nui.color, 2011-09-13) makes a pretty eloquent case that we *want* to\nswitch off color when letting format-patch write to files.\n\nBut there's a rub... If you specify --color *explicitly*, use_color is set\nto GIT_COLOR_ALWAYS and the file indeed contains ANSI sequences (i.e. my\nanalysis above left out the command-line part).\n\nIn short, I think you're right, I have to guard the assignment, with the\nminor adjustment to test use_color != GIT_COLOR_ALWAYS.\n\nWill reroll.\n\nCiao,\nDscho\n"},{"id":"289835","messageId":"CACRoPnTYjZzVe7iKTu_nzosbgm19fAq9csFiZs3rvEPqkAusPQ@mail.gmail.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606211555380.22630@virtualbox","subject":"Re: [PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2016-06-22T09:23:27Z","receivedAt":"2016-06-22T09:23:33Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi Johannes,\n\nOn Tue, Jun 21, 2016 at 10:12 PM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi Paul,\n>\n> On Tue, 21 Jun 2016, Paul Tan wrote:\n>\n>> On Tue, Jun 21, 2016 at 6:34 PM, Johannes Schindelin\n>> <johannes.schindelin@gmx.de> wrote:\n>> > - this uncovered a problem with builtin am, where it asked the diff\n>> >   machinery to close the file stream, but actually called the log_tree\n>> >   machinery (which might mean that this patch series inadvertently fixes\n>> >   a bug where `git am --rebasing` would write the commit message to\n>> >   stdout instead of the `patch` file when erroring out)\n>>\n>> Please correct me if I'm wrong: looking at log-tree.c, the commit\n>> message will not be printed when no_commit_id = 1, isn't it?\n>> This is because we do not hit the code paths that write to stdout since\n>> show_log() is not called.\n>\n> Why does builtin/am.c use log_tree_commit(), then? Why not simply run\n> things through the diff machinery?\n\nIt's because git-am.sh called \"git diff-tree\", which in turn calls\nlog_tree_commit(), so to be safe I used the same function to ensure\nthat there were no unintended behavioral changes. e.g. what happened\nwith 3ecc704 (am --skip/--abort: merge HEAD/ORIG_HEAD tree into index,\n2015-08-19)\n\nOf course, it may be true that the diff machinery should be called\ndirectly, although the code is quite involved so I can't really tell\nthe impact the change will have.\n\nThanks,\nPaul\n"},{"id":"289842","messageId":"alpine.DEB.2.20.1606221421000.10382@virtualbox","threadId":"42648","inReplyTo":"xmqqporapdu8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 1/9] am: stop ignoring errors reported by log_tree_diff()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T12:21:25Z","receivedAt":"2016-06-22T12:22:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 21 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Note: there are more places in the builtin am code that ignore\n> > errors returned from library functions. Fixing those is outside the\n> > purview of the current patch series, though.\n> \n> The caller of parse_mail() and parse_mail_rebase() is not prepared\n> to see an error code in the returned value from these function,\n> which are to return a boolean telling the caller to skip or use the\n> patch file.  At least the caller needs to notice negative return and\n> made to die/exit(128), instead of silently skipping a corrupt or\n> unopenable patch, no?  Otherwise this will change the behaviour.\n\nYeah, that is another rabbit hole I really do not want to dive in.\n\nWill leave it alone,\nDscho\n"},{"id":"289844","messageId":"ceb2fb1f2e63e3a74af870cfe7ea799bb2646e25.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 09/10] shortlog: respect the --output=<file> setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:02:07Z","receivedAt":"2016-06-22T15:02:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Thanks to the diff option parsing, we already know about this option.\nWe just have to make use of it.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/shortlog.c  | 4 +++-\n t/t4201-shortlog.sh | 6 ++++++\n 2 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 39d74fe..be80547 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -229,7 +229,6 @@ void shortlog_init(struct shortlog *log)\n \tlog->wrap = DEFAULT_WRAPLEN;\n \tlog->in1 = DEFAULT_INDENT1;\n \tlog->in2 = DEFAULT_INDENT2;\n-\tlog->file = stdout;\n }\n \n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n@@ -277,6 +276,7 @@ parse_done:\n \n \tlog.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;\n \tlog.abbrev = rev.abbrev;\n+\tlog.file = rev.diffopt.file;\n \n \t/* assume HEAD if from a tty */\n \tif (!nongit && !rev.pending.nr && isatty(0))\n@@ -290,6 +290,8 @@ parse_done:\n \t\tget_from_rev(&rev, &log);\n \n \tshortlog_output(&log);\n+\tif (log.file != stdout)\n+\t\tfclose(log.file);\n \treturn 0;\n }\n \ndiff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh\nindex a977365..bd699e1 100755\n--- a/t/t4201-shortlog.sh\n+++ b/t/t4201-shortlog.sh\n@@ -184,4 +184,10 @@ test_expect_success 'shortlog with revision pseudo options' '\n \tgit shortlog --exclude=refs/heads/m* --all\n '\n \n+test_expect_success 'shortlog with --output=<file>' '\n+\tgit shortlog --output=shortlog master >output &&\n+\ttest ! -s output &&\n+\ttest_line_count = 7 shortlog\n+'\n+\n test_done\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289845","messageId":"3c348a91842a38cbf3631fea11a832785d76a6ea.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 10/10] Ensure that log respects --output=<file>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:02:13Z","receivedAt":"2016-06-22T15:02:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The test script t4202-log.sh is already pretty long, and it is a good\nidea to test --output with a more obscure option, anyway. So let's\ntest it in conjunction with line-log.\n\nThe most important part of this test, of course, is to ensure that the\nfile is not closed after writing the diff, but only at the very end\nof the log output. That is the entire reason why the test tries to\ngenerate a log that covers more than one commit.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t4211-line-log.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh\nindex 4451127..9d87777 100755\n--- a/t/t4211-line-log.sh\n+++ b/t/t4211-line-log.sh\n@@ -99,4 +99,11 @@ test_expect_success '-L with --first-parent and a merge' '\n \tgit log --first-parent -L 1,1:b.c\n '\n \n+test_expect_success '-L with --output' '\n+\tgit checkout parallel-change &&\n+\tgit log --output=log -L :main:b.c >output &&\n+\ttest ! -s output &&\n+\ttest_line_count = 70 log\n+'\n+\n test_done\n-- \n2.9.0.118.g0e1a633\n"},{"id":"289846","messageId":"d8e62a39cbd507880617adb2cc3b38578d0b867c.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 07/10] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:59Z","receivedAt":"2016-06-22T15:02:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We just taught the relevant functions to respect the diffopt.file field,\nto allow writing somewhere else than stdout. Let's make use of it.\n\nTechnically, we do not need to avoid that call in a builtin: we assume\nthat builtins (as opposed to library functions) are stand-alone programs\nthat may do with their (global) state. Yet, we want to be able to reuse\nthat code in properly lib-ified code, e.g. when converting scripts into\nbuiltins.\n\nFurther, while we did not *have* to touch the cmd_show() and cmd_cherry()\ncode paths (because they do not want to write anywhere but stdout as of\nyet), it just makes sense to be consistent, making it easier and safer to\nmove the code later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 64 ++++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 33 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 5683a42..869c23b 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -236,7 +236,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)\n \t\tif (rev->commit_format != CMIT_FMT_ONELINE)\n \t\t\tputchar(rev->diffopt.line_termination);\n \t}\n-\tprintf(_(\"Final output: %d %s\\n\"), nr, stage);\n+\tfprintf(rev->diffopt.file, _(\"Final output: %d %s\\n\"), nr, stage);\n }\n \n static struct itimerval early_output_timer;\n@@ -454,7 +454,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)\n \tpp.fmt = rev->commit_format;\n \tpp.date_mode = rev->date_mode;\n \tpp_user_info(&pp, \"Tagger\", &out, buf, get_log_output_encoding());\n-\tprintf(\"%s\", out.buf);\n+\tfprintf(rev->diffopt.file, \"%s\", out.buf);\n \tstrbuf_release(&out);\n }\n \n@@ -465,7 +465,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con\n \tchar *buf;\n \tunsigned long size;\n \n-\tfflush(stdout);\n+\tfflush(rev->diffopt.file);\n \tif (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||\n \t    !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))\n \t\treturn stream_blob_to_fd(1, sha1, NULL, 0);\n@@ -505,7 +505,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)\n \t}\n \n \tif (offset < size)\n-\t\tfwrite(buf + offset, size - offset, 1, stdout);\n+\t\tfwrite(buf + offset, size - offset, 1, rev->diffopt.file);\n \tfree(buf);\n \treturn 0;\n }\n@@ -514,7 +514,8 @@ static int show_tree_object(const unsigned char *sha1,\n \t\tstruct strbuf *base,\n \t\tconst char *pathname, unsigned mode, int stage, void *context)\n {\n-\tprintf(\"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tFILE *file = context;\n+\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n \treturn 0;\n }\n \n@@ -574,7 +575,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stag %s%s\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stag %s%s\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tt->tag,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n@@ -593,12 +594,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\tcase OBJ_TREE:\n \t\t\tif (rev.shown_one)\n \t\t\t\tputchar('\\n');\n-\t\t\tprintf(\"%stree %s%s\\n\\n\",\n+\t\t\tfprintf(rev.diffopt.file, \"%stree %s%s\\n\\n\",\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive((struct tree *)o, \"\", 0, 0, &match_all,\n-\t\t\t\t\tshow_tree_object, NULL);\n+\t\t\t\t\tshow_tree_object, rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\n@@ -808,7 +809,7 @@ static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n-static int reopen_stdout(struct commit *commit, const char *subject,\n+static int open_next_file(struct commit *commit, const char *subject,\n \t\t\t struct rev_info *rev, int quiet)\n {\n \tstruct strbuf filename = STRBUF_INIT;\n@@ -832,7 +833,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,\n \tif (!quiet)\n \t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n \n-\tif (freopen(filename.buf, \"w\", stdout) == NULL)\n+\tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n \n \tstrbuf_release(&filename);\n@@ -891,15 +892,15 @@ static void gen_message_id(struct rev_info *info, char *base)\n \tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n-static void print_signature(void)\n+static void print_signature(FILE *file)\n {\n \tif (!signature || !*signature)\n \t\treturn;\n \n-\tprintf(\"-- \\n%s\", signature);\n+\tfprintf(file, \"-- \\n%s\", signature);\n \tif (signature[strlen(signature)-1] != '\\n')\n-\t\tputchar('\\n');\n-\tputchar('\\n');\n+\t\tputc('\\n', file);\n+\tputc('\\n', file);\n }\n \n static void add_branch_description(struct strbuf *buf, const char *branch_name)\n@@ -968,7 +969,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tcommitter = git_committer_info(0);\n \n \tif (!use_stdout &&\n-\t    reopen_stdout(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n+\t    open_next_file(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n \t\treturn;\n \n \tlog_write_email_headers(rev, head, &pp.subject, &pp.after_subject,\n@@ -991,7 +992,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tpp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);\n \tpp_remainder(&pp, &msg, &sb, 0);\n \tadd_branch_description(&sb, branch_name);\n-\tprintf(\"%s\\n\", sb.buf);\n+\tfprintf(rev->diffopt.file, \"%s\\n\", sb.buf);\n \n \tstrbuf_release(&sb);\n \n@@ -1000,6 +1001,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tlog.wrap = 72;\n \tlog.in1 = 2;\n \tlog.in2 = 4;\n+\tlog.file = rev->diffopt.file;\n \tfor (i = 0; i < nr; i++)\n \t\tshortlog_add_commit(&log, list[i]);\n \n@@ -1022,8 +1024,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,\n \tdiffcore_std(&opts);\n \tdiff_flush(&opts);\n \n-\tprintf(\"\\n\");\n-\tprint_signature();\n+\tfprintf(rev->diffopt.file, \"\\n\");\n+\tprint_signature(rev->diffopt.file);\n }\n \n static const char *clean_message_id(const char *msg_id)\n@@ -1333,7 +1335,7 @@ static void prepare_bases(struct base_tree_info *bases,\n \t}\n }\n \n-static void print_bases(struct base_tree_info *bases)\n+static void print_bases(struct base_tree_info *bases, FILE *file)\n {\n \tint i;\n \n@@ -1342,11 +1344,11 @@ static void print_bases(struct base_tree_info *bases)\n \t\treturn;\n \n \t/* Show the base commit */\n-\tprintf(\"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n+\tfprintf(file, \"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n \n \t/* Show the prerequisite patches */\n \tfor (i = bases->nr_patch_id - 1; i >= 0; i--)\n-\t\tprintf(\"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n+\t\tfprintf(file, \"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n \n \tfree(bases->patch_id);\n \tbases->nr_patch_id = 0;\n@@ -1704,7 +1706,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n \t\t\t\t  origin, nr, list, branch_name, quiet);\n-\t\tprint_bases(&bases);\n+\t\tprint_bases(&bases, rev.diffopt.file);\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n@@ -1750,7 +1752,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t}\n \n \t\tif (!use_stdout &&\n-\t\t    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n+\t\t    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))\n \t\t\tdie(_(\"Failed to create output files\"));\n \t\tshown = log_tree_commit(&rev, commit);\n \t\tfree_commit_buffer(commit);\n@@ -1765,15 +1767,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\trev.shown_one = 0;\n \t\tif (shown) {\n \t\t\tif (rev.mime_boundary)\n-\t\t\t\tprintf(\"\\n--%s%s--\\n\\n\\n\",\n+\t\t\t\tfprintf(rev.diffopt.file, \"\\n--%s%s--\\n\\n\\n\",\n \t\t\t\t       mime_boundary_leader,\n \t\t\t\t       rev.mime_boundary);\n \t\t\telse\n-\t\t\t\tprint_signature();\n-\t\t\tprint_bases(&bases);\n+\t\t\t\tprint_signature(rev.diffopt.file);\n+\t\t\tprint_bases(&bases, rev.diffopt.file);\n \t\t}\n \t\tif (!use_stdout)\n-\t\t\tfclose(stdout);\n+\t\t\tfclose(rev.diffopt.file);\n \t}\n \tfree(list);\n \tfree(branch_name);\n@@ -1805,15 +1807,15 @@ static const char * const cherry_usage[] = {\n };\n \n static void print_commit(char sign, struct commit *commit, int verbose,\n-\t\t\t int abbrev)\n+\t\t\t int abbrev, FILE *file)\n {\n \tif (!verbose) {\n-\t\tprintf(\"%c %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev));\n \t} else {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);\n-\t\tprintf(\"%c %s %s\\n\", sign,\n+\t\tfprintf(file, \"%c %s %s\\n\", sign,\n \t\t       find_unique_abbrev(commit->object.oid.hash, abbrev),\n \t\t       buf.buf);\n \t\tstrbuf_release(&buf);\n@@ -1894,7 +1896,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\tcommit = list->item;\n \t\tif (has_commit_patch_id(commit, &ids))\n \t\t\tsign = '-';\n-\t\tprint_commit(sign, commit, verbose, abbrev);\n+\t\tprint_commit(sign, commit, verbose, abbrev, revs.diffopt.file);\n \t\tlist = list->next;\n \t}\n \n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289847","messageId":"35a00cad311766d2bc64dada92e14c163e5a5806.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 08/10] format-patch: use stdout directly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:02:04Z","receivedAt":"2016-06-22T15:03:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Earlier, we freopen()ed stdout in order to write patches to files.\nThat forced us to duplicate stdout (naming it \"realstdout\") because we\n*still* wanted to be able to report the file names.\n\nAs we do not abuse stdout that way anymore, we no longer need to\nduplicate stdout, either.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 6 +-----\n 1 file changed, 1 insertion(+), 5 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 869c23b..2a42216 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -805,7 +805,6 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \treturn git_log_config(var, value, cb);\n }\n \n-static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n static int outdir_offset;\n \n@@ -831,7 +830,7 @@ static int open_next_file(struct commit *commit, const char *subject,\n \t\tfmt_output_subject(&filename, subject, rev);\n \n \tif (!quiet)\n-\t\tfprintf(realstdout, \"%s\\n\", filename.buf + outdir_offset);\n+\t\tprintf(\"%s\\n\", filename.buf + outdir_offset);\n \n \tif ((rev->diffopt.file = fopen(filename.buf, \"w\")) == NULL)\n \t\treturn error(_(\"Cannot open patch file %s\"), filename.buf);\n@@ -1639,9 +1638,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tget_patch_ids(&rev, &ids);\n \t}\n \n-\tif (!use_stdout)\n-\t\trealstdout = xfdopen(xdup(1), \"w\");\n-\n \tif (prepare_revision_walk(&rev))\n \t\tdie(_(\"revision walk setup failed\"));\n \trev.boundary = 1;\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289848","messageId":"3cc264b716f84b764e2fc1eef6bb8d8d65bfed67.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 05/10] shortlog: support outputting to streams other than stdout","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:49Z","receivedAt":"2016-06-22T15:03:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to avoid freopen() in `git format-patch`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/shortlog.c | 13 ++++++++-----\n shortlog.h         |  1 +\n 2 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex bfc082e..39d74fe 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -229,6 +229,7 @@ void shortlog_init(struct shortlog *log)\n \tlog->wrap = DEFAULT_WRAPLEN;\n \tlog->in1 = DEFAULT_INDENT1;\n \tlog->in2 = DEFAULT_INDENT2;\n+\tlog->file = stdout;\n }\n \n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n@@ -310,22 +311,24 @@ void shortlog_output(struct shortlog *log)\n \tfor (i = 0; i < log->list.nr; i++) {\n \t\tconst struct string_list_item *item = &log->list.items[i];\n \t\tif (log->summary) {\n-\t\t\tprintf(\"%6d\\t%s\\n\", (int)UTIL_TO_INT(item), item->string);\n+\t\t\tfprintf(log->file, \"%6d\\t%s\\n\",\n+\t\t\t\t(int)UTIL_TO_INT(item), item->string);\n \t\t} else {\n \t\t\tstruct string_list *onelines = item->util;\n-\t\t\tprintf(\"%s (%d):\\n\", item->string, onelines->nr);\n+\t\t\tfprintf(log->file, \"%s (%d):\\n\",\n+\t\t\t\titem->string, onelines->nr);\n \t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n \t\t\t\tconst char *msg = onelines->items[j].string;\n \n \t\t\t\tif (log->wrap_lines) {\n \t\t\t\t\tstrbuf_reset(&sb);\n \t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n-\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n+\t\t\t\t\tfwrite(sb.buf, sb.len, 1, log->file);\n \t\t\t\t}\n \t\t\t\telse\n-\t\t\t\t\tprintf(\"      %s\\n\", msg);\n+\t\t\t\t\tfprintf(log->file, \"      %s\\n\", msg);\n \t\t\t}\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', log->file);\n \t\t\tonelines->strdup_strings = 1;\n \t\t\tstring_list_clear(onelines, 0);\n \t\t\tfree(onelines);\ndiff --git a/shortlog.h b/shortlog.h\nindex de4f86f..5a326c6 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -17,6 +17,7 @@ struct shortlog {\n \tchar *common_repo_prefix;\n \tint email;\n \tstruct string_list mailmap;\n+\tFILE *file;\n };\n \n void shortlog_init(struct shortlog *log);\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289849","messageId":"4fa4ffb8c749509f91b646eb909fd45b899c011e.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 02/10] log-tree: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:32Z","receivedAt":"2016-06-22T15:03:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff options already know how to print the output anywhere else\nthan stdout. The same is needed for log output in general, e.g.\nwhen writing patches to files in `git format-patch`. Let's allow\nusers to use log_tree_commit() *without* changing global state via\nfreopen().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n log-tree.c | 64 +++++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 32 insertions(+), 32 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 456d7e3..cf24027 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -159,12 +159,12 @@ void load_ref_decorations(int flags)\n \t}\n }\n \n-static void show_parents(struct commit *commit, int abbrev)\n+static void show_parents(struct commit *commit, int abbrev, FILE *file)\n {\n \tstruct commit_list *p;\n \tfor (p = commit->parents; p ; p = p->next) {\n \t\tstruct commit *parent = p->item;\n-\t\tprintf(\" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n+\t\tfprintf(file, \" %s\", find_unique_abbrev(parent->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -172,7 +172,7 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n {\n \tstruct commit_list *p = lookup_decoration(&opt->children, &commit->object);\n \tfor ( ; p; p = p->next) {\n-\t\tprintf(\" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n+\t\tfprintf(opt->diffopt.file, \" %s\", find_unique_abbrev(p->item->object.oid.hash, abbrev));\n \t}\n }\n \n@@ -286,11 +286,11 @@ void show_decorations(struct rev_info *opt, struct commit *commit)\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\t\tfprintf(opt->diffopt.file, \"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n \tformat_decorations(&sb, commit, opt->diffopt.use_color);\n-\tfputs(sb.buf, stdout);\n+\tfputs(sb.buf, opt->diffopt.file);\n \tstrbuf_release(&sb);\n }\n \n@@ -364,18 +364,18 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\tsubject = \"Subject: \";\n \t}\n \n-\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n+\tfprintf(opt->diffopt.file, \"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n \tgraph_show_oneline(opt->graph);\n \tif (opt->message_id) {\n-\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n+\t\tfprintf(opt->diffopt.file, \"Message-Id: <%s>\\n\", opt->message_id);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \tif (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {\n \t\tint i, n;\n \t\tn = opt->ref_message_ids->nr;\n-\t\tprintf(\"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n+\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n \t\tfor (i = 0; i < n; i++)\n-\t\t\tprintf(\"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n+\t\t\tfprintf(opt->diffopt.file, \"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n \t\t\t       opt->ref_message_ids->items[i].string);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n@@ -432,7 +432,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)\n \treset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);\n \twhile (*bol) {\n \t\teol = strchrnul(bol, '\\n');\n-\t\tprintf(\"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n+\t\tfprintf(opt->diffopt.file, \"%s%.*s%s%s\", color, (int)(eol - bol), bol, reset,\n \t\t       *eol ? \"\\n\" : \"\");\n \t\tgraph_show_oneline(opt->graph);\n \t\tbol = (*eol) ? (eol + 1) : eol;\n@@ -553,17 +553,17 @@ void show_log(struct rev_info *opt)\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n-\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);\n+\t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->graph && !graph_is_commit_finished(opt->graph)) {\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_remainder(opt->graph);\n \t\t}\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -589,7 +589,7 @@ void show_log(struct rev_info *opt)\n \t\tif (opt->diffopt.line_termination == '\\n' &&\n \t\t    !opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \topt->shown_one = 1;\n \n@@ -607,28 +607,28 @@ void show_log(struct rev_info *opt)\n \t\tlog_write_email_headers(opt, commit, &ctx.subject, &extra_headers,\n \t\t\t\t\t&ctx.need_8bit_cte);\n \t} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);\n \t\tif (opt->commit_format != CMIT_FMT_ONELINE)\n-\t\t\tfputs(\"commit \", stdout);\n+\t\t\tfputs(\"commit \", opt->diffopt.file);\n \n \t\tif (!opt->graph)\n \t\t\tput_revision_mark(opt, commit);\n \t\tfputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),\n-\t\t      stdout);\n+\t\t      opt->diffopt.file);\n \t\tif (opt->print_parents)\n-\t\t\tshow_parents(commit, abbrev_commit);\n+\t\t\tshow_parents(commit, abbrev_commit, opt->diffopt.file);\n \t\tif (opt->children.name)\n \t\t\tshow_children(opt, commit, abbrev_commit);\n \t\tif (parent)\n-\t\t\tprintf(\" (from %s)\",\n+\t\t\tfprintf(opt->diffopt.file, \" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.oid.hash,\n \t\t\t\t\t\t  abbrev_commit));\n-\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);\n \t\tshow_decorations(opt, commit);\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n-\t\t\tputchar(' ');\n+\t\t\tputc(' ', opt->diffopt.file);\n \t\t} else {\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t\tgraph_show_oneline(opt->graph);\n \t\t}\n \t\tif (opt->reflog_info) {\n@@ -702,7 +702,7 @@ void show_log(struct rev_info *opt)\n \t}\n \n \tif (opt->show_log_size) {\n-\t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n+\t\tfprintf(opt->diffopt.file, \"log size %i\\n\", (int)msgbuf.len);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \n@@ -718,11 +718,11 @@ void show_log(struct rev_info *opt)\n \tif (opt->graph)\n \t\tgraph_show_commit_msg(opt->graph, &msgbuf);\n \telse\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, opt->diffopt.file);\n \tif (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {\n \t\tif (!opt->missing_newline)\n \t\t\tgraph_show_padding(opt->graph);\n-\t\tputchar(opt->diffopt.line_termination);\n+\t\tputc(opt->diffopt.line_termination, opt->diffopt.file);\n \t}\n \n \tstrbuf_release(&msgbuf);\n@@ -759,7 +759,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t\tstruct strbuf *msg = NULL;\n \t\t\t\tmsg = opt->diffopt.output_prefix(&opt->diffopt,\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\tfwrite(msg->buf, msg->len, 1, opt->diffopt.file);\n \t\t\t}\n \n \t\t\t/*\n@@ -774,8 +774,8 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t\t */\n \t\t\tif (!opt->shown_dashes &&\n \t\t\t    (pch & opt->diffopt.output_format) == pch)\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n+\t\t\t\tfprintf(opt->diffopt.file, \"---\");\n+\t\t\tputc('\\n', opt->diffopt.file);\n \t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n@@ -873,7 +873,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\treturn line_log_print(opt, commit);\n \n \tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n@@ -881,9 +881,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\tshown = 1;\n \t}\n \tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n-\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n+\t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n-\tmaybe_flush_or_die(stdout, \"stdout\");\n+\tmaybe_flush_or_die(opt->diffopt.file, \"stdout\");\n \tif (close_file)\n \t\tfclose(opt->diffopt.file);\n \treturn shown;\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289850","messageId":"c0fdb78fbb7b19e4b367c50a9c0c570193e98fa3.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 06/10] format-patch: explicitly switch off color when writing to files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:54Z","receivedAt":"2016-06-22T15:03:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We rely on the auto-detection (\"is stdout a terminal?\") to determine\nwhether to use color in the output of format-patch or not. That happens\nto work because we freopen() stdout when redirecting the output to files.\n\nHowever, we are about to fix that work-around, in which case the\nauto-detection has no chance to guess whether to use color or not.\n\nBut then, we do not need to guess to begin with. As argued in the commit\nmessage of 7787570c (format-patch: ignore ui.color, 2011-09-13), we do not\nallow the ui.color setting to affect format-patch's output. The only time,\ntherefore, that we allow color sequences to be written to the output files\nis when the user specified the --color command-line option explicitly.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 27bc88d..5683a42 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1578,6 +1578,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (output_directory) {\n+\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n+\t\t\trev.diffopt.use_color = 0;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289851","messageId":"044f3c6359b63eaa18229cd73e6815e8e15c9778.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 01/10] Prepare log/log-tree to reuse the diffopt.close_file attribute","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:28Z","receivedAt":"2016-06-22T15:03:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to teach the log-tree machinery to reuse the diffopt.file\nfield to output to a file stream other than stdout, in line with the\ndiff machinery already writing to diffopt.file.\n\nHowever, we might want to write something after the diff in\nlog_tree_commit() (e.g. with the --show-linear-break option), therefore\nwe must not let the diff machinery close the file (as per\ndiffopt.close_file.\n\nThis means that log_tree_commit() itself must override the\ndiffopt.close_file flag and close the file, and if log_tree_commit() is\ncalled in a loop, the caller is responsible to do the same.\n\nNote: format-patch has an `--output-directory` option. Due to the fact\nthat format-patch's options are parsed first, and that the parse-options\nmachinery accepts uniquely abbreviated options, the diff options\n`--output` (and `-o`) are shadowed. Therefore close_file is not set to 1\nso that cmd_format_patch() does *not* need to handle the close_file flag\ndifferently, even if it calls log_tree_commit() in a loop.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/log.c | 15 ++++++++++++---\n log-tree.c    |  5 ++++-\n 2 files changed, 16 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 099f4f7..27bc88d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -243,9 +243,10 @@ static struct itimerval early_output_timer;\n \n static void log_show_early(struct rev_info *revs, struct commit_list *list)\n {\n-\tint i = revs->early_output;\n+\tint i = revs->early_output, close_file = revs->diffopt.close_file;\n \tint show_header = 1;\n \n+\trevs->diffopt.close_file = 0;\n \tsort_in_topological_order(&list, revs->sort_order);\n \twhile (list && i) {\n \t\tstruct commit *commit = list->item;\n@@ -262,14 +263,19 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)\n \t\tcase commit_ignore:\n \t\t\tbreak;\n \t\tcase commit_error:\n+\t\t\tif (close_file)\n+\t\t\t\tfclose(revs->diffopt.file);\n \t\t\treturn;\n \t\t}\n \t\tlist = list->next;\n \t}\n \n \t/* Did we already get enough commits for the early output? */\n-\tif (!i)\n+\tif (!i) {\n+\t\tif (close_file)\n+\t\t\tfclose(revs->diffopt.file);\n \t\treturn;\n+\t}\n \n \t/*\n \t * ..if no, then repeat it twice a second until we\n@@ -331,7 +337,7 @@ static int cmd_log_walk(struct rev_info *rev)\n {\n \tstruct commit *commit;\n \tint saved_nrl = 0;\n-\tint saved_dcctc = 0;\n+\tint saved_dcctc = 0, close_file = rev->diffopt.close_file;\n \n \tif (rev->early_output)\n \t\tsetup_early_output(rev);\n@@ -347,6 +353,7 @@ static int cmd_log_walk(struct rev_info *rev)\n \t * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to\n \t * retain that state information if replacing rev->diffopt in this loop\n \t */\n+\trev->diffopt.close_file = 0;\n \twhile ((commit = get_revision(rev)) != NULL) {\n \t\tif (!log_tree_commit(rev, commit) && rev->max_count >= 0)\n \t\t\t/*\n@@ -367,6 +374,8 @@ static int cmd_log_walk(struct rev_info *rev)\n \t}\n \trev->diffopt.degraded_cc_to_c = saved_dcctc;\n \trev->diffopt.needed_rename_limit = saved_nrl;\n+\tif (close_file)\n+\t\tfclose(rev->diffopt.file);\n \n \tif (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&\n \t    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {\ndiff --git a/log-tree.c b/log-tree.c\nindex 78a5381..456d7e3 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -862,11 +862,12 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log\n int log_tree_commit(struct rev_info *opt, struct commit *commit)\n {\n \tstruct log_info log;\n-\tint shown;\n+\tint shown, close_file = opt->diffopt.close_file;\n \n \tlog.commit = commit;\n \tlog.parent = NULL;\n \topt->loginfo = &log;\n+\topt->diffopt.close_file = 0;\n \n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n@@ -883,5 +884,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n+\tif (close_file)\n+\t\tfclose(opt->diffopt.file);\n \treturn shown;\n }\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289852","messageId":"ac66b20d0b78e6002c4b38525a47ba8afd09c5c0.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 04/10] graph: respect the diffopt.file setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:44Z","receivedAt":"2016-06-22T15:03:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the caller overrides diffopt.file (which defaults to stdout),\nthe diff machinery already redirects its output, and the graph display\nshould also write to that file.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n graph.c | 30 +++++++++++++++++-------------\n 1 file changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/graph.c b/graph.c\nindex 1350bdd..8ad8ba3 100644\n--- a/graph.c\n+++ b/graph.c\n@@ -17,8 +17,8 @@\n static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);\n \n /*\n- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the\n- * first will be prefixed with the graph output.\n+ * Print a strbuf.  If the graph is non-NULL, all lines but the first will be\n+ * prefixed with the graph output.\n  *\n  * If the strbuf ends with a newline, the output will end after this\n  * newline.  A new graph line will not be printed after the final newline.\n@@ -1193,9 +1193,10 @@ void graph_show_commit(struct git_graph *graph)\n \n \twhile (!shown_commit_line && !graph_is_commit_finished(graph)) {\n \t\tshown_commit_line = graph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tif (!shown_commit_line)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t}\n \n@@ -1210,7 +1211,7 @@ void graph_show_oneline(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_next_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1222,7 +1223,7 @@ void graph_show_padding(struct git_graph *graph)\n \t\treturn;\n \n \tgraph_padding_line(graph, &msgbuf);\n-\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);\n \tstrbuf_release(&msgbuf);\n }\n \n@@ -1239,12 +1240,13 @@ int graph_show_remainder(struct git_graph *graph)\n \n \tfor (;;) {\n \t\tgraph_next_line(graph, &msgbuf);\n-\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);\n+\t\tfwrite(msgbuf.buf, sizeof(char), msgbuf.len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\tstrbuf_setlen(&msgbuf, 0);\n \t\tshown = 1;\n \n \t\tif (!graph_is_commit_finished(graph))\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t\telse\n \t\t\tbreak;\n \t}\n@@ -1259,7 +1261,8 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \tchar *p;\n \n \tif (!graph) {\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1277,7 +1280,7 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)\n \t\t} else {\n \t\t\tlen = (sb->buf + sb->len) - p;\n \t\t}\n-\t\tfwrite(p, sizeof(char), len, stdout);\n+\t\tfwrite(p, sizeof(char), len, graph->revs->diffopt.file);\n \t\tif (next_p && *next_p != '\\0')\n \t\t\tgraph_show_oneline(graph);\n \t\tp = next_p;\n@@ -1297,7 +1300,8 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * CMIT_FMT_USERFORMAT are already missing a terminating\n \t\t * newline.  All of the other formats should have it.\n \t\t */\n-\t\tfwrite(sb->buf, sizeof(char), sb->len, stdout);\n+\t\tfwrite(sb->buf, sizeof(char), sb->len,\n+\t\t\tgraph->revs->diffopt.file);\n \t\treturn;\n \t}\n \n@@ -1318,7 +1322,7 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * new line.\n \t\t */\n \t\tif (!newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \n \t\tgraph_show_remainder(graph);\n \n@@ -1326,6 +1330,6 @@ void graph_show_commit_msg(struct git_graph *graph,\n \t\t * If sb ends with a newline, our output should too.\n \t\t */\n \t\tif (newline_terminated)\n-\t\t\tputchar('\\n');\n+\t\t\tputc('\\n', graph->revs->diffopt.file);\n \t}\n }\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289853","messageId":"cover.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466505222.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 00/10] Let log-tree and friends respect diffopt's `file` field","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:22Z","receivedAt":"2016-06-22T15:03:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The idea is to allow callers to redirect log-tree's output to a file\nwithout having to freopen() stdout (which would modify global state,\na big no-no-no for library functions).\n\nI reviewed log-tree.c, graph.c, line-log.c, builtin/shortlog.c and\nbuiltin/log.c line by line to ensure that all calls that assumed stdout\npreviously now use the `file` field instead, of course. I would\nwelcome additional eyes to go over the code to confirm that I did not\nmiss anything.\n\nThis patch series ends up removing the freopen() call in the\nformat-patch command, but that is only a by-product. The ulterior motive\nbehind this series is to allow the sequencer to write a `patch` file as\npart of my endeavor to move large chunks of rebase -i into a builtin.\n\nIn contrast to the previous iteration of this patch series,\n\n- the use_color = 0 setting was made contingent on use_color != ALWAYS\n\n- close_file = 1 was made to work in more circumstances, most notably\n  when calling log_commit_tree() (and in builtin/log.c, where this\n  function is called in a loop)\n\n- the changes to builtin/am.c were backed out completely (this is a can\n  of worms I am not prepared to open for now)\n\n- I also taught shortlog to respect the --output=<file> option, because\n  it was so easy to do\n\n- I added a test case to ensure that `log --output=<file>` works\n\n\nJohannes Schindelin (10):\n  Prepare log/log-tree to reuse the diffopt.close_file attribute\n  log-tree: respect diffopt's configured output file stream\n  line-log: respect diffopt's configured output file stream\n  graph: respect the diffopt.file setting\n  shortlog: support outputting to streams other than stdout\n  format-patch: explicitly switch off color when writing to files\n  format-patch: avoid freopen()\n  format-patch: use stdout directly\n  shortlog: respect the --output=<file> setting\n  Ensure that log respects --output=<file>\n\n builtin/log.c       | 87 +++++++++++++++++++++++++++++------------------------\n builtin/shortlog.c  | 15 ++++++---\n graph.c             | 30 ++++++++++--------\n line-log.c          | 34 ++++++++++-----------\n log-tree.c          | 69 ++++++++++++++++++++++--------------------\n shortlog.h          |  1 +\n t/t4201-shortlog.sh |  6 ++++\n t/t4211-line-log.sh |  7 +++++\n 8 files changed, 142 insertions(+), 107 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/diffopt.file-v4\nInterdiff vs v3:\n\n diff --git a/builtin/am.c b/builtin/am.c\n index 47d78aa..3dfe70b 100644\n --- a/builtin/am.c\n +++ b/builtin/am.c\n @@ -1433,16 +1433,12 @@ static void get_commit_info(struct am_state *state, struct commit *commit)\n  /**\n   * Writes `commit` as a patch to the state directory's \"patch\" file.\n   */\n -static int write_commit_patch(const struct am_state *state, struct commit *commit)\n +static void write_commit_patch(const struct am_state *state, struct commit *commit)\n  {\n  \tstruct rev_info rev_info;\n  \tFILE *fp;\n -\tint res;\n  \n -\tfp = fopen(am_path(state, \"patch\"), \"w\");\n -\tif (!fp)\n -\t\treturn error(_(\"Could not open %s for writing\"),\n -\t\t\tam_path(state, \"patch\"));\n +\tfp = xfopen(am_path(state, \"patch\"), \"w\");\n  \tinit_revisions(&rev_info, NULL);\n  \trev_info.diff = 1;\n  \trev_info.abbrev = 0;\n @@ -1454,11 +1450,10 @@ static int write_commit_patch(const struct am_state *state, struct commit *commi\n  \tDIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);\n  \trev_info.diffopt.use_color = 0;\n  \trev_info.diffopt.file = fp;\n +\trev_info.diffopt.close_file = 1;\n  \tadd_pending_object(&rev_info, &commit->object, \"\");\n  \tdiff_setup_done(&rev_info.diffopt);\n -\tres = log_tree_commit(&rev_info, commit);\n -\tfclose(fp);\n -\treturn res;\n +\tlog_tree_commit(&rev_info, commit);\n  }\n  \n  /**\n @@ -1506,14 +1501,13 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)\n  \tunsigned char commit_sha1[GIT_SHA1_RAWSZ];\n  \n  \tif (get_mail_commit_sha1(commit_sha1, mail) < 0)\n -\t\treturn error(_(\"could not parse %s\"), mail);\n +\t\tdie(_(\"could not parse %s\"), mail);\n  \n  \tcommit = lookup_commit_or_die(commit_sha1, mail);\n  \n  \tget_commit_info(state, commit);\n  \n -\tif (write_commit_patch(state, commit) < 0)\n -\t\treturn -1;\n +\twrite_commit_patch(state, commit);\n  \n  \thashcpy(state->orig_commit, commit_sha1);\n  \twrite_state_text(state, \"original-commit\", sha1_to_hex(commit_sha1));\n diff --git a/builtin/log.c b/builtin/log.c\n index 2bfcc43..2a42216 100644\n --- a/builtin/log.c\n +++ b/builtin/log.c\n @@ -243,9 +243,10 @@ static struct itimerval early_output_timer;\n  \n  static void log_show_early(struct rev_info *revs, struct commit_list *list)\n  {\n -\tint i = revs->early_output;\n +\tint i = revs->early_output, close_file = revs->diffopt.close_file;\n  \tint show_header = 1;\n  \n +\trevs->diffopt.close_file = 0;\n  \tsort_in_topological_order(&list, revs->sort_order);\n  \twhile (list && i) {\n  \t\tstruct commit *commit = list->item;\n @@ -262,14 +263,19 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)\n  \t\tcase commit_ignore:\n  \t\t\tbreak;\n  \t\tcase commit_error:\n +\t\t\tif (close_file)\n +\t\t\t\tfclose(revs->diffopt.file);\n  \t\t\treturn;\n  \t\t}\n  \t\tlist = list->next;\n  \t}\n  \n  \t/* Did we already get enough commits for the early output? */\n -\tif (!i)\n +\tif (!i) {\n +\t\tif (close_file)\n +\t\t\tfclose(revs->diffopt.file);\n  \t\treturn;\n +\t}\n  \n  \t/*\n  \t * ..if no, then repeat it twice a second until we\n @@ -331,7 +337,7 @@ static int cmd_log_walk(struct rev_info *rev)\n  {\n  \tstruct commit *commit;\n  \tint saved_nrl = 0;\n -\tint saved_dcctc = 0;\n +\tint saved_dcctc = 0, close_file = rev->diffopt.close_file;\n  \n  \tif (rev->early_output)\n  \t\tsetup_early_output(rev);\n @@ -347,6 +353,7 @@ static int cmd_log_walk(struct rev_info *rev)\n  \t * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to\n  \t * retain that state information if replacing rev->diffopt in this loop\n  \t */\n +\trev->diffopt.close_file = 0;\n  \twhile ((commit = get_revision(rev)) != NULL) {\n  \t\tif (!log_tree_commit(rev, commit) && rev->max_count >= 0)\n  \t\t\t/*\n @@ -367,6 +374,8 @@ static int cmd_log_walk(struct rev_info *rev)\n  \t}\n  \trev->diffopt.degraded_cc_to_c = saved_dcctc;\n  \trev->diffopt.needed_rename_limit = saved_nrl;\n +\tif (close_file)\n +\t\tfclose(rev->diffopt.file);\n  \n  \tif (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&\n  \t    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {\n @@ -1570,7 +1579,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n  \t\tsetup_pager();\n  \n  \tif (output_directory) {\n -\t\trev.diffopt.use_color = 0;\n +\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n +\t\t\trev.diffopt.use_color = 0;\n  \t\tif (use_stdout)\n  \t\t\tdie(_(\"standard output, or directory, which one?\"));\n  \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n diff --git a/builtin/shortlog.c b/builtin/shortlog.c\n index 39d74fe..be80547 100644\n --- a/builtin/shortlog.c\n +++ b/builtin/shortlog.c\n @@ -229,7 +229,6 @@ void shortlog_init(struct shortlog *log)\n  \tlog->wrap = DEFAULT_WRAPLEN;\n  \tlog->in1 = DEFAULT_INDENT1;\n  \tlog->in2 = DEFAULT_INDENT2;\n -\tlog->file = stdout;\n  }\n  \n  int cmd_shortlog(int argc, const char **argv, const char *prefix)\n @@ -277,6 +276,7 @@ parse_done:\n  \n  \tlog.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;\n  \tlog.abbrev = rev.abbrev;\n +\tlog.file = rev.diffopt.file;\n  \n  \t/* assume HEAD if from a tty */\n  \tif (!nongit && !rev.pending.nr && isatty(0))\n @@ -290,6 +290,8 @@ parse_done:\n  \t\tget_from_rev(&rev, &log);\n  \n  \tshortlog_output(&log);\n +\tif (log.file != stdout)\n +\t\tfclose(log.file);\n  \treturn 0;\n  }\n  \n diff --git a/log-tree.c b/log-tree.c\n index 530297d..cf24027 100644\n --- a/log-tree.c\n +++ b/log-tree.c\n @@ -862,14 +862,12 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log\n  int log_tree_commit(struct rev_info *opt, struct commit *commit)\n  {\n  \tstruct log_info log;\n -\tint shown;\n -\n -\tif (opt->diffopt.close_file)\n -\t\tdie(\"BUG: close_file is incompatible with log_tree_commit()\");\n +\tint shown, close_file = opt->diffopt.close_file;\n  \n  \tlog.commit = commit;\n  \tlog.parent = NULL;\n  \topt->loginfo = &log;\n +\topt->diffopt.close_file = 0;\n  \n  \tif (opt->line_level_traverse)\n  \t\treturn line_log_print(opt, commit);\n @@ -886,5 +884,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n  \t\tfprintf(opt->diffopt.file, \"\\n%s\\n\", opt->break_bar);\n  \topt->loginfo = NULL;\n  \tmaybe_flush_or_die(opt->diffopt.file, \"stdout\");\n +\tif (close_file)\n +\t\tfclose(opt->diffopt.file);\n  \treturn shown;\n  }\n diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh\n index a977365..bd699e1 100755\n --- a/t/t4201-shortlog.sh\n +++ b/t/t4201-shortlog.sh\n @@ -184,4 +184,10 @@ test_expect_success 'shortlog with revision pseudo options' '\n  \tgit shortlog --exclude=refs/heads/m* --all\n  '\n  \n +test_expect_success 'shortlog with --output=<file>' '\n +\tgit shortlog --output=shortlog master >output &&\n +\ttest ! -s output &&\n +\ttest_line_count = 7 shortlog\n +'\n +\n  test_done\n diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh\n index 4451127..9d87777 100755\n --- a/t/t4211-line-log.sh\n +++ b/t/t4211-line-log.sh\n @@ -99,4 +99,11 @@ test_expect_success '-L with --first-parent and a merge' '\n  \tgit log --first-parent -L 1,1:b.c\n  '\n  \n +test_expect_success '-L with --output' '\n +\tgit checkout parallel-change &&\n +\tgit log --output=log -L :main:b.c >output &&\n +\ttest ! -s output &&\n +\ttest_line_count = 70 log\n +'\n +\n  test_done\n\n-- \n2.9.0.118.g0e1a633\n\nbase-commit: ab7797dbe95fff38d9265869ea367020046db118\n"},{"id":"289854","messageId":"778b8c0712a6a4baf8b25664e0682fe769cd3622.1466607667.git.johannes.schindelin@gmx.de","threadId":"42648","inReplyTo":"cover.1466607667.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 03/10] line-log: respect diffopt's configured output file stream","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:01:39Z","receivedAt":"2016-06-22T15:03:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The diff machinery can optionally output to a file stream other than\nstdout, by overriding diffopt.file. In such a case, the rest of the\nlog tree machinery should also write to that stream.\n\nCurrently, there is no user of the line level log that wants to\nredirect output to a file. Therefore, one might argue that it is\nsuperfluous to support that now. However, it is better to be\nconsistent now, rather than to face hard-to-debug problems later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n line-log.c | 34 +++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/line-log.c b/line-log.c\nindex bbe31ed..e62a7f4 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -841,7 +841,7 @@ static char *get_nth_line(long line, unsigned long *ends, void *data)\n \n static void print_line(const char *prefix, char first,\n \t\t       long line, unsigned long *ends, void *data,\n-\t\t       const char *color, const char *reset)\n+\t\t       const char *color, const char *reset, FILE *file)\n {\n \tchar *begin = get_nth_line(line, ends, data);\n \tchar *end = get_nth_line(line+1, ends, data);\n@@ -852,14 +852,14 @@ static void print_line(const char *prefix, char first,\n \t\thad_nl = 1;\n \t}\n \n-\tfputs(prefix, stdout);\n-\tfputs(color, stdout);\n-\tputchar(first);\n-\tfwrite(begin, 1, end-begin, stdout);\n-\tfputs(reset, stdout);\n-\tputchar('\\n');\n+\tfputs(prefix, file);\n+\tfputs(color, file);\n+\tputc(first, file);\n+\tfwrite(begin, 1, end-begin, file);\n+\tfputs(reset, file);\n+\tputc('\\n', file);\n \tif (!had_nl)\n-\t\tfputs(\"\\\\ No newline at end of file\\n\", stdout);\n+\t\tfputs(\"\\\\ No newline at end of file\\n\", file);\n }\n \n static char *output_prefix(struct diff_options *opt)\n@@ -898,12 +898,12 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\tfill_line_ends(pair->one, &p_lines, &p_ends);\n \tfill_line_ends(pair->two, &t_lines, &t_ends);\n \n-\tprintf(\"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n-\tprintf(\"%s%s--- %s%s%s\\n\", prefix, c_meta,\n+\tfprintf(opt->file, \"%s%sdiff --git a/%s b/%s%s\\n\", prefix, c_meta, pair->one->path, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s--- %s%s%s\\n\", prefix, c_meta,\n \t       pair->one->sha1_valid ? \"a/\" : \"\",\n \t       pair->one->sha1_valid ? pair->one->path : \"/dev/null\",\n \t       c_reset);\n-\tprintf(\"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n+\tfprintf(opt->file, \"%s%s+++ b/%s%s\\n\", prefix, c_meta, pair->two->path, c_reset);\n \tfor (i = 0; i < range->ranges.nr; i++) {\n \t\tlong p_start, p_end;\n \t\tlong t_start = range->ranges.ranges[i].start;\n@@ -945,7 +945,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t}\n \n \t\t/* Now output a diff hunk for this range */\n-\t\tprintf(\"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n+\t\tfprintf(opt->file, \"%s%s@@ -%ld,%ld +%ld,%ld @@%s\\n\",\n \t\t       prefix, c_frag,\n \t\t       p_start+1, p_end-p_start, t_start+1, t_end-t_start,\n \t\t       c_reset);\n@@ -953,18 +953,18 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n \t\t\tint k;\n \t\t\tfor (; t_cur < diff->target.ranges[j].start; t_cur++)\n \t\t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_context, c_reset);\n+\t\t\t\t\t   c_context, c_reset, opt->file);\n \t\t\tfor (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)\n \t\t\t\tprint_line(prefix, '-', k, p_ends, pair->one->data,\n-\t\t\t\t\t   c_old, c_reset);\n+\t\t\t\t\t   c_old, c_reset, opt->file);\n \t\t\tfor (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)\n \t\t\t\tprint_line(prefix, '+', t_cur, t_ends, pair->two->data,\n-\t\t\t\t\t   c_new, c_reset);\n+\t\t\t\t\t   c_new, c_reset, opt->file);\n \t\t\tj++;\n \t\t}\n \t\tfor (; t_cur < t_end; t_cur++)\n \t\t\tprint_line(prefix, ' ', t_cur, t_ends, pair->two->data,\n-\t\t\t\t   c_context, c_reset);\n+\t\t\t\t   c_context, c_reset, opt->file);\n \t}\n \n \tfree(p_ends);\n@@ -977,7 +977,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang\n  */\n static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)\n {\n-\tputs(output_prefix(&rev->diffopt));\n+\tfprintf(rev->diffopt.file, \"%s\\n\", output_prefix(&rev->diffopt));\n \twhile (range) {\n \t\tdump_diff_hacky_one(rev, range);\n \t\trange = range->next;\n-- \n2.9.0.118.g0e1a633\n\n\n"},{"id":"289856","messageId":"alpine.DEB.2.20.1606221716180.10382@virtualbox","threadId":"42648","inReplyTo":"xmqqa8iepcbx.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 2/9] Disallow diffopt.close_file when using the log_tree machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T15:17:56Z","receivedAt":"2016-06-22T15:18:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 21 Jun 2016, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Junio C Hamano <gitster@pobox.com> writes:\n> >\n> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >>\n> >>> We are about to teach the log_tree machinery to reuse the diffopt.file\n> >>> setting to output to a file stream different from stdout.\n> >>>\n> >>> This means that builtin am can no longer ask the diff machinery to\n> >>> close the file when actually calling the log_tree machinery (which\n> >>> wants to flush the very same file stream that would then already be\n> >>> closed).\n> >>\n> >> Sorry for being slow, but I am not sure why the first paragraph has\n> >> to mean the second paragraph.  This existing caller opens a new\n> >> stream, sets .fp to it, and expects that the log_tree_commit() to\n> >> close it if told by setting .close_file to true, all of which sounds\n> >> sensible.\n> >>\n> >> If a codepath wants to use the same stream for two or more calls to\n> >> log_tree by pointing the stream with .fp, it would be of course a\n> >> problem for the caller to set .close_file to true in its first call,\n> >> as .fp will be closed and no longer usable for second and subsequent\n> >> call, and that would be a bug, but for a single-shot call it feels\n> >> entirely a sensible request to make, no?\n> >>\n> >> Obviously you have looked at the codepaths involved a lot longer\n> >> than I did, and I do not doubt your conclusion, but I cannot quite\n> >> convince myself with the above explanation.\n> >>\n> >> The option parser of \"git diff\" family sets ->close_file to true\n> >> when the --output option is given.\n> >>\n> >> Wouldn't this patch break \"git log --output=foo -3\"?\n> >\n> > I wonder if the right approach is to stop using .close_file\n> > everywhere.\n> >\n> > With this \"do not set .close_file if you use log_tree_commit()\",\n> > \"git log --output=/dev/stdout -3\" gets broken, but removing that\n> > check is not sufficient to correct the same command with \"-p\", as\n> > letting .close_file to close the output file after finishing a\n> > single diff would mean that subsequent write to the same file\n> > descriptor will trigger a failure.\n> \n> We could say \"git log --output=foo -3 [-p]\" without any of your\n> patches is already broken, and it is a valid excuse to take this\n> change that we are not making things worse with it.\n> \n> It is just 3/9 is a logical first step to correct that exact\n> problem, i.e. some codepaths, even though there is a place that\n> holds the output stream and command line parser does prepare one for\n> \"foo\" when --output=foo is given, ignore it and send thigns to the\n> standard output stream.  You might not have written 3/9 in order to\n> fix that \"git log --output=foo\" problem, but a fix for it should\n> look exactly like your 3/9, I would think.\n> \n> And it is sad that this step makes that fix impossible.\n\nOkay, I ended up seeing that there is no way I can avoid Doing The Right\nThing.\n\nIt is not quite as pretty as I hoped it would be (callers of\nlog_tree_commit() that want to call it in a loop still have to override\nclose_file manually), but it does produce a nicer story.\n\nPlease see the fixes in the latest iteration I just sent out.\n\nCiao,\nDscho\n"},{"id":"289858","messageId":"xmqqeg7pns0a.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606220849480.10382@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-22T15:49:09Z","receivedAt":"2016-06-22T15:49:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> But there's a rub... If you specify --color *explicitly*, use_color is set\n> to GIT_COLOR_ALWAYS and the file indeed contains ANSI sequences (i.e. my\n> analysis above left out the command-line part).\n\nHeh, the command-line is the _ONLY_ thing I raised, as we knew\nui.color is not an issue in this codepath, in $gmane/297757.\n\nGoing back to that and reading again, I suggested to check with\nGIT_COLOR_AUTO (i.e. if it is left to \"auto\", disable it) because I\nthink the former is a much more future-proof way (imagine that we\nmay add --color=<some new setting> in the future) than checking with\nGIT_COLOR_ALWAYS (i.e. if it is not explicitly set to \"always\",\ndisable it).\n\n> In short, I think you're right, I have to guard the assignment, with the\n> minor adjustment to test use_color != GIT_COLOR_ALWAYS.\n\nSo I am not sure if you said \"use_color != GIT_COLOR_ALWAYS\" only to\nbe different from what I suggested (you seem to have a tendency to\ndo so whenever you can), or there was some other reason.\n"},{"id":"289861","messageId":"alpine.DEB.2.20.1606221811300.10382@virtualbox","threadId":"42648","inReplyTo":"xmqqeg7pns0a.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-22T16:14:43Z","receivedAt":"2016-06-22T16:15:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 22 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > But there's a rub... If you specify --color *explicitly*, use_color is set\n> > to GIT_COLOR_ALWAYS and the file indeed contains ANSI sequences (i.e. my\n> > analysis above left out the command-line part).\n> \n> Heh, the command-line is the _ONLY_ thing I raised, as we knew\n> ui.color is not an issue in this codepath, in $gmane/297757.\n> \n> Going back to that and reading again, I suggested to check with\n> GIT_COLOR_AUTO (i.e. if it is left to \"auto\", disable it) because I\n> think the former is a much more future-proof way (imagine that we\n> may add --color=<some new setting> in the future) than checking with\n> GIT_COLOR_ALWAYS (i.e. if it is not explicitly set to \"always\",\n> disable it).\n\nWell, if I change `rev.diffopt.use_color != GIT_COLOR_ALWAYS` to\n`rev.diffopt.use_color == GIT_COLOR_AUTO`, then the files will contain\nugly ANSI color sequences if I run `git format-patch -o . -3`.\n\nThe reason is, as I tried to explain, that the use_color field is *not*\ninitialized to GIT_COLOR_AUTO (which is equivalent to 2), but to -1.\n\nSo the difference between your version and mine is that mine works ;-)\n\nCiao,\nDscho\n"},{"id":"289874","messageId":"xmqqwplhm8f3.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606221811300.10382@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-22T17:37:36Z","receivedAt":"2016-06-22T17:37:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Well, if I change `rev.diffopt.use_color != GIT_COLOR_ALWAYS` to\n> `rev.diffopt.use_color == GIT_COLOR_AUTO`, then the files will contain\n> ugly ANSI color sequences if I run `git format-patch -o . -3`.\n>\n> The reason is, as I tried to explain, that the use_color field is *not*\n> initialized to GIT_COLOR_AUTO (which is equivalent to 2), but to -1.\n\nAh, OK.\n\nIt still feels safer to force no-color only when it is auto\n(i.e. the user said --color=auto) or -1 (i.e. the default), rather\nthan when it is anything other than always, though.\n"},{"id":"289876","messageId":"xmqqpor9m7o4.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"alpine.DEB.2.20.1606221811300.10382@virtualbox","subject":"Re: [PATCH 5/5] format-patch: avoid freopen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-22T17:53:47Z","receivedAt":"2016-06-22T17:53:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Well, if I change `rev.diffopt.use_color != GIT_COLOR_ALWAYS` to\n> `rev.diffopt.use_color == GIT_COLOR_AUTO`, then the files will contain\n> ugly ANSI color sequences if I run `git format-patch -o . -3`.\n>\n> The reason is, as I tried to explain, that the use_color field is *not*\n> initialized to GIT_COLOR_AUTO (which is equivalent to 2), but to -1.\n\nOK.  I thought forcing no-color only when it is set to COLOR_AUTO or\nit is set to -1 (the default) would be safer, but I changed my mind.\n\n\"when we add a new --color=<something.we.do.not.know.yet>,\noverriding that end-user wish with the unconditional no-color is\nlikely to be seen as bug.\" was the implicit bias behind that\nsuggestion, but that is not substanticated and substatiatable.\n\nIf we write\n\n\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n        \trev.diffopt.use_color = 0;\n\nand if a user of --color=<something.we.do.not.know.yet> wonders why\nher output is not colored, it is clear in the code above that we\ndisable unless it is set with --color=always, so it won't make\nfixing such a future breakage harder.  In fact, if we did\n\n\tif (rev.diffopt.use_color == GIT_COLOR_AUTO ||\n            rev.diffopt.use_color < 0)\n        \trev.diffopt.use_color = 0;\n\nit would make it _harder_ to spot where use_color is turned off when\nthe person who debugs such an issue.\n\nThanks.\n"},{"id":"290063","messageId":"xmqq60syfgqf.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"044f3c6359b63eaa18229cd73e6815e8e15c9778.1466607667.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 01/10] Prepare log/log-tree to reuse the diffopt.close_file attribute","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-24T20:56:40Z","receivedAt":"2016-06-24T20:56:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> We are about to teach the log-tree machinery to reuse the diffopt.file\n> field to output to a file stream other than stdout, in line with the\n> diff machinery already writing to diffopt.file.\n>\n> However, we might want to write something after the diff in\n> log_tree_commit() (e.g. with the --show-linear-break option), therefore\n> we must not let the diff machinery close the file (as per\n> diffopt.close_file.\n>\n> This means that log_tree_commit() itself must override the\n> diffopt.close_file flag and close the file, and if log_tree_commit() is\n> called in a loop, the caller is responsible to do the same.\n\nMakes sense.\n\n> Note: format-patch has an `--output-directory` option. Due to the fact\n> that format-patch's options are parsed first, and that the parse-options\n> machinery accepts uniquely abbreviated options, the diff options\n> `--output` (and `-o`) are shadowed. Therefore close_file is not set to 1\n> so that cmd_format_patch() does *not* need to handle the close_file flag\n> differently, even if it calls log_tree_commit() in a loop.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/log.c | 15 ++++++++++++---\n>  log-tree.c    |  5 ++++-\n>  2 files changed, 16 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 099f4f7..27bc88d 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -243,9 +243,10 @@ static struct itimerval early_output_timer;\n>  \n>  static void log_show_early(struct rev_info *revs, struct commit_list *list)\n>  {\n> -\tint i = revs->early_output;\n> +\tint i = revs->early_output, close_file = revs->diffopt.close_file;\n\nProbably not worth a reroll, but I find this kind of thing easier to\nread as two separate definitions.\n\n>  \tint show_header = 1;\n\nAnd this was separate from \"int i = ...;\" for the same reason.  It\nis OK to write \"int i, j, k;\" but \"show_header\" is something that\nkeeps track of the more important state during the control flow and\nit is easier to read if we make it stand out.  close_file falls into\nthe same category, I would think.\n\n>  \t\tcase commit_error:\n> +\t\t\tif (close_file)\n> +\t\t\t\tfclose(revs->diffopt.file);\n\nI wondered if we want to also clear, i.e. revs->diffopt.file = NULL, \nbut I do not think .close_file does that either, so this is good.\n\nThanks.\n"},{"id":"290069","messageId":"xmqq1t3mfdpy.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"c0fdb78fbb7b19e4b367c50a9c0c570193e98fa3.1466607667.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 06/10] format-patch: explicitly switch off color when writing to files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-24T22:01:45Z","receivedAt":"2016-06-24T22:02:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> We rely on the auto-detection (\"is stdout a terminal?\") to determine\n> whether to use color in the output of format-patch or not. That happens\n> to work because we freopen() stdout when redirecting the output to files.\n>\n> However, we are about to fix that work-around, in which case the\n> auto-detection has no chance to guess whether to use color or not.\n>\n> But then, we do not need to guess to begin with. As argued in the commit\n> message of 7787570c (format-patch: ignore ui.color, 2011-09-13), we do not\n> allow the ui.color setting to affect format-patch's output. The only time,\n> therefore, that we allow color sequences to be written to the output files\n> is when the user specified the --color command-line option explicitly.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nThe right fix in the longer term (long after this series lands, that\nis) is probably to update the world view that the codepath from\nwant_color_auto() to check_auto_color() has always held.  In their\nworld view, when they are asked to make --color=auto decision, the\noutput always goes the standard output, and that is why they\nhardcode isatty(1) to decide.  The existing freopen() was a part of\nthat world view.\n\nWe'd need a workaround like this patch if we want to leave the\nwant_color_auto() as-is, and as a workaround I think this is the\nleast invasive one, so let's queue it as-is.\n\nIf the codepaths that use diffopt.file (not just this one that is\nabout output directory hence known to be writing to a file, but all\nthe log/diff family of commands after this series up to 5/10 has\nbeen applied) have a way to tell want_color_auto() that the output\nis going to fileno(diffopt.file), and have check_auto_color() use\nthat fd instead of the hardcoded 1, the problem this step is trying\nto address goes away, and I think that would be the longer-term fix.\n\nThanks.\n\n>  builtin/log.c | 2 ++\n>  1 file changed, 2 insertions(+)\n>\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 27bc88d..5683a42 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1578,6 +1578,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \t\tsetup_pager();\n>  \n>  \tif (output_directory) {\n> +\t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n> +\t\t\trev.diffopt.use_color = 0;\n>  \t\tif (use_stdout)\n>  \t\t\tdie(_(\"standard output, or directory, which one?\"));\n>  \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n"},{"id":"290070","messageId":"xmqqwpledz22.fsf@gitster.mtv.corp.google.com","threadId":"42648","inReplyTo":"35a00cad311766d2bc64dada92e14c163e5a5806.1466607667.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 08/10] format-patch: use stdout directly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-24T22:03:49Z","receivedAt":"2016-06-24T22:03:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Earlier, we freopen()ed stdout in order to write patches to files.\n> That forced us to duplicate stdout (naming it \"realstdout\") because we\n> *still* wanted to be able to report the file names.\n>\n> As we do not abuse stdout that way anymore, we no longer need to\n> duplicate stdout, either.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/log.c | 6 +-----\n>  1 file changed, 1 insertion(+), 5 deletions(-)\n\nThe logical consequence of 07/10; very nice.\n"},{"id":"290180","messageId":"alpine.DEB.2.20.1606260847590.12947@virtualbox","threadId":"42648","inReplyTo":"xmqq1t3mfdpy.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 06/10] format-patch: explicitly switch off color when writing to files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-26T06:49:43Z","receivedAt":"2016-06-26T06:49:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 24 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > We rely on the auto-detection (\"is stdout a terminal?\") to determine\n> > whether to use color in the output of format-patch or not. That\n> > happens to work because we freopen() stdout when redirecting the\n> > output to files.\n> >\n> > However, we are about to fix that work-around, in which case the\n> > auto-detection has no chance to guess whether to use color or not.\n> >\n> > But then, we do not need to guess to begin with. As argued in the\n> > commit message of 7787570c (format-patch: ignore ui.color,\n> > 2011-09-13), we do not allow the ui.color setting to affect\n> > format-patch's output. The only time, therefore, that we allow color\n> > sequences to be written to the output files is when the user specified\n> > the --color command-line option explicitly.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> \n> The right fix in the longer term (long after this series lands, that\n> is) is probably to update the world view that the codepath from\n> want_color_auto() to check_auto_color() has always held.  In their\n> world view, when they are asked to make --color=auto decision, the\n> output always goes the standard output, and that is why they\n> hardcode isatty(1) to decide.  The existing freopen() was a part of\n> that world view.\n\nI agree, and I briefly looked into this. It looks a bit more involved than\nI am prepared for, what with my real goal being the rebase--helper, not\nclean-ups ;-)\n\n> We'd need a workaround like this patch if we want to leave the\n> want_color_auto() as-is, and as a workaround I think this is the\n> least invasive one, so let's queue it as-is.\n\nThanks!\nDscho\n"},{"id":"290181","messageId":"alpine.DEB.2.20.1606260853030.12947@virtualbox","threadId":"42648","inReplyTo":"xmqq60syfgqf.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 01/10] Prepare log/log-tree to reuse the diffopt.close_file attribute","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-26T06:56:08Z","receivedAt":"2016-06-26T06:56:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 24 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/builtin/log.c b/builtin/log.c\n> > index 099f4f7..27bc88d 100644\n> > --- a/builtin/log.c\n> > +++ b/builtin/log.c\n> > @@ -243,9 +243,10 @@ static struct itimerval early_output_timer;\n> >  \n> >  static void log_show_early(struct rev_info *revs, struct commit_list *list)\n> >  {\n> > -\tint i = revs->early_output;\n> > +\tint i = revs->early_output, close_file = revs->diffopt.close_file;\n> \n> Probably not worth a reroll, but I find this kind of thing easier to\n> read as two separate definitions.\n> \n> >  \tint show_header = 1;\n> \n> And this was separate from \"int i = ...;\" for the same reason.  It\n> is OK to write \"int i, j, k;\" but \"show_header\" is something that\n> keeps track of the more important state during the control flow and\n> it is easier to read if we make it stand out.  close_file falls into\n> the same category, I would think.\n\nMakes sense. I changed it locally, in case this patch series needs a\nre-roll.\n\n> >  \t\tcase commit_error:\n> > +\t\t\tif (close_file)\n> > +\t\t\t\tfclose(revs->diffopt.file);\n> \n> I wondered if we want to also clear, i.e. revs->diffopt.file = NULL, \n> but I do not think .close_file does that either, so this is good.\n\nIndeed, the current code in diff_flush() just fclose()s the stream, but\ndoes not reset the \"file\" field:\n\n\thttps://github.com/git/git/blob/v2.9.0/diff.c#L4715-L4716\n\nCiao,\nDscho\n"}]}