Существует два основных способа исправить такую ошибку:
Один из способов - переписать историю.Обычно это то, что хочет тот, кто совершил ошибку, потому что она почти полностью устраняет ошибку.Но это не всегда выполнимо, а иногда это просто не разрешено.
Другие варианты - принять ошибку как часть истории и добавить исправление также в историю.
Естьпо крайней мере пару способов сделать каждый из них.
Перезапись истории
Похоже, вы знаете подходящий способ выполнить перезапись истории, но этоне работает из-за шага push -f
.Если вы управляете удаленным репо, вы можете изменить разрешения / параметры защиты, чтобы разрешить принудительное принудительное продвижение к мастеру, но это не всегда хорошая идея по двум причинам:
Во-первых, защита главной ветви - этовероятно, хорошая идея большую часть времени, даже если не в этом конкретном случае.Конечно, можно снять защиту, переписать, а затем повторно защитить ее, если бы это было единственной проблемой.
Вторая возможная проблема заключается в том, если другие клоны уже включили ошибочные коммиты в master
, затем принудительное нажатие, чтобы удалить эти коммиты из истории master
, «сломает» эти клоны, и им придется предпринять шаги для восстановления.Если они выполняют неправильные шаги, они могут легко повторно ввести удаленные коммиты, и это особенно важно, если у них есть какая-либо новая работа, основанная на коммитах, которые вы бы отменили.См. git rebase
документы в разделе «Восстановление из исходной версии»;хотя то, что вы делаете, само по себе не является перебазированием, применима та же ситуация.
Так что вполне возможно, что вам нужно решение, которое не переписывает историю, и на этом я остановлюсьна время.
Добавление исправления
В настоящее время ваш репо может выглядеть как
... x <--(tony_branch)
\
A -- B -- C <--(master)(origin/master)
Теперь вам нужнопереместить origin/master
в x
;это сделало бы "самую чистую" историю.(И, если мы будем честными, это позволит избежать постоянного учета промахов. Это не означает, что у нас есть какие-либо причины вести учет ошибок; только то, что мы хотим избавиться от такогозапись может казаться более важной, чем она - , взвешенная с учетом стоимости переписывания истории.)
Но вы читали это далеко, потому что очевидно переписываете origin/master
История России не является приемлемым вариантом.Поэтому обычно люди предлагают использовать git revert
.Это приведет к чему-то вроде
... x <--(tony_branch)
\
A -- B -- C -- ~CBA <--(master)(origin/master)
, где ~CBA
отменяет изменения с A
, B
и C
, оставляя содержимое (объект TREE
) на master
простокак то, что было на x
.Это один из вариантов;это просто и оставляет вещи в прямом состоянии для любых будущих команд git.
Другой вариант - использовать коммит слияния для возврата.Это делает A
, B
и C
похожими на боковую ветвь, которая была оставлена.«Верх» состоит в том, что немного легче понять, что A
.. C
можно / нужно игнорировать; «недостатком» является то, что рассматриваемое слияние будет немного неестественным - то, что иногда называют"злое слияние".Если вы, вероятно, перебазируете этот раздел коммитов, тогда может возникнуть проблема «злого слияния», но опять же, поскольку мы идем туда, потому что переписывание истории master
не является чем-то, возможно, это не имеет значения,В любом случае, это выглядело бы как
... x <--(tony_branch)
|\
| \---------- Y <--(master)(origin/master)
\ /
A -- B -- C
Чтобы сделать это, вы могли бы сказать
git checkout master
git reset --hard tony_branch
git merge -s ours origin/master
git push
Обратите внимание, что при любом подходе коммиты с A
по C
включаются в историю master
.Это означает, что больше невозможно «повторно применить» изменения вашей ветви, объединив эти коммиты в master - потому что они «уже есть».Чтобы это исправить, вы можете создать копии коммитов A
- C
и поместить эти копии в tony_branch
.Это можно сделать до или после обновления master
(путем возврата или слияния);раньше это могло бы быть немного проще, но я не включил туда инструкции, потому что было проще объяснить почему здесь.
Так что вы бы сделали "принудительную перезагрузку" из A
до C
.Вопреки распространенному мнению, rebase
не «заменяет» исходные коммиты, а скорее копирует их.Принудительная операция просто означает «сделать копии, даже если я не изменяю основание или что-то еще».Первый шаг - проверить C
.Как это сделать, зависит от того, какие другие шаги вы предприняли:
(A) После применения слияния к master
вы могли бы
git checkout master^2
, что ставит вас в отдельное состояние HEAD
укажите второго родителя коммита слияния в master
.ИЛИ
(B) После применения отмены вы можете
git checkout master^
(если возврат был применен за один коммит; это было бы что-то вроде master~3
, если отдельные изменения отката были зафиксированыбыли использованы, заменив 3
тем не менее, было использовано много фактических возвращаемых коммитов).ИЛИ
(C) До исправления master
, вы можете просто
git checkout --detach master
, но учтите, что do хотите оказаться всостояние «обособленная ГОЛОВА», чтобы избежать неправильного перемещения ветки master
;следовательно, опция --detach
.
Теперь, проверив C
, вы можете
git rebase -f tony_branch
git branch -f tony_branch
, и вы получите что-то вроде
A' -- B' -- C' <--(tony_branch)
/
... x ----------- Y <--(master)(origin/master)
\ /
A -- B -- C