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

В Интернете полно статей о том, почему не стоит использовать подмодули Git. В отдельных ситуациях подмодули все же полезны, однако не лишены недостатков.
Есть ли альтернативы? Да! Как минимум два инструмента позволят вам отслеживать историю зависимостей ПО в проекте, по-прежнему работая с git:
Поддерево GitРепозиторий Google
В этой статье рассматривается команда git subtree и объясняется, почему это средство хотя и не идеально, но тем не менее лучше подмодулей Git.
Что такое команда git subtree и зачем ее использовать?
git subtree позволяет вложить один репозиторий в другой в виде подкаталога. Это один из способов управления зависимостями в проектах Git.

git subtree обладает следующими преимуществами.
Легко управлять несложными рабочими процессами.
Поддерживаются предыдущие версии Git (даже старше 1.5.2).
Код подпроекта доступен сразу после клонирования суперпроекта.
При использовании
git subtreeпользователям репозитория не нужно осваивать новые навыки. Управление зависимостями с помощьюgit subtreeникак не влияет на их работу.Использование
git subtreeне приводит к добавлению новых файлов метаданных, в отличие от применения подмодулей Git (файл .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 maingit subtree — отличная альтернатива
Поработав с подмодулями Git, вы заметите, что многих проблем можно избежать, перейдя на git subtree. Но, конечно, как и с другими функциями Git, придется потратить время и силы на освоение этой возможности, чтобы использовать ее максимально эффективно.
Ознакомьтесь с этой статьей о широких возможностях git subtree.