Как откатывать изменения в Git
Что проверяют
Умеете ли вы выбрать способ отката под ситуацию: изменения ещё не закоммичены, закоммичены локально или уже запушены в общую ветку. Следом обычно спрашивают, как вернуть коммит после неудачного reset --hard.
Как отвечать
- незакоммиченные правки:
git restore <файл>возвращает файл к версии из индекса,git restore --staged <файл>убирает его из индекса, не трогая содержимое. Эти команды появились в Git 2.23 и заменили перегруженныйgit checkout -- <файл>; - локальные коммиты:
git resetдвигает ветку.--softоставляет изменения в индексе,--mixed(по умолчанию) в рабочей копии,--hardвыбрасывает их. Последний коммит правят черезgit commit --amend; - запушенная общая ветка:
git revert <sha>создаёт новый коммит с обратными изменениями, история не переписывается. Для merge-коммита нуженgit revert -m 1 <sha>, где1это номер родителя, который считается основной линией; - своя ветка после пуша: переписать историю можно, но пушить через
--force-with-lease, а не--force, чтобы не затереть чужие коммиты; - спасение:
git reflogпомнит, куда указывалHEAD, и потерянный коммит возвращается черезgit reset --hard <sha>или новую ветку от него.
Для Go есть своя деталь. Опубликованную версию модуля не отозвать удалением тега: proxy.golang.org уже её закешировал, а хеш записан в go.sum у пользователей. Правильно выпустить новую версию с директивой retract в go.mod. А после неудачного go get обычно хватает вернуть go.mod и go.sum через git restore.
Где спотыкаются
Делают reset --hard и push --force в общей ветке. У коллег история расходится, и их следующий пуш либо отклоняется, либо возвращает удалённые коммиты.
Считают, что reflog спасёт всё. Он помнит только коммиты, а правки, которые не попали даже в индекс, после reset --hard потеряны безвозвратно.
Не знают, как вернуть откаченный мерж. Если сделать revert слияния, а потом снова влить ту же ветку, Git решит, что эти коммиты уже есть, и изменения не вернутся. Нужно сделать revert самого реверта.