Jak znaleźć kolejne zatwierdzenie w git? (dziecko / dzieci referencyjne)

236

ref^odnosi się do zatwierdzenia wcześniej ref, a co z uzyskaniem zatwierdzenia po ref ?

Na przykład, jeśli git checkout 12345mam sprawdzić następny zatwierdzenie?

Dzięki.

PS Tak, git to drzewo struktury wskaźnika węzła DAG. Jak znaleźć zatwierdzenie po tym?

Schwern
źródło
2
Zobacz także „ git children-of”!
VonC

Odpowiedzi:

185

Aby wyświetlić listę wszystkich zatwierdzeń, zaczynając od bieżącego, a następnie jego potomka itd. - w zasadzie standardowy dziennik git, ale idąc w drugą stronę, użyj czegoś w rodzaju

git log --reverse --ancestry-path 894e8b4e93d8f3^..master

gdzie 894e8b4e93d8f3 jest pierwszym zatwierdzeniem, które chcesz pokazać.

Tim Hunt
źródło
3
Dla przypadku określonego w pierwotnym pytaniu, tylko podstawić HEAD^za 894e8b4e93d8f3^.
Søren Løvborg
2
Być może dodanie --oneline jest lepszym krótkim wynikiem wyjściowym.
firo
1
Muszę użyć ..., ^..po cichu
zawiesza
1
Dostanie się fatal: unrecognized argument: --ancestry-pathdo wersji git 1.7.1
user151841,
6
Działa to tylko wtedy, gdy masterznajduje się na ścieżce przodków bieżącego zatwierdzenia. Zobacz drugi fragment kodu mojej odpowiedzi , aby znaleźć rozwiązanie, które zadziała we wszystkich przypadkach.
Tom Hale,
37

Twórca Hudson (obecnie Jenkins) Kohsuke Kawaguchi właśnie opublikował (listopad 2013):
kohsuke / git-children-of :

Biorąc pod uwagę zatwierdzenie, znajdź bezpośrednie dzieci tego zatwierdzenia.

#!/bin/bash -e
# given a commit, find immediate children of that commit.
for arg in "$@"; do
  for commit in $(git rev-parse $arg^0); do
    for child in $(git log --format='%H %P' --all | grep -F " $commit" | cut -f1 -d' '); do
      git describe $child
    done
  done
done

Jak ilustruje ten wątek , w VCS opartym na historii reprezentowanej przez DAG (Directed Acyclic Graph) nie ma „jednego rodzica” ani „jednego dziecka”.

        C1 -> C2 -> C3
      /               \
A -> B                  E -> F
      \               /
        D1 -> D2 ----/

Kolejność zatwierdzeń odbywa się poprzez „zamówienie topo” lub „zamówienie według daty” (patrz książka GitPro )

Ale od wersji Git 1.6.0 możesz wyświetlić listę potomków zatwierdzenia.

git rev-list --children
git log --children

Uwaga: w przypadku zatwierdzeń nadrzędnych występuje ten sam problem, z sufiksem ^parametru zmiany oznaczającego pierwszego rodzica tego obiektu zatwierdzenia. ^<n>oznacza <n>th rodzica (tj. rev^ jest równoważne rev^1).

Jeśli jesteś w oddziale fooi wydajesz „ git merge bar”, to foobędziesz pierwszym rodzicem.
Tj .: pierwszy element nadrzędny to gałąź, w której byłeś podczas scalania, a drugi to zatwierdzenie w gałęzi, w której się scaliłeś.

VonC
źródło
6
git rev-list --childrenna pewno wygląda tak, jak chcę, ale nie DWIM. Wydaje się, że zawiera listę wszystkich rodziców i ich dzieci. Przypuszczam, że mogę wymienić je wszystkie i przeanalizować je ... bleh, ale to coś.
Schwern
@ Schwern: prawda, git rev-list --childrennie jest na liście tylko dzieci, ale na liście rodziców z ich dziećmi ... zawsze trzeba parsować.
VCC
Wypróbowałem ten kod przy zatwierdzeniu z dwójką dzieci: $ git children0of 9dd5932 fatal: No annotated tags can describe '71d7b5dd89d241072a0a078ff2c7dfec05d52e1f'. However, there were unannotated tags: try --tags. Jakie wyniki otrzymujesz?
Tom Hale,
@TomHale Nie mogę teraz tego przetestować, ale zadaję nowe pytanie (w wersji OS i Git), w ten sposób każdy może przetestować.
VonC
1
Jedynym problemem git-children-ofjest to, że używa git opisu, który próbuje sformatować SHA jako czytelną dla człowieka, co może się nie powieść z błędem @ TomHale, i daje takie wyniki, v1.0.4-14-g2414721które są mylące, jeśli oczekujesz SHA. Zastąpienie go prostym echoczyni to doskonałe narzędzie, dzięki!
Nickolay
18

