Come posso sapere se un commit è un discendente di un altro commit?


Risposte:


51

Se si desidera verificarlo a livello di codice (ad es. Nello script), è possibile verificare se git merge-base A Bè uguale a git rev-parse --verify A(quindi A è raggiungibile da B) o se lo è git rev-parse --verify B(quindi B è raggiungibile da A). git rev-parseè qui necessario per convertire dal nome del commit in commit SHA-1 / commit id.

È anche possibile usare git rev-listcome nella risposta VonC .

Modifica: in Git moderno esiste un supporto esplicito per questa query sotto forma di git merge-base --is-ancestor.


Se uno dei commit di cui stai chiedendo è un suggerimento di filiale , allora git branch --contains <commit>o git branch --merged <commit>potrebbe essere una soluzione non programmatica migliore.


1
Forse il modo più veloce sarebbe git checkout -b quickcheck <more-recent-commit-ID>e poi git branch --contains <older-commit-ID>(e quindi git branch -D quickchecksbarazzarsi del ramo temporaneo).
clee

2
Due possibili approcci, entrambi molto peggiori di quello nella risposta di @ MattR.
jwg

6
@jwg: la risposta di MattR è migliore, ma questa risposta (e probabilmente viene accettata) precede git 1.8.0 e git merge-base --is-ancestordi 2 anni.
Jakub Narębski,

@ JakubNarębski Abbastanza giusto, scusa.
jwg

2
In un grande repository (2 milioni di commit), ho confrontato la velocità di git branch --contains <commit>e git merge-base --is-ancestor ...: 3m40s vs 0.14s
hagello

259

Da Git 1.8.0, questo è supportato come opzione per merge-base:

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

Dalla pagina man:

--is-antenato

Controllare se il primo è un antenato del secondo e uscire con lo stato 0 se vero, o con lo stato 1 in caso contrario. Gli errori sono segnalati da uno stato diverso da zero che non è 1.

Per esempio:

git merge-base --is-ancestor origin/master master; echo $?

4
Bello! Ecco uno script di shell che racchiude questa risposta in qualcosa con output raggiungibile dall'uomo
Simon Whitaker,

1
In altre parole: git merge-base THING --is-ancestor OF_THING && echo yes || echo noad es .:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594

2
@smarber git merge-base --is-ancestor -- commit commitlavora per gli hash al mio fianco con git2.1.4 (Debian / Devuan 7.10 jessie) e 1.9.1 (Ubuntu 14.04 fidato) che ora sono piuttosto antichi. Funziona anche con Debian wheezy, se lo fai sudo apt-get install git/wheezy-backports.
Tino,

15

Questo tipo di operazioni si basa sulla nozione di intervallo di revisioni dettagliato nella domanda SO: " Differenza in 'git log origin / master' rispetto a 'git log origin / master ..' ".

git rev-list dovrebbe essere in grado di tornare indietro da un commit, fino a un altro se raggiungibile.

Quindi proverei:

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

(I commit al confine sono preceduti da -)

Se l'ultimo commit visualizzato è uguale al primo commit nel git rev-listcomando, allora è un commit raggiungibile dal secondo commit.

Se il primo commit non è raggiungibile dal secondo, non git rev-listdovrebbe restituire nulla.

git rev-list --boundary A..B

finirebbe A, se Aè raggiungibile da B.
È lo stesso di:

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

, con Bun riferimento positivo e Aun riferimento negativo .
Inizierà a Be tornerà indietro nel grafico fino a quando non incontra una revisione raggiungibile da A.
Direi che se Aè direttamente raggiungibile B, incontrerà (e visualizzerà, a causa --boundarydell'opzione) Astessa.


Sembra un caso d'uso abbastanza comune che sono sorpreso che Git non abbia ancora pubblicato un comando "porcellanato" che fa esattamente questo.
Lawrence I. Siden,

1
@lsiden: true. Nota a margine: non dimenticare che per controllare qualcosa a livello di codice , non dovresti usare il comando porcellane (come in stackoverflow.com/questions/6976473/… ), ma i comandi idraulici (come illustrato in stackoverflow.com/questions / 3878624 /… )
VonC,

Oh, amico, sembra che debba tornare indietro e lavorare sulle mie costolette di scripting Shell!
Lawrence I. Siden,

1
domanda: perché -85e54e2...lo snippet ha un segno meno? anche un possibile errore di battitura: "... è lo stesso del primo commit ..."
sdaau

1
@sdaau -significa che è un commit di confine. Ho modificato la risposta per renderlo più chiaro, nonché per aggiornare i collegamenti ai documenti e correggere l'errore di battitura per questa risposta di 5 anni.
VonC

11

Un altro modo sarebbe usare git loge grep.

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

Questo produrrà una riga di output se commit def456 è un antenato di commit abc123 o altrimenti nessun output.

Di solito puoi --prettyevitare di omettere l' argomento, ma è necessario se vuoi assicurarti di cercare solo tra gli hash di commit effettivi e non attraverso i commenti del registro e così via.


Mi aspettavo che questa soluzione fosse lenta, ma in realtà è abbastanza veloce, anche per un progetto con 20k + commit
Renato Zannon

2
invece di --prettyusare --oneline: git log --oneline ce2ee3d | grep ec219ccfunziona benissimo
gens


1

git show-branch branch-sha1 commit-sha1

Dove:

  • branch-sha1: sha1 nel tuo ramo che vuoi controllare
  • commit-sha1: lo sha1 del commit che si desidera verificare

0

Se stai usando git merge-base --is-ancestor, assicurati di usare Git 2.28 (Q3 2020)

Con Git 2.28 (3 ° trimestre 2020), alcuni campi in " struct commit" che non devono essere sempre presenti sono stati spostati per il commit delle lastre.

Vedi commit c752ad0 , commit c49c82a , commit 4844812 , commit 6da43d9 (17 giu 2020) di Abhishek Kumar ( abhishekkumar2718) .
(Unita da Junio ​​C Hamano - gitster- in commit d80bea4 , 06 lug 2020)

commit-graph: introdurre commit_graph_data_slab

Firmato-fuori-da: Abhishek Kumar

Il commit di struct viene utilizzato in molti contesti. Tuttavia, i membri generatione graph_posvengono utilizzati solo per operazioni correlate al commit-graph e altrimenti sprecano memoria.

Questo spreco sarebbe stato più pronunciato mentre passavamo al numero di generazione v2, che utilizza il numero di generazione a 64 bit anziché gli attuali 32 bit.

Poiché sono spesso accessibili insieme, introduciamo struct commit_graph_datae spostiamoli su una commit_graph_datalastra.

Mentre la suite di test generale funziona altrettanto velocemente master, (serie: 26m48s,: master27m34s, più veloce del 2,87%), alcuni comandi come quelli git merge-base --is-ancestorsono stati rallentati del 40% come scoperto da Szeder Gábor .
Dopo aver ridotto al minimo l'accesso al commit-slab, il rallentamento persiste ma si avvicina al 20%.

Derrick Stolee ritiene che il rallentamento sia attribuibile all'algoritmo sottostante piuttosto che alla lentezza dell'accesso al commit-slab e che seguiremo in una serie successiva.


-1

Sulla base della risposta di itub, nel caso in cui sia necessario eseguire questa operazione per tutti i tag nel repository:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.