Есть бажный коммит, после него еще несколько коммитов, которые пока нельзя деплоить. Необходимо как-то выкинуть изменения этого коммита из проекта, затем развернуться из следующего перед ним коммита, а потом продолжить работу с последнего коммита, но исключая изменения бажного. Дайте, пожалуйста, последовательность действий.
Ответ
затем развернуться из следующего перед ним коммита
Подробная информация в вопросе Как вернуться (откатиться) к более раннему коммиту?. Кратко - когда всё почините, сможете просто перейти в нужный коммит или даже новую ветку создать в нём.
выкинуть изменения этого коммита из проекта,
Про это будет весь ответ дальше.
Самый важный вопрос:
Успели ли вы запушить эту ветку на удалённый репозиторий? Возможно ли, что другой разработчик уже получил с удаленного репозитория коммит B или один из последующих и продолжает работать над ним. Если ответ - да, то в общем случае желательно revert, но не rebase, cherry-pick и прочее, что переписывает историю Git, так как это создает большие проблемы с интеграцией.
Поскольку вам нужно развернуться из C, не содержащего изменений из B, то почти наверняка историю переписывать придётся. В таком случае предупредите коллег заранее. Им придётся аналогичным образом ребейзить свои изменения, например с коммита D на D'
Обозначим начальную ситуацию на следующей схеме:
A - B - C - D
↑
branchname
(HEAD)
A, B, C, D — коммиты в ветке branchname. B - "плохой коммит", его нам нужно удалить. Коммит C нужно задеплоить.
(HEAD) — местоположение указателя HEAD.
↑ обозначает коммит, на который указывает определенная ветка или указатель.
Вариант 1 - через rebase -i
Плюс - меньше мусора в истории. И можно гордиться, что освоил rebase.
Плюс - позволяет развернуться из С, не содержащего изменений из B.
Минус - переписывает историю, опасно при командной работе.
Команды:
git checkout branchname
git rebase -i A
git checkout -b deployme C'
В качестве A мы указываем коммит сразу перед тем, который будем исключать. При этом A останется на месте. Откроется редактор, в нём будут коммиты в обратном порядке. Меняем pick на drop, обозначая что мы хотим выкинуть этот коммит.
drop 323d4e6 comment for B
pick 31da2b9 comment for C
pick 24d420c comment for D
# Rebase c8893f9..24d420c onto c8893f9 (3 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Результат
A - C' - D'
↑
branchname
(HEAD)
↑
deployme
(HEAD)
У новых коммитов теперь другой предок, поэтому это - другие, новые коммиты (хотя содержимое то же за вычетом B). Новая ветка deployme смотрит на коммит C'
Вариант 2 - через revert
Плюс - не переписывает историю и безопасен при командной работе. Проще чем rebase.
Минус - ошибочный коммит останется в истории. Плохо, если там информация, которую нельзя показывать или код, который стыдно публиковать. :)
Минус - коммит C по-прежнему будет содержать изменения от B
Команды:
git checkout branchname
git revert B
Результат:
A - B - C - D - xB
↑
branchname
(HEAD)
Новый коммит xB содержит изменения, которые отменяют изменения коммита B. Однако у нас есть проблема: коммит C по-прежнему содержит изменения от B
Немного подробнее о revert
Документация
тут, пятый пункт
В дополнение:
Если в процессе ошибётесь или протеряете не тот коммит: Как отменить откат изменений (восстановить потерянный коммит)?
Заодно, при желании, можно что-нибудь сделать и с другими коммитами: Как разделить/склеить старый комит?
Комментариев нет:
Отправить комментарий