Объединить два репозитория Git, не нарушая историю файлов

Мне нужно объединить два репозитория Git в совершенно новый, третий репозиторий. Я'мы нашли много описаний того, как сделать это с помощью объединения поддеревьев (например,Якуб Наремbski»ответ наКак объединить два репозитория Git?) и следование этим инструкциям в основном работает, за исключением того, что когда я фиксирую объединение поддеревьев, все файлы из старых репозиториев записываются как новые добавленные файлы. Когда я делаю, я вижу историю коммитов из старых репозиториев.git log, но если я сделаюgit log  он показывает только один коммит для этого файла - слияние поддерева. Судя по комментариям на ответ выше, яЯ не одинок, видя эту проблему, но яМы не нашли опубликованных решений для этого.

Есть ли способ объединить репозитории и оставить историю отдельных файлов без изменений?

 xverges01 июн. 2017 г., 18:45
Автоматизированное решение, которое работало для меня, былоstackoverflow.com/a/30781527/239408
 nacross17 февр. 2014 г., 02:07
Я также нашел этот вопрос полезнымstackoverflow.com/questions/1683531/...
 Dimitri Dewaele27 февр. 2017 г., 15:20
Я создал дополнительный вопрос. Может быть интересно: объединить два репозитория Git и сохранить историю мастера:stackoverflow.com/questions/42161910/...
 Lucero24 окт. 2012 г., 02:12
я не использую Git, но в Mercurial IСначала сделайте преобразование, если необходимо, чтобы исправить пути к файлам репозиториев, которые должны быть объединены, а затем принудительно вытяните один репозиторий в цель, чтобы получить наборы изменений, а затем выполните объединение различных ветвей. Это проверено и работает;) Может быть, это помогает найти решение и для Git ... по сравнению с подходом слияния поддеревьев, я думаю, шаг преобразования отличается, когда история переписывается, а не просто отображает путь (если я понимаю, правильно). Это обеспечивает плавное слияние без какой-либо специальной обработки путей к файлам.

Ответы на вопрос(7)

Прошло несколько лет, и есть хорошо обоснованные решения, но я хочу поделиться своими, потому что это было немного по-другому, потому чтоЯ хотел объединить 2 удаленных репозитория в новый, не удаляя историю из предыдущих репозиториев.

Создайте новый репозиторий в Github.

Загрузите вновь созданный репозиторий и добавьте старый удаленный репозиторий.

git clone https://github.com/alexbr9007/Test.git
cd Test
git remote add OldRepo https://github.com/alexbr9007/Django-React.git
git remote -v

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

git fetch OldRepo
git branch -a

В основной ветке выполните слияние, чтобы объединить старое репо с вновь созданным.

git merge remotes/OldRepo/master --allow-unrelated-histories

Создайте новую папку для хранения всего нового созданного контента, который был добавлен из OldRepo, и переместите его файлы в эту новую папку.

Наконец, вы можете загрузить файлы из объединенных репозиториев и безопасно удалить OldRepo из GitHub.

Надеюсь, что это может быть полезно для всех, кто занимается слиянием удаленных репозиториев.

пожалуйста, посмотрите на использование

git rebase --root --preserve-merges --onto

связать две истории в начале своей жизни.

Если у вас есть пути, которые перекрываются, исправьте их

git filter-branch --index-filter

когда вы используете журнал, убедитесь, что вынайди копии сложнее с

git log -CC

Таким образом, вы найдете любые движения файлов в пути.

Выполните шаги для встраивания одного репо в другое репо, имея одну историю Git, объединяя обе истории Git.

Клонируйте оба репозитория, которые вы хотите объединить.

git clone [email protected]: пользователь / parent-repo.git

git clone [email protected]: пользователь / child-repo.git

Перейти в детское репо

cd child-repo /

выполните команду ниже, замените путьmy/new/subdir (3 случая) со структурой каталогов, где вы хотите иметь дочерний репозиторий.

git filter-branch --prune-empty --tree-filter ' если [ ! -e мой / новый / subdir]; затем mkdir -p my / new / subdir git ls-tree - только для имени $ GIT_COMMIT | xargs -I файлы mv файлы my / new / subdir fi '

Перейти в родительское репо

cd ../parent-repo/

Добавить удаленное к родительскому репо, указав путь к дочернему репо

