Объединить git-репозиторий в подкаталог - PullRequest
68 голосов
/ 21 июня 2011

Я бы хотел объединить удаленный git-репозиторий с моим рабочим git-репозиторием как его подкаталог. Я хотел бы, чтобы полученный репозиторий содержал объединенную историю двух репозиториев, а также чтобы каждый файл объединенного репозитория сохранял свою историю, как это было в удаленном репозитории. Я попытался использовать стратегию поддерева, как упомянуто в Как использовать стратегию слияния поддерева , но после выполнения этой процедуры, хотя результирующий репозиторий действительно содержит объединенную историю двух репозиториев, отдельные файлы поступают из удаленного не сохранили свою историю (`git log 'на любом из них просто показывает сообщение" Объединенная ветвь ... ").

Также я не хочу использовать подмодули, потому что я не хочу, чтобы два объединенных репозитория git больше не были отдельными.

Можно ли объединить удаленный репозиторий git в другом как подкаталог с отдельными файлами, поступающими из удаленного репозитория, сохранившими свою историю?

Большое спасибо за любую помощь.

EDIT: В настоящее время я пробую решение, которое использует git filter-branch для перезаписи объединенной истории репозитория. Кажется, это работает, но мне нужно проверить это еще немного. Я вернусь, чтобы сообщить о своих выводах.

РЕДАКТИРОВАТЬ 2: В надежде прояснить ситуацию я даю точные команды, которые я использовал со стратегией поддерева git, что приводит к очевидной потере истории файлов удаленного репозитория. Пусть A будет git-репо, в котором я сейчас работаю, а B - git-репо, которое я хотел бы включить в A как его подкаталог. Было сделано следующее:

git remote add -f B <url-of-B>
git merge -s ours --no-commit B/master
git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master
git commit -m "Merge B as subdirectory in subdir/Iwant/to/put/B/in."

После этих команд и перехода в каталог subdir / Iwant / to / put / B / in я вижу все файлы B, но git log на любом из них показывает только сообщение фиксации «Объединить B как подкаталог в subdir». / Iwant / к / ставить / B / в «. История их файлов в том виде, в каком они есть в B., утеряна.

То, что кажется работать (так как я новичок в git, я могу ошибаться), это следующее:

git remote add -f B <url-of-B>
git checkout -b B_branch B/master  # make a local branch following B's master
git filter-branch --index-filter \ 
   'git ls-files -s | sed "s-\t\"*-&subdir/Iwant/to/put/B/in/-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
                git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD 
git checkout master
git merge B_branch

Команда выше для ответвления фильтра взята из git help filter-branch, в котором я изменил только путь к subdir.

Ответы [ 6 ]

49 голосов
/ 21 сентября 2015

git-subtree - это сценарий, предназначенный именно для этого случая использования объединения нескольких репозиториев в одно при сохранении истории (и / или разбиения истории поддеревьев, хотя это, кажется, не имеет отношения к этому вопросу),Он распространяется как часть дерева мерзавцев с момента выпуска 1.7.11 .

Чтобы объединить репозиторий <repo> с ревизией <rev> в качестве подкаталога <prefix>, используйте git subtree add в качествеследует:

git subtree add -P <prefix> <repo> <rev>

git-subtree реализует стратегию слияния поддерева в более удобной для пользователя форме.

29 голосов
/ 22 июня 2011

После получения более полного объяснения того, что происходит, я думаю, что понимаю это, и в любом случае внизу у меня есть обходной путь. В частности, я считаю, что обнаружение переименования обманывается слиянием поддерева с --prefix. Вот мой тестовый пример:

mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA
cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB
cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
git read-tree --prefix=bdir -u B/master
git commit -m "subtree merge B into bdir"
cd bdir
echo BBB>>B
git commit -a -m BBB

Мы создаем каталоги git a и b с несколькими коммитами каждый. Мы делаем слияние поддерево, а затем мы делаем окончательный коммит в новом поддереве.

Запуск gitk (в z / a) показывает, что история действительно появляется, мы можем ее видеть. Запуск git log показывает, что история действительно появляется. Однако при просмотре определенного файла возникает проблема: git log bdir/B

Ну, есть трюк, который мы можем сыграть. Мы можем посмотреть историю переименования конкретного файла, используя --follow. git log --follow -- B. Это хорошо, но не очень, поскольку не удается связать историю предварительного слияния с последующим слиянием.

Я попытался поиграть с -M и -C, но не смог заставить его следовать одному конкретному файлу.

Итак, я считаю, что решение состоит в том, чтобы сообщить git о переименовании, которое будет происходить как часть слияния поддерева. К сожалению, git-read-tree довольно суетлив относительно слияний поддеревьев, поэтому нам нужно работать через временный каталог, но это может уйти, прежде чем мы сделаем коммит. После этого мы можем увидеть полную историю.

Сначала создайте репозиторий «А» и сделайте несколько коммитов:

mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA

Во-вторых, создайте репозиторий "B" и сделайте несколько коммитов:

cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB

И хитрость в том, чтобы заставить эту работу : заставить Git распознать переименование, создав подкаталог и переместив в него содержимое.

mkdir bdir
git mv B bdir
git commit -a -m bdir-rename

Вернитесь в хранилище "A", получите и объедините содержимое "B":

cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
# According to Alex Brown and pjvandehaar, newer versions of git need --allow-unrelated-histories
# git merge -s ours --allow-unrelated-histories --no-commit B/master
git read-tree --prefix= -u B/master
git commit -m "subtree merge B into bdir"

Чтобы показать, что они теперь объединены:

cd bdir
echo BBB>>B
git commit -a -m BBB

Чтобы доказать, что вся история сохраняется в связанной цепочке:

git log --follow B

После этого мы получаем историю, но проблема в том, что если вы на самом деле держите старое репо «б» и время от времени сливаетесь с ним (скажем, на самом деле это репо, поддерживаемый отдельно третьей стороной), то у вас проблемы эта третья сторона не сделала переименование. Вы должны попытаться объединить новые изменения в вашей версии b с переименованием, и я боюсь, что это не пройдет гладко. Но если б уходит, вы выиграете.

5 голосов
/ 22 июня 2011

Если вы действительно хотите сшить вещи вместе, ищите прививки.Вы также должны использовать git rebase --preserve-merges --onto.Существует также возможность сохранить дату автора для информации о коммитере.

4 голосов
/ 23 июля 2018

Я хотел

  1. сохранить линейную историю без явного слияния, а
  2. сделать так, чтобы файлы слитого репозитория всегда существовали в подкаталоге, и какпобочный эффект заставляет git log -- file работать без --follow.

Шаг 1 : переписать историю в исходном хранилище, чтобы она выглядела так, как будто все файлы всегда существовали ниже подкаталога.

Создайте временную ветвь для переписанной истории.

git checkout -b tmp_subdir

Затем используйте git filter-branch, как описано в Как я могу переписать историю так, чтобы все файлы, кроме тех, которые я уже переместил, находятся в подкаталоге? :

git filter-branch --prune-empty --tree-filter '
if [ ! -e foo/bar ]; then
    mkdir -p foo/bar
    git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files foo/bar
fi'

Шаг 2 : переключиться на целевой репозиторий.Добавьте исходный репозиторий как удаленный в целевой репозиторий и извлеките его содержимое.

git remote add sourcerepo .../path/to/sourcerepo
git fetch sourcerepo

Шаг 3 : используйте merge --onto, чтобы добавить коммиты переписанного исходного репозитория поверхцелевой репозиторий.

git rebase --preserve-merges --onto master --root sourcerepo/tmp_subdir

Вы можете проверить журнал, чтобы увидеть, что это действительно дает вам то, что вы хотели.

git log --stat

Шаг 4 : После перезагрузки выВы находитесь в состоянии «отсоединенная ГОЛОВА».Вы можете быстро перенести мастер на новую головку.

git checkout -b tmp_merged
git checkout master
git merge tmp_merged
git branch -d tmp_merged

Шаг 5 : Наконец, некоторая очистка: Удалить временный пульт.

git remote rm sourcerepo
3 голосов
/ 22 декабря 2012

Я нашел следующее решение работоспособным для меня. Сначала я иду в проект B, создаю новую ветку, в которой уже все файлы будут перемещены в новый подкаталог. Затем я подталкиваю эту новую ветку к источнику. Затем я иду в проект A, добавляю и извлекаю пульт B, затем извлекаю перемещенную ветку, возвращаюсь в master и сливаюсь:

# in local copy of project B
git checkout -b prepare_move
mkdir subdir
git mv <files_to_move> subdir/
git commit -m 'move files to subdir'
git push origin prepare_move

# in local copy of project A
git remote add -f B_origin <remote-url>
git checkout -b from_B B_origin/prepare_move
git checkout master
git merge from_B

Если я зайду в подкаталог subdir, я смогу использовать git log --follow и все еще иметь историю.

Я не эксперт по git, поэтому я не могу комментировать, является ли это особенно хорошим решением или у него есть предостережения, но пока все выглядит нормально.

3 голосов
/ 21 июня 2011

Вы пытались добавить дополнительный репозиторий как подмодуль git? Он не объединит историю с репозиторием, в котором он находится, фактически это будет независимый репозиторий.

Я упоминаю об этом, потому что вы этого не сделали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...