¿Cómo puedo saber si un commit es descendiente de otro commit?

146

Con Git, ¿cómo puedo saber si un commit en mi rama es descendiente de otro commit?

engie
fuente
2
La misma pregunta hizo lo contrario: stackoverflow.com/questions/18345157/…
Chris Cleeland
11
¿Podría cambiar su respuesta aceptada? A la mayoría le gusta la --is-ancestorsolución.
Robert Siemer

Respuestas:

51

Si desea verificar esto mediante programación (por ejemplo, en el script), puede verificar si git merge-base A Bes igual a git rev-parse --verify A(entonces A es accesible desde B), o si lo es git rev-parse --verify B(entonces B es accesible desde A). git rev-parseaquí se necesita para convertir el nombre de confirmación en commit SHA-1 / commit id.

Usar git rev-listcomo en la respuesta VonC también es una posibilidad.

Editar: en Git moderno hay soporte explícito para esta consulta en forma de git merge-base --is-ancestor.


Si uno de los compromisos sobre los que está preguntando es una sugerencia de sucursal , entonces git branch --contains <commit>o git branch --merged <commit>podría ser una mejor solución no programática.

Jakub Narębski
fuente
1
Posiblemente, la forma más rápida sería hacia git checkout -b quickcheck <more-recent-commit-ID>y luego git branch --contains <older-commit-ID>(y luego git branch -D quickcheckdeshacerse de la rama temporal).
clee
2
Dos posibles enfoques, ambos mucho peores que el de la respuesta de @ MattR.
jwg
66
@jwg: la respuesta de MattR es mejor, pero esta respuesta (y probablemente sea aceptada) es anterior a git 1.8.0 y git merge-base --is-ancestorpor 2 años.
Jakub Narębski
@ JakubNarębski Muy bien, lo siento.
jwg
2
En un gran repositorio (2 millones de commit), comparé la velocidad de git branch --contains <commit>y git merge-base --is-ancestor ...: 3m40s vs 0.14s
hagello
259

Desde Git 1.8.0, esto se admite como una opción para merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

Desde la página del manual:

--es-antepasado

Compruebe si el primero es ancestro del segundo y salga con el estado 0 si es verdadero o con el estado 1 si no. Los errores se señalan mediante un estado distinto de cero que no es 1.

Por ejemplo:

git merge-base --is-ancestor origin/master master; echo $?
Matt R
fuente
44
¡Agradable! Aquí hay una secuencia de comandos de shell que envuelve esta respuesta en algo con salida accesible para humanos: gist.github.com/simonwhitaker/6354592
Simon Whitaker
1
En otras palabras: git merge-base THING --is-ancestor OF_THING && echo yes || echo nopor ejemplo:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594
2
@smarber git merge-base --is-ancestor -- commit commitfunciona para los hashes a mi lado con git2.1.4 (Debian / Devuan 7.10 jessie) y 1.9.1 (Ubuntu 14.04 de confianza) que son bastante antiguos ahora. Funciona incluso para Debian wheezy, si lo hace sudo apt-get install git/wheezy-backports.
Tino
15

Este tipo de operaciones se basa en la noción de rango de revisiones detalladas en la pregunta SO: " Diferencia en 'git log origin / master' vs 'git log origin / master ..' ".

git rev-list debería poder regresar de un commit, hasta otro si es accesible.

Entonces intentaría:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Las confirmaciones de límite tienen el prefijo -)

Si la última confirmación mostrada es la misma que la primera confirmación en el git rev-listcomando, entonces es una confirmación accesible desde la segunda confirmación.

Si el primer commit no es accesible desde el segundo, no git rev-listdebería devolver nada.

git rev-list --boundary A..B

terminaría por A, si Aes accesible desde B.
Es lo mismo que:

git rev-list --boundary B --not A

, con Buna referencia positiva y Auna referencia negativa .
Comenzará en By retrocederá a través del gráfico hasta que encuentre una revisión a la que se pueda acceder A.
Yo diría que si Aes directamente accesible B, se encontrará (y se mostrará, debido a la --boundaryopción) en Así mismo.

