Hoe kan ik vrijstaand HEAD verzoenen met master/origin?
Ik ben nieuw in de branching complexiteit van Git. Ik werk altijd op een enkele branch en commit wijzigingen en push dan periodiek naar mijn oorsprong op afstand.
Ergens recentelijk, deed ik een reset van enkele bestanden om ze uit commit staging te halen, en later deed ik een rebase -i
om van een paar recente lokale commits af te komen. Nu ben ik in een toestand die ik niet helemaal begrijp.
In mijn werkgebied laat git log
precies zien wat ik'zou verwachten-- ik'ben op de goede weg met de commits die ik niet weg wilde hebben, en nieuwe commits daar, enz.
Maar ik heb net gepushed naar de remote repository, en wat's daar is is anders - een paar van de commits die ik'had gedood in de rebase werden gepushed, en de nieuwe commits die lokaal zijn gecommit'zijn er niet.
Ik denk dat "master/origin" is losgekoppeld van HEAD, maar ik'ben niet 100% duidelijk over wat dat betekent, hoe het te visualiseren met de opdrachtregel tools, en hoe het te repareren.
Laten we eerst verduidelijken wat HEAD is en wat het betekent als het wordt losgemaakt.
HEAD is de symbolische naam voor de huidige uitgecheckte commit. Als HEAD niet losgekoppeld is (de "normale"1 situatie: je hebt een branch uitgecheckt), dan wijst HEAD eigenlijk naar een branch's "ref" en de branch wijst naar de commit. HEAD is dus "verbonden" aan een branch. Als je een nieuwe vastlegging doet, dan wordt de tak waar HEAD naar wijst geüpdatet om naar de nieuwe vastlegging te wijzen. HEAD volgt automatisch, omdat het gewoon naar de tak wijst.
git symbolic-ref HEAD
geeftrefs/heads/master
De branch genaamd "master" wordt uitgechecked.git rev-parse refs/heads/master
levert17a02998078923f2d62811326d130de991d1a95a
op Die commit is de huidige tip of "head" van de master branch.git rev-parse HEAD
geeft ook17a02998078923f2d62811326d130de991d1a95a
Dit is wat het betekent om een "symbolische ref" te zijn. Het wijst naar een object via een andere verwijzing.(Symbolische refs werden oorspronkelijk geïmplementeerd als symbolische links, maar later veranderd in gewone bestanden met extra interpretatie, zodat ze gebruikt konden worden op platformen die geen symlinks hebben).
We hebben
HEAD
→refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
Als HEAD onthecht is, wijst het direct naar een commit-in plaats van indirect naar een commit via een branch. Je kunt aan een vrijgemaakte HEAD denken als zijnde op een naamloze branch.
git symbolic-ref HEAD
faalt metfatal: ref HEAD is geen symbolic ref
git rev-parse HEAD
geeft17a02998078923f2d62811326d130de991d1a95a
Omdat het geen symbolische ref is, moet het direct naar de commit zelf wijzen.We hebben
HEAD
→17a02998078923f2d62811326d130de991d1a95a
Het belangrijke ding om te onthouden met een vrijstaande HEAD is dat als de commit waar het naar wijst verder niet gerefereerd wordt (geen andere ref kan het bereiken), dan zal het "bungelen" als je een andere commit checkout. Uiteindelijk zullen zulke bungelende commits door het garbage collectie proces verwijderd worden (standaard worden ze tenminste 2 weken bewaard, en ze kunnen langer bewaard worden doordat ze door HEAD's reflog verwezen worden).
1 Het is prima om "normaal" werk te doen met een losgekoppelde HEAD, je moet alleen bijhouden wat je aan het doen bent om te voorkomen dat je gedropte geschiedenis uit de reflog moet vissen.
De tussenstappen van een interactieve rebase worden gedaan met een vrijstaande HEAD (gedeeltelijk om te voorkomen dat de reflog van de actieve branch vervuild raakt). Als je de volledige rebase operatie afrondt, zal het je originele branch updaten met het cumulatieve resultaat van de rebase operatie en HEAD weer aan de originele branch koppelen. Mijn gok is dat je het rebase proces nooit volledig voltooid hebt; dit zal je achterlaten met een los HEAD die wijst naar de commit die het meest recent door de rebase operatie verwerkt is.
Om van je situatie te herstellen, zou je een branch moeten maken die wijst naar de commit waar je ontkoppelde HEAD momenteel naar wijst:
(deze twee commando's kunnen afgekort worden als
git checkout -b temp
)Dit zal je HEAD opnieuw hechten aan de nieuwe
temp
branch.Vervolgens moet je de huidige commit (en zijn historie) vergelijken met de normale branch waar je verwacht aan te werken:
(Je zult waarschijnlijk willen experimenteren met de log opties: voeg
-p
toe, laat--pretty=...
weg om de hele log boodschap te zien, etc.)Als je nieuwe
temp
branch er goed uitziet, wil je misschien (b.v.)master
updaten om er naar te verwijzen:(deze twee commando's kunnen worden afgekort als
git checkout -B master temp
)Je kunt dan de tijdelijke branch verwijderen:
Tenslotte zul je waarschijnlijk de herstelde historie willen pushen:
Je moet misschien
--force
aan het eind van dit commando toevoegen om te pushen als de remote branch niet "fast-forwarded" kan worden naar de nieuwe commit (i.e. je hebt een bestaande commit laten vallen, of herschreven, of op een andere manier een stukje geschiedenis herschreven).Als je in het midden van een rebase operatie zat, dan moet je het waarschijnlijk opruimen. Je kunt controleren of een rebase bezig was door naar de map
.git/rebase-merge/
te zoeken. Je kunt de lopende rebase handmatig opruimen door gewoon die directory te verwijderen (bijvoorbeeld als je het doel en de context van de actieve rebase operatie niet meer weet). Normaal gesproken zou jegit rebase --abort
gebruiken, maar dat doet wat extra resetten dat je waarschijnlijk wilt vermijden (het verplaatst HEAD terug naar de originele branch en zet het terug naar de originele commit, wat wat van het werk dat we hierboven gedaan hebben ongedaan zal maken).Kijk hier voor basisuitleg van los hoofd:
http://git-scm.com/docs/git-checkout
Opdrachtregel om het te visualiseren:
of
dan krijg je output zoals hieronder:
De
* (no branch)
laat zien dat je in vrijstaand hoofd bent.Je had in deze toestand kunnen komen door een
git checkout somecommit
etc. te doen en het zou je gewaarschuwd hebben met het volgende:Nu, om ze op master te krijgen:
Doe een
git reflog
of zelfs gewoongit log
en noteer je commits. Nugit checkout master
engit merge
de commits.Bewerken:
Om toe te voegen, gebruik
git rebase -i
niet alleen voor het verwijderen / doden van commits die je niet'nodig hebt, maar ook voor het bewerken ervan. Vermeld gewoon "edit" in de commit lijst en je zult in staat zijn om je commit te wijzigen en dan eengit rebase --continue
uit te geven om door te gaan. Dit zou ervoor gezorgd hebben dat je nooit in een losgekoppelde HEAD terecht zou zijn gekomen.Zet je losgemaakte commit op zijn eigen branch
Voer eenvoudig
git checkout -b mynewbranch
uit.Voer dan
git log
uit, en je zult zien dat de commit nuHEAD
is op deze nieuwe branch.