znalazłem to

git rev-list --ancestry-path commit1..commit2

gdzie ustawiłem commit1jako bieżące zatwierdzenie i commit2do aktualnej głowy. To zwraca mi listę wszystkich zatwierdzeń, które budują ścieżkę pomiędzy commit1a commit2.

Ostatni wiersz wyniku to potomek commit1 (na ścieżce do commit2).

biały_gekon
źródło
3
Więc po prostu dodaj, | tail -1aby zdobyć dziecko.
Jesse Glick
8

Wiem co masz na myśli. Frustrujące jest posiadanie dużej składni do przechodzenia do poprzednich zatwierdzeń, ale nie przechodzenie do następnych. W złożonej historii problem „co to jest kolejne zatwierdzenie” staje się raczej trudny, ale w złożonym łączeniu pojawia się ta sama twardość z „poprzednimi” zobowiązaniami. W prostym przypadku w obrębie jednej gałęzi z liniową historią (nawet tylko lokalnie dla pewnej ograniczonej liczby zatwierdzeń) byłoby miło i sensownie byłoby iść do przodu i do tyłu.

Prawdziwym problemem jest jednak to, że do zatwierdzeń potomnych nie ma odniesienia, jest to tylko lista powiązana wstecz. Znalezienie zatwierdzenia potomnego wymaga wyszukiwania, co nie jest takie złe, ale prawdopodobnie nie jest czymś, co git chce umieścić w logice refspec.

W każdym razie natknąłem się na to pytanie, ponieważ chcę po prostu zrobić krok naprzód w historii, dokonując pojedynczych naruszeń, wykonując testy, a czasem trzeba zrobić krok naprzód, a nie wstecz. Po dłuższym zastanowieniu wymyśliłem to rozwiązanie:

Wybierz zatwierdzenie przed miejscem, w którym jesteś. Prawdopodobnie może to być głowa oddziału. Jeśli jesteś w oddziale ~ 10, to „git checkout branch ~ 9”, a następnie „git checkout branch ~ 8”, aby uzyskać następny, a następnie „git checkout branch ~ 7” i tak dalej.

Zmniejszenie liczby powinno być naprawdę łatwe w skrypcie, jeśli jest to potrzebne. O wiele łatwiej niż parsowanie git rev-list.

vontrapp
źródło
Chociaż jest użyteczny, nie znajduje następnego zatwierdzenia. Podchodzi do bieżącego zatwierdzenia.
Schwern
1
Cóż, więc sądzę, że możesz to zrobić: „ BRANCH=master; git co $BRANCH~$[ $(git rev-list HEAD..$BRANCH | wc -l) - 1 ]Musisz iść w kierunku gałęzi, nie ma mowy o tym.
vontrapp
8

Dwie praktyczne odpowiedzi:

Jedno dziecko

Na podstawie odpowiedzi @ Michaela zhakowałem childalias w swoim .gitconfig.

Działa zgodnie z oczekiwaniami w przypadku domyślnym, a także jest wszechstronny.

# Get the child commit of the current commit.
# Use $1 instead of 'HEAD' if given. Use $2 instead of curent branch if given.
child = "!bash -c 'git log --format=%H --reverse --ancestry-path ${1:-HEAD}..${2:\"$(git rev-parse --abbrev-ref HEAD)\"} | head -1' -"

Domyślnie podaje potomka HEAD (chyba że podano inny argument zatwierdzenia), wykonując przodek o krok w kierunku końca bieżącej gałęzi (chyba że drugi argument zatwierdzenia jest podany jako drugi argument).

Użyj %hzamiast, %Hjeśli chcesz mieć krótki formularz skrótu.

Wiele dzieci

Z odłączoną GŁOWĄ (nie ma gałęzi) lub aby uzyskać wszystkie dzieci niezależnie od gałęzi:

# For the current (or specified) commit-ish, get the all children, print the first child 
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\") ); shift; echo $1' -"

