Понимание и работа с подмодулями в Git

5 вещей, которые я хотел бы знать о Git Изучение

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

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

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

Хранение кода отдельно

Чтобы понять, почему подмодули Git действительно являются бесценной структурой, давайте рассмотрим случай без подмодулей. Когда вам нужно включить сторонний код (например, библиотеку с открытым исходным кодом), вы, конечно, можете пойти простым путем: просто загрузите код с GitHub и скопируйте его куда-нибудь в свой проект. Хотя этот подход, безусловно, быстрый, он определенно грязный по двум причинам:

  • Копируя сторонний код в свой проект грубой силой, вы эффективно смешиваете несколько проектов в один. Граница между вашим собственным проектом и проектом другого человека (библиотеки) начинает стираться.
  • Всякий раз, когда вам нужно обновить код библиотеки (потому что ее сопровождающий предоставил отличную новую функцию или исправил неприятную ошибку), вам снова придется загружать, копировать и вставлять. Это быстро становится утомительным процессом.

Общее правило в разработке программного обеспечения «держать отдельные вещи отдельно» существует не просто так. И это, безусловно, верно для управления сторонним кодом в ваших собственных проектах. К счастью, концепция подмодуля Git была создана именно для таких ситуаций.

Но, конечно, субмодули — не единственное доступное решение для такого рода проблем. Вы также можете использовать одну из различных систем «менеджеров пакетов», которые предоставляют многие современные языки и фреймворки. И в этом нет ничего плохого!

Однако вы можете возразить, что архитектура подмодулей Git имеет несколько преимуществ:

  • Подмодули обеспечивают согласованный и надежный интерфейс — независимо от того, какой язык или фреймворк вы используете. Особенно если вы работаете с несколькими технологиями, у каждой из них может быть свой собственный менеджер пакетов со своим набором правил и команд. С другой стороны, субмодули всегда работают одинаково.
  • Не каждый фрагмент кода может быть доступен через диспетчер пакетов. Возможно, вы просто хотите поделиться своим кодом между двумя проектами — ситуация, когда подмодули могут предложить самый простой рабочий процесс.

Что такое подмодули Git

Подмодули в Git — это просто стандартные репозитории Git. Никаких модных инноваций, все те же репозитории Git, которые мы все так хорошо знаем. Это тоже часть мощи субмодулей: они такие надежные и простые, потому что они «утомительны» (с технологической точки зрения) и прошли полевые испытания.

Единственное, что делает репозиторий Git подмодулем, — это то, что он помещается в другой, родительский репозиторий Git.

Помимо этого, подмодуль Git остается полностью функциональным репозиторием: вы можете выполнять все действия, которые вы уже знаете из своей «нормальной» работы с Git — от изменения файлов до фиксации, извлечения и отправки. В подмодуле возможно все.

Добавление подмодуля

Возьмем классический пример и предположим, что мы хотим добавить в наш проект стороннюю библиотеку. Прежде чем мы перейдем к какому-либо коду, имеет смысл создать отдельную папку, в которой такие вещи могут иметь дом:

$ mkdir lib
$ cd lib

Теперь мы готовы добавить сторонний код в наш проект — но упорядоченно, используя подмодули. Допустим, нам нужна небольшая библиотека JavaScript для преобразования часовых поясов:

$ git submodule add https://github.com/spencermountain/spacetime.git

Когда мы запускаем эту команду, Git начинает клонировать репозиторий в наш проект в виде подмодуля:

Cloning into 'carparts-website/lib/spacetime'...
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), pack-reused 6702
Receiving objects: 100% (7768/7768), 4.02 MiB | 7.78 MiB/s, done.
Resolving deltas: 100% (5159/5159), done.

И если мы посмотрим на нашу папку с рабочей копией, мы увидим, что файлы библиотеки действительно прибыли в наш проект.

Читайте также:  Вопросы по Python: быстрые ответы на распространенные вопросы о Python

И если мы посмотрим на нашу папку с рабочей копией

«Так в чем разница?» вы можете спросить. В конце концов, файлы сторонней библиотеки находятся здесь, как если бы мы скопировали их. Ключевое отличие в том, что они содержатся в собственном репозитории 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 status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  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впоследствии. Еще лучший способ — просто добавить —recurse-submodulesопцию сразу при звонке git clone.

