Stai controllando un vecchio commit e mantenendo la testa sul ramo master?


85

Attualmente per passare a un altro commit git (sullo stesso ramo ... in realtà, sul ramo master!), Sto eseguendo il comando

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Ora, ogni volta che lo faccio git mi dice che ora sono con una testa distaccata. Come posso passare a un commit più vecchio e mantenere comunque la testa sullo stesso ramo?


1
Dopo aver eseguito quel comando, il tuo riferimento HEAD cambia in quel commit. Non ha senso volere che la TESTA punti anche altrove.
Greg Hewgill

Da quello che ho capito dal messaggio di git, non punta da nessuna parte , il che è indesiderabile.
elisio divorato il

1
Il messaggio che Git mostra quando si controlla un commit specifico come quello dice "HEAD è ora su ea3d5ed ...", che ti dice che HEAD sta puntando da qualche parte. Sta solo puntando da qualche parte che non ha nessun altro nome tranne HEAD (al momento, poiché un nome git checkoutdi un altro commit o ramo sposterà HEAD in quella nuova posizione).
Greg Hewgill

Le risposte fornite spiegano adeguatamente questo o c'è qualcosa su cui potremmo essere più chiari? Sarei felice di chiarirti la mia risposta se non risponde alla tua domanda.
Brian Campbell

Se sei venuto qui cercando un modo per controllare un altro commit mantenendo l'HEAD completamente invariato (ad esempio per tornare a un commit più vecchio): git revert --no-commit 0766c053..HEADlo farà, dov'è 0766c053il commit che vuoi controllare. Questo è da stackoverflow.com/a/21718540/525872 .
Jo Liss

Risposte:


193

Il più delle volte quando lo faccio, eseguo il checkout in un ramo temporaneo:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Quindi, dopo aver finito, elimino semplicemente il ramo


80

Dipende da cosa vuoi fare quando effettui il checkout di quel commit. Se tutto ciò che stai facendo è verificarlo in modo da poter costruire o testare quella revisione, allora non c'è niente di sbagliato nel lavorare con una testa staccata. Ricorda solo di controllare un ramo effettivo prima di effettuare qualsiasi commit ( git checkout master, ad esempio), in modo da non creare commit che non siano inclusi in alcun ramo.

Se, invece, vuoi fare più commit a partire da quel punto, dovresti creare un branch. Se effettui commit che non sono referenziati da un ramo, possono perdersi facilmente e alla fine verranno ripuliti dal garbage collector di git, poiché nulla si riferisce a loro. Puoi creare un nuovo ramo eseguendo:

git checkout -b newbranch ea3d5ed

Per aiutare a visualizzare, ecco alcuni diagrammi che dimostrano come lavorare su una testa staccata differisce dal lavorare su un ramo.

Iniziamo con 3 commit su master, A, B e C. masterè il ramo corrente, quindi HEADpunta a master, che punta a commit C.

ABC
* - * - * <- master <- HEAD

Ora, se eseguiamo il commit, git creerà un commit che ha C come genitore (perché è il commit corrente, puntato da HEADvia master), e si aggiornerà masterper puntare a quel nuovo commit. Tutti i nostri commit sono ora in mastere HEADpunta al nuovo commit master.

ABCD
* - * - * - * <- master <- HEAD

Adesso controlliamo B, dandoci un distaccato HEAD.

ABCD
* - * - * - * <- master
   ^
    \-- CAPO

Tutto funziona bene qui; possiamo guardare tutti i file, costruire il nostro programma, testarlo, ecc. Possiamo anche creare nuovi commit; ma se lo facciamo, non c'è nessun ramo su cui siamo, quindi non possiamo puntare nessun ramo a quel nuovo commit. L'unica cosa che lo indica è HEAD:

ABCD
* - * - * - * <- master
    \
     * <- TESTA
     E

Se in seguito decidiamo di effettuare masternuovamente il check-out , non ci sarà nulla che si riferisca a E.

ABCD
* - * - * - * <- master <- HEAD
    \
     *
     E

Dal momento che non c'è nulla che si riferisca ad esso, può essere difficile da trovare e git considera i commit senza riferimenti da abbandonare (accadono abbastanza comunemente se si rebase, si squash patch o si fanno altre manipolazioni della cronologia divertenti; di solito rappresentano patch abbandonate che non ti interessa più). Dopo un certo periodo di tempo, git lo considererà spazzatura, da scartare la prossima volta che viene eseguito il garbage collection.

Quindi, invece di controllare una semplice revisione e ottenere una testa distaccata, se hai la sensazione di fare più commit, dovresti usare git checkout -b branch Bper creare un ramo e controllarlo. Ora i tuoi commit non andranno persi, poiché saranno inclusi in un ramo, a cui puoi facilmente fare riferimento e unire in seguito.

ABCD
* - * - * - * <- master
   ^
    \ - ramo <- TESTA

Se ti dimentichi di farlo e crei commit da un ramo, non devi preoccuparti. È possibile creare un ramo che fa riferimento alla revisione della testina con git checkout -b branch. Se sei già tornato al masterramo e ti rendi conto di aver dimenticato un commit vagante, puoi trovarlo usando git reflog, che ti mostrerà una cronologia di ciò a cui i commit HEADhanno puntato negli ultimi giorni. Tutto ciò che è ancora nel reflog non verrà raccolto nella spazzatura e generalmente i riferimenti vengono conservati nel reflog per almeno 30 giorni.