Zmień na, $1aby $*wydrukować wszystkie dzieci.

Możesz także zmienić --allna zatwierdzenie, aby wyświetlać tylko dzieci, które są przodkami tego zatwierdzenia - innymi słowy, aby wyświetlać tylko dzieci „w kierunku” danego zatwierdzenia. Może to pomóc zawęzić wyniki od wielu dzieci do jednego.

Tom Hale
źródło
7

Nie ma unikalnego „następnego zatwierdzenia”. Ponieważ historia w Git jest DAG, a nie wierszem, wiele zatwierdzeń może mieć wspólnego rodzica (gałęzie), a zatwierdzenia mogą mieć więcej niż jednego rodzica (scalenia).

Jeśli masz na myśli konkretną gałąź, możesz spojrzeć na jej log i zobaczyć, co zatwierdza na liście obecną jako nadrzędną.

Phil Miller
źródło
37
Zgodnie z tą logiką nie ma również „poprzedniego zatwierdzenia”, ale istnieje mnóstwo składni do uzyskania elementów nadrzędnych.
Schwern
7
@ Schwern: Nie ma też „poprzedniego zatwierdzenia”; <rev>^to „zatwierdzenie nadrzędne” („pierwszy nadrzędny” dla zatwierdzeń scalania).
Jakub Narębski
6

Wypróbowałem wiele różnych rozwiązań i żadne nie działało dla mnie. Musiałem wymyślić własne.

znajdź następny zatwierdzenie

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

znajdź poprzednie zatwierdzenie

function p() {
    git checkout HEAD^1
}
MK
źródło
6

W przypadku, gdy nie masz na myśli konkretnego zatwierdzenia „docelowego”, ale zamiast tego chcesz zobaczyć zatwierdzenia potomne, które mogą znajdować się w dowolnej gałęzi, możesz użyć tego polecenia:

git rev-list --children --all | grep ^${COMMIT}

Jeśli chcesz zobaczyć wszystkie dzieci i wnuki , musisz użyć rev-list --childrenrekurencyjnie:

git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
           grep ^${COMMIT} | \
           sed 's/ /|/g')\)

(Wersja, która daje tylko wnuczki, użyłaby bardziej złożonego sedi / lub cut.)

Na koniec możesz wprowadzić to do log --graphpolecenia, aby zobaczyć strukturę drzewa, w następujący sposób:

git log --graph --oneline --decorate \
\^${COMMIT}^@ \
$(git rev-list --children --all | \
  egrep ^\($(git rev-list --children --all | \
             grep ^${COMMIT} | \
             sed 's/ /|/g')\))

Uwaga : wszystkie powyższe polecenia zakładają, że zmienna powłoki została ustawiona na ${COMMIT}jakieś odniesienie (gałąź, znacznik, sha1) zatwierdzenia, którego dziećmi jesteś zainteresowany.

Matt McHenry
źródło
2
to odpowiada na pytanie, które nie wiedziałem, jak sformułować dla Google i Stackoverflow, ale próbowałem zadać. dziękuję za proaktywne rozpoznanie potrzeby
Tommy Knowlton,
dla mnie ${COMMIT}nie istnieje, ale możesz użyć $(git rev-parse HEAD) zamiast tego
Radon8472
przepraszam, dodałem notatkę o ${COMMIT}.
Matt McHenry
5

(Zaczęło się to jako odpowiedź na zduplikowane pytanie. Przeprowadziłem trochę lekkiej edycji, aby je oczyścić).

Wszystkie wewnętrzne strzałki Gita są jednokierunkowe, skierowane do tyłu. Dlatego nie ma krótkiej wygodnej składni do poruszania się do przodu: to po prostu niemożliwe.

Możliwe jest „poruszanie się przeciw strzałom”, ale sposób na zrobienie tego jest zaskakujący, jeśli nie widziałeś go wcześniej, a potem oczywisty później. Powiedzmy, że mamy:

A <-B <-C <-D <-E   <-- last
        ^
        |
         \--------- middle

Używanie middle~2następuje dwukrotnie zgodnie ze strzałkami od Ctyłu do A. Więc jak możemy przenieść Csię D? Odpowiedź brzmi: zaczynamy od E, używając nazwy lasti pracujemy wstecz, aż do niej dojdziemy middle, rejestrując punkty, które odwiedzamy po drodze . Następnie poruszamy się tak daleko, jak chcemy, w kierunku last: przesuń się o jeden krok Dlub dwa do E.

