Cách grep (tìm kiếm) mã đã cam kết trong lịch sử Git


1434

Tôi đã xóa một tập tin hoặc một số mã trong một tập tin trong quá khứ. Tôi có thể grep trong nội dung (không phải trong các thông điệp cam kết) không?

Một giải pháp rất kém là grep log:

git log -p | grep <pattern>

Tuy nhiên, điều này không trả về hàm băm ngay lập tức. Tôi chơi xung quanh mà git grepkhông có kết quả.


2
Những bài đăng trên blog của Junio ​​C Hamano (người bảo trì git) có thể thú vị đối với bạn: * Công cụ theo dõi nội dung cuối cùng của Linus (về tìm kiếm pickaxe tức là git log -Svà đổ lỗi) * [Vui với "git log --grep"] [2] (tìm kiếm thông điệp cam kết ) * [Vui với "git grep"] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Jakub Narębski


câu trả lời từ các bản sao có thể thực sự hoạt động: stackoverflow.com/a/1340245/492
CAD bloke

Vấn đề với điều này là nó không mang lại bất kỳ bối cảnh nào cho sự thay đổi .. tức là ai / khi nào
Sonic Soul

Câu trả lời:


1889

Để tìm kiếm nội dung cam kết (nghĩa là các dòng nguồn thực tế, trái với thông điệp cam kết và tương tự), bạn cần thực hiện:

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

git rev-list --all | xargs git grep <expression> sẽ hoạt động nếu bạn gặp phải lỗi "Danh sách đối số quá dài".

Nếu bạn muốn giới hạn tìm kiếm ở một số cây con (ví dụ: "lib / produc"), bạn sẽ cần chuyển nó cho rev-listtiểu ban và grepcả:

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

Điều này sẽ grep thông qua tất cả các văn bản cam kết của bạn cho regexp.

Lý do vượt qua đường dẫn trong cả hai lệnh là vì rev-listsẽ trả về danh sách sửa đổi, nơi tất cả các thay đổi lib/utilđã xảy ra, nhưng bạn cũng cần phải chuyển đến grepđể nó sẽ chỉ tìm kiếm trong đó lib/util.

Chỉ cần tưởng tượng kịch bản sau đây: grepcó thể tìm thấy điều tương tự <regexp>trên các tệp khác có trong cùng một bản sửa đổi được trả về rev-list(ngay cả khi không có thay đổi nào đối với tệp đó trong bản sửa đổi đó).

Dưới đây là một số cách hữu ích khác để tìm kiếm nguồn của bạn:

Tìm kiếm cây làm việc để khớp văn bản biểu thức chính quy regrec:

git grep <regexp>

Tìm kiếm cây làm việc cho các dòng văn bản khớp với biểu thức chính quy regrec1 hoặc regapi2:

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

Tìm kiếm cây làm việc cho các dòng văn bản khớp với biểu thức chính quy regrec1 và regapi2, chỉ báo cáo đường dẫn tệp:

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

Tìm kiếm cây làm việc cho các tệp có các dòng văn bản khớp với biểu thức chính quy regrec1 và các dòng văn bản khớp với biểu thức chính quy regrec2:

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

Tìm kiếm cây làm việc cho các dòng thay đổi của mẫu phù hợp văn bản:

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

Tìm kiếm tất cả các sửa đổi cho văn bản phù hợp với biểu thức chính quy regrec:

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

Tìm kiếm tất cả các sửa đổi giữa rev1 và rev2 để tìm văn bản khớp biểu thức chính quy regrec:

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

61
Cảm ơn, làm việc tuyệt vời! Thật đáng buồn khi "$ (git rev-list --all)" là cần thiết và không có công tắc thuận tiện để chỉ định tìm kiếm trong toàn bộ lịch sử của một chi nhánh.
Ortwin Gentz

3
Thông minh. +1. GitBook thêm một số chi tiết ( book.git-scm.com/4_finding_with_git_grep.html ) và Junio ​​C Hamano minh họa một số điểm của bạn: gitster.livejournal.com/27674.html
VonC

18
Thật không may, tôi không thể thực hiện điều này với msysgit-1.7.4. Nó nói với tôi sh.exe": /bin/git: Bad file number. Câu trả lời của VonC cũng hoạt động với msysgit.
eckes

4
Nếu bạn gặp lỗi "không thể đọc cây" khi bạn gọi lịch sử git grep với danh sách rev, bạn có thể cần phải dọn dẹp mọi thứ. Hãy thử git gchoặc kiểm tra: stackoverflow.com/questions/1507463/ từ
Anthony Panozzo

8
Vâng, điều này dường như cũng thất bại trên Windows, than ôi.
mlissner

552

Bạn nên sử dụng tùy chọn pickaxe ( -S) của git log.

