Peeja
Peeja

Reputation: 14294

How can I search Git branches for a file or directory?

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.

Upvotes: 462

Views: 213817

Answers (7)

CervEd
CervEd

Reputation: 4292

If I'm looking for a file across references, I often don't remember if it's in the stash, a remote or local branch and I also don't remember which one is the latest.

git find-files


#!/bin/sh

usage="\
git find-files <grep-args>

Find using grep parameters <grep-args> across _all_ refs
(tags, branches, log, stash, remotes)
and sort it according to authordate of last commit containing that file.

--

h,help   Show the help "

eval "$(echo "$usage" | git rev-parse --parseopt -- "$@" || echo exit $?)"
shift

for b in $(git for-each-ref --format="%(refname)"); do
    files=$(git ls-tree -r --name-only $b | grep "${@:-.}" )
    if [ -n "$files" ]; then
        echo "$files" | while IFS= read -r file; do
            ad="$(git log -1 --format="%as" "$b" -- "$file")"
            printf '%s\t%s\t%s\n' "$b" "$ad" "$file"
        done
    fi
done | sort -k 2

bonus, you can use FZF and do stuff like

git find-files | fzf

Upvotes: 0

Greg Hewgill
Greg Hewgill

Reputation: 994619

Command Line

You could use gitk --all and search for commits "touching paths" and the pathname you are interested in.

UI

ui fields

(Credit to: @MikeW's suggestion.)

Upvotes: 16

reddot
reddot

Reputation: 995

This command finds commits which introduced specified paths:

git log --source --all --diff-filter=A --name-only -- '**/my_file.png'

Upvotes: 6

Dustin
Dustin

Reputation: 91050

git log + git branch will find it for you:

% git log --all -- somefile

commit 55d2069a092e07c56a6b4d321509ba7620664c63
Author: Dustin Sallings <[email protected]>
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).

Upvotes: 626

albfan
albfan

Reputation: 12970

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.

Upvotes: 27

lumbric
lumbric

Reputation: 9093

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.

Upvotes: 12

ididak
ididak

Reputation: 5878

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.

Upvotes: 83

Related Questions