วิธี grep (ค้นหา) รหัสที่กำหนดในประวัติ Git


1434

ฉันได้ลบไฟล์หรือรหัสบางส่วนในไฟล์บางครั้งในอดีต ฉันสามารถ grep ในเนื้อหา (ไม่ใช่ในการส่งข้อความ)?

วิธีแก้ปัญหาที่แย่มากคือการ grep บันทึก:

git log -p | grep <pattern>

อย่างไรก็ตามสิ่งนี้จะไม่ส่งคืนแฮชการกระทำทันที ฉันเล่นไปรอบ ๆ โดยgit grepไม่มีประโยชน์


2
โพสต์บล็อกเหล่านี้โดย Junio ​​C Hamano (ผู้ดูแลคอมไพล์) อาจน่าสนใจสำหรับคุณ: * เครื่องมือติดตามเนื้อหาที่ดีที่สุดของ Linus (เกี่ยวกับการค้นหา pickaxe git log -Sและตำหนิ) * [สนุกกับ "git log --grep"] [2] (ค้นหาข้อความยืนยัน ) * [สนุกกับ "git grep"] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Jakub Narębski


คำตอบจากการทำซ้ำที่เป็นไปได้ใช้งานได้จริง: stackoverflow.com/a/1340245/492
CAD bloke

ปัญหานี้คือมันไม่ได้ให้บริบทกับการเปลี่ยนแปลง .. คือใคร / เมื่อใด
Sonic Soul

คำตอบ:


1889

ในการค้นหาการคอมมิชชันเนื้อหา (เช่นบรรทัดแหล่งที่มาที่แท้จริงซึ่งตรงข้ามกับการยอมรับข้อความและสิ่งที่คล้ายกัน) คุณต้องทำ:

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

นี้จะ grep regexpผ่านทุกกระทำของคุณข้อความสำหรับ

เหตุผลสำหรับการส่งผ่านเส้นทางในคำสั่งทั้งเป็นเพราะrev-listจะกลับรายการการแก้ไขที่มีการเปลี่ยนแปลงทั้งหมดที่จะlib/utilเกิดขึ้น แต่ยังคุณต้องผ่านไปเพื่อให้มันจะค้นหาในgreplib/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

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
หากคุณได้รับข้อผิดพลาด "ไม่สามารถอ่านต้นไม้" เมื่อคุณเรียกใช้ประวัติ greit git ด้วย rev-list คุณอาจต้องล้างสิ่งต่างๆ ลองgit gcหรือตรวจสอบ: stackoverflow.com/questions/1507463/…
Anthony Panozzo

8
ใช่ดูเหมือนว่าจะล้มเหลวบน Windows เช่นกันอนิจจา
mlissner

552

คุณควรใช้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 - ค้นหาบรรทัดที่หายไปโดยคำสำคัญเพิ่มเติม


ในฐานะที่เป็นJakub Narębskiแสดงความคิดเห็น:

  • นี้มีลักษณะที่แตกต่างที่แนะนำหรือเอาตัวอย่างของ <string>โดยปกติจะหมายถึง "การแก้ไขที่คุณเพิ่มหรือลบบรรทัดด้วย" Foo ""

  • --pickaxe-regexตัวเลือกที่ช่วยให้คุณสามารถใช้งานได้ยาวนาน POSIX regex แทนที่จะค้นหาสตริง ตัวอย่าง (จากgit log):git log -S"frotz\(nitfol" --pickaxe-regex


ตามที่Rob ให้ความเห็นการค้นหานี้จะคำนึงถึงตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก- เขาเปิดคำถามติดตามผลเกี่ยวกับวิธีค้นหาตัวพิมพ์เล็กและตัวพิมพ์ใหญ่


3
ขอบคุณฉันไม่รู้ตัวเลือกนี้ ดูเหมือนว่านี่เป็นทางออกที่ดีที่สุดหากคุณสนใจข้อความการส่งข้อความและโซลูชันของ Jeet นั้นเหมาะสมที่สุดหากคุณต้องการพฤติกรรม grep UNIX แบบดั้งเดิมของการจับคู่สายบริสุทธิ์
Ortwin Gentz

@Ortwin: เห็นด้วย (และฉันได้แก้ปัญหาที่เลือกไว้แล้ว) git logบิตในคำถามของคุณทำให้ผมสับสน;)
VonC

12
รวมกับ-pธงเพื่อเอาท์พุทความแตกต่าง
Sander

มีวิธีการยกเว้นไดเรกทอรีทั้งหมดที่ตรงกับรูปแบบเฉพาะโดยใช้ git log -S หรือไม่
BakaKuna

3
@Anentropic คุณจะต้องมี--branches --allตัวเลือกในการค้นหา repo ทั้งหมด
VonC

