如何在Git历史记录中grep(搜索)已提交的代码


1432

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

一个非常糟糕的解决方案是grep日志:

git log -p | grep <pattern>

但是,这不会立即返回提交哈希。我git grep无济于事。


2
Junio C Hamano(git维护者)的这些博客文章可能对您来说很有趣:* Linus的最终内容跟踪工具(关于镐头搜索git log -S和指责)* [有趣的是“ git log --grep”] [2](搜索提交消息) )* [带“ git grep”的乐趣] [3] [2]:gitster.livejournal.com/30195.html [3]:gitster.livejournal.com/27674.html
JakubNarębski2010年


可能重复的答案实际上有效:stackoverflow.com/a/1340245/492
CAD bloke

问题是它没有为更改提供任何上下文信息。即谁/何时
Sonic Soul

Answers:


1888

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

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

git rev-list --all | xargs git grep <expression> 如果遇到“参数列表过长”错误,此命令将起作用。

如果要将搜索限制为某个子树(例如,“ lib / util”),则需要将该搜索传递给rev-list子命令,grep以及:

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

这将遍历您对的所有提交文本regexp

在两个命令中都传递路径的原因是因为rev-list它将返回所有lib/util发生更改的修订列表,但是您也需要传递给,grep以便仅在中搜索lib/util

试想一下以下情况:grep可能<regexp>在返回的同一修订版中包含的其他文件上找到相同的文件rev-list(即使该修订版上的文件没有更改)。

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

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

git grep <regexp>

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

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

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

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

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

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

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

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

搜索所有修订以查找与正则表达式regexp匹配的文本:

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

搜索rev1和rev2之间的所有修订以查找与正则表达式regexp匹配的文本:

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

61
谢谢,效果很好!遗憾的是,虽然需要“ $(git rev-list --all)”,并且没有方便的开关来指定分支的整个历史记录。
Ortwin Gentz 2010年

3
优秀的。+1。GitBook添加了一些详细信息(book.git-scm.com/4_finding_with_git_grep.html),Junio C Hamano阐述了您的一些观点:gitster.livejournal.com/27674.html
VonC

18
不幸的是,我无法使用msysgit-1.7.4进行此操作。它告诉我sh.exe": /bin/git: Bad file number。VonC的答案也适用于msysgit。
eckes

4
如果在使用rev-list调用git grep历史记录时收到“无法读取树”错误,则可能需要清理。尝试git gc或退房:stackoverflow.com/questions/1507463/...
安东尼Panozzo

8
是的,这在Windows上似乎也失败了。
mlissner

551

您应该使用的pickaxe(-S选项git log

搜索Foo

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

查看Git历史记录-通过关键字查找丢失的行以获取更多信息。


正如JakubNarębski所说:

  • 这会寻找引入或删除实例的差异<string>。它通常指的是“使用'Foo'添加或删除行的修订”。

  • --pickaxe-regex选项允许您使用扩展的POSIX正则表达式而不是搜索字符串。示例(来自git log):git log -S"frotz\(nitfol" --pickaxe-regex


正如Rob所说,此搜索区分大小写-他就如何搜索不区分大小写提出了一个后续问题


3
谢谢,我不知道这个选项。如果您对提交消息感兴趣,这似乎是最好的解决方案,如果您需要传统的纯行匹配UNIX grep行为,那么Jeet的解决方案最合适。
Ortwin Gentz 2010年

@Ortwin:同意(我已经投票赞成选择的解决方案)。在git log你的问题有点把我迷惑;)
VonC

12
将其与-p标志组合以也输出差异。
桑德2014年

有什么办法可以使用git log -S排除所有匹配特定模式的目录?
BakaKuna 2014年

3
@Anentropic您将需要--branches --all选项来搜索所有回购。
VonC 2015年

249

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

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

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

  • -S选项从本质上计算提交前后搜索与文件匹配的次数。如果前后计数不同,则提交将显示在日志中。例如,这不会显示提交,将匹配搜索的行移到哪里。
  • 使用该-G选项,如果您的搜索与添加,删除或更改的任何行匹配,则提交将显示在日志中。

以该提交为例:

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


2
有没有办法在git日志输出中显示匹配的更改上下文?
Thilo-Alexander Ginkel 2014年

13
@ Thilo-AlexanderGinkel-我通常只添加-p用于显示每次提交差异的选项。然后,当在我的寻呼机中打开日志时,我将搜索所需的内容。如果您的寻呼机是lessgit log -Ghello -p,你可以输入/hello,按Enter,然后使用nN找到“你好”下一首/上一出现。
Tyler Holien 2014年

我发现了一个-G与Regex 有关的有趣问题:如果命令行使用UTF-8,而您正在查看的文件使用某些ISO-Latin(8位)编码,则会.*失败。例如,我有一个更改Vierter Entwurf-> Fünfter Entwurf,而'V.*ter Entwurf'生成匹配项'F.*ter Entwurf'却没有。
U. Windl

51

如果您想浏览代码更改(查看整个历史记录中给定单词实际更改的内容),请选择patch模式-我发现这样做非常有用:

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 ;) )

11
上面提到的解决方案对git log -S都不起作用。这一个!
rodvlopes 2014年

29

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

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

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

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

git branch -a --contains 8beeff00d

嗨,这些行似乎根本不起作用。我的命令是> git log -p --all -S'public string DOB {get; 组; } = string.Empty;' 每次我尝试运行它时,我都会得到>致命:模棱两可的参数'string':未知版本或不在工作树中的路径。>使用'-'将路径与修订版本分开,如下所示:>'git <命令> [<版本> ...]-[<文件> ...]'
user216652

@ user216652由于某种原因,'引号不会将您的搜索字符串作为单个参数分组在一起。相反,'public是的参数-S,并将其余部分视为单独的参数。我不确定您在哪个环境中运行,但是该上下文对于帮助进行故障排除是必要的。我建议您在需要时打开一个单独的StackOverflow问题,以帮助您解决git命令如何发送到shell的所有上下文。在我看来,它是通过其他命令发送的?这里的评论不是正确的解决方法。
爱德华·安德森

26

我接受了Jeet的答案并将其调整为Windows(由于此答案):

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

请注意,对我而言,由于某种原因,删除此正则表达式的实际提交未出现在命令的输出中,而是出现在命令之前。


2
+ --no-pager
1-

2
另外,我会注意到,附加到文本文件具有实际显示匹配文本的附加优势。(附加到一个文本文件,>>results.txt用于那些不熟悉Windows管道的人...
cgp 2012年

1
而且我认为bash的语法很丑陋:)
smido

23

搜索任何版本,任何文件

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

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

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

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

然后,您可以使用来获取更多信息,例如作者,日期和差异git show

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

11

为简单起见,我建议使用GUI:gitk-Git存储库浏览器。非常灵活

  1. 要搜索代码:

    在此处输入图片说明
  2. 搜索文件:

    在此处输入图片说明
  3. 当然,它也支持正则表达式:

    在此处输入图片说明

您可以使用向上/向下箭头浏览结果。


6

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

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

  1. Sourcetree仅显示已更改文件之一中包含搜索词的提交。查找包含搜索文本的确切文件还是手动任务。
  2. 不支持RegEx。

4

每当我发现自己在您的位置时,都会使用以下命令行:

git log -S "<words/phrases i am trying to find>" --all --oneline  --graph

说明:

  1. git log-需要在这里写更多吗?它按时间顺序显示日志。
  2. -S "<words/phrases i am trying to find>" -它显示所有Git提交,其中任何文件(添加/修改/删除)都带有我要查找的单词/短语,且没有'<>'符号。
  3. --all -在所有分支机构之间执行和搜索。
  4. --oneline -将Git日志压缩为一行。
  5. --graph -创建按时间顺序排列的提交图。

1
“只要我发现自己在您的位置,我就会感到需要使用git!”
塞比

1
这是一个很好的答案!
Alf Eaton

@AlfEaton我的荣幸!
surajs1n

2

Jeet的答案可在PowerShell中使用。

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

以下显示所有提交中包含的所有文件password

# Store intermediate result
$result = git grep -n "password" $(git rev-list --all)

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

1

那么,您是否要遍历旧版本的代码,以查找最后存在的内容?

如果执行此操作,则可能会使用git bisect。使用bisect,您可以指定一个已知的好版本,一个已知的坏版本以及一个简单的脚本,该脚本进行检查以查看该版本的好坏(在这种情况下,将使用grep来查看您要查找的代码是否存在) )。运行此命令将找到删除代码的时间。


2
是的,但是您的“测试”可以是一个脚本,可以仔细查看代码,如果代码存在,则返回“ true”,否则,则返回“ false”。
罗布·迪·马可

2
好吧,如果代码在第10版中是不好的,在第11版中是好的,而在第15版中又变得不好...
Paolo

2
我同意保罗。二进制搜索仅适用于“有序”值。对于git bisect,这意味着从参考点开始,所有“好”修订都在所有“坏”修订之前,但是在寻找过渡代码时无法做此假设。在某些情况下,此解决方案可能会起作用,但它不是一个好的通用解决方案。
肯特

我认为这是非常低效的,因为整棵树被多次检查为二等分。
U. Windl

0

场景:您使用IDE清理了代码。问题:IDE清理了过多的垃圾,现在您的代码无法编译(缺少资源等)。

解:

git grep --cached "text_to_find"

它将找到更改了“ text_to_find”的文件。

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


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

Jeet解决方案的一项调整,因此它在搜索时显示结果,而不仅仅是在结束时显示(在大型存储库中可能要花费很长时间)。


-1

就我而言,我需要搜索一个简短的提交,但不幸的是,列出的解决方案无法正常工作。

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

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
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.