Идеја код спајања подстабла је да имате два пројекта и један од пројеката се мапира у поддиректоријум другог и обрнуто. Када задате спајање подстабла, програм Гит је често довољно паметан да одреди ако је један подстабло оног другог, па да споји на погодан начин.
Проћи ћемо кроз пример додавања одвојеног пројекта у постојећи пројекат, па затим спајање кода другог у поддиректоријум првог.
На почетку, додајмо Rack апликацију нашем пројекту. Rack пројекат додајемо као удаљену референцу у пројекат, па је затим одјављујемо у њену сопствену грану:
$ 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"
Сада у rack_branch
имамо корен Rack пројекта и наш пројекат у master
грани.
Ако одјавите један па онда други, видећете да им се корени пројекта разликују:
$ ls
AUTHORS KNOWN-ISSUES Rakefile contrib lib
COPYING README bin example test
$ git checkout master
Switched to branch "master"
$ ls
README
Ово је прилично чудан концепт. Не морају све гране у вашем репозиторијуму да буду гране истог пројекта. Није уобичајено, јер је ретко од помоћи, али је прилично једноставно да имате гране које садрже потпуно различите историје.
У овом случају желимо да повучемо Rack пројекат у наш master
пројекат као поддиректоријум.
У програму Гит то можемо да урадимо помоћу git read-tree
.
Научићете више о read-tree
и његовим другарима у ch10-git-internals.asc, али за сада је довољно да знате да она учитава корено стабло једне од грана у ваш текући стејџ и радни директоријум.
Управо смо се вратили у вашу master
грану и повлачимо rack_branch
грану у rack
поддиректоријум наше master
гране главног пројекта:
$ git read-tree --prefix=rack/ -u rack_branch
Када комитујемо, изгледа као да у том поддиректоријуму имамо све Rack фајлове – као да смо их прекопирали из tarball архиве. Оно што је интересантно је да прилично једноставно можемо да спојимо измене из једне гране у другу. Дакле, ако се Rack пројекат ажурира, узводне промене можемо да повучемо тако што се пребацимо на ту грану и повучемо:
$ git checkout rack_branch
$ git pull
Те измене затим можемо да спојимо назад у нашу master
грану.
Да бисте повукли измене и унапред попунили комит подруку, употребите --squash
опцију, као и -Xsubtree
опцију рекурзивне стратегије спајања.
Рекурзивна стратегија је овде и иначе подразумевана, али је због јаснијег приказа овде наводимо.
$ 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
Све измене из Rack пројекта су спојене и спремне за локално комитовање.
Такође можете да урадите и супротно – направите измене у rack
поддиректоријуму ваше master
гране па их спојите у rack_branch
да их касније предате одржаваоцима или да их гурнете узводно.
Ово нам омогућава начин да имамо сличан процес рада као у случају подмодула само без потребе да се користе подмодули (које ћемо обрадити у ch07-git-tools.asc). У нашем репозиторијуму можемо да држимо гране са осталим повезаним пројектима и да их повремено спајамо као подстабло у наш пројекат. То је на неки начин фино, на пример сав кôд се комитује на једно место. Међутим, постоје и лоше стране јер је донекле комплексно и лакше је да се направе грешке приликом реинтеграције измена или нехотичног гурања гране у неповезани репозиторијум.
Још једна помало чудна ствар је начин на који добијате разлику између онога што се налази у rack
поддиректоријуму и кода у rack_branch
грани – како бисте видели да ли је потребно да их спојите – не можете употребити обичну diff
команду.
Уместо ње морате извршити git diff-tree
са граном коју желите да упоредите:
$ git diff-tree -p rack_branch
Или, да бисте упоредили оно што се налази у rack
поддиректоријуму са оним из master
гране на серверу последњи пут кад сте преузели са њега, можете да извршите:
$ git diff-tree -p rack_remote/master