Merge branch 'js/log-to-diffopt-file'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2016 20:22:15 +0000 (13:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2016 20:22:15 +0000 (13:22 -0700)
The commands in the "log/diff" family have had an FILE* pointer in the
data structure they pass around for a long time, but some codepaths
used to always write to the standard output. As a preparatory step
to make "git format-patch" available to the internal callers, these
codepaths have been updated to consistently write into that FILE*
instead.

* js/log-to-diffopt-file:
mingw: fix the shortlog --output=<file> test
diff: do not color output when --color=auto and --output=<file> is given
t4211: ensure that log respects --output=<file>
shortlog: respect the --output=<file> setting
format-patch: use stdout directly
format-patch: avoid freopen()
format-patch: explicitly switch off color when writing to files
shortlog: support outputting to streams other than stdout
graph: respect the diffopt.file setting
line-log: respect diffopt's configured output file stream
log-tree: respect diffopt's configured output file stream
log: prepare log/log-tree to reuse the diffopt.close_file attribute

1  2 
builtin/log.c
builtin/shortlog.c
graph.c
line-log.c
log-tree.c
diff --combined builtin/log.c
index 0b6f7392b9fac31fe08f3e8743b44d018f6d4740,1a6903b3f0e1d525898188355c5aac1193076ee5..fd1652f52b32fc8bfb7bdc6e0191affafaa21c9c
@@@ -33,7 -33,6 +33,7 @@@ static const char *default_date_mode = 
  static int default_abbrev_commit;
  static int default_show_root = 1;
  static int default_follow;
 +static int default_show_signature;
  static int decoration_style;
  static int decoration_given;
  static int use_mailmap_config;
@@@ -120,7 -119,6 +120,7 @@@ static void cmd_log_init_defaults(struc
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
 +      rev->show_signature = default_show_signature;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
  
        if (default_date_mode)
@@@ -238,16 -236,17 +238,17 @@@ static void show_early_header(struct re
                if (rev->commit_format != CMIT_FMT_ONELINE)
                        putchar(rev->diffopt.line_termination);
        }
-       printf(_("Final output: %d %s\n"), nr, stage);
+       fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
  }
  
  static struct itimerval early_output_timer;
  
  static void log_show_early(struct rev_info *revs, struct commit_list *list)
  {
-       int i = revs->early_output;
+       int i = revs->early_output, close_file = revs->diffopt.close_file;
        int show_header = 1;
  
+       revs->diffopt.close_file = 0;
        sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
                case commit_ignore:
                        break;
                case commit_error:
+                       if (close_file)
+                               fclose(revs->diffopt.file);
                        return;
                }
                list = list->next;
        }
  
        /* Did we already get enough commits for the early output? */
-       if (!i)
+       if (!i) {
+               if (close_file)
+                       fclose(revs->diffopt.file);
                return;
+       }
  
        /*
         * ..if no, then repeat it twice a second until we
@@@ -333,7 -337,7 +339,7 @@@ static int cmd_log_walk(struct rev_inf
  {
        struct commit *commit;
        int saved_nrl = 0;
-       int saved_dcctc = 0;
+       int saved_dcctc = 0, close_file = rev->diffopt.close_file;
  
        if (rev->early_output)
                setup_early_output(rev);
         * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
         * retain that state information if replacing rev->diffopt in this loop
         */
