From eef39f8124ba6045bdc1ae6e665d3d65e28385ab Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Thu, 11 Apr 2013 13:05:09 +0100 Subject: [PATCH 1/6] t0008: remove duplicated test fixture data The expected contents of STDOUT for the final --stdin tests can be derived from the expected contents of STDOUT for the same tests when --verbose is given, in the same way that test_expect_success_multi derives this for earlier tests. Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- t/t0008-ignores.sh | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index 9c1bde1fd6..314a86d961 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -575,21 +575,6 @@ cat <<-\EOF >stdin b/globaltwo ../b/globaltwo EOF -cat <<-\EOF >expected-default - ../one - one - b/on - b/one - b/one one - b/one two - "b/one\"three" - b/two - b/twooo - ../globaltwo - globaltwo - b/globaltwo - ../b/globaltwo -EOF cat <<-EOF >expected-verbose .gitignore:1:one ../one .gitignore:1:one one @@ -605,6 +590,7 @@ cat <<-EOF >expected-verbose $global_excludes:2:!globaltwo b/globaltwo $global_excludes:2:!globaltwo ../b/globaltwo EOF +sed -e 's/.* //' expected-verbose >expected-default sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \ tr "\n" "\0" >stdin0 From ae3caf4c912429c8e9755f2e10d2f0a7f6279c36 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Thu, 11 Apr 2013 13:05:10 +0100 Subject: [PATCH 2/6] check-ignore: add -n / --non-matching option If `-n` or `--non-matching` are specified, non-matching pathnames will also be output, in which case all fields in each output record except for will be empty. This can be useful when running check-ignore as a background process, so that files can be incrementally streamed to STDIN, and for each of these files, STDOUT will indicate whether that file matched a pattern or not. (Without this option, it would be impossible to tell whether the absence of output for a given file meant that it didn't match any pattern, or that the result simply hadn't been flushed to STDOUT yet.) Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- Documentation/git-check-ignore.txt | 15 ++++ builtin/check-ignore.c | 46 +++++++---- t/t0008-ignores.sh | 122 +++++++++++++++++++++-------- 3 files changed, 134 insertions(+), 49 deletions(-) diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt index 854e4d0c42..7e3cabc9dd 100644 --- a/Documentation/git-check-ignore.txt +++ b/Documentation/git-check-ignore.txt @@ -39,6 +39,12 @@ OPTIONS below). If `--stdin` is also given, input paths are separated with a NUL character instead of a linefeed character. +-n, --non-matching:: + Show given paths which don't match any pattern. This only + makes sense when `--verbose` is enabled, otherwise it would + not be possible to distinguish between paths which match a + pattern and those which don't. + OUTPUT ------ @@ -65,6 +71,15 @@ are also used instead of colons and hard tabs: +If `-n` or `--non-matching` are specified, non-matching pathnames will +also be output, in which case all fields in each output record except +for will be empty. This can be useful when running +non-interactively, so that files can be incrementally streamed to +STDIN of a long-running check-ignore process, and for each of these +files, STDOUT will indicate whether that file matched a pattern or +not. (Without this option, it would be impossible to tell whether the +absence of output for a given file meant that it didn't match any +pattern, or that the output hadn't been generated yet.) EXIT STATUS ----------- diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 0240f99b57..59acf74c58 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -5,7 +5,7 @@ #include "pathspec.h" #include "parse-options.h" -static int quiet, verbose, stdin_paths; +static int quiet, verbose, stdin_paths, show_non_matching; static const char * const check_ignore_usage[] = { "git check-ignore [options] pathname...", "git check-ignore [options] --stdin < ", @@ -22,21 +22,28 @@ static const struct option check_ignore_options[] = { N_("read file names from stdin")), OPT_BOOLEAN('z', NULL, &null_term_line, N_("input paths are terminated by a null character")), + OPT_BOOLEAN('n', "non-matching", &show_non_matching, + N_("show non-matching input paths")), OPT_END() }; static void output_exclude(const char *path, struct exclude *exclude) { - char *bang = exclude->flags & EXC_FLAG_NEGATIVE ? "!" : ""; - char *slash = exclude->flags & EXC_FLAG_MUSTBEDIR ? "/" : ""; + char *bang = (exclude && exclude->flags & EXC_FLAG_NEGATIVE) ? "!" : ""; + char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : ""; if (!null_term_line) { if (!verbose) { write_name_quoted(path, stdout, '\n'); } else { - quote_c_style(exclude->el->src, NULL, stdout, 0); - printf(":%d:%s%s%s\t", - exclude->srcpos, - bang, exclude->pattern, slash); + if (exclude) { + quote_c_style(exclude->el->src, NULL, stdout, 0); + printf(":%d:%s%s%s\t", + exclude->srcpos, + bang, exclude->pattern, slash); + } + else { + printf("::\t"); + } quote_c_style(path, NULL, stdout, 0); fputc('\n', stdout); } @@ -44,11 +51,14 @@ static void output_exclude(const char *path, struct exclude *exclude) if (!verbose) { printf("%s%c", path, '\0'); } else { - printf("%s%c%d%c%s%s%s%c%s%c", - exclude->el->src, '\0', - exclude->srcpos, '\0', - bang, exclude->pattern, slash, '\0', - path, '\0'); + if (exclude) + printf("%s%c%d%c%s%s%s%c%s%c", + exclude->el->src, '\0', + exclude->srcpos, '\0', + bang, exclude->pattern, slash, '\0', + path, '\0'); + else + printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0'); } } } @@ -89,15 +99,15 @@ static int check_ignore(const char *prefix, const char **pathspec) ? strlen(prefix) : 0, path); full_path = check_path_for_gitlink(full_path); die_if_path_beyond_symlink(full_path, prefix); + exclude = NULL; if (!seen[i]) { exclude = last_exclude_matching_path(&check, full_path, -1, &dtype); - if (exclude) { - if (!quiet) - output_exclude(path, exclude); - num_ignored++; - } } + if (!quiet && (exclude || show_non_matching)) + output_exclude(path, exclude); + if (exclude) + num_ignored++; } free(seen); clear_directory(&dir); @@ -161,6 +171,8 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (verbose) die(_("cannot have both --quiet and --verbose")); } + if (show_non_matching && !verbose) + die(_("--non-matching is only valid with --verbose")); if (stdin_paths) { num_ignored = check_ignore_stdin_paths(prefix); diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index 314a86d961..7af93ba928 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -66,16 +66,23 @@ test_check_ignore () { init_vars && rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" && - echo git $global_args check-ignore $quiet_opt $verbose_opt $args \ + echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \ >"$HOME/cmd" && + echo "$expect_code" >"$HOME/expected-exit-code" && test_expect_code "$expect_code" \ - git $global_args check-ignore $quiet_opt $verbose_opt $args \ + git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \ >"$HOME/stdout" 2>"$HOME/stderr" && test_cmp "$HOME/expected-stdout" "$HOME/stdout" && stderr_empty_on_success "$expect_code" } -# Runs the same code with 3 different levels of output verbosity, +# Runs the same code with 4 different levels of output verbosity: +# +# 1. with -q / --quiet +# 2. with default verbosity +# 3. with -v / --verbose +# 4. with -v / --verbose, *and* -n / --non-matching +# # expecting success each time. Takes advantage of the fact that # check-ignore --verbose output is the same as normal output except # for the extra first column. @@ -83,7 +90,9 @@ test_check_ignore () { # Arguments: # - (optional) prereqs for this test, e.g. 'SYMLINKS' # - test name -# - output to expect from -v / --verbose mode +# - output to expect from the fourth verbosity mode (the output +# from the other verbosity modes is automatically inferred +# from this value) # - code to run (should invoke test_check_ignore) test_expect_success_multi () { prereq= @@ -92,8 +101,9 @@ test_expect_success_multi () { prereq=$1 shift fi - testname="$1" expect_verbose="$2" code="$3" + testname="$1" expect_all="$2" code="$3" + expect_verbose=$( echo "$expect_all" | grep -v '^:: ' ) expect=$( echo "$expect_verbose" | sed -e 's/.* //' ) test_expect_success $prereq "$testname" ' @@ -101,23 +111,40 @@ test_expect_success_multi () { eval "$code" ' - for quiet_opt in '-q' '--quiet' - do - test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" " + # --quiet is only valid when a single pattern is passed + if test $( echo "$expect_all" | wc -l ) = 1 + then + for quiet_opt in '-q' '--quiet' + do + test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" " expect '' && $code " - done - quiet_opt= + done + quiet_opt= + fi for verbose_opt in '-v' '--verbose' do - test_expect_success $prereq "$testname${verbose_opt:+ with $verbose_opt}" " - expect '$expect_verbose' && - $code - " + for non_matching_opt in '' ' -n' ' --non-matching' + do + if test -n "$non_matching_opt" + then + my_expect="$expect_all" + else + my_expect="$expect_verbose" + fi + + test_code=" + expect '$my_expect' && + $code + " + opts="$verbose_opt$non_matching_opt" + test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code" + done done verbose_opt= + non_matching_opt= } test_expect_success 'setup' ' @@ -178,7 +205,7 @@ test_expect_success 'setup' ' # # test invalid inputs -test_expect_success_multi '. corner-case' '' ' +test_expect_success_multi '. corner-case' ':: .' ' test_check_ignore . 1 ' @@ -276,27 +303,39 @@ do where="in subdir $subdir" fi - test_expect_success_multi "non-existent file $where not ignored" '' " - test_check_ignore '${subdir}non-existent' 1 - " + test_expect_success_multi "non-existent file $where not ignored" \ + ":: ${subdir}non-existent" \ + "test_check_ignore '${subdir}non-existent' 1" test_expect_success_multi "non-existent file $where ignored" \ - ".gitignore:1:one ${subdir}one" " - test_check_ignore '${subdir}one' - " + ".gitignore:1:one ${subdir}one" \ + "test_check_ignore '${subdir}one'" - test_expect_success_multi "existing untracked file $where not ignored" '' " - test_check_ignore '${subdir}not-ignored' 1 - " + test_expect_success_multi "existing untracked file $where not ignored" \ + ":: ${subdir}not-ignored" \ + "test_check_ignore '${subdir}not-ignored' 1" - test_expect_success_multi "existing tracked file $where not ignored" '' " - test_check_ignore '${subdir}ignored-but-in-index' 1 - " + test_expect_success_multi "existing tracked file $where not ignored" \ + ":: ${subdir}ignored-but-in-index" \ + "test_check_ignore '${subdir}ignored-but-in-index' 1" test_expect_success_multi "existing untracked file $where ignored" \ - ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" " - test_check_ignore '${subdir}ignored-and-untracked' - " + ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore '${subdir}ignored-and-untracked'" + + test_expect_success_multi "mix of file types $where" \ +":: ${subdir}non-existent +.gitignore:1:one ${subdir}one +:: ${subdir}not-ignored +:: ${subdir}ignored-but-in-index +.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore ' + ${subdir}non-existent + ${subdir}one + ${subdir}not-ignored + ${subdir}ignored-but-in-index + ${subdir}ignored-and-untracked' + " done # Having established the above, from now on we mostly test against @@ -391,7 +430,7 @@ test_expect_success 'cd to ignored sub-directory with -v' ' # # test handling of symlinks -test_expect_success_multi SYMLINKS 'symlink' '' ' +test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' ' test_check_ignore "a/symlink" 1 ' @@ -574,22 +613,33 @@ cat <<-\EOF >stdin globaltwo b/globaltwo ../b/globaltwo + c/not-ignored EOF -cat <<-EOF >expected-verbose +# N.B. we deliberately end STDIN with a non-matching pattern in order +# to test that the exit code indicates that one or more of the +# provided paths is ignored - in other words, that it represents an +# aggregation of all the results, not just the final result. + +cat <<-EOF >expected-all .gitignore:1:one ../one + :: ../not-ignored .gitignore:1:one one + :: not-ignored a/b/.gitignore:8:!on* b/on a/b/.gitignore:8:!on* b/one a/b/.gitignore:8:!on* b/one one a/b/.gitignore:8:!on* b/one two a/b/.gitignore:8:!on* "b/one\"three" a/b/.gitignore:9:!two b/two + :: b/not-ignored a/.gitignore:1:two* b/twooo $global_excludes:2:!globaltwo ../globaltwo $global_excludes:2:!globaltwo globaltwo $global_excludes:2:!globaltwo b/globaltwo $global_excludes:2:!globaltwo ../b/globaltwo + :: c/not-ignored EOF +grep -v '^:: ' expected-all >expected-verbose sed -e 's/.* //' expected-verbose >expected-default sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \ @@ -615,6 +665,14 @@ test_expect_success '--stdin from subdirectory with -v' ' ) ' +test_expect_success '--stdin from subdirectory with -v -n' ' + expect_from_stdin Date: Thu, 11 Apr 2013 13:05:11 +0100 Subject: [PATCH 3/6] check-ignore: move setup into cmd_check_ignore() Initialisation of the dir_struct and path_exclude_check structs was previously done within check_ignore(). This was acceptable since check_ignore() was only called once per check-ignore invocation; however the next commit will convert it into an inner loop which is called once per line of STDIN when --stdin is given. Therefore moving the initialisation code out into cmd_check_ignore() ensures that initialisation is still only performed once per check-ignore invocation, and consequently that the output is identical whether pathspecs are provided as CLI arguments or via STDIN. Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- builtin/check-ignore.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 59acf74c58..e2d3006464 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -63,30 +63,20 @@ static void output_exclude(const char *path, struct exclude *exclude) } } -static int check_ignore(const char *prefix, const char **pathspec) +static int check_ignore(struct path_exclude_check *check, + const char *prefix, const char **pathspec) { - struct dir_struct dir; const char *path, *full_path; char *seen; int num_ignored = 0, dtype = DT_UNKNOWN, i; - struct path_exclude_check check; struct exclude *exclude; - /* read_cache() is only necessary so we can watch out for submodules. */ - if (read_cache() < 0) - die(_("index file corrupt")); - - memset(&dir, 0, sizeof(dir)); - dir.flags |= DIR_COLLECT_IGNORED; - setup_standard_excludes(&dir); - if (!pathspec || !*pathspec) { if (!quiet) fprintf(stderr, "no pathspec given.\n"); return 0; } - path_exclude_check_init(&check, &dir); /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with @@ -101,7 +91,7 @@ static int check_ignore(const char *prefix, const char **pathspec) die_if_path_beyond_symlink(full_path, prefix); exclude = NULL; if (!seen[i]) { - exclude = last_exclude_matching_path(&check, full_path, + exclude = last_exclude_matching_path(check, full_path, -1, &dtype); } if (!quiet && (exclude || show_non_matching)) @@ -110,13 +100,11 @@ static int check_ignore(const char *prefix, const char **pathspec) num_ignored++; } free(seen); - clear_directory(&dir); - path_exclude_check_clear(&check); return num_ignored; } -static int check_ignore_stdin_paths(const char *prefix) +static int check_ignore_stdin_paths(struct path_exclude_check *check, const char *prefix) { struct strbuf buf, nbuf; char **pathspec = NULL; @@ -139,17 +127,18 @@ static int check_ignore_stdin_paths(const char *prefix) } ALLOC_GROW(pathspec, nr + 1, alloc); pathspec[nr] = NULL; - num_ignored = check_ignore(prefix, (const char **)pathspec); + num_ignored = check_ignore(check, prefix, (const char **)pathspec); maybe_flush_or_die(stdout, "attribute to stdout"); strbuf_release(&buf); strbuf_release(&nbuf); - free(pathspec); return num_ignored; } int cmd_check_ignore(int argc, const char **argv, const char *prefix) { int num_ignored; + struct dir_struct dir; + struct path_exclude_check check; git_config(git_default_config, NULL); @@ -174,12 +163,24 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (show_non_matching && !verbose) die(_("--non-matching is only valid with --verbose")); + /* read_cache() is only necessary so we can watch out for submodules. */ + if (read_cache() < 0) + die(_("index file corrupt")); + + memset(&dir, 0, sizeof(dir)); + dir.flags |= DIR_COLLECT_IGNORED; + setup_standard_excludes(&dir); + + path_exclude_check_init(&check, &dir); if (stdin_paths) { - num_ignored = check_ignore_stdin_paths(prefix); + num_ignored = check_ignore_stdin_paths(&check, prefix); } else { - num_ignored = check_ignore(prefix, argv); + num_ignored = check_ignore(&check, prefix, argv); maybe_flush_or_die(stdout, "ignore to stdout"); } + clear_directory(&dir); + path_exclude_check_clear(&check); + return !num_ignored; } From 0c8e8c080b08632d6dbee33d093b5df648196b49 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Thu, 11 Apr 2013 13:05:12 +0100 Subject: [PATCH 4/6] check-ignore: allow incremental streaming of queries via --stdin Some callers, such as the git-annex web assistant, find it useful to invoke git check-ignore as a persistent background process, which can then have queries fed to its STDIN at any point, and the corresponding response consumed from its STDOUT. For this we need to invoke check_ignore() once per line of standard input, and flush standard output after each result. The above use case suggests that empty STDIN is actually a reasonable scenario (e.g. when the caller doesn't know in advance whether any queries need to be fed to the background process until after it's already started), so we make the minor behavioural change that "no pathspec given." is no longer emitted in when STDIN is empty. Even though check_ignore() could now be changed to operate on a single pathspec, we keep it operating on an array of pathspecs since that is a more convenient way of consuming the existing pathspec API. Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- builtin/check-ignore.c | 15 +++++---------- t/t0008-ignores.sh | 28 +++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index e2d3006464..c00a7d6bb9 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -107,10 +107,9 @@ static int check_ignore(struct path_exclude_check *check, static int check_ignore_stdin_paths(struct path_exclude_check *check, const char *prefix) { struct strbuf buf, nbuf; - char **pathspec = NULL; - size_t nr = 0, alloc = 0; + char *pathspec[2] = { NULL, NULL }; int line_termination = null_term_line ? 0 : '\n'; - int num_ignored; + int num_ignored = 0; strbuf_init(&buf, 0); strbuf_init(&nbuf, 0); @@ -121,14 +120,10 @@ static int check_ignore_stdin_paths(struct path_exclude_check *check, const char die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } - ALLOC_GROW(pathspec, nr + 1, alloc); - pathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf)); - strcpy(pathspec[nr++], buf.buf); + pathspec[0] = buf.buf; + num_ignored += check_ignore(check, prefix, (const char **)pathspec); + maybe_flush_or_die(stdout, "check-ignore to stdout"); } - ALLOC_GROW(pathspec, nr + 1, alloc); - pathspec[nr] = NULL; - num_ignored = check_ignore(check, prefix, (const char **)pathspec); - maybe_flush_or_die(stdout, "attribute to stdout"); strbuf_release(&buf); strbuf_release(&nbuf); return num_ignored; diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index 7af93ba928..fbf12ae4e2 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -216,11 +216,7 @@ test_expect_success_multi 'empty command line' '' ' test_expect_success_multi '--stdin with empty STDIN' '' ' test_check_ignore "--stdin" 1 /dev/null 2>&1 + then + test_set_prereq STDBUF + fi +' + +test_expect_success STDBUF 'streaming support for --stdin' ' + ( + echo one + sleep 2 + echo two + ) | stdbuf -oL git check-ignore -v -n --stdin >out & + pid=$! && + sleep 1 && + grep "^\.gitignore:1:one one" out && + test $( wc -l /dev/null +' test_done From f1ed7fea7974218db15155f3b8d2e29d3fe0971c Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Thu, 11 Apr 2013 13:05:13 +0100 Subject: [PATCH 5/6] Documentation: add caveats about I/O buffering for check-{attr,ignore} check-attr and check-ignore have the potential to deadlock callers which do not read back the output in real-time. For example, if a caller writes N paths out and then reads N lines back in, it risks becoming blocked on write() to check-*, and check-* is blocked on write back to the caller. Somebody has to buffer; the pipe buffers provide some leeway, but they are limited. Thanks to Peff for pointing this out: http://article.gmane.org/gmane.comp.version-control.git/220534 Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- Documentation/git-check-attr.txt | 5 +++++ Documentation/git-check-ignore.txt | 5 +++++ Documentation/git.txt | 5 +++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt index 5abdbaa51c..a7be80d48b 100644 --- a/Documentation/git-check-attr.txt +++ b/Documentation/git-check-attr.txt @@ -56,6 +56,11 @@ being queried and can be either: 'set';; when the attribute is defined as true. ;; when a value has been assigned to the attribute. +Buffering happens as documented under the `GIT_FLUSH` option in +linkgit:git[1]. The caller is responsible for avoiding deadlocks +caused by overfilling an input buffer or reading from an empty output +buffer. + EXAMPLES -------- diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt index 7e3cabc9dd..8e1f7ab7ea 100644 --- a/Documentation/git-check-ignore.txt +++ b/Documentation/git-check-ignore.txt @@ -81,6 +81,11 @@ not. (Without this option, it would be impossible to tell whether the absence of output for a given file meant that it didn't match any pattern, or that the output hadn't been generated yet.) +Buffering happens as documented under the `GIT_FLUSH` option in +linkgit:git[1]. The caller is responsible for avoiding deadlocks +caused by overfilling an input buffer or reading from an empty output +buffer. + EXIT STATUS ----------- diff --git a/Documentation/git.txt b/Documentation/git.txt index 65c37c4544..122b33f066 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -771,8 +771,9 @@ for further details. 'GIT_FLUSH':: If this environment variable is set to "1", then commands such as 'git blame' (in incremental mode), 'git rev-list', 'git log', - and 'git whatchanged' will force a flush of the output stream - after each commit-oriented record have been flushed. If this + 'git check-attr', 'git check-ignore', and 'git whatchanged' will + force a flush of the output stream after each 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 From b96114edb36e16d214af1e7945a1d19b8a5e1686 Mon Sep 17 00:00:00 2001 From: Adam Spiers Date: Mon, 29 Apr 2013 23:55:25 +0100 Subject: [PATCH 6/6] t0008: use named pipe (FIFO) to test check-ignore streaming sleeps in the check-ignore test suite are not ideal since they can fail when the system is under load, or when a tool like valgrind is used which drastically alters the timing. Therefore we replace them with a more robust solution using a named pipe (FIFO). Thanks to Jeff King for coming up with the redirection wizardry required to make this work. http://article.gmane.org/gmane.comp.version-control.git/220916 Signed-off-by: Adam Spiers Signed-off-by: Junio C Hamano --- t/t0008-ignores.sh | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index fbf12ae4e2..a56db804cb 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -688,27 +688,23 @@ do ' done -test_expect_success 'setup: have stdbuf?' ' - if which stdbuf >/dev/null 2>&1 - then - test_set_prereq STDBUF - fi -' +test_expect_success PIPE 'streaming support for --stdin' ' + mkfifo in out && + (git check-ignore -n -v --stdin out &) && -test_expect_success STDBUF 'streaming support for --stdin' ' - ( - echo one - sleep 2 - echo two - ) | stdbuf -oL git check-ignore -v -n --stdin >out & - pid=$! && - sleep 1 && - grep "^\.gitignore:1:one one" out && - test $( wc -l /dev/null + # We cannot just "echo >in" because check-ignore would get EOF + # after echo exited; instead we open the descriptor in our + # shell, and then echo to the fd. We make sure to close it at + # the end, so that the subprocess does get EOF and dies + # properly. + exec 9>in && + test_when_finished "exec 9>&-" && + echo >&9 one && + read response &9 two && + read response