Первым делом мы добавим в наш проект приложение Rack. Мы добавим Rack в наш собственный проект, как удаленный репозиторий, а затем выгрузим его в отдельную ветку.
$git remote add rack_remote https://github.com/rack/rack
$git fetch rack_remote
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From https://github.com/rack/rack
* [new branch] build -> rack_remote/build
* [new branch] master -> rack_remote/master
* [new branch] rack-0.4 -> rack_remote/rack-0.4
* [new branch] rack-0.9 -> rack_remote/rack-0.9
$git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
Таким образом, теперь у нас в ветке rack_branch находится основная ветка проекта Rack, а в ветке master – наш собственный проект. Если вы переключитесь сначала на одну ветку, а затем на другую, то увидите, что они имеют абсолютно разное содержимое:
$ls
AUTHORS KNOWN-ISSUES Rakefile contrib lib
COPYING README bin example test
$git checkout master
Switched to branch "master"
$ls
README
Может показаться странным, но, на самом деле, ветки в вашем репозитории не обязаны быть ветками одного проекта. Это мало распространено, так как редко бывает полезным, но иметь ветки, имеющие абсолютно разные истории, довольно легко.
В данном примере, мы хотим выгрузить проект Rack в субдиректорию нашего основного проекта. В Git мы можем выполнить это с помощью команды git read-tree. Вы узнаете больше о команде read-tree и её друзьях в Git изнутри, сейчас же вам достаточно знать, что она считывает содержимое некоторой ветки в ваш текущий индекс и рабочий каталог. Мы просто переключимся обратно на ветку master и выгрузим ветку rack в субдиректорию rack ветки master нашего основного проекта:
$git read-tree --prefix=rack/ -u rack_branch
Когда мы будем выполнять коммит, он будет выглядеть так, как будто все файлы проекта Rack были добавлены в эту субдиректорию – например, мы скопировали их из архива. Важно отметить, что слить изменения одной из веток в другую довольно легко. Таким образом, если проект Rack обновился, мы можем получить изменения из его репозитория просто переключившись на соответствующую ветку и выполнив операцию git pull:
$git checkout rack_branch
$git pull
Затем мы можем слить эти изменения обратно в нашу ветку master. Мы можем использовать git merge -s subtree и это будет прекрасно работать; но Git также сольет вместе истории проектов, а этого мы, возможно, не хотим. Для того, чтобы получить изменения и заполнить сообщение коммита используйте опции --squash и --no-commit, вместе с опцией -Xsubtree рекурсивной стратегии слияния. Вообще-то, по умолчанию используется именно рекурсивная стратегия слияния, но мы указали и её тоже для пущей ясности.
$git checkout master
$git merge --squash -s recursive -Xsubtree=rack --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
Все изменения из проекта Rack слиты и подготовлены для локального выполнения коммита. Вы также можете поступить наоборот – сделать изменения в субдиректории rack вашей основной ветки и затем слить их в вашу ветку rack_branch, чтобы позже передать их ответственным за проекты или отправить их в вышестоящий репозиторий проекта Rack.
Таким образом, слияние субдеревьев дает нам возможность использовать рабочий процесс в некоторой степени похожий на рабочий процесс с субмодулями, но при этом без использования субмодулей (которые мы рассмотрим в Подмодули). Мы можем держать ветки с другими связанными проектами в нашем репозитории и периодически сливать их как субдеревья в наш проект. С одной стороны это удобно, например, тем, что весь код хранится в одном месте. Однако, при этом есть и некоторые недостатки – субдеревья немного сложнее, проще допустить ошибки при повторной интеграции изменений или случайно отправить ветку не в тот репозиторий.
Другая небольшая странность состоит в том, что для получения различий между содержимым вашей субдиректории rack и содержимого ветки rack_branch – для того, чтобы увидеть необходимо ли выполнять слияния между ними – вы не можете использовать обычную команду diff. Вместо этого вы должны выполнить команду git diff-tree, указав ветку, с которой вы хотите выполнить сравнение:
$git diff-tree -p rack_branch
Для сравнения содержимого вашей субдиректории rack с тем, что находилось в ветке master сервера, когда вы последний раз извлекали из него изменения, вы можете выполнить:
$git diff-tree -p rack_remote/master
Функциональность git rerere – частично скрытый компонент Git. Ее имя является сокращением для “reuse recorded resolution” (“повторное использование сохраненных разрешений конфликтов”). Как следует из имени, эта функциональность позволяет попросить Git запомнить то, как вы разрешили некоторую часть конфликта, так что в случае возникновения такого же конфликта, Git сможет его разрешить автоматически.
Читать дальше