Jak mogę stwierdzić, czy jeden commit jest potomkiem innego commit?

146

W przypadku Git, jak mogę stwierdzić, czy jedno zatwierdzenie w mojej gałęzi jest potomkiem innego zatwierdzenia?

engie
źródło
2
To samo pytanie było odwrotne: stackoverflow.com/questions/18345157/…
Chris Cleeland,
11
Czy mógłbyś zmienić zaakceptowaną odpowiedź? Większość lubi takie --is-ancestorrozwiązanie.
Robert Siemer

Odpowiedzi:

51

Jeśli chcesz to sprawdzić programowo (np. W skrypcie), możesz sprawdzić, czy git merge-base A Bjest równe git rev-parse --verify A(wtedy A jest osiągalne z B), czy też jest git rev-parse --verify B(wtedy B jest osiągalne z A). git rev-parsejest tutaj potrzebne do konwersji nazwy zatwierdzenia na zatwierdzenie SHA-1 / identyfikator zatwierdzenia.

Korzystanie git rev-listjak w VonC odpowiedź jest również możliwość.

Edycja: w nowoczesnym Git istnieje wyraźna obsługa tego zapytania w postaci git merge-base --is-ancestor.


Jeśli jedno z zatwierdzeń, o które pytasz, jest wskazówką dotyczącą gałęzi , to git branch --contains <commit>lub git branch --merged <commit>może być lepszym rozwiązaniem nieprogramowym.

Jakub Narębski
źródło
1
Prawdopodobnie najszybszym sposobem byłoby to, git checkout -b quickcheck <more-recent-commit-ID>a potem git branch --contains <older-commit-ID>(a potem git branch -D quickcheckpozbycie się tymczasowej gałęzi).
clee
2
Dwa możliwe podejścia, oba znacznie gorsze niż to w odpowiedzi @ MattR.
jwg
6
@jwg: Odpowiedź MattR jest lepsza, ale ta odpowiedź (i prawdopodobnie jest akceptowana) jest starsza niż Git 1.8.0 i git merge-base --is-ancestoro 2 lata.
Jakub Narębski
@ JakubNarębski W porządku, przepraszam.
jwg
2
W dużym repozytorium (2 miliony zatwierdzeń) porównałem prędkość git branch --contains <commit>i git merge-base --is-ancestor ...: 3 min 40 s vs 0,14 s
hagello
259

Od wersji Git 1.8.0 jest to obsługiwane jako opcja merge-base:

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

Ze strony podręcznika:

- jest-przodkiem

Sprawdź, czy pierwszy jest przodkiem drugiego i zakończ ze statusem 0, jeśli prawda, lub ze statusem 1, jeśli nie. Błędy są sygnalizowane niezerowym stanem innym niż 1.

Na przykład:

git merge-base --is-ancestor origin/master master; echo $?
Matt R.
źródło
4
Miły! Oto skrypt powłoki, który opakowuje tę odpowiedź w coś z danymi wyjściowymi możliwymi do naprawienia przez człowieka: gist.github.com/simonwhitaker/6354592
Simon Whitaker
1
Innymi słowy: git merge-base THING --is-ancestor OF_THING && echo yes || echo nonp .:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594
2
@smarber git merge-base --is-ancestor -- commit commitpracuje dla skrótów po mojej stronie w gitwersjach 2.1.4 (Debian / Devuan 7.10 jessie) i 1.9.1 (Ubuntu 14.04 zaufany), które są teraz dość stare. Jeśli tak, to działa nawet dla Debiana wheezy sudo apt-get install git/wheezy-backports.
Tino
15

Ten rodzaj operacji opiera się na pojęciu zakresu zmian wyszczególnionym w pytaniu SO: „ Różnica w 'git log origin / master' a 'git log origin / master ..' ”.

git rev-list Powinien móc przejść od zatwierdzenia do następnego, jeśli jest osiągalny.

Więc spróbuję:

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

(Zatwierdzenia brzegowe są poprzedzone przedrostkiem -)

Jeśli ostatnie wyświetlone zatwierdzenie jest takie samo jak pierwsze zatwierdzenie w git rev-listpoleceniu, to jest to zatwierdzenie osiągalne z drugiego zatwierdzenia.

Jeśli pierwsze zatwierdzenie nie jest osiągalne od drugiego, nie git rev-listpowinno nic zwracać.

git rev-list --boundary A..B

zakończy się A, jeśli Ajest osiągalny z B.
To jest to samo, co:

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

z Bo dodatnim odniesienia , a Aw odniesieniu ujemnym .
Rozpoczyna się o godzinie Bi przechodzi z powrotem przez wykres, aż napotka wersję, do której można uzyskać dostęp A.
Twierdziłbym, że jeśli Ajest bezpośrednio dostępny z B, napotka (i wyświetli się, ze względu na tę --boundaryopcję) A.

