Come posso andare avanti e indietro tra i commit in git?


105

Sto facendo un git bisecte dopo essere arrivato al commit problematico, ora sto cercando di fare un passo avanti / indietro per assicurarmi di essere in quello giusto.

So di HEAD^tornare indietro nella storia, ma c'è un'altra scorciatoia per farmi andare avanti (verso un impegno specifico in futuro) in questo modo:

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

So che il mio obiettivo è F e voglio passare da C a D .


NOTA: questo non è un duplicato di Git: come spostarsi avanti e indietro tra i commit , la mia domanda è leggermente diversa e non trova risposta lì



git checkout -b new_branch HEAD~4per tornare 4 commit dalla testa come in stackoverflow.com/a/4940090/911945
Anton Tarasenko

Risposte:


57

Ho sperimentato un po 'e questo sembra fare il trucco per navigare in avanti ( modifica : funziona bene solo quando hai una cronologia lineare senza merge commit):

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

dove towardsè uno SHA1 del commit o un tag.

Spiegazione:

  • il comando all'interno $()significa: ottieni tutti i commit tra current HEADe towardscommit (escluso HEAD), e ordinali nell'ordine di precedenza (come git logper impostazione predefinita - invece dell'ordine cronologico che è stranamente l'impostazione predefinita per rev-list), quindi prendi l'ultimo ( tail), ovvero quello a cui vogliamo andare.
  • questo viene valutato nella subshell e passato a git checkoutper eseguire un checkout.

È possibile definire una funzione accessibile come alias in attesa di parametri nel .profilefile per navigare in avanti verso il commit specifico:

# 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~'

2
Andare avanti funziona bene su parti diritte della storia, ma va in loop quando si incontra un'unione.
Kostas

Sì, in realtà non l'ho testato sulle fusioni. Proverò a dare un'occhiata nel tempo libero, ma ho poco incentivo temporaneamente, dal momento che abbiamo deciso di avere una storia strettamente lineare nel nostro progetto;)
jakub.g

2
Bella risposta! Modificato per specificare automaticamente il ramo corrente: stackoverflow.com/a/23172256/480608
Raine Revere

Questo non è corretto. git logmostra i commit in ordine cronologico, per impostazione predefinita, lo stesso di rev-list, tranne quando si utilizza il --graphflag.
papiro

Prova abbastanza convincente che git è troppo complesso. Per qualcosa che di solito è semplice come Annulla o Ripristina, qui abbiamo un folle elenco di risposte contrastanti. E non sono nemmeno entrato in nessuna delle risposte collegate. FWIW, ho provato una versione di questo con un semplice insieme lineare di commit e alla fine mi sono arreso.
Snowcrash

49

Tutto ciò di cui hai bisogno per ottenere uno stato principale chiaro, non scollegato è ripristinare, non effettuare il checkout.

git reset HEAD@{1}

5
o git reset "HEAD@{1}"in alcune shell come fish e powershell .. git reflogpuò anche essere utile per trovare il commit corretto.
steve cook

1
Utilizzare sempre virgolette singole nei comandi della shell a meno che non si desideri esplicitamente che la shell tenti di interpretare / espandere il contenuto. Ciò è particolarmente importante in casi come questo, in cui l'obiettivo è impedire alla shell di interpretare caratteri speciali. In questo modo, non è necessario sapere se la stringa contiene qualcosa di problematico.
Chris Page

L'ho fatto per dare uno sguardo indietro nel tempo, poi l'ho fatto git reset HEADper tornare dove ero ... ora non ho idea in quale stato si trovi il mio repository e tutto fa paura. Cosa dovrei fare ora?
theonlygusti

47

Credo che tu possa fare:

git reset HEAD@{1}

Per andare avanti nel tempo. Per andare avanti con più commit, utilizza HEAD @ {2}, HEAD @ {3} e così via.


20

Questo è ciò che sto usando per navigare avanti e indietro.

passare al commit successivo

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

passaggio al commit precedente

function p() {
    git checkout HEAD^1
}

