Jak mogę pogodzić odłączone HEAD z master/origin?
Jestem nowy w zawiłościach rozgałęziania Git. Zawsze pracuję na pojedynczej gałęzi i wprowadzam zmiany, a następnie okresowo popycham do mojego zdalnego źródła.
Gdzieś ostatnio zrobiłem reset niektórych plików, aby usunąć je z commit staging, a później wykonałem rebase -i
, aby pozbyć się kilku ostatnich lokalnych commitów. Teraz jestem w stanie, którego nie do końca rozumiem.
W moim obszarze roboczym, git log
pokazuje dokładnie to, czego bym się spodziewał - jestem na właściwym torze z commitami, których nie chciałem, a nowe są tam, itd.
Ale właśnie pchnąłem do zdalnego repozytorium, a to co tam jest jest inne - kilka commitów, które zabiłem w rebase zostało pchniętych, a nowych commitów popełnionych lokalnie tam nie ma.
Myślę, że "master/origin" jest odłączony od HEAD, ale nie jestem w 100% pewien, co to oznacza, jak to zobrazować za pomocą narzędzi wiersza poleceń i jak to naprawić.
Po pierwsze, wyjaśnijmy czym jest HEAD i co oznacza, gdy jest odłączony.
HEAD jest symboliczną nazwą aktualnie sprawdzanego commitu. Kiedy HEAD nie jest odłączony (normalna 1 sytuacja: masz sprawdzoną gałąź), HEAD faktycznie wskazuje na "ref" gałęzi, a gałąź wskazuje na commit. HEAD jest więc "przywiązany" do gałęzi. Kiedy robisz nowy commit, gałąź, na którą wskazuje HEAD jest aktualizowana, aby wskazywać na nowy commit. HEAD podąża za tym automatycznie, ponieważ po prostu wskazuje na gałąź.
git symbolic-ref HEAD
dajerefs/heads/master
. Gałąź o nazwie "master" jest sprawdzana.git rev-parse refs/heads/master
yield17a02998078923f2d62811326d130de991d1a95a
. Ten commit jest aktualną końcówką lub "głową" gałęzi master.git rev-parse HEAD
również daje17a02998078923f2d62811326d130de991d1a95a
. To jest właśnie to, co oznacza bycie "symbolicznym refem". Wskazuje na obiekt poprzez jakieś inne odniesienie.(Symboliczne referencje były pierwotnie zaimplementowane jako dowiązania symboliczne, ale później zostały zmienione na zwykłe pliki z dodatkową interpretacją, aby mogły być używane na platformach, które nie mają symlinków).
Mamy
HEAD
→refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
.Kiedy HEAD jest odłączony, wskazuje bezpośrednio na commit - zamiast pośrednio wskazywać na niego poprzez gałąź. Możesz myśleć o odłączonym HEAD jak o gałęzi bez nazwy.
git symbolic-ref HEAD
kończy się niepowodzeniem zfatal: ref HEAD is not a symbolic ref
.git rev-parse HEAD
daje17a02998078923f2d62811326d130de991d1a95a
. Ponieważ nie jest to symboliczny ref, musi on wskazywać bezpośrednio na sam commit.Mamy
HEAD
→17a02998078923f2d62811326d130de991d1a95a
.Ważną rzeczą do zapamiętania z odłączonym HEAD jest to, że jeśli commit, na który wskazuje jest w inny sposób nieodwołany (żaden inny ref nie może do niego dotrzeć), to będzie on "dyndał" kiedy sprawdzisz inny commit. Ostatecznie, takie "miotające się" commit'y zostaną usunięte w procesie odśmiecania (domyślnie są przechowywane przez co najmniej 2 tygodnie, a mogą być przechowywane dłużej przez reflog HEAD'a).
1 Jest to całkowicie w porządku, aby wykonywać "normalną" pracę z odłączonym HEAD, musisz tylko śledzić, co robisz, aby uniknąć konieczności wyławiania zrzuconej historii z reflogu.
Pośrednie kroki interaktywnego rebase'u są wykonywane z odłączonym HEAD (częściowo po to, aby uniknąć zanieczyszczania reflogu aktywnej gałęzi). Jeśli zakończysz pełną operację rebase, zaktualizuje ona twoją oryginalną gałąź z łącznym wynikiem operacji rebase i ponownie dołączy HEAD do oryginalnej gałęzi. Zgaduję, że nigdy nie zakończyłeś w pełni procesu rebase; to pozostawi cię z odłączonym HEAD wskazującym na commit, który został ostatnio przetworzony przez operację rebase.
Aby wyjść z tej sytuacji, powinieneś utworzyć gałąź, która wskazuje na commit aktualnie wskazywany przez odłączony HEAD:
(te dwie komendy mogą być skrócone jako
git checkout -b temp
)Spowoduje to ponowne dołączenie twojego HEAD do nowej gałęzi
temp
.Następnie, powinieneś porównać aktualny commit (i jego historię) z normalną gałęzią, na której spodziewałeś się pracować:
(Prawdopodobnie będziesz chciał poeksperymentować z opcjami logów: dodać
-p
, opuścić--pretty=...
aby zobaczyć cały komunikat logu, itp.)Jeśli twój nowy oddział
temp
wygląda dobrze, możesz chcieć zaktualizować (np.)master
aby wskazywał na niego:(te dwie komendy mogą być skrócone jako
git checkout -B master temp
)Następnie możesz usunąć tymczasowy oddział:
Na koniec, prawdopodobnie będziesz chciał popchnąć przywróconą historię:
Możesz potrzebować dodać
--force
na końcu tej komendy do push, jeśli zdalna gałąź nie może być "fast-forwarded" do nowego commitu (tj. porzuciłeś, lub przepisałeś jakiś istniejący commit, lub w inny sposób przepisałeś jakiś fragment historii).Jeśli byłeś w trakcie operacji rebase, prawdopodobnie powinieneś to wyczyścić. Możesz sprawdzić, czy rebase był w toku, szukając katalogu
.git/rebase-merge/
. Możesz ręcznie wyczyścić trwający rebase przez usunięcie tego katalogu (np. jeśli nie pamiętasz już celu i kontekstu operacji rebase). Zwykle użyłbyśgit rebase --abort
, ale to powoduje dodatkowe resetowanie, którego prawdopodobnie chcesz uniknąć (przenosi HEAD z powrotem do oryginalnej gałęzi i resetuje go z powrotem do oryginalnego commitu, co cofnie część pracy, którą wykonaliśmy powyżej).Zobacz tutaj podstawowe wyjaśnienia dotyczące oderwanej głowy:
http://git-scm.com/docs/git-checkout
Linia poleceń, aby to zwizualizować:
lub
otrzymasz dane wyjściowe jak poniżej:
Znak
* (brak gałęzi)
pokazuje, że jesteś w odłączonej głowie.Mogłeś dojść do tego stanu wykonując
git checkout somecommit
itp. i system ostrzegłby cię następująco:Teraz, aby przenieść je na master:.
Zrób
git reflog
lub nawet po prostugit log
i zanotuj swoje polecenia. Terazgit checkout master
igit merge
z commitami.Edytuj:
Aby dodać, użyj
git rebase -i
nie tylko do usuwania / zabijania commitów, których nie'potrzebujesz, ale także do ich edycji. Wystarczy wspomnieć "edit" na liście commitów i będziesz mógł zmienić swój commit, a następnie wydaćgit rebase --continue
, aby przejść dalej. To zapewniłoby, że nigdy nie wszedłeś do odłączonego HEAD.Get your detached commit into its own branch
Po prostu uruchom
git checkout -b mynewbranch
.Następnie uruchom
git log
, a zobaczysz, że commit jest terazHEAD
na nowej gałęzi.