Để tìm kiếm Foo:

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

Xem lịch sử Git - tìm dòng bị mất theo từ khóa để biết thêm.


Như Jakub Narębski đã nhận xét:

  • cái này tìm sự khác biệt giới thiệu hoặc loại bỏ một thể hiện của<string> . Nó thường có nghĩa là "phiên bản mà bạn đã thêm hoặc xóa dòng bằng 'Foo'".

  • các --pickaxe-regextùy chọn cho phép bạn sử dụng mở rộng POSIX regex thay vì tìm kiếm một chuỗi. Ví dụ (từ git log):git log -S"frotz\(nitfol" --pickaxe-regex


Như Rob nhận xét, tìm kiếm này phân biệt chữ hoa chữ thường - anh ta đã mở một câu hỏi tiếp theo về cách tìm kiếm không phân biệt chữ hoa chữ thường.


3
Cảm ơn, tôi đã không biết về tùy chọn này. Có vẻ như đây là giải pháp tốt nhất nếu bạn quan tâm đến các thông điệp cam kết và giải pháp của Jeet là phù hợp nhất nếu bạn cần hành vi grep UNIX truyền thống của khớp dòng thuần.
Ortwin Gentz

@Ortwin: đã đồng ý (và tôi đã nâng cao giải pháp đã chọn). một git logchút trong câu hỏi của bạn đã làm tôi bối rối;)
VonC

12
Kết hợp nó với -pcờ để xuất ra diff.
Sander

Có cách nào để loại trừ tất cả các thư mục phù hợp với một mẫu cụ thể bằng git log -S không?
BakaKuna

3
@Anentropic bạn sẽ cần các --branches --alltùy chọn để tìm kiếm tất cả repo.
VonC

249

Cách ưa thích của tôi để làm điều đó là có git log's -Gtùy chọn (được thêm vào trong phiên bản 1.7.4).

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

Có một sự khác biệt tinh tế giữa cách thức -G-Scác tùy chọn xác định nếu một cam kết phù hợp:

  • Các -Stùy chọn cơ bản đếm số lần các trận đấu tìm kiếm của bạn trong một tập tin trước và sau khi một cam kết. Cam kết được hiển thị trong nhật ký nếu số trước và sau khác nhau. Điều này sẽ không, ví dụ, hiển thị các cam kết nơi một dòng phù hợp với tìm kiếm của bạn đã được di chuyển.
  • Với -Gtùy chọn, cam kết được hiển thị trong nhật ký nếu tìm kiếm của bạn khớp với bất kỳ dòng nào đã được thêm, xóa hoặc thay đổi.

Lấy cam kết này làm ví dụ:

diff --git a/test b/test
index dddc242..60a8ba6 100644
--- a/test
+++ b/test
@@ -1 +1 @@
-hello hello
+hello goodbye hello

Bởi vì số lần "xin chào" xuất hiện trong tệp là như nhau trước và sau khi cam kết này, nó sẽ không khớp với việc sử dụng -Shello. Tuy nhiên, vì đã có thay đổi đối với dòng khớp hello, nên cam kết sẽ được hiển thị bằng cách sử dụng -Ghello.


2
Có cách nào để hiển thị bối cảnh thay đổi phù hợp trong đầu ra nhật ký git không?
Thilo-Alexander Ginkel

13
@ Thilo-AlexanderGinkel - Tôi thường chỉ cần thêm -ptùy chọn để hiển thị một khác biệt cho mỗi cam kết. Sau đó, khi nhật ký được mở trong máy nhắn tin của tôi, tôi tìm kiếm bất cứ thứ gì tôi đang tìm kiếm. Nếu máy nhắn tin của bạn lessvà bạn git log -Ghello -p, bạn có thể nhập /hello, nhấn Entervà sử dụng nNđể tìm các lần xuất hiện tiếp theo / trước của "xin chào".
Tyler Holien

Tôi đã tìm thấy một vấn đề thú vị với -Gvà Regex: Nếu dòng lệnh sử dụng UTF-8 và tệp bạn đang xem sử dụng một số mã hóa ISO-Latin (8 bit), .*không thành công. Ví dụ: tôi có một thay đổi Vierter Entwurf-> Fünfter Entwurfvà trong khi 'V.*ter Entwurf'tạo ra một kết quả khớp 'F.*ter Entwurf'thì không.
U. Windl

51

Nếu bạn muốn duyệt các thay đổi mã (xem những gì thực sự đã được thay đổi với từ đã cho trong toàn bộ lịch sử), hãy chuyển sang patchchế độ - Tôi đã tìm thấy một sự kết hợp rất hữu ích khi thực hiện:

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
Giải pháp được chấp nhận không hoạt động đối với tôi cũng không phải là git log -S. Cái này đã làm!
Rodvlopes

