Git Bisect nasıl kullanılır?


438

Bunun git bisectharika olduğunu söyleyen bazı makaleler okudum . Ancak, ana dili İngilizce değilim ve neden harika olduğunu anlayamıyorum.

Birisi lütfen bazı kod örneği ile gösterebilir:

  1. Bu nasıl kullanılır?
  2. Aynen öyle svn blamemi?

@ 01: Git kitabının dediği gibi: projenin tarihi boyunca kaba kuvvetli bir arama yapın .
Eckes

7
Öyle değil /// brute :-), ikili arama kullanır.
cojocar

"git blame", "svn blame" ile benzerdir. "git bisect" tamamen farklı bir şey
William Pursell

Değer için Pro Pro'da bisect'in iyi bir açıklaması var . Sylvain'in cevabı buna iyi bir atış. Tüm bunlara baktıktan sonra hala anlamadıysanız, daha spesifik bir soru sormanızı öneririm. Genel sorular genel cevapları doğurur.
Cascabel

Yanıtlar:


655

Bunun arkasındaki fikir git bisect, tarihte belirli bir gerileme bulmak için ikili bir arama yapmaktır. Aşağıdaki geliştirme geçmişine sahip olduğunuzu düşünün:

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

Programınızın currentrevizyonda düzgün çalışmadığını ve revizyonda çalıştığını biliyorsunuz 0. Regresyon olasılıkla kaydedilmesini birinde tanıtıldı Yani 1, 2, 3, 4, 5, current.

Her bir taahhüdü kontrol etmeyi, inşa etmeyi, regresyonun var olup olmadığını kontrol etmeyi deneyebilirsiniz. Çok sayıda taahhüt varsa, bu uzun zaman alabilir. Bu doğrusal bir aramadır. İkili arama yaparak daha iyisini yapabiliriz. Bu ne git bisectkomut yok. Her adımda potansiyel olarak kötü olan revizyon sayısını yarı yarıya azaltmaya çalışır.

Komutu şu şekilde kullanacaksınız:

$ 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

Bu komuttan sonra, gitbir taahhütte bulunulur. Bizim durumumuzda, taahhüt edilecek 3. Programınızı oluşturmanız ve regresyonun mevcut olup olmadığını kontrol etmeniz gerekir. gitBu düzeltmenin durumunu git bisect bad, regresyon git bisect goodvarsa veya yoksa da söylemeniz gerekir .

Diyelim ki regresyon tamamlandı 4. O zaman bu revizyonda regresyon mevcut değildir ve bunu söyleriz git.

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

Daha sonra başka bir taahhütte bulunacaktır. Ya 4ya 5(sadece iki taahhüt olduğu için). Diyelim ki onu aldım 5. Bir derlemeden sonra programı test ediyoruz ve regresyonun mevcut olduğunu görüyoruz. Daha sonra şunu söyleriz git:

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

Son revizyonu test ediyoruz 4. Ve regresyonu başlatan kişi olduğu için şunu söylüyoruz git:

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

Bu basit durumda, sadece teste 3 sürümleri vardı ( 3, 4, 5) yerine 4 ( 1, 2, 3, 4). Bu küçük bir galibiyet, ama bunun nedeni tarihimizin çok küçük olması. Arama aralığı N olarak ayarlanmışsa, git bisectkabaca N / 2'nin doğrusal bir arama ile yapması yerine, 1 + log2 N'nin test etmesini beklemeliyiz .

Regresyonu başlatan taahhüdü bulduktan sonra, sorunu bulmak için çalışabilirsiniz. Bu yapıldıktan sonra, komutu git bisect resetkullanmadan önce her şeyi orijinal durumuna geri koymak için git bisectkullanın.


5
Burada karşıt olacağım, bu bisect'in iyi bir açıklaması ama gerçekten onu kullanmama yardım etmiyor. Özellikle iyi bir taahhüt bulmayı başardığım ve şu an bu daldayım. Bu konumdan bu açıklama hiç yardımcı değildir. Kötü dalı kontrol etmeden nasıl belirtebilirim, örneğin
PandaWood

