He leído algunos artículos que dicen que git bisect
es increíble. Sin embargo, no soy un hablante nativo y no puedo entender por qué es increíble.
¿Podría alguien demostrar con algún ejemplo de código:
- ¿Cómo usarlo?
- ¿Es como
svn blame
?
He leído algunos artículos que dicen que git bisect
es increíble. Sin embargo, no soy un hablante nativo y no puedo entender por qué es increíble.
¿Podría alguien demostrar con algún ejemplo de código:
svn blame
?Respuestas:
La idea detrás git bisect
es realizar una búsqueda binaria en el historial para encontrar una regresión particular. Imagine que tiene el siguiente historial de desarrollo:
... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current
Usted sabe que su programa no funciona correctamente en la current
revisión y que estaba funcionando en la revisión 0
. Por lo que la regresión fue probablemente introducido en una de las confirmaciones 1
, 2
, 3
, 4
, 5
, current
.
Puede intentar verificar cada confirmación, compilarla, verificar si la regresión está presente o no. Si hay una gran cantidad de confirmaciones, esto puede llevar mucho tiempo. Esta es una búsqueda lineal. Podemos hacerlo mejor haciendo una búsqueda binaria. Esto es lo que hace el git bisect
comando. En cada paso, trata de reducir a la mitad el número de revisiones que son potencialmente malas.
Usarás el comando así:
$ 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
Después de este comando, git
pagará un commit. En nuestro caso, será commit 3
. Necesita construir su programa y verificar si la regresión está presente o no. También deberá indicar git
el estado de esta revisión, ya sea git bisect bad
si la regresión está presente o git bisect good
no.
Supongamos que la regresión se introdujo en commit 4
. Entonces la regresión no está presente en esta revisión, y le decimos que lo haga git
.
$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5
Luego verificará otro commit. Cualquiera 4
o 5
(ya que solo hay dos confirmaciones). Supongamos que escogió 5
. Después de una compilación, probamos el programa y vemos que la regresión está presente. Luego le decimos que git
:
$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4
Probamos la última revisión, 4
. Y como es el que introdujo la regresión, le decimos que git
:
$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >
En esta situación simple, sólo tuvimos que prueba 3 versiones ( 3
, 4
, 5
) en lugar de 4 ( 1
, 2
, 3
, 4
). Esta es una pequeña victoria, pero esto se debe a que nuestra historia es muy pequeña. Si el rango de búsqueda es de N commits, deberíamos esperar probar 1 + log2 N commits en git bisect
lugar de aproximadamente N / 2 commits con una búsqueda lineal.
Una vez que haya encontrado el commit que introdujo la regresión, puede estudiarlo para encontrar el problema. Una vez hecho esto, puede git bisect reset
volver a poner todo en el estado original antes de usar el git bisect
comando.
git bisect bad <rev> [<rev>...]
para marcar revisiones específicas como malas (o buenas con git bisect good <rev> [<rev>...]
). rev
puede ser cualquier identificador de revisión como un nombre de rama, una etiqueta, un hash de confirmación (o un prefijo único de hash de confirmación), ...
git bisect reset
para volver a poner todo en la confirmación reciente
git bisect run
bisección automáticaSi tiene un ./test
script automatizado que tiene el estado de salida 0 si la prueba es correcta, puede encontrar automáticamente el error con 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
Esto supone, por supuesto, que si el script de prueba ./test
es rastreado, no desaparece en algún commit anterior durante la bisección.
He descubierto que muy a menudo puedes escapar simplemente copiando el script del árbol fuera del árbol, y posiblemente jugando con PATH
variables similares, y ejecutándolo desde allí.
Por supuesto, si la infraestructura de prueba de la que test
depende se rompe con los commits más antiguos, entonces no hay solución, y tendrá que hacer las cosas manualmente, decidiendo cómo probar los commits uno por uno.
Sin embargo, he descubierto que el uso de esta automatización a menudo funciona, y puede ser un gran ahorro de tiempo para las pruebas más lentas que se encuentran en su cartera de tareas, donde puede dejar que se ejecute durante la noche y posiblemente identificar su error a la mañana siguiente, vale la pena. el intento
Manténgase en la primera confirmación fallida después de la bisección en lugar de volver a master
:
git bisect reset HEAD
start
+ inicial bad
y good
de una vez:
git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~
es lo mismo que:
git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT
Vea lo que se ha probado hasta ahora (por manual good
y / bad
o run
):
git bisect log
Salida de muestra:
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
Muestre referencias buenas y malas en git log para obtener una mejor noción del tiempo:
git log --decorate --pretty=fuller --simplify-by-decoration master
Esto solo muestra confirmaciones con una referencia correspondiente, lo que reduce el ruido real, pero incluye referencias de tipo autogeneradas:
refs/bisect/good*
refs/bisect/bad*
que nos dicen qué compromisos hemos marcado como buenos o malos.
Considera este repositorio de prueba si quieres jugar con el comando.
Algunas veces:
Para esos casos, por ejemplo, suponiendo que la falla siempre ocurre dentro de 5 segundos, y si somos flojos para hacer la prueba más específica como realmente deberíamos, podemos usarla timeout
como en:
#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
exit 1
fi
Esto funciona desde las timeout
salidas 124
mientras que la falla de las test-command
salidas 1
.
git bisect run
es un poco exigente con los estados de salida:
cualquier cosa por encima de 127 hace que la bisección falle con algo como:
git bisect run failed:
exit code 134 from '../test -aa' is < 0 or >= 128
En particular, una C assert(0)
conduce a SIGABRT
ay sale con el estado 134, muy molesto.
125 es mágico y hace que se salte la carrera git bisect skip
.
La intención de esto es ayudar a omitir compilaciones rotas debido a razones no relacionadas.
Ver man git-bisect
para los detalles.
Por lo tanto, es posible que desee utilizar 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"
Probado en git 2.16.1.
test_script
conjunto de pruebas bien diseñado y modular, y ejecútelo desde el archivo separado mientras biseca. Cuando corrija, combine la prueba en el conjunto de pruebas principal.
bisect run
especialmente útil cuando la prueba tarda mucho en terminar, y estoy bastante seguro de que el sistema de prueba no se romperá. De esta manera, puedo dejarlo ejecutándose en segundo plano, o de la noche a la mañana si consume demasiados recursos, sin perder el tiempo de cambio de contexto cerebral.
$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>
Bisecting: X revisions left to test after this (roughly Y steps)
El problema aún existe?
$ git bisect bad
$ git bisect good
<abcdef> is the first bad commit
git bisect reset
git bisect good
para pasar al siguiente commit.
Solo para agregar un punto más:
Podemos especificar un nombre de archivo o ruta git bisect start
en caso de que sepamos que el error proviene de archivos particulares. Por ejemplo, supongamos que sabíamos que los cambios que causaron la regresión estaban en el directorio com / workingDir, entonces podemos ejecutar git bisect start com/workingDir
Esto significa que solo se verificarán las confirmaciones que cambiaron el contenido de este directorio, y esto hace que las cosas sean aún más rápidas.
Además, si es difícil saber si un commit en particular es bueno o malo, puede ejecutarlo git bisect skip
, lo que lo ignorará. Dado que hay suficientes otras confirmaciones, git bisect usará otra para limitar la búsqueda.
$ git bisect ..
Básicamente una herramienta Git para depurar . 'Git Bisect' depura pasando por los commits anteriores desde su último commit de trabajo (conocido). Utiliza la búsqueda binaria para pasar por todas esas confirmaciones, para llegar a la que introdujo la regresión / error.
$ git bisect start
# Bisecta de inicio
$ git bisect bad
# indicando que el commit actual (v1.5) tiene el punto 'malo' de regresión / configuración
$ git bisect good v1.0
# mencionándolo como el último buen compromiso de trabajo (sin regresión)
Esta mención de los puntos 'malo' y 'bueno' ayudará a git bisect (búsqueda binaria) a elegir el elemento medio (commit v1.3). Si la regresión está allí en commit v1.3, la configurará como el nuevo punto 'malo', es decir ( Bueno -> v1.0 y Malo -> v1.3 )
$ git bisect bad
o de manera similar si el commit v1.3 está libre de errores, lo configurará como el nuevo 'Punto bueno', es decir (* Bueno -> v1.3 y Malo -> v1.6).
$ git bisect good
Nota: los términos good
y bad
no son los únicos que puede usar para marcar una confirmación con o sin una determinada propiedad.
Git 2.7 (cuarto trimestre de 2015) introdujo nuevas git bisect
opciones.
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
Con documentación agregando:
A veces no está buscando el compromiso que introdujo una ruptura, sino más bien un compromiso que causó un cambio entre algún otro estado "antiguo" y "nuevo" .
Por ejemplo, podría estar buscando la confirmación que introdujo una solución particular.
O podría estar buscando la primera confirmación en la que los nombres de los archivos de código fuente finalmente se convirtieron al estándar de nomenclatura de su empresa. O lo que sea.En tales casos, puede ser muy confuso usar los términos "bueno" y "malo" para referirse a "el estado antes del cambio" y "el estado después del cambio".
Por lo tanto, puede usar los términos "
old
" y "new
", respectivamente, en lugar de "good
" y "bad
".
(Pero tenga en cuenta que no puede mezclar "good
" y "bad
" con "old
" y "new
" en una sola sesión).En este uso más general, proporciona
git bisect
un "new
" commit tiene alguna propiedad y un "old
" commit que no tiene esa propiedad.Cada vez que
git bisect
comprueba una confirmación, prueba si esa confirmación tiene la propiedad:
si la tiene, marque la confirmación como "new
"; de lo contrario, márquelo como "old
".Cuando se realiza la bisección,
git bisect
informará qué confirmación introdujo la propiedad.
Ver commit 06e6a74 , commit 21b55e3 , commit fe67687 (29 de junio de 2015) por Matthieu Moy ( moy
) .
Ver commit 21e5cfd (29 de junio de 2015) por Antoine Delaite ( CanardChouChinois
) .
(Fusionada por Junio C Hamano - gitster
- en commit 22dd6eb , 05 oct 2015)