Git - ฉันจะดูประวัติการเปลี่ยนแปลงของวิธีการ / ฟังก์ชันได้อย่างไร


93

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

ฉันรู้ว่าสิ่งนี้จะต้องใช้คอมไพล์ในการวิเคราะห์โค้ดและการวิเคราะห์จะแตกต่างกันสำหรับภาษาต่างๆ แต่การประกาศวิธีการ / ฟังก์ชันดูคล้ายกันมากในภาษาส่วนใหญ่ดังนั้นฉันจึงคิดว่าอาจมีคนใช้ฟีเจอร์นี้

ภาษาที่ฉันกำลังใช้งานอยู่คือ Objective-C และ SCM ที่ฉันกำลังใช้อยู่คือ git แต่ฉันสนใจที่จะทราบว่าคุณลักษณะนี้มีอยู่สำหรับ SCM / ภาษาใด ๆ หรือไม่


1
ฉันเคยเห็นฟังก์ชันดังกล่าวในข้อเสนอ Git GSoG
Vi.

นี่คือข้อเสนอที่คุณกำลังพูดถึงใช่ไหม list-archives.org/git/…
Erik B


@lpapp คำถามที่นี่ถูกถามเมื่อ 10 เดือนก่อนหน้านี้คำถามอื่นควรถูกทำเครื่องหมายว่าหลอกลวงของคำถามนี้ (ถ้าพวกเขาเป็นคนโง่เลย)
Dirty-flow

2
@lpapp คำถามสองคำถามที่แตกต่างกันอย่างสิ้นเชิง คุณสามารถเขียนสคริปต์ที่แก้ไขชื่อฟังก์ชันเป็นช่วงของบรรทัดจากนั้นใช้เทคนิคจากคำถามนั้นเพื่อรับประวัติสำหรับบรรทัดเหล่านั้น แต่ในตัวมันเองมันไม่ได้ตอบคำถามนี้
Erik B

คำตอบ:


100

เวอร์ชันล่าสุดของการgit logเรียนรู้รูปแบบพิเศษของ-Lพารามิเตอร์:

-L: <funcname>: <ไฟล์>

ติดตามวิวัฒนาการของช่วงบรรทัดที่กำหนดโดย"<start>,<end>"(หรือชื่อฟังก์ชัน regex <funcname>) ภายในไฟล์<file>. คุณไม่สามารถให้ตัว จำกัด pathspec ใด ๆ ขณะนี้ จำกัด เฉพาะการเดินโดยเริ่มจากการแก้ไขครั้งเดียวกล่าวคือคุณสามารถให้อาร์กิวเมนต์การแก้ไขเชิงบวกเป็นศูนย์หรือเชิงบวกได้เพียงครั้งเดียว คุณสามารถระบุตัวเลือกนี้ได้มากกว่าหนึ่งครั้ง
...
ถ้า“:<funcname>”กำหนดให้แทน<start>และ<end>เป็นนิพจน์ทั่วไปที่แสดงช่วงจากบรรทัด funcname แรกที่ตรงกัน<funcname>จนถึงบรรทัด funcname ถัดไป “:<funcname>”ค้นหาจากจุดสิ้นสุดของ-Lช่วงก่อนหน้าหากมีหรือจากจุดเริ่มต้นของไฟล์ “^:<funcname>”ค้นหาจากจุดเริ่มต้นของไฟล์

กล่าวอีกนัยหนึ่ง: ถ้าคุณขอ Git ไปgit log -L :myfunction:path/to/myfile.cตอนนี้จะพิมพ์ประวัติการเปลี่ยนแปลงของฟังก์ชันนั้นอย่างมีความสุข


16
สิ่งนี้อาจใช้ได้ผลกับวัตถุประสงค์ -c นอกกรอบ แต่ถ้าคุณกำลังทำสิ่งนี้สำหรับภาษาอื่น (เช่น Python, Ruby เป็นต้น) คุณอาจต้องเพิ่มการกำหนดค่าที่เหมาะสมภายในไฟล์. gitattributes เพื่อให้ git จดจำฟังก์ชัน / วิธีการ การประกาศในภาษานั้น สำหรับ python ให้ใช้ * .py diff = python สำหรับการใช้ทับทิม * .rb diff = ruby
samaspin

