Come posso spostare HEAD indietro in una posizione precedente? (Testa distaccata) e Annulla commit


179

In Git, stavo cercando di fare un squash commitunendomi in un altro ramo e poi ripristinando HEADla posizione precedente tramite:

git reset origin/master

Ma devo uscire da questo. Come posso spostare HEAD nella posizione precedente?

Ho il frammento SHA-1 ( 23b6772) del commit in cui devo spostarlo. Come posso tornare a questo commit?


12
HEAD è solo un puntatore alla posizione corrente (o revisione per essere precisi). git checkout 23b6772dovresti ... dovrebbe.
Yaroslav Admin,


1
@YaroslavAdmin No dovrebbe non . Il check out diretto di un commit è il motivo per cui si è verificato lo stato HEAD distaccato (poiché i rami di tracciamento remoto non possono essere estratti da soli e rinviano automaticamente al commit a cui puntano quando si tenta di farlo come ha fatto OP) Inoltre, scusate il negromantico commento :-) Spero che il problema iniziale sia già risolto ...
RomainValeri

Risposte:


398

Prima di rispondere, aggiungiamo alcuni retroscena, spiegando di cosa si HEADtratta.

First of all what is HEAD?

HEADè semplicemente un riferimento al commit corrente (più recente) sul ramo corrente.
Può essercene solo uno HEADin qualsiasi momento (escluso git worktree).

Il contenuto di HEADè memorizzato all'interno .git/HEADe contiene i 40 byte SHA-1 del commit corrente.


detached HEAD

Se non si esegue l'ultimo commit, ovvero HEADviene chiamato un commit precedente nella cronologia detached HEAD.

Inserisci qui la descrizione dell'immagine

Sulla riga di comando, sarà simile al seguente: SHA-1 invece del nome del ramo poiché HEADnon punta alla punta del ramo corrente:

Inserisci qui la descrizione dell'immagine

Inserisci qui la descrizione dell'immagine


Alcune opzioni su come recuperare da una HEAD staccata:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Questo verificherà il nuovo ramo che punta al commit desiderato.
Questo comando eseguirà il checkout per un determinato commit.
A questo punto, puoi creare un ramo e iniziare a lavorare da questo punto in poi.

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Puoi sempre usare refloganche quello.
git reflog visualizzerà qualsiasi modifica che ha aggiornato il HEADe il checkout della voce di reflog desiderata HEADriporterà questo commit.

Ogni volta che HEAD viene modificato, ci sarà una nuova voce in reflog

git reflog
git checkout HEAD@{...}

Questo ti riporterà al commit desiderato

Inserisci qui la descrizione dell'immagine


git reset --hard <commit_id>

"Sposta" HEAD indietro al commit desiderato.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Nota: ( da Git 2.7 ) puoi anche usare git rebase --no-autostashanche questo.

git revert <sha-1>

"Annulla" l'intervallo di commit o commit specificato.
Il comando reset "annulla" tutte le modifiche apportate nel commit dato.
Verrà eseguito il commit di un nuovo commit con la patch di annullamento mentre il commit originale rimarrà anche nella cronologia.

# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Questo schema illustra quale comando fa cosa.
Come puoi vedere lì, reset && checkoutmodifica il HEAD.

Inserisci qui la descrizione dell'immagine


Se non si esegue l'ultimo commit, il che significa che HEAD punta a un commit precedente nella cronologia chiamato HEAD distaccato, a meno che tale commit precedente nella cronologia non sia la punta di un ramo diverso. Nella mia esperienza, potresti dire che sei distaccato se HEAD non punta a un commit che è indicato anche da qualsiasi ramo. Questo non si applica ai tag.
Tim

Puoi essere in HEAD distaccato e allo stesso tempo avere un ramo con lo stesso commit dell'HEAD di questo ramo. Non capisco il tuo commento
CodeWizard

3
Ho dei problemi con l'uso del markup del codice inline per le intestazioni :)
jub0bs

Impossibile trovare un modo migliore per enfatizzarlo. sentiti libero di modificare. siete più che benvenuti
CodeWizard,

22

Fare

git reset 23b6772

Per vedere se sei nella posizione giusta:

git status

Vedrai qualcosa

Sul master della filiale La tua filiale è dietro "origine / master" di 17 commit e può essere fatta avanzare rapidamente.

Quindi riscrivi la cronologia sul telecomando per riflettere la modifica:

git push --force-with-lease // a useful command @oktober mentions in comments

1
Sii eccezionalmente attento con git push --force. In molte situazioni ti renderà la persona meno popolare della squadra per un po 'di tempo ....
Kay V

da aggiungere alla nota sopra, ho appena trovato questa citazione su about.gitlab.com/blog/2014/11/26/keeping-your-code-protected e ho dovuto aggiungerlo: "Un singolo comando git - force può facilmente rovinare la giornata a molte persone: [186 Jenkins] i repository hanno riavvolto le loro diramazioni per indicare impegni più vecchi, e in effetti i nuovi impegni sono stati collocati in modo errato dopo la brutta spinta all'inganno ". - sviluppatore molto impopolare ....
Kay V

