我可以使用Git在存储库中搜索匹配的文件名吗?


76

只是说我在Git存储库中的多个子目录中有一个文件:“ HelloWorld.pm”。

我想发出一个命令来查找所有与“ HelloWorld.pm”匹配的文件的完整路径:

例如:

/path/to/repository/HelloWorld.pm
/path/to/repository/but/much/deeper/down/HelloWorld.pm
/path/to/repository/please/dont/make/me/search/through/the/lot/HelloWorld.pm

如何使用Git有效地找到与给定文件名匹配的所有完整路径?

我意识到我可以使用Linux / Unix find命令来做到这一点,但我希望避免扫描所有子目录以查找文件名实例。

Answers:


114

git ls-files将为您提供存储库当前状态(缓存或索引)中所有文件的列表。您可以传入一个模式以获得与该模式匹配的文件。

git ls-files HelloWorld.pm '**/HelloWorld.pm'

如果您想查找一组文件并通过其内容进行grep设置,可以使用git grep

git grep some-string -- HelloWorld.pm '**/HelloWorld.pm'

ls文件也可以采用某种模式。
Josh Lee

1
请记住使用“ ** / HelloWorld.pm”而不是“ * / HelloWorld.pm”在存储库的任何深度搜索匹配项。OP的示例包含不同级别的文件。
约翰·里克斯

8
'git ls-files'不在存储库中列出文件。它在索引(暂存区)或工作树中列出文件名。文件名位于存储库中的某个位置,但不在索引树或工作树中,这是完全正常的-例如,该文件名可能位于与您当前检出的分支不同的分支上。@GregHewgill的答案在这里应该被认为是更正确的。
stevegt 2014年

1
(缺少5分钟的评论编辑窗口...)Uwe Geuder和Dean Hall的答案实质上是在Greg上扩展的,方法是遍历所有分支和标签,处理在其他分支上命名的文件(或已删除)的情况。 。
stevegt 2014年

1
请注意,这不会在项目的根目录中找到HelloWorld.pm。在这种情况下,您需要使用git ls-files 'HelloWorld.pm' '*/HelloWorld.pm'
克里斯·梅斯

44

嗯,最初的问题是关于存储库的。一个存储库包含多个提交(至少在通常情况下),但是在搜索之前给出的答案只能通过一个提交进行。

因为找不到真正搜索整个提交历史的答案,所以我写了一个快速蛮力脚本git-find-by-name,几乎(考虑)了所有提交。

#! /bin/sh
tmpdir=$(mktemp -td git-find.XXXX)
trap "rm -r $tmpdir" EXIT INT TERM

allrevs=$(git rev-list --all)
# well, nearly all revs, we could still check the log if we have
# dangling commits and we could include the index to be perfect...

for rev in $allrevs
do
  git ls-tree --full-tree -r $rev >$tmpdir/$rev 
done

cd $tmpdir
grep $1 * 

也许有一种更优雅的方式。

请注意,将参数传递到grep的方法很简单,因此它将匹配文件名的一部分。如果不需要,请锚定您的搜索表达式和/或添加合适的grep选项。

对于较深的历史记录,输出可能太吵了,我想到了一个脚本,它将修订版本列表转换为范围,就像git rev-list可以执行的相反。但是到目前为止,它仍然是一个想法。


很棒的剧本。但是我无法使用它,因为我的git repo太大,以至于脚本淹没了我的硬盘驱动器:(
ArneBöckmann2013年

@ArneBöckmann只需将grep命令移至最后一个循环,并在每次grep之后删除所有内容。
Uwe Geuder 2013年

9
您的代码可以做成单行代码:git rev-list --all | xargs -I '{}' git ls-tree --full-tree -r '{}' | grep '.*HelloWorld\.pm$'。这也解决了硬盘驱满的问题。
subhacom

@subhacom您的oneliner应该是公认的答案
滚刀

24

尝试:

git ls-tree -r HEAD | grep HelloWorld.pm

1
或在Windows上:git ls-tree -r HEAD | findstr HelloWorld.pm
John Rix

man git ls-tree表示-r“递归到子树”。我不知道那是什么意思 您能解释一下这是什么意思吗?
加布里埃尔·斯台普斯

@JohnRix,最后我检查了一下,如果您使用的是Git for Windows提供的终端(我强烈建议在Windows上使用),它支持常见的Linux命令,例如管道连接到grep,运行bash脚本等,因此此答案应该可以正常工作照原样。试试看,让我知道。几年前,我完全放弃了Windows for Ubuntu。
加布里埃尔·斯台普斯

@GabrielStaples,对还是错,当涉及到Windows中的备用终端时(可能部分是由于多年前CygWin使它变灰了),我有点挑剔,并且倾向于坚持使用最低的公分母永远对我有用。(另一方面,即将在Windows 10上发布WSL 2,并且报告说它会非常有效地运行,所以也许我最终会告别旧的Windows命令提示符!)
John Rix

顺便说一句,-r应该使ls-tree命令搜索存储库中的子目录。
约翰·里克斯


4

[我承认,这有点滥用评论,但是我还不能发表评论,以为我会改善@ uwe-geuder的答案。]

#!/bin/bash
#
#

# I'm using a fixed string here, not a regular expression, but you can easily
# use a regular expression by altering the call to grep below.
name="$1"

# Verify usage.
if [[ -z "$name" ]]
then
    echo "Usage: $(basename "$0") <file name>" 1>&2
    exit 100
fi  

# Search all revisions; get unique results.
while IFS= read rev
do
    # Find $name in $rev's tree and only use its path.
    grep -F -- "$name" \
        <(git ls-tree --full-tree -r "$rev" | awk '{ print $4 }')
done < \
    <(git rev-list --all) \
    | sort -u

同样,对@ uwe-geuder +1可以得到一个很好的答案。

如果您对BASH本身感兴趣:

除非可以确保在for循环中分词(例如使用数组:),否则for item in "${array[@]}"我强烈建议while IFS= read var ; do ... ; done < <(command)在要循环的命令输出用换行符分隔时使用(或read -d''时当输出用。空字符串$'\0')。虽然git rev-list --all可以保证使用40字节的十六进制字符串(不带空格),但我从不喜欢冒险。我现在可以轻松地将命令从更改为git rev-list --all产生行的任何命令

我还建议使用内置的BASH机制来注入输入和过滤输出,而不是临时文件。


当您可以简单地通过管道git rev-list --all | while read rev; do; git ls-tree --full-tree -r $rev | cut -c54- | fgrep -- "$name"; done | sort -u
传输

脚本回显文件,但找不到该文件的修订版本。有助于回$rev显显示其修订内容
。– LB2

2

Uwe Geuder(@ uwe-geuder)编写的脚本很棒,但是实际上不需要将每个ls-tree输出转储到其自己的目录中,无需过滤。

更快,更少的存储空间:在输出上运行grep,然后将其存储,如本要点所示


要点可以更改,并且为了方便起见,最好还是在答案中包括代码段,尤其是在简短的时候。我建议您将要点的代码段复制到答案中。只需留下要点的链接就可以引用它作为源,以防万一您要更新要点而不是此答案。
加布里埃尔·斯台普斯

现在,我仔细查看了您的脚本,我发现这实际上非常有用。但是,您的答案需要1)标题:# How to find a long-lost file by searching all commits和2)要点中的代码直接粘贴到此答案中。
加布里埃尔·斯台普斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.