VonC
źródło
Brzmi to jak dość powszechny przypadek użycia, więc jestem zaskoczony, że git nie opublikował jeszcze polecenia „porcelain”, które robi dokładnie to.
Lawrence I. Siden
1
@lsiden: true. Na marginesie: nie zapominaj, że aby sprawdzić coś programowo , nie powinieneś używać polecenia porcelain (jak w stackoverflow.com/questions/6976473/… ), ale poleceń hydraulicznych (jak pokazano na stackoverflow.com/questions / 3878624 /… )
VonC
Och, stary, wygląda na to, że muszę wrócić i popracować nad moimi kotletami do skryptów Shell!
Lawrence I. Siden
1
pytanie: dlaczego -85e54e2...fragment we fragmencie ma minus? również możliwa literówka: "... to to samo, co pierwsze zatwierdzenie ..."
sdaau
1
@sdaau -oznacza, że ​​jest to zatwierdzenie granic. Zredagowałem odpowiedź, aby była jaśniejsza, a także aby odświeżyć linki do dokumentów i poprawić literówkę w tej odpowiedzi sprzed 5 lat.
VonC
11

Innym sposobem byłoby użycie git logi grep.

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

Spowoduje to wyświetlenie jednej linii wyniku, jeśli zatwierdzenie def456 jest przodkiem zatwierdzenia abc123 lub nie ma żadnego wyjścia.

Zwykle można pominąć --prettyargument, ale jest to potrzebne, jeśli chcesz się upewnić, że przeszukujesz tylko rzeczywiste skróty zatwierdzeń, a nie komentarze dziennika i tak dalej.

itub
źródło
Spodziewałem się, że to rozwiązanie będzie powolne, ale w rzeczywistości jest dość szybkie, nawet dla projektu z 20 000 + zatwierdzeń
Renato Zannon
2
zamiast --prettyużywać --oneline: git log --oneline ce2ee3d | grep ec219ccdziała świetnie
gen
3

https://stackoverflow.com/a/13526591/895245 wspomina o tym, teraz, aby uczynić go bardziej przyjaznym dla człowieka:

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 郝海东 冠状 病 六四 事件 法轮功
źródło
zwróci to „niepowiązane”, jeśli oba parametry wskazują na to samo zatwierdzenie
mik
1

git show-branch branch-sha1 commit-sha1

Gdzie:

  • branch-sha1: sha1 w twojej gałęzi, który chcesz sprawdzić
  • commit-sha1: sha1 zatwierdzenia, z którym chcesz sprawdzić
viq
źródło
0

Jeśli używasz git merge-base --is-ancestor, upewnij się, że używasz Git 2.28 (Q3 2020)

W Git 2.28 (Q3 2020), kilka pól w " struct commit", które nie muszą być zawsze obecne, zostało przeniesionych do zatwierdzania płyt.

Zobacz commit c752ad0 , commit c49c82a , commit 4844812 , commit 6da43d9 (17 czerwca 2020) autor: Abhishek Kumar ( abhishekkumar2718) .
(Scalone przez Junio ​​C Hamano - gitster- w zatwierdzeniu d80bea4 , 06 lipca 2020)

commit-graph: przedstawiać commit_graph_data_slab

Podpisał: Abhishek Kumar

Struct commit jest używany w wielu kontekstach. Jednak elementy członkowskie generationi graph_possą używane tylko do operacji związanych z grafem zatwierdzania iw inny sposób marnują pamięć.

Straty te byłyby bardziej widoczne, gdy przechodzimy do generacji v2, która używa 64-bitowego numeru generacji zamiast obecnych 32-bitowych.

Ponieważ często są one dostępne razem, wprowadźmy struct commit_graph_datai przenieśmy je na commit_graph_datapłytę.

Podczas gdy cały zestaw testów działa tak samo szybko jak master(serie: 26m48s,: master27m34s, szybciej o 2,87%), niektóre polecenia, takie jak, git merge-base --is-ancestorzostały spowolnione o 40%, jak odkrył Szeder Gábor .
Po zminimalizowaniu dostępu do płyty zatwierdzającej, spowolnienie utrzymuje się, ale jest bliższe 20%.

Derrick Stolee uważa, że ​​spowolnienie można przypisać raczej bazującemu na nim algorytmowi, a nie powolnemu dostępowi do zatwierdzania danych, i zajmiemy się nim w późniejszej serii.

VonC
źródło
-1

Opierając się na odpowiedzi itub, na wypadek, gdybyś musiał to zrobić dla wszystkich tagów w repozytorium:

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