Eu li alguns artigos dizendo que isso git bisect
é incrível. No entanto, eu não sou um falante nativo e não consigo entender por que é incrível.
Alguém poderia demonstrar com algum exemplo de código:
- Como usá-lo?
- É como
svn blame
?
Eu li alguns artigos dizendo que isso git bisect
é incrível. No entanto, eu não sou um falante nativo e não consigo entender por que é incrível.
Alguém poderia demonstrar com algum exemplo de código:
svn blame
?Respostas:
A idéia por trás disso git bisect
é realizar uma pesquisa binária no histórico para encontrar uma regressão específica. Imagine que você tem o seguinte histórico de desenvolvimento:
... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current
Você sabe que seu programa não está funcionando corretamente na current
revisão e que estava funcionando na revisão 0
. Assim, a regressão foi provavelmente introduzido em um dos commits 1
, 2
, 3
, 4
, 5
, current
.
Você pode tentar verificar cada commit, compilar, verificar se a regressão está presente ou não. Se houver um grande número de confirmações, isso pode levar um longo tempo. Esta é uma pesquisa linear. Podemos fazer melhor fazendo uma pesquisa binária. É isso que o git bisect
comando faz. Em cada etapa, ele tenta reduzir pela metade o número de revisões potencialmente ruins.
Você usará o comando assim:
$ 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
Após este comando, git
fará o checkout de uma confirmação. No nosso caso, será confirmado 3
. Você precisa criar seu programa e verificar se a regressão está presente ou não. Você também precisará informar git
o status dessa revisão git bisect bad
se a regressão estiver presente ou git bisect good
se não estiver.
Vamos supor que a regressão foi introduzida no commit 4
. Então a regressão não está presente nesta revisão, e dizemos a ela git
.
$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5
Ele fará o checkout de outro commit. De qualquer 4
ou 5
(como há apenas dois commits). Vamos supor que escolheu 5
. Após uma construção, testamos o programa e vemos que a regressão está presente. Em seguida, dizemos para git
:
$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4
Testamos a última revisão 4
,. E como é a pessoa que introduziu a regressão, dizemos a ela git
:
$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >
Nesta situação simples, só tivemos de teste 3 versões ( 3
, 4
, 5
) em vez de 4 ( 1
, 2
, 3
, 4
). Esta é uma pequena vitória, mas é porque nossa história é muito pequena. Se o intervalo de pesquisa for de N confirmações, devemos esperar 1 + log2 N confirma com, em git bisect
vez de aproximadamente N / 2 confirma com uma pesquisa linear.
Depois de encontrar o commit que introduziu a regressão, você pode estudá-lo para encontrar o problema. Feito isso, você git bisect reset
coloca tudo de volta ao estado original antes de usar o git bisect
comando.
git bisect bad <rev> [<rev>...]
para marcar revisões específicas como ruins (ou boas git bisect good <rev> [<rev>...]
). rev
pode ser qualquer identificador de revisão como um nome de filial, uma marca, um commit de hash (ou prefixo único de cometer de hash), ...
git bisect reset
para colocar tudo de volta no commit recente
git bisect run
bissecção automáticaSe você tiver um ./test
script automatizado com o status de saída 0, se o teste for bom, poderá encontrar automaticamente o erro com 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
Isso supõe, é claro, que, se o script de teste ./test
for rastreado pelo git, ele não desaparecerá em algum commit anterior durante a bissecção.
Descobri que muitas vezes você pode se safar apenas copiando o script in-tree da árvore e, possivelmente, jogando com PATH
variáveis -like, e executando-o a partir daí.
Obviamente, se a infraestrutura de teste da qual test
depende as confirmações mais antigas, não há solução e você terá que fazer as coisas manualmente, decidindo como testar as confirmações uma a uma.
Descobri, no entanto, que o uso dessa automação geralmente funciona e pode economizar muito tempo em testes mais lentos em sua lista de tarefas, em que você pode deixá-lo executar da noite para o dia e possivelmente ter seu bug identificado na manhã seguinte, vale a pena. a tentativa.
Permaneça no primeiro commit com falha após a divisão em vez de voltar para master
:
git bisect reset HEAD
start
+ inicial bad
e good
de uma só vez:
git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~
é o mesmo que:
git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT
Veja o que foi testado até agora (por manual good
e bad
ou run
):
git bisect log
Saída de amostra:
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
Mostre boas e más referências no git log para obter uma noção melhor do tempo:
git log --decorate --pretty=fuller --simplify-by-decoration master
Isso mostra apenas confirmações com uma referência correspondente, o que reduz muito o ruído, mas inclui referências do tipo geradas automaticamente:
refs/bisect/good*
refs/bisect/bad*
que nos dizem quais confirmações marcamos como boas ou ruins.
Considere este repositório de teste se quiser brincar com o comando.
As vezes:
Nesses casos, por exemplo, supondo que a falha sempre ocorra em 5 segundos, e se tivermos preguiça de tornar o teste mais específico como realmente deveríamos, podemos usar timeout
como em:
#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
exit 1
fi
Isso funciona desde que timeout
sai 124
enquanto a falha de test-command
saídas 1
.
git bisect run
é um pouco exigente quanto aos status de saída:
qualquer coisa acima de 127 faz com que a bissecção falhe com algo como:
git bisect run failed:
exit code 134 from '../test -aa' is < 0 or >= 128
Em particular, um C assert(0)
leva a SIGABRT
e sai com o status 134, muito irritante.
125 é mágico e faz com que a corrida seja pulada git bisect skip
.
A intenção disso é ajudar a pular compilações quebradas devido a razões não relacionadas.
Veja man git-bisect
para os detalhes.
Então você pode querer usar algo como:
#!/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"
Testado no git 2.16.1.
test_script
conjunto de testes modular + bem criado e execute-o a partir do arquivo separado durante a divisão. Ao corrigir, mescle o teste no conjunto de testes principal.
bisect run
especialmente útil quando o teste demora muito para terminar e tenho certeza de que o sistema de teste não será interrompido. Dessa forma, posso deixá-lo em execução em segundo plano, ou da noite para o dia, se consumir muitos recursos, sem perder o tempo de troca de contexto do cérebro.
$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>
Bisecting: X revisions left to test after this (roughly Y steps)
O problema ainda existe?
$ git bisect bad
$ git bisect good
<abcdef> is the first bad commit
git bisect reset
git bisect good
para passar para a próxima confirmação.
Apenas para adicionar mais um ponto:
Podemos especificar um nome de arquivo ou caminho para o git bisect start
caso de sabermos que o bug veio de arquivos específicos. Por exemplo, suponha que soubéssemos que as mudanças que causaram a regressão estavam no diretório com / workingDir, podemos executar. git bisect start com/workingDir
Isso significa que apenas as confirmações que alteraram o conteúdo desse diretório serão verificadas e isso tornará as coisas ainda mais rápidas.
Além disso, se for difícil dizer se um commit específico é bom ou ruim, você pode executar git bisect skip
, o que o ignorará. Dado que existem outras confirmações suficientes, o git bisect usará outra para restringir a pesquisa.
$ git bisect ..
basicamente uma ferramenta Git para depuração . O 'Git Bisect' debuga ao passar pelas confirmações anteriores desde a sua última confirmação de trabalho (conhecida). Ele usa pesquisa binária para passar por todos esses commits, para chegar ao que introduziu a regressão / bug.
$ git bisect start
# Iniciando a divisão
$ git bisect bad
# afirmando que o commit atual (v1.5) tem o ponto de regressão / configuração 'ruim'
$ git bisect good v1.0
# mencionar o último bom commit de trabalho (sem regressão)
Essa menção de pontos 'ruins' e 'bons' ajudará o git bisect (pesquisa binária) a escolher o elemento do meio (commit v1.3). Se a regressão estiver presente na confirmação v1.3, você a definirá como o novo ponto 'ruim' (ie - Bom -> v1.0 e Ruim -> v1.3 )
$ git bisect bad
ou da mesma forma, se o commit v1.3 estiver livre de erros, você o definirá como o novo 'ponto positivo', ou seja (* Bom -> v1.3 e Ruim -> v1.6)
$ git bisect good
Nota: os termos good
e bad
não são os únicos que você pode usar para marcar um commit com ou sem uma determinada propriedade.
O Git 2.7 (quarto trimestre de 2015) introduziu novas git bisect
opções.
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
Com a adição de documentação:
Às vezes, você não está procurando a confirmação que introduziu uma quebra, mas sim uma confirmação que causou uma alteração entre algum outro estado "antigo" e "novo" .
Por exemplo, você pode estar procurando a confirmação que introduziu uma correção específica.
Ou você pode estar procurando o primeiro commit no qual os nomes de arquivos do código-fonte foram finalmente convertidos no padrão de nomes da sua empresa. Como queiras.Nesses casos, pode ser muito confuso usar os termos "bom" e "ruim" para se referir a "o estado antes da mudança" e "o estado após a mudança".
Portanto, você pode usar os termos "
old
" e "new
", respectivamente, no lugar de "good
" e "bad
".
(Mas observe que você não pode misturar "good
" e "bad
" com "old
" e "new
" em uma única sessão.)Nesse uso mais geral, você fornece
git bisect
um "new
" commit com alguma propriedade e um "old
" commit que não possui essa propriedade.Sempre que
git bisect
fizer check-out de um commit, você testa se esse commit possui a propriedade:
Se houver, marque o commit como "new
"; caso contrário, marque-o como "old
".Quando a bisseção for concluída,
git bisect
informará qual confirmação introduziu a propriedade.
Consulte commit 06e6a74 , commit 21b55e3 , commit fe67687 (29 de junho de 2015) por Matthieu Moy ( moy
) .
Veja commit 21e5cfd (29 de junho de 2015) por Antoine Delaite ( CanardChouChinois
) .
(Mesclado por Junio C Hamano - gitster
- na confirmação 22dd6eb , 05 de outubro de 2015)