Рисунок: ветвление в git

git subtree: альтернатива git submodule

Николя Паолуччи
Николя Паолуччи
Назад к списку

В Интернете полно статей о том, почему не стоит использовать подмодули Git. В отдельных ситуациях подмодули все же полезны, однако не лишены недостатков.

Есть ли альтернативы? Да! Как минимум два инструмента позволят вам отслеживать историю зависимостей ПО в проекте, по-прежнему работая с git:

  • Поддерево Git
  • Репозиторий Google

В этой статье рассматривается команда git subtree и объясняется, почему это средство хотя и не идеально, но тем не менее лучше git submodule.

Что такое команда git subtree и зачем ее использовать?

git subtree позволяет вложить один репозиторий в другой в виде подкаталога. Это один из способов управления зависимостями в проектах Git.

Схема: взаимодействие между двумя репозиториями до и после использования команды git subtree.

git subtree обладает следующими преимуществами.

  • Легко управлять несложными рабочими процессами.
  • Поддерживаются предыдущие версии Git (даже старше 1.5.2).
  • Код подпроекта доступен сразу после клонирования суперпроекта.
  • При использовании git subtree пользователям репозитория не нужно осваивать новые навыки. Управление зависимостями с помощью git subtree никак не влияет на их работу.
  • Использование git subtree не приводит к добавлению новых файлов метаданных, в отличие от применения git submodule (файл .gitmodules).
  • Содержимое модуля может быть изменено без копирования зависимости в отдельный репозиторий.

Недостатки (которые мы в большинстве случаев считаем несущественными) перечислены ниже.

  • Придется освоить новую стратегию слияния (команду git subtree).
  • Возвращать код подпроектов в вышестоящий проект немного сложнее.
  • Нужно самостоятельно следить за тем, чтобы код суперпроекта и подпроектов в коммитах не смешивался.

Использование git subtree

Команда git subtree доступна в стандартной версии Git с мая 2012 года (т. е. начиная с версии 1.7.11). В версии, которую устанавливает утилита Homebrew в OS X, команда subtree уже настроена, но на некоторых платформах может понадобиться выполнить инструкции по установке.

Ниже приведен классический пример того, как можно отслеживать плагин vim с помощью git subtree.

Быстрый, но неоптимальный способ без удаленного отслеживания

Если вас интересуют однострочные скрипты, которые можно просто скопировать и вставить, достаточно будет прочитать этот раздел. Сначала добавьте git subtree в папку с указанным префиксом:

 git subtree add --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

(Обычно в главном репозитории не хранят полную историю подпроекта, но если вы захотите ее оставить, то просто не используйте флаг --squash.)

Вывод приведенной выше команды:

git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git main
warning: no common commits
remote: Counting objects: 338, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 338 (delta 101), reused 323 (delta 89)
Receiving objects: 100% (338/338), 71.46 KiB, done.
Resolving deltas: 100% (101/101), done.
From https://bitbucket.org/vim-plugins-mirror/vim-surround.git
* branch main -} FETCH_HEAD
Added dir '.vim/bundle/tpope-vim-surround'

Как видите, в результате создан коммит слияния путем склеивания коммитов по всей истории репозитория vim-surround:

1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as '.vim/bundle/tpope-vim-surround' [Nicola Paolucci]
ca1f4da [3 minutes ago] Squashed '.vim/bundle/tpope-vim-surround/' content from commit 02199ea [Nicola Paolucci]

Если позднее вы захотите обновить код плагина содержимым из вышестоящего репозитория, можете просто осуществить pull в git subtree:

git subtree pull --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

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

Добавление подпроекта в качестве удаленного подключения

Добавив поддерево subtree в виде удаленного подключения, мы сможем ссылаться на него в краткой форме:

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Теперь, как и раньше, добавим subtree, только на этот раз ссылка на удаленный репозиторий будет краткой:

git subtree add --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

Команда для последующего обновления подпроекта будет выглядеть так:

git fetch tpope-vim-surround main
git subtree pull --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

Отправка кода в вышестоящую ветку

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

git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

Теперь можно выполнить команду subtree push:

git subtree push --prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround main
git push using: durdn-vim-surround main
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/durdn/vim-surround.git
02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} main

Все готово, и можно открывать запрос pull для специалиста, сопровождающего пакет.

Можно ли сделать то же самое без команды git subtree?

Конечно! git subtree и слияние поддерева — разные стратегии. Слияние можно использовать, даже если по какой-то причине команда git subtree недоступна. Порядок действий следующий.

Добавим зависимость как простое удаленное подключение Git:

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

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

git merge -s ours --no-commit tpope-vim-surround/main

Вывод команды будет следующим:

Automatic merge went well; stopped before committing as requested

Считаем содержимое последнего дерева из репозитория плагина в рабочий каталог, готовый к коммиту:

git read-tree --prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/main

Теперь можно выполнить коммит (это будет коммит слияния, при котором сохранится история считанного дерева):

git ci -m"[subtree] adding tpope-vim-surround"
[stree 779b094] [subtree] adding tpope-vim-surround

Чтобы обновить проект, можно осуществить pull, используя стратегию слияния git subtree:

git pull -s subtree tpope-vim-surround main

git subtree — отличная альтернатива

Поработав с git submodule, вы заметите, что многих проблем можно избежать, перейдя на git subtree. Но конечно, как и с другими функциями Git, придется потратить время и силы на освоение этой возможности, чтобы использовать ее максимально эффективно.

Подпишитесь на меня в Twitter (@durdn), чтобы пополнять свои знания о Git. А если вы ищете хороший инструмент для управления репозиториями Git, советую попробовать Atlassian Bitbucket.

Обновление: опубликовав эту статью, я написал еще одну — о мощных возможностях git subtree.

Готовы изучить Git?

Ознакомьтесь с этим интерактивным обучающим руководством.

Начните прямо сейчас