Som nováčikom v zložitosti vetvenia systému Git. Vždy pracujem na jednej vetve a odovzdávam zmeny a potom ich pravidelne posielam do svojho vzdialeného pôvodu.
Niekde som nedávno urobil reset niektorých súborov, aby som ich dostal z commit staging, a neskôr som urobil rebase -i
, aby som sa zbavil niekoľkých nedávnych lokálnych revízií. Teraz som v stave, ktorému celkom nerozumiem.
V pracovnej oblasti mi git log
ukazuje presne to, čo by som očakával - som v správnom vlaku s commitmi, ktoré som nechcel odstrániť, a novými, ktoré tam sú, atď.
Ale práve som tlačil do vzdialeného repozitára a to, čo tam je, je iné -- pár revízií, ktoré som zabil v rebase, sa tlačilo a nové, ktoré som commitoval lokálne, tam nie sú.
Myslím, že "master/origin" je oddelený od HEAD, ale nie je mi na 100 % jasné, čo to znamená, ako to vizualizovať pomocou nástrojov príkazového riadku a ako to opraviť.
Najprv si objasnime, čo je HEAD a čo znamená, keď je odpojený.
HEAD je symbolický názov pre aktuálne skontrolovanú revíziu. Keď HEAD nie je odpojené ("normálna" 1 situácia: máte odkontrolovanú vetvu), HEAD v skutočnosti ukazuje na "ref" vetvy a vetva ukazuje na revíziu. HEAD je teda "pripojený" k vetve. Keď vykonáte novú revíziu, vetva, na ktorú HEAD ukazuje, sa aktualizuje tak, aby ukazovala na novú revíziu. HEAD nasleduje automaticky, pretože práve ukazuje na vetvu.
git symbolic-ref HEAD
dáva refs/heads/master
Vetva s názvom "master" je odškrtnutá.git rev-parse refs/heads/master
yield 17a02998078923f2d62811326d130de991d1a95a
Táto revízia je aktuálny tip alebo "hlava" vetvy master.git rev-parse HEAD
je tiež 17a02998078923f2d62811326d130de991d1a95a
Toto je to, čo znamená byť "symbolickým ref". Ukazuje na objekt prostredníctvom nejakého iného odkazu.Máme HEAD
→ refs/heads/master
→ 17a02998078923f2d62811326d130de991d1a95a
Keď je HEAD oddelené, ukazuje priamo na revíziu - namiesto toho, aby na ňu ukazovalo nepriamo cez vetvu. Odpojený HEAD si môžete predstaviť ako nepomenovanú vetvu.
git symbolic-ref HEAD
zlyhá s hlásením fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
je 17a02998078923f2d62811326d130de991d1a95a
Keďže nejde o symbolický ref, musí ukazovať priamo na samotnú revíziu.Máme HEAD
→ 17a02998078923f2d62811326d130de991d1a95a
Dôležitá vec, ktorú si treba zapamätať pri odpojenom HEAD, je, že ak je revízia, na ktorú ukazuje, inak neodkazovaná (žiadny iný ref sa na ňu nedostane), potom sa stane "visiacou", keď odhlásite nejakú inú revíziu. Nakoniec budú takéto visiace revízie vyradené z procesu zberu odpadu (štandardne sa uchovávajú aspoň 2 týždne a môžu sa uchovávať dlhšie, ak sa na ne odkazuje v reflogu HEAD).
1 Je úplne v poriadku vykonávať "normálnu" prácu s odpojeným HEAD, len musíte sledovať, čo robíte, aby ste nemuseli loviť zahodenú históriu z reflogu.
Medzikroky interaktívneho rebase sa vykonávajú s oddeleným HEAD (čiastočne preto, aby sa zabránilo znečisteniu reflogu aktívnej vetvy). Ak dokončíte celú operáciu rebase, aktualizuje sa pôvodná vetva kumulatívnym výsledkom operácie rebase a HEAD sa znovu pripojí k pôvodnej vetve. Predpokladám, že ste proces rebase nikdy úplne nedokončili; zostane vám tak odpojený HEAD ukazujúci na revíziu, ktorá bola naposledy spracovaná operáciou rebase.
Aby ste sa z tejto situácie dostali, mali by ste vytvoriť vetvu, ktorá bude ukazovať na revíziu, na ktorú v súčasnosti ukazuje váš odpojený HEAD:
git branch temp
git checkout temp
(tieto dva príkazy možno skrátiť ako git checkout -b temp
)
Týmto sa váš HEAD znovu pripojí k novej vetve temp
.
Potom by ste mali porovnať aktuálnu revíziu (a jej históriu) s normálnou vetvou, na ktorej ste očakávali, že budete pracovať:
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp
(Pravdepodobne budete chcieť experimentovať s možnosťami denníka: pridať -p
, vynechať --pretty=...
, aby ste videli celú správu denníka, atď.)
Ak vaša nová vetva temp
vyzerá dobre, možno budete chcieť aktualizovať (napr.) master
, aby na ňu ukazoval:
git branch -f master temp
git checkout master
(tieto dva príkazy možno skrátiť ako git checkout -B master temp
)
Potom môžete dočasnú vetvu odstrániť:
git branch -d temp
Nakoniec budete pravdepodobne chcieť poslať obnovenú históriu:
git push origin master
Možno budete musieť pridať --force
na koniec tohto príkazu na push, ak vzdialená vetva nemôže byť "rýchlo preklopená" na novú revíziu (t.j. upustili ste, alebo prepísali nejakú existujúcu revíziu, alebo inak prepísali nejaký kúsok histórie).
Ak ste boli uprostred operácie rebase, mali by ste ju pravdepodobne vyčistiť. To, či prebiehala rebaze, môžete skontrolovať vyhľadaním adresára .git/rebase-merge/
. Prebiehajúcu rebase môžete ručne vyčistiť tak, že tento adresár jednoducho odstránite (napr. ak si už nepamätáte účel a kontext aktívnej operácie rebase). Zvyčajne by ste použili git rebase --abort
, ale to robí nejaké dodatočné resetovanie, ktorému sa pravdepodobne chcete vyhnúť (presúva HEAD späť do pôvodnej vetvy a resetuje ju späť na pôvodnú revíziu, čo zruší časť práce, ktorú sme vykonali vyššie).
Základné vysvetlenie odpojenej hlavy nájdete tu:
http://git-scm.com/docs/git-checkout.
Príkazový riadok na vizualizáciu:
git branch
alebo
git branch -a
dostanete výstup ako je uvedený nižšie:
* (no branch)
master
branch1
Znak * (bez vetvy)
ukazuje, že ste v oddelenej hlave.
Do tohto stavu ste sa mohli dostať vykonaním príkazu git checkout somecommit
atď. a upozornilo by vás to nasledujúcim spôsobom:
Ste v stave 'detached HEAD'. Ste môžete sa rozhliadnuť, urobiť experimentálne zmeny a odovzdať ich a môžete zahodiť všetky revízie, ktoré ste v tomto stave bez toho, aby ste ovplyvnili akékoľvek vetvy vykonaním ďalšieho checkoutu.
Ak chcete vytvoriť novú vetvu zachovať vytvorené revízie, môžete to urobiť tak (teraz alebo neskôr) pomocou príkazu -b s príponou príkazom checkout znova. Príklad:
git checkout -b new_branch_name
Nynie, ako ich dostať na master:
Vykonajte git reflog
alebo dokonca len git log
a zaznamenajte svoje revízie. Teraz git checkout master
a git merge
revízie.
git merge HEAD@{1}
Upravte:
Na doplnenie, použite git rebase -i
nielen na odstránenie/zabitie revízií, ktoré nepotrebujete, ale aj na ich úpravu. Stačí v zozname revízií uviesť "editovať" a budete môcť zmeniť svoju revíziu a potom vydať príkaz git rebase --continue
na pokračovanie. To by zabezpečilo, že by ste nikdy neprišli k odpojenému HEAD.