Skip to content

Commit 036465a

Browse files
committed
Merge branch 'jk/grep-no-index-fix'
The code to parse the command line "git grep <patterns>... <rev> [[--] <pathspec>...]" has been cleaned up, and a handful of bugs have been fixed (e.g. we used to check "--" if it is a rev). * jk/grep-no-index-fix: grep: treat revs the same for --untracked as for --no-index grep: do not diagnose misspelt revs with --no-index grep: avoid resolving revision names in --no-index case grep: fix "--" rev/pathspec disambiguation grep: re-order rev-parsing loop grep: do not unnecessarily query repo for "--" grep: move thread initialization a little lower
2 parents c96bc18 + 131f3c9 commit 036465a

File tree

2 files changed

+121
-27
lines changed

2 files changed

+121
-27
lines changed

builtin/grep.c

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
967967
int dummy;
968968
int use_index = 1;
969969
int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
970+
int allow_revs;
970971

971972
struct option options[] = {
972973
OPT_BOOL(0, "cached", &cached,
@@ -1149,26 +1150,69 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11491150

11501151
compile_grep_patterns(&opt);
11511152

1152-
/* Check revs and then paths */
1153+
/*
1154+
* We have to find "--" in a separate pass, because its presence
1155+
* influences how we will parse arguments that come before it.
1156+
*/
1157+
for (i = 0; i < argc; i++) {
1158+
if (!strcmp(argv[i], "--")) {
1159+
seen_dashdash = 1;
1160+
break;
1161+
}
1162+
}
1163+
1164+
/*
1165+
* Resolve any rev arguments. If we have a dashdash, then everything up
1166+
* to it must resolve as a rev. If not, then we stop at the first
1167+
* non-rev and assume everything else is a path.
1168+
*/
1169+
allow_revs = use_index && !untracked;
11531170
for (i = 0; i < argc; i++) {
11541171
const char *arg = argv[i];
11551172
unsigned char sha1[20];
11561173
struct object_context oc;
1157-
/* Is it a rev? */
1158-
if (!get_sha1_with_context(arg, 0, sha1, &oc)) {
1159-
struct object *object = parse_object_or_die(sha1, arg);
1160-
if (!seen_dashdash)
1161-
verify_non_filename(prefix, arg);
1162-
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
1163-
continue;
1164-
}
1174+
struct object *object;
1175+
11651176
if (!strcmp(arg, "--")) {
11661177
i++;
1167-
seen_dashdash = 1;
1178+
break;
11681179
}
1169-
break;
1180+
1181+
if (!allow_revs) {
1182+
if (seen_dashdash)
1183+
die(_("--no-index or --untracked cannot be used with revs"));
1184+
break;
1185+
}
1186+
1187+
if (get_sha1_with_context(arg, 0, sha1, &oc)) {
1188+
if (seen_dashdash)
1189+
die(_("unable to resolve revision: %s"), arg);
1190+
break;
1191+
}
1192+
1193+
object = parse_object_or_die(sha1, arg);
1194+
if (!seen_dashdash)
1195+
verify_non_filename(prefix, arg);
1196+
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
11701197
}
11711198

1199+
/*
1200+
* Anything left over is presumed to be a path. But in the non-dashdash
1201+
* "do what I mean" case, we verify and complain when that isn't true.
1202+
*/
1203+
if (!seen_dashdash) {
1204+
int j;
1205+
for (j = i; j < argc; j++)
1206+
verify_filename(prefix, argv[j], j == i && allow_revs);
1207+
}
1208+
1209+
parse_pathspec(&pathspec, 0,
1210+
PATHSPEC_PREFER_CWD |
1211+
(opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
1212+
prefix, argv + i);
1213+
pathspec.max_depth = opt.max_depth;
1214+
pathspec.recursive = 1;
1215+
11721216
#ifndef NO_PTHREADS
11731217
if (list.nr || cached || show_in_pager)
11741218
num_threads = 0;
@@ -1190,20 +1234,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11901234
}
11911235
#endif
11921236

1193-
/* The rest are paths */
1194-
if (!seen_dashdash) {
1195-
int j;
1196-
for (j = i; j < argc; j++)
1197-
verify_filename(prefix, argv[j], j == i);
1198-
}
1199-
1200-
parse_pathspec(&pathspec, 0,
1201-
PATHSPEC_PREFER_CWD |
1202-
(opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
1203-
prefix, argv + i);
1204-
pathspec.max_depth = opt.max_depth;
1205-
pathspec.recursive = 1;
1206-
12071237
if (recurse_submodules) {
12081238
gitmodules_config();
12091239
compile_submodule_options(&opt, &pathspec, cached, untracked,
@@ -1245,8 +1275,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
12451275

12461276
if (!use_index || untracked) {
12471277
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
1248-
if (list.nr)
1249-
die(_("--no-index or --untracked cannot be used with revs."));
12501278
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
12511279
} else if (0 <= opt_exclude) {
12521280
die(_("--[no-]exclude-standard cannot be used for tracked contents."));

t/t7810-grep.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,72 @@ test_expect_success 'grep -e -- -- path' '
982982
test_cmp expected actual
983983
'
984984

985+
test_expect_success 'dashdash disambiguates rev as rev' '
986+
test_when_finished "rm -f master" &&
987+
echo content >master &&
988+
echo master:hello.c >expect &&
989+
git grep -l o master -- hello.c >actual &&
990+
test_cmp expect actual
991+
'
992+
993+
test_expect_success 'dashdash disambiguates pathspec as pathspec' '
994+
test_when_finished "git rm -f master" &&
995+
echo content >master &&
996+
git add master &&
997+
echo master:content >expect &&
998+
git grep o -- master >actual &&
999+
test_cmp expect actual
1000+
'
1001+
1002+
test_expect_success 'report bogus arg without dashdash' '
1003+
test_must_fail git grep o does-not-exist
1004+
'
1005+
1006+
test_expect_success 'report bogus rev with dashdash' '
1007+
test_must_fail git grep o hello.c --
1008+
'
1009+
1010+
test_expect_success 'allow non-existent path with dashdash' '
1011+
# We need a real match so grep exits with success.
1012+
tree=$(git ls-tree HEAD |
1013+
sed s/hello.c/not-in-working-tree/ |
1014+
git mktree) &&
1015+
git grep o "$tree" -- not-in-working-tree
1016+
'
1017+
1018+
test_expect_success 'grep --no-index pattern -- path' '
1019+
rm -fr non &&
1020+
mkdir -p non/git &&
1021+
(
1022+
GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
1023+
export GIT_CEILING_DIRECTORIES &&
1024+
cd non/git &&
1025+
echo hello >hello &&
1026+
echo goodbye >goodbye &&
1027+
echo hello:hello >expect &&
1028+
git grep --no-index o -- hello >actual &&
1029+
test_cmp expect actual
1030+
)
1031+
'
1032+
1033+
test_expect_success 'grep --no-index complains of revs' '
1034+
test_must_fail git grep --no-index o master -- 2>err &&
1035+
test_i18ngrep "cannot be used with revs" err
1036+
'
1037+
1038+
test_expect_success 'grep --no-index prefers paths to revs' '
1039+
test_when_finished "rm -f master" &&
1040+
echo content >master &&
1041+
echo master:content >expect &&
1042+
git grep --no-index o master >actual &&
1043+
test_cmp expect actual
1044+
'
1045+
1046+
test_expect_success 'grep --no-index does not "diagnose" revs' '
1047+
test_must_fail git grep --no-index o :1:hello.c 2>err &&
1048+
test_i18ngrep ! -i "did you mean" err
1049+
'
1050+
9851051
cat >expected <<EOF
9861052
hello.c:int main(int argc, const char **argv)
9871053
hello.c: printf("Hello world.\n");

0 commit comments

Comments
 (0)