4
git bisect bad <rev> [<rev>...]Belirli düzeltmeleri kötü (veya iyi git bisect good <rev> [<rev>...]) olarak işaretlemek için kullanabilirsiniz . revbir şube adı, bir etiket, bir karma karması (veya karma karmasının benzersiz öneki), ... gibi herhangi bir düzeltme tanımlayıcısı olabilir ...
Sylvain Defresne

39
... ve işiniz bittiğinde, git bisect resether şeyi son
taahhüde

17
Yığında en müthiş cevaplardan biri. Çok iyi belden kırılmış. Bu süreci yıllardır manuel olarak yapıyorum, sadece iyi ve kötü bir taahhüt arasında rastgele bir yarım nokta seçiyorum, sonra tekrar iyi ve kötü olup olmadığına bağlı olarak iyi ve kötü arasında bir nokta seçiyorum. Her zaman kıçından büyük bir acı oldu ve bu git subcommand'ı bugüne kadar hiç duymamıştım ... hahaha
Chev

3
@Nemoden, evet, olabilir, temelde her tür proje için yararlı olabilir. Sadece "test yapmak" adımı yerine "web sitesini dağıtmak ve sorunu yeniden oluşturmak" yerine
geçmelisiniz

159

git bisect run otomatik ikiye bölme

./testTest tamamsa 0 çıkış durumuna sahip otomatik bir komut dosyanız varsa, hatayı otomatik olarak şu şekilde bulabilirsiniz 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

Bu, elbette, test komut dosyası ./testgit izlenirse, ikiye ayırma sırasında daha önceki bazı taahhütlerde kaybolmadığını varsayar .

Çok sık, sadece ağaç içi komut dosyasını ağaçtan kopyalayarak ve muhtemelen- PATHbenzeri değişkenlerle oynayarak ve oradan çalıştırarak kurtulabileceğinizi gördüm .

Tabii ki, testdaha eski taahhütlere bağlı olan test altyapısı varsa, çözüm yoktur ve işleri nasıl yapacağınızı tek tek nasıl yapacağınıza karar vererek işleri manuel olarak yapmanız gerekecektir.

Bununla birlikte, bu otomasyonu kullanmanın genellikle işe yaradığını ve görevlerin birikiminde yatan daha yavaş testler için büyük bir zaman tasarrufu sağlayabildiğini gördüm, burada bir gecede çalışmasına izin verebilirsiniz ve muhtemelen hatalarınızı ertesi sabah tanımlamanıza değer deneyin.

Diğer ipuçları

İki noktadan sonra yerine geri dönmek yerine ilk başarısızlık taahhüdünde kalın master:

git bisect reset HEAD

start+ ilk badve goodbir seferde:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~

aynıdır:

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

Şimdiye kadar nelerin test edildiğini görün (manuel goodve badveya run):

git bisect log

Örnek çıktı:

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

Daha iyi bir zaman kavramı elde etmek için git günlüğünde iyi ve kötü referansları göster:

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

Bu sadece karşılık gelen bir ref ile taahhütleri gösterir, bu da gürültüyü büyük ölçüde azaltır, ancak türün otomatik olarak oluşturulmuş ref'lerini içerir:

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

hangi taahhütleri iyi veya kötü olarak işaretlediğimizi söyler.

Komutla oynamak istiyorsanız bu test deposunu düşünün .

Başarısızlık hızlı, başarı yavaş

Ara sıra:

  • başarısızlık hızlı olur, örneğin ilk testlerden biri kesilir
  • Başarı biraz zaman alır, örneğin kırık test geçer ve umursadığımız diğer tüm testler

Bu gibi durumlarda, örneğin hatanın her zaman 5 saniye ile gerçekleştiğini varsayarsak ve testi gerçekten olması gerektiği gibi daha spesifik hale getirmek için tembel kalırsak, aşağıdaki gibi kullanabiliriz timeout:

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

