gitgit-grep

git grepping on filenames


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'


Solution

  • TL;DR

    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.)

    Moderately long

    It's not clear precisely what result you'd like, but on the assumption that what you want is:

    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:

    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.