Jest to szczególnie ważne, gdy mamy oddziały:

          D--E   <-- feature1
         /
...--B--C   <-- master
         \
          F--G   <-- feature2

Który zatwierdzenie jest krok po kroku C? Nie ma poprawnej odpowiedzi, dopóki nie dodasz do pytania: w kierunku cechy___ (wypełnij puste pole).

Aby wyliczyć commits pomiędzy C(wykluczając C) samą i, powiedzmy, Gużywamy:

git rev-list --topo-order --ancestry-path master..feature2

W --topo-ordergwarantuje, że nawet w obecności złożonego rozgałęzienia i-łączących się commity wyjdzie w topologicznie-posortowanych. Jest to wymagane tylko wtedy, gdy łańcuch nie jest liniowy. Te --ancestry-pathśrodki ograniczające że gdy pracujemy z tyłu feature2, tylko commity list, które mają zobowiązać Cjako jeden z ich własnych przodków. To znaczy, jeśli wykres - lub jego odpowiednia część - faktycznie wygląda tak:

A--B--C   <-- master
 \     \
  \     F--G--J   <-- feature2
   \         /
    H-------I   <-- feature3

prosty wniosek formie feature2..masterwylicza zobowiązuje J, Ga I, a F, a Hw pewnym porządku. Z --ancestry-pathnokautujemy Hi I: nie są potomkami C, tylko z A. Dzięki --topo-ordernam upewnić się, że rzeczywista kolejność jest wyliczenie J, potem G, potem F.

git rev-listKomenda wycieki tych identyfikatorów hash na standardowe wyjście, po jednej w wierszu. Aby przejść o krok do przodu w kierunku feature2, chcemy tylko ostatniej linii.

Jest możliwe (i kuszące i może być użyteczne) dodawanie, --reversedzięki czemu git rev-listpo wygenerowaniu drukuje zatwierdzenia w odwrotnej kolejności. To działa, ale jeśli używasz go w potoku takim jak ten:

git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1

aby uzyskać „następny zatwierdzenie w kierunku id2”, a istnieje bardzo długa lista zatwierdzeń, git rev-listpolecenie może uzyskać zepsuty potok, gdy próbuje zapisać, do headktórego przestał czytać dane wejściowe i zakończył działanie. Ponieważ błędy w potokach są zwykle ignorowane przez powłokę, działa to głównie. Tylko upewnij się, że są ignorowane w twoim użyciu.

Kuszące jest także dodanie -n 1do git rev-listpolecenia wraz z --reverse. Nie rób tego! To git rev-listzatrzymuje się po przejściu o jeden krok wstecz , a następnie odwróceniu (jednopunktowej) listy odwiedzonych zatwierdzeń. Tak powstaje za <id2>każdym razem.

Ważna uwaga dodatkowa

Zauważ, że w przypadku fragmentów graficznych „diament” lub „pierścień benzenowy”:

       I--J
      /    \
...--H      M--...  <-- last
      \    /
       K--L

przesunięcie jednego zatwierdzenia „do przodu” z w Hkierunku lastspowoduje albo I albo K . Nic nie możesz na to poradzić: oba zobowiązania są o krok do przodu! Jeśli następnie zaczniesz od wynikowego zatwierdzenia i przejdziesz do następnego kroku, jesteś teraz zaangażowany w dowolną ścieżkę, którą zacząłeś.

Lekarstwem na to jest unikanie przemieszczania się o krok i blokowanie łańcuchów zależnych od ścieżki. Zamiast tego, jeśli planujesz odwiedzić cały łańcuch ścieżki przodków, zanim zrobisz cokolwiek innego , utwórz pełną listę wszystkich zatwierdzeń w łańcuchu:

git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits

Następnie odwiedzaj każdy zatwierdzenie na tej liście, pojedynczo, a otrzymasz cały łańcuch. --topo-orderZadba trafisz I-i- Jw tej kolejności, a K-i- Lw tej kolejności (choć nie ma łatwego sposobu, aby przewidzieć, czy zrobisz parę IJ przed lub po parę KL).

Trek
źródło
Nie ma poprawnej odpowiedzi, dopóki nie dodasz do pytania: w kierunku cechy___ ” To bardzo dobra uwaga. Dziękuję za odpowiedź.
Schwern
4

Mam ten alias w ~/.gitconfig

