Skip to content

Commit 6f82be0

Browse files
committed
Merge branch 'jn/grep-open'
* jn/grep-open: t/t7811-grep-open.sh: remove broken/redundant creation of fake "less" script t/t7811-grep-open.sh: ensure fake "less" is made executable t/lib-pager.sh: remove unnecessary '^' from 'expr' regular expression grep -O: allow optional argument specifying the pager (or editor) grep: Add the option '--open-files-in-pager' Unify code paths of threaded greps grep: refactor grep_objects loop into its own function Conflicts: t/t7006-pager.sh
2 parents a53deac + 0c72cea commit 6f82be0

File tree

7 files changed

+276
-39
lines changed

7 files changed

+276
-39
lines changed

Documentation/git-grep.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SYNOPSIS
1414
[-E | --extended-regexp] [-G | --basic-regexp]
1515
[-F | --fixed-strings] [-n]
1616
[-l | --files-with-matches] [-L | --files-without-match]
17+
[(-O | --open-files-in-pager) [<pager>]]
1718
[-z | --null]
1819
[-c | --count] [--all-match] [-q | --quiet]
1920
[--max-depth <depth>]
@@ -104,6 +105,13 @@ OPTIONS
104105
For better compatibility with 'git diff', `--name-only` is a
105106
synonym for `--files-with-matches`.
106107

108+
-O [<pager>]::
109+
--open-files-in-pager [<pager>]::
110+
Open the matching files in the pager (not the output of 'grep').
111+
If the pager happens to be "less" or "vi", and the user
112+
specified only one pattern, the first file is positioned at
113+
the first match automatically.
114+
107115
-z::
108116
--null::
109117
Output \0 instead of the character that normally follows a

builtin/grep.c

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "tree-walk.h"
1212
#include "builtin.h"
1313
#include "parse-options.h"
14+
#include "string-list.h"
15+
#include "run-command.h"
1416
#include "userdiff.h"
1517
#include "grep.h"
1618
#include "quote.h"
@@ -556,6 +558,33 @@ static int grep_file(struct grep_opt *opt, const char *filename)
556558
}
557559
}
558560

561+
static void append_path(struct grep_opt *opt, const void *data, size_t len)
562+
{
563+
struct string_list *path_list = opt->output_priv;
564+
565+
if (len == 1 && *(const char *)data == '\0')
566+
return;
567+
string_list_append(path_list, xstrndup(data, len));
568+
}
569+
570+
static void run_pager(struct grep_opt *opt, const char *prefix)
571+
{
572+
struct string_list *path_list = opt->output_priv;
573+
const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
574+
int i, status;
575+
576+
for (i = 0; i < path_list->nr; i++)
577+
argv[i] = path_list->items[i].string;
578+
argv[path_list->nr] = NULL;
579+
580+
if (prefix && chdir(prefix))
581+
die("Failed to chdir: %s", prefix);
582+
status = run_command_v_opt(argv, RUN_USING_SHELL);
583+
if (status)
584+
exit(status);
585+
free(argv);
586+
}
587+
559588
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
560589
{
561590
int hit = 0;
@@ -590,7 +619,6 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
590619
if (hit && opt->status_only)
591620
break;
592621
}
593-
free_grep_patterns(opt);
594622
return hit;
595623
}
596624

@@ -675,6 +703,25 @@ static int grep_object(struct grep_opt *opt, const char **paths,
675703
die("unable to grep from object of type %s", typename(obj->type));
676704
}
677705

706+
static int grep_objects(struct grep_opt *opt, const char **paths,
707+
const struct object_array *list)
708+
{
709+
unsigned int i;
710+
int hit = 0;
711+
const unsigned int nr = list->nr;
712+
713+
for (i = 0; i < nr; i++) {
714+
struct object *real_obj;
715+
real_obj = deref_tag(list->objects[i].item, NULL, 0);
716+
if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
717+
hit = 1;
718+
if (opt->status_only)
719+
break;
720+
}
721+
}
722+
return hit;
723+
}
724+
678725
static int grep_directory(struct grep_opt *opt, const char **paths)
679726
{
680727
struct dir_struct dir;
@@ -689,7 +736,6 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
689736
if (hit && opt->status_only)
690737
break;
691738
}
692-
free_grep_patterns(opt);
693739
return hit;
694740
}
695741

@@ -786,9 +832,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
786832
int cached = 0;
787833
int seen_dashdash = 0;
788834
int external_grep_allowed__ignored;
835+
const char *show_in_pager = NULL, *default_pager = "dummy";
789836
struct grep_opt opt;
790837
struct object_array list = { 0, 0, NULL };
791838
const char **paths = NULL;
839+
struct string_list path_list = { NULL, 0, 0, 0 };
792840
int i;
793841
int dummy;
794842
int nongit = 0, use_index = 1;
@@ -872,6 +920,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
872920
OPT_BOOLEAN(0, "all-match", &opt.all_match,
873921
"show only matches from files that match all patterns"),
874922
OPT_GROUP(""),
923+
{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
924+
"pager", "show matching files in the pager",
925+
PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
875926
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
876927
"allow calling of grep(1) (ignored by this build)"),
877928
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
@@ -947,6 +998,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
947998
argc--;
948999
}
9491000

