Git - Bagaimana cara melihat riwayat perubahan suatu metode / fungsi?

93

Jadi saya menemukan pertanyaan tentang cara melihat riwayat perubahan file, tetapi riwayat perubahan file khusus ini sangat besar dan saya benar-benar hanya tertarik pada perubahan metode tertentu. Jadi, apakah mungkin melihat riwayat perubahan hanya untuk metode tertentu itu?

Saya tahu ini akan memerlukan git untuk menganalisis kode dan analisisnya akan berbeda untuk bahasa yang berbeda, tetapi deklarasi metode / fungsi terlihat sangat mirip di sebagian besar bahasa, jadi saya pikir mungkin seseorang telah menerapkan fitur ini.

Bahasa yang saya gunakan saat ini adalah Objective-C dan SCM yang saat ini saya gunakan adalah git, tapi saya tertarik untuk mengetahui apakah fitur ini tersedia untuk SCM / bahasa apa pun.

Erik B
sumber
1
Saya telah melihat fungsionalitas seperti itu dalam proposal Git GSoG.
Vi.
Apakah ini proposal yang Anda bicarakan? list-archives.org/git/…
Erik B
1
kemungkinan duplikat dari Ambil log komit untuk baris tertentu dalam file?
lpapp
@lpapp pertanyaan di sini telah ditanyakan 10 bulan sebelumnya, pertanyaan lain harus ditandai sebagai penipu yang satu ini (jika mereka ditipu sama sekali).
Aliran kotor
2
@lpapp Itu adalah dua pertanyaan yang sangat berbeda. Anda mungkin dapat menulis skrip yang menyelesaikan nama fungsi ke berbagai baris dan kemudian menggunakan teknik dari pertanyaan tersebut untuk mendapatkan riwayat untuk baris tersebut, tetapi teknik itu sendiri tidak menjawab pertanyaan ini.
Erik B

Jawaban:

100

Versi terbaru dari git logmempelajari bentuk khusus dari -Lparameter:

-L: <funcname>: <file>

Lacak evolusi rentang garis yang diberikan oleh "<start>,<end>"(atau nama fungsi regex <funcname>) di dalam <file>. Anda tidak boleh memberikan pembatas pathspec. Ini saat ini terbatas pada jalan yang dimulai dari satu revisi, yaitu, Anda hanya boleh memberikan nol atau satu argumen revisi positif. Anda dapat menentukan opsi ini lebih dari sekali.
...
Jika “:<funcname>”diberikan sebagai pengganti <start>dan <end>, ini adalah ekspresi reguler yang menunjukkan rentang dari baris funcname pertama yang cocok <funcname>, hingga baris funcname berikutnya. “:<funcname>”mencari dari akhir -Lrentang sebelumnya , jika ada, sebaliknya dari awal file. “^:<funcname>”mencari dari awal file.

Dengan kata lain: jika Anda meminta Git git log -L :myfunction:path/to/myfile.c, sekarang dengan senang hati akan mencetak riwayat perubahan fungsi itu.

eckes
sumber
16
ini mungkin berfungsi untuk objektif-c di luar kotak tetapi jika Anda melakukan ini untuk bahasa lain (misalnya Python, Ruby dll.) Anda mungkin perlu menambahkan konfigurasi yang sesuai di dalam file .gitattributes agar git mengenali fungsi / metode deklarasi dalam bahasa itu. Untuk penggunaan python * .py diff = python, untuk penggunaan ruby ​​* .rb diff = ruby
samaspin
1
Bagaimana git melacak fungsinya?
nn0p
@ nn0p Saya berasumsi dengan memiliki pengetahuan sintaks dari beberapa bahasa dan dengan demikian mengetahui bagaimana mengisolasi fungsi dan melacak perubahannya.
JasonGenX
4
Memperluas komentar @ samaspin, untuk bahasa lain Anda dapat merujuk ke dokumen di sini: git-scm.com/docs/gitattributes#_generating_diff_text
edhgoose
Ini tidak berfungsi untuk bahasa seperti Scala dan Java dan bahkan C yang menggunakan tanda kurung kurawal bertingkat (ekspresi reguler tidak dapat menanganinya secara umum), dan bahkan bentuk awal dan akhir tidak berfungsi saat fungsi dipindahkan dalam file. dan dimodifikasi dalam komit yang sama.
Robin Green
16

