пятница, 28 марта 2014 г.

Git subtree

С git submodule многие плюются, но есть альтернатива.

Первоначально до версии Git 1.7.11 , использовалась subtree merge strategy , после этой версии написали удобную комманду git subtree.

Начнем с преимуществ и недостатков этого подхода по сравнению с git submodule, ну а потом рассмотрим сначало старый  подход работы, после - новый, с командой:

+:
1) Управляться с простым воркфлоу просто:)
2) Достаточно старые версии поддерживаются (v1.5.2-)
3) Код подпроектов сразу появляется после clone проекта.
4) subtree не принуждает пользователей проекта учить что-то новое, и они даже не обязаны знать об использовании мердж стратегии сабтри.
5) subtree не добавляет новых файлов метадаты как submodules (.gitmodule)

-:
1) нужно познакомиться с новой мердж стратегией.
2) комитить код в репозитории подмодулей на порядок сложнее.
3) Ответсвенность за то, чтобы не мешать изменения в проекте и подмодуле в одних коммитах лежат на плечах разработчиков, а не системы.



Subtree merge strategy

Начнем с более старого способа, но который будет работать на более широком диапазоне версий Git.

Начинается все с того, что мы добавляем отдельный ремоут, который ссылается на репозиторий внешнего проекта, который будет у нас подмодулем. Тут это можно сделать как я понимаю многими вариациями, но главный вопрос, который нам нужно решать, это нужна ли нам история подмодуля в репозитории нашего проекта или нет.

Расмотрим два варианта решения задачи, один с класической книги http://git-scm.com/book/  , второй от https://bitbucket.org, для которого нужно поменьше буков нажимать.

Класический способ

$ git remote add submodule1-remote https://bitbucket.org/some-mirr/submodule1.git
$ git fetch submodule1-remote
Или чтобы вводить меньше буков:
$ git remote add -f submodule1-remote https://bitbucket.org/some-mirr/submodule1.git
-f - ключ, который означает сделать сразу fetch после дабавления этого ремоута

Создадим бранчу только с кодом подмодуля
$ git checkout -b submodule1-branch  submodule1-remote/master 

А следующая комманда создает в нашем проекте подкаталог submodule1-dir/, код которого вяжется на нашу локальную бранчу submodule1-branch, которая подвязана на удаленную указанную бранчу репозитория подмодуля (submodule1-remote/master)
$ git read-tree --prefix=submodule1-dir/ -u submodule1-branch 

Получим код с удаленки подмодуля
$ git checkout submodule1-branch 
$ git pull

Сейчас код подмодуля есть в локальном репозитории, а нам нужно сделать так чтобы он появился в поддиректории нашего проекта, и чтобы он тоже начал трекаться проектовским репозиторием. Для этого нужен мердж,а стратегия для такого мерджа назівается subtree
На этот раз мы решили, что история подмодуля в нашем проекте нам не интересна, поэтому ключ --squash. (При этом последующие обновления подмодуля будут приносить новые логи его истории в нашу историю).
$ git checkout master
$ git merge --squash -s subtree --no-commit submodule1-branch 
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

Теперь мы готовы делать комит в наш бранч этого мерджа с осмысленным мессиджом:
git commit -m"[subtree] adding submodule1"

Чтобы увидеть изменения, которые мы сделали в репозитории проекта в подмодуле, мы должны сделать diff между кодом из подпапки и локальным бранчем чистого кода подмодуля, делается это с помощью отдельной комманды:
$ git diff-tree -p submodule1-branch 
Если нас интересуют новые внешние изменения в разработке подмодуля, мы можем не обновляя локальный бранч, сделать сравнение с удаленкой
$ git diff-tree -p submodule1-remote/master

Способ с отдельной командой для subtree стратегии 

Команда эта из последних версий гита, поэтому ее желательно обновить на убунте:
sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

При этом она по-умолчанию не включена, ее нужно включить:
sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
sudo ln -s /usr/share/doc/git/contrib/subtree/git-subtree.sh /usr/lib/git-core/git-subtree

А теперь как же ее пользоваться.
Следующая команда создает указанный подкаталог для модуля, делает fetch с удаленки  указанного бранча (мастер в данном случае) и этот код, добавив в новосозданный каталог, коммитит нам в репозиторий проекта, после этого мерджит одинокий коммит в поточную бранчу проекта: 
git subtree add --prefix submodule1-dir https://bitbucket.org/some-mirr/submodule1.git master --squash
Чтобы история подмодуля добавилась нужно не писать директиву --squash

Если после этой команды проверить логи, мы увидим два новых коммита:
1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as 'submodule1-dir' [Name Surname]
ca1f4da [3 minutes ago] Squashed 'submodule1-dir' content from commit 02199ea [Name Surname]

Теперь каждый раз когда нам нужно обновить подмодуль с его репозитория(upstream-а) делаем следующее:
$ git subtree pull --prefix submodule1-dir https://bitbucket.org/some-mirr/submodule1.git master --squash
Как видно по коммандам мы не завязались на удаленку подмодуля, а делаем обновления по факту, и не впрыскиваем стороннюю историю в свою  -- это оправдано в случае большого проекта и опасности частого обновления подмодулей, особенно если не сохраняется совместимость между версиями.

Ну впринципе, чтобы меньше клацать буков в командах, в случае если мы все же довольно часто решили обоновлять подмодуль, лучше ремоут зарегать в себя в локальном репозитории:
$ git remote add -f submodule1-remote https://bitbucket.org/some-mirr/submodule1.git
-f - ключ, который означает сделать сразу fetch после дабавления этого ремоута

Теперь сослемся на зареганый ремоут, а не на урл:
$ git subtree add --prefix submodule1-dir submodule1-remote master --squash

Когда пришло время обновления делаем:
$ git fetch submodule1-remote
$ git subtree pull --prefix submodule1-dir submodule1-remote master --squash

И конечно же, если мы решили законтрибутить в подмодуль, мы можем это сделать.
Но нужно обязательно помнить что все изменения, которые касаются отдельного подмодуля, должны комиться только с изменениями из этого подмодуля, потому что в противном случае, мы не сможем запушить в удаленные репозитории
git subtree push --prefix=submodule1-dir submodule1-remote master


http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/
http://git-scm.com/book/ch6-7.html




Комментариев нет:

Отправить комментарий