1001+
if (show_in_pager == default_pager)
1002+
show_in_pager = git_pager(1);
1003+
if (show_in_pager) {
1004+
opt.name_only = 1;
1005+
opt.null_following_name = 1;
1006+
opt.output_priv = &path_list;
1007+
opt.output = append_path;
1008+
string_list_append(&path_list, show_in_pager);
1009+
use_threads = 0;
1010+
}
1011+
9501012
if (!opt.pattern_list)
9511013
die("no pattern given.");
9521014
if (!opt.fixed && opt.ignore_case)
@@ -1003,44 +1065,51 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
10031065
paths[1] = NULL;
10041066
}
10051067

1068+
if (show_in_pager && (cached || list.nr))
1069+
die("--open-files-in-pager only works on the worktree");
1070+
1071+
if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
1072+
const char *pager = path_list.items[0].string;
1073+
int len = strlen(pager);
1074+
1075+
if (len > 4 && is_dir_sep(pager[len - 5]))
1076+
pager += len - 4;
1077+
1078+
if (!strcmp("less", pager) || !strcmp("vi", pager)) {
1079+
struct strbuf buf = STRBUF_INIT;
1080+
strbuf_addf(&buf, "+/%s%s",
1081+
strcmp("less", pager) ? "" : "*",
1082+
opt.pattern_list->pattern);
1083+
string_list_append(&path_list, buf.buf);
1084+
strbuf_detach(&buf, NULL);
1085+
}
1086+
}
1087+
1088+
if (!show_in_pager)
1089+
setup_pager();
1090+
1091+
10061092
if (!use_index) {
1007-
int hit;
10081093
if (cached)
10091094
die("--cached cannot be used with --no-index.");
10101095
if (list.nr)
10111096
die("--no-index cannot be used with revs.");
10121097
hit = grep_directory(&opt, paths);
1013-
if (use_threads)
1014-
hit |= wait_all();
1015-
return !hit;
1016-
}
1017-
1018-
if (!list.nr) {
1019-
int hit;
1098+
} else if (!list.nr) {
10201099
if (!cached)
10211100
setup_work_tree();
10221101

10231102
hit = grep_cache(&opt, paths, cached);
1024-
if (use_threads)
1025-
hit |= wait_all();
1026-
return !hit;
1027-
}
1028-
1029-
if (cached)
1030-
die("both --cached and trees are given.");
1031-
1032-
for (i = 0; i < list.nr; i++) {
1033-
struct object *real_obj;
1034-
real_obj = deref_tag(list.objects[i].item, NULL, 0);
1035-
if (grep_object(&opt, paths, real_obj, list.objects[i].name)) {
1036-
hit = 1;
1037-
if (opt.status_only)
1038-
break;
1039-
}
1103+
} else {
1104+
if (cached)
1105+
die("both --cached and trees are given.");
1106+
hit = grep_objects(&opt, paths, &list);
10401107
}
10411108

10421109
if (use_threads)
10431110
hit |= wait_all();
1111+
if (hit && show_in_pager)
1112+
run_pager(&opt, prefix);
10441113
free_grep_patterns(&opt);
10451114
return !hit;
10461115
}

git.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ static void handle_internal_command(int argc, const char **argv)
329329
{ "fsck-objects", cmd_fsck, RUN_SETUP },
330330
{ "gc", cmd_gc, RUN_SETUP },
331331
{ "get-tar-commit-id", cmd_get_tar_commit_id },
332-
{ "grep", cmd_grep, USE_PAGER },
332+
{ "grep", cmd_grep },
333333
{ "hash-object", cmd_hash_object },
334334
{ "help", cmd_help },
335335
{ "index-pack", cmd_index_pack },

t/lib-pager.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
3+
test_expect_success 'determine default pager' '
4+
test_might_fail git config --unset core.pager &&
5+
less=$(
6+
unset PAGER GIT_PAGER;
7+
git var GIT_PAGER
8+
) &&
9+
test -n "$less"
10+
'
11+
12+
if expr "$less" : '[a-z][a-z]*$' >/dev/null
13+
then
14+
test_set_prereq SIMPLEPAGER
15+
fi

t/t7006-pager.sh

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
test_description='Test automatic use of a pager.'
44

55
. ./test-lib.sh
6+
. "$TEST_DIRECTORY"/lib-pager.sh
67

78
cleanup_fail() {
89
echo >&2 cleanup failed
@@ -158,21 +159,12 @@ test_expect_success 'color when writing to a file intended for a pager' '
158159
colorful colorful.log
159160
'
160161

161-
test_expect_success 'determine default pager' '
162-
unset PAGER GIT_PAGER;
163-
test_might_fail git config --unset core.pager ||
164-
cleanup_fail &&
165-
166-
less=$(git var GIT_PAGER) &&
167-
test -n "$less"
168-
'
169-
170-
if expr "$less" : '[a-z][a-z]*$' >/dev/null && test_have_prereq TTY
162+
if test_have_prereq SIMPLEPAGER && test_have_prereq TTY
171163
then
172-
test_set_prereq SIMPLEPAGER
164+
test_set_prereq SIMPLEPAGERTTY
173165
fi
174166

175-
test_expect_success SIMPLEPAGER 'default pager is used by default' '
167+
test_expect_success SIMPLEPAGERTTY 'default pager is used by default' '
176168
unset PAGER GIT_PAGER;
177169
test_might_fail git config --unset core.pager &&
178170
rm -f default_pager_used ||
File renamed without changes.

0 commit comments

Comments
 (0)