first-child = "!f() { git log  --reverse --ancestry-path --pretty=%H $1..${2:-HEAD} | head -1; }; f"
słaby
źródło
co jest f()? I to musi być head -1pierwsze dziecko, w przeciwnym razie zgłosi to HEAD.
Xerus
@ Xerus Ponieważ używa jakiejś „złożonej” składni powłoki, a git nie rozpozna jej, jeśli nie zostanie zawinięta f(). Tak, head -1to odważne domysły.
słaby
Miły! Zmodyfikowałem mój do: nextref = "!f() { git log --reverse --ancestry-path --pretty=%H $1..HEAD | head -${2:-1} | tail -1; }; f"abyś mógł wybrać, jak daleko naprzód, opcjonalnie
Z. Khullah
2

Udało mi się znaleźć następne dziecko w następujący sposób:

git log --reverse --children -n1 HEAD (where 'n' is the number of children to show)
DevRQ
źródło
1
dla mnie to nie pokazuje pierwszego dziecka, pokazuje bieżące zatwierdzenie
Radon8472
2

Jeśli wszystkie zatwierdzenia potomne znajdują się w jakiejś gałęzi, możesz użyć gitk --all commit^.., gdzie „zatwierdzenie” jest czymś identyfikującym zatwierdzenie. Na przykład, jeśli skrót SHA-1 zatwierdzenia to c6661c5, wpiszgitk --all c6661c5^..

Prawdopodobnie będziesz musiał wprowadzić pełny SHA-1 w komórce gitk „SHA1 ID:”. Będziesz potrzebował pełnego SHA-1, który w tym przykładzie można uzyskać za pośrednictwemgit rev-parse c6661c5

Alternatywnie, git rev-list --all --children | grep '^c6661c5883bb53d400ce160a5897610ecedbdc9d'stworzy wiersz zawierający wszystkie dzieci tego zatwierdzenia, przypuszczalnie bez względu na to, czy w grę wchodzi oddział.

użytkownik339589
źródło
0

Każde zatwierdzenie przechowuje wskaźnik do swojego elementu nadrzędnego (rodziców, w przypadku zatwierdzenia scalania (standard)).

Zatem nie ma sposobu, aby wskazać zatwierdzenie dziecka (jeśli istnieje) od rodzica.

Lakshman Prasad
źródło
Zatwierdzenie nie może przechowywać wskaźników dla swoich elementów podrzędnych, ponieważ dodatkowe elementy podrzędne (punkty rozgałęziające) można dodać w późniejszym czasie.
Jakub Narębski
@Jakub Naprawdę nie podążam. Nie można ich później dodać?
Schwern
1
Jakub: To dokładnie to, co powiedziałem. „Każde zatwierdzenie przechowuje wskaźniki tylko dla swojego rodzica”.
Lakshman Prasad
@ Schwern: Zatwierdzenia w git są niezmienne (co ma niezłą konsekwencję odpowiedzialności), więc wskaźniki do dzieci nie mogą być „dodane później”. Jednym z powodów jest to, że identyfikator zatwierdzenia (używany np. W linkach „nadrzędnych”) zależy od zawartości commit; jest to jedyne rozwiązanie w systemie rozproszonym, bez centralnego urzędu numeracji. Również „potomki” zatwierdzeń zależą od posiadanych gałęzi, a to z kolei może różnić się od repozytorium do repozytorium (i zatwierdzenia są takie same w każdym repozytorium).
Jakub Narębski
4
@becomingGuru I down głosował. Może to prawda, ale nie odpowiada na moje pytanie. Pytanie brzmi: „jak znaleźć następny zatwierdzenie w git?” To nie jest „czy git commit przechowuje wskaźnik do swoich dzieci?”
Schwern
0

Istniejące odpowiedzi zakładają, że masz gałąź zawierającą szukany zatwierdzenie.

W moim przypadku zatwierdzenie, którego szukałem, nie było włączone, git rev-list --allponieważ żadna gałąź tego nie zawierała.

Skończyłem przeglądać gitk --reflogręcznie.

Jeśli nie możesz znaleźć swojego zatwierdzenia nawet w dzienniku, spróbuj:

  • git fsck --full aby wyświetlić listę zwisających (tzn. nie w żadnej gałęzi) zatwierdzeń, lub
  • git fsck --lost-found zrobić referencje wskazujące na wiszące zobowiązania do zastosowania technik w innych odpowiedziach.
snipsnipsnip
źródło