Есть бажный коммит, после него еще несколько коммитов, которые пока нельзя деплоить. Необходимо как-то выкинуть изменения этого коммита из проекта, затем развернуться из следующего перед ним коммита, а потом продолжить работу с последнего коммита, но исключая изменения бажного. Дайте, пожалуйста, последовательность действий.
4 Answers
затем развернуться из следующего перед ним коммита
Подробная информация в вопросе Как вернуться (откатиться) к более раннему коммиту?. Кратко - когда всё почините, сможете просто перейти в нужный коммит или даже новую ветку создать в нём.
выкинуть изменения этого коммита из проекта,
Про это будет весь ответ дальше.
Самый важный вопрос:
Успели ли вы запушить эту ветку на удалённый репозиторий? Возможно ли, что другой разработчик уже получил с удаленного репозитория коммит 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:
В дополнение:
- Если в процессе ошибётесь или протеряете не тот коммит: Как отменить откат изменений (восстановить потерянный коммит)?
- Заодно, при желании, можно что-нибудь сделать и с другими коммитами: Как разделить/склеить старый комит?.
- 34,094
-
Спасибо. Решил попробовать rebase. Но что-то пошло не так: выпало Could not apply (....последний коммит....), я выполнил git rebase --skip и в итоге - последние коммиты пропали. Остался только коммит A – Mik Apr 05 '16 at 08:58
-
1@Mik там есть инструкция по восстановлению. У вас был конфликт слияния, его надо вручную было разрешить. Теперь git reflog, смотрите куда восстановиться. – Nick Volynkin Apr 06 '16 at 11:34
-
Просто сделал reset --hard к последнему коммиту, и все восстановилось. В итоге коммит было решено не удалять, так как баг легко исправил через ветвление. Но, тем не менее, Ваш ответ мегаполезен и наверняка пригодится в будущем. Спасибо! – Mik Apr 06 '16 at 16:24
Создать ветку из нужного вам коммита и продолжить работу в этой ветке.
git checkout -b <имя ветки> <номер коммита>
Затем замержить в эту ветку нужные вам коммиты.
git cherry-pick <номер коммита>
- 3,599
Коммит, удаляющий изменения, сохраненные нежелательным коммитом
git revert HEAD
Удаление коммита из ветки
git reset --hard <tag or hash>
- 873
- 5
- 14
git revert - создаёт откатывающий коммит, см. https://git-scm.com/docs/git-revert
git cherry-pick - позволяет в другом бранче создать Новый коммит с изменениями указанного коммита https://git-scm.com/docs/git-cherry-pick
Теоретически этого сочетания должно быть достаточно
- 1,183