Skip to content

Commit

Permalink
Don't fflush(stdout) when it's not helpful
Browse files Browse the repository at this point in the history
This patch arose from a discussion started by Jim Meyering's patch
whose intention was to provide better diagnostics for failed writes.
Linus proposed a better way to do things, which also had the added
benefit that adding a fflush() to git-log-* operations and incremental
git-blame operations could improve interactive respose time feel, at
the cost of making things a bit slower when we aren't piping the
output to a downstream program.

This patch skips the fflush() calls when stdout is a regular file, or
if the environment variable GIT_FLUSH is set to "0".  This latter can
speed up a command such as:

GIT_FLUSH=0 strace -c -f -e write time git-rev-list HEAD | wc -l

a tiny amount.

Signed-off-by: "Theodore Ts'o" <[email protected]>
Acked-by: Linus Torvalds <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
tytso authored and gitster committed Jul 1, 2007
1 parent ee36856 commit 06f59e9
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 1 deletion.
10 changes: 10 additions & 0 deletions Documentation/git.txt
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@ other
'GIT_PAGER'::
This environment variable overrides `$PAGER`.

'GIT_FLUSH'::
If this environment variable is set to "1", then commands such
as git-blame (in incremental mode), git-rev-list, git-log,
git-whatchanged, etc., will force a flush of the output stream
after each commit-oriented record have been flushed. If this
variable is set to "0", the output of these commands will be done
using completely buffered I/O. If this environment variable is
not set, git will choose buffered or record-oriented flushing
based on whether stdout appears to be redirected to a file or not.

'GIT_TRACE'::
If this variable is set to "1", "2" or "true" (comparison
is case insensitive), git will print `trace:` messages on
Expand Down
1 change: 1 addition & 0 deletions builtin-blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent)
printf("boundary\n");
}
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
}

Expand Down
2 changes: 1 addition & 1 deletion builtin-rev-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ static void show_commit(struct commit *commit)
printf("%s%c", buf, hdr_termination);
free(buf);
}
fflush(stdout);
maybe_flush_or_die(stdout, "stdout");
if (commit->parents) {
free_commit_list(commit->parents);
commit->parents = NULL;
Expand Down
2 changes: 2 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,8 @@ extern char git_default_name[MAX_GITNAME];
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;

/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
extern int read_in_full(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
Expand Down
1 change: 1 addition & 0 deletions log-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,5 +408,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
shown = 1;
}
opt->loginfo = NULL;
maybe_flush_or_die(stdout, "stdout");
return shown;
}
40 changes: 40 additions & 0 deletions write_or_die.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
#include "cache.h"

/*
* Some cases use stdio, but want to flush after the write
* to get error handling (and to get better interactive
* behaviour - not buffering excessively).
*
* Of course, if the flush happened within the write itself,
* we've already lost the error code, and cannot report it any
* more. So we just ignore that case instead (and hope we get
* the right error code on the flush).
*
* If the file handle is stdout, and stdout is a file, then skip the
* flush entirely since it's not needed.
*/
void maybe_flush_or_die(FILE *f, const char *desc)
{
static int skip_stdout_flush = -1;
struct stat st;
char *cp;

if (f == stdout) {
if (skip_stdout_flush < 0) {
cp = getenv("GIT_FLUSH");
if (cp)
skip_stdout_flush = (atoi(cp) == 0);
else if ((fstat(fileno(stdout), &st) == 0) &&
S_ISREG(st.st_mode))
skip_stdout_flush = 1;
else
skip_stdout_flush = 0;
}
if (skip_stdout_flush && !ferror(f))
return;
}
if (fflush(f)) {
if (errno == EPIPE)
exit(0);
die("write failure on %s: %s", desc, strerror(errno));
}
}

int read_in_full(int fd, void *buf, size_t count)
{
char *p = buf;
Expand Down

0 comments on commit 06f59e9

Please sign in to comment.