As adding /dev/null may create spurious output, and that grep applied to a singleton does not works the same way than when it's applied to more than one file, split the arguments in a way that the last batch has never a singleton. Though, when there is only one file in the repository, force adding /dev/null as we cannot do anything about it. Also, as we "spare" one argument from the batch before the last (supposedly a full batch) we must be sure this batch with one element less has at least 2 elements. Meaning that batches must have at least 3 available slots for the algorithm to work. As it's a very unreasonable case (user passing more than MAXARGS - 3 option flags !!!) die loudly. Signed-off-by: Pierre Habouzit <madcoder@xxxxxxxxxx> --- builtin-grep.c | 25 ++++++++++++++++++++++--- t/t7002-grep.sh | 4 ++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/builtin-grep.c b/builtin-grep.c index e13cb31..8f25020 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -189,7 +189,7 @@ static int exec_grep(int argc, const char **argv) static int external_grep(struct grep_opt *opt, const char **paths, int cached) { - int i, nr, argc, hit, len, status; + int i, nr, nbargs, argc, hit, len, status; const char *argv[MAXARGS+1]; char randarg[ARGBUF]; char *argptr = randarg; @@ -253,6 +253,15 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg(p->pattern); } + for (nbargs = i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!S_ISREG(ntohl(ce->ce_mode))) + continue; + if (!pathspec_matches(paths, ce->name)) + continue; + nbargs++; + } + /* * To make sure we get the header printed out when we want it, * add /dev/null to the paths to grep. This is unnecessary @@ -261,8 +270,16 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) * * GNU grep has "-H", but this is portable. */ - if (!opt->name_only && !opt->unmatch_name_only) + if (!opt->name_only && !opt->unmatch_name_only && nbargs == 1) { push_arg("/dev/null"); + } + + /* for the algorithm that spares one file name if possible, we must have + * at least 3 file name slots, so that sparing one, doesn't make a new + * singleton, so rather than incorect output, die loudly. + */ + if (nr >= MAXARGS - 2) + die("You have an insanely long number of options"); hit = 0; argc = nr; @@ -281,12 +298,14 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) memcpy(name + 2, ce->name, len + 1); } argv[argc++] = name; - if (argc < MAXARGS && !ce_stage(ce)) + /* if `nbargs + nr' overflows MAXARGS of one, spare one split now */ + if (argc < MAXARGS - (nbargs + nr == MAXARGS + 1) && !ce_stage(ce)) continue; status = exec_grep(argc, argv); if (0 < status) hit = 1; argc = nr; + nbargs -= argc; if (ce_stage(ce)) { do { i++; diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index 6bfb899..68b2b92 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -107,6 +107,10 @@ do diff expected actual ' + test_expect_failure "grep -c $L (no /dev/null)" ' + git grep -c test $H | grep -q "/dev/null" + ' + done test_done -- 1.5.3.1 - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html