Рассмотрим следующий сценарий:
Я разработал небольшой экспериментальный проект A в своем собственном репозитории Git. Сейчас он созрел, и я бы хотел, чтобы A стал частью более крупного проекта B, у которого есть свой большой репозиторий. Теперь я хотел бы добавить A в качестве подкаталога B.
Как мне слить А в Б, не теряя истории ни с какой стороны?
git
merge
repository
git-subtree
static_rtti
источник
источник
Ответы:
Одна ветвь другого хранилища может быть легко размещена в подкаталоге, сохраняя свою историю. Например:
Это будет выглядеть как один коммит, в котором все файлы главной ветки Rails будут добавлены в каталог "rails". Однако заголовок коммита содержит ссылку на старое древо истории:
Где
<rev>
хеш коммита SHA-1. Вы все еще можете увидеть историю, виноваты некоторые изменения.Обратите внимание, что отсюда не видно префикса каталога, поскольку это старая ветка, оставшаяся без изменений. Вы должны рассматривать это как обычный коммит перемещения файла: вам понадобится дополнительный прыжок при достижении этого.
Есть более сложные решения, такие как ручная работа или переписывание истории, как описано в других ответах.
Команда git-subtree является частью официального git-contrib, некоторые менеджеры пакетов устанавливают ее по умолчанию (OS X Homebrew). Но вам может потребоваться установить его самостоятельно в дополнение к git.
источник
git co v1.7.11.3
на... v1.8.3
).git log rails/somefile
не будет отображаться история коммитов этого файла, кроме коммит слияния. Как предложил @artfulrobot, проверьте ответ Грега Хьюгилла . И вам может понадобиться использоватьgit filter-branch
репо, который вы хотите включить.git subtree
возможно, не делать то, что вы думаете! Смотрите здесь для более полного решения.Если вы хотите слить
project-a
вproject-b
:Взято из: git, объединить разные репозитории?
Этот метод работал очень хорошо для меня, он короче и, на мой взгляд, намного чище.
Если вы хотите поместить
project-a
в подкаталог, вы можете использоватьgit-filter-repo
(filter-branch
не рекомендуется ). Выполните следующие команды перед командами выше:Пример объединения двух больших репозиториев и помещения одного из них в подкаталог: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Примечание:
--allow-unrelated-histories
параметр существует только с Git> = 2.9. См. Git - Документация по git merge / --allow-unrelated-historyОбновление : добавлено
--tags
в соответствии с предложением @jstadler для сохранения тегов.источник
git mv source-dir/ dest/new-source-dir
git merge
Шаг терпит неудачу здесь сfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
исправляет это, как описано в документации .--allow-unrelated-histories
был введен в git 2.9 . В более ранних версиях это было поведение по умолчанию.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Вот два возможных решения:
подмодули
Либо скопируйте репозиторий A в отдельный каталог в более крупном проекте B, либо (возможно, лучше) клонируйте репозиторий A в подкаталог в проекте B. Затем используйте подмодуль git, чтобы сделать этот репозиторий подмодулем репозитория B.
Это хорошее решение для слабо связанных хранилищ, где развитие в хранилище А продолжается, и основная часть развития является отдельной автономной разработки в области А. Смотрите также SubmoduleSupport и GitSubmoduleTutorial страниц на Git Wiki.
Поддерево слияния
Вы можете объединить хранилище A в подкаталог проекта B, используя стратегию объединения поддеревьев . Это описано в « Слиянии поддеревьев и вас » Маркуса Принца.
(Опция
--allow-unrelated-histories
необходима для Git> = 2.9.0.)Или вы можете использовать инструмент git поддерево ( репозиторий на GitHub ) от apenwarr (Avery Pennarun), о котором было объявлено, например, в его блоге Новая альтернатива подмодулям Git: поддерево git .
Я думаю, что в вашем случае (A должен быть частью более крупного проекта B) правильное решение было бы использовать слияние поддеревьев .
источник
git log dir-B/somefile
не покажет ничего, кроме одного слияния. См . Ответ Грега Хьюгилла на этот важный вопрос.Подмодульный подход хорош, если вы хотите поддерживать проект отдельно. Однако, если вы действительно хотите объединить оба проекта в одном репозитории, вам нужно проделать еще немного работы.
Первым делом можно использовать,
git filter-branch
чтобы переписать имена всего во втором хранилище, чтобы оно находилось в подкаталоге, где вы хотели бы, чтобы они заканчивались. Поэтому вместо тогоfoo.c
,bar.html
вы быprojb/foo.c
иprojb/bar.html
.Затем вы должны быть в состоянии сделать что-то вроде следующего:
git pull
Будет делатьgit fetch
сопровождаемыйgit merge
. Не должно быть никаких конфликтов, если репозиторий, к которому вы обращаетесь, еще не имеетprojb/
каталога.Далее поиск показывает , что нечто подобное было сделано для слияния
gitk
вgit
. Джунио С. Хамано пишет об этом здесь: http://www.mail-archive.com/git@vger.kernel.org/msg03395.htmlисточник
git filter-branch
для достижения этой цели. На странице руководства говорится об обратном: сделать subdir / стать корнем, но не наоборот.git-subtree
это хорошо, но это, вероятно, не тот, который вы хотите.Например, если
projectA
каталог, созданный в B, послеgit subtree
,перечисляет только один коммит: слияние. Коммиты из объединенного проекта предназначены для разных путей, поэтому они не отображаются.
Ответ Грега Хьюгилла наиболее близок, хотя на самом деле он не говорит, как переписать пути.
Решение удивительно простое.
(1) В А,
Примечание: это переписывает историю, поэтому, если вы намереваетесь продолжать использовать этот репозиторий A, вы можете сначала скопировать (скопировать) его одноразовую копию.
Примечание Bene: вам нужно изменить скрипт замены внутри команды sed в случае, если вы используете не-ascii символы (или белые символы) в именах файлов или пути. В этом случае расположение файла внутри записи, создаваемой "ls-files -s", начинается с кавычки.
(2) Затем в Б, запустить
Вуаля! У вас есть
projectA
каталог в B. Если вы запуститеgit log projectA
, вы увидите все коммиты из A.В моем случае я хотел две подкаталоги,
projectA
иprojectB
. В этом случае я сделал шаг (1) для B, а также.источник
"$GIT_INDEX_FILE"
должен быть заключен в кавычки (дважды), иначе ваш метод потерпит неудачу, если, например, путь содержит пробелы.Ctrl-V <tab>
Если оба репозитория имеют одинаковые типы файлов (например, два репозитория Rails для разных проектов), вы можете извлечь данные из вторичного репозитория в ваш текущий репозиторий:
а затем объединить его с текущим хранилищем:
Если ваша версия Git меньше 2.9, удалите
--allow-unrelated-histories
.После этого могут возникнуть конфликты. Вы можете решить их, например, с
git mergetool
.kdiff3
может использоваться исключительно с клавиатурой, поэтому 5 конфликтных файлов занимают при чтении кода всего несколько минут.Не забудьте завершить слияние:
источник
Я продолжал терять историю при использовании слияния, поэтому в итоге я использовал rebase, так как в моем случае два репозитория достаточно различны, чтобы не заканчиваться слиянием при каждом коммите:
=> разрешать конфликты, а затем продолжать столько раз, сколько необходимо ...
Это приводит к тому, что один проект имеет все коммиты от projA, за которыми следуют коммиты от projB
источник
В моем случае у меня был
my-plugin
репозиторий иmain-project
репозиторий, и я хотел сделать вид, чтоmy-plugin
он всегда разрабатывался вplugins
подкаталогеmain-project
.По сути, я переписал историю
my-plugin
репозитория так, чтобы казалось, что вся разработка происходила вplugins/my-plugin
подкаталоге. Затем я добавил историю развитияmy-plugin
вmain-project
историю и объединил два дерева. Посколькуplugins/my-plugin
вmain-project
хранилище еще не было каталога , это было тривиальное слияние без конфликтов. Получившийся репозиторий содержал всю историю обоих исходных проектов и имел два корня.TL; DR
Длинная версия
Сначала создайте копию
my-plugin
хранилища, потому что мы собираемся переписать историю этого хранилища.Теперь перейдите к корню
my-plugin
хранилища, проверьте вашу основную ветку (возможноmaster
) и выполните следующую команду. Конечно, вы должны заменитьmy-plugin
иplugins
какими бы ни были ваши настоящие имена.Теперь для объяснения.
git filter-branch --tree-filter (...) HEAD
запускает(...)
команду при каждом доступном коммитеHEAD
. Обратите внимание, что это работает непосредственно с данными, хранящимися для каждого коммита, поэтому нам не нужно беспокоиться о понятиях «рабочий каталог», «индекс», «подготовка» и так далее.Если вы запустите
filter-branch
команду, которая потерпит неудачу, она оставит позади некоторые файлы в.git
каталоге, и при следующей попыткеfilter-branch
она будет жаловаться на это, если вы не укажете эту-f
опциюfilter-branch
.Что касается самой команды, мне не особо повезло, когда я сделал
bash
то, что хотел, поэтому вместо этого я использую командуzsh -c
makezsh
. Сначала я установилextended_glob
опцию, которая включает^(...)
синтаксис вmv
команде, а такжеglob_dots
опцию, которая позволяет мне выбирать точечные файлы (такие как.gitignore
) с помощью glob (^(...)
).Далее я использую
mkdir -p
команду для создания одновременноplugins
иplugins/my-plugin
одновременно.Наконец, я использую
zsh
функцию «негативный глобус»,^(.git|plugins)
чтобы сопоставить все файлы в корневом каталоге репозитория, кроме.git
и только что созданнойmy-plugin
папки. (Исключение здесь.git
может и не понадобиться, но попытка переместить каталог в себя является ошибкой.)В моем репозитории начальная фиксация не включала никаких файлов, поэтому
mv
команда вернула ошибку при первоначальной фиксации (так как ничего не было доступно для перемещения). Поэтому я добавил|| true
так, чтоgit filter-branch
бы не прерывать.--all
Вариант говоритfilter-branch
переписать историю для всех ветвей в хранилище, а дополнительный--
надо сказать ,git
чтобы интерпретировать его как часть списка опций для филиалов переписывать, а в качестве опцииfilter-branch
себе.Теперь перейдите в свой
main-project
репозиторий и отметьте любую ветку, в которую вы хотите объединиться. Добавьте вашу локальную копиюmy-plugin
хранилища (с измененной историей) в качестве удаленногоmain-project
с:Теперь у вас будет два несвязанных дерева в истории коммитов, которые вы можете визуализировать, используя:
Чтобы объединить их, используйте:
Обратите внимание, что в Git до 2.9.0 эта
--allow-unrelated-histories
опция не существует. Если вы используете одну из этих версий, просто опустите опцию:--allow-unrelated-histories
предупреждающее сообщение об ошибке также было добавлено в 2.9.0.Вы не должны иметь никаких конфликтов слияния. Если вы это сделаете, это, вероятно, означает, что либо
filter-branch
команда работала некорректно, либо в ней уже былplugins/my-plugin
каталогmain-project
.Обязательно введите пояснительное сообщение о коммите для всех будущих участников, задающихся вопросом, что за хакерство собиралось сделать хранилище с двумя корнями.
Вы можете визуализировать новый граф коммитов, который должен иметь два корневых коммита, используя приведенную выше
git log
команду. Обратите внимание, что будет объединена толькоmaster
ветка . Это означает, что если у вас есть важная работа над другимиmy-plugin
ветвями, которые вы хотите объединить вmain-project
дерево, вы должны воздерживаться от удаленияmy-plugin
удаленного, пока вы не выполните эти слияния. Если вы этого не сделаете, то коммиты из этих веток все еще будут вmain-project
репозитории, но некоторые будут недоступны и подвержены возможной сборке мусора. (Кроме того, вам придется ссылаться на них по SHA, потому что удаление удаленного удаляет его ветви удаленного отслеживания.)По желанию, после того, как вы объединили все, что хотите сохранить
my-plugin
, вы можете удалитьmy-plugin
пульт, используя:Теперь вы можете безопасно удалить копию
my-plugin
репозитория, историю которого вы изменили. В моем случае я также добавил уведомление об устаревании в реальныйmy-plugin
репозиторий после того, как слияние было завершено и отправлено.Протестировано на Mac OS X El Capitan с
git --version 2.9.0
иzsh --version 5.2
. Ваш пробег может варьироваться.Ссылки:
источник
--allow-unrelated-histories
?man git-merge
. По умолчанию команда git merge отказывается объединять истории, которые не имеют общего предка. Эта опция может использоваться для отмены этой безопасности при объединении историй двух проектов, которые начали свою жизнь независимо. Поскольку это очень редкий случай, никакая переменная конфигурации, которая бы включала это по умолчанию, не существует и не будет добавлена.git version 2.7.2.windows.1
?Я пытался сделать то же самое в течение нескольких дней, я использую git 2.7.2. Поддерево не сохраняет историю.
Вы можете использовать этот метод, если вы не будете использовать старый проект снова.
Я бы посоветовал вам сначала ветку B и работать в ветке.
Вот шаги без ветвления:
Если вы теперь войдете в любой из файлов в subdir A, вы получите полную историю
Этот пост помог мне сделать это:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
источник
Если вы хотите поместить файлы из ветви в хранилище B в поддерево хранилища A, а также сохранить историю, продолжайте чтение. (В приведенном ниже примере я предполагаю, что мы хотим, чтобы основная ветвь репо B была объединена с основной ветвью репо А.)
В репо А сначала сделайте следующее, чтобы сделать репо В доступным:
Теперь мы создаем совершенно новую ветку (только с одним коммитом) в репо А, который мы называем
new_b_root
. Полученный коммит будет иметь файлы, которые были зафиксированы в первом коммите главной ветви репозитория B, но помещены в подкаталог с именемpath/to/b-files/
.Объяснение:
--orphan
Опция команды checkout извлекает файлы из главной ветки A, но не создает коммит. Мы могли бы выбрать любой коммит, потому что затем мы все равно очищаем все файлы. Затем, еще не фиксируя (-n
), мы выбираем первый коммит из основной ветви B. (Вишневый выбор сохраняет исходное сообщение о фиксации, которое, похоже, не делает прямая проверка.) Затем мы создаем поддерево, в которое мы хотим поместить все файлы из репозитория B. Затем мы должны переместить все файлы, которые были введены в вишня к поддереву. В приведенном выше примере есть толькоREADME
файл для перемещения. Затем мы фиксируем нашу корневую фиксацию B-repo, и в то же время мы также сохраняем временную метку первоначальной фиксации.Теперь мы создадим новую
B/master
ветку поверх вновь созданнойnew_b_root
. Мы называем новый филиалb
:Теперь мы объединяем нашу
b
ветку вA/master
:Наконец, вы можете удалить
B
удаленные и временные ветви:Конечный график будет иметь такую структуру:
источник
Я собрал много информации здесь о Stack OverFlow и т. Д., И мне удалось собрать сценарий, который решает проблему для меня.
Предостережение заключается в том, что он учитывает только ветвь «разработка» каждого репозитория и объединяет ее в отдельный каталог в совершенно новом репозитории.
Теги и другие ветки игнорируются - это может быть не то, что вы хотите.
Скрипт даже обрабатывает ветки и теги объектов - переименовывая их в новом проекте, чтобы вы знали, откуда они пришли.
Вы также можете получить его с http://paste.ubuntu.com/11732805
Сначала создайте файл с URL-адресом для каждого хранилища, например:
Затем вызовите скрипт с указанием имени проекта и пути к скрипту:
Сам сценарий имеет много комментариев, которые должны объяснить, что он делает.
источник
Я знаю, что это долго после факта, но я не был доволен другими ответами, которые я нашел здесь, поэтому я написал это:
источник
if [[ $dirname =~ ^.*\.git$ ]]; then
Если вы пытаетесь просто склеить два репозитория, то подмодули и слияния поддеревьев - это неправильный инструмент, поскольку они не сохраняют всю историю файлов (как люди отмечали в других ответах). Смотрите этот ответ здесь для простого и правильного способа сделать это.
источник
У меня была похожая проблема, но в моем случае мы разработали одну версию кодовой базы в репо A, а затем клонировали ее в новый репо B для новой версии продукта. После исправления некоторых ошибок в репо А нам нужно было оформить изменения в репо Б. Закончилось следующим:
Работал лакомством :)
источник
Аналогично @Smar, но использует пути файловой системы, заданные в PRIMARY и SECONDARY:
Затем вы вручную сливаете.
(взято из поста Анара Манафова )
источник
Слияние 2 репо
источник
Если вы хотите объединить три или более проектов в один коммит, выполните шаги, описанные в других ответах (
remote add -f
,merge
). Затем (мягкая) сбросить индекс на старую голову (где слияние не произошло). Добавить все файлы (git add -A
) и зафиксируйте их (сообщение «Объединение проектов A, B, C и D в один проект). Теперь это идентификатор фиксации master.Теперь создайте
.git/info/grafts
со следующим содержанием:Беги
git filter-branch -- head^..head head^2..head head^3..head
. Если у вас более трех веток, просто добавьте столько,head^n..head
сколько у вас есть ветвей. Чтобы обновить теги, добавьте--tag-name-filter cat
. Не всегда добавляйте это, потому что это может вызвать переписывание некоторых коммитов. Для получения дополнительной информации см. Справочную страницу filter-branch , ищите «grafts».Теперь с вашим последним коммитом связаны правильные родители.
источник
Чтобы объединить А в Б:
1) В проекте А
2) В проекте Б
В этой ветке делайте все необходимые операции и фиксируйте их.
C) Затем вернемся к мастеру и классическому слиянию двух ветвей:
источник
Эта функция клонирует удаленное репо в локальный каталог репо, после объединения все коммиты будут сохранены,
git log
будут показаны исходные коммиты и правильные пути:Как пользоваться:
Если вы сделаете небольшие изменения, вы даже можете переместить файлы / каталоги объединенного репо по разным путям, например:
Notices
Paths заменяется на via
sed
, поэтому после слияния убедитесь, что он перемещен по правильным путям. Параметр существует только с Git> = 2.9.--allow-unrelated-histories
источник
Данная команда - лучшее возможное решение, которое я предлагаю.
источник
Я слегка объединяю проекты вручную, что позволяет мне избежать необходимости сталкиваться с конфликтами слияния.
во-первых, скопируйте файлы из другого проекта, как хотите.
следующий шаг в истории
скажи git слиться с историей последней полученной вещи
Теперь совершите, однако вы обычно делаете
источник
Я хотел переместить небольшой проект в подкаталог большего. Поскольку мой небольшой проект не имел много коммитов, я использовал
git format-patch --output-directory /path/to/patch-dir
. Тогда на более крупном проекте я использовалgit am --directory=dir/in/project /path/to/patch-dir/*
.Это чувствует себя способ менее страшно и способ более чище , чем фильтр-отрасли. Конечно, это может быть применимо не ко всем случаям.
источник