Ошибка слияния Git: «фатальная: отказ от слияния несвязанных историй» после слияния с флагом «--allow-unrelated-histories», разрешение конфликтов вручную

1. Предыстория

Я недавно перешел на новый компьютер, куда скопировал все файлы со своего старого. Короче говоря, я допустил несколько ошибок при правильной настройке удаленных репозиториев git и GitHub. Я думаю, что это началось с невозможности повторно подключиться к GitHub, и в итоге я удалил свою старую папку .git (вероятно, не самый мудрый шаг).

У меня есть две ветки master и apprentice, где я использую ветку apprentice для опробования вещей, а затем сливаюсь с master, когда получаю работающий код.

На своем новом компьютере я начал работать локально и перешел в локальную ветку ученика. Затем я попытался нажать на удаленную ветку, но это не сработало. Затем я принудительно нажал и в итоге перезаписал свою историю коммитов на ученике, фактически потеряв все коммиты в этой ветке до переключения компьютера. Это отстой, но я не видел способа исправить ущерб, и это не было ужасно важно, поскольку у меня был самый последний код. Поэтому я продолжал работать и делать коммиты для ученика.

2. Проблема

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

Поскольку вся история коммитов в apprentice была эффективно переписана, простое слияние не сработало, поэтому я попытался слить с помощью флага --allow-unrelated-histories. Это позволило продолжить слияние после ручного разрешения всех конфликтов слияния. Затем я смог завершить фиксацию и отправить ее на удаленный сервер, а в удаленной основной ветке теперь были добавлены изменения от ученика. (Разрешение конфликтов слияния, фиксация и отправка были выполнены в VS Code.)

Выход терминала:
% git checkout master
% git merge apprentice                       
fatal: refusing to merge unrelated histories

% git merge --squash --allow-unrelated-histories apprentice
Auto-merging .Rprofile
CONFLICT (add/add): Merge conflict in <myfile_1>
CONFLICT (add/add): Merge conflict in <myfile_2>
⋮
CONFLICT (add/add): Merge conflict in <myfile_n>
Automatic merge failed; fix conflicts and then commit the result.

После слияния и отправки я обновил все удаленные репозитории.

git push --all

Однако GitHub сообщает мне, что ученик «[все еще] на 6 коммитов впереди, на 19 коммитов позади мастера».*

Глядя на то, в чем эти различия, это говорит мне только о том, что сравнивать нечего, потому что ученик и мастер - это совершенно разные истории коммитов.

Поэтому я попытался снова объединиться (на этот раз нет --squash):

% git merge --no-ff -X theirs master
fatal: refusing to merge unrelated histories

% git merge --allow-unrelated-histories apprentice
Auto-merging .Rprofile
CONFLICT (add/add): Merge conflict in <myfile_1>
CONFLICT (add/add): Merge conflict in <myfile_2>
⋮
CONFLICT (add/add): Merge conflict in <myfile_n>
Automatic merge failed; fix conflicts and then commit the result.

Как и раньше, я приступил к разрешению конфликтов вручную в VS Code, зафиксировал изменения и отправил их на удаленный сервер. Это привело к тому, что коммиты на ученике были добавлены поверх предыдущего раздавленного коммита. Тем не менее, GitHub по-прежнему говорит мне, что мастер и ученик — это совершенно разные истории коммитов.

3. Вопросы

Это приводит меня к следующим вопросам:

  1. Почему не разрешаются истории коммитов, когда конфликты слияния были разрешены и слияние завершено? При условии, что я уже завершил слияние, разрешив любые конфликты вручную, я бы подумал, что эта ошибка больше не должна возникать, что приводит меня ко второму вопросу:
  2. Как я могу воссоздать историю коммитов, чтобы я мог продолжить слияние «обычно» (т.е. без флага --allow-unrelated-histories) при последующих слияниях двух ветвей?

Если вы сжимаете как часть слияния, вы делаете сжатые копии коммитов, которые затем добавляются к целевой ветке, но вы не получаете фиксацию слияния. Исходная ветвь остается нетронутой со всеми исходными, не раздавленными коммитами. После выполнения сквош-слияния вы должны воссоздать свою ветку ученика поверх ветки мастера. Вам определенно не следует продолжать использовать исходную ветку, так как это определенно будет давать вам эту ситуацию впереди + позади каждый раз, когда вы захотите выполнить слияние.

Lasse V. Karlsen 14.04.2023 12:25

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

Lasse V. Karlsen 14.04.2023 12:26

Ага, тогда я понимаю. Я сделаю так. Добавьте свой комментарий в качестве ответа, и я отмечу его как решенный.

Pål Bjartan 14.04.2023 12:43

Я переписал свои комментарии как полный ответ.

Lasse V. Karlsen 14.04.2023 12:54
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По сути, у вас была такая ситуация из-за перезапуска ветки на новом компьютере:

             master
               v
O--O--O--O--O--O


   O--O--O--O--O
               ^
           apprentice

Если бы вы сделали обычное слияние с --allow-unrelated-history, у вас была бы такая ситуация:

                master
                  v
O--O--O--O--O--O--M
                 /
                /
   O--O--O--O--O
               ^
           apprentice

Это было бы хорошо, и любая дальнейшая работа над apprentice могла бы быть объединена поверх master, и git справился бы с этим, как вы ожидаете, только пытаясь объединить изменения, внесенные новыми коммитами.

Однако, поскольку вы сделали сквош, вместо этого у вас есть такая ситуация:

                master
                  v
O--O--O--O--O--O--S


   O--O--O--O--O
               ^
           apprentice

S здесь представляет одну или несколько коммитов сквоша, которые были созданы путем сквоша коммитов из apprentice. Обратите внимание на одну вещь: коммит слияния не задействован. Таким образом, git не хранит никакой информации о взаимосвязи между apprentice и master, и в следующий раз, когда вы попытаетесь выполнить еще одно слияние между ними, все коммиты снова будут в игре, воспроизводя все те конфликты слияния, которые вы разрешили во время сквоша. + объединить.

Если вы намерены раздавить коммиты при слиянии, я бы посоветовал использовать один из следующих двух методов:

  • Сожмите коммиты или иным образом очистите историю на apprentice перед слиянием, таким образом обе ветки будут иметь сжатые коммиты, и может быть создан правильный коммит слияния.

                    master
                      v
    O--O--O--O--O--O--M
                     /
                    /
                   S
                   ^
               apprentice
    
  • После выполнения сквош+слияния перетащите и воссоздайте свою ветку apprentice поверх мастера.

                    master
                      v
    O--O--O--O--O--O--S
                      ^
                  apprentice
    
       O--O--O--O--O
                   ^
               (no reference, will be garbage collected)
    

Третий вариант, который я не представил, который вы, возможно, хотели бы иметь, сохранить исходную ветку нераздавленной, чтобы сохранить историю, но сжатие + слияние поверх мастера нецелесообразно, так как git будет отображать всю историю при каждой попытке слияния. . Вместо этого вам придется начать использовать вишневый выбор и тому подобное, чтобы каждый раз делать копии «новых» коммитов, и хлопоты того не стоят.


Для полноты есть способ записать фиксацию слияния без использования обычной команды слияния с git, по сути записывая эту связь. Однако это включает в себя явное указание git, как все связано, и как игнорировать двойные изменения, опять же, хлопоты того не стоят (на мой взгляд), поэтому я не буду подробно останавливаться на этом здесь. Достаточно сказать, что это включает в себя git commit-tree и довольно низкоуровневое.

Другие вопросы по теме