git remote добавить child-remote ../child-repo/

Получить детское репо

git fetch child-remote

Слить истории

git merge --allow-unrelated-историй child-remote / master

Если вы сейчас проверите журнал git в родительском репо, он должен объединить коммиты дочернего репо. Вы также можете увидеть тег, указывающий из источника коммита.

Приведенная ниже статья помогла мне встроить одно хранилище в другое, создав одну историю Git, объединив обе истории Git.

http://ericlathrop.com/2014/01/combining-git-repositories/

Надеюсь это поможет. Удачного кодирования!

Вот'это способ, который непереписать любую историю, поэтому все идентификаторы коммитов останутся действительными. Конечным результатом является то, что второй репоФайлы s окажутся в подкаталоге.

Добавьте второй репо в качестве удаленного:

cd firstgitrepo/
git remote add secondrepo [email protected]:andsoon

Убедитесь, что выскачал все второй репоs совершает:

git fetch secondrepo

Создать локальную ветку из второго репофилиал:

git branch branchfromsecondrepo secondrepo/master

Переместите все его файлы в подкаталог:

git checkout branchfromsecondrepo
mkdir subdir/
git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} subdir/
git commit -m "Moved files to subdir/"

Слить вторую ветку в первый репоМастерская ветка:

git checkout master
git merge --allow-unrelated-histories branchfromsecondrepo

Ваш репозиторий будет иметь более одного корневого коммита, но это не должноЭто не проблема.

 Keith31 янв. 2014 г., 16:47
Шаг 2 нене работает для меня: fatal: Недопустимое имя объекта: 'secondrepo / мастер.
 monkjack15 мар. 2014 г., 16:27
Я должен был сделать выборку, чтобы сломать это также. Между 1 и 2 я сделал git fetch secondrepo
 Flimm17 мар. 2014 г., 12:26
@monkjack: яя отредактировал мой ответ, включив шаг git fetch. Не стесняйтесь редактировать ответ самостоятельно в будущем.
 Lucademicus09 янв. 2019 г., 06:20
Шаг 4 не работает в Windows (в Powershell), необходимо заменить команду xargs. У кого-нибудь есть идеи, как переписать Шаг 4 для Windows? @Flimm?
 Eric Smith02 дек. 2016 г., 11:35
Однажды я'выполнив все эти шаги, могу ли я безопасно удалить ветку из второго репо? Или это приведет к потере истории?
 Fredrik Erlandsson28 апр. 2016 г., 09:12
Для части 5, возможно, вы хотели бы использоватьgit merge branchfromsecondrepo -s recursive -X no-renames так дерьмопытается найти переименованные файлы.
 Flimm02 дек. 2016 г., 11:53
@EricSmith Вы можете безопасно удалить его.git branch -d branchfromsecondrepo победил'удалить ветку, если она неЭто безопасно. (The-D флаг вызывает удаление, но-d безопасно.)
 Flimm09 янв. 2019 г., 15:04
@Lucademicus Я видел твои предложенные изменения. Первоначально я одобрил это, но затем я откатился назад, поскольку он не является строго эквивалентным эквиваленту bash. Я'я не уверен, правильно ли он обрабатывает игнорируемые Git файлы.
 DarkteK13 апр. 2015 г., 22:51
Это н'я думаю, что один шаг пропущен или неправильный, потому что яЯ пытаюсь объединить, как этот учебник, и все, что я получил, это просто Unmerged ошибка во многих файлах, кроме того, все мои основные файлы идут в папку подкаталога ....
 Mikhail Orlov30 авг. 2016 г., 11:03
Шаг 5 требует.--allow-unrelated-histories
 Flimm10 авг. 2017 г., 16:46
@MartijnHeemels Для более старой версии Git просто опустите--allow-unrelated-histories, Смотрите историю этого поста.
 Flimm04 февр. 2014 г., 11:59
@Keith: убедитесь, что вымы добавили второй репо в качестве удаленного имениsecondrepo»и что у этого репо есть ветка с именем "мастер" (вы можете просматривать ветки на удаленном репо с помощью команды)git remote show secondrepo
 C S07 мая 2018 г., 03:35