2
@KayV dare un'occhiata a git push --force-with-lease(articolo Thoughtbot: thoughtbot.com/blog/git-push-force-with-lease )
ottobre

1
Bandiera utile, @oktober, e un buon articolo. Grazie per averlo aggiunto qui e per avermi fatto un ping.
Kay V,

1
grazie! questo mi ha aiutato a disfare una cattiva unione. poiché le fusioni non reagiscono allo stesso modo delle revertcommesse, mi sono trovato in una situazione incredibilmente difficile. force-with-leasemi ha dato la sicurezza di riscrivere la storia git del ramo senza influenzare il lavoro degli altri. Bravo!
anon58192932

11

La soluzione più rapida possibile (solo 1 passaggio)

Uso git checkout -

Vedrai Switched to branch <branch_name>. Conferma che è il ramo che desideri.


Breve spiegazione: questo comando riporterà HEAD nella sua ultima posizione. Vedi la nota sui risultati alla fine di questa risposta.


Mnemonico: questo approccio è molto simile all'utilizzo cd -per tornare alla directory precedentemente visitata. La sintassi e i casi applicabili sono una corrispondenza piuttosto buona (ad es. È utile quando si desidera che HEAD torni esattamente dove si trovava).


Soluzione più metodica (2 passaggi, ma memorabile)

L'approccio rapido risolve la domanda del PO. E se la tua situazione fosse leggermente diversa: supponi di aver riavviato Bash e di ritrovarti con HEAD staccato. In tal caso, ecco 2 passaggi semplici e facili da ricordare.

1. Scegli il ramo che ti serve

Uso git branch -v

Viene visualizzato un elenco di filiali locali esistenti. Prendi il nome della filiale che si adatta alle tue esigenze.

2. Spostare HEAD su di esso

Uso git checkout <branch_name>

Vedrai Switched to branch <branch_name>. Successo!


risultati

Con entrambi i metodi, ora puoi continuare ad aggiungere e impegnare il tuo lavoro come prima: le tue prossime modifiche verranno monitorate <branch_name>.

Si noti che entrambi git checkout -e git checkout <branch_name>forniranno istruzioni aggiuntive se sono state apportate modifiche mentre HEAD era scollegato.


Questo non funziona perché se lo faccio (supponendo che 8acc968 sia HEAD ~ 2), git checkout 8acc968allora git branch -vha MyBranchnell'elenco sotto ... ma poi git checkout MyBranchcancella i miei commenti.
amuliar

Ciao @amuliar - git checkout 8acc968verificherà un commit, non un ramo. Se MyBranchhai i commit che vuoi, prova git checkout MyBranch. Se non contiene le modifiche in commit 8acc968, è necessario unire tali modifiche dopo aver verificato il ramo.
Kay V,

Grazie per la risposta! Ho git checkoutvisto un precedente commit e volevo tornare all'ultimo commit. Ma senza l'ultimo hash di commit, ero praticamente perso. Questa soluzione è perfetta per la mia situazione!
zyy

4

La domanda può essere letta come:

Ero in stato distaccato con HEADat 23b6772e digitato git reset origin/master(perché volevo schiacciare). Ora ho cambiato idea, come posso tornare ad HEADessere a 23b6772?

La risposta semplice è: git reset 23b6772

Ma ho risposto a questa domanda perché mi sono stancato di scrivere (copiare e incollare) commit hash o la sua abbreviazione ogni volta che volevo fare riferimento al precedente HEADe cercavo su Google se ci fosse qualche tipo di stenografia.

Si scopre che c'è!

git reset -(o nel mio caso git cherry-pick -)

Che per inciso è stato lo stesso cd -di tornare alla directory corrente precedente in * nix! Quindi evviva, ho imparato due cose con una fava.


0

Quando si esegue il comando, git checkout commit_idHEAD si stacca 13ca5593d(say commit-id)e il ramo sarà più disponibile.

Torna alla posizione precedente esegui il comando saggio:

  1. git pull origin branch_name (dì padrone)
  2. git checkout branch_name
  3. git pull origin branch_name

Tornerai alla posizione precedente con un commit aggiornato dal repository remoto.


0

Oggi, ho erroneamente verificato un commit e ho iniziato a lavorarci su, facendo alcuni commit su uno stato HEAD distaccato. Quindi ho spinto al ramo remoto usando il seguente comando:

git push origin HEAD: <My-remote-branch>

Poi

git checkout <My-remote-branch>

Poi

git pull

Alla fine ho ottenuto tutte le modifiche nel mio ramo che ho fatto staccando HEAD.


0

Questa potrebbe non essere una soluzione tecnica, ma funziona. (se qualcuno del tuo compagno di squadra ha la stessa filiale in locale)

Supponiamo che il nome della tua filiale sia branch-xxx .

I passaggi per risolvere:

  • Non aggiornare o tirare - niente
  • Basta creare un nuovo ramo ( branch-yyy ) da branch-xxx sulla sua macchina
  • Questo è tutto, tutte le modifiche esistenti saranno in questo nuovo ramo ( branch-yyy ). Puoi continuare il tuo lavoro con questo ramo.

Nota: ancora una volta, questa non è una soluzione tecnica, ma sarà sicuramente di aiuto.

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.