Проверка исправлений

В «обычном» репозитории Git мы обычно проверяем ветки. Используя git checkout или более новую версию git switch , мы сообщаем Git, какой должна быть наша текущая активная ветка. Когда в этой ветке делаются новые коммиты, указатель HEAD автоматически перемещается на самую последнюю фиксацию. Это важно понимать, потому что подмодули Git работают по-другому!

В подмодуле мы всегда проверяем конкретную ревизию, а не ветку! Даже когда вы выполняете команду, например, git checkout mainв подмодуле, в фоновом режиме, отмечается последняя на данный момент фиксация в этой ветке, а не сама ветка.

Такое поведение, конечно, не ошибка. Подумайте об этом: когда вы включаете стороннюю библиотеку, вы хотите иметь полный контроль над тем, какой именно код используется в вашем основном проекте. Когда сопровождающий библиотеки выпускает новую версию, это хорошо… но вы не обязательно хотите, чтобы эта новая версия автоматически использовалась в вашем проекте. Просто потому, что вы не знаете, могут ли эти новые изменения сломать ваш проект!

Если вы хотите узнать, какую ревизию используют ваши подмодули, вы можете запросить эту информацию в своем основном проекте:

$ git submodule status
   ea703a7d557efd90ccae894db96368d750be93b6 lib/spacetime (6.16.3)

Это возвращает текущую проверенную ревизию нашего lib/spacetimeподмодуля. И это также позволяет нам узнать, что эта ревизия является тегом с именем «6.16.3». При работе с подмодулями в Git довольно часто используются теги.

Читайте также:  Несколько потоков на примере Pthread_join

Допустим, вы хотели, чтобы ваш подмодуль использовал более старую версию, которая была помечена как «6.14.0». Во-первых, мы должны изменить каталоги, чтобы наша команда Git выполнялась в контексте подмодуля, а не нашего основного проекта. Затем мы можем просто запустить git checkoutс именем тега:

$ 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.)

Однако внимательно посмотрите на вывод: маленький +символ перед этим хешем SHA-1 говорит нам, что подмодуль имеет другую ревизию, чем в настоящее время хранится в родительском репозитории. Поскольку мы только что изменили проверенную ревизию, это выглядит правильно.

Звонок git statusв наш основной проект теперь тоже сообщает нам об этом факте:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
  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
Fast-forward
   lib/spacetime | 2 +-
   1 file changed, 1 insertion(+), 1 deletion(-)

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

$ git submodule status
+7f78d50156ae1205aa50675ddede81a61a45fade lib/spacetime (6.14.)

Я уверен, что вы помните этот маленький +знак: это означает, что указатель подмодуля был перемещен! Чтобы обновить нашу локально проверенную ревизию до «официальной», которую выбрал наш товарищ по команде, мы можем запустить updateкоманду:

$ git submodule update lib/spacetime 
Submodule path 'lib/spacetime': checked out '5e3d70a88180879ae0222b6929551c41c3e5309e'

Хорошо! Наш подмодуль теперь проверен в ревизии, записанной в нашем основном репозитории проекта!

Работа с подмодулями в Git

Мы рассмотрели основные строительные блоки работы с подмодулями Git. Остальные рабочие процессы действительно вполне стандартные!

Например, проверка наличия новых изменений в подмодуле работает так же, как и в любом другом репозитории Git: вы запускаете git fetchкоманду внутри репозитория подмодуля, за которой, возможно, следует что-то вроде того, git pull origin mainесли вы действительно хотите использовать обновления.

Внесение изменений в подмодуль также может быть для вас вариантом использования, особенно если вы сами управляете кодом библиотеки (потому что это внутренняя библиотека, а не сторонняя). Вы можете работать с подмодулем, как с любым другим репозиторием Git: вы можете вносить изменения, фиксировать их, отправлять их и т.д.

Использование всей мощи Git

Под капотом Git скрывается огромная мощь. Но многие из его продвинутых инструментов, таких как подмодули Git, малоизвестны. Очень жаль, что так много разработчиков упускают из виду много мощных вещей!

Оцените статью
bestprogrammer.ru
Добавить комментарий