Большинство современных программных проектов зависят от работы других. Было бы пустой тратой времени изобретать велосипед в собственном коде, когда
Git, самая популярная в мире система контроля версий, предлагает отличный способ элегантного и надежного управления этими зависимостями. Его концепция «подмодуля» позволяет нам включать сторонние библиотеки и управлять ими, четко отделяя их от нашего собственного кода.
В этой статье вы узнаете, чем так полезны подмодули в Git, что они из себя представляют и как работают.
Хранение кода отдельно
Чтобы понять, почему подмодули Git действительно являются бесценной структурой, давайте рассмотрим случай без подмодулей. Когда вам нужно включить сторонний код (например, библиотеку с открытым исходным кодом), вы, конечно, можете пойти простым путем: просто загрузить код с GitHub и выгрузить его
Путем грубого копирования стороннего кода в ваш проект вы эффективно смешиваете несколько проектов в один. Граница между вашим собственным проектом и чужим (библиотекой) начинает стираться.
Всякий раз, когда вам нужно обновить код библиотеки (потому что ее сопровождающий добавил замечательную новую функцию или исправил неприятную ошибку), вам снова придется загружать, копировать и вставлять. Это быстро становится утомительным процессом.
Общее правило в разработке программного обеспечения «разделять отдельные вещи» существует не просто так. И это, безусловно, верно для управления сторонним кодом в ваших собственных проектах. К счастью, концепция подмодуля Git была создана именно для таких ситуаций.
Но, конечно, подмодули — не единственное доступное решение для такого рода проблем. Вы также можете использовать одну из различных систем «менеджера пакетов», которые предоставляют многие современные языки и фреймворки. И в этом нет ничего плохого!
Однако вы можете возразить, что архитектура подмодулей Git имеет несколько преимуществ:
Подмодули обеспечивают согласованный и надежный интерфейс — независимо от того, какой язык или инфраструктуру вы используете. Особенно, если вы работаете с несколькими технологиями, у каждой из них может быть свой менеджер пакетов со своим набором правил и команд. Подмодули, с другой стороны, всегда работают одинаково.
Не каждый фрагмент кода может быть доступен через диспетчер пакетов. Может быть, вы просто хотите поделиться своим собственным кодом между двумя проектами — ситуация, когда подмодули могут предложить простейший возможный рабочий процесс.
Что такое подмодули Git на самом деле
Подмодули в Git — это просто стандартные репозитории Git. Никаких модных инноваций, только те репозитории Git, которые мы все уже так хорошо знаем. В этом также заключается часть силы субмодулей: они настолько надежны и просты, потому что они такие «скучные» (с технологической точки зрения) и проверенные в полевых условиях.
Единственное, что делает репозиторий Git подмодулем, это то, что он помещается внутри другого, родительского репозитория Git.
В остальном подмодуль Git остается полностью функциональным репозиторием: вы можете выполнять все действия, которые вы уже знаете из своей «обычной» работы с Git — от изменения файлов до фиксации, извлечения и отправки. В подмодуле возможно все.
Добавление подмодуля
Возьмем классический пример и скажем, что мы хотели бы добавить стороннюю библиотеку в наш проект. Прежде чем мы начнем получать
$ mkdir lib
$ cd lib
Теперь мы готовы закачать в наш проект сторонний код, но упорядоченно, используя подмодули. Допустим, нам нужна небольшая
$ git submodule add https://github.com/spencermountain/spacetime.git
Когда мы запускаем эту команду, Git начинает клонировать репозиторий в наш проект как подмодуль:
Cloning into '
remote: Enumerating objects: 7768, done.
remote: Counting objects: 100% (1066/1066), done.
remote: Compressing objects: 100% (445/445), done.
remote: Total 7768 (delta 615), reused 975 (delta 588),
Receiving objects: 100% (7768/7768), 4.02 MiB | 7.78 MiB/s, done.
Resolving deltas: 100% (5159/5159), done.
И если мы посмотрим на папку с нашей рабочей копией, то увидим, что файлы библиотеки действительно прибыли в наш проект.
Файлы нашей библиотеки находятся здесь, включены в подмодуль
— Так какая разница? Вы можете спросить. В конце концов, файлы сторонних библиотек находятся здесь, как если бы мы их скопировали и вставили. Ключевое отличие состоит в том, что они действительно содержатся в собственном репозитории Git! Если бы мы просто загрузили несколько файлов, добавили их в наш проект, а затем закоммитили — как и другие файлы в нашем проекте — они были бы частью того же репозитория Git. Однако подмодуль следит за тем, чтобы файлы библиотеки не «просочились» в репозиторий нашего основного проекта.
Посмотрим, что еще произошло:.gitmodulesв корневой папке нашего основного проекта создан новый файл. Вот что он содержит:
[submodule «lib/spacetime»]
path = lib/spacetime
url = https://github.com/spencermountain/spacetime.git
Этот.gitmodulesфайл является одним из нескольких мест, где Git отслеживает подмодули в нашем проекте. Другой —.git/config, который теперь заканчивается так:
[submodule «lib/spacetime»]
url = https://github.com/spencermountain/spacetime.git
active = true
И, наконец, Git также хранит копию.gitрепозитория каждого подмодуля во внутренней.git/modulesпапке.
Все это технические детали, которые вам не нужно помнить. Однако это, вероятно, поможет вам понять, что внутреннее обслуживание подмодулей Git довольно сложно. Вот почему важно убрать одну вещь: не возитесь с конфигурацией подмодуля Git вручную! Если вы хотите переместить, удалить или иным образом манипулировать подмодулем, сделайте себе одолжение и не пытайтесь делать это вручную. Либо используйте соответствующие команды Git, либо графический интерфейс рабочего стола для Git, например «Tower», который позаботится об этих деталях за вас.
Графические интерфейсы Git для настольных ПК, такие как Tower, упрощают работу с подмодулями Git.
Давайте посмотрим на статус нашего основного проекта, теперь, когда мы добавили подмодуль:
$ git status
On branch master
Changes to be committed:
(use «git restore —staged
new file:.gitmodules
new file: lib/spacetime
Как видите, Git рассматривает добавление подмодуля как изменение, как и любое другое. Соответственно, мы должны зафиксировать это изменение, как и любое другое:
$ git commit -m «Add timezone converter library as a submodule»
Клонирование проекта с помощью подмодулей Git
В нашем примере выше мы добавили новый подмодуль в существующий репозиторий Git. А как насчет «наоборот», когда вы клонируете репозиторий, который уже содержит подмодули?
Если бы мы выполнили ваниль git clone <
В таком случае, чтобы заполнить подмодули после того, как вы клонировали их родительский репозиторий, вы можете просто выполнить git submodule update —init —recursiveпотом. Еще лучший способ — просто добавить —
Проверка изменений
В «обычном» репозитории Git мы обычно проверяем ветки. Используя git checkout
В подмодуле мы всегда проверяем конкретную ревизию, а не ветку! Даже когда вы выполняете команду, как git checkout mainв подмодуле, в фоновом режиме отмечается последняя на данный момент фиксация в этой ветке, а не сама ветка.
Такое поведение, конечно, не является ошибкой. Подумайте об этом: когда вы включаете стороннюю библиотеку, вы хотите иметь полный контроль над тем, какой именно код используется в вашем основном проекте. Когда мейнтейнер библиотеки выпускает новую версию, это все хорошо... но вы не обязательно хотите, чтобы эта новая версия автоматически использовалась в вашем проекте. Просто потому, что вы не знаете, могут ли эти новые изменения сломать ваш проект!
Если вы хотите узнать, какую версию используют ваши подмодули, вы можете запросить эту информацию в своем основном проекте:
$ git submodule status
ea703a7d557efd90ccae894db96368d750be93b6 lib/spacetime (6.16.3)
Это возвращает текущую проверенную версию нашего lib/spacetimeподмодуля. И это также позволяет нам узнать, что эта версия является тегом с именем «6.16.3». Теги часто используются при работе с подмодулями в Git.
Допустим, вы хотели, чтобы ваш подмодуль использовал более старую версию с тегом «6.14.0».
$ cd lib/spacetime/
$ git checkout 6.14.0
Previous HEAD position was ea703a7 Merge pull request #301 from spencermountain/dev
HEAD is now at 7f78d50 Merge pull request #268 from spencermountain/dev
Если мы теперь вернемся в наш основной проект и git submodule statusснова выполним его, мы увидим отражение нашей проверки:
$ cd... /...
$ git submodule status
+7f78d50156ae1205aa50675ddede81a61a45fade lib/spacetime (6.14.0)
Однако внимательно посмотрите на вывод: маленький +символ перед этим хешем
Вызов git statusнашего основного проекта теперь также информирует нас об этом факте:
$ git status
On branch master
Changes not staged for commit:
(use «git add
(use «git restore
modified: lib/spacetime (new commits)
Вы можете видеть, что Git рассматривает перемещение указателя подмодуля как изменение, как и любое другое: мы должны зафиксировать его в репозитории, если мы хотим, чтобы он был сохранен:
$ git commit -m «Changed checked out revision in submodule»
$ git push
Обновление подмодуля Git
В приведенных выше шагах именно мы перемещали указатель подмодуля: мы были теми, кто решил проверить другую ревизию, зафиксировать ее и отправить в удаленный репозиторий нашей команды. А что, если
Давайте сделаем простое git pullв нашем основном проекте — как мы, вероятно, делали бы довольно часто — чтобы получить новые изменения из общего удаленного репозитория:
$ git pull
From https://github.com/gntr/git-crash-course
d86f6e0... 055333e main → origin/main
Updating d86f6e0... 055333e
lib/spacetime | 2 +-
1 file changed, 1 insertion (+), 1 deletion (-)
Предпоследняя строка указывает,
$ git submodule status
+7f78d50156ae1205aa50675ddede81a61a45fade lib/spacetime (6.14.0)
Я уверен, вы помните этот маленький +знак: он означает, что указатель подмодуля был перемещен! Чтобы обновить нашу локально извлеченную версию до «официальной», которую выбрал наш товарищ по команде, мы можем запустить updateкоманду:
$ git submodule update lib/spacetime
Submodule path 'lib/spacetime’: checked out '5e3d70a88180879ae0222b6929551c41c3e5309e’
Хорошо! Наш подмодуль теперь проверен в ревизии, которая записана в нашем основном репозитории проекта!
Работа с подмодулями в Git
Мы рассмотрели основные строительные блоки работы с подмодулями Git. Остальные рабочие процессы действительно вполне стандартны!
Например, проверка новых изменений в подмодуле работает так же, как и в любом другом репозитории Git: вы запускаете git fetchкоманду внутри репозитория подмодуля, за которой, возможно, следует
Внесение изменений в подмодуль также может быть полезным для вас, особенно если вы сами управляете кодом библиотеки (поскольку это внутренняя библиотека, а не сторонняя). Вы можете работать с подмодулем, как и с любым другим репозиторием Git: вы можете вносить изменения, фиксировать их, отправлять их и так далее.
Использование полной мощности Git
Под капотом Git скрыта огромная мощь. Но многие из его передовых инструментов, например подмодули Git, малоизвестны. Очень жаль, что так много разработчиков упускают много мощных вещей!
Если вы хотите углубиться и получить представление о некоторых других продвинутых методах Git, я настоятельно рекомендую «Advanced Git Kit «: это (бесплатная!) коллекция коротких видеороликов, которые знакомят вас с такими темами, как Reflog, Interactive Rebase, Cherry -Выбирая и даже разветвляя стратегии.