VonC
fuente
Esto suena como un caso de uso bastante común que me sorprende que git aún no haya publicado un comando de "porcelana" que hace exactamente esto.
Lawrence I. Siden
1
@lsiden: cierto. Nota al margen: no olvide que para verificar algo mediante programación , no debe usar el comando de porcelana (como en stackoverflow.com/questions/6976473/… ), sino comandos de plomería (como se ilustra en stackoverflow.com/questions / 3878624 / ... )
VonC
¡Oh, hombre, parece que tengo que regresar y trabajar en mis habilidades de scripting de Shell!
Lawrence I. Siden
1
pregunta: ¿por qué -85e54e2...el fragmento de código tiene un signo menos? también un error tipográfico: "... es lo mismo que la primera confirmación ..."
sdaau
1
@sdaau -significa que es un compromiso de límite. He editado la respuesta para aclarar eso, así como para actualizar los enlaces de documentos y corregir el error tipográfico para esta respuesta de 5 años.
VonC
11

Otra forma sería usar git logy grep.

git log --pretty=format:%H abc123 | grep def456

Esto producirá una línea de salida si commit def456 es un antecesor de commit abc123, o no hay salida de otra manera.

Por lo general, puede salirse con la suya omitiendo el --prettyargumento, pero es necesario si desea asegurarse de que solo busca a través de hashes de confirmación reales y no a través de comentarios de registro, etc.

itub
fuente
Esperaba que esta solución fuera lenta, pero en realidad es bastante rápida, incluso para un proyecto con más de 20k commits
Renato Zannon
2
en lugar de --prettyusar --oneline: git log --oneline ce2ee3d | grep ec219ccfunciona muy bien
gens
3

https://stackoverflow.com/a/13526591/895245 lo menciona, ahora para hacerlo más amigable para los humanos:

git-is-ancestor() (
  if git merge-base --is-ancestor "$1" "$2"; then
      echo 'ancestor'
  elif git merge-base --is-ancestor "$2" "$1"; then
      echo 'descendant'
  else
      echo 'unrelated'
  fi
)
alias giia='git-is-ancestor'
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
esto devolverá 'no relacionado' si ambos parámetros apuntan a la misma confirmación
mik
1

git show-branch branch-sha1 commit-sha1

Dónde:

  • branch-sha1: el sha1 en su rama que desea verificar
  • commit-sha1: el sha1 del commit con el que quieres verificar
viq
fuente
0

Si está usando git merge-base --is-ancestor, asegúrese de usar Git 2.28 (Q3 2020)

Con Git 2.28 (Q3 2020), algunos campos en " struct commit" que no tienen que estar siempre presentes se han movido para confirmar losas.

Ver commit c752ad0 , commit c49c82a , commit 4844812 , commit 6da43d9 (17 de junio de 2020) por Abhishek Kumar ( abhishekkumar2718) .
(Fusión por Junio ​​C Hamano - gitster- en commit d80bea4 , 06 de julio de 2020)

commit-graph: presentar commit_graph_data_slab

Firmado por: Abhishek Kumar

La estructura commit se usa en muchos contextos. Sin embargo, los miembros generationy graph_possolo se usan para operaciones relacionadas con gráficas de confirmación y, de lo contrario, desperdician memoria.

Este desperdicio habría sido más pronunciado a medida que hacemos la transición al número de generación v2, que utiliza el número de generación de 64 bits en lugar de los 32 bits actuales.

Como a menudo se accede a ellos juntos, vamos a introducir struct commit_graph_datay moverlos a una commit_graph_datalosa.

Si bien el conjunto de pruebas en general se ejecuta tan rápido como master(serie: 26m48s,: master27m34s, más rápido en 2.87%), ciertos comandos como git merge-base --is-ancestorse ralentizaron en un 40% como descubrió Szeder Gábor .
Después de minimizar el acceso a la losa de compromiso, la desaceleración persiste pero se acerca al 20%.

Derrick Stolee cree que la desaceleración es atribuible al algoritmo subyacente más que a la lentitud del acceso de commit-slab y haremos un seguimiento en una serie posterior.

VonC
fuente
-1

Construyendo sobre la respuesta de itub, en caso de que necesite hacer esto para todas las etiquetas en el repositorio:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
ocroquette
fuente