Git en GitHub – terughalen vorige versie (2)
- Inleiding
- Private situatie
- git reset -hard / git push -force
- Git-locaties
- git restore
- git reset
- Git Commando’s
- Slot
Inleiding
In de Git en GitHub – terughalen vorige versie-post maakten we een onderscheid tussen “public” en “private” aanpassingen waarbij we voor de public aanpassingen gebruik hebben gemaakt van de git revert.
In deze post zullen we nader ingaan op de “private” aanpassingen die betrekking hebben op situaties waarin je remote repository private is en een afspiegeling van je lokale PC mag wezen,
of situaties waarbij je nog niks hebt gepushed naar de remote repository en alles nog lokaal staat en je dingen wil corrigeren alvorens je de daadwerkelijke push doet naar de remote repository.
Voor de “private” situaties kan gebruik gemaakt worden van de git restore en de git reset welke we in deze post nader zullen bekijken.
Private situatie
Een scenario voor een “private” aanpassing is dat je remote repository een afspiegeling mag zijn van je lokale PC. Daarbij wordt geen rekening gehouden met situaties dat andere mensen de private repository hebben gedownload en afhankelijk zijn van de versies uit die remote repository.
GitHub heeft de optie om repositories private te maken (de optie is sinds 2020 gratis) waarbij je de leesrechten kunt beperken tot alleen jouw persoon en een select groepje (indien gewenst). Het doorvoeren van een beperking in de repository dat niet iedereen een bijdrage kan leveren aan de GitHub repository is sowieso mogelijk (ongeacht of het een public of private repository is). Zie verder de documentatie van GitHub.
Je kunt een harde git reset met een geforceerde push doen als de laatste commits in je private remote repository alleen maar vervuilend zijn voor je repository. Je remote GitHub commit history zal dan overschreven worden met de commit history van je lokale PC (git) met als resultaat dat een deel van de commit history met alle bijbehorende versies uit de remote GitHub repository zal verdwijnen.
Misschien niet helemaal de bedoeling, maar een harde git reset met een geforceerde push kan ook voor een public remote repository gedaan worden. Bijvoorbeeld als een bepaalde gebruiker een laatste bijdrage heeft gedaan en we diens laatste bijdrage gewist willen hebben uit de public repository. We zullen in deze post ingaan op zulks scenario.
Een ander scenario voor een “private” aanpassing is dat alle aanpassingen nog niet zijn gepushed naar de remote GitHub repository. In zulke situaties kun je voor het doen terughalen van een vorige versie gebruik maken van de git restore en de verschillende git reset-varianten.
git reset –hard / git push –force
We lichten het één en ander toe aan de hand van een gebruiker die een ongewenste bijdrage doet aan de GitHub remote repository.
De gebruiker (laten we die wappie noemen) trekt met een pull opdracht de gegevens uit de GitHub remote repository naar zijn/haar lokale Git repository. Gebruiker wappie voegt aan index.html deze tekst toe:
De maanlanding heeft nooit plaatsgevonden, het is in scene gezet door de NASA en de regering!
Gebruiker wappie bekijkt nog even de verkondigde opvatting met:
git diff
en brengt alle wijzigingen aan in de master branche. Wappie pusht alles meteen naar de GitHub remote repository zonder branches en pull requests (en dat is in dit fictieve scenario een oorzaak van het “probleem” dat een wappie dingen direct mag toevoegen aan een master hoofdbranche in de remote repository zonder pull request):
git commit -a -m "Alles is een complot ..."
git push origin master
En de remote Github repository is uitgebreid met de commit van gebruiker wappie:
De laatste “bijdrage” van wappie aan de GitHub remote repository beschouwen we als “vervuiling” zonder enig toegevoegde waarde. We willen weer terug naar een schone uitgangssituatie waarin de laatste bijdrage van wappie is verwijderd.
De desbetreffende bijdrage hoeft verder ook niet aanwezig te zijn in de historie van gedane commits.
We trekken met een pull de “vervuilde” remote repository naar de lokale PC om van daaruit de herstelactie uit te voeren. We zien dat de situatie voordat wappie bezig is geweest tot en met commit ID bb66b08 gaat. Alles voorbij dat punt mag weg:
git log --oneline
We doen een “harde” git reset met:
git reset --hard bb66b08
waarmee de desbetreffende commit “hard” uit lokale Git repository wordt verwijderd. We krijgen echter een afwijzing zodra we de remote GitHub repository met een push willen üpdaten:
git push origin master
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/mrasoftGithub/GitMRA.git'
De afwijzing wordt veroorzaakt doordat Git en GitHub hun commit historie hebben. Beide partijen verlangen van elkaars commit historie dat die synchroon met elkaar loopt.
GitHub constateert nu dat met de push een commit aanwezig is die Git niet meer heeft omdat we die hebben verwijderd met een “harde” git reset. De commit komt echter nog wel voor in de historie van GitHub en GitHub verwerpt daardoor de push-actie omdat alles niet meer synchroon loopt als de push-actie wordt uitgevoerd.
Met een “geforceerde” push staan we toe dat alles in GitHub overschreven mag worden met datgene uit Git. Ook als dingen verloren mochten gaan in GitHub. In dit voorbeeld komt het erop neer dat we de commit van gebruiker wappie in de remote GitHub repository gaan verliezen. Dat is in dit geval geen probleem. Het is precies datgene wat we willen:
git push origin master --force
En we hebben weer de ” schone” situatie tot en met commit ID bb66b08 zonder de perikelen van gebruiker wappie:
Of je je eigen commits of die van een ander vanaf een bepaald punt in de vergetelheid wil laten verdwijnen, hou goed in gedachten dat met een harde git reset en een geforceerde push alles nadien ook echt weg is! Doublecheck dus de Commit ID vanaf welke je alles geschoond wilt hebben.
Git-locaties
De Git software versiebeheersysteem kent vier locaties en het is de bedoeling dat alle relevante bestanden alle locaties doorlopen en eindigen in een remote repository in de cloud of weer terugkomen in de lokale folder op je PC:
- Allereerst is er een folder lokaal op de PC met daarin je broncode
(de desbetreffende folder is je working directory1) - Vervolgens hebben we een staging area2
(Git hanteerde voorheen de term ‘index’) - Daarna volgt de local Git repository3
(die zich nog steeds lokaal op de PC bevindt) en - Ten slotte hebben we de remote (GitHub) repository4 in de cloud
(we gebruiken in dit voorbeeld de hosting service van GitHub)
Met deze locaties in gedachten demonstreren we het gebruik van de git restore en de git reset. We gaan uit van het voorbeeld eerder in deze post waarin we weer een uitgangssituatie hebben met commit bb66b08 (en de bijdrage van gebruiker wappie inmiddels is verwijderd):
git restore
De git restore kun je toepassen in situaties waarin je dingen al hebt doorgezet tot en met de staging area2, maar nog niet hebt doorgevoerd naar de local Git repository3.
Git gaat bestanden “volgen” zodra deze zijn opgenomen in de staging area2. De bestanden zijn dan “tracked” en Git houdt in de gaten of het desbetreffende bestand is gemuteerd of niet. Een bestand zet je als volgt in de staging area2:
git add index.html
Als vervolgens iets is aangepast aan het bestand in de working directory1 dan heeft Git door dat het bestand is gewijzigd t.o.v. de versie die in de staging area2 staat. In dit voorbeeld hebben we de tekst “Mercurius is de eerste planeet vanaf de zon.” toegevoegd aan index.html:
wat tot uiting komt in de:
git status
waarbij je de verschillen kunt zien met de:
git diff
Er zullen, als het gewijzigde bestand met git add weer is toegevoegd aan de staging area2 geen verschillen meer zijn t.o.v. de versie in de working directory1 en een git diff zal geen verschillen tonen en met een opmerking komen dat het geheel gecommit mag worden naar de local Git repository3. Git merkt dan ook op dat je eventueel een:
git restore --staged index.html
kunt doen en het gevolg is dat het bestand weer wordt teruggehaald uit de staging area2 en in de working directory1 wordt gezet. In deze post gaven we het voorbeeld dat we een bestand hard verwijderden uit de working directory1 maar dat we het bestand met een git restore weer kunnen terughalen uit de staging area2:
Het bestand is na een git restore weer “untracked” en je moet voor het bestand weer een git add doen om het bestand in de Git-“carrousel” te laten doen terugkeren.
git reset
Er zijn situaties denkbaar dat alles inmiddels al is doorgezet naar de local Git repository3, maar nog niet naar de remote GitHub repository4. Je hebt dan voor het doen “terugdraaien” van dingen de mogelijkheid tot een git reset waarbij een aantal varianten mogelijk zijn. We zullen deze varianten nader bekijken.
> mixed
Git reset kent de –mixed-optie dat tevens de default is als je niks opgeeft. De mixed-optie zorgt ervoor dat een commit uit de local Git repository3 en de staging area2 worden gehaald en..
git reset --mixed bb66b08
de desbetreffende bestanden weer als “unstaged” terugkeren in de working directory1.
> soft
De –soft-optie verwijdert een commit uit de local Git repository3, maar het laat alle, bij die commit horende bestanden staan in de staging area2:
git reset --soft bb66b08
Deze situatie is van toepassing indien je meerdere bestanden wilt opnemen in de local Git repository3, maar je een bestand uit de working directory1 bent vergeten.
Je kunt dan de commit met de soft-optie verwijderen waarna je het bestand dat je bent vergeten alsnog toevoegt aan de staging area2 (waar de andere bestanden nog staan).
Je doet daarna een nieuwe commit zodat de commit voor de Git repository3 uit de gewenste bestanden zal bestaan (inclusief dat ene bestand dat je aanvankelijk vergeten bent).
> hard
De –hard-optie verwijdert bestanden uit de local Git repository3 en de staging area2, maar het werkt ook met terugwerkende kracht de bestanden bij in de working directory1. Zo hadden we in het voorbeeld eerder in deze post deze harde reset gedaan:
git reset --hard bb66b08
wat tot gevolg heeft dat in de working directory1 een index.html wordt neergezet dat expliciet betrekking heeft op commit bb66b08. De tekst “Mercurius is de eerste planeet vanaf de zon.” in index.html zullen we daardoor nergens meer terugvinden omdat die tekst nog niet aanwezig was in het bestand dat hoort bij commit bb66b08.
Git commando’s
Samenvattend de Git commando’s die we in deze post gebruikt hebben:
# toont de status van de
# bestanden in de working dir
# zijn ze gewijzigd?
# moeten ze 'gecommit' worden?
git status
# toon het verschil tussen
# de working directory en
# de staging area
git diff
# hard reset
# om een commit te herstellen tot en met
# een bepaalde Commit ID
# de bestanden in de working directory en
# de staging area horen expliciet
# bij die commit, alle wijzigingen
# nadien zijn weg!
git reset --hard <Commit ID>
# een geforceerde push om
# de remote repository bij te werken
# commit history en
# versies in de remote repository
# na de eerder opgegeven Commit ID
# gaan verloren!
git push origin <hoofdbranche> --force
# mixed reset (default)
# om een commit te herstellen tot en met
# een bepaalde Commit ID
# het gewijzigde bestand is
# nadien "untracked"
# en alleen nog maar aanwezig in
# de working directory
git reset --mixed <Commit ID>
# soft reset
# om een commit te herstellen tot en met
# een bepaalde Commit ID
# nadien zijn de gewijzigde bestanden
# nog aanwezig in de
# staging area en de working directory
git reset --soft <Commit ID>
Slot
In deze post zijn we ingegaan op wat we de “private” aanpassingen in “private” situaties hebben genoemd.
We begonnen de post met een scenario waarin van een remote (GitHub) repository4 commit history en versies verloren mogen gaan bij een update (push) vanuit de local Git repository3. We gebruikten daarvoor de git reset –hard en de git push origin –force. Hou goed in gedachten dat met een harde git reset en een geforceerde push alles nadien ook echt weg is! Doublecheck dus de Commit ID vanaf welke je alles geschoond wilt hebben.
Daarna hebben we situaties de revue laten doen passeren waarin nog niks is gepushed naar de remote (GitHub) repository4. In zo’n situatie staat alles nog lokaal en dingen kunnen nog gecorrigeerd worden alvorens de daadwerkelijke push naar de remote (GitHub) repository4 plaatsvindt. We hebben daarvoor de git restore en de git reset gebruikt waarbij de git reset een aantal varianten kent (mixed, soft en hard).
Een versiebeheersysteem verliest snel aan bestaansrecht als de versiebeheersysteem niet gebruikt kan worden voor het doen terughalen van een vorige versie en we hebben laten zien dat het terughalen van een vorige versie ook mogelijk is in “private” situaties.
Hopelijk ben je met deze posting weer wat wijzer geworden en ik hoop je weer terug te zien in één van mijn volgende blog posts. Klik op de Home-button om te zien wat ik nog meer geschreven heb…