Как исправить историю в неправильном слиянии
У меня есть проблема в Git, которую я не могу исправить. Я искал похожие случаи, но мне не удалось найти хорошее решение для моей ситуации.
У меня в основном 2 ветки, master и bugfix. Bugfix происходит от master в тот момент, когда мы сделали релиз. Каждый раз, когда мы исправляем что-то в bugfix и делаем релиз bugfix, нам нужно объединить это в master. Мастер содержит новые функции (и, конечно, никогда не должен быть объединен с исправлением ошибок). Так получилось, что кто-то по ошибке слил master с bugfix.
This is what happened:
master: x1---x2
\
bugfix: y1--y2--M
where M is the merge between bugfix and master
Решением для этого было отменить слияние, которое я сделал с помощью "git revert -m 1 M" (здесь M - это SHA от слияния), как описано здесь. Я сделал возврат, потому что у нас уже были другие изменения в ветке исправлений, которые мы не хотели терять, и это работало нормально для ветки исправлений.
Current state:
master: x1--x2------------x---x
bugfix: y1--x1--y2--x2--M--y--y--R1
where R1 is the revert of M
Проблема появляется позже, когда нам нужно объединить ветку исправления ошибок в master. Как описано в ссылке, которую я разместил, возврат исправляет изменения кода, но не историю, это означает, что когда я объединяю исправление в мастере, восстановление вступает в силу и удаляет изменения из мастера (x1 и x2). Что я могу сделать, так это отменить возврат в багфиксе, прежде чем я объединю его с мастером.
Solution:
master: x1--x2------------x---x--------M
/
bugfix: y1--x1--y2--x2--M--y--y--R1--R2
where R2 is the revert of R1
Это работает, но не является окончательным решением, потому что каждый раз, когда мы выполняем слияние, нам нужно будет делать "возврат к возврату". Я искал постоянное решение для этого в течение нескольких дней, и кажется, что единственным вариантом было бы удалить основные коммиты (x1 и x2) из истории ветки исправлений. Но x1 и x2 используются здесь только в качестве примера, в действительности у нас есть гораздо больше коммитов, и анализ истории ветки исправлений очень труден и подвержен ошибкам. Я уверен, что Git может помочь мне сделать это, но я не знаю как.
Любые идеи будут очень полезны.
3 ответа
Я бы лично рекомендовал переписать историю (см. Ответ " Бесполезно") и рассказать вашей команде. Но это требует от каждого определенного уровня знаний. Если подход переписывания слишком рискован для вас, вот альтернатива:
Что происходит
Всякий раз, когда вы сливаетесь A
в B
, git добавит все коммиты / изменения, которые доступны из A
но нет B
в B
, Ваш возврат является одним из них. Как вы заметили, без переписывания ничего не поделаешь.
Решение
Вам придется слить возвращение в мастер. Если вы хотите сохранить материал, который вы вернули, в мастере, вам также нужно отменить этот возврат в мастере (= достижимый от мастера). Лучший способ добиться этого с помощью этих команд:
git checkout -b bugfix-revert bugfix
git revert R1
git checkout master
git merge bugfix-revert
git branch -d bugfix-revert
После этих команд каждая ветвь будет в том состоянии, в котором вы хотите, и теперь вы можете снова объединить исправление ошибок. Ваша история будет выглядеть так:
[master] x1---x2----------------------H
\ /
[bugfix-revert] \ M''
\ /
[bugfix] y1--y2----M--y3--y4--M'--y5
Как вы видете, master
содержит M'
, но его изменения не в H
, так как M''
отменить те. M'
никогда не будет применяться к master
потому что это уже содержит это. Штат bugfix
не изменился и не должен будет.
Почему бы просто не создать новую ветку с исправлениями ошибок без слияния? Вам нужно будет координировать переключение веток со всеми, чтобы не потерять коммиты
Так что у тебя есть:
[master] x1---x2
\
[bugfix] y1--y2--M--y3--y4--M'
(где M'
это возвращение M
). Но вы можете создать новую ветку:
$ git branch bugfix-fix y4
[master] x1---x2
\
[bugfix] y1--y2--M--y3--y4--M'
^^
[bugfix-fix]
отделить его от проблемного слияния и возврата
$ git rebase --onto y2 M bugfix-fix
[master] x1---x2
\
[bugfix] y1--y2--M--y3--y4--M'
\
[bugfix-fix] y3'--y4'
а затем, как только вы проверили все, что вам нужно, на bugfix-fix
, вы можете просто переименовать обе ветви
$ git branch -m bugfix bugfix-broken
$ git branch -m bugfix-fix bugfix
(вам нужно принудительно нажимать на это, что безопасно, если вы координируете свои действия со всеми, кто мог бы это проверить).
Ваше решение правильное... Вам нужно "отменить возврат", ТО затем выполнить еще одно слияние с вашей веткой исправления ошибок (чтобы извлечь любые исправления исправлений, которые были сделаны после первого неверного объединения), как вы показали. Это восстановит исправления из ваших коммитов y1 и y2, сохранит правильность вашей истории и внесет обновленные изменения, выполненные после первой ошибочной точки слияния.
Вам не нужно беспокоиться об этом при будущих слияниях, так как последняя точка слияния - это место в истории, из которого git будет пытаться выполнить слияние, а не что-либо до него (по той же самой точной причине, по которой вам нужно выполнить возврат к возврату). ")...
Не знаю, почему вы говорите, что это оставляет исправление в неисправном состоянии. Следующее работает просто отлично. При необходимости вы можете продолжить работу над веткой исправления ошибок, не беспокоясь о необходимости что-либо возвращать (если вы, очевидно, не сделаете то же самое снова...). Если вам понадобится снова объединиться с master в будущем, вы можете без проблем (не нужно делать ничего особенного), поскольку git знает историю и будет искать проблемы / конфликты слияния только с той точки, в которой две ветви были последними общими (это последнее слияние).
https://github.com/g19fanatic/stackru-16713251-496405
Вот ссылка с Линусом, описывающая эту проблему и представляющую предложенное мной выше решение как один из возможных способов ее устранения...