29

git log có thể là một cách hiệu quả hơn để tìm kiếm văn bản trên tất cả các chi nhánh, đặc biệt là nếu có nhiều kết quả khớp và bạn muốn xem các thay đổi gần đây (có liên quan) trước.

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

Các danh sách lệnh log này cam kết thêm hoặc xóa chuỗi tìm kiếm / regex đã cho, (nói chung) gần đây hơn trước. Các -ptùy chọn làm cho diff có liên quan sẽ được hiển thị nơi các mô hình đã được bổ sung hoặc loại bỏ, vì vậy bạn có thể nhìn thấy nó trong ngữ cảnh.

Đã tìm thấy một cam kết có liên quan có thêm văn bản bạn đang tìm kiếm (ví dụ: 8beeff00d), hãy tìm các nhánh có chứa cam kết:

git branch -a --contains 8beeff00d

Xin chào, những dòng này dường như không hoạt động. Lệnh của tôi là> git log -p --all -S 'chuỗi công khai DOB {get; bộ; } = chuỗi.Empty; ' và mỗi khi tôi cố chạy nó, tôi nhận được> fatal: đối số mơ hồ 'chuỗi': sửa đổi không xác định hoặc đường dẫn không nằm trong cây làm việc. > Sử dụng '-' để tách các đường dẫn khỏi các phiên bản, như thế này:> 'git <lệnh> [<sửa đổi> ...] - [<file> ...]'
user216652

@ user216652 Vì một số lý do, các 'trích dẫn không nhóm các chuỗi tìm kiếm của bạn lại với nhau dưới dạng một đối số. Thay vào đó, 'publiclà đối số -Svà nó coi phần còn lại là đối số riêng biệt. Tôi không chắc bạn đang chạy trong môi trường nào, nhưng bối cảnh đó sẽ là cần thiết để giúp khắc phục sự cố. Tôi khuyên bạn nên mở một câu hỏi StackOverflow riêng nếu cần để giúp bạn khắc phục sự cố, với tất cả bối cảnh về cách lệnh git của bạn được gửi đến trình bao. Dường như với tôi rằng nó được gửi qua một số lệnh khác? Bình luận ở đây không phải là nơi thích hợp để tìm ra điều này.
Edward Anderson

26

Tôi đã lấy câu trả lời của Jeet và điều chỉnh nó cho Windows (nhờ câu trả lời này ):

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

Lưu ý rằng đối với tôi, vì một số lý do, cam kết thực tế đã xóa regex này không xuất hiện trong đầu ra của lệnh, mà là một cam kết trước đó.


2
+1 - và nếu bạn muốn tránh nhấn "q" sau mỗi lần tìm, hãy thêm --no-pagervào lệnh git ở cuối
cgp

2
Ngoài ra, tôi sẽ lưu ý rằng việc thêm vào tệp văn bản có thêm lợi thế là thực sự hiển thị văn bản phù hợp. (nối vào tệp văn bản bằng cách sử dụng >>results.txtcho những người không thành thạo trong đường ống Windows ...
cgp

1
Và tôi nghĩ cú pháp của bash là xấu xí :)
smido

23

Tìm kiếm trong mọi sửa đổi, bất kỳ tập tin :

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

Chỉ tìm kiếm trong một số tệp đã cho, ví dụ: tệp XML:

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

Các dòng kết quả sẽ trông như thế này: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: văn bản của dòng mà nó tìm thấy ...

Sau đó, bạn có thể nhận thêm thông tin như tác giả, ngày tháng và diff bằng cách sử dụng git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

11

Để đơn giản, tôi khuyên bạn nên sử dụng GUI: gitk - Trình duyệt kho Git . Nó khá linh hoạt

  1. Để tìm kiếm mã:

    Nhập mô tả hình ảnh ở đây
  2. Để tìm kiếm tập tin:

    Nhập mô tả hình ảnh ở đây
  3. Tất nhiên, nó cũng hỗ trợ các biểu thức thông thường:

    Nhập mô tả hình ảnh ở đây

Và bạn có thể điều hướng qua các kết quả bằng cách sử dụng mũi tên lên / xuống.


6

Đối với bất kỳ ai khác đang cố gắng thực hiện điều này trong Sourcetree , không có lệnh trực tiếp nào trong giao diện người dùng cho nó (kể từ phiên bản 1.6.21.0). Tuy nhiên, bạn có thể sử dụng các lệnh được chỉ định trong câu trả lời được chấp nhận bằng cách mở cửa sổ Terminal (nút có sẵn trên thanh công cụ chính) và sao chép / dán chúng vào đó.

