Git subtree в деталях
Принимая решение об использовании того или иного средства в собственных проектах, инженеру приходится не только изучать сопроводительную документацию, но и проводить серию экспериментов для того, чтобы избежать потенциальных проблем в будущем. Если же речь идет о CM-политике, рассчитанной на длительную перспективу, цена ошибки выбора становится достаточно высока.
Целью настоящей работы является практическое изучение средства управления поддеревьями Git.
Начиная с ревизии 1.7.11 upstream-репозиторий Git, в каталоге contrib/subtree, содержит средство автоматизации работы с поддеревьями.
Сервис git-subtree (1) фактически является полезной надстройкой, использующей функции git-read-tree (1) и git-write-tree (1). Поэтому ссылки в командах git-subtree (1) add/pull/push:
git subtree add --prefix=
могут представлять собой, как имена веток, так и имена тегов удаленного репозитория.
Кроме того, если заранее добавить удаленный репозиторий в конфигурационный файл локального репозитория .git/config, с помошью команды:
bash-4.4$ git remote add build-system ../../remote/build-system.git
где build-system является именем удаленного репозитория …/…/remote/build-system.git, то в дальнейшем, при использовании команд git-subtree (1) add/pull/push, мы сможем ссылаться на upstream-репозиторий remote/build-system.git по имени.
На данный момент git-subtree (1) практически не развивается, а лишь поддерживается в актуальном состоянии для текущей степени развития проекта Git.
Однако git-subtree (1) является наиболее популярным и мощным средством работы с поддеревьями.
Тестовое окружение
В предыдущей статье, посвященной git-subrepo (1), мы использовали простую структуру каталогов с тестовыми репозиториями для демонстрации работы функций на практике. Воспроизведем и теперь такое окружение:
bash-4.4$ vim _init.sh
#!/bin/sh
CWD=`pwd`
mkdir remote owner user
cd remote
git init --bare build-system.git
git init --bare platform.git
cd ../owner
git clone $CWD/remote/build-system.git
git clone $CWD/remote/platform.git
cd build-system
echo -e "\n[master] build-system 1.0.0\n" >README
git add README
git commit -m "init build-system master 1.0.0"
git push
cd ../platform
echo -e "\n[master] platform 1.0.0\n" >README
git add README
git commit -m "init platform master 1.0.0"
git push
cd ../../user
git clone $CWD/remote/build-system.git
git clone $CWD/remote/platform.git
cd $CWD
:wq
bash-4.4$ chmod a+x ./_init.sh
bash-4.4$ ./_init.sh
bash-4.4$
Здесь,
owner | — | рабочий каталог автора проектов; |
remote | — | каталог представляющий сервер автора проектов, на котором располагаются upstream-репозитории основного проекта platform.git и подпроекта build-system.git; |
user | — | рабочий каталог пользователя или участника команды разработчиков. |
В качестве целей изучения возможностей git-subtree (1) мы будем рассматривать всё те же задачи, о которых мы говорили в статье Git Subrepo, но с учетом различий между этими двумя средствами.
Подключение поддерева
Запомним текущее состояние репозитория remote/platform.git:
bash-4.4$
bash-4.4$ cd owner/platform/
bash-4.4$ git log
commit 7fad4becbd13258216fb95cbe9d987dd33f0be6d (HEAD -> master, origin/master)
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init platform master 1.0.0
bash-4.4$
и подключим master-ветку upstream-репозитория remote/build-system.git в каталог build-system:
bash-4.4$
bash-4.4$ git subtree add --prefix=build-system ../../remote/build-system.git/ master
git fetch ../../remote/build-system.git/ master
warning: no common commits
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From ../../remote/build-system
* branch master -> FETCH_HEAD
Added dir 'build-system'
bash-4.4$
Рассмотрим новое состояние локальной копии репозитория remote/platform.git:
bash-4.4$
bash-4.4$ git log --graph
* commit 47905bcb80be6f7cb3030513986fad4df548f812 (HEAD -> master)
|\ Merge: 7fad4be 783c6d5
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:20:20 2018 +0300
| |
| | Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
| |
| | git-subtree-dir: build-system
| | git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
| | git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
| | git-subtree-repo: ../../remote/build-system.git/
| | git-subtree-ref: master
| |
| * commit 783c6d5af1100e9665f930c818c861ff011bed19
| Author: user <___@_______>
| Date: Thu Nov 1 20:16:33 2018 +0300
|
| init build-system master 1.0.0
|
* commit 7fad4becbd13258216fb95cbe9d987dd33f0be6d (origin/master)
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init platform master 1.0.0
bash-4.4$
Здесь следует обратить внимание на сообщение, которое оставила команда git-subtree (1) add. Фактически, с помощью данной команды, в репозиторий platform мы поставили разность:
bash-4.4$
bash-4.4$ git diff 7fad4becbd13258216fb95cbe9d987dd33f0be6d 47905bcb80be6f7cb3030513986fad4df548f812
diff --git a/build-system/README b/build-system/README
new file mode 100644
index 0000000..73a41c7
--- /dev/null
+++ b/build-system/README
@@ -0,0 +1,3 @@
+
+[master] build-system 1.0.0
+
bash-4.4$
Далее, когда история изменений уйдет несколько дальше, мы более подробно изучим детали подключения поддеревьев, а сейчас поставим наши изменения в upstream-репозиторий remote/platform.git:
bash-4.4$
bash-4.4$ git push
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 582 bytes | 582.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git
7fad4be..47905bc master -> master
bash-4.4$
и еще раз посмотрим на сообщение команды git-subtree (1) add:
Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
git-subtree-dir: build-system
git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
git-subtree-repo: ../../remote/build-system.git/
git-subtree-ref: master
Эти сообщения полезны для того, чтобы, в случае необходимости, можно было найти историю подключения и текущее состояние поддерева. Оригинальная команда git-subtree (1) не добавляет последние две строки сообщения. Мы немного доработали эту утилиту для того, чтобы было легче искать информацию о том, когда и от какой именно ветки удаленного репозитория было создано то или иное поддерево нашего проекта.
Полный diff-файл наших изменений вы можете получить следующим образом:
bash-4.4$
bash-4.4$ git clone https://github.com/radix-platform/git.git
bash-4.4$ cd git
bash-4.4$ git checkout git-subtree-2.19.1
bash-4.4$ git diff v2.19.1 > ../git-subtree-2.19.1.patch
bash-4.4$
Для придания нашим примерам более реалистичного характера, сделаем изменения в upstream-репозитории remote/build-system.git:
bash-4.4$
bash-4.4$ cd owner/build-system/
bash-4.4$ vim README
bash-4.4$ cat README
[master] build-system 1.0.1
bash-4.4$
Сохраним эти изменения:
bash-4.4$
bash-4.4$ git add README
bash-4.4$ git commit -m "update build-system version to 1.0.1"
[master e5c5446] update build-system version to 1.0.1
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
и передадим их в upstream-репозиторий remote/build-system.git:
bash-4.4$
bash-4.4$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 274 bytes | 274.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../remote/build-system.git
783c6d5..e5c5446 master -> master
bash-4.4$
Итак, ревизия репозитория build-system изменилась с 783c6d5 на e5c5446:
bash-4.4$
bash-4.4$ git log
commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65 (HEAD -> master, origin/master)
Author: user <___@_______>
Date: Thu Nov 1 20:26:52 2018 +0300
update build-system version to 1.0.1
commit 783c6d5af1100e9665f930c818c861ff011bed19
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init build-system master 1.0.0
bash-4.4$
Запомним это состояние и перейдем к работе с репозиторием-контейнером remote/platform.git.
Получение изменений из upstream-репозитория поддерева
Допустим, что мы еще не знаем об изменениях в upstream-репозитории поддерева и работаем над усовершенствованием кода platform:
bash-4.4$
bash-4.4$ cd owner/platform/
bash-4.4$ vim README
bash-4.4$ cat README
[master] platform 1.0.1
bash-4.4$
bash-4.4$ git add README
bash-4.4$ git commit -m "update platform version to 1.0.1"
[master 442c9e9] update platform version to 1.0.1
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
bash-4.4$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 306 bytes | 306.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git
47905bc..442c9e9 master -> master
bash-4.4$
В результате нашей работы мы получили следующее состояние репозитория remote/platform.git:
bash-4.4$
bash-4.4$ git log --graph
* commit 442c9e94c9890032fb2f3123661345d465e2849f (HEAD -> master, origin/master)
| Author: user <___@_______>
| Date: Thu Nov 1 20:41:40 2018 +0300
|
| update platform version to 1.0.1
|
* commit 47905bcb80be6f7cb3030513986fad4df548f812
|\ Merge: 7fad4be 783c6d5
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:20:20 2018 +0300
| |
| | Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
| |
| | git-subtree-dir: build-system
| | git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
| | git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
| | git-subtree-repo: ../../remote/build-system.git/
| | git-subtree-ref: master
| |
| * commit 783c6d5af1100e9665f930c818c861ff011bed19
| Author: user <___@_______>
| Date: Thu Nov 1 20:16:33 2018 +0300
|
| init build-system master 1.0.0
|
* commit 7fad4becbd13258216fb95cbe9d987dd33f0be6d
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init platform master 1.0.0
bash-4.4$
Теперь, было бы не плохо узнать, что происходило в upstream-репозитории поддерева build-system в то время, пока мы занимались совершенствованием кода в основном репозитории нашего проекта. Пролистаем сначала поддеревья с помощью команды git subtree --list:
bash-4.4$
bash-4.4$ git subtree --list
build-system ../../remote/build-system.git/ branch master HEAD
bash-4.4$
Данная команда, в простом формате, предстваляет каталог-поддерева, URL upstream-репозитория поддерева, тип ссылки (ветка или тэг), название ссылки, а также ревизию указанной ветки или тэга репозитория, код которого мы поместили в наше поддерево. Однако мы помним, что разработка подпроекта build-system ушла вперед и указание на голову master-ветки уже не действительно. Скорее это сообщение о том, что бы мы хотели иметь в нашем репозитории, а не действительное положение дел.
Для того, чтобы узнать, насколько продвинулся код в upstream-репозитории поддерева, нам необходимо так же пролистать поддеревья, но с использованием опции -d:
bash-4.4$
bash-4.4$ git subtree -d --list
Looking for externals...
Commit: 47905bcb80be6f7cb3030513986fad4df548f812
build-system ../../remote/build-system.git/ branch master HEAD
The 'build-system' subtree seems not updated:
original revision: 783c6d5af1100e9665f930c818c861ff011bed19
remote revision: e5c5446967599065dc02a269d8fcfc2c1d3c4f65
You can update 'build-system' subtree by following command:
git subtree pull --prefix=build-system ../../remote/build-system.git/ master
bash-4.4$
Теперь, полученный вывод говорит о том, что пока мы работали над кодом основного репозитория, master-ветка upstream-репозитория поддерева build-system ушла вперед. Кроме того, команда git subtree -d --list вывела подсказку о том, что мы можем получить изменения upstream-репозитория поддерева следующим образом:
bash-4.4$
bash-4.4$ git subtree pull --prefix=build-system ../../remote/build-system.git/ master
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From ../../remote/build-system
* branch master -> FETCH_HEAD
hint: Waiting for your editor to close the file...
данная команда, поскольку мы не задали -m «commit message», открывает редактор с текстом сообщения:
Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
Заменим этот текст на более информативный:
Pull changes from master of upstream build-system.git repository:
Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
После сохранения данного сообщения и закрытия редактора, мы получим следующий вывод:
Merge made by the 'recursive' strategy.
build-system/README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
То есть, мы получили изменения upstream-репозитория remote/build-system.git и сохранили их в поддереве build-system локальной копии основного репозитория remote/platform.git.
Проверим статус локального репозитория:
bash-4.4$
bash-4.4$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
bash-4.4$
Для того, чтобы остальные пользователи проекта смогли получить эти изменения, мы должны поставить их в upstream-репозиторий remote/platform.git:
bash-4.4$
bash-4.4$ git push
Enumerating objects: 9, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 583 bytes | 583.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git
442c9e9..ea52eab master -> master
bash-4.4$
Посмотрим, что теперь содержит локальная копия репозитория platform, а также upstream-репозиторий remote/platform.git:
bash-4.4$
bash-4.4$ git log --graph
* commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (HEAD -> master, origin/master)
|\ Merge: 442c9e9 e5c5446
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
| |
* | commit 47905bcb80be6f7cb3030513986fad4df548f812
|\ \ Merge: 7fad4be 783c6d5
| |/ Author: user <___@_______>
| | Date: Thu Nov 1 20:20:20 2018 +0300
| |
| | Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
| |
| | git-subtree-dir: build-system
| | git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
| | git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
| | git-subtree-repo: ../../remote/build-system.git/
| | git-subtree-ref: master
| |
| * commit 783c6d5af1100e9665f930c818c861ff011bed19
| Author: user <___@_______>
| Date: Thu Nov 1 20:16:33 2018 +0300
|
| init build-system master 1.0.0
|
* commit 7fad4becbd13258216fb95cbe9d987dd33f0be6d
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init platform master 1.0.0
bash-4.4$
Мы видим, что начальное состояние репозитория remote/build-system.git, в момент подключения его в качестве поддерева платформы, было равно 783c6d5af1100e9665f930c818c861ff011bed19. Однако пока мы работали над кодом в репозитории platform, состояние репозитория изменилось и стало равным e5c5446967599065dc02a269d8fcfc2c1d3c4f65.
Начальное состояние build-system (783c6d5af1100e9665f930c818c861ff011bed19) уже было в истории репозитория platform, когда он находился в точке 442c9e94c9890032fb2f3123661345d465e2849f. Следовательно, нам надо взять состояние platform в точке 442c9e94c9890032fb2f3123661345d465e2849f и состояние build-system в точке e5c5446967599065dc02a269d8fcfc2c1d3c4f65, вычислить разность между ними, и наложить полученную разность на master-ветку репозитория platform.
Это стандартная операция слияния веток, суть которой можно выразить формулой
p[n] = p[n-1] + diff(p[n-1], b[n])
где,
p[n] = ea52eabd5910159efabd80adcf522f22bf6a2af2,
p[n-1] = 442c9e94c9890032fb2f3123661345d465e2849f,
b[n] = e5c5446967599065dc02a269d8fcfc2c1d3c4f65.
Здесь p выступает в роли master-ветки, а b, — играет роль ветки, отделившейся ранее от master с целью создания новой функциональности.
Рассмотрим еще раз сообщение, которое для нас приготовила утилита git-subtree (1) во время выполнения команды git-subtree-pull:
git subtree pull --prefix=build-system ../../remote/build-system.git/ master
Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
В этом сообщении есть существенная для нас информация, а именно состояние
b[n] = e5c5446967599065dc02a269d8fcfc2c1d3c4f65
в котором находилась master-ветка репозитория remote/build-system.git перед «заливкой» на master-ветку репозитория platform.
Конечно, не очень удобно при выполнении команды git-subtree-pull вручную редактировать commit message, однако, другого способа передать нам состояние
b[n] = e5c5446967599065dc02a269d8fcfc2c1d3c4f65
у утилиты git-subtree (1), в данном случае, просто не существует.
Если бы мы воспользовались управлением -m
git subtree pill -m "Our own message" ...
то мы бы потеряли значение состояния b[n] и, наш коментарий оказался не информативным, и, стало быть, совершенно бесполезным.
Следует заметить, что после того, как состояние master-ветки репозитория build-system сдвинулось с изначальной точки монтирования 47905bcb80be6f7cb3030513986fad4df548f812, мы всегда будем получать уведомление об изменениях в remote/build-system.git во время просмотра списка подключенных поддеревьев. Иными словами, использование опции -d в команде:
git subtree -d --list
будет всегда приводить к выводу сообщения, подобного следующему:
bash-4.4$
bash-4.4$ git subtree -d --list
Looking for externals...
Commit: 47905bcb80be6f7cb3030513986fad4df548f812
build-system ../../remote/build-system.git/ branch master HEAD
The 'build-system' subtree seems not updated:
original revision: 783c6d5af1100e9665f930c818c861ff011bed19
remote revision: e5c5446967599065dc02a269d8fcfc2c1d3c4f65
You can update 'build-system' subtree by following command:
git subtree pull --prefix=build-system ../../remote/build-system.git/ master
bash-4.4$
Происходить это будет по тому, что вся информация о подключенном поддереве находится в сообщении, сопроводившим коммит 47905bcb80be6f7cb3030513986fad4df548f812:
bash-4.4$
bash-4.4$ git show 47905bcb80be6f7cb3030513986fad4df548f812
commit 47905bcb80be6f7cb3030513986fad4df548f812
Merge: 7fad4be 783c6d5
Author: user <___@_______>
Date: Thu Nov 1 20:20:20 2018 +0300
Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
git-subtree-dir: build-system
git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
git-subtree-repo: ../../remote/build-system.git/
git-subtree-ref: master
diff --cc build-system/README
index 0000000,0000000..73a41c7
new file mode 100644
--- /dev/null
+++ b/build-system/README
@@@ -1,0 -1,0 +1,3 @@@
++
++[master] build-system 1.0.0
++
bash-4.4$
и нет никакого другого хранилища данной информации.
Единственно, теперь после каждого обновления master-ветки репозитория remote/build-system.git, на стороне platform, при выполнении команды
git subtree -d --list
будет меняться лишь значение строки
remote revision: e5c5446967599065dc02a269d8fcfc2c1d3c4f65
и нам самим будет необходимо принимать решения о том, нужны ли нам новые изменения поддерева build-system или, на данный момент, не нужны.
Однако нам не составит труда выполнять команду pull так часто, как нам это будет необходимо, поскольку если репозиторий remote/build-system.git не имел актуальных изменений, команда:
git subtree pull --prefix=build-system ../../remote/build-system.git/ master
выдаст адекватное сообщение:
bash-4.4$
bash-4.4$ git subtree pull --prefix=build-system ../../remote/build-system.git/ master
From ../../remote/build-system
* branch master -> FETCH_HEAD
Already up to date.
bash-4.4$
Получение кода пользователями
Теперь все пользователи upstream-репозитория remote/platform.git могут получить полностью настроенное дерево исходного кода с помощью одной команды git-pull (1):
bash-4.4$
bash-4.4$ cd user/platform/
bash-4.4$
bash-4.4$ git pull
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 13 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (13/13), done.
From ../../remote/platform
7fad4be..ea52eab master -> origin/master
Updating 7fad4be..ea52eab
Fast-forward
README | 2 +-
build-system/README | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
create mode 100644 build-system/README
bash-4.4$
Кроме того, пользователи смогут отслеживать историю развития не только основного репозитория проекта, но и всех его поддеревьев:
bash-4.4$
bash-4.4$ git log -3 --graph
* commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (HEAD -> master, origin/master, origin/HEAD)
|\ Merge: 442c9e9 e5c5446
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
bash-4.4$
Поставка изменений поддерева в upstream-репозиторий
Запомним состояние файлов, а также самого репозитория platform:
bash-4.4$
bash-4.4$ git log -3 --graph
* commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (HEAD -> master, origin/master, origin/HEAD)
|\ Merge: 442c9e9 e5c5446
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
bash-4.4$
Внесем изменения в поддерево build-system. Для этого отредактируем файл platform/build-system/README:
bash-4.4$
bash-4.4$ cd owner/platform/
bash-4.4$
bash-4.4$ vim build-system/README
bash-4.4$ cat build-system/README
[master] build-system 1.0.2
bash-4.4$
bash-4.4$ git add build-system/README
bash-4.4$ git commit -m "build-system is updated to version 1.0.2 from platform side"
[master abaa2c5] build-system is updated to version 1.0.2 from platform side
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
Но не будем заносить эти изменения в origin/master репозитория platform. То есть не будем выполнять команду git-push (1), а выполним git-subtree-push напямую в origin репозиторий remote/build-system.git:
bash-4.4$
bash-4.4$ git subtree push --prefix=build-system ../../remote/build-system.git/ master
git push using: ../../remote/build-system.git/ master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 290 bytes | 290.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../remote/build-system.git/
e5c5446..0673142 0673142942ccf53514a276e855a98514952bb713 -> master
bash-4.4$
Пролистаем поддеревья и убедимся в том, что HEAD master-ветки оригинального репозитория remote/build-system.git ушел вперед:
bash-4.4$
bash-4.4$ git subtree -d --list
Looking for externals...
Commit: 47905bcb80be6f7cb3030513986fad4df548f812
build-system ../../remote/build-system.git/ branch master HEAD
The 'build-system' subtree seems not updated:
original revision: 783c6d5af1100e9665f930c818c861ff011bed19
remote revision: 0673142942ccf53514a276e855a98514952bb713
You can update 'build-system' subtree by following command:
git subtree pull --prefix=build-system ../../remote/build-system.git/ master
bash-4.4$
Кроме того, посмотрим на состояние локальной копии репозитория platform, помня о том, что последний коммит еще не поставлен нами в upstream-репозиторий:
bash-4.4$
bash-4.4$ git log -4 --graph
* commit abaa2c5edd49dd0cf395c99877b4711d0170af37 (HEAD -> master)
| Author: user <___@_______>
| Date: Thu Nov 1 21:48:40 2018 +0300
|
| build-system is updated to version 1.0.2 from platform side
|
* commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (origin/master)
|\ Merge: 442c9e9 e5c5446
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
bash-4.4$
Правильный путь поставки изменений в upstream-репозиторий основного проекта, состоит в том, чтобы все наши изменения поддерева не попадали непосредственно в основной репозиторий, а приходили из upstream-репозитория поддерева так, как будто мы их получали с помощью команды git-subtree-pull. Далее мы поясним смысл наших действий, а сейчас, для того, чтобы избежать последующих неприятностей, сделаем revert последнего коммита (abaa2c5edd49dd0cf395c99877b4711d0170af37) в локальную копию репозитория platform:
bash-4.4$
bash-4.4$ git reset --hard HEAD^
HEAD is now at ea52eab Pull changes from master of upstream build-system.git repository.
bash-4.4$
чтобы затем «снять» этот же самый коммит обратно, но уже из оригинального upstream-репозитория remote/build-system.git. Но прежде убедимся в том, что мы удалили последний коммит abaa2c5edd49dd0cf395c99877b4711d0170af37:
bash-4.4$
bash-4.4$ git log -3 --graph
* commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (HEAD -> master, origin/master)
|\ Merge: 442c9e9 e5c5446
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
bash-4.4$
Да, мы действительно вернулись к нашему предыдущему состоянию ea52eabd5910159efabd80adcf522f22bf6a2af2 и теперь мы можем получить обратно те изменения, которые мы недавно отправили в оригинальный репозиторий remote/build-system.git. Для этого нам, как обычно, необходимо выполнить команду git-subtree-pull:
bash-4.4$
bash-4.4$ git subtree pull --prefix=build-system ../../remote/build-system.git/ master
From ../../remote/build-system
* branch master -> FETCH_HEAD
hint: Waiting for your editor to close the file...
Далее, нам будет предложено отредактировать сообщение о коммите:
Merge commit '0673142942ccf53514a276e855a98514952bb713'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
которое мы заменим на:
Pull changes from master of origin remote/build-system repository.
Merge commit '0673142942ccf53514a276e855a98514952bb713'
и наконец, в качестве продолжения предыдущего вывода, получим:
Merge made by the 'recursive' strategy.
build-system/README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
Теперь мы имеем нормальную историю и, что более важно, мы избежали нетривиального мержа изменений из remote/build-system.git на какой-либо из будущих стадий развития проекта. Произошло это по тому, что мы не оставляли изменений поддерева build-system в локальном репозитории platform, а получили их путем передачи через оригинальный репозиторий remote/build-system.git:
bash-4.4$
bash-4.4$ git log --graph
* commit 04a13bac91d1c445994ffc19db8b479d5e644e17 (HEAD -> master)
|\ Merge: ea52eab 0673142
| | Author: user <___@_______>
| | Date: Thu Nov 1 21:59:45 2018 +0300
| |
| | Pull changes from master of origin remote/build-system repository.
| |
| | Merge commit '0673142942ccf53514a276e855a98514952bb713'
| |
| * commit 0673142942ccf53514a276e855a98514952bb713
| | Author: user <___@_______>
| | Date: Thu Nov 1 21:48:40 2018 +0300
| |
| | build-system is updated to version 1.0.2 from platform side
| |
* | commit ea52eabd5910159efabd80adcf522f22bf6a2af2 (origin/master)
|\ \ Merge: 442c9e9 e5c5446
| |/ Author: user <___@_______>
| | Date: Thu Nov 1 20:48:05 2018 +0300
| |
| | Pull changes from master of upstream build-system.git repository.
| |
| | Merge commit 'e5c5446967599065dc02a269d8fcfc2c1d3c4f65'
| |
| * commit e5c5446967599065dc02a269d8fcfc2c1d3c4f65
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:26:52 2018 +0300
| |
| | update build-system version to 1.0.1
| |
* | commit 442c9e94c9890032fb2f3123661345d465e2849f
| | Author: user <___@_______>
| | Date: Thu Nov 1 20:41:40 2018 +0300
| |
| | update platform version to 1.0.1
| |
* | commit 47905bcb80be6f7cb3030513986fad4df548f812
|\ \ Merge: 7fad4be 783c6d5
| |/ Author: user <___@_______>
| | Date: Thu Nov 1 20:20:20 2018 +0300
| |
| | Add 'build-system/' from commit '783c6d5af1100e9665f930c818c861ff011bed19'
| |
| | git-subtree-dir: build-system
| | git-subtree-mainline: 7fad4becbd13258216fb95cbe9d987dd33f0be6d
| | git-subtree-split: 783c6d5af1100e9665f930c818c861ff011bed19
| | git-subtree-repo: ../../remote/build-system.git/
| | git-subtree-ref: master
| |
| * commit 783c6d5af1100e9665f930c818c861ff011bed19
| Author: user <___@_______>
| Date: Thu Nov 1 20:16:33 2018 +0300
|
| init build-system master 1.0.0
|
* commit 7fad4becbd13258216fb95cbe9d987dd33f0be6d
Author: user <___@_______>
Date: Thu Nov 1 20:16:33 2018 +0300
init platform master 1.0.0
bash-4.4$
На данном этапе, ни в коем случае нельзя забывать о том, что мы должны поставить произошедшие изменения из локальной копии в upstream-репозиторий remote/platform.git.
bash-4.4$
bash-4.4$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
bash-4.4$
bash-4.4$ git push
Enumerating objects: 9, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 600 bytes | 600.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git
ea52eab..04a13ba master -> master
bash-4.4$
В противном случае, когда пользователи самостоятельно выполнят команду git-subtree-pull, у них будут большие неприятности. Дело в том, что если автор не выполнит команду git-push (1), то другие участники проекта, при наличии соответствующих прав, смогут сделать это раньше автора.
Действительно, поскольку upstream-репозиторий remote/platform.git так и не перешел в состояние 04a13bac91d1c445994ffc19db8b479d5e644e17, но репозиторий remote/build-system.git уже обновился, следовательно, после выполнения команды git-subtree-pull, любой участник проекта сможет поставить изменения в upstream-репозиторий remote/platform.git, а это приведет к рассогласованности upstream-репозитория remote/platform.git и его локальной копии на стороне автора (owner/platform).
Использование утилиты git-format-patch
Если, в своей работе, мы используем git-subtre, основными нашими помощниками будут
команды:
git log -- . ":(exclude)build-system"
git log -- build-system
которые позволяют видеть изменения, либо основного репозитория, либо поддерева соответственно.
А вот утилитой git-format-patch следует пользоваться осторожно.
Так, например, если мы попытаемся сформировать серию patch-файлов от состояния ea52eabd5910159efabd80adcf522f22bf6a2af2, с помощью утилиты git-format-patch:
bash-4.4$
bash-4.4$ git format-patch ea52eabd5910159efabd80adcf522f22bf6a2af2 --stdout
From 0673142942ccf53514a276e855a98514952bb713 Mon Sep 17 00:00:00 2001
From: user <___@_______>
Date: Thu, 1 Nov 2018 21:48:40 +0300
Subject: [PATCH] build-system is updated to version 1.0.2 from platform side
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index 629b3f4..4fbbbaf 100644
--- a/README
+++ b/README
@@ -1,3 +1,3 @@
-[master] build-system 1.0.1
+[master] build-system 1.0.2
--
2.19.1
bash-4.4$
мы увидим, что разность состояний файла build-system/README рассматривается относительно каталога build-system/, а не от корня дерева исходного кода:
--- a/README
+++ b/README
В это же время, рассчитав разность между состояниями того же файла с помощью команды
git-diff:
bash-4.4$
bash-4.4$ git diff ea52eabd5910159efabd80adcf522f22bf6a2af2 04a13bac91d1c445994ffc19db8b479d5e644e17
diff --git a/build-system/README b/build-system/README
index 629b3f4..4fbbbaf 100644
--- a/build-system/README
+++ b/build-system/README
@@ -1,3 +1,3 @@
-[master] build-system 1.0.1
+[master] build-system 1.0.2
bash-4.4$
мы получим правильный абсолютный путь в patch-файле:
--- a/build-system/README
+++ b/build-system/README
Параметры команды git-diff (1), в предылущем примере, мы определили исходя из списка изменений, сделанных в основном репозитории platform.
Политики
Наличие подпроектов накладывает дополнительные требования к CM-политике и требует введения некоторых правил, соблюдение которых всеми участниками проекта позволит избежать конфликтов и непредвиденных затрат времени. Самыми простыми из этих правил могут быть, например, следующие:
- создавать новые ветки в upstream-репозиториях подпроектов имеет ограниченный круг разработчиков;
- никто не должен иметь возможности изменять код upstream-репозитория, если он подключен к основному проекту по тэгу.
Для того, чтобы рассмотреть подобные ограничения нам необходимо несколько доработать содержимое наших тестовых репозиториев.
Зафиксируем, для начала, текуще состояние upstream-репозитория remote/build-system.git:
bash-4.4$
bash-4.4$ cd owner/build-system/
bash-4.4$ cat README
[master] build-system 1.0.2
bash-4.4$
Допустим теперь, что автор upstream-репозитория remote/build-system.git решил внести в проект изменения, которые, согласно его версионной политике, должны повысить minor компонент текущей версии 1.0.2. Для этого ему необходимо создать новую ветку проекта с именем build-system-1.1.x и далее, по мере разработки, фиксировать состояния тегами: 1.1.0, 1.1.1, 1.1.2, и так далее.
Создадим новую ветку:
bash-4.4$
bash-4.4$ git checkout -b build-system-1.1.x
Switched to a new branch 'build-system-1.1.x'
bash-4.4$ git branch
* build-system-1.1.x
master
bash-4.4$
Повысим версию, записанную в файле README до версии 1.1.0:
bash-4.4$
bash-4.4$ vim README
bash-4.4$ cat README
[master] build-system 1.1.0
bash-4.4$
bash-4.4$ git commit -a -m "Move on to developing 1.1.x functionality"
[build-system-1.1.x f6d79c1] Move on to developing 1.1.x functionality
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
и внесем изменения в upstream-репозиторий remote/build-system.git:
bash-4.4$
bash-4.4$ git push --set-upstream origin build-system-1.1.x
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 280 bytes | 280.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../remote/build-system.git
* [new branch] build-system-1.1.x -> build-system-1.1.x
Branch 'build-system-1.1.x' set up to track remote branch 'build-system-1.1.x' from 'origin'.
bash-4.4$
Теперь допустим, что, по мере разработки, мы продвинулись до версии 1.1.1:
bash-4.4$
bash-4.4$ vim README
bash-4.4$ cat README
[master] build-system 1.1.1
bash-4.4$
bash-4.4$ git commit -a -m "Update build-system version to 1.1.1"
[build-system-1.1.x f9544a4] Update build-system version to 1.1.1
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
bash-4.4$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 276 bytes | 276.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../remote/build-system.git
f6d79c1..f9544a4 build-system-1.1.x -> build-system-1.1.x
bash-4.4$
и зафиксировали данное состояние тэгом:
bash-4.4$
bash-4.4$ git tag -a 1.1.1 -m "Created tag for release (version 1.1.1)"
bash-4.4$ git push origin 1.1.1
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 170 bytes | 170.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To ../../remote/build-system.git
* [new tag] 1.1.1 -> 1.1.1
bash-4.4$
После этого в upstream-репозитории remote/build-system.git мы можем наблюдать следующие ссылки:
bash-4.4$
bash-4.4$ cd remote/build-system.git/
bash-4.4$ tree refs
refs
├── heads
│ ├── build-system-1.1.x
│ └── master
└── tags
└── 1.1.1
2 directories, 3 files
bash-4.4$
Тем временем, разработчики проекта platform получили задачу стабилизировать сборку с использованием новой версии build-system. Естественно, для выполнения поставленной задачи, они создали новую ветку, например, platform-1.0.2:
bash-4.4$
bash-4.4$ cd user/platform/
bash-4.4$ git branch
* master
bash-4.4$ git pull
Already up to date.
bash-4.4$
bash-4.4$ git checkout -b platform-1.0.2
Switched to a new branch 'platform-1.0.2'
bash-4.4$ vim README
bash-4.4$ cat README
[master] platform 1.0.2
bash-4.4$ git commit -a -m "Сreated platform-1.0.2 branch for the transition to the system 1.1.1"
[platform-1.0.2 00a1250] Сreated platform-1.0.2 branch for the transition to the system 1.1.1
1 file changed, 1 insertion(+), 1 deletion(-)
bash-4.4$
И подключили к ней, в качестве поддерева, уже не мастер-ветку репозитория remote/build-system.git, а тэг 1.1.1:
bash-4.4$
bash-4.4$ git rm -rf build-system/
rm 'build-system/README'
bash-4.4$ git commit -a -m "Removed subtre based on build-system/master"
[platform-1.0.2 7db0f54] Removed subtre based on build-system/master
1 file changed, 3 deletions(-)
delete mode 100644 build-system/README
bash-4.4$
bash-4.4$
bash-4.4$ git push --set-upstream origin platform-1.0.2
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 550 bytes | 550.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git/
* [new branch] platform-1.0.2 -> platform-1.0.2
Branch 'platform-1.0.2' set up to track remote branch 'platform-1.0.2' from 'origin'.
bash-4.4$
bash-4.4$
bash-4.4$ git subtree add --prefix=build-system ../../remote/build-system.git/ 1.1.1
git fetch ../../remote/build-system.git/ 1.1.1
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (7/7), done.
From ../../remote/build-system
* tag 1.1.1 -> FETCH_HEAD
Added dir 'build-system'
bash-4.4$
Итак мы имеем новое состояние подпроекта build-system и должны внести эти изменения в upstream-репозиторий remote/platform.git:
bash-4.4$
bash-4.4$ git push
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (8/8), 889 bytes | 889.00 KiB/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To ../../remote/platform.git/
7db0f54..6f1a50e platform-1.0.2 -> platform-1.0.2
bash-4.4$
Проверим историю:
bash-4.4$
bash-4.4$ git log -3 --graph
* commit 6f1a50e249e01f69c54f343b65747d28abc6456d (HEAD -> platform-1.0.2, origin/platform-1.0.2)
|\ Merge: 7db0f54 f9544a4
| | Author: user <___@_______>
| | Date: Fri Nov 2 18:24:54 2018 +0300
| |
| | Add 'build-system/' from commit 'f045926542e9f685034545a45317093383fddf99'
| |
| | git-subtree-dir: build-system
| | git-subtree-mainline: 7db0f5452e67086dc4e381a0ccb14f25d48ecf0b
| | git-subtree-split: f045926542e9f685034545a45317093383fddf99
| | git-subtree-repo: ../../remote/build-system.git/
| | git-subtree-ref: 1.1.1
| |
| * commit f9544a4cc2650a83b96f400fdfc95ba64a38ec6e
| | Author: user <___@_______>
| | Date: Fri Nov 2 17:59:43 2018 +0300
| |
| | Update build-system version to 1.1.1
| |
| * commit f6d79c12ada29438454739fe6f6db9592d413be2
| | Author: user <___@_______>
| | Date: Fri Nov 2 17:54:35 2018 +0300
| |
| | Move on to developing 1.1.x functionality
bash-4.4$
Мы действительно подключили нужный нам тэг и, теперь задача CM-инженера состоит в том, чтобы разработчики проекта platform, работающие на ветке platform-1.0.2 даже не пытались править build-system. Это было бы не логично и, более того, так как поддерево build-system ссылается на тэг, совершенно недопустимо.
Для того, чтобы запретить изменения в upstream-репозитории со стороны поддерева build-system проекта platform, необходимо разработать pre-receive хук и поместить его в файл
remote/build-system.git/hooks/pre-receive
Займемся созданием простого скрипта, запрещающего коммиты в том случае, если совершается попытка изменить состояние тэга:
#!/bin/sh
zero_commit="0000000000000000000000000000000000000000"
LC_COLLATE='C'
allowed_users=(habr habrahabr)
while read oldrev newrev refname; do
#
# git-subtree(1) push имеет в своем распоряжении только имя ссылки,
# которую вы пытаетесь изменить. Поэтому нам надо проверить два
# факта:
#
# 1) не пытаетесь ли вы создать новую ветку, а если пытаетесь, то
# 2) не яляется ли это имя фактической ссылкой на tag, но так как
# имя задано вами без указания полного пути, то есть, например,
# не 'refs/tags/1.1.1', а '1.1.1', утилита git-subtree(1)
# могла ошибиться и передать его как 'refs/heads/1.1.1',
# то есть как имя ветки.
# :
if [[ $oldrev == $zero_commit && $refname =~ ^refs/heads/ ]]; then
refbase=$(basename $refname)
refpath=$(git show-ref $refbase | cut -f2 -d' ')
if [[ $refpath =~ ^refs/tags/.*$ ]]; then
echo ""
echo "ERROR: Trying to change TAG named as '$refpath'."
echo ""
exit 1
fi
if [[ ! ${allowed_users[*]} =~ $USER ]]; then
echo ""
echo "ERROR: Trying to create NEW BRANCH with name '$refname'."
echo ""
exit 1
fi
fi
done
exit 0
Данный скрипт требует пояснений. Наверное странно видеть, что сначала мы проверяем, не является ли ссылка веткой, а затем снова пытаемся проверить, не представляет ли она, фактически, тэг. Все дело в том, что функция, реализующая команду:
git subtree push --prefix=
не анализирует параметр и не сверяется с историей сообщений, сохраненных на этапе подключения поддерева в каталог
Существующее положение дел можно поправить, если доработать функциональность git-subtree (1). Однако сейчас мы не