вторник, 25 марта 2014 г.

Git submodules

Позволяет удобно разделять библиотеки между многими проектами, облегчая обновление версий библиотек и не только.

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

A Git repo inside a Git repo
 + Pull down updates easily
 + Test your changes with an actual dependent project
 + Share changes easily
 + History independent of containing repo

Сабмодуль - это репозиторий внутри другого репозитория.


И так чтобы создать сабмодуль мы делаем:
1) Создаем отдельный репозиторий (например на сайте хостера) и получаем урл к нему.

2) Добавляем новый сабмодуль в наш проект
$ git submodule add git@example.com:modulename.git
Cloning into 'modulename'...
done.
Выполнение этой магической команды создаст директорию с модулем, а также создаст файл в корне проекта .gitmodules, в котором будут находится конфигурации для сабмодулей проекта.

3) При этом эти изменения мы должны закомитить в репозиторий проекта.
$ git status
# On branch master
# Changes to be committed:
#   new file:    .gitmodules
#   new file:    modulename

$ git commit -m "Add CSS submodule."
$ git push


Заглянем внутрь файла .gitmodules, там в каждом блоке находится имя модуля, путь в данном проекте к нему, урл на удаленный репозиторий где хранится данная библиотека(origin репозитория либы).
$ cat .gitmodules
[submodule "modulename"]
   path = modulename
   url  = git@example.com:modulename.git 


Правка сабмодулей

$ cd modulename
$ git checkout master

1) Нам нужно полюбому подвязать к какой-то бранче сабмодуль, потому что по-умолчанию он просто не подвязан.

2) Дальше мы делаем измения и потом комитим их и пушим на удаленный репозиторий библиотеки.

3) Но наш проект все еще привязан на предыдущую версию библиотеки. Поэтому мы должны сделать комит в котором подвяжемся на следующую.
Мы можем увидеть подвязку на старую версию и факт новой версии так:
$ cd ..

$ git status
# On branch master
# Changes not staged for commit:
#    modified:    modulename (new commits)
no changes added to commit
Что обязует нас сделать следующее:
$ git add modulename

$ git commit -m "New version is attached to current branch"
[master 4e4e316] New version is attached to current branch
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push

Начало работы с существующим проектом, который содержит сабмодули

1) клонирование проекта
$ git clone git@example.com:myproject.git 
Cloning into 'myproject'...
done.

$ cd myproject
$ ls -la
.gitmodules
some_prj_stuff
module1
module2
...
По наличию .gitmodules мы можем догадаться, что у нас в проекте находится подмодули, заглянув внтурь мы узнаем по каким относительным путям они находятся.
Но посетив их, мы увидим, что они пусты, просто нужно еще сделать их инициализацию.

2) Инициализация сабмодулей в проекте
Следующая команда делается в корне проекта:
$ git submodule init
Submodule 'module1' (git@example.com:module1.git) registered for path 'module1'
Submodule 'module2' (git@example.com:module2.git) registered for path 'module2' 
Происходит следующее, гит анализирует .gitmodules и добавляет описанные модули в файл конфигурации репозитория проекта
$ cat .git/config
...
[submodule "module1"]
   url  = git@example.com:module1.git 
[submodule "module2"]
   url  = git@example.com:module2.git 

3) Получение кода сабмодулей с их удаленных репозиториев:
$ git submodule update
Cloning into 'module1'...
done.
Submodule path to 'module1': checked out '4eac8bd...'
Cloning into 'module2'...
done.
Submodule path to 'module2': checked out '01си95с...'
Эту команду нужно выполнять каждый раз когда на удаленных репозиториях сабмодулей просиходят изменения, чтобы мы их тоже получили локально.

4) Делание изменений в сабмодулях
Ну все как обычно мы заходим поддиректорию сабмодуля и делаем там изменения, потом комитим их. Только вот чаще всего мы не обратим внимание на то, что  сабмодули не вяжутся ни на какой бранч из собственного репозитория, вернее они отвязуются после каждого git submodule update.

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

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


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

Чтобы проверять такую ситуации и не подствлять своих колег, которые не смогут апдейтить проект, пока вы не запушите библиотеку, нужно пушиться в проектах с сабмодулями с такой директивой:
$ git push --recurse-submodules=check
The following submodule paths contains changes that can not be found on any remote:
   module1
Please try
   git push --recurse-submodules=on-demand
or cd to the path and use
   git push
to push then to a remote.
fatal: Aborting 
Как видно директива делает проверку, обрывает пуш и подсказывает нам, что нужно запушить также изменения из сабмодуля, также показывает два способа как мы это можем сделать. Если изменения сабмодулей все синхронизированны с удаленками пуш проходит.

Чтобы не пушить дважды мы можем воспользоваться советом из предыдущей команды и вызывать раз из проекта:
$ git push --recurse-submodules=on-demand
А еще лучше сделать алиас на команду с такой директивой:
$ git config alias.pushall "push --recurse-submodules=on-demand"
и пользоваться ею.


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

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