Subboom mergen
Nu je de moeilijkheden van het submodulesysteem hebt gezien, laten we eens kijken naar een alternatieve manier om hetzelfde probleem aan te pakken. Zodra Git merge’t, kijkt het naar wat het samen moet mergen en kiest dan een toepasselijke mergestrategie om te gebruiken. Als je twee branches aan het mergen bent, zal Git een recursive strategie gebruiken. Als je meer dan twee branches aan het mergen bent, zal Git de octopus strategie kiezen. Deze strategieën worden automatisch voor je gekozen, omdat de recursieve strategie complexe drie-weg merge situaties kan behandelen – bijvoorbeeld, meer dan één gezamenlijke voorouder – maar het kan slechts twee branches behandelen. De octopus merge kan meerdere branches behandelen, maar is voorzichtiger om moeilijke conflicten te vermijden, dus is het gekozen als de standaard strategie als je meer dan twee branches probeert te mergen.
Maar er zijn meerdere strategieën die je ook kunt kiezen. Eén ervan is de subtree merge, en je kunt het gebruiken om met het subproject probleem om te gaan. Hier zul je zien hoe je dezelfde rack inbedding kunt doen als in de laatste sectie, maar in plaats daarvan subboommerges te gebruiken.
Het idee van de subboommerge is dat je twee projecten hebt, en één van de projecten wijst naar de submap van de andere en vice versa. Als je een subboommerge specificeerd, dan is Git slim genoeg om uit te vogelen dat de ene een subboom van de andere is en toepasselijk te mergen – het is erg verbazingwekkend.
Eerst voeg je de Rack applicatie toe aan je project. Dan voeg je het Rack project toe als een remote reference in je eigen project en checked het dan uit in zijn eigen branch:
$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From git@github.com:schacon/rack
* [new branch] build -> rack_remote/build
* [new branch] master -> rack_remote/master
* [new branch] rack-0.4 -> rack_remote/rack-0.4
* [new branch] rack-0.9 -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
Nu heb je de wortel van het Rack project in je rack_branch
branch en je eigen project in de master
branch. Als je één uitchecked en dan de andere, kun je zien dat ze verschillende project wortels hebben:
$ ls
AUTHORS KNOWN-ISSUES Rakefile contrib lib
COPYING README bin example test
$ git checkout master
Switched to branch "master"
$ ls
README
Je wilt het Rack project in je master
project pullen als een submap. Je kunt dat in Git doen met git read-tree
. Je zult meer over read-tree
en zijn vrienden leren in Hoofdstuk 9, maar weet voor nu dat het de wortel boom van een branch in je huidige staging area en werkmap leest. Je hebt zojuist teruggewisseld naar je master
branch, en je pulled de rack
branch in de rack
submap van je master
branch in je hoofdproject:
$ git read-tree --prefix=rack/ -u rack_branch
Als je commit, ziet het eruit alsof je alle Rack bestanden in die submap hebt – alsof je ze uit een tarball gekopieerd hebt. Wat interessant is is dat je vrij makkelijk veranderingen van één branch in de andere kunt mergen. Dus als het Rack project vernieuwd kun je alle stroomopwaartse wijzigingen binnenhalen door naar die branch te wisselen en te pullen:
$ git checkout rack_branch
$ git pull
Dan kun je die veranderingen in je master branch mergen. Je kunt git merge -s subtree
gebruiken en het zal prima werken; maar Git zal ook de geschiedenissen samenvoegen, wat je waarschijnlijk niet wilt. Om de veranderingen binnen te halen en het commit bericht te vullen, gebruik dan de --squash
en --no-commit
opties samen met de -s subtree
strategie optie:
$ git checkout master
$ git merge --squash -s subtree --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
Alle wijzigingen van je Rack project worden ingemerged en zijn klaar om lokaal gecommit te worden. Je kunt ook het tegenovergestelde doen – veranderingen doen in de rack
submap van je master branch en dan die later in je rack_branch
branch mergen om ze naar de eigenaren te sturen of ze stroomopwaarts te pushen.
Om een diff te krijgen tussen wat je in je rack
submap hebt en de code in je rack_branch
branch – om te zien of je ze moet mergen – kun je niet het gebruikelijke diff
commando toepassen. In plaats daarvan moet je git diff-tree
uitvoeren met de branch waarmee je wilt vergelijken:
$ git diff-tree -p rack_branch
Of, om te vergelijken met wat in je rack
submap zit met wat in de master
branch op de server zat de laatste keer dat je gefetched hebt, kun je dit uitvoeren:
$ git diff-tree -p rack_remote/master