我在过去的某个时候删除了一个文件或文件中的一些代码。我可以在内容中(而不是在提交消息中)grep 吗?
一个非常糟糕的解决方案是 grep 日志:
git log -p | grep <pattern>
但是,这不会立即返回提交哈希。我玩了 git grep
无济于事。
git log -S
和责备)* [Fun with "git log --grep"][2](搜索提交消息)* [有趣的“git grep”][3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
要搜索提交内容(即实际的源代码行,而不是提交消息等),您需要执行以下操作:
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
可能会在 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>)
您应该使用 git log
的 pickaxe (-S
) 选项。
要搜索 Foo
:
git log -SFoo -- path_containing_change
git log -SFoo --since=2009.1.1 --until=2010.1.1 -- path_containing_change
有关更多信息,请参见Git history - find lost line by keyword。
正如 Jakub Narębski 评论的那样:
这会查找引入或删除
--pickaxe-regex 选项允许您使用扩展的 POSIX 正则表达式,而不是搜索字符串。示例(来自 git log): git log -S"frotz\(nitfol" --pickaxe-regex
正如 Rob 所评论的,此搜索区分大小写 - 他打开了 follow-up question 关于如何搜索不区分大小写的内容。
-p
标志结合起来也可以输出差异。
--branches --all
选项来搜索所有 repo。
我最喜欢的方法是使用 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
显示提交。
-p
选项来显示每个提交的差异。然后,当在我的寻呼机中打开日志时,我会搜索我正在寻找的任何内容。如果您的寻呼机是 less
而您是 git log -Ghello -p
,您可以输入 /hello
,按 Enter
,然后使用 n
和 N
查找下一个/上一个“hello”。
-G
和 Regex 的一个有趣问题:如果命令行使用 UTF-8 并且您正在查看的文件使用了一些 ISO-Latin(8 位)编码,则 .*
失败。例如,我有一个更改 Vierter Entwurf
-> Fünfter Entwurf
,而 'V.*ter Entwurf'
会产生匹配,而 'F.*ter Entwurf'
不会。
git log
可能是在所有分支中搜索文本的更有效方式,尤其是在有很多匹配项并且您希望首先查看最近(相关)更改的情况下。
git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'
这些日志命令列出添加或删除给定搜索字符串/正则表达式的提交,(通常)首先是最近的。 -p
选项会导致在添加或删除模式的位置显示相关差异,以便您可以在上下文中看到它。
找到添加了您要查找的文本的相关提交(例如,8beeff00d)后,找到包含该提交的分支:
git branch -a --contains 8beeff00d
'
引号没有将您的搜索字符串组合在一起作为单个参数。相反,'public
是 -S
的参数,并将其余部分视为单独的参数。我不确定您在什么环境中运行,但该上下文对于帮助进行故障排除是必要的。如果需要帮助您进行故障排除,我建议打开一个单独的 StackOverflow 问题,其中包含 git 命令如何发送到 shell 的所有上下文。在我看来,它是通过其他命令发送的?这里的评论不是解决这个问题的正确地方。
如果您想浏览代码更改(查看整个历史中给定单词实际更改的内容),请使用 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 ;) )
在任何修订版、任何文件(Unix/Linux)中搜索:
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
我采用了 Jeet's answer 并将其改编为 Windows(感谢 this answer):
FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt
请注意,对我来说,出于某种原因,删除此正则表达式的实际提交并没有出现在命令的输出中,而是出现在它之前的一个提交中。
--no-pager
添加到最后的 git 命令
>>results.txt
附加到文本文件...
每当我发现自己在您的位置时,我都会使用以下命令行:
git log -S "<words/phrases i am trying to find>" --all --oneline --graph
解释:
git log - 我需要在这里写更多;它按时间顺序显示日志。 -S "
对于其他尝试在 Sourcetree 中执行此操作的人,UI 中没有直接命令(从版本 1.6.21.0 开始)。但是,您可以通过打开终端窗口(主工具栏中的可用按钮)并在其中复制/粘贴它们来使用接受的答案中指定的命令。
注意:Sourcetree 的搜索视图可以为您进行部分文本搜索。按 Ctrl + 3 转到搜索视图(或单击底部的搜索选项卡)。从最右边开始,将搜索类型设置为文件更改,然后键入要搜索的字符串。与上述命令相比,此方法有以下限制:
Sourcetree 仅显示其中一个已更改文件中包含搜索词的提交。查找包含搜索文本的确切文件又是一项手动任务。不支持正则表达式。
受答案 https://stackoverflow.com/a/2929502/6041515 的启发,我发现 git grep
似乎在每次提交时都搜索完整的代码库,而不仅仅是差异,结果往往是重复且冗长的。下面的脚本将只搜索每个 git 提交的差异:
for commit in $(git rev-list --all); do
# search only lines starting with + or -
if git show "$commit" | grep "^[+|-].*search-string"; then
git show --no-patch --pretty=format:'%C(yellow)%h %Cred%ad %Cblue%an%Cgreen%d %Creset%s' --date=short $commit
fi
done
示例输出,底部的 git commit 是首先引入我正在搜索的更改的那个:
csshx$ for commit in $(git rev-list --all); do
> if git show "$commit" | grep "^[+|-].*As csshX is a command line tool"; then
> git show --no-patch --pretty=format:'%C(yellow)%h %Cred%ad %Cblue%an%Cgreen%d %Creset%s' --date=short $commit
> fi
> done
+As csshX is a command line tool, no special installation is needed. It may
987eb89 2009-03-04 Gavin Brock Added code from initial release
我在这里有点惊讶,也许我错过了我正在寻找的答案,但我来这里是为了寻找所有分支的头部。不是针对存储库中的每个修订版,所以对我来说,使用 git rev-list --all
信息太多。
换句话说,对我来说最有用的变化是
git grep -i searchString $(git branch -r)
或者
git branch -r | xargs git grep -i searchString
或者
git branch -r | xargs -n1 -i{} git grep -i searchString {}
而且,当然,您可以在此处尝试正则表达式方法。这里的方法很酷的是它直接针对远程分支工作。我不必对这些分支机构中的任何一个进行检查。
Jeet's answer 在 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 "(^.*?:)|(:.*)", "" }
zsh: parse error near `-unique'`
为已经存在的答案添加更多内容。如果您知道您可能制作的文件,请执行以下操作:
git log --follow -p -S 'search-string' <file-path>
--follow:列出文件的历史记录
好的,今天两次我看到人们想要一个更接近 hg grep
的等价物,它类似于 git log -pS
,但将其输出限制为(带注释的)更改的行。
如果您快速浏览一下,我想这会比寻呼机中的 /pattern/
更方便。
所以这里有一个 diff-hunk 扫描器,它接受 git log --pretty=%h -p
输出并吐出带注释的更改行。将其放入 diffmarkup.l
,例如 make ~/bin/diffmarkup
,然后像这样使用它
git log --pretty=%h -pS pattern | diffmarkup | grep pattern
%option main 8bit nodefault
// vim: tw=0
%top{
#define _GNU_SOURCE 1
}
%x commitheader
%x diffheader
%x hunk
%%
char *afile=0, *bfile=0, *commit=0;
int aline,aremain,bline,bremain;
int iline=1;
<hunk>\n ++iline; if ((aremain+bremain)==0) BEGIN diffheader;
<*>\n ++iline;
<INITIAL,commitheader,diffheader>^diff.* BEGIN diffheader;
<INITIAL>.* BEGIN commitheader; if(commit)free(commit); commit=strdup(yytext);
<commitheader>.*
<diffheader>^(deleted|new|index)" ".* {}
<diffheader>^"---".* if (afile)free(afile); afile=strdup(strchrnul(yytext,'/'));
<diffheader>^"+++".* if (bfile)free(bfile); bfile=strdup(strchrnul(yytext,'/'));
<diffheader,hunk>^"@@ ".* {
BEGIN hunk; char *next=yytext+3;
#define checkread(format,number) { int span; if ( !sscanf(next,format"%n",&number,&span) ) goto lostinhunkheader; next+=span; }
checkread(" -%d",aline); if ( *next == ',' ) checkread(",%d",aremain) else aremain=1;
checkread(" +%d",bline); if ( *next == ',' ) checkread(",%d",bremain) else bremain=1;
break;
lostinhunkheader: fprintf(stderr,"Lost at line %d, can't parse hunk header '%s'.\n",iline,yytext), exit(1);
}
<diffheader>. yyless(0); BEGIN INITIAL;
<hunk>^"+".* printf("%s:%s:%d:%c:%s\n",commit,bfile+1,bline++,*yytext,yytext+1); --bremain;
<hunk>^"-".* printf("%s:%s:%d:%c:%s\n",commit,afile+1,aline++,*yytext,yytext+1); --aremain;
<hunk>^" ".* ++aline, ++bline; --aremain; --bremain;
<hunk>. fprintf(stderr,"Lost at line %d, Can't parse hunk.\n",iline), exit(1);
那么,您是否试图通过 grep 旧版本的代码来查看最后存在的位置?
如果我这样做,我可能会使用 git bisect。使用 bisect,您可以指定一个已知的好版本、一个已知的坏版本和一个简单的脚本来检查版本是好还是坏(在这种情况下,一个 grep 来查看您正在寻找的代码是否存在)。运行它会发现代码何时被删除。
git rev-list --all | xargs -n 5 git grep EXPRESSION
是对 Jeet's solution 的调整,因此它会在搜索时显示结果,而不仅仅是在最后(在大型存储库中可能需要很长时间)。
git grep
来为任何好奇的人提供“实时”结果。
场景:您使用 IDE 对代码进行了大量清理。问题:IDE 清理的超出了应有的范围,现在您的代码无法编译(缺少资源等)
解决方案:
git grep --cached "text_to_find"
它将找到“text_to_find”被更改的文件。
您现在可以撤消此更改并编译您的代码。
A. 完整的、唯一的、排序的路径:
# Get all unique filepaths of files matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | sort | uniq
B. 唯一的、排序的文件名(不是路径):
# Get all unique filenames matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | xargs basename | sort | uniq
第二个命令对 BFG 很有用,因为它只接受文件名而不接受 repo-relative/system-absolute 路径。
查看我的 full answer here 以获得更多解释。
不定期副业成功案例分享
sh.exe": /bin/git: Bad file number
。 VonC 的答案也适用于 msysgit。git gc
或查看:stackoverflow.com/questions/1507463/…