GIT: versiebeheer van jouw code

Ingediend door Dirk Hornstra op 02-feb-2022 20:24

Toen ik in 2001 afstudeerde aan de opleiding Hogere Informatica hadden we het niet over "versiebeheer" gehad. Als je een website, een applicatie of andere software oplevert, dan wil je de code "ergens" opslaan. En als je dan fouten moet oplossen of nieuwe zaken moet toevoegen, dan wil je met de juiste code werken en ook zorgen dat jouw code de nieuwe "huidige versie" wordt. Een map versie 1.2, een map versie 1.2-mee-bezig, dat zijn zaken waar je niet blij van wordt. Maar goed, dat waren zaken die waarschijnlijk toen nog niet echt speelden. SQL-injection, daar had ik toen ook nog niets over gehoord, we weten dat dit nog steeds actueel is, het staat nog steeds in de OWASP top 10: link.

Je hebt CVS, daar heb ik niet mee gewerkt. Bij HSCG werkten we met SVN. En dat werkte best wel goed. Het goede daaraan vond ik dat je een bepaalde versie kon uitchecken. Dus de versie die in maart nog goed was, vervolgens in een andere map de huidige versie en dan de boel gaan vergelijken: wat is er veranderd en wat zorgt er nu voor dat er  fouten naar voren komen die we vroeger niet hadden?

Later heb ik daar nog wel mee gewerkt en vervolgens zijn we (zoals zo'n beetje de rest van de wereld?) overgegaan naar GIT. De grote partijen die je daarbij hebt zijn GitLab en GitHub. GitHub was een los bedrijf, maar is later door Microsoft overgenomen.

Mijn ervaring met GIT is praktijk-ervaring. Dus een repo aanmaken, uitchecken (doe je via clone), je wijzigingen doen, commit, push. En dan vervolgens de master-branche mergen naar de production-branche, zodat die live gezet kan worden. Vervolgens gingen we met meerdere branches werken. Geen ervaring mee, dus in eerste instantie wat koud-water-vrees: doe ik dit wel goed, maak ik geen dingen kapot. Maar dat gaat goed. Vervolgens zijn er zaken in branches die meegenomen moeten worden, maar bepaalde zaken ook weer niet. En die ga je dan met cherry-picking eruit halen, zodat je ze mee kan nemen. Ook daar een beetje huiverig voor, maar het werkt, dus "no sweat".

Nou ja, eigenlijk wil je dat niet. Want soms heb je van die mooie visuele weergaves en als branches naast elkaar lopen, in de één wordt wat aangepast, er wordt wat meegenomen naar een andere branche, daar kun je een flinke wirwar van overlappingen krijgen. En als developers houden we van het KISS principe: Keep it simple, stupid!

Dus eigenlijk vind ik het wel tijd voor het credo wat ik op mijn persoonlijke site heb staan: "earste leare, dan kinne, dan begjinne", oftewel, eerst leren, dan kunnen en dan beginnen. Laat ik eerst eens wat documentatie, uitleg en voorbeelden van GIT erbij zoeken. Misschien maakt dit dingen helder, geeft het andere inzichten en bovenal: word ik straks een GIT-goeroe en kan ik mijn collega's helpen. Want wie heeft het niet gehad: je hebt een repo, maar je hebt allemaal conflicten en andere meldingen, dus het enige wat je nu nog kunt doen is je lokale map weggooien en zelf stuk voor stuk dingen herstellen. Horror!

Gelukkig hebben we de podcasts van Scott Hanselman, in podcast 350 en podcast 359 zijn namelijk een aantal handige linkjes benoemd:

  • Startpunt, de officiële GIT-pagina: link.
  • Het online GIT-boek op deze site: link.
  • De documentatie die Github aanbiedt: link.
  • Git in Powershell, iets wat Scott Hanselman zelf ook gebruikt: link.
  • En ook wordt nog LibGit genoemd, een code-bibliotheek voor GIT: link, en de LibGit2Sharp voor C#: link.


Het laatste onderdeel sla ik even over (want ik ga geen GIT-calls vanuit code opstarten), het gaat nu om de theorie, hoe het "hoort". Dus eerst het online GIT-boek en dat is een goede keuze. Een beschrijving van de historie, hoe Torvald Linus (maker Linux) met GIT gestart is met een aantal uitgangspunten:

  • Snel
  • Simpel ontwerp
  • Sterke ondersteuning van niet-lineaire ontwikkeling van code (duizenden parallele branches)
  • Volledig gedistribueerd
  • In staat om hele grote projecten efficiënt te kunnen beheren (snelheid en hoeveelheid data)
     

GIT maakt snapshots van je bestanden, als er niets verandert wordt er een verwijzing opgeslagen. Alles staat lokaal, dus je kunt offline werken. Integriteit wordt afgedwongen met checksums, SHA-1-hashes, een string van 40 tekens. De broncode van GIT is te vinden op Github: link. En nog even een snelle link naar de pagina waarop staat hoe je met verschillende systemen (Linux, Mac, Windows) GIT kunt installeren: link. Het online boek gaat vervolgens de boel vanuit de command-prompt uitvoeren. Laat je hier niet door afschrikken, het boek geeft aan dat een programma met een GUI meestal slechts een deel van de functionaliteit geïmplementeerd heeft, die kun je straks wel gebruiken, maar dan weet je in ieder geval hoe je bij problemen zelf via de command-prompt zaken kunt fixen.

Basics zijn git clone REPO LOCATIE, dat het geen "check-out" is, omdat je een volledige GIT-repo binnen haalt. Met git commit -m "commit toelichting" zet je jouw wijzigingen in een nieuwe snapshot.

Als je bestanden wil stagen (toevoegen en wijzigingen doorvoeren) doe je dat met git add. Wil je de status zien dan gebruik je git status. Met git status -s krijg je een kort overzicht te zien. Wil je zien wat er in staging staat en hoe jouw bestanden daarvan afwijken, dat kun je met git diff zien. Als je al zaken in stage gezet hebt en wilt zien wat er gewijzigd is, dat kun je met git diff --staged zien.

Met git rm BESTAND kun je een bestand verwijderen uit je repo. Als je een bestand per ongeluk ingecheckt hebt (vergeten in de .gitignore te zetten), maar je wilt m wel op je eigen schijf houden, dan doe je een git rm --cached BESTAND

Als een bestand een foutieve naam heeft, dan kun je het bestand hernoemen, dan een git rm BESTAND-OUDENAAM git add BESTAND-NIEUWENAAM, maar met een git mv BESTAND-OUDENAAM BESTAND-NIEUWENAAM heb je een "nette" hernoem-actie.

We komen bij de volgende commando's, zoals git log waarmee je de commit-acties kunt zien. Cool! Met git log -p krijg je de diff-wijzigingen te zien en als je bijvoorbeeld -2 als parameter mee geeft, dan krijg je 2 commits te zien. Met git log --pretty=oneline krijg je alle losse commits in 1 regel, zoals je het meestal ook in een GUI ziet. Met de waarde format kun je zorgen voor een vast formaat, handig als je de uitput zelf geautomatiseerd gebruikt en deze altijd gelijk moet blijven: git log --pretty=format:"%h - %an, %ar : %s".

Token Omschrijving
%H Commit hash
%h Abbreviated commit hash
%T Tree hash
%t Abbreviated tree hash
%P Parent hashes
%p Abbreviated parent hashes
%an Author name
%ae Author email
%ad Author date (format respects the --date=option)
%ar Author date, relative
%cn Committer name
%ce Committer email
%cd Committer date
%cr Committer date, relative
%s Subject


Met --grep kun je zoeken op woorden in de commit-omschrijvingen. Ook kun je zoeken op --author (naam auteur) en met -S kun je zoeken op expliciete wijzigingen op een bepaald item in je code. Een handige optie hierbij is nog de --no-merges zodat je niet de merge-acties ziet, die interesseren je 9 van de 10x niet.

Stel, je hebt wijzigingen gecommit en denk "shit, deze 2 bestanden moeten er ook nog bij!". Dan kun je die met een git add BESTANDEN toevoegen. Door vervolgens een git commit --amend uit te voeren maak je een nieuwe commit aan die de vorige overschrijft, zodat je 1 nette commit over houdt!

Als je een aantal bestanden gewijzigd hebt en in staging hebt staan, maar denkt "die ene moet nog niet mee!", dan kun je met git reset HEAD BESTAND die weer uit je staging halen. Vanaf git 2.23.0 is dit trouwens git restore --staged BESTAND geworden.

En stel dat je een bestand hebt dat je aangepast hebt (even wat testen), maar die wil je terug zetten naar hoe die was, want er hoeft niets in aangepast te worden, dan doe je een git checkout -- BESTAND en je hebt weer de originele versie. En ook hier geldt dat dit nu git restore BESTAND geworden is.

 

Tot nu toe zijn veel lokale GIT-repo's voorbij gekomen, maar je werkt waarschijnlijk ook met Gitlab en/of Github. Als je daar een clone-actie op gedaan hebt, kun je in die map op jouw pc met een git remote en en git remote -v zien waar dit aan gekoppeld zit.

Je kunt een externe locatie toevoegen met een git remote add KORTENAAM URL. Vervolgens kun je met git fetch KORTENAAM de data binnen krijgen.

En hier komt dus het "pushen" om de hoek kijken, je wilt jouw code op de online locatie krijgen. Je doet dan een git push KORTENAAM BRANCHENAAM wat vaak een git push origin master zal zijn.

Met git remote show KORTENAAM (dus bijvoorbeeld git remote show origin) geeft je terug in welke branche je zit. En je kunt ook acties op de remote uitvoeren, met een git remote remove KORTENAAM en git remote rename OUDEKORTENAAM NIEUWEKORTENAAM

 

Door "tags" te geven kun je bepaalde belangrijke releases zichtbaar maken. Met een git tag kun je zien welke tags er zijn. En met git tag -l "OMSCHRIJVING" kun je zoeken. Door git tag -a TAGNAAM -m "omschrijving" maak je een annotated tag aan. Dit type tag is "heavy", checksum erop, koppeling commit-user, je kunt deze signen met GPG, GNU Privacy Guard, iets voor een echte release dus. Door een korte git tag -a TAGNAAM uit te voeren, maak je een "lichtgewicht" tag aan. En als je vergeten bent een tag toe te voegen, dan kun je dat later nog doen. Zelfde commando, maar na een spatie voeg je een deel van de hash van de commit toe. Met git tag -d TAGNAAM kun je een tag weer verwijderen.

Standaard blijven tags op je eigen pc staan. Wil je dat die ook naar de remote gaan, dan doe je dat met git push origin TAGNAME of als dat veel werk is een git push origin --tags waardoor alle missende tags ook naar de server gaan. En met git push origin --delete TAGNAME verwijder je een tag van de server.

En kijk, hier zie ik een soort manier om ergens "even" een check-out te maken van een oude versie. Stel je hebt een tag op een punt en je wilt zien hoe de bestanden er toen uit zagen. Dat kun je doen met een git checkout TAGNAME maar let wel op, de boel is "detached", dus wijzigingen die je doet komen "nergens" te staan. Het advies is dus ook om dit dan in een eigen branche te zetten: git checkout -b NIEUWEBRANCHENAME TAGNAME zodat je daar zaken kunt testen en aanpassen en later weer zaken kunt mergen, mocht je dat willen.

Sommige teksten zijn nogal lang, met aliassen kun je een eigen afkorting aan zulke zaken koppelen. Bijvoorbeeld git config alias.co checkout zodat je alleen maar git co voor een checkout hoeft in te typen, meer voorbeelden hier: link.

 

We gaan door met branches. Met git branch NAAM maak je een nieuwe branche aan van de huidige versie. Met git log --oneline --decorate kun je de verschillende branches zien. Door git checkout BRANCHNAAM in te voeren kun je switchen tussen de verschillende branches.

Met git branch kun je zien welke branches je hebt (en de actieve bevat een * aan het begin). Als je jouw branche wilt hernoemen dan doe je een git branch --move OUDENAAM NIEUWENAAM vervolgens een git push --set-upstream origin NIEUWENAAM en als alles klopt dan doe je een git push origin --delete OUDENAAM

We krijgen nog het advies om zoveel mogelijk git fetch en git merge te gebruiken en niet git pull wie deze acties in één keer uitvoert en soms verwarrend kan werken.

 

Er volgt uitleg over "rebase". Je kunt een andere branch maken, hier je wijzigingen in doen en dat mergen naar je master. Gebeurt dat vaak dan krijg je allemaal merge-commits, dat kan er "lelijk" uit zien. Als alternatief kun je een rebase doen. Hierdoor lijkt het alsof al die aanpassingen in de normale branch gedaan zijn, al die wijzigingen uit die andere branch komen als sequentiële acties terug. Een waarschuwing is hier op zijn plaats, baseren anderen hun werk hier op, dan kun je anderen flink in de problemen brengen. Dus als je een solo-projectje hebt, mogelijk een goed idee: link met uitleg.


Een overzicht van de verschillende workflows: link. Op deze pagina een uitgebreide uitleg over hoe je zorgt voor "goede commits" en hoe je met een team aan een project kunt werken: link. Ook hier bij het onderhouden van een project: link.

De overige hoofdstukken gaan dieper de materie in, bijvoorbeeld over het GIT-protocol en hoe je zaken zelf op een server kunt installeren, dat ga ik niet behandelen. Mocht je er interesse in hebben, met bovenstaande links kun je jezelf daarin verdiepen. Nog wel even een link naar de broncode van Gitlab: link, die je op een eigen server kunt draaien en een link naar de verschillende installatiepakketten om dat te doen: link.

En ik heb GitPosh geïnstalleerd. Ik ben bezig een laptop voor mezelf in te richten, daar stond nog geen GIT op, geïnstalleerd via de site (link), vervolgens de stappen gevolgd van Keith Dahlby: link. Er stond 1 project van mezelf op die laptop, als ik naar de map ga zie ik [master] in mijn command-prompt erbij komen en de tekst rood en een pijltje naar beneden, ik loop dus achter. Nadat ik een git pull uitvoer is de boel weer in sync.