Jak poruszać się do przodu i do tyłu między zatwierdzeniami w git?

105

Robię git bisecti po dojściu do problematycznego zatwierdzenia, próbuję teraz zrobić krok do przodu / do tyłu, aby upewnić się, że jestem w tym właściwym.

Wiem o HEAD^cofnięciu się w historii, ale czy istnieje inny skrót, który poprowadzi mnie naprzód (w kierunku konkretnego zobowiązania w przyszłości), na przykład:

A - B - C(HEAD) - D - E - F

Wiem, że mój cel jest F i chcę przejść z C do D .


UWAGA: to nie jest duplikat Git: Jak poruszać się w tę iz powrotem między zatwierdzeniami , moje pytanie jest nieco inne i nie ma tam odpowiedzi

Kostas
źródło
git checkout -b new_branch HEAD~4cofnąć 4 zatwierdzenia z HEAD, jak w stackoverflow.com/a/4940090/911945
Anton Tarasenko

Odpowiedzi:

57

Trochę poeksperymentowałem i wydaje się, że to wystarcza, aby przejść do przodu ( edycja : działa dobrze tylko wtedy, gdy masz liniową historię bez zatwierdzeń scalania):

git checkout $(git rev-list --topo-order HEAD..towards | tail -1)

gdzie towardsjest SHA1 zatwierdzenia lub tagu.

Wyjaśnienie:

  • polecenie wewnątrz $()oznacza: pobierz wszystkie zatwierdzenia między aktualnym HEADa towardszatwierdzeniem (z wyłączeniem HEAD) i sortuj je w kolejności pierwszeństwa (tak jak git logdomyślnie - zamiast kolejności chronologicznej, która jest dziwnie domyślna rev-list), a następnie weź ostatnią ( tail), czyli ten, do którego chcemy się udać.
  • jest to oceniane w podpowłoce i przekazywane git checkoutdo wykonania pobrania.

Możesz zdefiniować funkcję dostępną jako alias oczekujący parametrów w twoim .profilepliku, aby przejść do konkretnego zatwierdzenia:

# Go forward in Git commit hierarchy, towards particular commit 
# Usage:
#  gofwd v1.2.7
# Does nothing when the parameter is not specified.
gofwd() {
  git checkout $(git rev-list --topo-order HEAD.."$*" | tail -1)
}

# Go back in Git commit hierarchy
# Usage: 
#  goback
alias goback='git checkout HEAD~'
jakub.g
źródło
2
Kontynuacja działa dobrze w prostych częściach historii, ale w przypadku scalenia przechodzi w pętle.
Kostas
Tak, właściwie nie testowałem tego na scaleniach. Spróbuję zajrzeć w wolnym czasie, ale chwilowo nie mam zachęty, bo zgodziliśmy się na ściśle liniową historię naszego projektu;)
jakub.g
2
Świetna odpowiedź! Zmodyfikowano, aby automatycznie określać bieżącą gałąź: stackoverflow.com/a/23172256/480608
Raine Revere
To jest niepoprawne. git logpokazuje rewizje w porządku chronologicznym, domyślnie tak samo, jak rev-list, z wyjątkiem użycia --graphflagi.
papiro
Dość przekonujące dowody na to, że git jest zbyt złożony. W przypadku czegoś, co zwykle jest tak proste, jak Cofnij lub Ponów, mamy tutaj szaloną listę sprzecznych odpowiedzi. I nawet nie zagłębiłem się w żadną z połączonych odpowiedzi. FWIW, wypróbowałem wersję tego z prostym liniowym zestawem zatwierdzeń i ostatecznie po prostu się poddałem.
Snowcrash
49

Wszystko, czego potrzebujesz, aby uzyskać jasny, nie odłączony stan głowy, to zresetować, a nie zapłacić.

git reset HEAD@{1}
d3day
źródło
5
lub git reset "HEAD@{1}"w niektórych muszlach, takich jak fish i powershell .. git reflogmoże być również przydatne do znalezienia prawidłowego zatwierdzenia.
Steve Cook
1
Zawsze używaj pojedynczych cudzysłowów w poleceniach powłoki, chyba że wyraźnie chcesz, aby powłoka próbowała interpretować / rozszerzać zawartość. Jest to szczególnie ważne w takich przypadkach, w których celem jest zapobieganie interpretowaniu przez powłokę znaków specjalnych. W ten sposób nie musisz wiedzieć, czy ciąg zawiera coś, co jest problematyczne.
Chris Page
Zrobiłem to, żeby spojrzeć w przeszłość, a potem git reset HEADcofnąłem się do miejsca, w którym byłem ... teraz nie mam pojęcia, w jakim stanie jest moje repozytorium i wszystko jest przerażające. Co mam teraz zrobić?
theonlygusti
47

Wierzę, że możesz:

git reset HEAD@{1}

Aby przejść o jedno zobowiązanie do przodu w czasie. Aby przejść do wielu zatwierdzeń, użyj HEAD @ {2}, HEAD @ {3} itd.

w0utert
źródło
20

To jest to, czego używam do nawigacji tam iz powrotem.

przejście do następnego zatwierdzenia

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

