Come posso riconciliare l’HEAD distaccato con master / origine?

Sono nuovo alla complessità della ramificazione di Git. Lavoro sempre su un singolo ramo e commetto modifiche e quindi spingo periodicamente all’origine remota.

Da qualche parte di recente, ho fatto un reset di alcuni file per farli uscire dal commit staging, e più tardi ho fatto un rebase -i per sbarazzarmi di un paio di commit locali recenti. Ora sono in uno stato che non capisco.

Nella mia area di lavoro, git log mostra esattamente quello che mi aspetterei: sono sul treno giusto con i commit che non volevo, e nuovi lì, ecc.

Ma ho semplicemente spinto verso il repository remoto, e cosa c’è di diverso? Un paio dei commit che ho ucciso nel rebase sono stati spinti, e quelli nuovi che sono stati commessi localmente non ci sono.

Penso che “master / origine” sia scollegato da HEAD, ma non sono al 100% chiaro su cosa significhi, come visualizzarlo con gli strumenti della riga di comando e come risolverlo.

Innanzitutto, chiariamo cos’è HEAD e cosa significa quando è staccato.

HEAD è il nome simbolico per il commit attualmente estratto. Quando HEAD non è staccato (la situazione “normale” 1 : hai un ramo estratto), HEAD punta effettivamente al “ref” di un ramo e il ramo punta al commit. HEAD è quindi “attaccato” a un ramo. Quando si effettua un nuovo commit, il ramo su cui HEAD punta viene aggiornato per puntare al nuovo commit. HEAD segue automaticamente dal momento che punta solo al ramo.

  • git symbolic-ref HEAD produce refs/heads/master
    Il ramo chiamato “master” è estratto.
  • git rev-parse refs/heads/master yield 17a02998078923f2d62811326d130de991d1a95a
    Quel commit è l’attuale punta o “testa” del ramo master.
  • git rev-parse HEAD restituisce anche 17a02998078923f2d62811326d130de991d1a95a
    Questo è ciò che significa essere un “riferimento simbolico”. Punta ad un object attraverso qualche altro riferimento.
    (I riferimenti simbolici sono stati inizialmente implementati come collegamenti simbolici, ma in seguito sono stati convertiti in file semplici con interpretazione extra in modo che possano essere utilizzati su piattaforms che non dispongono di collegamenti simbolici.)

Abbiamo HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a

Quando HEAD è staccato, punta direttamente a un commit, invece di indirizzare indirettamente a uno attraverso un ramo. Puoi pensare a un TESTO distaccato come su un ramo senza nome.

  • git symbolic-ref HEAD fallisce con fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEAD restituisce 17a02998078923f2d62811326d130de991d1a95a
    Poiché non è un riferimento simbolico, deve puntare direttamente al commit stesso.

Abbiamo HEAD17a02998078923f2d62811326d130de991d1a95a

La cosa importante da ricordare con un HEAD distaccato è che se il commit a cui punta è altrimenti non referenziato (nessun altro ref può raggiungerlo), allora diventerà “penzoloni” quando esegui il checkout di qualche altro commit. Alla fine, tali impegni penzolanti verranno eliminati attraverso il processo di raccolta dei dati inutili (per impostazione predefinita, vengono conservati per almeno 2 settimane e potrebbero essere mantenuti più lunghi facendo riferimento al reflog di HEAD).

1 È perfettamente corretto fare un lavoro “normale” con un HEAD distaccato, devi solo tenere traccia di ciò che stai facendo per evitare di dover pescare la storia abbandonata dal reflog.


I passaggi intermedi di un rebase interattivo vengono eseguiti con un HEAD distaccato (parzialmente per evitare di inquinare il reflog del ramo attivo). Se si completa l’operazione completa di rebase, aggiornerà il ramo originale con il risultato cumulativo dell’operazione di rebase e ricollegherà HEAD al ramo originale. La mia ipotesi è che non hai mai completato completamente il processo di rebase; questo ti lascerà con un HEAD distaccato che punta al commit che è stato più recentemente elaborato dall’operazione rebase.