1
git ติดตามฟังก์ชั่นอย่างไร
nn0p

@ nn0p ฉันคิดว่าโดยมีความรู้ทางไวยากรณ์ในหลายภาษาจึงรู้วิธีแยกฟังก์ชันและติดตามการเปลี่ยนแปลงของเธอ
JasonGenX

4
การขยายความคิดเห็นของ @ samaspin สำหรับภาษาอื่น ๆ คุณสามารถอ้างถึงเอกสารได้ที่นี่: git-scm.com/docs/gitattributes#_generating_diff_text
edhgoose

สิ่งนี้ใช้ไม่ได้กับภาษาเช่น Scala และ Java และแม้แต่ C ที่ใช้วงเล็บปีกกาซ้อนกัน (นิพจน์ทั่วไปไม่สามารถจัดการได้โดยทั่วไป) และแม้แต่รูปแบบเริ่มต้นสิ้นสุดก็ไม่ทำงานเมื่อมีการย้ายฟังก์ชันในไฟล์ และแก้ไขในคอมมิตเดียวกัน
Robin Green

16

ใช้git gui blameเป็นเรื่องยากที่จะทำให้การใช้ในสคริปต์และในขณะที่git log -Gและgit log --pickaxeแต่ละคนสามารถแสดงให้คุณเห็นเมื่อนิยามวิธีการปรากฏตัวขึ้นหรือหายไปฉันไม่ได้พบวิธีที่จะทำให้พวกเขาแสดงรายการการเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นกับร่างกายของวิธีการของคุณ

อย่างไรก็ตามคุณสามารถใช้gitattributesและtextconvคุณสมบัติเพื่อรวมโซลูชันที่ทำเช่นนั้นได้ แม้ว่าคุณสมบัติเหล่านี้มีไว้เพื่อช่วยให้คุณทำงานกับไฟล์ไบนารีได้ แต่ก็ทำงานได้ดีเช่นกัน

กุญแจสำคัญคือให้ Git ลบออกจากไฟล์ทุกบรรทัดยกเว้นรายการที่คุณสนใจก่อนที่จะดำเนินการต่าง ๆ จากนั้นgit log, git diffฯลฯ จะเห็นเฉพาะพื้นที่ที่คุณกำลังสนใจใน

นี่คือโครงร่างของสิ่งที่ฉันทำในภาษาอื่น คุณสามารถปรับแต่งได้ตามความต้องการของคุณเอง

  • เขียนเชลล์สคริปต์สั้น ๆ (หรือโปรแกรมอื่น ๆ ) ที่ใช้อาร์กิวเมนต์เดียว - ชื่อของไฟล์ต้นฉบับ - และแสดงผลเฉพาะส่วนที่น่าสนใจของไฟล์นั้น (หรือไม่มีอะไรเลยหากไม่มีสิ่งใดที่น่าสนใจ) ตัวอย่างเช่นคุณอาจใช้สิ่งsedต่อไปนี้:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
    
  • กำหนดtextconvตัวกรองGit สำหรับสคริปต์ใหม่ของคุณ (ดูgitattributesรายละเอียดเพิ่มเติมในหน้า man) ชื่อของตัวกรองและตำแหน่งของคำสั่งสามารถเป็นอะไรก็ได้ที่คุณต้องการ

    $ git config diff.my_filter.textconv /path/to/my_script
    
  • บอกให้ Git ใช้ตัวกรองนั้นก่อนคำนวณความแตกต่างของไฟล์ที่เป็นปัญหา

    $ echo "my_file diff=my_filter" >> .gitattributes
    
  • ตอนนี้ถ้าคุณใช้-G.(สังเกต.) เพื่อแสดงรายการคอมมิตทั้งหมดที่สร้างการเปลี่ยนแปลงที่มองเห็นได้เมื่อใช้ตัวกรองของคุณคุณจะมีคอมมิตที่คุณสนใจทุกประการตัวเลือกอื่น ๆ ที่ใช้กิจวัตรต่างของ Git เช่น--patchจะ ยังได้รับมุมมองที่ จำกัด นี้

    $ git log -G. --patch my_file
    
  • โวล่า!

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

$ git config diff.my_filter.textconv "/path/to/my_command other_func"

