Вы не можете это исправить, не устроив беспорядок, потому что беспорядок уже создан. Последний отказоустойчивый, прежде чем у вас есть беспорядок, это толчок к общему пульту.
Так что вопрос в том, как лучше всего навести порядок. Есть два варианта.
Один из вариантов - переписать историю. Это будет иметь «самый чистый» конечный результат, но требует координации с каждым пользователем репо. Если вы не можете координировать свою деятельность со всеми, у кого может быть клон репо, то вы не можете безопасно переписать историю (и если вы попытаетесь сделать это, она все равно может быть отменена).
Другой вариант - добавить новые коммиты, чтобы добраться до желаемого состояния, оставив «грязную» историю позади. Это не очень хорошо, но для общих репозиториев, как правило, они «разбираются» без больших усилий по координации, в отличие от переписывания истории.
Переписать историю
«Переписывание истории» - это любая операция, которая удаляет коммиты из истории ветки. В вашем случае вы имели
O -- x -- x <--(master)(origin/master)
\
A -- B -- C <--(branch)
и вы случайно нажали branch
на origin/master
, что дало вам
O -- x -- x <--(master)
\
A -- B -- C <--(branch)(origin/master)
Что касается пульта дистанционного управления, A
, B
и C
теперь являются частью master
, поэтому для отмены можно удалить A
, B
и C
из оригинала master
- но это переписать историю.
Перезапись дает самый чистый результат, но это действительно только подходящее решение, если (а) вы один используете пульт, или (б) пульт совместно используется небольшой (или, самое большее, среднего размера) группой, так что вы может разумно координировать свои действия со всеми членами команды.
Проблема с переписыванием истории заключается в том, что она вызывает неожиданное перемещение ветки; и если бы git относился к этому виду движения как к рутинному, это привело бы к потере параллельных изменений - противоположность того, для чего предназначены ветвления и слияния.
Вы можете сделать переписывание истории локально несколькими способами - но в этом случае вам это не нужно, потому что ваши локальные ветви уже находятся в правильном состоянии. Но чтобы пульт дистанционного управления переписывал историю его ветки, вам нужно сделать «принудительный толчок». Вы можете или не можете сделать это в зависимости от того, как настроено репо origin
.
Если вам разрешено это сделать, то первым шагом является информирование всех пользователей репо о том, что будет происходить перезапись истории (предпочтительно с объяснением того, что произошло, зачем нужна перезапись и когда должно случиться). Это связано с тем, что после того, как вы нажмете переписать, любой, кто вытащил какой-либо из A
, B
и C
в свою копию master
, будет в "сломанном" состоянии и должен будет выполнить локальную очистку. процедура.
Подробнее о проблеме и о том, как ее устранить, вы можете прочитать в git rebase
документах в разделе «восстановление после исходной перезагрузки». (Это задокументировано в rebase
, но применяется к любой удаленной перезаписи истории.) Обратите внимание, что любой пользователь репозитория может выполнить свою очистку неправильно, увековечив проблему и потенциально отменив переписывание.
Как только у вас будет все на борту, вы будете
git checkout master
git push --force-with-lease origin master
Пока никто не выдвинул другие изменения в origin/master
поверх C
, это должно переместить origin/master
туда, где он должен, и другие пользователи могут начать при необходимости очищать свое локальное состояние.
Обратите внимание, что --force-with-lease
заменяет более старую опцию --force
(или -f
), которая менее безопасна в том смысле, что она будет беззвучно блокировать любые коммиты, которые были помещены поверх C
. --force-with-lease
будет прервана в этом случае, и это будет еще одной причиной не делать переписывание истории (или дополнительную работу, которую нужно выполнить, если вы продолжите переписывать).
Без перезаписи истории
Если по какой-либо причине переписывание истории не подходит, или если вы просто предпочитаете менее разрушительное решение и можете жить с «грязной» историей, вы должны сделать что-то подобное. Снова у вас есть
O -- x -- x <--(master)
\
A -- B -- C <--(branch)(origin/master)(origin/branch)
Нам нужен новый коммит, который приведёт содержимое origin/master
в соответствие с master
. Для этого мы можем использовать git revert
git checkout origin/master
git checkout -b temp
git revert -n master..branch
git commit
Это дает вам
O -- x -- x <-(master)
\
A -- B -- C <--(branch)(origin/master)(origin/branch)
\
~CBA <--(temp)
и с помощью git diff
вы можете проверить, что temp
соответствует master
. В конечном итоге мы переместим master
на temp
, но прежде чем мы сделаем это, мы должны решить новую проблему:
На этом этапе A
, B
и C
интегрированы в то, что будет историей master
, но изменения не отражаются в конечном состоянии (потому что они были отменены) ; это означает, что branch
нельзя объединить обратно. Исправить это немного сложнее, потому что вы нажали branch
на origin/branch
, и мы не хотим создавать другой сценарий перезаписи истории.
Тем не менее, чтобы это исправить, нам нужны новые копии A
, B
и C
.
git rebase --onto temp master branch
Будет создавать копии
O -- x -- x <-(master)
\
A -- B -- C <--(origin/master)(origin/branch)
\
~CBA <--(temp)
\
A' -- B' -- C' <--(branch)
Теперь все можно решить с помощью ускоренных слияний и ускоренных перемещений.
git checkout master
git merge temp
git branch -D temp
git push
выходы
O -- x -- x -- A -- B -- C <--(origin/branch)
\
~CBA <--(master)(origin/master)
\
A' -- B' -- C' <--(branch)
, а затем
git checkout branch
git push
выходы
O -- x -- x -- A -- B -- C -- ~CBA <--(master)(origin/master)
\
A' -- B' -- C' <--(branch)(origin/branch)
Обратите внимание, что на этих шагах я использовал стандартные ссылки для толчков. Вы могли бы сказать
git push origin master
и
git push origin branch
но у IMO слишком много привычки указывать, что делает слишком легким переход к неправильной удаленной ветви. Вместо этого я рекомендую настроить конфигурацию так, чтобы вы могли полагаться на значения по умолчанию большую часть времени.