Lưu ý: Chế độ xem Tìm kiếm của Sourcetree có thể thực hiện một phần tìm kiếm văn bản cho bạn. Nhấn Ctrl+ 3để đi đến Chế độ xem tìm kiếm (hoặc nhấp vào tab Tìm kiếm có sẵn ở dưới cùng). Từ ngoài cùng bên phải, đặt loại Tìm kiếm thành Thay đổi tệp và sau đó nhập chuỗi bạn muốn tìm kiếm. Phương pháp này có những hạn chế sau so với lệnh trên:

  1. Sourcetree chỉ cho thấy cam kết có chứa các từ tìm kiếm trong một trong các tập tin đã thay đổi. Tìm tập tin chính xác có chứa văn bản tìm kiếm lại là một nhiệm vụ thủ công.
  2. RegEx không được hỗ trợ.

4

Bất cứ khi nào tôi thấy mình ở vị trí của bạn, tôi sử dụng dòng lệnh sau:

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

Giải trình:

  1. git log- Cần tôi viết thêm ở đây; nó hiển thị các bản ghi theo thứ tự thời gian.
  2. -S "<words/phrases i am trying to find>" - Nó hiển thị tất cả các cam kết Git trong đó bất kỳ tệp nào (được thêm / sửa đổi / xóa) có các từ / cụm từ tôi đang cố gắng tìm mà không có ký hiệu '<>'.
  3. --all - Để thực thi và tìm kiếm trên tất cả các chi nhánh.
  4. --oneline - Nó nén nhật ký Git trong một dòng.
  5. --graph - Nó tạo ra biểu đồ của các cam kết theo thứ tự thời gian.

1
"Bất cứ khi nào tôi thấy mình ở vị trí của bạn, tôi cảm thấy cần phải sử dụng git!"
Sebi

1
Đây là một câu trả lời tuyệt vời!
Alf Eaton

@AlfEaton niềm vui của tôi!
surajs1n

2

Câu trả lời của Jeet hoạt động trong PowerShell.

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

Dưới đây hiển thị tất cả các tệp, trong bất kỳ cam kết nào, có chứa a password.

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

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

1

Vì vậy, bạn đang cố gắng grep qua các phiên bản cũ hơn của mã để xem nơi nào tồn tại cuối cùng?

Nếu tôi đang làm điều này, tôi có thể sẽ sử dụng git bisect . Sử dụng bisect, bạn có thể chỉ định một phiên bản tốt đã biết, một phiên bản xấu đã biết và một tập lệnh đơn giản để kiểm tra xem phiên bản đó là tốt hay xấu (trong trường hợp này là một grep để xem liệu mã bạn đang tìm kiếm có hiện diện không ). Chạy này sẽ tìm thấy khi mã đã được gỡ bỏ.


2
Có, nhưng "kiểm tra" của bạn có thể là một tập lệnh greps cho mã và trả về "true" nếu mã tồn tại và "false" nếu không.
Rob Di Marco

2
Chà, điều gì sẽ xảy ra nếu mã bị xấu trong phiên bản 10, trở nên tốt trong phiên bản 11 và trở lại xấu trong phiên bản 15 ...
Paolo

2
Tôi đồng ý với Paolo. Tìm kiếm nhị phân chỉ thích hợp cho các giá trị "được đặt hàng". Trong trường hợp git bisect, điều này có nghĩa là tất cả các sửa đổi "tốt" đều xuất hiện trước tất cả các sửa đổi "xấu", bắt đầu từ điểm tham chiếu, nhưng giả định đó không thể được thực hiện khi tìm mã tạm thời. Giải pháp này có thể hoạt động trong một số trường hợp, nhưng nó không phải là giải pháp cho mục đích chung tốt.
Kent

Tôi nghĩ rằng điều này là không hiệu quả cao vì toàn bộ cây được kiểm tra nhiều lần cho bisect.
U. Windl

0

Kịch bản: Bạn đã làm sạch mã bằng cách sử dụng IDE của mình. Vấn đề: IDE đã dọn sạch hơn mức cần thiết và bây giờ mã của bạn không biên dịch (thiếu tài nguyên, v.v.)

Giải pháp:

git grep --cached "text_to_find"

Nó sẽ tìm tập tin trong đó "text_to_find" đã được thay đổi.

Bây giờ bạn có thể hoàn tác thay đổi này và biên dịch mã của bạn.


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

là một tinh chỉnh cho giải pháp của Jeet , vì vậy nó hiển thị kết quả trong khi tìm kiếm và không chỉ ở cuối (có thể mất nhiều thời gian trong một kho lưu trữ lớn).


-1

Trong trường hợp của tôi, tôi cần tìm kiếm một cam kết ngắn và các giải pháp được liệt kê không may không hoạt động.

Tôi đã quản lý để làm điều đó bằng (thay thế mã thông báo 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.