Menggunakan git gui blamesulit untuk digunakan dalam skrip, dan sementara git log -Gdan git log --pickaxemasing-masing dapat menunjukkan kepada Anda ketika definisi metode muncul atau hilang, saya belum menemukan cara untuk membuat daftar semua perubahan yang dibuat pada tubuh metode Anda.

Namun, Anda dapat menggunakan gitattributesdan textconvproperti untuk mengumpulkan solusi yang dapat melakukannya. Meskipun fitur ini pada awalnya dimaksudkan untuk membantu Anda bekerja dengan file biner, fitur ini berfungsi dengan baik di sini.

Kuncinya adalah meminta Git menghapus semua baris dari file kecuali yang Anda minati sebelum melakukan operasi diff. Kemudian git log,, git diffdll. Hanya akan melihat area yang Anda minati.

Inilah garis besar dari apa yang saya lakukan dalam bahasa lain; Anda dapat menyesuaikannya untuk kebutuhan Anda sendiri.

  • Tulis skrip shell pendek (atau program lain) yang menggunakan satu argumen - nama file sumber - dan hanya menampilkan bagian yang menarik dari file itu (atau tidak ada jika tidak ada yang menarik). Misalnya, Anda dapat menggunakan sedsebagai berikut:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
    
  • Tentukan textconvfilter Git untuk skrip baru Anda. (Lihat gitattributeshalaman manual untuk lebih jelasnya.) Nama filter dan lokasi perintah bisa apapun yang Anda suka.

    $ git config diff.my_filter.textconv /path/to/my_script
    
  • Beri tahu Git untuk menggunakan filter itu sebelum menghitung diff untuk file yang dimaksud.

    $ echo "my_file diff=my_filter" >> .gitattributes
    
  • Sekarang, jika Anda menggunakan -G.(perhatikan .) untuk membuat daftar semua komit yang menghasilkan perubahan yang terlihat saat filter Anda diterapkan, Anda akan memiliki komit yang Anda minati. Opsi lain yang menggunakan rutinitas diff Git, seperti --patch, akan juga dapatkan tampilan terbatas ini.

    $ git log -G. --patch my_file
    
  • Voilà!

Satu perbaikan berguna yang mungkin ingin Anda lakukan adalah membuat skrip filter Anda menggunakan nama metode sebagai argumen pertamanya (dan file sebagai yang kedua). Ini memungkinkan Anda menentukan metode baru yang menarik hanya dengan memanggil git config, daripada harus mengedit skrip Anda. Misalnya, Anda mungkin berkata:

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

Tentu saja, skrip filter dapat melakukan apa pun yang Anda suka, mengambil lebih banyak argumen, atau apa pun: ada banyak fleksibilitas di luar yang telah saya tunjukkan di sini.

Paul Whittaker
sumber
1
Mengambil lebih dari satu argumen tidak berhasil untuk saya, tetapi memasukkan nama fungsi dalam kerja keras dengan baik dan ini benar-benar luar biasa!
qwertzguy
Cemerlang, meskipun saya bertanya-tanya bagaimana nyamannya beralih di antara banyak metode yang berbeda. Juga, apakah Anda mengetahui program yang dapat menarik seluruh fungsi mirip-C?
nafg
12

git log memiliki opsi '-G' dapat digunakan untuk menemukan semua perbedaan.

-G Carilah perbedaan yang baris yang ditambahkan atau dihapus cocok dengan yang diberikan <regex>.

Cukup berikan regex yang tepat dari nama fungsi yang Anda pedulikan. Sebagai contoh,

$ 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
lht
sumber
5
Saya tidak menjalankan perintah, tetapi menurut saya perintah ini hanya akan menampilkan komit yang menyentuh baris yang cocok dengan regex, yang bukan keseluruhan metode / fungsi.
Erik B
jika Anda menginginkan lebih banyak konteks, Anda dapat mengganti --onelinedengan-p
lfender6445
3
Tetapi bagaimana jika perubahan dilakukan 20 baris ke dalam metode?
nafg
+1. Bagi saya, jawaban teratas berhasil tetapi hanya menunjukkan komitmen terbaru. Mungkin karena rebases atau fungsi berpindah beberapa kali, tidak yakin. Dengan mencari baris kode sebenarnya alih-alih fungsinya (meskipun satu baris) jawaban ini memungkinkan saya untuk dengan mudah menemukan komit yang saya cari. Syukurlah saya biasanya menulis pesan komit yang berguna!
Dave S
11