Per recuperare dalla situazione, dovresti creare un ramo che punta al commit attualmente indicato dal tuo HEAD distaccato:

 git branch temp git checkout temp 

(questi due comandi possono essere abbreviati come git checkout -b temp )

Questo ricollegherà il tuo HEAD al nuovo ramo temp .

Successivamente, dovresti confrontare il commit corrente (e la sua cronologia) con il ramo normale su cui ti aspettavi di lavorare:

 git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp git diff master temp git diff origin/master temp 

(Probabilmente vorrai sperimentare le opzioni del registro: aggiungi -p , lascia fuori --pretty=… per vedere l’intero messaggio del registro, ecc.)

Se il tuo nuovo ramo temp sembra buono, potresti voler aggiornare (ad esempio) il master per indicarlo:

 git branch -f master temp git checkout master 

(questi due comandi possono essere abbreviati come git checkout -B master temp )

È quindi ansible eliminare il ramo temporaneo:

 git branch -d temp 

Infine, probabilmente vorrai spingere la storia ripristinata:

 git push origin master 

Potrebbe essere necessario aggiungere --force alla fine di questo comando per spingere se il ramo remoto non può essere “inoltrato rapidamente” al nuovo commit (ovvero hai eliminato, riscritto alcuni commit esistenti o riscritto in altro modo un po ‘di storia ).

Se ti trovavi nel mezzo di un’operazione di rebase dovresti probabilmente ripulirlo. È ansible verificare se un rebase fosse in corso cercando la directory .git/rebase-merge/ . È ansible pulire manualmente il rebase in corso semplicemente cancellando quella directory (ad es. Se non si ricorda più lo scopo e il contesto dell’operazione di rebase triggers). Di solito dovresti usare git rebase --abort , ma questo fa un po ‘di reset extra che probabilmente vuoi evitare (sposta HEAD al branch originale e lo reimposta al commit originale, che annullerà parte del lavoro che abbiamo fatto sopra ).

Basta fare questo:

 git checkout master 

Oppure, se hai delle modifiche che vuoi mantenere, fai questo:

 git checkout -b temp git checkout -B master temp 

Mi sono imbattuto in questo problema e quando ho letto la risposta più votata:

HEAD è il nome simbolico per il commit attualmente estratto.

Ho pensato: Ah-ha! Se HEAD è il nome simbolico per il commit di checkout currenlty, posso riconciliarlo con master rebasandolo contro master :

 git rebase HEAD master 

Questo comando:

  1. controlla master
  2. identifica il genitore si impegna di HEAD nuovo al punto HEAD divergente dal master
  3. suona quelli che si impongono in cima al master

Il risultato finale è che tutti i commit che erano in HEAD ma non master sono poi anche in master . master rimangono estratti.


Per quanto riguarda il telecomando:

un paio dei commit che ho ucciso nel rebase sono stati spinti, e quelli nuovi commessi localmente non ci sono.

La cronologia remota non può più essere inoltrata rapidamente utilizzando la cronologia locale. Dovrai forzare a spingere ( git push -f ) per sovrascrivere la cronologia remota. Se hai dei collaboratori, di solito ha senso coordinarli con loro in modo che tutti siano sulla stessa pagina.

Dopo aver inviato il master origin remota, l’ origin/master ramo di tracciamento remoto verrà aggiornato in modo che punti allo stesso commit di master .

Guarda qui per la spiegazione di base della testa staccata:

http://git-scm.com/docs/git-checkout

Riga di comando per visualizzarla:

 git branch 

o

 git branch -a 

otterrai l’output come di seguito:

 * (no branch) master branch1 

Il * (no branch) mostra che sei in testa staccata.

Potresti essere arrivato in questo stato facendo un git checkout somecommit un git checkout somecommit ecc. E ti avrebbe avvisato con quanto segue:

Sei nello stato di “TESTA distaccata”. Puoi guardarti intorno, apportare modifiche sperimentali e commetterli, e puoi scartare qualsiasi commit effettuato in questo stato senza influire su eventuali rami eseguendo un altro checkout.

Se vuoi creare un nuovo ramo per conservare i commit che crei, puoi farlo (ora o più tardi) usando di nuovo -b con il comando checkout. Esempio:

git checkout -b new_branch_name

Ora, per metterli sul master:

Fai un git reflog o anche solo git log e annota i tuoi commit. Ora git checkout master e git merge i commit.

 git merge HEAD@{1} 

Modificare:

Per aggiungere, usa git rebase -i non solo per eliminare / uccidere i commit che non ti servono, ma anche per modificarli. Basta menzionare “modifica” nella lista di commit e sarai in grado di modificare il tuo commit e quindi emettere una git rebase --continue – continua per andare avanti. Ciò avrebbe assicurato che non sei mai entrato in un TESTO distaccato.

Ottieni il tuo impegno distaccato sul proprio ramo

Basta eseguire git checkout -b mynewbranch .

Quindi esegui git log e vedrai che commit è ora HEAD su questo nuovo ramo.

se hai appena imparato il ramo e vuoi tornare a “sviluppare” o una funzione, fai questo:

 git checkout origin/develop 

Nota: verificare origine / sviluppo .

Sei nello stato di TESTA distaccato . Puoi guardarti intorno, apportare modifiche sperimentali e commetterli, e puoi scartare qualsiasi commit effettuato in questo stato senza influire su eventuali rami eseguendo un altro pagamento …

poi

 git checkout -b develop 

Funziona 🙂

Se vuoi spingere il tuo attuale TEST distaccato (controlla il git log prima), prova:

 git push origin HEAD:master 

per inviare il tuo capo distaccato al ramo principale all’origine. Se la tua spinta viene respinta, prova prima a git pull origin master per ottenere le modifiche dall’origine. Se non ti preoccupi delle modifiche da origine e viene rifiutato, perché hai fatto qualche rebase intenzionale e vuoi sostituire origin / master con il tuo ramo attualmente distaccato, puoi forzarlo ( -f ). Nel caso in cui hai perso un accesso ai commit precedenti, puoi sempre eseguire git reflog per vedere la cronologia da tutti i rami.


Per tornare su un ramo principale, mantenendo le modifiche, prova i seguenti comandi:

 git rebase HEAD master git checkout master 

Vedi: Git: “Non attualmente su nessun ramo.” C’è un modo semplice per tornare su un ramo, mantenendo le modifiche?

Quanto segue ha funzionato per me (utilizzando solo il master di settore):

 git push origin HEAD:master git checkout master git pull 

Il primo spinge il TEST distaccato all’origine remota.

Il secondo si sposta sul master del ramo.

Il terzo recupera l’HEAD che viene collegato al master di ramo.

Potrebbero sorgere problemi al primo comando se la spinta viene respinta. Ma questo non sarebbe più un problema di testa distaccata, ma riguarda il fatto che il TEST distaccato non è a conoscenza di alcune modifiche remote.

Se sei completamente sicuro che HEAD è il buono stato:

 git branch -f master HEAD git checkout master 

Probabilmente non puoi spingere all’origine, dal momento che il tuo padrone si è allontanato dall’origine. Se sei sicuro che nessun altro stia utilizzando il repository, puoi forzare a spingere:

 git push -f 

Molto utile se ci si trova su un ramo di funzionalità che nessun altro sta utilizzando.

Tutto quello che devi fare è ‘git checkout [nome-ramo]’ dove [nome-diramazione] è il nome del ramo originale dal quale sei entrato in uno stato di testa distaccato. Il (staccato da asdfasdf) scomparirà.

Quindi, per esempio, nel ramo ‘dev’ controlli il commit asdfasd14314 ->

 'git checkout asdfasd14314' 

