title | author | fontsize | geometry | paper | lang | template |
---|---|---|---|---|---|---|
Лабораторная работа 6 |
А.В. Родионов |
14pt |
margin=2cm |
a4 |
russian |
default.html |
Зачастую, при работе над сложным проектом, возникает необходимость использовать наработки из другого проекта. При этом, объединять их в один репозиторий может быть нецелесообразно, например, если над включаемым проектом работает сторонняя команда разработчиков. Такой внешний проект может представлять собой библиотеку для языка программирования, изолированную подсистему, внешний сетевой сервис и т.п. Во многих случаях, удается использовать средства управления зависимостями выбранного языка программирования, такие как easy install или pip для Python, но подключение сторонних проектов как зависимостей, не дает разработчикам основного проекта возможности участвовать в их развитии и самим оперативно вносить в них изменения. Одним из решений этой проблемы является механизм подмодулей Git. Подмодуль позволяет включить репозиторий, как директорию внутри другого репозитория. При этом, коммиты подмодулей хранятся независимо от коммитов основного репозитория и имеют собственную базу Git.
Внешний проект в качестве подмодуля можно добавить командой:
git submodule add <URL репозитория> <директория>
Если имя директории не указано, подмодуль будет автоматически размещен в директории с именем,
совпадающим с названием его репозитория. После выполнения этой команды, в дереве проекта появится
соответствующая поддиректория, а так же файл .gitmodules
. В нем содержится информация обо всех
используемых в проекте подмодулях. Директории подмодулей отслеживаются Git вместе с файлом
.gitmodules
, однако для директорий фиксируется только хэш коммита, на котором находится
соответствующий проект. Команды Git работают в дереве основного проекта и в подмодулях независимо
друг от друга, поэтому можно добавлять коммиты в подмодули и обновлять их время от времени.
При клонировании проекта, по умолчанию для подмодулей создаются только пустые директории. Чтобы
загрузить изменения из репозиториев подмодулей, после выполнения команды git clone <URL проекта>
,
необходимо перейти в его рабочее дерево и выполнить команды:
git submodule init
git submodule update
Состояние подмодулей после выполнения этих команд будет установлено в тот коммит, который был
зафиксирован при добавлении их в основной проект. Чтобы сразу начать работу над проектом, можно
объединить его клонирование с рекурсивной загрузкой подмодулей, командой git clone --recursive <URL основного проекта>
.
Если нужно обновить подмодуль, достаточно перейти в его директорию и выполнить команду git fetch
,
чтобы получить актуальные изменения, а затем перевести указатель подмодуля на последний коммит --
командой git merge origin/master
. После этого, новое положение подмодуля будет считаться
изменением в основном проекте, которое необходимо добавить в индекс командой git add
, а затем
сохранить, командой git commit
. По умолчанию, команда git diff
отображает изменения в состоянии
подмодулей только на уровне хешей коммитов. Чтобы вывести более подробную информацию, можно
выполнить команду git diff --submodule
. Также с ключом --submodule
можно вызывать команду git log
.
Объединить команды git fetch
и git merge
для обновления подмодулей, можно выполнив команду git submodule update --remote <имя подмодуля>
. Если не указывать имя директории подмодуля, то будут
обновлены все имеющиеся в проекте.
По умолчанию предполагается, что подмодули отслеживают ветку master
соответствующих репозиториев.
Изменить ветку для конкретного подмодуля можно либо командой:
git config -f .gitmodules submodule.<имя подмодуля>.branch <имя ветки>
либо путем непосредственного редактирования файла .gitmodules
.
Подмодули в проекте хранятся в "безголовом" состоянии, т.е. не имеют по умолчанию текущей ветки.
Добавленные коммиты не будут отслеживаться в соответствующем репозитории, пока в подмодуле не будет
создана для них ветка командой git checkout -b <новая ветка>
. Впоследствии, ее можно будет
отправить в репозиторий подмодуля командой git push -u origin <новая ветка>
.
Чтобы избежать обратного перехода подмодуля в "безголовое" состояние, нужно будет выполнять команду
git submodule update --remote
с дополнительным ключом --merge
. Выбранная в подмодуле ветка или
коммит будет при этом слита с текущими изменениями в репозитории подмодуля. Также, вместо слияния,
можно использовать перебазирование, для этого вместо ключа --merge
применяется --rebase
. В
обоих случаях Git не позволит применить коммиты из внешнего репозитория, если в дереве подмодуля
есть несохраненные изменения. Чтобы эти изменения были учтены, нужно будет выполнить в обычном
порядке git add
и git commit
в дереве подмодуля, после чего, возможно, потребуется ручное
разрешение конфликтов с внешним репозиторием.
Если подмодуль был переведен в режим отслеживания ветки, добавленные в него изменения можно
отправить в его репозиторий командой git push
. Это необходимо сделать раньше, чем будут отправлены
изменения основного проекта. В противном случае, подмодуль будет ссылаться на еще несуществующий в
репозитории коммит. Чтобы этого избежать, следует отправлять изменения в основном проекте с помощью
команды
git push --recurse-submodules=check
В этом режиме отправка изменений будет остановлена, если подмодуль ссылается на еще не переданный в удаленный репозиторий коммит. Можно объединить отправку изменений в основном и вложенных репозиториях командой
git push --recurse-submodules=on-demand
Если разные ветки основного репозитория ссылаются на разные коммиты в одном и том же подмодуле,
попытка их слияния вызовет конфликт. Чтобы слияние завершилось успешно, необходимо перевести
подмодуль на коммит, который учитывает информацию из обоих веток. Для этого следует определить
идентификаторы конфликтующих в ветках коммитов командой git diff
. Затем нужно перейти в директорию
подмодуля, в котором имеется конфликт, и создать ветку для конфликтующего коммита командой:
git branch <временное имя ветки> <идентификатор коммита из origin>
и слить эту ветку с текущим коммитом подмодуля командой
git merge <временное имя ветки>
Возможные конфликты в исходных файлах подмодуля можно будет затем разрешить путем редактирования.
Далее, командами git add
, git commit
и git push
подмодуль переводится в позицию нового
коммита, который является потомком конфликтующих. Временную ветку можно будет удалить обычным путем,
командой git branch -d <временное имя ветки>
, а после перехода в основное дерево, нужно будет
выполнить git add <имя подмодуля>
и git commit
, разрешив тем самым конфликт в ветках.
Если в базе коммитов подмодуля уже имеется коммит, являющийся потомком конфликтных коммитов, Git может определить его идентификатор автоматически и предложить его при выводе сообщения о конфликте. Можно перевести позицию подмодуля в этот коммит, командой:
git update-index --cacheinfo 160000 <идентификатор коммита> <имя подмодуля>
Чтобы перед разрешением конфликта изучить изменения, которые включает в себя предложенный коммит,
желательно предварительно перейти в дерево подмодуля и командами git diff
, git log
или git show
определить, действительно ли он содержит подходящие изменения. После этого, можно просто
выполнить в дереве подмодуля git merge <идентификатор коммита>
и перейти на него ускоренной
перемоткой вперед.
Для выполнения рутинных операций над подмодулями можно использовать команду git submodule foreach "действие"
. Например, чтобы перевести все подмодули проекта из "безголового" состояния в
отслеживание ветки master
, можно выполнить git submodule foreach "git checkout master"
. Также
может быть полезным выполнение команды git diff
для всех подмодулей проекта или команды git stash
.
Убрать из репозитория подмодуль, добавленный туда по ошибке, можно командой git rm <директория подмодуля>
. Если добавить после этого изменения в индекс, соответствующие записи в .gitmodules
будут удалены автоматически, однако коммиты из репозитория подмодуля останутся в базе Git. Это может
помешать повторному добавлению в проект подмодуля с тем же именем, но из другого репозитория.
Поэтому, для полного удаления подмодуля, следует также удалить с диска директорию .git/modules/<имя подмодуля>
, находящуюся в рабочем дереве проекта.
- Создайте новый репозиторий и добавьте туда несколько файлов
- Добавьте в него удаленный или локальный репозиторий в виде подмодуля
- Добавьте в репозиторий подмодуля коммиты
- Обновите положение указателя подмодуля в проекте, загрузив изменения из его репозитория
- Добавьте изменения в подмодуль и отправьте их в его репозиторий
- Создайте вторую ветку в основном репозитории и переведите в ней подмодуль в положение,
отличающееся от ветки
master
- Слейте новую ветку с
master
в основном репозитории и разрешите образовавшийся конфликт - Проверьте действие команды
git submodule foreach
, переведя все подмодули в режим отслеживанияmaster
- Удалите подмодуль из репозитория и создайте на его месте новый, с тем же именем, но ссылающийся на другой репозиторий