Grazie! Lo sto usando adesso! Note per altri principianti come me : da riattaccare HEAD, si git checkout <current branch>allega all'ultimo commit. git checkout -b <new-branch-name>dal commit corrente consente modifiche nel nuovo ramo. git rebase -ifunziona anche. Inoltre , ho chiamato la mia n()funzione in modo nx()da evitare conflitti con il gestore delle versioni del nodo "n". Assicurati di controllare gli alias!
Steven Choi

function () {...} è per la creazione di un file di scripting bash Unix / Linux, vengo da Windows, un po 'difficile da capire in primo luogo
IcyBrk

9

Diciamo che F è l'ultimo commit su trunk(inserisci il tuo nome di ramo qui) ... puoi riferirti ad esso come trunk~0(o semplicemente trunk), E come trunk~1, D come trunk~2ecc.

Dai un'occhiata nel tuo reflog per ulteriori modi per nominare i commit.


1
~ torna indietro, non avanti, il tronco ~ 2 è A
EmmanuelMess

Sì. Questa risposta presuppone che tu abbia un ramo chiamato trunk che punta a Fe che tu sappia dove nella storia di quel ramo vuoi spostarti. Non sta cercando di spostarsi in avanti rispetto a HEAD, ma meno indietro rispetto al tronco.
Inutile

@EmmanuelMess come sta trunk~2A?
theonlygusti

@theonlygusti Ti allontani da HEAD due volte.
EmmanuelMess

Stai ancora assumendo che il ramo trunke la tua corrente HEADsiano identici, il che non è mostrato nella domanda, che ho affermato non è ciò che presumo, e che è molto improbabile a metà di unbisect
Inutile il

3

Attraversare all'indietro è banale poiché ti stai spostando giù dall'albero e c'è sempre una strada da percorrere

  function git_down
        git checkout HEAD^
  end

Quando si procede in avanti, si sta risalendo l'albero, quindi è necessario essere espliciti su quale ramo si sta mirando:

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

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


Anche tornare indietro non è del tutto unico, quando sono coinvolte le fusioni. Anche se di HEAD^solito è un valore predefinito ragionevole.
kdb

2

Se vuoi vedere avanti, puoi fare questo trucco, poiché Git non ha un comando rigoroso per questo.

git log --reverse COMMIT_HASH..

Esempio

Elenco degli hash della cronologia dei log:

A
B
C -> put this
D

utilizzando il comando git log --reverse C.., in uscita si vedrà B e A .


1

Probabilmente non è il modo più carino, ma puoi utilizzare git logper visualizzare l'elenco dei commit e quindi utilizzare git checkout [sha1 of D]per passare a D.


6
Non capisco, se è in C, git log gli mostrerà solo C, B e A.
Bilthon

Ok, capito, ma dovrai fare un git-log complicato come indicato nel link fornito da VonC
Bilthon

1

Ho appena fatto un test su questo. dì per esempio che sei nel ramo principale Quindi fai:

git checkout HEAD@{3}

Quindi la testa si stacca e puoi quindi riprovare per passare a qualsiasi altro commit:

git checkout HEAD@{4}

Una volta che hai finito di guardarti intorno, puoi tornare al tuo stato originale semplicemente effettuando il check-out in quel ramo. Nel mio esempio: master branch

git checkout master

Se non vuoi tornare allo stato originale e vuoi mantenere uno dei commit come testa e continuare da lì, allora devi diramare da lì. ad esempio, dopo "git checkout HEAD @ {4}", puoi emettere

git checkout -b MyNewBranch

0

Come soluzione alternativa, puoi semplicemente tornare a HEAD con

git checkout <branch>

E poi passa al commit che desideri, con

git checkout HEAD~<offset>

0

Se stai usando vs code, la cronologia di Git è un fantastico plugin in cui puoi vedere in modo efficiente i commit e controllare i loro contenuti nell'editor stesso. controlla il link


0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

dove:

branchName è uguale al nome del ramo

commitInOrder è uguale a un commit in ordine dal primo commit nel ramo selezionato (quindi 1 è il primo commit, 2 è il secondo commit nel ramo, ecc.)

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.