Skip to content

Commit 06f59e9

Browse files
tytsogitster
authored andcommitted
Don't fflush(stdout) when it's not helpful
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]>
1 parent ee36856 commit 06f59e9

File tree

6 files changed

+55
-1
lines changed

6 files changed

+55
-1
lines changed

Documentation/git.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,16 @@ other
396396
'GIT_PAGER'::
397397
This environment variable overrides `$PAGER`.
398398

399+
'GIT_FLUSH'::
400+
If this environment variable is set to "1", then commands such
401+
as git-blame (in incremental mode), git-rev-list, git-log,
402+
git-whatchanged, etc., will force a flush of the output stream
403+
after each commit-oriented record have been flushed. If this
404+
variable is set to "0", the output of these commands will be done
405+
using completely buffered I/O. If this environment variable is
406+
not set, git will choose buffered or record-oriented flushing
407+
based on whether stdout appears to be redirected to a file or not.
408+
399409
'GIT_TRACE'::
400410
If this variable is set to "1", "2" or "true" (comparison
401411
is case insensitive), git will print `trace:` messages on

builtin-blame.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent)
14591459
printf("boundary\n");
14601460
}
14611461
write_filename_info(suspect->path);
1462+
maybe_flush_or_die(stdout, "stdout");
14621463
}
14631464
}
14641465

builtin-rev-list.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ static void show_commit(struct commit *commit)
100100
printf("%s%c", buf, hdr_termination);
101101
free(buf);
102102
}
103-
fflush(stdout);
103+
maybe_flush_or_die(stdout, "stdout");
104104
if (commit->parents) {
105105
free_commit_list(commit->parents);
106106
commit->parents = NULL;

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ extern char git_default_name[MAX_GITNAME];
532532
extern const char *git_commit_encoding;
533533
extern const char *git_log_output_encoding;
534534

535+
/* IO helper functions */
536+
extern void maybe_flush_or_die(FILE *, const char *);
535537
extern int copy_fd(int ifd, int ofd);
536538
extern int read_in_full(int fd, void *buf, size_t count);
537539
extern int write_in_full(int fd, const void *buf, size_t count);

log-tree.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,5 +408,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
408408
shown = 1;
409409
}
410410
opt->loginfo = NULL;
411+
maybe_flush_or_die(stdout, "stdout");
411412
return shown;
412413
}

write_or_die.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,45 @@
11
#include "cache.h"
22

3+
/*
4+
* Some cases use stdio, but want to flush after the write
5+
* to get error handling (and to get better interactive
6+
* behaviour - not buffering excessively).
7+
*
8+
* Of course, if the flush happened within the write itself,
9+
* we've already lost the error code, and cannot report it any
10+
* more. So we just ignore that case instead (and hope we get
11+
* the right error code on the flush).
12+
*
13+
* If the file handle is stdout, and stdout is a file, then skip the
14+
* flush entirely since it's not needed.
15+
*/
16+
void maybe_flush_or_die(FILE *f, const char *desc)
17+
{
18+
static int skip_stdout_flush = -1;
19+
struct stat st;
20+
char *cp;
21+
22+
if (f == stdout) {
23+
if (skip_stdout_flush < 0) {
24+
cp = getenv("GIT_FLUSH");
25+
if (cp)
26+
skip_stdout_flush = (atoi(cp) == 0);
27+
else if ((fstat(fileno(stdout), &st) == 0) &&
28+
S_ISREG(st.st_mode))
29+
skip_stdout_flush = 1;
30+
else
31+
skip_stdout_flush = 0;
32+
}
33+
if (skip_stdout_flush && !ferror(f))
34+
return;
35+
}
36+
if (fflush(f)) {
37+
if (errno == EPIPE)
38+
exit(0);
39+
die("write failure on %s: %s", desc, strerror(errno));
40+
}
41+
}
42+
343
int read_in_full(int fd, void *buf, size_t count)
444
{
545
char *p = buf;

0 commit comments

Comments
 (0)