Как удалить или изменить коммит, находящийся далеко в истории Git
Что проверяют
Понимаете ли вы разницу между переписыванием истории и её дополнением и умеете ли выбрать способ в зависимости от того, запушен ли коммит. Следом обычно спрашивают, что будет с коллегами после force push и как вернуть ветку, если ребейз пошёл не так.
Как отвечать
Начните с развилки: коммит только у вас локально или уже лежит в общей ветке.
git revert <hash>: создаёт новый коммит с обратным изменением. История не переписывается, поэтому это единственный безопасный путь дляmainи любой общей ветки;git rebase -i <hash>^: интерактивный ребейз от родителя нужного коммита. В списке меняетеpickнаedit(остановиться и поправить),reword(только сообщение),drop(выбросить) илиfixup(влить в предыдущий);git commit --fixup=<hash>: исправление сразу помечается как поправка к старому коммиту, аgit rebase -i --autosquashсам ставит его на место;git rebase --continueи--abort: продолжить после правки или конфликта либо вернуть всё как было.
Все коммиты после изменённого получают новые хэши. Если ветка уже на сервере, нужен git push --force-with-lease: в отличие от --force, он откажет, если кто-то успел запушить поверх.
Для iOS-проекта упомяните project.pbxproj: при ребейзе через десяток коммитов конфликты в нём всплывают почти гарантированно и разрешаются на каждом шаге. А если в старом коммите утёк ключ API или сертификат, ребейз не спасёт, копия уже есть у всех, кто клонировал. Ключ отзывают, историю чистят git filter-repo.
Где спотыкаются
Предлагают rebase для общей ветки. После force push история у коллег расходится, и их следующий пуш либо конфликтует, либо тихо возвращает удалённое.
Путают revert и reset. git reset --hard двигает указатель ветки и выбрасывает всё, что было после, это не отмена одного коммита из середины.
Не знают про git reflog. Неудачный ребейз почти всегда обратим: прежнее состояние ветки лежит в reflog, и git reset --hard HEAD@{n} возвращает его.