Peter Bengtsson
Peter Bengtsson

Reputation: 7515

How can I 'grep' files in a Git repository?

I love git grep to search in all files checked in to a repository. It's great. But is it possible to use it (or some other Git command) to just use to find files (independent of content)?

At the moment I do this:

find . | grep middleware

which works, but it's not using the Git index which means it's going through every found file and it reports on files that are matching the .gitignore.

Are there some clever tricks?

Upvotes: 37

Views: 50695

Answers (6)

mirabilos
mirabilos

Reputation: 5346

I have the same problem regularily, and I just went and hacked git find – if you don’t use the Debian package you can just copy the git-find script to /usr/lib/git-core/ (or comparable) and enjoy it.

It can be used in several modes, the easiest of which is indeed:

git find \*middleware\*        # or
git find '*middleware*'        # which is short for
git find -name '*middleware*'

Combining is also possible (and almost as flexible as regular find, you just have to write the -a explicitly):

git find \( -name \*.java -o -name \*.js \) -a ! -ipath \*/test/\*

It has a couple more options, most of which handle with filtering the name or full (partial, i.e. below the current working directory) path, some of them case-insensitively (-iname and friends), and two global options, one to toggle regexp between POSIX Basic (default) and POSIX Extended, the other toggles symlinks (default on); this finds only files (and symlinks), not directories or submodules (“gitlinks”) by design.

It can also pass the file list to regular find(1) if it’s not too long (it must be passed on the command line), which allows for things like…

git find -- -mtime -100

… at slight filesystem cost (find does access the filesystem), but on the other hand, almost all (not search depth-specific stuff) of find works, and you only get to operate on files “in the index”, i.e. known to git (present in the HEAD commit or git added).

It’s a bit picky about unresolved conflicts though. If you notice any problems with it, just drop me a note (here, or via IRC).

PS: Feel free to lobby the official git people to subtree-merge the git-find repository, I’d be more than happy to have it integrated into git proper (the licence is even more liberal, you just need the mksh shell in a somewhat recent (50 should be sufficient) version, but it’s the most widespread Unix shell nowadays so that’s okay).

Upvotes: 0

Joakim
Joakim

Reputation: 11998

Pure Git solution

git grep has built-in support to limit the grep to a glob of files. The other answers all use external tools to do the actual grepping, which misses the point.

Example from the git grep man page.

git grep 'time_t' -- '*.[ch]'

Looks for time_t in all tracked .c and .h files in the working directory and its subdirectories.

From the option descriptions.

-- Signals the end of options; the rest of the parameters are limiters.

<pathspec>…​ If given, limit the search to paths matching at least one pattern. Both leading paths match and glob(7) patterns are supported.

So to translate your example (which did not include something to limit the search however so I added it here):

find . -name '*.txt' | grep middleware

You would do:

git grep middleware -- '*.txt'

Upvotes: 2

Mike Seplowitz
Mike Seplowitz

Reputation: 10385

You might consider a non-Git solution in this case.

find itself has the ability to do what you want in a more efficient manner than piping its results into grep:

find . -name 'middleware*'

You will need to quote the pattern so that the * isn't expanded by the shell before being passed to find.

There is a powerful program called ack that is, well, better than grep, and one of my favorite uses for ack is exactly what you've mentioned—finding files that match a pattern within a tree. ack uses Perl regular expressions (PCRE), not shell fileglobs, though.

ack -g middleware

If you want to search within those files, ack lets you do that more easily than writing a shell loop over the results of find that greps within each file. Compare the two and see which one you prefer:

for f in $(find . -name 'middleware*')
do
    grep 'pattern in file' $f
done

versus

ack -G 'middleware' 'pattern in file'

I highly recommend ack as something to add to your toolkit.

Upvotes: 6

Steven
Steven

Reputation: 2275

I think git ls-files will do the trick for you.

So:

git ls-files "*middleware*"

Upvotes: 27

user214810
user214810

Reputation: 11

Git now has a matured search functionality (as the previous poster mentioned). You can search file names, extensions, by programming language, etc. You can search inside at file contents, etc.

You search when you log into GitHub, at the search field in the upper left of the screen.

See this for details: Searching code (legacy)

Upvotes: 1

araqnid
araqnid

Reputation: 133792

You may want git ls-files which lists the files in the index (and automatically adjusts for your current directory inside the Git work directory).

Upvotes: 50

Related Questions