+       rev->diffopt.close_file = 0;
        while ((commit = get_revision(rev)) != NULL) {
                if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
                        /*
        }
        rev->diffopt.degraded_cc_to_c = saved_dcctc;
        rev->diffopt.needed_rename_limit = saved_nrl;
+       if (close_file)
+               fclose(rev->diffopt.file);
  
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
@@@ -411,10 -418,6 +420,10 @@@ static int git_log_config(const char *v
                use_mailmap_config = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "log.showsignature")) {
 +              default_show_signature = git_config_bool(var, value);
 +              return 0;
 +      }
  
        if (grep_config(var, value, cb) < 0)
                return -1;
@@@ -451,7 -454,7 +460,7 @@@ static void show_tagger(char *buf, int 
        pp.fmt = rev->commit_format;
        pp.date_mode = rev->date_mode;
        pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
-       printf("%s", out.buf);
+       fprintf(rev->diffopt.file, "%s", out.buf);
        strbuf_release(&out);
  }
  
@@@ -462,7 -465,7 +471,7 @@@ static int show_blob_object(const unsig
        char *buf;
        unsigned long size;
  
-       fflush(stdout);
+       fflush(rev->diffopt.file);
        if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, sha1, NULL, 0);
@@@ -502,7 -505,7 +511,7 @@@ static int show_tag_object(const unsign
        }
  
        if (offset < size)
-               fwrite(buf + offset, size - offset, 1, stdout);
+               fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
        free(buf);
        return 0;
  }
@@@ -511,7 -514,8 +520,8 @@@ static int show_tree_object(const unsig
                struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
  {
-       printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+       FILE *file = context;
+       fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
        return 0;
  }
  
@@@ -571,7 -575,7 +581,7 @@@ int cmd_show(int argc, const char **arg
  
                        if (rev.shown_one)
                                putchar('\n');
-                       printf("%stag %s%s\n",
+                       fprintf(rev.diffopt.file, "%stag %s%s\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                case OBJ_TREE:
                        if (rev.shown_one)
                                putchar('\n');
-                       printf("%stree %s%s\n\n",
+                       fprintf(rev.diffopt.file, "%stree %s%s\n\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
-                                       show_tree_object, NULL);
+                                       show_tree_object, rev.diffopt.file);
                        rev.shown_one = 1;
                        break;
                case OBJ_COMMIT:
@@@ -680,9 -684,9 +690,9 @@@ static int auto_number = 1
  
  static char *default_attach = NULL;
  
 -static struct string_list extra_hdr;
 -static struct string_list extra_to;
 -static struct string_list extra_cc;
 +static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_to = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
  
  static void add_header(const char *value)
  {
@@@ -801,11 -805,10 +811,10 @@@ static int git_format_config(const cha
        return git_log_config(var, value, cb);
  }
  
- static FILE *realstdout = NULL;
  static const char *output_directory = NULL;
  static int outdir_offset;
  
- static int reopen_stdout(struct commit *commit, const char *subject,
+ static int open_next_file(struct commit *commit, const char *subject,
                         struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
                fmt_output_subject(&filename, subject, rev);
  
        if (!quiet)
-               fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
+               printf("%s\n", filename.buf + outdir_offset);
  
-       if (freopen(filename.buf, "w", stdout) == NULL)
+       if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
                return error(_("Cannot open patch file %s"), filename.buf);
  
        strbuf_release(&filename);
@@@ -888,15 -891,15 +897,15 @@@ static void gen_message_id(struct rev_i
        info->message_id = strbuf_detach(&buf, NULL);
  }
  
- static void print_signature(void)
+ static void print_signature(FILE *file)
  {
        if (!signature || !*signature)
                return;
  
-       printf("-- \n%s", signature);
+       fprintf(file, "-- \n%s", signature);
        if (signature[strlen(signature)-1] != '\n')
-               putchar('\n');
-       putchar('\n');
+               putc('\n', file);
+       putc('\n', file);
  }
  
  static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@@ -959,13 -962,13 +968,13 @@@ static void make_cover_letter(struct re
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
  
 -      if (rev->commit_format != CMIT_FMT_EMAIL)
 +      if (!cmit_fmt_is_mail(rev->commit_format))
                die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
  
        if (!use_stdout &&
-           reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
+           open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
  
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
        pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
        pp_remainder(&pp, &msg, &sb, 0);
        add_branch_description(&sb, branch_name);
-       printf("%s\n", sb.buf);
+       fprintf(rev->diffopt.file, "%s\n", sb.buf);
  
        strbuf_release(&sb);
  
        log.wrap = 72;
        log.in1 = 2;
        log.in2 = 4;
+       log.file = rev->diffopt.file;
        for (i = 0; i < nr; i++)
                shortlog_add_commit(&log, list[i]);
  
        diffcore_std(&opts);
        diff_flush(&opts);
  
-       printf("\n");
-       print_signature();
+       fprintf(rev->diffopt.file, "\n");
+       print_signature(rev->diffopt.file);
  }
  
  static const char *clean_message_id(const char *msg_id)
@@@ -1330,7 -1334,7 +1340,7 @@@ static void prepare_bases(struct base_t
        }
  }
  
- static void print_bases(struct base_tree_info *bases)
+ static void print_bases(struct base_tree_info *bases, FILE *file)
  {
        int i;
  
                return;
  
        /* Show the base commit */
-       printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+       fprintf(file, "base-commit: %s\n", oid_to_hex(&bases->base_commit));
  
        /* Show the prerequisite patches */
        for (i = bases->nr_patch_id - 1; i >= 0; i--)
-               printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+               fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
  
        free(bases->patch_id);
        bases->nr_patch_id = 0;
@@@ -1575,6 -1579,8 +1585,8 @@@ int cmd_format_patch(int argc, const ch
                setup_pager();
  
        if (output_directory) {
+               if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
+                       rev.diffopt.use_color = GIT_COLOR_NEVER;
                if (use_stdout)
                        die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
                get_patch_ids(&rev, &ids);
        }
  
-       if (!use_stdout)
-               realstdout = xfdopen(xdup(1), "w");
        if (prepare_revision_walk(&rev))
                die(_("revision walk setup failed"));
        rev.boundary = 1;
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
-               print_bases(&bases);
+               print_bases(&bases, rev.diffopt.file);
                total++;
                start_number--;
        }
                }
  
                if (!use_stdout &&
-                   reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
+                   open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free_commit_buffer(commit);
                        rev.shown_one = 0;
                if (shown) {
                        if (rev.mime_boundary)
-                               printf("\n--%s%s--\n\n\n",
+                               fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
-                               print_signature();
-                       print_bases(&bases);
+                               print_signature(rev.diffopt.file);
+                       print_bases(&bases, rev.diffopt.file);
                }
                if (!use_stdout)
-                       fclose(stdout);
+                       fclose(rev.diffopt.file);
        }
        free(list);
        free(branch_name);
@@@ -1800,15 -1803,15 +1809,15 @@@ static const char * const cherry_usage[
  };
  
  static void print_commit(char sign, struct commit *commit, int verbose,
-                        int abbrev)
+                        int abbrev, FILE *file)
  {
        if (!verbose) {
-               printf("%c %s\n", sign,
+               fprintf(file, "%c %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
-               printf("%c %s %s\n", sign,
+               fprintf(file, "%c %s %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev),
                       buf.buf);
                strbuf_release(&buf);
@@@ -1889,7 -1892,7 +1898,7 @@@ int cmd_cherry(int argc, const char **a
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
-               print_commit(sign, commit, verbose, abbrev);
+               print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
                list = list->next;
        }
  
diff --combined builtin/shortlog.c
index f83984e8a1264054b5bd32b3d559c75708a4f428,be8054791e1d4fd656c7e311f2e4f21ff5ca94d4..25fa8a6aed72bb91c3b99121059a90cc976976a5
@@@ -233,11 -233,11 +233,11 @@@ void shortlog_init(struct shortlog *log
  
  int cmd_shortlog(int argc, const char **argv, const char *prefix)
  {
 -      static struct shortlog log;
 -      static struct rev_info rev;
 +      struct shortlog log = { STRING_LIST_INIT_NODUP };
 +      struct rev_info rev;
        int nongit = !startup_info->have_repository;
  
 -      static const struct option options[] = {
 +      const struct option options[] = {
                OPT_BOOL('n', "numbered", &log.sort_by_number,
                         N_("sort output according to the number of commits per author")),
                OPT_BOOL('s', "summary", &log.summary,
@@@ -276,6 -276,7 +276,7 @@@ parse_done
  
        log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
        log.abbrev = rev.abbrev;
+       log.file = rev.diffopt.file;
  
        /* assume HEAD if from a tty */
        if (!nongit && !rev.pending.nr && isatty(0))
                get_from_rev(&rev, &log);
  
        shortlog_output(&log);
+       if (log.file != stdout)
+               fclose(log.file);
        return 0;
  }
  
@@@ -310,22 -313,24 +313,24 @@@ void shortlog_output(struct shortlog *l
        for (i = 0; i < log->list.nr; i++) {
                const struct string_list_item *item = &log->list.items[i];
                if (log->summary) {
-                       printf("%6d\t%s\n", (int)UTIL_TO_INT(item), item->string);
+                       fprintf(log->file, "%6d\t%s\n",
+                               (int)UTIL_TO_INT(item), item->string);
                } else {
                        struct string_list *onelines = item->util;
-                       printf("%s (%d):\n", item->string, onelines->nr);
+                       fprintf(log->file, "%s (%d):\n",
+                               item->string, onelines->nr);
                        for (j = onelines->nr - 1; j >= 0; j--) {
                                const char *msg = onelines->items[j].string;
  
                                if (log->wrap_lines) {
                                        strbuf_reset(&sb);
                                        add_wrapped_shortlog_msg(&sb, msg, log);
-                                       fwrite(sb.buf, sb.len, 1, stdout);
+                                       fwrite(sb.buf, sb.len, 1, log->file);
                                }
                                else
-                                       printf("      %s\n", msg);
+                                       fprintf(log->file, "      %s\n", msg);
                        }
-                       putchar('\n');
+                       putc('\n', log->file);
                        onelines->strdup_strings = 1;
                        string_list_clear(onelines, 0);
                        free(onelines);
diff --combined graph.c
index ad766facad65e174b4354ab969cef715ad687470,8ad8ba362f36cab9774b95726dcd60534ba16d54..dd1720148dc51740c96da0f4b17b59b97994abac
+++ b/graph.c
@@@ -17,8 -17,8 +17,8 @@@
  static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
  
  /*
-  * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
-  * first will be prefixed with the graph output.
+  * Print a strbuf.  If the graph is non-NULL, all lines but the first will be
+  * prefixed with the graph output.
   *
   * If the strbuf ends with a newline, the output will end after this
   * newline.  A new graph line will not be printed after the final newline.
@@@ -669,13 -669,6 +669,13 @@@ static void graph_output_padding_line(s
        graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
  }
  
 +
 +int graph_width(struct git_graph *graph)
 +{
 +      return graph->width;
 +}
 +
 +
  static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
  {
        /*
@@@ -1200,9 -1193,10 +1200,10 @@@ void graph_show_commit(struct git_grap
  
        while (!shown_commit_line && !graph_is_commit_finished(graph)) {
                shown_commit_line = graph_next_line(graph, &msgbuf);
-               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
+                       graph->revs->diffopt.file);
                if (!shown_commit_line)
-                       putchar('\n');
+                       putc('\n', graph->revs->diffopt.file);
                strbuf_setlen(&msgbuf, 0);
        }
  
@@@ -1217,7 -1211,7 +1218,7 @@@ void graph_show_oneline(struct git_grap
                return;
  
        graph_next_line(graph, &msgbuf);
-       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
        strbuf_release(&msgbuf);
  }
  
@@@ -1229,7 -1223,7 +1230,7 @@@ void graph_show_padding(struct git_grap
                return;
  
        graph_padding_line(graph, &msgbuf);
-       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
        strbuf_release(&msgbuf);
  }
  
@@@ -1246,12 -1240,13 +1247,13 @@@ int graph_show_remainder(struct git_gra
  
        for (;;) {
                graph_next_line(graph, &msgbuf);
-               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
+                       graph->revs->diffopt.file);
                strbuf_setlen(&msgbuf, 0);
                shown = 1;
  
                if (!graph_is_commit_finished(graph))
-                       putchar('\n');
+                       putc('\n', graph->revs->diffopt.file);
                else
                        break;
        }
@@@ -1266,7 -1261,8 +1268,8 @@@ static void graph_show_strbuf(struct gi
        char *p;
  
        if (!graph) {
-               fwrite(sb->buf, sizeof(char), sb->len, stdout);
+               fwrite(sb->buf, sizeof(char), sb->len,
+                       graph->revs->diffopt.file);
                return;
        }
  
                } else {
                        len = (sb->buf + sb->len) - p;
                }
-               fwrite(p, sizeof(char), len, stdout);
+               fwrite(p, sizeof(char), len, graph->revs->diffopt.file);
                if (next_p && *next_p != '\0')
                        graph_show_oneline(graph);
                p = next_p;
@@@ -1304,7 -1300,8 +1307,8 @@@ void graph_show_commit_msg(struct git_g
                 * CMIT_FMT_USERFORMAT are already missing a terminating
                 * newline.  All of the other formats should have it.
                 */
-               fwrite(sb->buf, sizeof(char), sb->len, stdout);
+               fwrite(sb->buf, sizeof(char), sb->len,
+                       graph->revs->diffopt.file);
                return;
        }
  
                 * new line.
                 */
                if (!newline_terminated)
-                       putchar('\n');
+                       putc('\n', graph->revs->diffopt.file);
  
                graph_show_remainder(graph);
  
                 * If sb ends with a newline, our output should too.
                 */
                if (newline_terminated)
-                       putchar('\n');
+                       putc('\n', graph->revs->diffopt.file);
        }
  }
diff --combined line-log.c
index 1fbbe4f0af5863ebb1d0f393a3ce1ddec61e9671,e62a7f4ac4102556cc49bbec76b9a0e737efea77..a30c89a30966cda66f55f294b986049fdfeed34e
@@@ -480,7 -480,8 +480,7 @@@ static struct commit *check_single_comm
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags & UNINTERESTING)
                        continue;
 -              while (obj->type == OBJ_TAG)
 -                      obj = deref_tag(obj, NULL, 0);
 +              obj = deref_tag(obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (commit)
@@@ -840,7 -841,7 +840,7 @@@ static char *get_nth_line(long line, un
  
  static void print_line(const char *prefix, char first,
                       long line, unsigned long *ends, void *data,
-                      const char *color, const char *reset)
+                      const char *color, const char *reset, FILE *file)
  {
        char *begin = get_nth_line(line, ends, data);
        char *end = get_nth_line(line+1, ends, data);
                had_nl = 1;
        }
  
-       fputs(prefix, stdout);
-       fputs(color, stdout);
-       putchar(first);
-       fwrite(begin, 1, end-begin, stdout);
-       fputs(reset, stdout);
-       putchar('\n');
+       fputs(prefix, file);
+       fputs(color, file);
+       putc(first, file);
+       fwrite(begin, 1, end-begin, file);
+       fputs(reset, file);
+       putc('\n', file);
        if (!had_nl)
-               fputs("\\ No newline at end of file\n", stdout);
+               fputs("\\ No newline at end of file\n", file);
  }
  
  static char *output_prefix(struct diff_options *opt)
@@@ -897,12 -898,12 +897,12 @@@ static void dump_diff_hacky_one(struct 
                fill_line_ends(pair->one, &p_lines, &p_ends);
        fill_line_ends(pair->two, &t_lines, &t_ends);
  
-       printf("%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
-       printf("%s%s--- %s%s%s\n", prefix, c_meta,
+       fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
+       fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
               pair->one->sha1_valid ? "a/" : "",
               pair->one->sha1_valid ? pair->one->path : "/dev/null",
               c_reset);
-       printf("%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
+       fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
        for (i = 0; i < range->ranges.nr; i++) {
                long p_start, p_end;
                long t_start = range->ranges.ranges[i].start;
                }
  
                /* Now output a diff hunk for this range */
-               printf("%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
+               fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
                       prefix, c_frag,
                       p_start+1, p_end-p_start, t_start+1, t_end-t_start,
                       c_reset);
                        int k;
                        for (; t_cur < diff->target.ranges[j].start; t_cur++)
                                print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-                                          c_context, c_reset);
+                                          c_context, c_reset, opt->file);
                        for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
                                print_line(prefix, '-', k, p_ends, pair->one->data,
-                                          c_old, c_reset);
+                                          c_old, c_reset, opt->file);
                        for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
                                print_line(prefix, '+', t_cur, t_ends, pair->two->data,
-                                          c_new, c_reset);
+                                          c_new, c_reset, opt->file);
                        j++;
                }
                for (; t_cur < t_end; t_cur++)
                        print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-                                  c_context, c_reset);
+                                  c_context, c_reset, opt->file);
        }
  
        free(p_ends);
   */
  static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
  {
-       puts(output_prefix(&rev->diffopt));
+       fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
        while (range) {
                dump_diff_hacky_one(rev, range);
                range = range->next;
diff --combined log-tree.c
index 9f678abc92a82ce8c0feac6b66044291b730aac9,cf240273532fdb2ecce5c61852540b599c92fef0..d0062e6788e95620bb6e1a534661dbb70bdbb098
@@@ -159,12 -159,12 +159,12 @@@ void load_ref_decorations(int flags
        }
  }
  
- static void show_parents(struct commit *commit, int abbrev)
+ static void show_parents(struct commit *commit, int abbrev, FILE *file)
  {
        struct commit_list *p;
        for (p = commit->parents; p ; p = p->next) {
                struct commit *parent = p->item;
-               printf(" %s", find_unique_abbrev(parent->object.oid.hash, abbrev));
+               fprintf(file, " %s", find_unique_abbrev(parent->object.oid.hash, abbrev));
        }
  }
  
@@@ -172,7 -172,7 +172,7 @@@ static void show_children(struct rev_in
  {
        struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
        for ( ; p; p = p->next) {
-               printf(" %s", find_unique_abbrev(p->item->object.oid.hash, abbrev));
+               fprintf(opt->diffopt.file, " %s", find_unique_abbrev(p->item->object.oid.hash, abbrev));
        }
  }
  
@@@ -286,11 -286,11 +286,11 @@@ void show_decorations(struct rev_info *
        struct strbuf sb = STRBUF_INIT;
  
        if (opt->show_source && commit->util)
-               printf("\t%s", (char *) commit->util);
+               fprintf(opt->diffopt.file, "\t%s", (char *) commit->util);
        if (!opt->show_decorations)
                return;
        format_decorations(&sb, commit, opt->diffopt.use_color);
-       fputs(sb.buf, stdout);
+       fputs(sb.buf, opt->diffopt.file);
        strbuf_release(&sb);
  }
  
@@@ -364,18 -364,18 +364,18 @@@ void log_write_email_headers(struct rev
                subject = "Subject: ";
        }
  
-       printf("From %s Mon Sep 17 00:00:00 2001\n", name);
+       fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
        graph_show_oneline(opt->graph);
        if (opt->message_id) {
-               printf("Message-Id: <%s>\n", opt->message_id);
+               fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
                graph_show_oneline(opt->graph);
        }
        if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
                int i, n;
                n = opt->ref_message_ids->nr;
-               printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
+               fprintf(opt->diffopt.file, "In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
                for (i = 0; i < n; i++)
-                       printf("%s<%s>\n", (i > 0 ? "\t" : "References: "),
+                       fprintf(opt->diffopt.file, "%s<%s>\n", (i > 0 ? "\t" : "References: "),
                               opt->ref_message_ids->items[i].string);
                graph_show_oneline(opt->graph);
        }
@@@ -432,7 -432,7 +432,7 @@@ static void show_sig_lines(struct rev_i
        reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);
        while (*bol) {
                eol = strchrnul(bol, '\n');
-               printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
+               fprintf(opt->diffopt.file, "%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
                       *eol ? "\n" : "");
                graph_show_oneline(opt->graph);
                bol = (*eol) ? (eol + 1) : eol;
@@@ -553,17 -553,17 +553,17 @@@ void show_log(struct rev_info *opt
  
                if (!opt->graph)
                        put_revision_mark(opt, commit);
-               fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);
+               fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file);
                if (opt->print_parents)
-                       show_parents(commit, abbrev_commit);
+                       show_parents(commit, abbrev_commit, opt->diffopt.file);
                if (opt->children.name)
                        show_children(opt, commit, abbrev_commit);
                show_decorations(opt, commit);
                if (opt->graph && !graph_is_commit_finished(opt->graph)) {
-                       putchar('\n');
+                       putc('\n', opt->diffopt.file);
                        graph_show_remainder(opt->graph);
                }
-               putchar(opt->diffopt.line_termination);
+               putc(opt->diffopt.line_termination, opt->diffopt.file);
                return;
        }
  
                if (opt->diffopt.line_termination == '\n' &&
                    !opt->missing_newline)
                        graph_show_padding(opt->graph);
-               putchar(opt->diffopt.line_termination);
+               putc(opt->diffopt.line_termination, opt->diffopt.file);
        }
        opt->shown_one = 1;
  
         * Print header line of header..
         */
  
 -      if (opt->commit_format == CMIT_FMT_EMAIL) {
 +      if (cmit_fmt_is_mail(opt->commit_format)) {
                log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
                                        &ctx.need_8bit_cte);
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
-               fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
+               fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
                if (opt->commit_format != CMIT_FMT_ONELINE)
-                       fputs("commit ", stdout);
+                       fputs("commit ", opt->diffopt.file);
  
                if (!opt->graph)
                        put_revision_mark(opt, commit);
                fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),
-                     stdout);
+                     opt->diffopt.file);
                if (opt->print_parents)
-                       show_parents(commit, abbrev_commit);
+                       show_parents(commit, abbrev_commit, opt->diffopt.file);
                if (opt->children.name)
                        show_children(opt, commit, abbrev_commit);
                if (parent)
-                       printf(" (from %s)",
+                       fprintf(opt->diffopt.file, " (from %s)",
                               find_unique_abbrev(parent->object.oid.hash,
                                                  abbrev_commit));
-               fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);
+               fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);
                show_decorations(opt, commit);
                if (opt->commit_format == CMIT_FMT_ONELINE) {
-                       putchar(' ');
+                       putc(' ', opt->diffopt.file);
                } else {
-                       putchar('\n');
+                       putc('\n', opt->diffopt.file);
                        graph_show_oneline(opt->graph);
                }
                if (opt->reflog_info) {
        ctx.output_encoding = get_log_output_encoding();
        if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
                ctx.from_ident = &opt->from_ident;
 +      if (opt->graph)
 +              ctx.graph_width = graph_width(opt->graph);
        pretty_print_commit(&ctx, commit, &msgbuf);
  
        if (opt->add_signoff)
  
        if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
            ctx.notes_message && *ctx.notes_message) {
 -              if (ctx.fmt == CMIT_FMT_EMAIL) {
 +              if (cmit_fmt_is_mail(ctx.fmt)) {
                        strbuf_addstr(&msgbuf, "---\n");
                        opt->shown_dashes = 1;
                }
        }
  
        if (opt->show_log_size) {
-               printf("log size %i\n", (int)msgbuf.len);
+               fprintf(opt->diffopt.file, "log size %i\n", (int)msgbuf.len);
                graph_show_oneline(opt->graph);
        }
  
        if (opt->graph)
                graph_show_commit_msg(opt->graph, &msgbuf);
        else
-               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, opt->diffopt.file);
        if (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {
                if (!opt->missing_newline)
                        graph_show_padding(opt->graph);
-               putchar(opt->diffopt.line_termination);
+               putc(opt->diffopt.line_termination, opt->diffopt.file);
        }
  
        strbuf_release(&msgbuf);
@@@ -761,7 -759,7 +761,7 @@@ int log_tree_diff_flush(struct rev_inf
                                struct strbuf *msg = NULL;
                                msg = opt->diffopt.output_prefix(&opt->diffopt,
                                        opt->diffopt.output_prefix_data);
-                               fwrite(msg->buf, msg->len, 1, stdout);
+                               fwrite(msg->buf, msg->len, 1, opt->diffopt.file);
                        }
  
                        /*
                         */
                        if (!opt->shown_dashes &&
                            (pch & opt->diffopt.output_format) == pch)
-                               printf("---");
-                       putchar('\n');
+                               fprintf(opt->diffopt.file, "---");
+                       putc('\n', opt->diffopt.file);
                }
        }
        diff_flush(&opt->diffopt);
@@@ -864,17 -862,18 +864,18 @@@ static int log_tree_diff(struct rev_inf
  int log_tree_commit(struct rev_info *opt, struct commit *commit)
  {
        struct log_info log;
-       int shown;
+       int shown, close_file = opt->diffopt.close_file;
  
        log.commit = commit;
        log.parent = NULL;
        opt->loginfo = &log;
+       opt->diffopt.close_file = 0;
  
        if (opt->line_level_traverse)
                return line_log_print(opt, commit);
  
        if (opt->track_linear && !opt->linear && !opt->reverse_output_stage)
-               printf("\n%s\n", opt->break_bar);
+               fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
        shown = log_tree_diff(opt, commit, &log);
        if (!shown && opt->loginfo && opt->always_show_header) {
                log.parent = NULL;
                shown = 1;
        }
        if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
-               printf("\n%s\n", opt->break_bar);
+               fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
        opt->loginfo = NULL;
-       maybe_flush_or_die(stdout, "stdout");
+       maybe_flush_or_die(opt->diffopt.file, "stdout");
+       if (close_file)
+               fclose(opt->diffopt.file);
        return shown;
  }