Bu yana çalışan timeoutçıkışların 124başarısızlığı ise test-commandçıkışlarında 1.

Sihirli çıkış durumları

git bisect run çıkış durumları hakkında biraz seçici:

  • 127'nin üzerindeki herhangi bir şey, bölünmeyi aşağıdaki gibi bir şeyle başarısız kılar:

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

    Özellikle, bir C assert(0)bir SIGABRT134'e yol açar ve çok rahatsız edici olan durum 134 ile çıkar.

  • 125 büyülüdür ve koşuyu atlar git bisect skip.

    Bunun amacı, ilgisiz nedenlerden dolayı kırık yapıların atlanmasına yardımcı olmaktır.

Ayrıntılar man git-bisectiçin bakınız.

Yani şöyle bir şey kullanmak isteyebilirsiniz:

#!/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"

Git 2.16.1'de test edilmiştir.


7
Git, önceki / kötü bir düzeltmeye (yeni yazılmış testinize sahip olmayan) geri dönerken / ikiye bölerken yeni testinizin kaybolmasını nasıl engelleyebilir?
thebjorn

8
@thebjorn bir noktanız var: bildiğim kadarıyla, test PATH'de harici bir yürütülebilir dosyada veya repodaki izlenmemiş bir dosyada olmalıdır. Birçok durumda bu mümkündür: testi ayrı bir dosyaya koyun, iyi hazırlanmış test_script+ modüler test takımıyla gerekli test plakasını ekleyin ve ikiye ayırırken ayrı dosyadan çalıştırın. Düzelttiğinizde, testi ana test paketiyle birleştirin.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

1
@CiroSantilli 六四 事件 法轮功 纳米比亚 威 视 Üzgünüm, düzenlemenizi geri aldım, çünkü yeni başlayanlar için daha açıklayıcı hissediyorum ve sadece cevabınıza başka bir nokta ekledim (sizinki gibi tam bir cevap değil - bu yüzden, nokta eklemek için)
Nicks

1
'Git bisect run' ile yanlış gitmenin birçok yolu vardır - örneğin, iyi bir taahhüdün kötü birleşme ile nasıl tersine çevrildiğini görmek. İçeri, dışarı, tekrar içeri ve dışarı gelir ve sadece son "dışarı" kötüdür. Ancak, her zaman manuel bir 'git bisect' yapabilirsiniz. Çünkü bu bir ikilidir, sadece birkaç adım alır - örneğin 10 adımda 1024 işlem yapılır.
combinatorist

1
@combinatorist başarısız olabilir haklısın. bisect runTestin tamamlanması uzun zaman aldığında özellikle yararlı buluyorum ve test sisteminin kırılmayacağından eminim. Bu şekilde, onu herhangi bir beyin bağlamı geçiş zamanını kaybetmeden arka planda çalıştırabilir veya çok fazla kaynak alırsa bir gecede bırakabilirim.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

124

TL; DR

Başlat:

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

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

Tekrar et:

Sorun hala var mı?

  • Evet: $ git bisect bad
  • Hayır: $ git bisect good

Sonuç:

<abcdef> is the first bad commit

Tamamlandığında:

git bisect reset

3
Git deponuzun kökünde olduğunuzdan emin olun, yoksa garip bir "Bu komutu çalışma ağacının üst düzeyinden çalıştırmanız gerekir." hata.
PR Whitehead

Ben hata mevcut değildi bu yüzden benim HEAD kötü git ve ilk taahhüt iyi git yaptı. Şimdi ne yapmalı? hata mevcut değil git bisect sonraki taahhüt taşımak için iyi?
Gobliins

@ Gobliins Hata mevcut değilse, bir git bisect goodsonraki işleme geçmek için düzeltin .
Geoffrey Hale

40

Sadece bir nokta daha eklemek için:

git bisect startHatanın belirli dosyalardan geldiğini bilmemiz durumunda bir dosya adı veya yolu belirleyebiliriz . Örneğin, regresyona neden olan değişikliklerin com / workingDir dizininde olduğunu bildiğimizi varsayalım, o zaman çalıştırabiliriz git bisect start com/workingDir.

