Skip to content

Latest commit

 

History

History
106 lines (87 loc) · 5.37 KB

subtree-merges.asc

File metadata and controls

106 lines (87 loc) · 5.37 KB
Het mergen van subtrees

Het idee achter de subtree merge is dat je twee projecten hebt, en een van de projecten verwijst naar een subdirectory van de ander en vice versa. Als je een subtree merge specificeert, is Git vaak slim genoeg om uit te vinden dat de ene een subtree van de ander en zal daarvoor passend mergen.

We zullen een voorbeeld doornemen van het toevoegen van een separaat project in een bestaand project en dan de code mergen van de tweede naar een subdirectory van de eerste.

Eerst zullen we de Rack applicatie aan ons project toevoegen. We gaan het Rack project als een remote referentie in ons project toevoegen en deze dan uitchecken in zijn eigen branch:

$ git remote add rack_remote https://github.com/rack/rack
$ git fetch rack_remote --no-tags
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 https://github.com/rack/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 hebben we de root van het Rack project in onze rack_branch-branch en ons eigen project in de master-branch. Als je eerst de ene en dan de andere uitcheckt, kan je zien dat ze verschillende project roots hebben:

$ ls
AUTHORS         KNOWN-ISSUES   Rakefile      contrib         lib
COPYING         README         bin           example         test
$ git checkout master
Switched to branch "master"
$ ls
README

Dit is een beetje een raar concept. Het is niet verplicht dat alle branches in je repository branches van hetzelfde project zijn. Het is niet iets wat vaak voorkomt, omdat het zelden behulpzaam is, maar het is relatief eenvoudig om branches te hebben die volledig verschillende histories hebben.

In dit geval willen we het Rack project in onze master-project binnenhalen (pull) als een subdirectory. We kunnen dat in Git doen met git read-tree. Je zult meer over read-tree en zijn vriendjes leren in ch10-git-internals.asc, maar neem voor nu aan dat het de root tree van een branch naar je huidige staging area en werk dirctory inleest. We zijn zojuist teruggeschakeld naar de master-branch, en we pullen de rack_branch-branch in de rack subdirectory van de master-branch van ons hoofdproject:

$ git read-tree --prefix=rack/ -u rack_branch

Als we committen, zal het lijken alsof we alle Rack bestanden onder die subdirectory hebben - alsof we ze vanuit een tarball gekopieerd hebben. Wat dit interessant maakt, is dat we relatief eenvoudig wijzigingen van de ene branch naar de andere kunnen mergen. Dus, als het Rack project wijzigt, kunnen we upstream wijzigingen binnenhalen door naar die branch over te schakelen en te pullen:

$ git checkout rack_branch
$ git pull

Daarna kunnen we die wijzigingen in onze master-branch mergen. Om de wijzigingen binnen te halen en de commit message alvast in te vullen, gebruik je de --squash optie zowel als de -Xsubtree optie van de recursieve merge strategy. (De recursieve strategie is hier de standaard, maar we voegen het voor de duidelijkheid toe).

$ git checkout master
$ git merge --squash -s recursive -Xsubtree=rack rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

All de wijzigingen van het Rack project zijn gemerged en klaar om lokaal te worden gecommit. Je kunt ook het tegenovergestelde doen - de wijzigingen in de rack subdirectory van je master branch maken en ze dan later naar je rack_branch-branch mergen om ze dan in te dienen bij de beheerders of ze stroomopwaarts te pushen.

Dit is een manier om een workflow te krijgen die lijkt op de submodule workflow zonder submodules te gebruiken (wat we in ch07-git-tools.asc zullen behandelen). We kunnen in onze repository branches aanmaken met andere gerelateerde projecten en ze bij tijd en wijle subtree mergen in ons project. Dit is in sommige opzichten handig, bijvoorbeeld omdat alle code op een enkele plaats wordt gecommit. Het heeft echter ook nadelen in de zin dat het iets complexer is en gevoeliger voor fouten in het herintegreren van wijzigingen of abusievelijk een branch te pushen naar een niet gerelateerde repository.

Een ander gek iets is dat om een diff te krijgen tussen wat je in je rack subdirectory hebt en de code in je rack_branch-branch - om te zien of je ze moet mergen - kan je niet het normale diff commando gebruiken. In plaats daarvan moet je git diff-tree aanroepen met de branch waar het je mee wilt vergelijken:

$ git diff-tree -p rack_branch

Of, om wat in je rack subdirectory zit te vergelijken met wat de master-branch op de server was de laatste keer dat je gefetcht hebt kan je dit aanroepen

$ git diff-tree -p rack_remote/master