249

วิธีที่ฉันชอบที่จะทำมันขึ้นอยู่กับgit log's -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

เพราะจำนวนครั้ง "สวัสดี" -Shelloจะปรากฏในแฟ้มเดียวกันคือก่อนและหลังการกระทำนี้ก็จะไม่ตรงกับการใช้ แต่เนื่องจากมีการเปลี่ยนแปลงไปสู่การจับคู่สายที่กระทำจะถูกแสดงโดยใช้hello-Ghello


2
มีวิธีแสดงบริบทการเปลี่ยนแปลงที่ตรงกันในเอาต์พุตบันทึก git หรือไม่?
Thilo-Alexander Ginkel

13
@ Thilo-AlexanderGinkel - ฉันมักจะเพิ่ม-pตัวเลือกในการแสดงความแตกต่างสำหรับการกระทำแต่ละครั้ง จากนั้นเมื่อมีการเปิดบันทึกในเพจเจอร์ของฉันฉันค้นหาสิ่งที่ฉันกำลังมองหา หากเพจเจอร์ของคุณเป็นlessคุณgit log -Ghello -pคุณสามารถพิมพ์/helloกดEnterและใช้nและNเพื่อค้นหาสิ่งที่เกิดขึ้น "Hello" ครั้งต่อไป
Tyler Holien

ฉันพบปัญหาที่น่าสนใจกับ-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
โซลูชัน accepeted ไม่ทำงานสำหรับฉันทั้ง git log -S อันนี้ทำ!
rodvlopes

29

git log อาจเป็นวิธีที่มีประสิทธิภาพมากขึ้นในการค้นหาข้อความในทุกสาขาโดยเฉพาะอย่างยิ่งหากมีการจับคู่จำนวนมากและคุณต้องการเห็นการเปลี่ยนแปลงล่าสุด (เกี่ยวข้อง) ก่อน

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

รายการคำสั่งบันทึกการใช้งานเหล่านี้ยอมรับว่าจะเพิ่มหรือลบสตริงการค้นหา / regex ที่กำหนด (โดยทั่วไป) ล่าสุดก่อน -pตัวเลือกที่ทำให้เกิดความแตกต่างที่เกี่ยวข้องกับการแสดงที่รูปแบบที่ถูกเพิ่มหรือลบออกเพื่อให้คุณสามารถเห็นมันในบริบท

เมื่อพบการกระทำที่เกี่ยวข้องซึ่งเพิ่มข้อความที่คุณกำลังค้นหา (เช่น 8beeff00d) ให้ค้นหาสาขาที่มีการส่งข้อมูล:

git branch -a --contains 8beeff00d

สวัสดีเส้นเหล่านี้ดูเหมือนจะไม่ทำงานเลย คำสั่งของฉัน> git log -p - all - สตริงสาธารณะ DOB {รับ; ตั้ง; } = string.Empty; ' และทุกครั้งที่ฉันพยายามเรียกใช้ฉันจะได้รับ> ร้ายแรง: อาร์กิวเมนต์ที่ไม่ชัดเจน 'สตริง': การแก้ไขที่ไม่รู้จักหรือเส้นทางที่ไม่ได้อยู่ในแผนผังการทำงาน > ใช้ '-' เพื่อแยกเส้นทางจากการแก้ไขเช่นนี้: '' git <command> [<revision> ... ] - [<file> ... ] '
user216652

@ user216652 ด้วยเหตุผลบางอย่าง'เครื่องหมายคำพูดไม่ได้จัดกลุ่มสตริงการค้นหาของคุณเข้าด้วยกันเป็นอาร์กิวเมนต์เดียว แทนที่จะ'publicเป็นข้อโต้แย้ง-Sและมันก็จะถือว่าส่วนที่เหลือเป็นข้อโต้แย้งแยก ฉันไม่แน่ใจว่าคุณกำลังใช้สภาพแวดล้อมใด แต่บริบทนั้นจำเป็นสำหรับการแก้ไขปัญหา ฉันขอแนะนำให้เปิดคำถาม StackOverflow แยกต่างหากหากจำเป็นเพื่อช่วยคุณแก้ไขปัญหาบริบทของการส่งคำสั่ง git ไปยังเชลล์ สำหรับฉันดูเหมือนว่ามันจะถูกส่งผ่านคำสั่งอื่น ๆ ? ความคิดเห็นที่นี่ไม่ใช่สถานที่ที่เหมาะสมในการค้นหาสิ่งนี้
Edward Anderson

26

ฉันรับคำตอบของ Jeetและปรับใช้กับ Windows (ขอบคุณคำตอบนี้ ):

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

โปรดทราบว่าสำหรับฉันด้วยเหตุผลบางอย่างการกระทำที่เกิดขึ้นจริงที่ถูกลบ regex นี้ไม่ปรากฏในผลลัพธ์ของคำสั่ง แต่มีเพียงหนึ่งการกระทำก่อนหน้านี้


2
+1 - และหากคุณต้องการหลีกเลี่ยงการกดปุ่ม "q" หลังการค้นหาแต่ละครั้งให้เพิ่ม--no-pagerคำสั่ง git ในตอนท้าย
cgp

2
นอกจากนี้ฉันจะทราบว่าการผนวกเข้ากับไฟล์ข้อความมีข้อดีเพิ่มเติมของการแสดงข้อความที่ตรงกันจริง ๆ (ผนวกเข้ากับไฟล์ข้อความที่ใช้>>results.txtสำหรับผู้ที่ไม่ได้มีประสบการณ์ในการวางท่อของ Windows ...
cgp

1
และฉันคิดว่าไวยากรณ์ทุบตีเป็นที่น่าเกลียด :)
smido

23

ค้นหาในการแก้ไขใด ๆ ไฟล์ใด ๆ :

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

ค้นหาเฉพาะบางไฟล์เท่านั้นเช่นไฟล์ XML:

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

บรรทัดผลลัพธ์ควรมีลักษณะดังนี้: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: ข้อความของบรรทัดที่พบ ...

จากนั้นคุณสามารถรับข้อมูลเพิ่มเติมเช่นผู้แต่งวันที่และการใช้ diff git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

11

สำหรับความเรียบง่ายผมขอแนะนำให้ใช้ GUI: gitk - เดอะเบราว์เซอร์ที่เก็บ มันค่อนข้างยืดหยุ่น

  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
"เมื่อใดก็ตามที่ฉันพบตัวเองในสถานที่ของคุณฉันรู้สึกว่าจำเป็นต้องใช้คอมไพล์!"
Sebi

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

คุณกำลังพยายาม grep ผ่านรหัสรุ่นเก่าเพื่อดูว่ามีบางสิ่งที่มีอยู่ครั้งสุดท้ายหรือไม่

ถ้าผมทำเช่นนี้ผมก็อาจจะใช้แบ่งครึ่งคอมไพล์ การใช้ bisect คุณสามารถระบุรุ่นที่ดีที่รู้จักรุ่นที่ไม่ดีที่รู้จักและสคริปต์ง่าย ๆ ที่ตรวจสอบเพื่อดูว่ารุ่นนั้นดีหรือไม่ดี (ในกรณีนี้ grep จะดูว่ามีรหัสที่คุณกำลังมองหาอยู่หรือไม่ ) การรันสิ่งนี้จะค้นหาเมื่อรหัสถูกลบ


2
ใช่ แต่ "ทดสอบ" ของคุณสามารถเป็นสคริปต์ที่ greps สำหรับรหัสและส่งกลับ "จริง" ถ้ารหัสที่มีอยู่และ "เท็จ" หากไม่ได้
Rob Di Marco

2
แล้วถ้าโค้ดไม่ดีในการแก้ไขครั้งที่ 10 จะดีในการแก้ไขครั้งที่ 11 และจะแย่อีกครั้งในการแก้ไข 15 ...
เปาโล

2
ฉันเห็นด้วยกับเปาโล การค้นหาแบบไบนารีเหมาะสำหรับค่า "สั่งซื้อ" เท่านั้น ในกรณีของ git bisect นี่หมายถึงการแก้ไข "ดี" ทั้งหมดมาก่อนการแก้ไข "ไม่ดี" ทั้งหมดเริ่มต้นจากจุดอ้างอิง แต่ไม่สามารถทำการสันนิษฐานได้เมื่อมองหารหัสชั่วคราว วิธีแก้ปัญหานี้อาจใช้งานได้ในบางกรณี แต่มันไม่ใช่วิธีแก้ปัญหาทั่วไปที่ดี
Kent

ฉันคิดว่านี่ไม่มีประสิทธิภาพสูงเพราะมีการตัดทอนต้นไม้ทั้งหมดหลายครั้ง
U. Windl

0

สถานการณ์จำลอง: คุณล้างข้อมูลโค้ดของคุณโดยใช้ IDE ของคุณ ปัญหา: IDE ล้างข้อมูลมากกว่าที่ควรและตอนนี้โค้ดของคุณไม่ได้รวบรวม (ทรัพยากรที่ขาดหายไป ฯลฯ )

สารละลาย:

git grep --cached "text_to_find"

มันจะค้นหาไฟล์ที่เปลี่ยน "text_to_find"

ตอนนี้คุณสามารถยกเลิกการเปลี่ยนแปลงนี้และรวบรวมรหัสของคุณ



-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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.