przejście do poprzedniego zatwierdzenia

function p() {
    git checkout HEAD^1
}
MK
źródło
Dzięki! Używam tego teraz! Uwagi dla innych początkujących, takich jak ja : aby ponownie założyć HEAD, dołączyćgit checkout <current branch> do ostatniego commita. git checkout -b <new-branch-name>od bieżącego zatwierdzenia umożliwia zmiany w nowej gałęzi. git rebase -irównież działa. Ponadto nazwałem swoją n()funkcję, nx()aby uniknąć konfliktu z menedżerem wersji węzła „n”. Koniecznie sprawdź aliasy!
Steven Choi,
function () {...} służy do tworzenia skryptów bash w systemie Unix / Linux, pochodzę z systemu Windows, po pierwsze trochę trudny do zrozumienia
IcyBrk
9

Powiedz F jest najnowszym zatwierdzeniem trunk(wstaw tutaj własną nazwę gałęzi) ... możesz nazywać go trunk~0(lub po prostu trunk), E as trunk~1, D as trunk~2itd.

Spójrz na swój reflog, aby poznać więcej sposobów nazywania zmian.

Bezużyteczny
źródło
1
~ wraca, nie do przodu, bagażnik ~ 2 to A
EmmanuelMess
Tak. Ta odpowiedź zakłada, że ​​masz gałąź zwaną główną, wskazującą na Fi że wiesz, gdzie w historii tej gałęzi chcesz się przenieść. Nie próbuje poruszać się do przodu w stosunku do HEAD, ale mniej do tyłu w stosunku do tułowia.
Bezużyteczne
@EmmanuelMess jak tam trunk~2A?
theonlygusti
@theonlygusti Dwukrotnie cofasz się z HEAD.
EmmanuelMess
Wciąż zakładasz, że gałąź trunki twój prąd HEADsą identyczne, co nie jest pokazane w pytaniu, które, jak stwierdziłem, nie jest tym, co zakładam, i jest bardzo mało prawdopodobne w połowiebisect
Bezużyteczne
3

Przechodzenie do tyłu jest trywialne, ponieważ poruszasz się w dół drzewa, a zawsze jest jedna droga

  function git_down
        git checkout HEAD^
  end

Przechodząc do przodu, poruszasz się w górę drzewa, więc musisz dokładnie określić, do której gałęzi celujesz:

  function git_up 
        git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
  end

Zastosowanie: git down,git up <branch-name>

Dziamid
źródło
Cofanie się do tyłu również nie jest całkowicie wyjątkowe, jeśli chodzi o fuzje. Chociaż HEAD^jest to zwykle rozsądne niewykonanie zobowiązania.
kdb
2

Jeśli chcesz patrzeć w przyszłość, możesz zrobić tę sztuczkę, ponieważ Git nie ma do tego ścisłych poleceń.

git log --reverse COMMIT_HASH..

Przykład

Lista skrótów historii logów:

A
B
C -> put this
D

za pomocą polecenia git log --reverse C.., na wyjściu pojawi się B i A .

dimpiax
źródło
1

Prawdopodobnie nie jest to najprzyjemniejszy sposób, ale możesz użyć, git logaby wyświetlić listę zatwierdzeń, a następnie użyć, git checkout [sha1 of D]aby przejść do D.

pokolenie bb
źródło
6
Nie rozumiem, jeśli jest w C, to git log pokaże mu tylko C, B i A.
Bilthon
OK, rozumiem, ale będziesz musiał zrobić trudny git-log, jak wskazano w linku podanym przez VonC
Bilthon
1

Właśnie zrobiłem test na tym. powiedz na przykład, że jesteś w gałęzi głównej Następnie wykonaj:

git checkout HEAD@{3}

Głowa zostaje odłączona i możesz spróbować ponownie, aby przejść do innego zatwierdzenia:

git checkout HEAD@{4}

Kiedy skończysz się rozglądać, możesz wrócić do pierwotnego stanu, po prostu sprawdzając tę ​​gałąź. W moim przykładzie: master branch

git checkout master

Jeśli nie chcesz przejść do pierwotnego stanu i chcesz zachować jedno ze zatwierdzeń jako głowę i kontynuować od tego momentu, musisz rozgałęzić się stamtąd. na przykład po „git checkout HEAD @ {4}” możesz wystawić

git checkout -b MyNewBranch
user1322977
źródło
0

Aby obejść ten problem, możesz po prostu wrócić do HEAD za pomocą

git checkout <branch>

A następnie przejdź do zmiany, którą chcesz, za pomocą

git checkout HEAD~<offset>
Patrizio Bertoni
źródło
0

Jeśli używasz kodu vs code, to historia Git jest niesamowitą wtyczką, w której możesz efektywnie przeglądać zatwierdzenia i sprawdzać ich zawartość w samym edytorze. sprawdź link

Rahil Ahmad
źródło
0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

gdzie:

branchName równa się nazwie gałęzi

commitInOrder równa się zatwierdzeniu w kolejności od pierwszego zatwierdzenia w wybranej gałęzi (więc 1 to pierwszy zatwierdzenie, 2 to drugie zatwierdzenie w gałęzi itp.)

Gość
źródło