Отличный ответ, выручил меня. :) На последнем шаге, что является причиной сделатьmerge а неrebase? я сделалgit rebase branchfromsecondrepo чтобы первый репо начал с истории второго репо, и я думаю, чтоБолее предпочтительным в такой ситуации.
 Martijn Heemels10 авг. 2017 г., 16:45
Обратите внимание, что--allow-unrelated-histories опция git merge на шаге 5 является относительно новой. Git 2.7 (версия по умолчанию в Ubuntu 16.04) неНе знаю, поэтому я установил Git 2.13 из PPA git-core. Процедура отлично работает!
 rmunn21 мар. 2016 г., 10:26
Этот ответ сработал для меня (или, точнее, для коллеги, который только что спросил меня, как это сделать). Он нене нужно перемещать какие-либо файлы в подкаталог, поскольку в двух репозиториях были разные структуры каталогов (проект C #, объединяющий репозитории FooProject и BarProject - все уже находилось в папках с именами FooProject или BarProject). Было несколько легко решаемых конфликтов слияния в таких файлах, как.gitignore, но это'сидеть. Все файлы FooProject и BarProject теперь находятся в одном репо с полной историей, сохраненной для каждого.
 Flimm08 июл. 2016 г., 14:54
@ user5359531 Нет, тамНет необходимости нажимать что-либо после шага 4, в него не вносятся никакие изменения. Если вам нравится результат, вы можете нажать на результат после того, как все шаги в моем ответе были выполнены. На шаге 5 вы должны объединиться сbranchfromsecondrepo, и неsecondrepo/master или жеbranchfromsecondrepo/master или любая ветка удаленного слежения. Я думаю, вы могли бы быть смущены различиями между локальными ветвями, ветвями удаленного отслеживания и ветвями на удаленных устройствах, см. Этот постstackoverflow.com/a/24785777/247696
 user535953108 июл. 2016 г., 12:35
После шага 4 и до шага 5, вы должны подтолкнутьbranchfromsecondrepo вернуться к соответствующему пульту? Потому что, когда я перешел к шагу 5, переключил обратноmaster филиал, а затем попыталсяgit merge branchfromsecondrepo/masterизменения, которые я сделал на шаге 4, больше не применялись, это было так, как если бы они слились прямо с пульта, а не с локальногоbranchfromsecondrepo, Я не смог бежатьgit merge branchfromsecondrepogit выдал ошибку, что это не то, что можно объединить
 Flimm31 авг. 2016 г., 11:11
Извините @Xiong Chiamiov, я откатил вашу правку, потому что я предпочитаю разбивать команду вродеgit checkout -b BLA BLA2 на две команды, когда яЯ учу кого-то чему-то. Кроме того, ваши изменения сделали команду больше не соответствующей описанию команды перед ней.

Эта функция клонирует удаленное репо в локальный каталог репо:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

Как пользоваться:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

Прибыль! Я

 Patrick Beard12 июл. 2017 г., 04:20
Проблема обсуждалась здесь:stackoverflow.com/questions/7798142/... mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" иногда терпит неудачу, поэтому вы должны добавить.if test
 Patrick Beard12 июл. 2017 г., 00:52
м, используя zsh, а не bash, и v2.13.0 из git. Неважно, что яЯ пытался, у меня нетне смог получитьgit filter-branch --index-filter работать. Обычно я получаю сообщение об ошибке, что файл индекса .new нене существует. Это звонит в какие-нибудь колокола?
 Andrey Izman12 июл. 2017 г., 02:22
@PatrickBeard я нет зш, вы можете создать отдельный файлgit-add-repo.sh с функцией выше, в конце файла поставьте эту строкуgit-add-repo "[email protected]", После этого вы можете использовать его из Zsh, какcd current/git/package а такжеbash path/to/git-add-repo.sh https://github.com/example/example dir/to/save
Решение Вопроса

Оказывается, что ответ будет намного проще, если вымы просто пытаемся склеить два репозитория и сделать так, чтобы все выглядело так, а не управлять внешней зависимостью. Вам просто нужно добавить пульты к своим старым репозиториям, объединить их с новым мастером, переместить файлы и папки в подкаталог, зафиксировать перемещение и повторить для всех дополнительных репо. Подмодули, слияния поддеревьев и причудливые перебазировки предназначены для решения немного другой проблемы и нене подходит для того, что я пытался сделать.

Вот'Вот пример скрипта Powershell для склеивания двух репозиториев:

# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init

# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
git commit --allow-empty -m "Initial dummy commit"

# Add a remote for and fetch the old repo
git remote add -f old_a 

# Merge the files from old_a/master into new/master
git merge old_a/master --allow-unrelated-histories

# Move the old_a repo files and folders into a subdirectory so they don't collide with the other repo coming later
mkdir old_a
dir -exclude old_a | %{git mv $_.Name old_a}

# Commit the move
git commit -m "Move old_a files into subdir"

# Do the same thing for old_b
git remote add -f old_b 
git merge old_b/master --allow-unrelated-histories
mkdir old_b
dir –exclude old_a,old_b | %{git mv $_.Name old_b}
git commit -m "Move old_b files into subdir"

Очевидно, что вместо этого вы можете объединить old_b со old_a (который становится новым объединенным репо), если выЯ бы предпочел сделать это - изменить сценарий, чтобы удовлетворить.

Если вы также хотите перенести текущие ветви функций, используйте это:

# Bring over a feature branch from one of the old repos
git checkout -b feature-in-progress
git merge -s recursive -Xsubtree=old_a old_a/feature-in-progress

Тот'Единственная неочевидная часть процесса - этоэто не слияние поддеревьев, а скорее аргумент к обычному рекурсивному слиянию, которое сообщает Git, что мы переименовали цель, и помогает Git правильно выстраивать все.

Я написал чуть более подробное объяснениеВот.

 Jon29 янв. 2014 г., 14:06
домен / хозяин здесь old_b
 Dominique Vial26 янв. 2016 г., 12:14
Это1 (номер один) дляlsи капиталглаз» заxargs, Спасибо вам за этот совет!
 Eric Lee28 янв. 2014 г., 18:59
@ Джон Нет, если тымы сначала переместили первый репо в подкаталог, затем второй репоне вызывает никаких конфликтов. Если это так, выВозможно, мы пропустили шаг или обнаружили какое-то поведениене знаю о. Я
 Eric Lee09 февр. 2013 г., 00:47
Это'Так же, как и любая другая операция перемещения / переименования в Git: из командной строки вы можете получить всю историю, выполнивgit log --followили все инструменты GUI делают это автоматически. С поддеревом сливаю тебяне может получить историю для отдельных файлов, насколько я знаю, так что этот метод лучше.
 EML09 февр. 2017 г., 15:25
+1, но учтите, что если у вас есть .gitignore в обоих репозиториях, то вы получите конфликт во втором слиянии, который должен быть исправлен. Обратите внимание, что кто-то еще отредактировал ваш ответ, чтобы добавить--allow-unrelated-histories к вашей команде слияния. Это новое и нет в вашем блоге, и, вероятно, должны быть возвращены ...
 Jon27 янв. 2014 г., 11:15
@EricLee Когда репозиторий old_b объединен, я получаю много конфликтов слияния. Это ожидается? Я получаю КОНФЛИКТ (переименовать / удалить)
 mholm81504 февр. 2013 г., 16:34
это решение с использованиемgit mv Безразлично»так хорошо работает. когда вы позже используетеgit log для одного из перемещенных файлов вы получаете коммит только с движения. Вся предыдущая история потеряна. это потому чтоgit mv действительноgit rm; git add нов один шаг.
 user335688523 февр. 2016 г., 19:15
Этот метод объединения git-репозиториев должен быть тщательно оценен, если вы планируете использовать код из нового git-репозитория для процесса сборки / выпуска. Похоже, что операция git move испортила все ветви и теги в коде во вновь созданном репозитории. Хотя вы можете увидеть свои теги на месте, но этоне пригодны для использования вообще.
 Jon29 янв. 2014 г., 14:04
м на Git 1.8.5 в Windows, и это всегда вызывает проблемы при запуске слияния на old_b и яЯ не уверен, почему. CONFLICT (переименовать / удалить): DeleteFeedCommand.cs удален в HEAD и переименован в домен / мастер. Версия домена / мастера DeleteFeedCommand.cs оставлена в дереве.
 axd20 окт. 2017 г., 19:26
Безразлично»Я всегда работаю. при объединении нескольких проектов, имеющих одинаковую структуру каталогов, возникнет огромное количество конфликтов из-за попытки объединения следующего подпроекта, следующего за a.git mv
 xverges01 июн. 2017 г., 18:44
stackoverflow.com/a/30781527/239408 обеспечивает другой автоматизированный подход, основанный на Bash
 IsmailS30 июл. 2015 г., 16:33
Я сливал B-репо в A-репо. В обоих репозиториях у меня была ветвь, которая находится в стадии разработки Я хотел вытащить текущие изменения B репо в A репо. В репо я сделалgit checkout feature-in-progress > merge master > git merge -s recursive -Xsubtree=B old_a/feature-in-progress, Он слился правильно, но недавно добавленные файлы вB/feature-in-progress не хватает. :(
 BrianVPS24 нояб. 2015 г., 16:47
Я использую Git Bash в Windows, и у меня была та же проблема, что и у Джорджа. Его команда работает, но этоне ясно, что переключается на обаLS' а также 'xargs' это столицаглаз» и не нижеэль.
 Matthew Wise23 февр. 2017 г., 12:35
Пришлось сделать это, и ваши шаги выше работали отлично, спасибо! Просто интересно, можно ли в конце удалить ссылки на старые пульты и действительно ли можно обнулить старые репозитории или же история в объединенном репо все еще указывает на старые репозитории?
 Jeremy05 дек. 2018 г., 17:46
dir --ignore old_a не-e
 acumartini21 мар. 2018 г., 00:07
В контексте Bash я предпочитаю:find . -maxdepth 1 -not -name ".git" -and -not -name 'old_a' -exec git mv {} old_a/ \;
 Jon29 янв. 2014 г., 16:26
Я думаю, что git add -u на конфликт, кажется, обходит его
 hussfelt13 сент. 2017 г., 13:24
На OSX / MacOSdir -exclude old_a | %{git mv $_.Name old_a} может бытьls | grep -v 'old_a' | xargs -I '{}' git mv '{}' old_a
 George11 нояб. 2015 г., 03:40
Когда я пытаюсьdir -exclude old_a | % {git mv $ _. Имя old_a} "Я получаю sh.exe ": dir: команда не найдена и sh.exe ": git: команда не найдена. Используя это работает: ls -I old_a | xargs -I '{}» Git Mv '{}» old_a /
 Jeffery Utter13 апр. 2016 г., 05:00
Я оцениваю этот вариант, и он, кажется, работает довольно хорошо. Одна проблема, с которой я сталкиваюсь, состоит в том, что журналы git для некоторых файлов содержатмного больше коммитов после слияния. Файл с 4 коммитамиУ исходного репо 400 после слияния в репо с 4000 коммитов. Я'Я не уверен, где он собирает другие коммиты. Есть идеи?

Я перевернулрешение из @Flimm это вgit alias как это (добавлено в мой~/.gitconfig):

[alias]
 mergeRepo = "!mergeRepo() { \
  [ $# -ne 3 ] && echo \"Three parameters required,   \" && exit 1; \
  git remote add newRepo $1; \
  git fetch newRepo; \
  git branch \"$2\" newRepo/master; \
  git checkout \"$2\"; \
  mkdir -vp \"${GIT_PREFIX}$3\"; \
  git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} \"${GIT_PREFIX}$3\"/; \
  git commit -m \"Moved files to '${GIT_PREFIX}$3'\"; \
  git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames \"$2\"; \
  git branch -D \"$2\"; git remote remove newRepo; \
}; \
mergeRepo"
 neowulf3312 мая 2017 г., 23:35
Какие's значение?$GIT_PREFIX
 Parker Coates29 нояб. 2016 г., 13:55
Просто любопытно: вы действительно делаете это достаточно часто, чтобы использовать псевдоним?
 Fredrik Erlandsson15 мая 2017 г., 10:50
github.com/git/git/blob/...      'GIT_PREFIX» устанавливается как возвращаемое при запуске 'git rev-parse --show-prefix '     из оригинального текущего каталога. Смотрите linkgit: git-rev-parse [1].
 quetzalcoatl04 мая 2017 г., 20:17
Да .. но попробуйте сменить компьютер и забыть переместить псевдонимы;)
 Fredrik Erlandsson05 дек. 2016 г., 21:39
Нет я неНо никогда не вспоминайте, как это сделать, так что псевдоним - это просто способ запомнить это.

Ваш ответ на вопрос