แน่นอนว่าสคริปต์ตัวกรองสามารถทำอะไรก็ได้ที่คุณต้องการรับข้อโต้แย้งมากขึ้นหรืออะไรก็ได้: มีความยืดหยุ่นมากกว่าที่ฉันแสดงไว้ที่นี่


1
Take มากกว่าหนึ่งอาร์กิวเมนต์ไม่ได้ผลสำหรับฉัน แต่การใส่ชื่อฟังก์ชั่นในการทำงานที่ดีและมันยอดเยี่ยมจริงๆ!
qwertzguy

ยอดเยี่ยมแม้ว่าฉันจะสงสัยว่า (ใน) สะดวกแค่ไหนในการสลับระหว่างวิธีการต่างๆมากมาย นอกจากนี้คุณรู้จักโปรแกรมที่สามารถดึงฟังก์ชันคล้าย C ทั้งหมดออกมาได้หรือไม่?
nafg

12

git logมีตัวเลือก '-G' ที่สามารถใช้เพื่อค้นหาความแตกต่างทั้งหมด

-G <regex>ดูความแตกต่างที่มีการเพิ่มหรือเส้นที่ถูกนำออกตรงกับที่กำหนด

เพียงแค่ระบุ regex ที่เหมาะสมของชื่อฟังก์ชันที่คุณสนใจ ตัวอย่างเช่น,

$ git log --oneline -G'^int commit_tree'
40d52ff make commit_tree a library function
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
7b9c0a6 git-commit-tree: make it usable from other builtins

5
ฉันไม่ได้เรียกใช้คำสั่ง แต่สำหรับฉันแล้วดูเหมือนว่าคำสั่งนี้จะแสดงเฉพาะการคอมมิตที่แตะบรรทัดที่ตรงกับ regex ซึ่งไม่ใช่วิธีการ / ฟังก์ชันทั้งหมด
Erik B

หากคุณต้องการบริบทเพิ่มเติมคุณสามารถแทนที่--onelineด้วย-p
lfender6445

3
แต่ถ้ามีการเปลี่ยนแปลง 20 บรรทัดในวิธีการ?
nafg

+1. สำหรับฉันคำตอบด้านบนใช้ได้ผล แต่แสดงเฉพาะการคอมมิตล่าสุดเท่านั้น อาจเนื่องมาจาก rebases หรือฟังก์ชั่นขยับไปมาสองสามครั้งไม่แน่ใจ ด้วยการค้นหาบรรทัดจริงของโค้ดแทนที่จะเป็นฟังก์ชันที่อยู่ใน (แม้ว่าจะเป็นแบบซับเดียว) คำตอบนี้ทำให้ฉันสามารถระบุคอมมิตที่ฉันกำลังมองหาได้อย่างง่ายดาย โชคดีที่ฉันมักจะเขียนข้อความที่มีประโยชน์!
Dave S

11

สิ่งที่ใกล้เคียงที่สุดที่คุณสามารถทำได้คือกำหนดตำแหน่งของฟังก์ชันของคุณในไฟล์ (เช่นบอกว่าฟังก์ชันของคุณi_am_buggyอยู่ที่บรรทัด 241-263 ของfoo/bar.c) จากนั้นเรียกใช้บางสิ่งเพื่อให้ได้ผล:

git log -p -L 200,300:foo/bar.c

สิ่งนี้จะเปิดน้อยลง (หรือเพจเจอร์ที่เทียบเท่า) ตอนนี้คุณสามารถพิมพ์/i_am_buggy(หรือเทียบเท่าเพจเจอร์ของคุณ) และเริ่มก้าวผ่านการเปลี่ยนแปลง

สิ่งนี้อาจได้ผลขึ้นอยู่กับรูปแบบรหัสของคุณ:

git log -p -L /int i_am_buggy\(/,+30:foo/bar.c

ซึ่งจะ จำกัด การค้นหาจาก Hit แรกของ regex นั้น (ตามหลักการแล้วการประกาศฟังก์ชันของคุณ) ถึงสามสิบบรรทัดหลังจากนั้น อาร์กิวเมนต์สิ้นสุดสามารถเป็น regexp ได้เช่นกันแม้ว่าการตรวจพบว่าด้วย regexp เป็นประพจน์ iffier


หวาน! FYI นี่เป็นสิ่งใหม่ใน Git v1.8.4 (เดาว่าฉันควรอัพเกรด) แม้ว่าวิธีแก้ปัญหาที่แม่นยำกว่านี้จะดีกว่า ... เช่นถ้ามีคนเขียนสคริปต์คำตอบของ Paul Whittaker
Greg Price

@GregPrice เห็นได้ชัดว่าขอบของการค้นหาอาจเป็นนิพจน์ทั่วไปได้ดังนั้นอย่างน้อยคุณก็สามารถมีจุดเริ่มต้นที่แม่นยำมากขึ้นหรือน้อยลง
badp

โอ้ว้าว. ในความเป็นจริง: แทนที่จะเขียน regexp ของคุณเองคุณสามารถพูด-L ":int myfunc:foo/bar.c"และ จำกัด เฉพาะฟังก์ชันด้วยชื่อนั้นได้ ยอดเยี่ยมมาก - ขอบคุณสำหรับตัวชี้! ตอนนี้ถ้าเพียงการตรวจจับฟังก์ชันมีความน่าเชื่อถือมากขึ้นเล็กน้อย ...
Greg Price

3

วิธีที่ถูกต้องคือการใช้งานgit log -L :function:path/to/fileที่อธิบายไว้ในคำตอบ Eckes

แต่นอกจากนี้หากฟังก์ชันของคุณมีความยาวมากคุณอาจต้องการดูเฉพาะการเปลี่ยนแปลงที่คอมมิตต่างๆได้แนะนำไม่ใช่บรรทัดฟังก์ชันทั้งหมดรวมทั้งไม่ได้ปรับเปลี่ยนสำหรับแต่ละคอมมิตที่อาจแตะเพียงบรรทัดเดียว เหมือนปกติdiffไม่

ปกติgit logสามารถดูความแตกต่างกับแต่ตอนนี้ไม่ได้ทำงานร่วมกับ-p -Lดังนั้นคุณต้องgrep git log -Lแสดงเฉพาะบรรทัดที่เกี่ยวข้องและส่วนหัว commits / files เพื่อกำหนดบริบท เคล็ดลับในที่นี้คือการจับคู่เฉพาะเส้นสีเทอร์มินัลเพิ่ม--colorสวิตช์ด้วยนิพจน์ทั่วไป สุดท้าย:

git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3

โปรดทราบว่าควรจะเกิดขึ้นจริงที่แท้จริง^[ ^[คุณสามารถพิมพ์ได้โดยการกด ^ V ^ [ในทุบตีที่เป็นCtrl+ V, +Ctrl [อ้างอิงที่นี่

-3สวิตช์สุดท้ายยังอนุญาตให้พิมพ์บริบทเอาต์พุต 3 บรรทัดก่อนและหลังแต่ละบรรทัดที่ตรงกัน คุณอาจต้องการปรับเปลี่ยนตามความต้องการของคุณ


2

git ตำหนิแสดงให้คุณเห็นว่าใครเป็นคนสุดท้ายที่เปลี่ยนแต่ละบรรทัดของไฟล์ คุณสามารถระบุบรรทัดที่จะตรวจสอบเพื่อหลีกเลี่ยงการรับประวัติของบรรทัดนอกฟังก์ชันของคุณ


4
ด้วยgit gui blameคุณสามารถนำทางเวอร์ชันเก่า
Vi.

0
  1. แสดงประวัติฟังก์ชันด้วยgit log -L :<funcname>:<file>ดังที่แสดงในคำตอบของ eckesและgit doc

    หากไม่แสดงอะไรเลยโปรดดูที่การกำหนดส่วนหัว hunk ที่กำหนดเองเพื่อเพิ่มสิ่งที่ต้องการ*.java diff=javaลงใน.gitattributes ไฟล์เพื่อรองรับภาษาของคุณ

  2. แสดงประวัติฟังก์ชันระหว่างคอมมิตกับ git log commit1..commit2 -L :functionName:filePath

  3. แสดงประวัติฟังก์ชันที่มากเกินไป (อาจมีหลายฟังก์ชันที่มีชื่อเดียวกัน แต่มีพารามิเตอร์ต่างกัน) ด้วย git log -L :sum\(double:filepath

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