Ayrıca, belirli bir taahhüdün iyi mi kötü mü olduğunu söylemek zorsa, çalıştırabilirsiniz git bisect skip, bu da görmezden gelir. Yeterli başka taahhüt olduğu düşünüldüğünde, git bisect aramayı daraltmak için bir başkasını kullanacaktır.


15

$ git bisect ..temelde hata ayıklama için bir Git aracı . 'Git Bisect' son (bilinen) çalışma taahhüdünüzden bu yana önceki taahhütlerden geçerek hata ayıklar . Tüm bu taahhütleri atlatmak, regresyon / hatayı tanıtan birine ulaşmak için ikili arama kullanır.

$ git bisect start # Başlangıç ​​bisect

$ git bisect bad # geçerli işlemin (v1.5) regresyon / ayar 'kötü' noktasına sahip olduğunu belirterek

$ git bisect good v1.0 # son iyi çalışma taahhüdünden bahsediyor (regresyon olmadan)

'Kötü' ve 'iyi' noktalardan bahsetmek git bisect'in (ikili arama) orta elemanı seçmesine yardımcı olacaktır (v1.3 taahhüdü). Regresyon, v1.3 komutunda varsa, bunu yeni 'kötü' nokta olarak ayarlarsınız, yani ( İyi -> v1.0 ve Kötü -> v1.3 )

$ git bisect bad

veya benzer bir şekilde v1.3 komutu hatasız ise, yeni 'İyi nokta' olarak ayarlarsınız (yani * İyi -> v1.3 ve Kötü -> v1.6).

$ git bisect good

2

Not: Belirli bir mülkle veya belirli bir mülk olmadan bir taahhüdü işaretlemek için kullanabileceğiniz terimler goodve badyalnızca terimler değildir.

Git 2.7 (4. Çeyrek 2015) yeni git bisectseçenekler sundu .

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

Belgeler eklenerek:

Bazen bir kırılma getiren taahhüdü değil, başka bir "eski" durum ile "yeni" durum arasında bir değişikliğe neden olan bir taahhüdü arıyorsunuz .

Örneğin, belirli bir düzeltmeyi getiren taahhüdü arıyor olabilirsiniz.
Veya kaynak kod dosya adlarının sonunda şirketinizin adlandırma standardına dönüştürüldüğü ilk taahhüdü arıyor olabilirsiniz. Ya da her neyse.

Bu gibi durumlarda, "değişiklikten önceki durum" ve "değişiklikten sonraki durum" anlamına gelmek için "iyi" ve "kötü" terimlerini kullanmak çok kafa karıştırıcı olabilir.

Bunun yerine, aşağıdaki terimleri "kullanabilirsiniz old" ve " new" yerine sırasıyla " good" ve " bad".
(Ama "karıştırmak olamaz notu good" ve " bad" "ile old" ve " newtek bir oturumda".)

Bu daha genel kullanımda, git bisect" new" taahhüdünün bir özelliği vardır ve oldbu özelliği olmayan bir " " taahhüdü sağlarsınız.

git bisectBir taahhüdü her kontrol ettiğinde, söz konusu taahhüdün özelliği olup olmadığını test edersiniz:
Varsa, taahhüdü " new" olarak işaretleyin ; aksi halde " old" olarak işaretleyin .

İkiye bölme bittiğinde, git bisecthangi taahhüdün mülkü tanıttığını rapor edecektir.


Bkz. Taahhüt 06e6a74 , taahhüt 21b55e3 , taahhüt fe67687 (29 Haziran 2015), Matthieu Moy ( moy) .
Bakınız Antoine Delaite ( ) tarafından 21e5cfd (29 Haz 2015) taahhüdüCanardChouChinois .
(Tarafından Birleştirilmiş Junio C Hamano - gitster- içinde taahhüt 22dd6eb , 5 Ekim 2015)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.