Non è del tutto chiaro per me perché quando controlli un vecchio commit la testa si stacca . Forse il problema è su cosa significa una testa staccata ? D'altra parte, se il controllo di un vecchio commit con una testa staccata lo andrà solo perso, perché qualcuno dovrebbe farlo? Solo per scherzare e provare le cose? Perché git lo consente, in primo luogo?
elisio divorato il

5
@devoured elysium "testa staccata" significa che hai un HEADref che punta direttamente a SHA-1 di un commit, piuttosto che a un ramo che a sua volta punta a un commit. Poiché la tua testa non fa riferimento a un ramo, Git non sa quale ramo aggiornare quando aggiungi nuovi commit. Come ho spiegato all'inizio della mia risposta, va benissimo avere una testa distaccata se torni a una vecchia versione solo per creare o testare il codice; puoi sempre tornare a una filiale con git checkout mastero simili. È un problema solo se ti impegni mentre hai la testa distaccata.
Brian Campbell

@BrianCampbell - Dopo aver effettuato commit sul ramo (dove si trova attualmente la tua testa), unisci il ramo in B e unisci B nel master. Cosa dovresti fare dopo?
amey1908

La tua spiegazione ha fatto "clic" su molte altre cose per me. Grazie. Ora potrei finalmente aver capito git ...
Joe

8

Se vuoi semplicemente tornare a un commit precedente per giocarci senza apportare modifiche, puoi farlo

git co <previous-commit-id>

ti troverai su un ramo chiamato "(nessun ramo)" dopo questo comando.

Confermalo da

git br

Dopo aver giocato con questo codice precedentemente impegnato, puoi passare al ramo in cui ti trovavi

git co <the-branch-you-were-on>

Il "(nessun ramo)" verrà eliminato automaticamente. In questo modo non è necessario creare un ramo temporaneo.


5

Git's HEAD è semplicemente un puntatore che dice cosa c'è nella directory di lavoro. Se vuoi controllare un commit che non è il capo di un ramo, devi semplicemente reindirizzare il tuo HEAD in modo che punti a quel commit. Non c'è modo di aggirarlo. Puoi creare un ramo temporaneo in quel commit, ma HEAD sarà comunque diretto lontano dal master.

Questa è la breve spiegazione. Si spera che il livello di dettaglio di seguito aiuti a capire in che modo HEAD e master sono diversi:

Normalmente, le cose assomigliano a questo:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

Vale a dire: “Il genitore di C è B, e il genitore di B è A. Il branch master punta a C, e attualmente ho controllato il contenuto di master. Inoltre, quando mi impegno, il master verrà aggiornato. "

Alcune ipotesi sono implicite in questo che sono necessarie per una comprensione completa del grafo di commit. Vale a dire, i commit si riferiscono solo ai loro genitori, e il contenuto di un branch sono quei commit (e solo quei commit) che possono essere raggiunti seguendo i link genitore. Il contenuto (non modificato) dell'albero di lavoro e dell'indice devono corrispondere al commit denominato da HEAD, indirettamente ("simbolico") o direttamente ("scollegato").

Pertanto, se si desidera eseguire il check out di un vecchio commit, è necessario aggiornare HEAD in modo che punti al commit desiderato. git-checkoutfa proprio questo:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Ora, hai lasciato il tuo ramo dietro di te, dato che stai guardando qualcosa di vecchio. Va benissimo, come ti dice con calma il consiglio della "testa distaccata" (enfasi mia):

Puoi guardarti intorno, apportare modifiche sperimentali e eseguirne il commit e puoi annullare qualsiasi commit effettuato in questo stato senza influire sui rami eseguendo un altro checkout.

D'altra parte, mentre resettando il tuo ramo ottieni anche HEAD dove deve essere, avrebbe un effetto molto diverso!

C
↓
B ← refs/heads/master ← HEAD
↓
A

Il commit C diventerà spazzatura, poiché hai dichiarato che non desideri più che faccia parte del ramo master.

In breve, tutto ciò che devi fare è capire cosa significa git per "HEAD" - è dove sei , non dove si trova un determinato ramo. E se dove sei non è uguale a dove si trova un ramo, non c'è altra scelta che usare una TESTA staccata.

(Forse guarda anche GitHub, gitk o gitweb per sfogliare la cronologia dei commit, se il deragliamento di HEAD continua a infastidirti.)


1

La domanda è un po 'vaga, ma se vuoi semplicemente cambiare i file nel tuo albero di lavoro, puoi semplicemente farlo:

git checkout [commit|branch] -- .

Puoi quindi mettere in scena le modifiche e creare un nuovo commit se lo desideri. Questo è abbastanza utile a volte.


0

Penso di aver capito le tue domande. Ecco cosa ho trovato per risolverlo. e non esiste una soluzione GUI, puoi solo usare il comando per risolverlo, ed è davvero semplice.

passaggio 1: crea un tag del vecchio commit che desideri ripristinare.

come tag v2.0

passaggio 2: git checkout v2.0

eccolo, ora il tuo HEAD punta al commit 'v2.0', ma il master punta ancora all'ultimo commit.

C:\Program Files\Git\doc\git\html\git-checkout.html questo documento può aiutarti

oppure digita git help <checkout>

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.