Bagaimana cara menggunakan git bisect?

438

Saya telah membaca beberapa artikel yang mengatakan itu git bisectluar biasa. Namun, saya bukan penutur asli dan saya tidak bisa mengerti mengapa itu luar biasa.

Bisakah seseorang mendemonstrasikan dengan beberapa contoh kode:

  1. Bagaimana cara menggunakannya?
  2. Apakah itu seperti svn blame?
IAdapter
sumber
@ 01: Seperti yang dikatakan buku git: lakukan pencarian dengan kekerasan melalui sejarah proyek .
eckes
7
Tidak begitu /// brute :-), ia menggunakan pencarian biner.
cojocar
"git menyalahkan" mirip dengan "svn menyalahkan". "git bisect" adalah hal yang sama sekali berbeda
William Pursell
Untuk apa nilainya, ada deskripsi yang baik tentang membagi dua di Pro Git juga. Jawaban Sylvain adalah kesempatan yang bagus untuk itu. Jika setelah melihat semua itu Anda masih belum mengerti, saya sarankan Anda mengajukan pertanyaan yang lebih spesifik. Pertanyaan umum menghasilkan jawaban umum.
Cascabel
4
tautan buku yang diperbarui: git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search
gdelfino

Jawaban:

655

Gagasan di belakang git bisectadalah melakukan pencarian biner dalam sejarah untuk menemukan regresi tertentu. Bayangkan Anda memiliki riwayat pengembangan berikut:

... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current

Anda tahu bahwa program Anda tidak berfungsi dengan baik di currentrevisi, dan itu berfungsi di revisi 0. Jadi regresi tersebut kemungkinan diperkenalkan di salah satu komit 1, 2, 3, 4, 5, current.

Anda dapat mencoba memeriksa setiap komit, membangunnya, memeriksa apakah regresi ada atau tidak. Jika ada banyak komitmen, ini bisa memakan waktu lama. Ini adalah pencarian linear. Kita dapat melakukan yang lebih baik dengan melakukan pencarian biner. Inilah yang dilakukan git bisectperintah. Pada setiap langkah itu mencoba untuk mengurangi jumlah revisi yang berpotensi buruk hingga setengahnya.

Anda akan menggunakan perintah seperti ini:

$ git stash save
$ git bisect start
$ git bisect bad
$ git bisect good 0
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[< ... sha ... >] 3

Setelah perintah ini, gitakan checkout komit. Dalam kasus kami, itu akan dikomit 3. Anda perlu membangun program Anda, dan memeriksa apakah regresi ada atau tidak. Anda juga harus memberi tahu gitstatus revisi ini dengan baik git bisect badjika ada regresi, atau git bisect goodjika tidak.

Misalkan regresi diperkenalkan dalam komit 4. Maka regresi tidak ada dalam revisi ini, dan kami kirim ke git.

$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5

Kemudian akan checkout komit lain. Salah satu 4atau 5(karena hanya ada dua komitmen). Anggap saja itu diambil 5. Setelah membangun kami menguji program dan melihat bahwa ada regresi. Kami kemudian kirim ke git:

$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4

Kami menguji revisi terakhir 4,. Dan karena itu yang memperkenalkan regresi, kami kirim ke git:

$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >

Dalam situasi sederhana ini, kita hanya harus uji 3 versi ( 3, 4, 5) bukan 4 ( 1, 2, 3, 4). Ini adalah kemenangan kecil, tetapi ini karena sejarah kita sangat kecil. Jika rentang pencarian komit N, kita harus menguji 1 + log2 N komit dengan git bisectbukannya N / 2 komit dengan pencarian linier.

Setelah Anda menemukan komit yang memperkenalkan regresi, Anda dapat mempelajarinya untuk menemukan masalah. Setelah ini selesai, Anda gunakan git bisect resetuntuk mengembalikan semuanya ke kondisi semula sebelum menggunakan git bisectperintah.

