Я недавно перешел на новый компьютер, куда скопировал все файлы со своего старого. Короче говоря, я допустил несколько ошибок при правильной настройке удаленных репозиториев git и GitHub. Я думаю, что это началось с невозможности повторно подключиться к GitHub, и в итоге я удалил свою старую папку .git
(вероятно, не самый мудрый шаг).
У меня есть две ветки master и apprentice, где я использую ветку apprentice для опробования вещей, а затем сливаюсь с master, когда получаю работающий код.
На своем новом компьютере я начал работать локально и перешел в локальную ветку ученика. Затем я попытался нажать на удаленную ветку, но это не сработало. Затем я принудительно нажал и в итоге перезаписал свою историю коммитов на ученике, фактически потеряв все коммиты в этой ветке до переключения компьютера. Это отстой, но я не видел способа исправить ущерб, и это не было ужасно важно, поскольку у меня был самый последний код. Поэтому я продолжал работать и делать коммиты для ученика.
Моя текущая проблема связана с попыткой объединить изменения в ученике с мастером.
Поскольку вся история коммитов в 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 по-прежнему говорит мне, что мастер и ученик — это совершенно разные истории коммитов.
Это приводит меня к следующим вопросам:
--allow-unrelated-histories
) при последующих слияниях двух ветвей?В общем, если вам нужно раздавить, сделайте это в своей исходной ветке перед слиянием, таким образом вы получите обычное слияние, и вы сможете продолжать использовать исходную ветку в будущем.
Ага, тогда я понимаю. Я сделаю так. Добавьте свой комментарий в качестве ответа, и я отмечу его как решенный.
Я переписал свои комментарии как полный ответ.
По сути, у вас была такая ситуация из-за перезапуска ветки на новом компьютере:
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
и довольно низкоуровневое.
Если вы сжимаете как часть слияния, вы делаете сжатые копии коммитов, которые затем добавляются к целевой ветке, но вы не получаете фиксацию слияния. Исходная ветвь остается нетронутой со всеми исходными, не раздавленными коммитами. После выполнения сквош-слияния вы должны воссоздать свою ветку ученика поверх ветки мастера. Вам определенно не следует продолжать использовать исходную ветку, так как это определенно будет давать вам эту ситуацию впереди + позади каждый раз, когда вы захотите выполнить слияние.