Git: могу ли я поддерево объединить только подпуть репозитория?
У меня есть пульты Foo и Bar. Foo - это веб-приложение с множеством каталогов, среди которых есть /public
, который содержит различные файлы и другие каталоги.
Bar - это набор библиотек и ничего не используется в интерфейсе, как таковой, он должен идти в /public/bar
в Foo. В Foo нет файлов.
Это все будет куском пирога с подмодулями или слиянием поддерева. Однако...
Барное дерево беспорядочно, у него есть всевозможные файлы предварительного производства, такие как PSD и FLA, и единственной действительно полезной частью этого является то, что находится внутри его /www/tools
.
Итак, что я хочу сделать, это слить Bar /www/tools
в Foo /public/bar
и притвориться, что остальная часть Bar Tree даже не существует.
Можно?
(Я бы предположил, что это очень похоже на то, как вы сливаетесь с проектом, который первоначально объединил ваш в качестве поддерева. Который я не знаю, как это сделать.)
Ответы
Ответ 1
Я успешно сделал это, используя следующие команды (переписан для вашего сценария).
$ git remote add Bar /path/to/bar
$ git merge -s ours --no-commit Bar/master
$ git read-tree --prefix=public/bar -u Bar/master:www/tools/
ОГРОМНОЕ ПРЕДУПРЕЖДЕНИЕ! Я все еще в процессе выяснения, как сделать fetch/merge из Bar. Это приведет всего лишь один раз.
Мой текущий способ слияния изменений выглядит так:
$ get fetch Bar
$ git pull -X subtree=public/bar Bar master
Это оставит вас с конфликтами, говоря, что тонна файлов удалена, вы можете просто git rm
их, а затем git commit
.
Я, конечно, открыт для предложений по лучшему способу вытащить изменения.
Поскольку это старый поток, я должен добавить, что я запускаю Git 1.7.9.
Ответ 2
Попробуйте использовать git поддерево для этого. Он позволяет вам извлечь поддерево проекта в другой проект, который затем можно объединить в ваш.
Ответ 3
Изменить: Мне кажется, что вы можете сделать это только с помощью git merge --no-commit
. Это попытается слить, и даже если оно не будет конфликтовать, оно остановится перед совершением. На этом этапе вы можете удалить все нежелательные файлы, которые вам не нужны (в том числе восстанавливать конфликтующие файлы, если это необходимо) и создать коммит слияния, содержащий только требуемое поддерево.
Оригинальный ответ:
Вы можете использовать для этого фильтр-ветку. Схема:
Клонирование исходного репо:
git clone --bare /path/to/bar /path/to/bar_clone
Использование голого клона сэкономит вам время и пространство для создания рабочего каталога.
Затем используйте ветвь filter на ветке:
git filter-branch --index-filter 'git rm -rf <unwanted files/directories>' -- --all
--all
позволяет ему знать, что вы хотите использовать все ссылки ref, а не только текущую HEAD. Теперь у вас будет репозиторий, содержащий только нужный подкаталог и всю связанную с ним историю.
Примечание. Извините, я не знаю очень простой способ удалить все, кроме того, что вы хотите. Вы должны быть осторожны с подстановочными знаками, потому что вы не хотите сжимать какие-либо каталоги git. Здесь что-то будет работать, хотя и медленнее, особенно если у вас много файлов:
git filter-branch --index-filter 'git rm -f `git ls-files | grep -v ^www/tools`' -- --all
В любом случае, однако вы управляете списком файлов для удаления, вы можете продолжить слияние ваших поддеревьев, потянув с bar_clone
на foo
.
Ответ 4
Я не думаю, что это можно сделать с помощью слияния, но эти шаги могут сделать трюк, по крайней мере, до конечного продукта. Во-первых:
% git fetch Bar
Это извлекает последние фиксации из репозитория Bar, но не пытается их объединить. Он записывает SHA для фиксации в .git/FETCH_HEAD
% cat .git/FETCH_HEAD
b91040363160aab4b5dd46e61e42092db74b65b7 branch 'Bar' of ssh://blah...
Это показывает, что идентификатор SHA для последней фиксации из удаленной ветки.
% git checkout b91040363160aab4b5dd46e61e42092db74b65b7 www/tools
Это берет копию файлов в www/tools для извлеченного фиксации и перезаписывает то, что в рабочем дереве. Затем вы можете перенести эти изменения в свой локальный репозиторий, как обычно. Результирующая фиксация не будет иметь никакой ссылки на то, откуда она появилась, но она должна по крайней мере получить ваш репозиторий с версиями файлов, которые вы хотите.
Ответ 5
Я бы предложил использовать поддерево два раза - один раз, чтобы извлечь все /www/tools и один раз, чтобы извлечь/публиковать - похоже, что /public должно быть в своем собственном репо, поэтому я бы предложил вам push/public для нового repo - со всеми его историями поддерева, затем слияние /www/tools поддерево в новое/публичное репо и добавление обратно в виде поддерева Foo.
cd foo
git subtree split --prefix=public --branch=new-shared-public --annotate='(split) '
cd../bar
git subtree split --prefix=www/tools --rejoin --branch=new-shared-www-tools --annotate='(split) '
Теперь у вас есть два новых ветки в ваших репозиториях. Один с публичной историей фиксации, а другой с www/tools. Я опускаю cd
отсюда.
Создайте новое публичное репо (я предполагаю github здесь, но похоже, что вы, возможно, захотите сделать это локально) и нажмите на свое поддерево: git checkout new-shared-public git push git @github.com: my_id/new-shared-repo.git HEAD: мастер
Затем слейте другую ветку в следующую строку:
git checkout new-shared-www-tools
git remote add Foo [email protected]:my_id/new-shared-repo.git
git merge -s ours --no-commit Bar/master
Хотя я указал ours
как политику фиксации, он, вероятно (вероятно) не имеет значения.
Наконец, после того, как вы подготовили слияние и перетащили его в новое репо, вернитесь к репозиторию Foo. git rm публичную историю оттуда и добавить новое публичное репо в качестве поддерева:
git subtree add --squash --prefix shared [email protected]:my_id/new-shared-repo.git master
git subtree pull --squash --prefix shared [email protected]:my_id/new-shared-repo.git master
git push