Hal terdekat yang dapat Anda lakukan adalah menentukan posisi fungsi Anda dalam file (mis. Katakanlah fungsi Anda i_am_buggyada di baris 241-263 dari foo/bar.c), kemudian jalankan sesuatu yang mempengaruhi:

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

Ini akan membuka lebih sedikit (atau pager yang setara). Sekarang Anda dapat mengetik /i_am_buggy(atau setara pager Anda) dan mulai melangkah melalui perubahan.

Ini bahkan mungkin berhasil, tergantung pada gaya kode Anda:

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

Ini membatasi pencarian dari klik pertama regex itu (idealnya deklarasi fungsi Anda) hingga tiga puluh baris setelah itu. Argumen akhir juga bisa berupa regexp, meskipun mendeteksi bahwa dengan regexp adalah proposisi iffier.

badp
sumber
Manis! FYI, ini baru di Git v1.8.4. (Sepertinya saya harus meningkatkan.) Meskipun solusi yang lebih tepat akan menyenangkan ... seperti jika seseorang membuat skrip jawaban Paul Whittaker.
Harga Greg
@GregPrice Rupanya tepi pencarian bisa jadi ekspresi reguler , jadi Anda setidaknya bisa memiliki titik awal yang lebih atau kurang tepat.
badp
Oh wow. Faktanya: alih-alih menulis regexp Anda sendiri, Anda cukup mengucapkan -L ":int myfunc:foo/bar.c"dan membatasi fungsi dengan nama itu. Ini luar biasa - terima kasih atas penunjuknya! Sekarang jika saja pendeteksian fungsi sedikit lebih dapat diandalkan ...
Greg Price
3

Cara yang benar adalah menggunakan git log -L :function:path/to/fileseperti yang dijelaskan dalam jawaban eckes .

Tetapi sebagai tambahan, jika fungsi Anda sangat panjang, Anda mungkin ingin melihat hanya perubahan yang telah diperkenalkan oleh berbagai komit, bukan seluruh baris fungsi, termasuk yang tidak dimodifikasi, untuk setiap komit yang mungkin hanya menyentuh satu dari baris ini. Seperti orang biasa diff.

Biasanya git logdapat melihat perbedaan dengan -p, tetapi ini tidak berfungsi -L. Jadi, Anda harus grep git log -Lmenunjukkan hanya baris yang terlibat dan header commit / files untuk mengontekstualisasikannya. Triknya di sini adalah mencocokkan hanya garis berwarna terminal, menambahkan --colorsakelar, dengan regex. Akhirnya:

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

Perhatikan bahwa ^[harus aktual, literal ^[. Anda dapat mengetiknya dengan menekan ^ V ^ [di bash, yaitu Ctrl+ V, Ctrl+ [. Referensi di sini .

Juga -3saklar terakhir , memungkinkan untuk mencetak 3 baris konteks keluaran, sebelum dan sesudah setiap baris yang cocok. Anda mungkin ingin menyesuaikannya dengan kebutuhan Anda.

Hazzard17
sumber
2

git menyalahkan menunjukkan kepada Anda siapa yang terakhir mengubah setiap baris file; Anda dapat menentukan baris yang akan diperiksa untuk menghindari histori baris di luar fungsi Anda.

Akan
sumber
4
dengan git gui blameAnda dapat menavigasi revisi lama.
Vi.
0
  1. Tampilkan riwayat fungsi dengan git log -L :<funcname>:<file>seperti yang ditunjukkan dalam jawaban eckes dan git doc

    Jika hal itu menunjukkan tidak ada, lihat Mendefinisikan kustom sepotong-header untuk menambahkan sesuatu seperti *.java diff=javake .gitattributes file yang mendukung bahasa Anda.

  2. Tampilkan riwayat fungsi antara komit dengan git log commit1..commit2 -L :functionName:filePath

  3. Tampilkan riwayat fungsi yang kelebihan beban (mungkin ada banyak fungsi dengan nama yang sama, tetapi dengan parameter berbeda) dengan git log -L :sum\(double:filepath

Kris Roofe
sumber