How can I pipe to git grep
a list of files on which I want to search a specific pattern?
Specifically I do this:
git grep -l 'string to search'
and I want to do another git grep
on the result files of the previous search.
Trying
git grep -l -e 'string to search' --and -e 'another string'
does not give any results but I know there are matches
Doing
git grep -l 'string to search' | git grep -l 'another string'
gives the same result as git grep -l 'another string'
You can't pipe the names, but you can supply the names (as pathspecs) to one of the two git grep
commands.
(Oddly, the --and
should have done what you wanted in the first place—I'm just answering the question of how to do it without --and
.)
It's not clear precisely what result you'd like, but on the assumption that what you want is:
for each file in (some commit | some set-of-commits | the index | the work-tree)
if the file contains pattern 1
and the file contains, anywhere else in the same file (not necessarily on the same line) pattern 2
then list the file's name
there are two somewhat obvious ways to attempt to make that happen, but one of them doesn't work because Git won't match across newlines.1 So we're left with the other obvious method:
git grep -l
to produce a list of files matching the first expression.git grep -l
to produce a list of files matching the second expression, optionally, starting only with those files produced by the first git grep
.To combine these as an and operation, you have the fairly obvious option of using one of the two git grep -l
outputs as a series of pathname arguments to the other git grep
. Note, however, that git grep
takes pathspec arguments, rather than path name arguments, after the --
that delimits them (see the documentation for details). This means that if any of your file names have pathspec metacharacters in them, this simple shell expansion may fail. It's also probably a good idea to verify that the first grep
actually lists some files, but combining them into one bash style command:
git grep -l pattern2 -- $(git grep -l pattern1)
may often suffice.
To combine the above two as an or operation (list files that contain either pattern), it's easiest to just use one git grep
with more than one pattern. Note, however, that you can run multiple git grep -l
operations with the resulting file names directed to a temporary file, then use a program like comm
to report what's common and unique to each set of results. (This assumes you have comm
in your command-line-interpreter toolbox.) Using comm
also gets around the pathspec issues with peculiar file names like (top)file.c
or got*you.sh
or whatever.
1If Git did match across newlines, you could use the extended or Perl pattern: (foo(.|\n)*bar)|(bar(.|\n)*foo)
to allow arbitrary text between the two other patterns foo
and bar
, with either pattern in either order.