Como posso saber se um commit é descendente de outro commit?

146

Com o Git, como posso saber se um commit no meu ramo é descendente de outro commit?

engie
fonte
2
A mesma pergunta foi feita ao contrário: stackoverflow.com/questions/18345157/… #
Chris Cleeland
11
Você poderia mudar sua resposta aceita? A maioria gosta da --is-ancestorsolução.
Robert Siemer

Respostas:

51

Se você deseja verificar isso programaticamente (por exemplo, no script), pode verificar se git merge-base A Bé igual a git rev-parse --verify A(então A é acessível a partir de B), ou se é git rev-parse --verify B(então B é acessível a partir de A). git rev-parseaqui é necessário para converter do nome de confirmação para confirmar o ID SHA-1 / commit.

Usar git rev-listcomo na resposta do VonC também é possível.

Edit: no Git moderno, há suporte explícito para esta consulta na forma de git merge-base --is-ancestor.


Se um dos commits que você está perguntando for uma dica de ramificação , então git branch --contains <commit>ou git branch --merged <commit>pode ser uma solução não programática melhor.

Jakub Narębski
fonte
1
Possivelmente, o caminho mais rápido seria para git checkout -b quickcheck <more-recent-commit-ID>e depois git branch --contains <older-commit-ID>(e git branch -D quickcheckpara se livrar do ramo temporário).
clee
2
Duas abordagens possíveis, ambas muito piores que a da resposta de @ MattR.
Jwg 9/08
6
@jwg: A resposta do MattR é melhor, mas essa resposta (e provavelmente está sendo aceita) é anterior ao git 1.8.0 e git merge-base --is-ancestorpor 2 anos.
Jakub Narębski
@ JakubNarębski Justo, desculpe.
GTC
2
Em um grande repositório (2 milhões de commits), comparei a velocidade git branch --contains <commit>e git merge-base --is-ancestor ...: 3m40s vs 0.14s
hagello
259

No Git 1.8.0, isso é suportado como uma opção para merge-base:

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

Na página do manual:

--é-ancestral

Verifique se o primeiro é um ancestral do segundo e saia com o status 0, se verdadeiro, ou com o status 1, se não for. Os erros são sinalizados por um status diferente de zero que não é 1.

Por exemplo:

git merge-base --is-ancestor origin/master master; echo $?
Matt R
fonte
4
Agradável! Aqui está um script shell que envolve esta resposta em algo com saída human-reabable: gist.github.com/simonwhitaker/6354592
Simon Whitaker
1
Em outras palavras: git merge-base THING --is-ancestor OF_THING && echo yes || echo nopor exemplo:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594 14/02
2
O @smarber git merge-base --is-ancestor -- commit committrabalha com hashes ao meu lado com git2.1.4 (Debian / Devuan 7.10 jessie) e 1.9.1 (Ubuntu 14.04 confiável), que são bastante antigos agora. Funciona mesmo para o Debian wheezy, se você o fizer sudo apt-get install git/wheezy-backports.
Tino
15

Esse tipo de operação baseia-se na noção de intervalo de revisões detalhadas na pergunta SO: " Diferença em 'origem do git log / mestre' vs 'origem do git log / mestre ..' ".

git rev-list deve poder voltar de um commit, até outro, se possível.

Então, eu tentaria:

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

(Confirmações de limite são prefixadas -)

Se o último commit exibido for o mesmo que o primeiro commit no git rev-listcomando, será um commit acessível a partir do segundo commit.

Se o primeiro commit não estiver acessível a partir do segundo, git rev-listnada retornará.

git rev-list --boundary A..B

terminaria por A, se Aestiver acessível a partir de B.
É o mesmo que:

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

, com Buma referência positiva e Auma referência negativa .
Ele inicia Be volta ao gráfico até encontrar uma revisão acessível a partir dele A.
Eu diria que, se Afor diretamente acessível B, ele encontrará (e será exibido, devido à --boundaryopção) em Asi.

