Работа с подмодулями
Наша система представляет собой набор модулей или плагинов, достаточно независимых, чтобы можно было вести разработку различных подсистем изолированно друг от друга и при этом сохранять целостность, подключая модули в конечный продукт, коим является интернет-банк.
Нам приходится отслеживать то, какие именно модули включаются в интернет-банк, вносить изменения во взаимосвязанные модули подсистем синхронно, чтобы не нарушить целостность кода. Кроме того процесс осложняется наличием как минимум двух активных веток: master - основного ствола разработки и текущей стабильной ветки, в которую вносятся только исправления ошибок.
Типичная проблема, возникающая при использовании подобного сценария, это, как сделать так, чтобы иметь возможность рассматривать их как отдельные, всё же имея возможность использовать один модуль внутри другого.
Git решает эту задачу, используя подмодули. Подмодули позволяют содержать один Git-репозиторий как подкаталог другого Git-репозитория. Это даёт возможность клонировать репозитории внутрь проекта, храня коммиты для вложенных репозиториев отдельно.
Начало использования подмодулей
Предположим, вы хотите добавить новую кастомизацию cust-ibank-fl-newbank в суперпроект ibank_root. Первое, что требуется сделать, это клонировать внешний репозиторий cust-ibank-fl-newbank в подкаталог супер-репозитория ibank_root. Добавление внешних проектов в качестве подмодулей делается командой git submodule add
:
>git submodule add git@source.isimplelab.com:ibank/cust-ibank-fl-newbank.git cust-ibank-fl-newbank
Initialized empty Git repository in ibank_root/cust-ibank-fl-newbank/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.
После этого в суперрепозитории ibank_root будет изменён файл .gitmodules
. Это конфигурационный файл, который содержит соответствие между URL проекта и локальным подкаталогом, в который был загружен подмодуль:
>type .gitmodules
[submodule "cust-ibank-fl-newbank"]
path = cust-ibank-fl-newbank
url = git@source.isimplelab.com:ibank/cust-ibank-fl-newbank.git
Теперь внутри проекта ibank_root в подкаталоге с именем cust-ibank-fl-newbank находится репозиторий cust-ibank-fl-newbank. Мы можем работать с ним как с обычным Git-репозиторием: вносить изменения и отправлять их в связанный удалённый репозиторий.
Если мы внесём изменения в этом подкаталоге и сделаем коммит, основной проект заметит, что HEAD в подмодуле был изменён, и зарегистрирует крайний хеш коммита в подмодуле. Теперь нам нужно зафиксировать добавление подмодуля в суперрепозитории и протолкнуть изменения в удалённый репозиторий:
>cd ibank_root
>git commit -m 'Добавлена кастомизация NewBank'
[master 0550271] Добавлена кастомизация NewBank
2 files changed, 4 insertions(+), 0 deletions(-)
create mode 100644 .gitmodules
create mode 160000 cust-ibank-fl-newbank
>git push origin master
Таким образом, если теперь кто-то склонирует ibank_root, он сможет воссоздать окружение в точности.
Клонирование проекта с подмодулями
Сейчас мы склонируем проект, содержащий подмодули. После получения такого проекта в вашей копии будут каталоги, содержащие подмодули, но без единого файла в них:
>git clone git@source.isimplelab.com:ibank/ibank_root.git
Initialized empty Git repository in ibank_root/.git/
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (6/6), done.
Каталог cust-ibank-fl-newbank присутствует, но он пустой. Чтобы выкачать его содержимое необходимо выполнить команду:
>git submodule update --init --remote
Initialized empty Git repository in ibank_root/cust-ibank-fl-newbank/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.
Submodule path 'cust-ibank-fl-newbank': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433'
Флаг --init
используется для инициализации вашего локального файла конфигурации и получения изменений, до состояния, указанного в .gitmodules
.
Флаг --remote
автоматически обновляет вложенные репозитории до последей версии на указанной в .gitmodules
ветке. В подавляющем большинстве случаев это именно то что вам нужно.
Особенности работы с подмодулями
При использование подмодулей необходимо помнить, что команда
git submodule update
, возвращает определённую версию проекта, но не внутри ветви. Это называется состоянием с отделённым HEAD (detached HEAD) — это означает, что файл HEAD указывает на конкретный коммит, а не на символическую ссылку.
Проблема в том, что теперь легко потерять изменения. Если вы сделаете первоначальный submodule update, сделаете коммит в каталоге подмодуля, не создавая ветки для работы в ней, и затем вновь выполните git submodule update из основного проекта, Git затрёт ваши изменения в подмодуле без предупреждения. Технически вы не потеряете проделанную работу, но у вас не будет ветки, указывающей на неё, так что восстановить её можно будет только зная хэш коммита. Для предотвращения этой проблемы переключайтесь на ветку после клонирования суперпроекта командой:
>cd ibank_root
>git submodule foreach git checkout master
Либо создавайте ветвь, когда работаете в каталоге подмодуля с использованием команды git checkout -b <имя ветки>
.
Когда вы сделаете обновление подмодуля командой submodule update в следующий раз, она откатит вашу работу, но у вас будет указатель для возврата назад.