如何在git历史中grep(搜索)已提交的代码?

How to grep (search) committed code in the git history?

过去某个时候我删除了一个文件或文件中的一些代码。我可以在内容(而不是提交消息)中写得更好吗?

一个非常糟糕的解决方案是将日志变灰:

1
git log -p | grep <pattern>

但是,这不会立即返回提交哈希。我和git grep一起玩,没有用。


要搜索提交内容(即实际的源代码行,而不是提交消息等),您需要做的是:

1
git grep <regexp> $(git rev-list --all)

更新:如果遇到"参数列表太长"错误,git rev-list --all | xargs git grep 将工作。

如果要将搜索限制在某些子树(例如"lib/util"),则需要将其传递给rev-list子命令和grep

1
git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util

这将使您的regexp的所有提交文本变灰。

在这两个命令中传递路径的原因是,rev-list将返回所有对lib/util所做更改的修订列表,但还需要传递到grep以便它只在lib/util上搜索。

想象一下下面的场景:grep可能会在rev-list返回的同一版本中包含的其他文件上找到相同的(即使该版本中的文件没有更改)。

以下是搜索源的其他有用方法:

在工作树中搜索与正则表达式regexp匹配的文本:

1
git grep <regexp>

在工作树中搜索与正则表达式regexp1或regexp2匹配的文本行:

1
git grep -e <regexp1> [--or] -e <regexp2>

在工作树中搜索与正则表达式regexp1和regexp2匹配的文本行,仅报告文件路径:

1
git grep -e <regexp1> --and -e <regexp2>

在工作树中搜索文本行与正则表达式regexp1匹配的文件和文本行与正则表达式regexp2匹配的文件:

1
git grep -l --all-match -e <regexp1> -e <regexp2>

在工作树中搜索文本匹配模式的更改行:

1
git diff --unified=0 | grep <pattern>

在所有修订中搜索与正则表达式regexp匹配的文本:

1
git grep <regexp> $(git rev-list --all)

在Rev1和Rev2之间的所有修订中搜索与正则表达式regexp匹配的文本:

1
git grep <regexp> $(git rev-list <rev1>..<rev2>)


你应该使用git log的鹤嘴锄(-S选项)。

搜索Foo

1
2
git log -SFoo -- path_containing_change
git log -SFoo --since=2009.1.1 --until=2010.1.1 -- path_containing_change

更多信息请参见Git历史记录-按关键字查找丢失的行。

作为Jakub Nar?B滑雪评论说:

  • 这将寻找引入或删除实例的差异。它通常意味着"在添加或删除带有‘foo’的行的地方进行修订"。

  • --pickaxe-regex选项允许您使用扩展的posix regex,而不是搜索字符串。

正如Rob所评论的,这个搜索是区分大小写的——他打开了一个关于如何搜索不区分大小写的后续问题。


我最喜欢的方法是使用git log-G选项(在版本1.7.4中添加)。

1
2
-G<regex>
       Look for differences whose added or removed line matches the given <regex>.

-G-S选项确定提交是否匹配的方式之间存在细微差异:

  • -S选项主要计算提交前后在文件中搜索匹配的次数。如果前后计数不同,提交将显示在日志中。例如,如果移动了与搜索匹配的行,则不会显示提交。
  • 使用-G选项,如果搜索匹配任何已添加、删除或更改的行,则提交将显示在日志中。

以这个提交为例:

1
2
3
4
5
6
7
diff --git a/test b/test
index dddc242..60a8ba6 100644
--- a/test
+++ b/test
@@ -1 +1 @@
-hello hello
+hello goodbye hello

因为"hello"出现在文件中的次数在提交之前和之后是相同的,所以使用-Shello时它将不匹配。但是,由于匹配hello的行发生了更改,因此将使用-Ghello显示提交。


如果您想浏览代码更改(查看整个历史中给定单词实际更改的内容),请转到patch模式-我发现了一个非常有用的组合:

1
2
3
4
git log -p
# hit '/' for search mode
# type in the word you are searching
# if the first search is not relevant hit 'n' for next (like in vim ;) )


我接受了@jeet的答案,并将其应用于windows(多亏了这个答案):

1
FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt

注意,对于我来说,出于某种原因,删除这个regex的实际提交并没有出现在命令的输出中,而是出现在它之前的一个提交中。


git log是一种更有效的跨所有分支搜索文本的方法,尤其是在有许多匹配项的情况下,并且您希望首先看到更新的(相关的)更改。

1
2
git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'

这些日志命令列表提交添加或删除给定的搜索字符串/regex,(通常)最新的。-p选项导致在添加或删除模式的位置显示相关的差异,因此您可以在上下文中看到它。

找到一个添加了您要查找的文本(如8beeff00d)的相关提交后,找到包含该提交的分支:

1
git branch -a --contains 8beeff00d


在任何修订中搜索任何文件:

1
git rev-list --all | xargs git grep <regexp>

仅搜索某些给定文件,例如XML文件:

1
git rev-list --all | xargs -I{} git grep <regexp> {} --"*.xml"

结果行应该如下所示:6988BEC26B1503D45EB0B2E8A4364AFB87DDE7AF:bla.xml:找到的行的文本…

然后可以使用git show获取更多信息,如作者、日期、差异:

1
git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

对于在sourcetree中尝试执行此操作的其他任何人,在其UI中都没有直接命令(从1.6.21.0版开始)。但是,您可以通过打开终端窗口(主工具栏中的可用按钮)并在其中复制/粘贴来使用接受答案中指定的命令。

注意:SourceTree的搜索视图可以部分为您执行文本搜索。按ctrl+3转到"搜索"视图(或单击底部的"搜索"选项卡)。从最右边,将"搜索类型"设置为"文件更改",然后键入要搜索的字符串。与上述命令相比,此方法有以下限制:

  • sourcetree只显示其中一个已更改文件中包含搜索词的提交。查找包含搜索文本的确切文件也是一项手动任务。
  • 不支持regex。

  • 为了简单起见,我建议使用gui:gitk——git存储库浏览器,它非常灵活

  • 搜索代码:enter image description here
  • 搜索文件:enter image description here
  • 因为它还支持regex:enter image description here
  • 您可以使用上/下箭头在结果中导航


    @Jeet的答案在PowerShell中有效。

    1
    git grep -n <regex> $(git rev-list --all)

    下面显示任何提交中包含password的所有文件。

    1
    2
    3
    4
    5
    # store intermediate result
    $result = git grep -n"password" $(git rev-list --all)

    # display unique file names
    $result | select -unique { $_ -replace"(^.*?:)|(:.*)","" }

    那么,您是否在尝试通过旧版本的代码进行grep,以查看最后存在的东西在哪里?

    如果我这样做,我可能会使用git bisct。使用二分法,您可以指定一个已知的好版本、一个已知的坏版本和一个简单的脚本,该脚本检查版本是好还是坏(在本例中是grep,查看您要查找的代码是否存在)。运行此命令将在删除代码时找到。


    1
    git rev-list --all | xargs -n 5 git grep EXPRESSION

    是对@jeet解决方案的一个调整,因此它在搜索时显示结果,而不仅仅是在搜索结束时(在大型回购中可能需要很长时间)。


    场景:您使用您的IDE对代码进行了大量清理。问题:IDE清理的次数超过了它应该清理的次数,现在您的代码没有编译(缺少资源等)

    解决方案:

    git grep --cached"text_to_find"

    它将找到更改了"文本到查找"的文件。

    现在可以撤消此更改并编译代码。


    在我的案例中,我需要搜索一个简短的提交,不幸的是,列出的解决方案不起作用。

    我设法做到了:(替换regex令牌)

    1
    2
    3
    4
    5
    6
    for commit in $(git rev-list --all --abbrev-commit)
    do
        if [[ $commit =~ __REGEX__ ]]; then
            git --no-pager show -s --format='%h %an - %s' $commit
        fi
    done