VonC
fonte
Isso soa como um caso de uso bastante comum, e fico surpreso que o git ainda não tenha publicado um comando de "porcelana" que faça exatamente isso.
Lawrence I. Siden
1
@lsiden: verdade. Nota lateral: não esqueça que, para verificar algo programaticamente , você não deve usar o comando porcelain (como em stackoverflow.com/questions/6976473/… ), mas os comandos de encanamento (conforme ilustrado em stackoverflow.com/questions / 3878624 /… )
VonC 15/07
Oh, cara, parece que eu tenho que voltar e trabalhar nas minhas costeletas de script do Shell!
Lawrence I. Siden
1
pergunta: por que -85e54e2...o snippet tem um sinal de menos? também um possível erro de digitação: "... é o mesmo que o primeiro cometer ..."
sdaau
1
@sdaau -significa que é um commit de limite. Editei a resposta para torná-la mais clara, além de atualizar os links dos documentos e corrigir o erro de digitação desta resposta de 5 anos.
VonC 15/03/2015
11

Outra maneira seria usar git loge grep.

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

Isso produzirá uma linha de saída se a confirmação def456 for um ancestral da confirmação abc123, ou nenhuma saída de outra forma.

Geralmente, você pode omitir o --prettyargumento, mas é necessário se você quiser ter certeza de que apenas pesquisa através de hashes de confirmação reais e não através de comentários de log e assim por diante.

itub
fonte
Eu esperava que esta solução a ser lento, mas é realmente muito rápido, mesmo para um projeto com 20k commits +
Renato Zannon
2
em vez de --prettyusar --oneline: git log --oneline ce2ee3d | grep ec219ccworks great
gens
3

https://stackoverflow.com/a/13526591/895245 menciona, agora para torná-lo mais amigável ao ser humano:

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 adicionou uma nova foto
fonte
isso retornará 'unrelated' se ambos os parâmetros apontarem para o mesmo commit
mik
1

git show-branch branch-sha1 commit-sha1

Onde:

  • branch-sha1: o sha1 no seu ramo que você deseja verificar
  • commit-sha1: o sha1 do commit que você deseja verificar
viq
fonte
0

Se você estiver usando git merge-base --is-ancestor, certifique-se de usar o Git 2.28 (terceiro trimestre de 2020)

Com o Git 2.28 (terceiro trimestre de 2020), alguns campos " struct commit" que nem sempre precisam estar presentes foram movidos para confirmar lajes.

Consulte commit c752ad0 , commit c49c82a , commit 4844812 , commit 6da43d9 (17 de junho de 2020) por Abhishek Kumar ( abhishekkumar2718) .
(Incorporado por Junio ​​C Hamano - gitster- in commit d80bea4 , 06 de julho de 2020)

commit-graph: introduzir commit_graph_data_slab

Assinado por: Abhishek Kumar

O commit da estrutura é usado em muitos contextos. No entanto, os membros generatione graph_possão usados ​​apenas para operações relacionadas a commit-graph e, de outra forma, desperdiçam memória.

Esse desperdício teria sido mais pronunciado na transição para o número de geração v2, que usa o número de geração de 64 bits em vez dos atuais 32 bits.

Como eles geralmente são acessados ​​juntos, vamos apresentar o struct commit_graph_datae movê-los para uma commit_graph_datalaje.

Enquanto o conjunto de testes geral é executado tão rápido quanto master(séries: 26m48s,: master27m34s, mais rápido em 2,87%), alguns comandos como git merge-base --is-ancestorforam retardados em 40%, conforme descoberto por Szeder Gábor .
Depois de minimizar o acesso à laje de consolidação, a desaceleração persiste, mas está mais próxima de 20%.

Derrick Stolee acredita que a desaceleração é atribuível ao algoritmo subjacente, e não à lentidão do acesso de commit-slab, e faremos o acompanhamento em uma série posterior.

VonC
fonte
-1

Desenvolvendo a resposta do itub, caso você precise fazer isso para todas as tags no repositório:

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