sei ora in uno stato di testa distaccato

‘git branch’ elencherà qualcosa come ->

 * (detached from asdfasdf) dev prod stage 

ma per uscire dallo stato di testa distaccato e tornare a dev ->

 'git checkout dev' 

e quindi ‘git branch’ elencherà ->

 * dev prod stage 

ma questo è, naturalmente, se non si intende mantenere le modifiche dallo stato della testa staccata, ma mi trovo a fare questo molto non intendendo apportare modifiche, ma solo per guardare un commit precedente

Come indicato da Chris, ho avuto la seguente situazione

git symbolic-ref HEAD fallisce con fatal: ref HEAD is not a symbolic ref

Comunque git rev-parse refs/heads/master puntava a un buon commit da dove potevo recuperare (nel mio caso l’ultimo commit e puoi vedere che commit usando git show [SHA]

Dopo ho fatto molte cose disordinate, ma quello che sembra aver risolto è solo,

git symbolic-ref HEAD refs/heads/master

E la testa è attaccata!

Ho appena incontrato questo problema oggi e sono abbastanza sicuro di averlo risolto facendo:

 git branch temp git checkout master git merge temp 

Ero sul mio computer di lavoro quando ho capito come farlo, e ora mi imbatto nello stesso problema sul mio personal computer. Quindi dovrò aspettare fino a lunedì quando torno al computer di lavoro per vedere esattamente come l’ho fatto.

Ho avuto lo stesso problema e l’ho risolto seguendo i seguenti passaggi.

Se hai bisogno di mantenere le tue modifiche

  1. Per prima cosa devi eseguire il comando git checkout master per riportarti al ramo principale.
  2. Se hai bisogno di mantenere le tue modifiche, esegui git checkout -b changes e git checkout -B master changes

Se non hai bisogno delle tue modifiche

  1. Per rimuovere tutti i file non tracciati dalla filiale, git clean -df .

  2. Quindi è necessario cancellare tutte le modifiche non applicate all’interno del repository. Per farlo devi eseguire git checkout --

  3. Infine devi rimettere il ramo al ramo master usando il comando git checkout master .

Ho avuto questo problema oggi, dove avevo aggiornato un sottomodulo, ma non ero su nessun ramo. Avevo già commesso, quindi la conservazione, il checkout, il disimpegno non avrebbe funzionato. Ho finito con il cherry-picking del commit della testa staccata. Quindi, subito dopo che mi sono impegnato (quando la spinta falliva), ho fatto:

 git checkout master git cherry-pick 99fe23ab 

Il mio pensiero è andato: sono su una testa distaccata, ma voglio essere padrona. Supponendo che il mio stato di distacco non sia molto diverso dal master, se potessi applicare il mio commit al master, sarei impostato. Questo è esattamente ciò che fa il cherry-pick.

Invece di fare git checkout origin/master

basta fare il git checkout master

allora il git branch confermerà il tuo ramo.

Se hai eseguito alcune commit in cima al master e vuoi semplicemente eseguire il “backwards merge” master (cioè vuoi che il master indichi HEAD ), il one-liner sarebbe:

 git checkout -B master HEAD 
  1. Questo crea un nuovo ramo chiamato master , anche se esiste già (che è come spostare il master ed è quello che vogliamo).
  2. Il ramo appena creato è impostato per puntare a HEAD , che è dove sei.
  3. Il nuovo ramo viene estratto, quindi in seguito sarai master .

L’ho trovato particolarmente utile nel caso di sotto-repository, che spesso si trovano anche in uno stato separato.

Per me è stato facile come cancellare di nuovo il ramo locale, dato che non avevo alcun commit locale che volevo spingere:

Così ho fatto:

 git branch -d branchname 

E poi controllando di nuovo il ramo:

 git checkout branchname 

In parole semplici, lo stato di TESTA distaccato significa che non sei controllato a HEAD (o punta) di nessun ramo .

Capire con un esempio

Un ramo nella maggior parte dei casi è una sequenza di commit multipli come:

Impegno 1: master -> branch_HEAD (123be6a76168aca712aea16076e971c23835f8ca)

Commit 2: master -> 123be6a76168aca712aea16076e971c23835f8ca -> branch_HEAD (100644a76168aca712aea16076e971c23835f8ca)

Come puoi vedere sopra in caso di sequenza di commit, il tuo ramo punta al tuo ultimo commit. Quindi, in tal caso, se effettui il checkout per commettere 123be6a76168aca712aea16076e971c23835f8ca, allora saresti in stato di testa distaccato poiché HEAD del tuo ramo punta a 100644a76168aca712aea16076e971c23835f8ca e tecnicamente sei stato escluso da HEAD di nessun ramo. Quindi, sei nello stato di TESTA distaccato.

Spiegazione teorica

In questo Blog è chiaro che un repository Git è un albero di commit, con ogni commit che punta al suo antenato con ogni puntatore di commit che viene aggiornato e questi puntatori a ogni ramo sono memorizzati nelle sottodirectory .git / refs. I tag sono memorizzati in .git / refs / tag e i rami sono memorizzati in .git / refs / heads. Se guardi uno qualsiasi dei file, troverai che ogni tag corrisponde a un singolo file, con un hash di commit di 40 caratteri e come spiegato sopra da @Chris Johnsen e @Yaroslav Nikitenko, puoi controllare questi riferimenti.

Sono entrato in uno stato davvero stupido, dubito che qualcun altro lo troverà utile …. ma per ogni evenienza

 git ls-remote origin 0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b HEAD 6f96ad0f97ee832ee16007d865aac9af847c1ef6 refs/heads/HEAD 0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b refs/heads/master 

che alla fine ho risolto

 git push origin :HEAD 

Questo ha funzionato perfettamente per me:

1. git stash per salvare le modifiche locali

Se vuoi scartare le modifiche
git clean -df
git checkout -- .
git clean rimuove tutti i file non tracciati (attenzione: mentre non cancella i file ignorati menzionati direttamente in .gitignore, può eliminare i file ignorati che risiedono nelle cartelle) e git checkout cancella tutte le modifiche non modificate.

2. git checkout master per passare al ramo principale (supponendo che tu voglia usare master)
3. git pull per estrarre l’ultimo commit dal ramo master
4. git status per verificare che tutto sia perfetto

 On branch master Your branch is up-to-date with 'origin/master'. 

Quando mi trovo personalmente in una situazione in cui risulta che ho apportato alcune modifiche mentre non sono nel master (ovvero HEAD è scollegato sopra il master e non ci sono commit in mezzo) la memorizzazione potrebbe aiutare:

 git stash # HEAD has same content as master, but we are still not in master git checkout master # switch to master, okay because no changes and master git stash apply # apply changes we had between HEAD and master in the first place 

Nel mio caso, ho eseguito lo git status , e ho visto che avevo alcuni file non tracciati nella mia directory di lavoro.

Per far funzionare il rebase, dovevo semplicemente pulirli (dato che non ne avevo bisogno).

Se stai usando EGit in Eclipse: supponi che il tuo master sia il tuo ramo di sviluppo principale

  • ti impegni a cambiare ramo, normalmente uno nuovo
  • quindi tirare dal telecomando
  • quindi fai clic con il pulsante destro del mouse sul nodo del progetto, scegli il team, quindi scegli la cronologia
  • quindi fai clic con il pulsante destro del mouse sul master, quindi scegli il check out
  • se Eclipse ti dice, ci sono due master uno locale remoto, scegli il telecomando

Dopodiché dovresti essere in grado di ricolbind il master di origine.

 git checkout checksum # You could use this to peek previous checkpoints git status # You will see HEAD detached at checksum git checkout master # This moves HEAD to master branch