summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2007-06-29 13:40:46 -0400
committerJunio C Hamano <gitster@pobox.com>2007-06-30 20:16:12 -0700
commit06f59e9f5daa06fc4bd51cf4c508b3edd3ed514a (patch)
tree589caae1830bf51eb10f1022cdb0a8f5fa97ba07
parentee36856d8c8c1f8d53650a70d8f61561fb65ba38 (diff)
downloadgit-06f59e9f5daa06fc4bd51cf4c508b3edd3ed514a.tar.gz
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" <tytso@mit.edu> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--Documentation/git.txt10
-rw-r--r--builtin-blame.c1
-rw-r--r--builtin-rev-list.c2
-rw-r--r--cache.h2
-rw-r--r--log-tree.c1
-rw-r--r--write_or_die.c40
6 files changed, 55 insertions, 1 deletions
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 20b5b7bb48..826914837b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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
diff --git a/builtin-blame.c b/builtin-blame.c
index f7e2c13885..da23a6f9c9 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -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");
}
}
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 813aadf596..86db8b03fe 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -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;
diff --git a/cache.h b/cache.h
index ed83d92c5a..0525c4ee55 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/log-tree.c b/log-tree.c
index 0cf21bc051..ced3f332ef 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -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;
}
diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc8515a..e125e11d3b 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -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;