lenec ru

← все посты

fatal: refusing to merge unrelated histories — что делать

18K

Ставишь git pull, а в ответ:

fatal: refusing to merge unrelated histories

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

В моей практике эта ошибка вылезает в трёх ситуациях:

  • создала локальный репо и пытаюсь подцепить его к уже существующему удалённому;
  • удалённый репо был случайно перезаписан или пересоздан;
  • сделала git init внутри проекта, в котором уже был .git, и теперь история сбита.

Что под капотом

Git хранит историю как граф коммитов. Когда ты делаешь git pull (=fetch + merge), он ищет общего предка между твоей текущей веткой и тем, что прилетело из удалённого. Если общего предка нет — смерджить нельзя по обычной логике fast-forward или recursive. Раньше git тихо в таких случаях создавал общий merge-коммит, но с версии 2.9 это поведение запрещено по умолчанию для безопасности.

Самое быстрое решение

Если ты уверена, что хочешь именно слить эти два дерева в одно (например, есть локальная папка с проектом и пустой репо в GitHub):

git pull origin main --allow-unrelated-histories

Флаг --allow-unrelated-histories разрешает то, чего git боится: сделать merge-коммит, у которого два родителя без общей точки.

Дальше скорее всего получишь конфликты — git попытается совместить файлы. Особенно весело, если в обоих ветках есть README с разным содержимым. Решаешь конфликты, коммитишь — всё.

Когда это нормально, а когда — звоночек

Нормально: первый push локального проекта в новый удалённый

Сценарий:

cd ~/projects/site
git init
git add .
git commit -m "initial"
git remote add origin git@github.com:me/site.git
git pull origin main

На GitHub при создании репо ты могла поставить галочку «Initialize with README» — тогда там уже есть один коммит. У тебя локально — свой коммит. Общего предка нет.

Тут смысл есть, и --allow-unrelated-histories работает корректно.

Опасно: репо пересоздан

Сценарий: команда у вас работает с репо, кто-то удалил его и создал заново. Теперь у тебя локально твоя история на 200 коммитов, а в origin — пара новых коммитов от того, кто пересоздал. Git ругается.

В этом случае --allow-unrelated-histories склеит две истории, но это вряд ли то, что нужно. Скорее всего, надо разобраться, что произошло, и решить, какая история правильная. Иначе в репозиторий попадут «два первых коммита», и логи разъедутся.

Опасно: ты сделала git init поверх существующего репо

Если зашла в каталог, где уже был .git, и зачем-то сделала git init, ты не сломала старую историю — она лежит там же. Но если ты ещё и закоммитила сверху — у тебя теперь смешение. Лекарство — посмотреть на git log и git reflog, найти, где история разошлась, и вернуться куда надо. Иногда проще удалить .git и заново склонировать репо в чистую папку.

Альтернативное решение: rebase вместо merge

Если хочется, чтобы история не была разветвлённой, а лежала ровной линией, можно использовать rebase:

git pull --rebase origin main

На неподходящих историях rebase тоже потребует --allow-unrelated-histories через переменные:

git pull --rebase=true --allow-unrelated-histories origin main

Точнее, в современных git rebase прокинет ту же логику. Я обычно делаю проще — тяну с merge и принимаю один merge-коммит, потом следующая работа идёт обычным образом.

Что делать, если в локальной ветке ничего не нужно

Бывает: ты сделала git init в свежей папке, наделала пару пробных коммитов, потом подключила к существующему репо и поняла — твои локальные коммиты ничего ценного не несут. Тогда проще их выкинуть:

git fetch origin
git reset --hard origin/main

Локальная история стирается, рабочая копия становится точно такой же, как на сервере. Файлы из локальной папки, которых нет в коммите, остаются (если они не отслеживались).

Внимание: это разрушительная операция. Если у тебя там были изменения, которые не закоммичены, — сначала git stash.

Чтобы не попадать снова

Привычки, которые помогают:

  • Не делать git init в каталогах, в которых уже что-то есть, без проверки .git.
  • При создании удалённого репо не ставить галочку «Initialize with README», если планируешь сразу залить туда локальный проект. Создавай пустой, тогда первый push пройдёт чисто.
  • Если работаешь в команде — никогда не пересоздавай удалённые репозитории «чтоб как с нуля». Используй ветки.

Простой способ запомнить логику

Git отказывается смерджить, когда не уверен, что эти две истории стоит сливать. Флаг --allow-unrelated-histories — это твоё подтверждение «я знаю, что делаю, склей их».

Применяй его осознанно. Если делаешь первый push свежего проекта — спокойно. Если получил эту ошибку при обычной командной работе с привычным репо — лучше сначала разобраться, что произошло, чтобы не залить в основной репо мусор от случайной перезаписи истории.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

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