Что означает ошибка "refusing to merge unrelated histories"

Источник: «What is refusing to merge unrelated histories Error?»
Рассмотрим как решить распространённую ошибку Git: "refusing to merge unrelated histories" при объединении репозиториев с несвязанными историями.

Введение

TL;DR: Решение проблемы "Refusing to Merge Unrelated Histories" в Git

Ошибка Git "refusing to merge unrelated histories" возникает, когда Git пытается объединить ветки или репозитории без общей истории коммитов. Это может быть случай, когда кто-то пытается объединить два независимых репозитория, объединить два проекта или даже синхронизировать локальный проект с новым удалённым репозиторием.

Если у вас нет времени читать полную статью, вот краткие решения для устранения ошибки:

Основные решения:

Давайте рассмотрим подробности, поскольку я вкратце затронул эту тему выше: Работая с Git'ом на протяжении многих лет, я могу подтвердить наличие ряда интересных проблем; одна из них, которая регулярно застаёт разработчиков врасплох, — это печально известная ошибка "refusing to merge unrelated histories".

Обычно это происходит, когда команды пытаются объединить репозитории или ветки, которые, по мнению Git, не имеют общего происхождения.

Я расскажу, почему это происходит и как правильно с этим справиться.

Понимание ошибки

Обычно ошибка выглядит следующим образом:

git merge origin/main
fatal: refusing to merge unrelated histories

Это не просто придирки Git'а, он на самом деле пытается помочь. Ошибка возникает, когда Git обнаруживает, что у двух веток или репозиториев, которые вы пытаетесь объединить, нет общей истории коммитов. Это может произойти при нескольких распространённых обстоятельствах:

  1. Когда вы пытаетесь объединить два независимо созданных репозитория
  2. Создание нового репозитория с README на GitHub и попытка запушить существующий локальный проект

Распространённые сценарии возникновения ошибки

Хочу поделиться наиболее частыми сценариями возникновения этой ошибки:

Новый удалённый репозиторий с существующим локальным проектом

Когда вы пытаетесь подтянуть из только что созданного удалённого репозитория в существующий локальный проект, Git будет жаловаться на несвязанные истории:

hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
fatal: Need to specify how to reconcile divergent branches.

Это происходит потому, что вы инициализировали локальный репозиторий, а затем удалённый по отдельности. У них обоих есть свои собственные деревья истории.

Комбинирование двух независимых проектов

Когда вы пытаетесь объединить два проекта, начатые по отдельности, Git предотвратит слияние, чтобы вы случайно не объединили несвязанные кодовые базы:

# В основной директории проекта
git remote add other-project./path/to/other-project
git fetch other-project
git merge other-project/main # Ошибки с несвязанными историями

Как решить эту ошибку

Справиться с этим можно разными способами, в зависимости от ваших требований:

Использование --allow-unrelated-histories

Самый простое решение — использовать флаг --allow-unrelated-histories. Вот что происходит при его использовании:

$ git merge origin/main --allow-unrelated-histories
Auto-merging README.md
CONFLICT (add/add): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

Как видите, Git пытается объединить обе истории вместе и автоматически разрешает некоторые конфликты. После разрешения всех конфликтов видно, что слияние прошло успешно:

$ git commit -m "Merge remote repository with local, resolving conflicts"
[main 62c10f7] Merge remote repository with local, resolving conflicts
$ git status
On branch main
nothing to commit, working tree clean

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

Начните с чистого листа (чистый подход)

Иногда лучше начать с чистого листа:

# Резервное копирование текущей работы
cp -r project project_backup

# Начинаем с чистого листа
git clone git@github.com:username/repo.git
cd repo
# Копируем файлы (кроме каталога .git)
cp -r ../project_backup/* .
git add .
git commit -m "Integrate existing project"
git push origin main

Переписывание истории (продвинутый уровень)

В более сложных случаях может понадобиться переписать историю:

# Создание ветки сироты
git checkout --orphan temp_branch
# Добавление всех файлов
git add .
# Создание начального коммита
git commit -m "Initial commit"
# Удаление ветки main
git branch -D main
# Переименование текущей ветки в main
git branch -m main
# Принудительный push в remote
git push -f origin main

Рекомендации/лучшие практики обработки несвязанных историй

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

Когда использовать каждый из подходов

Мои рекомендации по выбору правильного подхода:

Используйте --allow-unrelated-histories, когда:

Начните с чистого листа, когда:

Переписывайте историю, когда:

Заключение

Хотя ошибка "refusing to merge unrelated histories" раздражает, на самом деле это Git пытается уберечь вас от потенциально проблемных слияний. Понимание причин возникновения этой ошибки и знание подходящих решений позволит уверенно справляться с подобными ситуациями и выбирать лучший подход для конкретного случая.

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

Комментарии


Дополнительные материалы

Предыдущая Статья

Доступ к связанным с маршрутом моделям в запросах формы Laravel с #[RouteParameter]

Следующая Статья

Пять примеров использования defer() в Laravel