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@debian.org>
---
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@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html