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

时间:2010-05-28 11:36:06

标签: git grep diff

我过去曾在某个时间删除过某个文件或某些代码。我可以在内容中进行grep(不在提交消息中)吗?

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

git log -p | grep <pattern>

然而,这并不会立即返回提交哈希。我玩git grep无济于事。

18 个答案:

答案 0 :(得分:1673)

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

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

答案 1 :(得分:478)

您应该使用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 history - find lost line by keyword


Jakub Narębski评论:

  • 会查找引入或移除<string> 实例的差异。
    它通常意味着“使用'Foo'添加或删除行的修订”。

  • --pickaxe-regex选项允许您使用扩展POSIX正则表达式而不是搜索字符串。


正如Rob所述,此搜索区分大小写 - 他打开了follow-up question,了解如何搜索不区分大小写的内容。

答案 2 :(得分:223)

我最喜欢的方法是使用git log的{​​{1}}选项(在1.7.4版中添加)。

-G

-G<regex> Look for differences whose added or removed line matches the given <regex>. -G选项确定提交是否匹配的方式之间存在细微差别:

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

以此提交为例:

-G

因为文件中出现“hello”的次数在此提交之前和之后是相同的,所以使用diff --git a/test b/test index dddc242..60a8ba6 100644 --- a/test +++ b/test @@ -1 +1 @@ -hello hello +hello goodbye hello 将不匹配。但是,由于对匹配-Shello的行进行了更改,因此将使用hello显示提交。

答案 3 :(得分:43)

如果您想浏览代码更改(请参阅整个历史记录中实际更改的内容),请转到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 ;) )

答案 4 :(得分:24)

我带了@Jeet's answer并将其添加到Windows(感谢this answer):

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

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

答案 5 :(得分:19)

git log可以更有效地在所有分支中搜索文本,特别是如果有很多匹配项,并且您希望首先查看更多近期(相关)更改。

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

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

找到一个添加了你正在寻找的文本的相关提交(例如8beeff00d),找到包含提交的分支:

git branch -a --contains 8beeff00d

答案 6 :(得分:17)

搜索任何修订版,任何文件

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

答案 7 :(得分:6)

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

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

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

答案 8 :(得分:5)

为简单起见,我建议使用GUI:gitk - The Git repository browser,它非常灵活

  1. 搜索代码: enter image description here
  2. 搜索文件: enter image description here
  3. 因为它也支持正则表达式: enter image description here
  4. 您可以使用向上/向下箭头

    浏览结果

答案 9 :(得分:2)

那么您是否正在尝试使用旧版本的代码来查看最近存在的内容?

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

答案 10 :(得分: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 "(^.*?:)|(:.*)", "" }

答案 11 :(得分:2)

好吧,今天有两次[em] 我见过人们想要更接近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);

答案 12 :(得分:1)

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

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

答案 13 :(得分:1)

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

解决方案:

git grep --cached "text_to_find"

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

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

答案 14 :(得分:1)

受到答案 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

答案 15 :(得分:0)

In my case I needed to search a Short Commit and the listed solutions were unfortunately not working.

I managed to do it with: (replace the REGEX token)

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

答案 16 :(得分:0)

只要我发现自己在您的位置,就会使用以下命令行:

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-创建按时间顺序排列的提交图。

答案 17 :(得分:-1)

为已经存在的答案添加更多内容。 如果您知道制作文件所在的位置,请执行以下操作:

git log --follow -p -S 'search-string' <file-path>

-关注:列出文件的历史记录