How to grep (search) committed code in the git history?
过去某个时候我删除了一个文件或文件中的一些代码。我可以在内容(而不是提交消息)中写得更好吗?
一个非常糟糕的解决方案是将日志变灰:
1 | git log -p | grep <pattern> |
但是,这不会立即返回提交哈希。我和
要搜索提交内容(即实际的源代码行,而不是提交消息等),您需要做的是:
1 | git grep <regexp> $(git rev-list --all) |
更新:如果遇到"参数列表太长"错误,
如果要将搜索限制在某些子树(例如"lib/util"),则需要将其传递给
1 | git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util |
这将使您的regexp的所有提交文本变灰。
在这两个命令中传递路径的原因是,
想象一下下面的场景:
以下是搜索源的其他有用方法:
在工作树中搜索与正则表达式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>) |
你应该使用
搜索
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所评论的,这个搜索是区分大小写的——他打开了一个关于如何搜索不区分大小写的后续问题。
我最喜欢的方法是使用
1 2 | -G<regex> Look for differences whose added or removed line matches the given <regex>. |
-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"出现在文件中的次数在提交之前和之后是相同的,所以使用
如果您想浏览代码更改(查看整个历史中给定单词实际更改的内容),请转到
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的实际提交并没有出现在命令的输出中,而是出现在它之前的一个提交中。
1 2 | git log -p --all -S 'search string' git log -p --all -G 'match regular expression' |
这些日志命令列表提交添加或删除给定的搜索字符串/regex,(通常)最新的。
找到一个添加了您要查找的文本(如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转到"搜索"视图(或单击底部的"搜索"选项卡)。从最右边,将"搜索类型"设置为"文件更改",然后键入要搜索的字符串。与上述命令相比,此方法有以下限制:
为了简单起见,我建议使用gui:gitk——git存储库浏览器,它非常灵活
您可以使用上/下箭头在结果中导航
@Jeet的答案在PowerShell中有效。
1 | git grep -n <regex> $(git rev-list --all) |
下面显示任何提交中包含
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清理的次数超过了它应该清理的次数,现在您的代码没有编译(缺少资源等)
解决方案:
它将找到更改了"文本到查找"的文件。
现在可以撤消此更改并编译代码。
在我的案例中,我需要搜索一个简短的提交,不幸的是,列出的解决方案不起作用。
我设法做到了:(替换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 |