Sylvain Defresne
sumber
5
Saya akan contrare di sini, ini adalah penjelasan yang baik tentang dua bagian tetapi benar-benar tidak membantu saya menggunakannya. Terutama karena saya sudah berhasil menemukan komit yang baik dan saya di cabang itu sekarang. Dari posisi ini penjelasan ini sama sekali tidak membantu. Bagaimana cara menentukan cabang buruk tanpa memeriksanya, misalnya
PandaWood
4
Anda dapat menggunakan git bisect bad <rev> [<rev>...]untuk menandai revisi tertentu sebagai buruk (atau bagus dengan git bisect good <rev> [<rev>...]). revdapat berupa pengidentifikasi revisi apa pun seperti nama cabang, tag, hash komit (atau awalan unik hash komit), ...
Sylvain Defresne
39
... dan setelah Anda selesai, Anda mengetik git bisect resetuntuk mengembalikan semuanya pada komit terbaru
peetonn
17
Salah satu jawaban paling mengagumkan di stack. Diartikulasikan dengan sangat baik. Saya telah melakukan proses ini secara manual selama bertahun-tahun, hanya memilih titik setengah yang sewenang-wenang antara komitmen yang baik dan yang buruk, kemudian lagi antara yang satu dan yang baik / buruk tergantung pada apakah itu baik / buruk itu sendiri. Itu selalu menjadi sakit besar di pantat dan aku belum pernah mendengar tentang perintah sub git ini sampai hari ini ... hahaha
Chev
3
@Nemoden, ya, itu bisa, pada dasarnya itu bisa berguna untuk semua jenis proyek. Anda hanya perlu mengganti langkah "uji coba" dengan "menyebarkan situs web dan mereproduksi masalah"
alex.b
159

git bisect run membagi dua otomatis

Jika Anda memiliki ./testskrip otomatis yang memiliki status keluar 0 jika pengujian OK, Anda dapat secara otomatis menemukan bug dengan bisect run:

git checkout KNOWN_BAD_COMMIT
git bisect start

# Confirm that our test script is correct, and fails on the bad commit.
./test
# Should output != 0.
echo $?
# Tell Git that the current commit is bad.
git bisect bad

# Same for a known good commit in the past.
git checkout KNOWN_GOOD_COMMIT
./test
# Should output 0.
echo $?
# After this, git automatically checks out to the commit
# in the middle of KNOWN_BAD_COMMIT and KNOWN_GOOD_COMMIT.
git bisect good

# Bisect automatically all the way to the first bad or last good rev.
git bisect run ./test

# End the bisect operation and checkout to master again.
git bisect reset

Tentu saja ini mengandaikan bahwa jika script tes ./testdilacak git, bahwa itu tidak hilang pada beberapa komit sebelumnya selama pembagian dua.

Saya telah menemukan bahwa sangat sering Anda bisa lolos hanya dengan menyalin skrip in-tree keluar dari pohon, dan mungkin bermain dengan PATHvariabel-like, dan menjalankannya dari sana.

Tentu saja, jika infrastruktur pengujian testbergantung pada komit yang lebih lama, maka tidak ada solusi, dan Anda harus melakukan hal-hal secara manual, memutuskan bagaimana menguji komit satu per satu.

Namun saya telah menemukan bahwa menggunakan otomasi ini sering berhasil, dan dapat menjadi penghemat waktu yang sangat besar untuk tes yang lebih lambat di tumpukan tugas Anda, di mana Anda dapat membiarkannya berjalan dalam semalam, dan mungkin bug Anda diidentifikasi pada pagi berikutnya, ada baiknya mencoba.

Lebih banyak tips

Tetap pada komit pertama yang gagal setelah membagi dua alih-alih kembali ke master:

git bisect reset HEAD

start+ awal baddan goodsekali jalan:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

sama dengan:

git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT

Lihat apa yang telah diuji sejauh ini (secara manual gooddan badatau run):

git bisect log

Output sampel:

git bisect log
git bisect start
# bad: [00b9fcdbe7e7d2579f212b51342f4d605e53253d] 9
git bisect bad 00b9fcdbe7e7d2579f212b51342f4d605e53253d
# good: [db7ec3d602db2d994fe981c0da55b7b85ca62566] 0
git bisect good db7ec3d602db2d994fe981c0da55b7b85ca62566
# good: [2461cd8ce8d3d1367ddb036c8f715c7b896397a5] 4
git bisect good 2461cd8ce8d3d1367ddb036c8f715c7b896397a5
# good: [8fbab5a3b44fd469a2da3830dac5c4c1358a87a0] 6
git bisect good 8fbab5a3b44fd469a2da3830dac5c4c1358a87a0
# bad: [dd2c05e71c246f9bcbd2fbe81deabf826c54be23] 8
git bisect bad dd2c05e71c246f9bcbd2fbe81deabf826c54be23
# bad: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05] 7
git bisect bad c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05
# first bad commit: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c0

Tunjukkan referensi yang baik dan buruk pada log git untuk mendapatkan gagasan waktu yang lebih baik:

git log --decorate --pretty=fuller --simplify-by-decoration master

Ini hanya menunjukkan komit dengan referensi yang sesuai, yang mengurangi kebisingan kebisingan, tetapi tidak menyertakan ref tipe autogenerated:

refs/bisect/good*
refs/bisect/bad*

yang memberi tahu kami mana yang ditandai sebagai baik atau buruk.

Pertimbangkan repo tes ini jika Anda ingin bermain-main dengan perintah.

Kegagalan cepat, kesuksesan lambat

Terkadang:

  • kegagalan terjadi dengan cepat, misalnya salah satu tes pertama rusak
  • keberhasilan butuh waktu, misalnya tes yang gagal lulus, dan semua tes lain yang tidak kita ikuti

Untuk kasus-kasus tersebut, misalkan anggap kegagalan selalu terjadi dalam 5 detik, dan jika kita malas membuat tes lebih spesifik seperti yang seharusnya, kita dapat menggunakan timeoutseperti pada:

#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
  exit 1
fi

Ini berfungsi sejak timeoutkeluar 124sementara kegagalan test-commandkeluar 1.

Status keluar ajaib

git bisect run agak pilih-pilih tentang status keluar:

  • apa pun di atas 127 membuat pembelahan dua gagal dengan sesuatu seperti:

    git bisect run failed:
    exit code 134 from '../test -aa' is < 0 or >= 128
    

    Secara khusus, huruf C assert(0)mengarah ke a SIGABRTdan keluar dengan status 134, sangat menjengkelkan.

  • 125 ajaib dan membuat lari dilewati git bisect skip.

    Maksud dari ini adalah untuk membantu melewati bangunan yang rusak karena alasan yang tidak terkait.

Lihat man git-bisectdetailnya.

Jadi, Anda mungkin ingin menggunakan sesuatu seperti:

#!/usr/bin/env bash
set -eu
./build
status=0
./actual-test-command || status=$?
if [ "$status" -eq 125 ] || [ "$status" -gt 127 ]; then
  status=1
fi
exit "$status"

Diuji pada git 2.16.1.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
7
Bagaimana git tahu agar tes baru Anda tidak hilang ketika mengembalikan / membagi dua kembali ke revisi sebelumnya / buruk (yang tidak memiliki tes tertulis baru Anda)?
thebjorn
8
@thebjorn Anda ada benarnya: sejauh yang saya tahu, baik tes harus pada executable eksternal di PATH, atau dalam file yang tidak terlacak dalam repo. Dalam banyak kasus ini dimungkinkan: letakkan tes pada file yang terpisah, sertakan pelat uji yang diperlukan dengan test_scripttest suite modular + yang dibuat dengan baik , dan jalankan dari file terpisah saat membagi dua. Saat Anda memperbaikinya, gabungkan tes ke dalam test suite utama.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 六四 事件 法轮功 纳米比亚 威 视 Maaf, saya memutar kembali hasil edit Anda di bawah, karena saya merasa ini lebih jelas untuk pemula dan hanya menambahkan poin lebih lanjut untuk jawaban Anda (bukan jawaban yang tepat seperti milik Anda - jadi sebutkan itu, ini untuk menambah poin)
Nicks
1
Ada banyak cara salah dengan 'git bisect run' - misalnya melihat bagaimana komit yang baik dibalik oleh penggabungan yang buruk. Itu masuk, keluar, kembali masuk dan keluar lagi dan hanya "keluar" terakhir yang buruk. Namun, Anda selalu dapat melakukan manual 'git bisect'. Karena itu dua bagian, hanya dibutuhkan beberapa langkah - misalnya 1024 komit dalam 10 langkah.
Combinatorist
1
@combinatorist Anda benar mungkin gagal. Saya merasa bisect runsangat berguna ketika ujian membutuhkan waktu lama untuk menyelesaikannya, dan saya cukup yakin bahwa sistem pengujian tidak akan rusak. Dengan cara ini, saya bisa membiarkannya berjalan di latar belakang, atau bermalam jika menghabiskan terlalu banyak sumber daya, tanpa kehilangan waktu pergantian konteks otak.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
124

TL; DR

Mulailah:

$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>

Bisecting: X revisions left to test after this (roughly Y steps)

Ulang:

Masih ada masalah?

  • Iya: $ git bisect bad
  • Tidak: $ git bisect good

Hasil:

<abcdef> is the first bad commit

Ketika selesai:

git bisect reset
Geoffrey Hale
sumber
3
Pastikan Anda berada di root git repo Anda, atau Anda akan mendapatkan aneh "Anda harus menjalankan perintah ini dari tingkat atas pohon yang bekerja." kesalahan.
PR Whitehead
Jadi saya melakukan git buruk pada KEPALA saya dan git bagus pada komit pertama, ketika kesalahan tidak ada. Lalu apa yang harus dilakukan selanjutnya? ketika bug tidak ada git bisect bagus untuk pindah ke commit selanjutnya?
Gobliins
@Gobliins Ketika bug tidak ada, perbaiki, git bisect gooduntuk pindah ke komit berikutnya.
Geoffrey Hale
40

Hanya untuk menambahkan poin lebih lanjut:

Kita dapat menentukan nama file atau jalur untuk git bisect startberjaga-jaga jika kita tahu bahwa bug tersebut berasal dari file tertentu. Sebagai contoh, Misalkan kita tahu bahwa perubahan yang menyebabkan regresi berada di direktori com / workingDir maka kita dapat menjalankan git bisect start com/workingDirIni berarti bahwa hanya komit yang mengubah isi direktori ini yang akan diperiksa, dan ini membuat segalanya menjadi lebih cepat.

Juga, Jika sulit untuk mengetahui apakah komit tertentu baik atau buruk, Anda dapat menjalankan git bisect skip, yang akan mengabaikannya. Mengingat ada cukup banyak komitmen lain, git bisect akan menggunakan yang lain untuk mempersempit pencarian.

Torehan
sumber
15

$ git bisect ..pada dasarnya alat Git untuk debugging . Debit 'Git Bisect' dengan menelusuri komitmen sebelumnya sejak komit kerja terakhir Anda (yang diketahui). Ini menggunakan pencarian biner untuk melalui semua komitmen tersebut, untuk sampai ke yang memperkenalkan regresi / bug.

$ git bisect start # Mulai membagi dua

$ git bisect bad # menyatakan bahwa komit saat ini (v1.5) memiliki titik regresi / pengaturan 'buruk'

$ git bisect good v1.0 # menyebutkannya komit kerja bagus terakhir (tanpa regresi)

Penyebutan poin 'buruk' dan 'baik' ini akan membantu git membagi dua (pencarian biner) memilih elemen tengah (komit v1.3). Jika regresi ada di komit v1.3, Anda akan menetapkannya sebagai titik 'buruk' baru yaitu ( Baik -> v1.0 dan Buruk -> v1.3 )

$ git bisect bad

atau sama jika komit v1.3 bebas bug Anda akan menetapkannya sebagai 'Good point' yang baru yaitu (* Baik -> v1.3 dan Buruk -> v1.6).

$ git bisect good
Nabeel Ahmed
sumber
2

Catatan: syarat gooddan badbukan satu-satunya yang dapat Anda gunakan untuk menandai komit dengan atau tanpa properti tertentu.

Git 2.7 (Q4 2015) memperkenalkan git bisectopsi baru .

 git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
                  [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]

Dengan menambah dokumentasi:

Kadang-kadang Anda tidak mencari komit yang memperkenalkan kerusakan, tetapi untuk komit yang menyebabkan perubahan antara beberapa negara "lama" dan negara "baru" lainnya .

Misalnya, Anda mungkin mencari komit yang memperkenalkan perbaikan tertentu.
Atau Anda mungkin mencari komit pertama di mana nama file kode sumber akhirnya dikonversi ke standar penamaan perusahaan Anda. Atau terserah.

Dalam kasus seperti itu bisa sangat membingungkan untuk menggunakan istilah "baik" dan "buruk" untuk merujuk pada "negara sebelum perubahan" dan "negara setelah perubahan".

Jadi alih-alih, Anda dapat menggunakan istilah " old" dan " new", masing-masing, sebagai ganti " good" dan " bad".
(Tetapi perhatikan bahwa Anda tidak dapat mencampur " good" dan " bad" dengan " old" dan " new" dalam satu sesi.)

Dalam penggunaan yang lebih umum ini, Anda memberikan git bisect" new" commit memiliki beberapa properti dan " old" komit yang tidak memiliki properti itu.

Setiap kali git bisectmemeriksa komit, Anda menguji apakah komit itu memiliki properti:
Jika ya, tandai komit sebagai " new"; jika tidak, tandai sebagai " old".

Ketika pembelahan selesai, git bisectakan melaporkan yang berkomitmen memperkenalkan properti.


Lihat komit 06e6a74 , komit 21b55e3 , komit fe67687 (29 Jun 2015) oleh Matthieu Moy ( moy) .
Lihat komit 21e5cfd (29 Jun 2015) oleh Antoine Delaite ( CanardChouChinois) .
(Digabung oleh Junio ​​C Hamano - gitster- dalam komit 22dd6eb , 05 Okt 2015)

VONC
sumber