簡體   English   中英

如何在 Git 歷史記錄中 grep(搜索)提交代碼

[英]How to grep (search) committed code in the Git history

我過去某個時候刪除了一個文件或文件中的一些代碼。 我可以在內容中輸入 grep(不在提交消息中)嗎?

一個非常糟糕的解決方案是 grep 日志:

git log -p | grep <pattern>

但是,這不會立即返回提交 hash。 我玩了git grep無濟於事。

要搜索提交內容(即,實際的源代碼行,而不是提交消息等),您需要執行以下操作:

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

git rev-list --all | xargs git grep <expression> 如果您遇到“參數列表太長”錯誤,則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鎬( -S選項。

搜索Foo

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

有關更多信息,請參閱Git 歷史記錄 - 按關鍵字查找丟失的行


正如Jakub Narębski評論的那樣:

  • 這會查找引入或刪除<string>實例的差異 它通常表示“您添加或刪除帶有‘Foo’的行的修訂版”。

  • --pickaxe-regex選項允許您使用擴展的 POSIX 正則表達式而不是搜索字符串。 示例(來自git log ): git log -S"frotz\\(nitfol" --pickaxe-regex


正如Rob評論的那樣,此搜索區分大小寫 - 他打開了一個關於如何搜索不區分大小寫的后續問題

我最喜歡的方法是使用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顯示提交。

如果您想瀏覽代碼更改(查看整個歷史記錄中給定單詞的實際更改內容),請使用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 ;) )

git log可以是在所有分支中搜索文本的更有效方式,尤其是在有很多匹配項並且您希望首先查看更多最近(相關)更改的情況下。

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

這些日志命令列出添加或刪除給定搜索字符串/正則表達式的提交,(通常)首先是最近的。 -p選項導致相關差異顯示在添加或刪除模式的位置,因此您可以在上下文中查看它。

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

git branch -a --contains 8beeff00d

任何修訂版、任何文件(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 的回答並將其改編為 Windows(感謝這個回答):

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

請注意,對我而言,出於某種原因,刪除此正則表達式的實際提交並未出現在命令的輸出中,而是出現在它之前的一個提交中。

為簡單起見,我建議使用 GUI: gitk - The Git repository browser 它非常靈活

  1. 搜索代碼:

    在此處輸入圖片說明
  2. 搜索文件:

    在此處輸入圖片說明
  3. 當然,它也支持正則表達式:

    在此處輸入圖片說明

您可以使用向上/向下箭頭瀏覽結果。

每當我發現自己在您的位置時,我都會使用以下命令行:

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 - 它創建按時間順序提交的圖表。

對於嘗試在Sourcetree 中執行此操作的任何其他人,UI 中沒有針對它的直接命令(從 1.6.21.0 版開始)。 但是,您可以通過打開終端窗口(主工具欄中可用的按鈕)並在其中復制/粘貼它們來使用接受的答案中指定的命令。

注意:Sourcetree 的搜索視圖可以為您進行部分文本搜索。 Ctrl + 3轉到“搜索”視圖(或單擊底部可用的“搜索”選項卡)。 從最右邊,將搜索類型設置為文件更改,然后鍵入要搜索的字符串。 與上述命令相比,此方法具有以下限制:

  1. Sourcetree 僅顯示在已更改文件之一中包含搜索詞的提交 查找包含搜索文本的確切文件又是一項手動任務。
  2. 不支持正則表達式。

好的,就在今天兩次,我看到人們想要更接近hg grep等效項,這類似於git log -pS但將其輸出限制在(帶注釋的)更改的行中。

如果您正在快速瀏覽,我想這會比尋呼機中的/pattern/更方便。

所以這里有一個 diff- git log --pretty=%h -p掃描器,它接受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);

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

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 "(^.*?:)|(:.*)", "" }

那么您是否試圖通過舊版本的代碼來查看最后存在的內容?

如果我這樣做,我可能會使用git bisect 使用 bisect,你可以指定一個已知的好版本、一個已知的壞版本,以及一個簡單的腳本來檢查版本是好還是壞(在這種情況下,一個 grep 來查看你正在尋找的代碼是否存在)。 運行它會找到代碼被刪除的時間。

在已經存在的答案中添加更多內容。 如果您知道您可能在其中制作的文件,請執行以下操作:

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

--follow:列出文件的歷史記錄

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

是對Jeet 解決方案的調整,因此它在搜索時顯示結果,而不僅僅是在最后(在大型存儲庫中可能需要很長時間)。

場景:您使用 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 路徑。

在這里查看我的完整答案以獲取更多解釋。

我在這里有點驚訝,也許我錯過了我正在尋找的答案,但我來到這里是為了尋找所有分支的頭部。 並非針對 repo 中的每個 rev。 所以對我來說,使用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 {} 

而且,當然,您可以在這里嘗試正則表達式方法。 這里的方法很酷的是,它直接針對遠程分支工作。 我不必檢查這些分支中的任何一個。

命令在 git 歷史記錄中搜索

git log -S"alter" --author="authorname" --since=2021.1.1 --until=2023.1.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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM