-
1. Aan de slag
- 1.1 Over versiebeheer
- 1.2 Een kort historisch overzicht van Git
- 1.3 Wat is Git?
- 1.4 De commando-regel
- 1.5 Git installeren
- 1.6 Git klaarmaken voor eerste gebruik
- 1.7 Hulp krijgen
- 1.8 Samenvatting
-
2. Git Basics
-
3. Branchen in Git
- 3.1 Branches in vogelvlucht
- 3.2 Eenvoudig branchen en mergen
- 3.3 Branch-beheer
- 3.4 Branch workflows
- 3.5 Branches op afstand (Remote branches)
- 3.6 Rebasen
- 3.7 Samenvatting
-
4. Git op de server
- 4.1 De protocollen
- 4.2 Git op een server krijgen
- 4.3 Je publieke SSH sleutel genereren
- 4.4 De server opzetten
- 4.5 Git Daemon
- 4.6 Slimme HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Hosting oplossingen van derden
- 4.10 Samenvatting
-
5. Gedistribueerd Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revisie Selectie
- 7.2 Interactief stagen
- 7.3 Stashen en opschonen
- 7.4 Je werk tekenen
- 7.5 Zoeken
- 7.6 Geschiedenis herschrijven
- 7.7 Reset ontrafeld
- 7.8 Mergen voor gevorderden
- 7.9 Rerere
- 7.10 Debuggen met Git
- 7.11 Submodules
- 7.12 Bundelen
- 7.13 Vervangen
- 7.14 Het opslaan van inloggegevens
- 7.15 Samenvatting
-
8. Git aanpassen
- 8.1 Git configuratie
- 8.2 Git attributen
- 8.3 Git Hooks
- 8.4 Een voorbeeld van Git-afgedwongen beleid
- 8.5 Samenvatting
-
9. Git en andere systemen
- 9.1 Git als een client
- 9.2 Migreren naar Git
- 9.3 Samenvatting
-
10. Git Binnenwerk
- 10.1 Binnenwerk en koetswerk (plumbing and porcelain)
- 10.2 Git objecten
- 10.3 Git Referenties
- 10.4 Packfiles
- 10.5 De Refspec
- 10.6 Uitwisseling protocollen
- 10.7 Onderhoud en gegevensherstel
- 10.8 Omgevingsvariabelen
- 10.9 Samenvatting
-
A1. Bijlage A: Git in andere omgevingen
- A1.1 Grafische interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Visual Studio Code
- A1.4 Git in Eclipse
- A1.5 Git in Sublime Text
- A1.6 Git in Bash
- A1.7 Git in Zsh
- A1.8 Git in PowerShell
- A1.9 Samenvatting
-
A2. Bijlage B: Git in je applicaties inbouwen
- A2.1 Commando-regel Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Bijlage C: Git Commando’s
- A3.1 Setup en configuratie
- A3.2 Projecten ophalen en maken
- A3.3 Basic Snapshotten
- A3.4 Branchen en mergen
- A3.5 Projecten delen en bijwerken
- A3.6 Inspectie en vergelijking
- A3.7 Debuggen
- A3.8 Patchen
- A3.9 Email
- A3.10 Externe systemen
- A3.11 Beheer
- A3.12 Binnenwerk commando’s (plumbing commando’s)
7.13 Git Tools - Vervangen
Vervangen
Zoals we eerder hebben benadrukt zijn de objecten in de database van Git onwijzigbaar, maar Git heeft een interessante manier om te doen alsof je objecten in de database vervangt met andere objecten.
Het replace
commando laat je een object in Git opgeven en te zeggen dat "elke keer als je dit object ziet, doe alsof het dit een ander object is".
Dit is het nuttigst voor het vervangen van een commit in je historie met een andere zonder de gehele historie te vervangen met, laten we zeggen, git filter-branch
.
Bijvoorbeeld, stel dat je een enorme code historie hebt en je wilt je repository opsplitsen in een korte historie voor nieuwe ontwikkelaars en een veel langere en grotere historie voor mensen die geïnteresseerd zijn in het graven in gegevens (data mining). Je kunt de ene historie op de andere enten door de vroegste commit in de nieuwe lijn met de laatste commit van de oude lijn te "vervangen". Dit is prettig omdat het betekent dat je niet echt alle commits in de nieuwe historie hoeft te herschrijven, wat je normaalgesproken wel zou moeten doen om ze samen te voegen (omdat de voorouderschap de SHA-1’s beïnvloedt).
Laten we dat eens uitproberen.
Laten we een bestaande repository nemen, en deze in twee repositories splitsen, een recente en een historische, en laten we dan kijken hoe we ze kunnen herschikken zonder de SHA-1 waarden van de recente repository te wijzigen met behulp van replace
.
We zullen een eenvoudige repository met vijf simpele commits gebruiken:
$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
We willen deze opdelen in twee historische lijnen. Een lijn gaat van commit een tot commit vier - dat zal de historische worden. De tweede lijn zal alleen commits vier en vijf zijn - dat is dan de recente historie.
Nu, de historische historie maken is eenvoudig, we kunnen gewoon een branch in de geschiedenis zetten en dan die branch naar de master branch pushen van een nieuwe remote repository.
$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Nu kunnen we de nieuwe history
-branch naar de master
-branch van onze nieuwe repository pushen:
$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
* [new branch] history -> master
Goed, onze historie is nu gepubliceerd. Nu is het moeilijkere gedeelte het terugsnoeien van onze recente historie zodat deze kleiner wordt. We moeten een overlapping maken op zo’n manier dat we een commit kunnen vervangen in een repository die een gelijke commit heeft, dus we gaan deze afkappen tot alleen commits vier en vijf (dus de vierde commit overlapt).
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Het is in dit geval handig om een basis commit te maken die instructies bevat hoe de historie uit te breiden, zodat andere ontwikkelaars weten wat te doen als ze de eerste commit in de afgekapte historie tegenkomen en meer nodig hebben. Dus wat we hier gaan doen is een initieel commit object maken als onze basis en daar instructies in zetten, dan rebasen we de overige commits (vier en vijf) daar bovenop.
Om dat te doen, moeten we een punt kiezen om af te splitsen, wat voor ons de derde commit is, welke 9c68fdc
in SHA-spraak is.
Dus onze basis commit zal van die tree af worden getakt.
We kunnen onze basis commit maken met het commit-tree
commando, wat gewoon een tree neemt en ons een SHA-1 teuggeeft van een gloednieuw, ouderloos commit object.
$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Noot
|
Het |
Goed, nu we dus een basis commit hebben, kunnen we de rest van onze historie hier boven op rebasen met git rebase --onto
.
Het --onto
argument zal de SHA-1 zijn die we zojuist terugkregen van commit-tree
en het rebase punt zal de derde commit zijn (de ouder van de eerste commit die we willen bewaren: 9c68fdc
):
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
Mooi, dus we hebben onze recente historie herschreven bovenop een weggooi basis commit die nu onze instructies bevat hoe de gehele historie weer te herbouwen als we dat zouden willen. We kunnen die nieuwe historie op een nieuw project pushen en nu, als mensen die repository klonen, zullen ze alleen de meest recente twee commits zien en dan een basis commit met instructies.
Laten we de rollen nu omdraaien naar iemand die het project voor het eerst kloont en die de hele historie wil hebben. Om de historische gegevens na het klonen van deze gesnoeide repository te krijgen, moet je een tweede remote toevoegen voor de historische repository en dan fetchen:
$ git clone https://github.com/schacon/project
$ cd project
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
* [new branch] master -> project-history/master
Nu zal de medewerker hun recente commits in de master
-branch hebben en de historische commits in de project-history/master
-branch.
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git log --oneline project-history/master
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Om deze te combineren, kan je simpelweg git replace
aanroepen met de commit die je wilt vervangen en dan de commit waarmee je het wilt vervangen.
Dus we willen de "fourth" commit in de master branch met de "fourth" commit in de project-history/master
-branch vervangen:
$ git replace 81a708d c6e1e95
Als je nu naar de historie van de master
-branch kijkt, lijkt het er zo uit te zien:
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Gaaf, toch? Zonder alle SHA-1 stroomopwaarts te hoeven vervangen, waren we toch in staat om een commit in onze history te vervangen met een compleet andere commit en alle normale instrumenten (bisect
, blame
, etc.) blijven werken zoals we van ze mogen verwachten.
Interessant genoeg, blijf het nog steeds 81a708d
als de SHA-1 laten zien, zelfs als het in werkelijkheid de gegevens van de c6e1e95
commit gebruikt waar we het mee hebben vervangen.
Zelfs als je een commando als cat-file
aanroept, zal het je de vervangen gegevens tonen:
$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700
fourth commit
Onthoud dat de echte ouder van 81a708d
onze plaatsvervangende commit was (622e88e
), niet 9c68fdce
zoals hier vermeld staat.
Het andere interessante is dat deze gegevens in onze referenties opgeslagen zijn:
$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040
Dit houdt in dat het eenvoudig is om onze vervanging met anderen te delen, omdat we deze naar onze server kunnen pushen en andere mensen het eenvoudig kunnen downloaden. Dit is niet zo nuttig in het scenario van historie-enten welke we hier nu behandeld hebben (als iedereen toch beide histories zou gaan downloaden, waarom zouden we ze dan gaan splitsen) maar het kan handig zijn in andere omstandigheden.