- Git
Git is een versiebeheersysteem. Het laat je onder andere toe om met meerdere mensen samen te werken aan projecten en een historiek van het project bij te houden.
Het werd gelanceerd in april 2005 door Linus Torvalds - de vader van Linux. Codebeheersystemen waren niets nieuws, enkele belangrijke systemen in het verleden waren:
- SCSS begin de jaren 70
- RCS begin de jaren 80
- CVS werd gelanceerd in 1986
- SVN in 2001
De Linux ontwikkeling gebeurde in de beginjaren met Bitkeeper VCS. In 2005 legde Bitkeeper echter enkele extra restricties op aan de gratis open source versie, waardoor Linux niet langer via dit systeem ontwikkeld kon worden. Linus zocht naar alternatieven, maar vond er geen die voldeden aan z'n eisen:
- Eenvoudig aan distributed development doen
- Scalable zijn: met meer dan 1000 ontwikkelaars samen kunnen werken
- Snel & Efficient en betrouwbaar zijn
- Weten wie wat gedaan heeft
- Ondersteuning voor transacties: meerdere acties bundelen
- Branching ondersteuning: afsplitsingen van het project die terug samengevoegd kunnen worden met het hoofdproject.
- Distributed repositories: project met historiek wordt niet centraal bijgehouden.
- Gratis
Toen hij geen alternatief vond die aan deze requirements voldeed besloot hij z'n eigen systeem te ontwikkelen: GIT was geboren.
De naam GIT is geen afkorting, maar een verwijzing naar Linus zelf: het is slang voor "een onaangenaam persoon". Net zoals Linux, wou hij een naam die naar zichzelf verwijst. Nadien is er door de community de afkorting "Global Information Tracker" op geplakt.
Je bestanden worden opgeslaan in een repository. Deze repository bevat informatie over de inhoud van de bestanden, hun naam, locatie en de historiek.
- de inhoud van bestanden wordt opgeslaan in blob objecten
- de historiek wordt bijgehouden in commit objecten
- de structuur wordt bijgehouden in tree objecten
Wanneer je meerdere files hebt met dezelfde inhoud, dan zal in de tree meerdere keren verwezen worden naar hetzelfde blob object, waardoor er plaats bespaard wordt.
We zullen git gebruiken vanaf de command line. Er zijn wel enkele applicaties die toelaten om via een GUI git commando's uit te voeren, maar wanneer je meer geavanceerde acties wil uitvoeren zul je sowieso terug moeten grijpen naar de command line.
Git wordt mee geïntalleerd wanneer je de xcode command line tools installeert. Indien je dit dus reeds gedaan hebt (bijvoorbeeld om Node.js te laten werken), dan kun je meteen aan de slag. Een andere optie (zonder de command line tools) is git downloaden en installeren van op http://git-scm.com/downloads.
Vooraleer we git specifieke commandos gaan gebruiken, zullen we even een korte opfrissing doen van enkele belangrijke Terminal commando's. Volgende commando's zou elke developer vlot moeten kunnen gebruiken:
ls
: Toon een lijst van bestanden en mappen in de actieve map. Indien je ook de verborgen bestanden & de bestandsattributen wil zien, kun jels -al
uitvoeren.cd naamvanmap
: Hiermee navigeer je naar de map met de naam die na het commando staat. Deze map wordt dan de actieve map in je Terminal. In dit voorbeeld navigeer je naar de map met de naam "naamvanmap". In plaats van een relatieve naam (dus een naam van een map die in de huidige actieve map staat), kun je ook een volledig pad invullen (beginnen met een forward slash). Tip: je kan vanuit je finder een map of bestand slepen naar het terminal venster om het volledige pad naar die map of bestand te plaatsen in het venster.cd ..
: Navigeer naar de bovenliggende map, zodat deze de actieve map wordt.mkdir naamvanmap
: maak een map aan met de naam gespecifieerd na het commando. In dit geval maak je een map aan met de naam "naamvanmap" in de actieve map.rm -d naamvanmap
: wis een bepaalde map. Dit lukt enkel als de map leeg is. Wanneer je een niet-lege map wil wissen, dan kun je extra opties gebruiken:rm -rdf naamvanmap
zal de map inclusief submappen en files wissen.rm naamvanbestand
: wis een bepaald bestand.mv origineleneem nieuwenaam
: verplaats of hernoem een bestand. Je kan ook absolute / relatieve paden gebruiken om bestanden te verplaatsen naar een andere map.cp originelenaam nieuwenaam
: kopieer een bestand naar een nieuwe locatie.cat naamvanbestand
: toon de inhoud van een bepaald bestand in je terminal venster.echo "hello world"
: toon de tekst tussen de quotes in het terminal venster.
Door gebruik te maken van de tab
-toets, kun je autocomplete triggeren op commando's of bestands en mapnamen. Typ een deel van het commando of een deel van het pad en druk op tab om automatisch aan te vullen. Zo kun je heel wat tijd besparen, en vermijd je bovendien typfouten.
Je kan ook output van een commando wegschrijven naar een bestand, door gebruik te maken van het >
karakter:
ls > test.txt
zal de output van het ls commando wegschrijven in een bestand met de naam test.txt.echo "hello world" > hello.txt
zal de output van het echo commando (de tekst "hello world") wegschrijven in een bestand met de naam hello.txt.
Dit zijn enkele basiscommando's die je als je broekzak moet kennen. Dit is niets git-specifiek! Je zal deze commando's ook gebruiken wanneer je aan de slag gaat met Node.js en module bundlers zoals Gulp, Webpack of ViteJS.
Je kan van elke bestaande map een git repository maken met het git init
commando. Het maakt niet uit of hier al bestanden of submappen in aanwezig zijn. Wij zullen starten met een nieuwe lege map, maar weet dat dit niet nodig is om een project via git te beheren.
Maak een nieuwe map aan en maak hiervan een git repository (opmerking: de dollartekens duiden erop dat dit over een command line input gaat door de gebruiker, en typ je niet):
$ mkdir project
$ cd project
$ git init
Initialized empty Git repository in /Users/frederikduchi/Documents/development3/project/.git/
Het git init
commando zal een verborgen map met de naam .git aanmaken. Hierin zit alle informatie over de repository & z'n historiek.
Via het git status
commando kun je wat meer informatie opvragen over de huidige status van de repository. Dit gebruik je vaak om te zien welke bestanden er gewijzigd zijn, of er nieuwe bestanden zijn, of er bestanden gewist zijn.
$ git status
# On branch master
#
# No commits yet
#
nothing to commit (create/copy files and use "git add" to track)
Maak een nieuw bestand hello.txt aan met de tekst "hello world":
$ echo "hello world" > hello.txt
Voer opnieuw het git status
commando uit:
$ git status
# On branch master
#
# No commits yet
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# hello.txt
nothing added to commit but untracked files present (use "git add" to track)
Git laat ons weten dat er bestanden aanwezig zijn in je directory die nog niet bijgehouden worden door Git (untracked files present). Wanneer je de files wil bijhouden (tracken), dan moet je ze nog adden met het git add
commando:
$ git add hello.txt
$ git status
# On branch master
#
# No commits yet
#
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
#
# new file: hello.txt
#
Nu is git op de hoogte van die file. Dit wil nog niet zeggen dat git automatisch van elke wijziging aan die file een snapshot zal nemen. Je moet zelf beslissen wanneer je een snapshot zal nemen van de wijzigingen in je bestand(en) en deze wil toevoegen aan je git historiek. Dit doe je door een commit uit te voeren:
$ git commit -m "eerste commit"
[master (root-commit) 5588c39] eerste commit
1 file changed, 1 insertion(+)
create mode 100644 hello.txt
git add
en git commit
zijn de twee commando's die je heel vaak zal gebruiken tijdens het werken met git.
Pas de inhoud van het bestand aan naar "hello devine", en voer het git status commando uit:
$ echo "hello devine" > hello.txt
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git restore <file>..." to discard changes in working directory)
#
# modified: hello.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
Git heeft gedetecteerd dat er wijzigingen zijn gebeurd aan een reeds getracked bestand. Je zal deze opnieuw moeten "adden", om deze wijzigingen in de wachtrij te zetten voor een commit (wordt stage for commit genoemd):
$ git add hello.txt
Commit vervolgens deze wijzigingen:
$ git commit -m "world gewijzigd naar devine"
[master 7f65053] world gewijzigd naar devine
1 file changed, 1 insertion(+), 1 deletion(-)
Je kan natuurlijk ook meerdere wijzigingen in 1x adden en committen. Pas de tekst aan, en maak een tweede file aan:
$ echo "hello development 3" > hello.txt
$ echo "hello" > world.txt
Git status zal nu 2 zaken rapporteren: het wijzigen van een reeds getrackte file, en het detecteren van een nieuwe file die nog niet getracked wordt:
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git restore <file>..." to discard changes in working directory)
#
# modified: hello.txt
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# world.txt
no changes added to commit (use "git add" and/or "git commit -a")
Je kan nu deze twee zaken afzonderlijk stagen door 2x een git add uit te voeren, maar dit kan ook in 1 keer, door git add .
$ git add .
$ git status
# On branch master
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
#
# modified: hello.txt
# new file: world.txt
#
Nu zijn beide gestaged voor commit, en kan je met 1 commit ze loggen in de git repository:
$ git commit -m "welcome dev3 + new world file"
[master 9720321] welcome dev3 + new world file
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 world.txt
We hebben al eerder gezien dat je met git add .
alle wijzigingen kunt stagen voor commit. Nu is het zo dat deletes (wissen van files) niet mee gestaged worden. Daarvoor moet je nog de flag -A toevoegen aan het commando.
Er zijn een 3-tal scenario's wanneer je misschien wijzigingen wil ongedaan maken:
- Wanneer je wijzigingen hebt aangebracht, maar nog niet ge-add hebt.
- Wanneer wijzigingen ge-add zijn, maar nog niet gecommit
- Wanneer je na een commit wil terugkeren naar een vroegere versie.
In elk van deze scenario's kun je wijzigingen ongedaan maken door de juiste git commando's te gebruiken.
Pas de tekst in hello.txt opnieuw aan naar "hello devine", maar add deze nog niet:
$ echo "hello devine" > hello.txt
Voer een git status uit, git heeft changes gedetecteerd die nog niet staged for commit zijn:
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git restore <file>..." to discard changes in working directory)
#
# modified: hello.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
Je krijgt hier al een hint hoe je de wijzigingen in je working directory kunt ongedaan maken: via het git restore commando. Voer dit commando uit, en controleer de status van de repository:
$ git restore hello.txt
$ git status
# On branch master
nothing to commit (working directory clean)
De wijzigingen aan hello.txt zijn ongedaan gemaakt. Als je de inhoud van de file bekijkt via het cat commando, zul je zien dat deze de inhoud bevat van je laatste commit:
$ cat hello.txt
hello development 3
Pas de tekst opnieuw aan naar "hello devine", stage deze wijzigingen via het add commando, maar commit deze nog niet:
$ echo "hello devine" > hello.txt
$ git add hello.txt
$ git status
# On branch master
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
#
# modified: hello.txt
#
Je krijgt een hint hoe je deze wijzigingen kunt unstagen: via git restore --staged:
$ git restore --staged hello.txt
De file is nog steeds gewijzigd, maar de wijzigingen zijn niet meer gestaged:
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git restore <file>..." to discard changes in working directory)
#
# modified: hello.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
Je kunt deze nu definitief ongedaan maken, door - net zoals in voorgaande topic - git restore te gebruiken:
$ git restore hello.txt
Stel: je hebt bepaalde wijzigingen gecommit, maar om een of andere reden wil je die ongedaan maken.
$ echo "hello devine" > hello.txt
$ git add hello.txt
$ git commit -m "changed, but not sure about it"
Je kan 2 dingen doen: ofwel een nieuwe commit maken die de wijzigingen uit de vorige commit ongedaan maakt (git revert HEAD
), ofwel de commit volledig uit de historiek gaan wissen (git reset --hard referentie-naar-commit
).
$ git revert HEAD
Je terminal zal nu een vim of nano editor tonen, waarin je een commit message kan typen. Pas eventueel de default message aan, en typ in vim :quit
wanneer je klaar bent. Voor nano gebruik je Ctrl + X, gevolgd door Yes.
[master 2bc740f] Revert "changed, but not sure about it"
1 file changed, 1 insertion(+), 1 deletion(-)
De file zal nu de inhoud bevatten van voor de laatste commit.
Je kan ook een andere editor instellen voor de merge message edits. In plaats van vim is nano een gebruiksvriendelijker alternatief:
git config --global core.editor "nano"
Je kan trouwens de commit historiek van een repository bekijken via het git log
commando:
$ git log
commit 2bc740f2af93348267c6b3558e1037c5db540600 (HEAD -> master)
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:57:33 2021 +0200
Revert "changed, but not sure about it"
This reverts commit 8d977cb2d4855a782e261015645e017c1e128000.
commit 8d977cb2d4855a782e261015645e017c1e128000
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:56:55 2021 +0200
changed, but not sure about it
commit 5aa411822d8546ef1cd73addc20a022a74deae91
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:54:26 2021 +0200
changed, but not sure about it
commit 9720321677e0798b540b287398edeaadcb630b17
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:35:21 2021 +0200
welcome dev3 + new world file
commit 7f65053ee86593fb29a3102e03f7ae3f44b112d7
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:32:22 2021 +0200
world gewijzigd naar devine
commit 5588c399cd15bc841c89158d72cc4d3938581196
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:30:22 2021 +0200
eerste commit
Je ziet dat de "foute" commit nog steeds in de git historiek aanwezig is, en dat de wijzigingen via een nieuwe commit ongedaan gemaakt werden. Merk ook op dat elke commit een unieke SHA1-hash id heeft. Deze ids kun je gebruiken om commits te specifiëren bij bepaalde commano's.
Een meer drastische manier om commits ongedaan te maken, is via het git reset
commando. Via git reset ga je je working tree gaan resetten naar een bepaalde commit en alle commits na die commit wissen uit de repository.
We willen terug naar de status gaan van onze commit "welcome dev3 + new world file". Via git log, komen we te weten dat het SHA1 id 9720321677e0798b540b287398edeaadcb630b17
is. Specifieer het SHA1 id van de commit naar waar je wil resetten na het commando. Je hoeft niet het ganse id te specifiëren: de eerste 4 karakters zijn vaak voldoende (ten minste als deze uniek zijn):
$ git reset --hard 9720
HEAD is now at 9720321 welcome dev3 + new world file
Wanneer je nu opnieuw git log uitvoert, zie je dat de commits na 9720 verdwenen zijn:
$ git log
commit 9720321677e0798b540b287398edeaadcb630b17 (HEAD -> master)
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:35:21 2021 +0200
welcome dev3 + new world file
commit 7f65053ee86593fb29a3102e03f7ae3f44b112d7
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:32:22 2021 +0200
world gewijzigd naar devine
commit 5588c399cd15bc841c89158d72cc4d3938581196
Author: Frederik Duchi <[email protected]>
Date: Wed Sep 1 09:30:22 2021 +0200
eerste commit
Zo'n git reset
commando ga je enkel gebruiken om lokale commits ongedaan te maken. Eens je samenwerkt met anderen, en commits reeds verspreid zijn onder andere users, dan gebruik je het git revert
commando om een nieuwe commit te maken die een vorige commit ongedaan maakt.
Het zal niet nodig zijn om alle bestanden in een projectmap bij te houden via Git. Zo is het bijvoorbeeld geen goed idee om een node_modules
map te tracken in je repository. Ook verborgen systeembestanden, zoals .DS_Store
hebben geen enkel nut in een repository.
Initialiseer je map als een node project, en koppel bijvoorbeeld webpack aan dit project:
$ npm init -y
$ npm install webpack --dev
We zullen nu "per ongeluk" node_modules
committen, en kijken om dit nadien recht te zetten.
$ git add .
$ git commit -m "initial project"
[master 0ead6d0] initial project
2435 files changed, 385719 insertions(+)
create mode 120000 node_modules/.bin/acorn
create mode 120000 node_modules/.bin/browserslist
create mode 120000 node_modules/.bin/terser
...
create mode 100644 node_modules/yocto-queue/license
create mode 100644 node_modules/yocto-queue/package.json
create mode 100644 node_modules/yocto-queue/readme.md
create mode 100644 package-lock.json
create mode 100644 package.json
Je hebt nu het volledige project, inclusief de node_modules
toegevoegd aan je git repository.
Dit is overkill, het is niet nodig om de volledige dependency tree mee op te slaan in je git repository. We zullen deze fout nu rechtzetten:
$ git rm -r --cached node_modules/
rm 'node_modules/.bin/acorn'
rm 'node_modules/.bin/browserslist'
rm 'node_modules/.bin/terser'
rm 'node_modules/.bin/webpack'
rm 'node_modules/@types/eslint-scope/LICENSE'
rm 'node_modules/@types/eslint-scope/README.md'
...
De --cached optie zorgt ervoor dat de file gewist word uit de repository index, maar wel blijft staan in jouw filesysteem.
Een git status geeft nu het volgende resultaat:
$ git status
# On branch master
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
#
# deleted: node_modules/.bin/acorn
# deleted: node_modules/.bin/browserslist
# deleted: node_modules/.bin/terser
# deleted: node_modules/.bin/webpack
# deleted: node_modules/@types/eslint-scope/LICENSE
# deleted: node_modules/@types/eslint-scope/README.md
...
Add & commit nu deze deletes, door de flag -A te gebruiken bij je add:
$ git add -A .
$ git commit -m "removed node_modules folder"
On branch master
nothing to commit, working tree clean
We hebben zopas een commit aangemaakt, waarin deze node_modules
opnieuw verwijderd worden. Er is echter nog steeds een commit waarin deze node_modules
wél aanwezig zijn, wat onze repository onnodig groot maakt. Indien je mappen of bestanden wil verwijderen uit de volledige historiek van je project, dan moet je nog een stap verder gaan.
Via het git filter-branch
commando kun je de git historiek aanpassen. Om onze node_modules
map te wissen, doen we het volgende:
$ git filter-branch --tree-filter 'rm -rf node_modules' HEAD
WARNING: git-filter-branch has a glut of gotchas generating mangled history
rewrites. Hit Ctrl-C before proceeding to abort, then use an
alternative filtering tool such as 'git filter-repo'
(https://github.com/newren/git-filter-repo/) instead. See the
filter-branch manual page for more details; to squelch this warning,
set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...
De map wordt gewist op je filesysteem én in de historiek. Let op, wanneer je via npm opnieuw de node_modules
koppelt, kun je ze per ongeluk opnieuw adden aan de historiek, wat niet de bedoeling is. We zullen dit vermijden met behulp van een .gitignore
bestand.
We zullen nu specifieren welke files we in de toekomst niet meer willen tracken. Dit kan mbv een .gitignore
file. Dit is een tekstbestand in je repository dat specifieert welke bestanden en mappen genegeerd mogen worden door git.
Maak een nieuw bestand aan met de naam .gitignore
in de root van je repository. Geef dit de volgende inhoud:
.DS_Store
node_modules/
Dit zorgt ervoor dat het bestand .DS_Store
en de map node_modules
(& alle submappen van node_modules
en submappen met deze naam) genegeerd worden in de toekomst.
Add en commit dit bestand, zodat dit mee opgenomen wordt in je repository.
$ git add -A .
$ git commit -m "added .gitignore"
In de praktijk zal je als één van je eerste bestanden zo'n .gitignore
bestand aanmaken. Zo vermijd je dat je repository "vervuild" wordt met onnodige bestanden, en vermijd je ingrijpende acties zoals het verwijderen van mappen & bestanden uit de historiek.
Voor een lijst van nuttige .gitignore files kan je hier terecht: https://github.com/github/gitignore
Tot nu toe hebben we lokaal gewerkt met git. Je kan ook met meerdere mensen samenwerken aan 1 repository door te werken met een remote server. Iedereen heeft een lokale kopie van de repository staan, en kan zijn wijzigingen via een remote server gaan synchronizeren met andere mensen die de repository hebben staan.
In principe kan je elke computer waarop je de git command tools hebt geïnstalleerd gebruiken als server. Het is dan alleen niet zo praktisch om samen te werken van thuis uit, je moet er dan voor zorgen dat je computer extern bereikbaar is en iedereen die samenwerkt moet dan je ip adres of hostname weten.
Daarom maken we gebruik van een hosted git omgeving. Hier zijn meerdere opties voor, wij zullen gebruik maken van GitHub.
We zullen eenmalig een GitHub account aanmaken en onze GitHub credentials linken aan onze computer, zodat we niet telkens opnieuw ons wachtwoord moeten ingeven.
Surf naar https://github.com en maak een account aan. Ga daarna naar https://education.github.com/pack en vraag een educational pack aan door te klikken op Get your pack, dit zal handig zijn om private repositories aan te maken.
Open daarna een terminal window. We zullen onze naam & email adres koppelen aan git, zodat git weet wie een commit uitvoert. Zorg dat je email adres het adres is dat je gebruikte om je GitHub account aan te maken:
$ git config --global user.name "Your Name Here"
$ git config --global user.email "[email protected]"
Daarna configureren we git om gebruik te maken van onze OSX keychain om paswoorden op te slaan:
$ git config --global credential.helper osxkeychain
Login op je GitHub account, en klik op de "New repository" button. Kies een naam voor je repository en klik op de "Create Repository" button.
Open een terminal venster en navigeer via cd
commandos naar de map van de git repository met de "hello world" files. We zullen er voor zorgen dat we onze lokale repository via GitHub kunnen synchronizeren, door een "remote" te adden. Een remote is een locatie waarmee je een git repository kan synchronizeren:
$ git remote add origin https://github.com/frederikduchi/dev3-demo.git
$ git branch -M main
$ git push -u origin main
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 12 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (17/17), 8.98 KiB | 4.49 MiB/s, done.
Total 17 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/frederikduchi/dev3-demo.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Het git push
commando zal lokale, niet-gesynchronizeerde wijzigingen uploaden naar de remote lokatie. Het -u
attribuut gebruik je de allereerste keer, om ervoor te zorgen dat je bij toekomstige synchonizaties de remote name niet meer moet opgeven. Je kan dan in de toekomst gewoon git push
uitvoeren.
Vergeet niet om meteen al een .gitignore
bestand aan te maken, met onder andere ignores voor node_modules
, .DS_Store
en eventueel dist
. Zo vermijd je dat deze mappen en bestanden mee gesynchroniseerd worden!
Vanaf nu kun je commits gaan pushen naar de online repository. Meestal bundel je verschillende lokale commits wanneer je gaat pushen. Dit hebben we in feite reeds gedaan met onze eerste push: alle commits die we eerder uitvoerden, staan nu ook op de online repository.
Wis het world.txt
bestand, add & commit. We maken gebruik van git add -u .
om ervoor te zorgen dat de delete actie van die file gestaged wordt:
$ rm world.txt
$ git add -u .
$ git commit -m "removed world file"
[master 0b0d3b8] removed world file
1 file changed, 1 deletion(-)
delete mode 100644 world.txt
Maak een README.md bestand aan, met een beetje info over de repository:
$ echo "Demo repository" > README.md
$ git add .
$ git commit -m "added readme file"
[master a8515e0] added readme file
1 file changed, 1 insertion(+)
create mode 100644 README.md
Voer een git status
uit. Je ziet in het status rapport dat de repository 2 commits voorsprong heeft op de online versie:
# On branch master
# Your branch is ahead of 'origin/main' by 2 commits.
# (use "git push" to publish your local commits)
nothing to commit (working directory clean)
Doe een git push
om je commits op GitHub te plaatsen:
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 488 bytes | 488.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To https://github.com/frederikduchi/dev3-demo.git
f1e8b69..a8515e0 main -> main
Wanneer je de repository via je browser bekijkt, zal je zien dat de inhoud van de README.md
file, getoond wordt onder de lijst van files. Dit is een bestand in het Markdown formaat. Markdown is een eenvoudige markup taal om documenten op te maken. Meer informatie hierover kan je onder andere vinden op wikipedia: http://en.wikipedia.org/wiki/Markdown.
Eens een repository aangemaakt is, en op een server zoals GitHub staat, kun je hem ook op andere computers / locaties binnenhalen. De repository een eerste keer binnenhalen doe je via het git clone
commando. Vanaf dan kun je updates binnenhalen via het git pull
commando. git pull
is het omgekeerde van git push
, en zal je gebruiken om updates die je nog niet lokaal hebt staan binnen te halen.
Open een tweede terminal venster, navigeer naar de bovenliggende map van je originele git repository en maak een map aan waarin je een duplicaat van de repository zal clonen:
$ mkdir project2
$ cd project2
Voer het git clone commando uit, om de online repository binnen te halen (wijzig wel de url naar die van je eigen repository:
$ git clone https://github.com/frederikduchi/dev3-demo
Cloning into 'dev3-demo'...
remote: Enumerating objects: 22, done.
remote: Counting objects: 100% (22/22), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 22 (delta 3), reused 22 (delta 3), pack-reused 0
Unpacking objects: 100% (22/22), done.
Je hebt nu 2 clones van dezelfde repository op je computer. Eentje in de map project en eentje in de map project2. We zullen deze twee gebruiken om synchronisaties en merges te testen.
We simuleren samenwerken met 2 users door op in twee verschillende mappen met dezelfde remote (online repository) te synchronizeren. Let bij de volgende acties op in welke map je de nodige commando's uitvoert (de project map staat vanaf nu telkens voor het dollarteken in het uit te voeren commando).
In map 1 maken we een nieuw bestand project.txt
aan, we adden, committen en pushen naar de remote:
project$ echo "created in project" > project.txt
project$ git add .
project$ git commit -m "added project.txt file"
[main 3dc1c52] add project.txt file
1 file changed, 1 insertion(+)
create mode 100644 project.txt
project$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 300 bytes | 300.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/frederikduchi/dev3-demo.git
a8515e0..3dc1c52 main -> main
In map 2 (dev3-demo) maken we een nieuw bestand project2.txt
aan, doen we een add & commit en proberen we te pushen naar de remote:
project2$ echo "created in project2" > project2.txt
project2$ git add .
project2$ git commit -m "added project2.txt file"
[master 3bdfe55] added project2.txt file
1 file changed, 1 insertion(+)
create mode 100644 project2.txt
project2$ git push
To https://github.com/frederikduchi/dev3-demo
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'https://github.com/frederikduchi/dev3-demo'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
We krijgen een fout: er zijn nieuwe commits op de remote, die we nog niet hebben binnengehaald in onze project2 clone. We moeten deze eerst pullen, voor we een push kunnen doen:
project2$ git pull
We krijgen een vim of nano editor te zien om een merge uit te voeren. Je kan hier een custom message ingeven. Geef :quit
in om de vim editor te sluiten en verder te doen met de merge.
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/frederikduchi/dev3-demo
a8515e0..3dc1c52 main -> origin/main
Merge made by the 'recursive' strategy.
project.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 project.txt
Wanneer je een git status
uitvoert, zal je zien dat je 2 commits voor bent op de remote: 1 commit is de merge commit, en een tweede commit is de project2.txt
commit:
project2$ git status
# On branch main
# Your branch is ahead of 'origin/main' by 2 commits.
#
nothing to commit (working directory clean)
Doe opnieuw een push van die commits naar de remote. Het pushen lukt deze keer wel:
project2$ git push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 551 bytes | 551.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To https://github.com/frederikduchi/dev3-demo
3dc1c52..c18628a main -> main
Haal nu deze commits ook op in je eerste map (niet vergeten van map te wisselen dus) via een git pull
commando:
project$ git pull
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 5 (delta 2), reused 5 (delta 2), pack-reused 0
Unpacking objects: 100% (5/5), done.
From https://github.com/frederikduchi/dev3-demo
3dc1c52..c18628a main -> origin/main
Updating 3dc1c52..c18628a
Fast-forward
project2.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 project2.txt
Wij zullen voorlopig werken met een centralized workflow: we werken met meerdere mensen samen op 1 master branch via 1 remote. Er zijn nog andere, meer geavanceerde workflows die werken via branches en forks. Deze zijn echter al iets complexer.
In het voorgaande scenario hebben we een merge commit toegevoegd om een niet up-to-date repository te fast-forwarden. Deze is bij een centralized workflow overbodig: we kunnen beter gebruik maken van een rebase bij de git pull
. Een rebase zal eerst alle commits van de remote uitvoeren, en jouw commits erachter uitvoeren, in plaats van standaard een merge commit uit te voeren.
Doe een delete van project.txt
in de project map:
project$ rm project.txt
project$ git add -u .
project$ git commit -m "removed project.txt"
[main de80b79] removed project.txt
1 file changed, 1 deletion(-)
delete mode 100644 project.txt
project$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 233 bytes | 233.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/frederikduchi/dev3-demo.git
c18628a..de80b79 main -> main
Doe daarna een delete van project2.txt
in de project2
map. We zullen ook hier eerst een pull moeten doen voordat we kunnen pushen. Het verschil bij de pull is dat we een rebase flag meegeven, om ervoor te zorgen dat eerst alle commits van de remote repository uitgevoerd worden, en daarna onze eigen commits:
project2$ rm project2.txt
project2$ git add -u .
project2$ git commit -m "removed project2.txt"
[main 6be65fb] removed project2.txt
1 file changed, 1 deletion(-)
delete mode 100644 project2.txt
project2$ git pull --rebase
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1), pack-reused 0
Unpacking objects: 100% (2/2), done.
From https://github.com/frederikduchi/dev3-demo
c18628a..de80b79 main -> origin/main
First, rewinding head to replay your work on top of it...
Applying: removed project2.txt
Op deze manier wordt er geen merge commit aangemaakt. Wanneer je een git status
uitvoert, zie je dat je nu maar 1 commit voorsprong hebt op de remote repository, in plaats van 2:
project2$ git status
# On branch main
# Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)
#
nothing to commit (working directory clean)
Push naar de remote repository. Doe ook opnieuw een pull in de project map, zodat beide repositories up-to-date zijn.
Tot nu toe hebben we braafjes in aparte files wijzigingen aangebracht. Het kan echter gebeuren dat je met 2 personen in dezelfde file wijzigingen hebt aangebracht, en dat er een conflict optreedt.
Pas in de project
map de tekst aan in hello.txt
en push deze naar de remote repository:
project$ echo "edit in project" > hello.txt
project$ git add .
project$ git commit -m "changed hello.txt"
[main f1c4e08] changed hello.txt
1 file changed, 1 insertion(+), 1 deletion(-)
project$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 284 bytes | 284.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/frederikduchi/dev3-demo.git
8f2400d..f1c4e08 main -> main
Pas nu ook in de project2
map de tekst in dezelfde file aan en probeer te pushen:
project2$ echo "edit in project2" > hello.txt
project2$ git add .
project2$ git commit -m "changed hello.txt in project2"
[main 45b7ea9] chenged hello.txt in project2
1 file changed, 1 insertion(+), 1 deletion(-)
project2$ git push
To https://github.com/frederikduchi/dev3-demo
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'https://github.com/frederikduchi/dev3-demo'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
We moeten eerst nog pullen, voor we kunnen pushen:
project2$ git pull --rebase
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/frederikduchi/dev3-demo
8f2400d..f1c4e08 main -> origin/main
First, rewinding head to replay your work on top of it...
Applying: chenged hello.txt in project2
Using index info to reconstruct a base tree...
M hello.txt
Falling back to patching base and 3-way merge...
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
error: Failed to merge in the changes.
Patch failed at 0001 chenged hello.txt in project2
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
We krijgen een merge conflict, doordat we in beide repositories dezelfde file wijzigden. We moeten eerst dit conflict oplossen, voor dat de rebase kan verderdoen.
Via git status
kun je een lijst opvragen met de merge conflicts:
project2$ git status
#rebase in progress; onto f1c4e08
#You are currently rebasing branch 'main' on 'f1c4e08'.
# (fix conflicts and then run "git rebase --continue")
# (use "git rebase --skip" to skip this patch)
# (use "git rebase --abort" to check out the original branch)
#
#Unmerged paths:
# (use "git restore --staged <file>..." to unstage)
# (use "git add <file>..." to mark resolution)
# both modified: hello.txt
no changes added to commit (use "git add" and/or "git commit -a")
Je krijgt de melding dat je aan het rebasen bent. Bij "Unmerged paths" zie je de files die in conflict zijn. Je moet eerste het conflict oplossen, door de file te editen, voor je kan verder doen met de rebase.
Open het txt bestand in een teksteditor. Je zal zien dat zowel de content uit project
als project2
aanwezig is:
<<<<<<< HEAD
edit in project
=======
edit in project2
>>>>>>> changed hello.txt in project2
Pas de tekst aan naar:
edit in project
and edit in project2
Add deze aan de rebase actie en continue:
project2$ git add hello.txt
project2$ git rebase --continue
Applying: changed hello.txt in project2
Wanneer je nu een git status uitvoert, zal je zien dat je opnieuw op de master / main branch zit, en 1 commit voorsprong hebt op de remote:
project2$ git status
# On branch main
# Your branch is ahead of 'origin/main' by 1 commit.
#
nothing to commit (working directory clean)
Nu kan je opnieuw pushen naar de remote:
project2$ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 326 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/devinekask/git-demo
7f3b200..920c81f master -> master
Doe een git pull
in de andere map, zodat beide mappen terug in sync zijn.
Via Git kan je ook gebruik maken van branches.
Je kan branches toevoegen om:
- Nieuwe features te ontwikkelen
- Bugs te fixen
- Te experimenteren met nieuwe ideeën zonder je production code (code die online staat) te beïnvloeden
Dit is een walkthrough om te werken met 2 branches, namelijk develop
en master
branches. Op de master
branch kan je de production ready code terugvinden. Op de develop
branch daarentegen kan je code pushen die nog niet volledig klaar is om aan je eindgebruiker te tonen.
Vanaf het moment dat je tevreden bent met de (bugvrije) code in de develop
branch, kan je deze "mergen" naar de master
branch. Dit proces wordt verder uitgelegd.
Je werkt verder op het huidige project.
Maak dan een branch develop
aan door het volgende commando uit te voeren:
$ git branch develop
Een overzicht van je branches kun je opvragen via deze commando:
$ git branch
Nu we een nieuwe develop
branch hebben aangemaakt, kunnen we ernaar switchen door git checkout develop
uit te voeren:
$ git checkout develop
Push deze develop
branch naar remote en bekijk het resultaat op GitHub:
$ git push origin develop
Doe enkele commits op de develop
branch. Je kan deze tussendoor gerust pushen naar Github (via git push origin develop
).
We zouden nu graag onze code van develop
mergen met onze master
code, zodat beide branches dezelfde code bevatten (nu heeft develop
namelijk meer commits dan master
, oftewel develop
staat voor op master
- bekijk dit verschil ook eens in Github). Hiervoor moeten we eerst terugswitchen naar onze master
branch om in een volgende stap develop
erin te mergen:
$ git checkout main
De merge van develop
in master
kan via een git merge
commando:
$ git merge develop main
Switch onmiddellijk terug naar develop
, zodat je niet per ongeluk zit te ontwikkelen in de master
branch (we willen dat onze develop
branch altijd de meest recente code bevat):
$ git checkout develop
Push de branches:
$ git push origin main
$ git push origin develop
Als alternatief kan je ze ook allemaal in één keer pushen:
$ git push --all origin
Op een andere locatie kun je dan de branches pullen (binnentrekken):
$ git pull --rebase origin develop
$ git pull --rebase origin main
Dit is een basis introductie om je op weg te helpen met git. Er is nog heel wat meer mogelijk met git & github. Meer informatie & tutorials kun je onder andere vinden op volgende locaties: