Перейти к содержимому

Git Subtree

Как уже упоминалось на нашей предыдущей странице, Git Submodule полезен для определённых случаев. Для отслеживания зависимостей программного обеспечения многие разработчики предпочитают Git Subtree.

Что такое Git Subtree

Git Subtree — это альтернатива Git Submodule. Он позволяет вкладывать один репозиторий в другой в виде подкаталога. Это один из способов отслеживать историю зависимостей программного обеспечения. Но subtree не следует путать с submodule. В отличие от submodule, subtree не требуют файлов .gitmodules или gitlink'ов в репозитории. Subtree — это просто подкаталог, в который можно коммитить, создавать ветки и выполнять слияния вместе с вашим проектом.

Зачем использовать Git Subtree

Плюсы

  • Поддерживается Git 1.7.10 и более поздними версиями.
  • Простое управление рабочим процессом.
  • Код подпроекта доступен после завершения суперпроекта.
  • Для использования git subtree не требуется новых знаний Git.
  • Не добавляет новых файлов метаданных (например, .gitmodules).
  • Позволяет изменять содержимое без отдельной копии репозитория зависимости.

Минусы

  • Требуется изучить новую стратегию слияния.
  • Сложный процесс внесения изменений обратно в upstream для подпроектов.
  • Код суперпроекта и подпроекта смешан в одном репозитории.

Как использовать Git Subtrees

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

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

bash
git subtree add --prefix .vim/bundle/example https://github.com/Example/vim-example.git master --squash

Это свернёт всю историю проекта vim-example в вашу папку .vim/bundle/example, записав SHA-1 ветки master на тот момент для будущего использования. В результате будут выведены следующие два коммита:

bash
commit 6d7054b3acea64e2e31f4d6fb2e3be12e5865e87
Merge: 87fa91e ef86deb
Author: Ann Smith<[email protected]m>
Date:   Tue Jun 10 13:37:03 2016 +0200
    Merge commit 'fe67ddf158faccff4082d78a25c45d8cd93e8ba8' as '.vim/bundle/example'
commit fe67ddf158faccff4082d78a25c45d8cd93e8ba8
Author: Ann Smith<[email protected]m>
Date:   Tue May 12 13:37:03 2015 +0200
    Squashed '.vim/bundle/example/' content from commit b999b09
    git-subtree-dir: .vim/bundle/example
    git-subtree-split: b999b09cd9d69f359fa5668e81b09dcfde455cca

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

bash
git subtree pull --prefix .vim/bundle/example https://github.com/Example/vim-example.git master --squash

Однако git subtree хранит идентификаторы коммитов подпроекта, а не ссылки в метаданных. Найдите символическое имя, связанное с коммитом:

bash
git ls-remote https://github.com/Example/vim-example.git | grep <commit-sha>

Замените <commit-sha> на фактический хеш коммита.

Перебазирование после Git Subtree

Чтобы выполнить перебазирование репозитория, содержащего subtree, используйте режим --interactive команды git rebase. Вы можете удалить или свернуть merge-коммиты subtree, а затем выполнить git rebase --continue. Учтите, что переписывание истории требует повторного выполнения git subtree add или git subtree pull после этого. Имейте в виду, что переписывание истории может вызвать конфликты слияния при перебазировании subtree, поскольку структура коммитов изменяется.

OPTIONS

-q, --quietПодавляет ненужные сообщения о результате в stderr.
-d, --debugВыводит дополнительные отладочные сообщения в stderr.
-P <prefix>, --prefix=<prefix>Определяет путь в репозитории к subtree, которым вы хотите управлять. Это обязательно для всех команд.
-m <message>, --message=<message>Указывает <message> как сообщение коммита для merge-коммита. Действительно только для add, merge и pull.

Использование Git Subtree без отслеживания удалённого репозитория

Добавьте git subtree в указанную папку prefix. Используйте флаг --squash, чтобы сохранить всю историю подпроекта в основном репозитории:

bash
git subtree add --prefix .vim/bundle/vim-double-upon https://hostname.org/example/vim-plugins.git master --squash

Команда выполняет fetch и сворачивает историю. Обычно вывод показывает ход fetch, а затем подтверждение добавления:

bash
git fetch https://hostname.org/example/vim-plugins.git  master
warning: no common commits
remote: Counting objects: 325, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 325 (delta 101), reused 313 (delta 89)
Receiving objects: 100% (325/325), 61.47 KiB, done.
Resolving deltas: 100% (110/110), done.
From https://hostname.org/vim-plugins.git
* branch master -> FETCH_HEAD
Added dir '.vim/bundle/vim-double-upon'

Это создаёт merge-коммит, сворачивая всю историю подпроекта в один:

bash
3bca0ad [4 minutes ago] (HEAD, stree) Merge commit 'fa2f5dc4f1b94356bca8a440c786a94f75dc0a45' as '.vim/bundle/vim-double-upon' [John Brown]
fa2f5dc [4 minutes ago] Squashed '.vim/bundle/vim-double-upon/' content from commit 13189ec [John Brown]

Чтобы обновить код плагина из upstream-репозитория, выполните git subtree pull:

bash
git subtree pull --prefix .vim/bundle/vim-double-upon https://hostname.org/example/vim-plugins.git master --squash

Чтобы внести изменения обратно в upstream-репозиторий, извлеките историю subtree с помощью git subtree split:

bash
git subtree split --prefix .vim/bundle/vim-double-upon -b split-branch
git push split-branch

Это создаёт новую ветку, содержащую только историю subtree, которую затем можно отправить в upstream-репозиторий.

Чтобы сделать команды короче, добавьте подпроект как удалённый репозиторий.

Добавление подпроекта как удалённого репозитория

Добавление как remote сокращает процесс:

bash
git remote add -f vim-double-upon https://hostname.org/example/vim-plugins.git

Добавьте subtree:

bash
git subtree add --prefix .vim/bundle/vim-double-upon vim-double-upon master --squash

Обновите подпроект так:

bash
git fetch vim-double-upon master
git subtree pull --prefix .vim/bundle/vim-double-upon vim-double-upon master --squash

Git Subtree — это альтернатива submodule. В то время как submodule помещают другой проект в каталог и синхронизируют удалённый репозиторий, Git Subtree хранит подпроект как обычный каталог. Внесение изменений обратно в upstream-репозиторий требует использования git subtree split для извлечения истории подпроекта, поскольку прямая двусторонняя синхронизация нативно не поддерживается.

Practice

What are the features and usage of Git subtree?

Считаете ли это полезным?

Предпросмотр dual-run — сравните с маршрутами Symfony на продакшене.