In Git, how could I search for a file or directory by path across a number of branches?
I've written something in a branch, but I don't remember which one. Now I need to find it.
Clarification: I'm looking for a file which I created on one of my branches. I'd like to find it by path, and not by its contents, as I don't remember what the contents are.
git log
+ git branch
will find it for you:
% git log --all -- somefile
commit 55d2069a092e07c56a6b4d321509ba7620664c63
Author: Dustin Sallings <dustin@spy.net>
Date: Tue Dec 16 14:16:22 2008 -0800
added somefile
% git branch -a --contains 55d2069
otherbranch
Supports globbing, too:
% git log --all -- '**/my_file.png'
The single quotes are necessary (at least if using the Bash shell) so the shell passes the glob pattern to git unchanged, instead of expanding it (just like with Unix find
).
git ls-tree might help. To search across all existing branches:
for branch in `git for-each-ref --format="%(refname)" refs/heads`; do
echo $branch :; git ls-tree -r --name-only $branch | grep '<foo>'
done
The advantage of this is that you can also search with regular expressions for the file name.
git for-each-ref --format="%(refname)" refs/heads
; do" (c) "git ls-tree --name-only" will make the output tidier (d) it might be worth pointing out in your answer that there's an advantage of this over Dustin's solution, namely that you don't need to exactly know the filename - you can search by regular expression matching any part of the path
refs/remotes
instead of refs/heads
. To search everything (local branches, remote-tracking branches and tags), just use refs/
.
Although ididak's response is pretty cool, and Handyman5 provides a script to use it, I found it a little restricted to use that approach.
Sometimes you need to search for something that can appear/disappear over time, so why not search against all commits? Besides that, sometimes you need a verbose response, and other times only commit matches. Here are two versions of those options. Put these scripts on your path:
git-find-file
for branch in $(git rev-list --all)
do
if (git ls-tree -r --name-only $branch | grep --quiet "$1")
then
echo $branch
fi
done
git-find-file-verbose
for branch in $(git rev-list --all)
do
git ls-tree -r --name-only $branch | grep "$1" | sed 's/^/'$branch': /'
done
Now you can do
$ git find-file <regex>
sha1
sha2
$ git find-file-verbose <regex>
sha1: path/to/<regex>/searched
sha1: path/to/another/<regex>/in/same/sha
sha2: path/to/other/<regex>/in/other/sha
See that using getopt you can modify that script to alternate searching all commits, refs, refs/heads, been verbose, etc.
$ git find-file <regex>
$ git find-file --verbose <regex>
$ git find-file --verbose --decorated --color <regex>
Checkout https://github.com/albfan/git-find-file for a possible implementation.
You could use gitk --all
and search for commits "touching paths" and the pathname you are interested in.
Copy & paste this to use git find-file SEARCHPATTERN
Printing all searched branches:
git config --global alias.find-file '!for branch in `git for-each-ref --format="%(refname)" refs/heads`; do echo "${branch}:"; git ls-tree -r --name-only $branch | nl -bn -w3 | grep "$1"; done; :'
Print only branches with results:
git config --global alias.find-file '!for branch in $(git for-each-ref --format="%(refname)" refs/heads); do if git ls-tree -r --name-only $branch | grep "$1" > /dev/null; then echo "${branch}:"; git ls-tree -r --name-only $branch | nl -bn -w3 | grep "$1"; fi; done; :'
These commands will add some minimal shell scripts directly to your ~/.gitconfig
as global git alias.
This command finds commits which introduced specified paths:
git log --source --all --diff-filter=A --name-only -- '**/my_file.png'
A quite decent implementation of the find
command for Git repositories can be found here:
https://github.com/mirabilos/git-find
Success story sharing
somefile
: if you need regex search over the path/filename, for example, then you can use ididak's answer.git log --all -- **/my_file.png
git log --grep='branch' --author='me' -- *.sql
. Worked like a charm. git version 1.7.11.1gitk
supports the globbing as well. That is an excellent answer @webmat! If you want to see where it's been deleted / created /etc, you can usegit log --all --stat -- **/my_file.png
, that way you won't have to guess if you're checking it out from a commit that deleted it.