37

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?

6 Answers 6

50

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

Sign up to request clarification or add additional context in comments.

8 Comments

This is the correct answer. I would do git ls-files | grep 'name you're looking for' to filter on the big list of files git ls-files will return. (I also alias ls-files to ls because I use it frequently)
We can do it with the command git ls-files "*.sh" or git ls-files | grep .sh
I've long had the alias: find = ! git ls-files | grep, but sometimes I would really like to limit the search to a particular path, e.g., git find foo bar/baz. Any ideas how to implement that?
To answer my own Q, here's what I've come up with find = ! sh -c 'git ls-files $2 | grep $1' -
@JFlo I implemented your request in this commit in git find, hope that helps!
|
27

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

So:

git ls-files "*middleware*" 

3 Comments

+1 for that. I always did 'git ls-tree -r HEAD|grep "toSearchFor"'
That's really cool too but with git ls-files | grep ... I get the power of grep right at my finger tips and since I have colour highlighting on by default on grep it displays nicer.
@PeterBengtsson the git find I wrote (and which someone downvoted for unknown reasons today) is basically git ls-files | grep but with find-like syntax (and it can even delegate to find, still avoiding non-git files)
6

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.

2 Comments

This is great! Thanks for introducing me to ack! Just want to note: "The -G option has been removed. Two regular expressions on the command line was considered too confusing; to simulate -G's functionality, you may use the new -x option to pipe filenames from one invocation of ack into another." from ack(1). If I figure out the fix, I'll edit this answer.
Note that for f in $(find ...) is an antipattern. Much better practice to find . -name .git -prune -o -name 'middleware*' -exec grep 'pattern in file' {} +, which also gives you roughly the same advantages xargs does in only starting the fewest possible instances of grep, and replicates one of the advantages of git grep of not spending time searching through the .git directory.
2

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' 

2 Comments

this does not answer the question. OP is asking for a git-limited flavor of find in the same sense that git grep is a git-limited flavor of grep. i.e., OP wants to search git-tracked files by name, not by contents.
'*.[ch]' is a find like filter on the file name. man gitglossary and search on pathspec. It may not be equivalent to find. But in such cases, a non-limited human can type find.
1

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)

Comments

0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.