Git — сценарии спасения
что набрать, когда всё сломалось
Что это
Не курс по git, а список ситуаций. Все команды прогнаны в настоящем репозитории, вывод настоящий. Основное про git — в статье 5.1.
Каждый день
git status # что изменено прямо сейчас
git diff # что именно изменено, построчно
git diff --staged # что уйдёт в коммит
git add . # взять всё изменённое
git commit -m "..." # зафиксировать
git log --oneline # история коротким спискомПеред тем как просить ИИ о крупной правке, стоит сделать коммит. Это одна команда, и она превращает любую неудачу в «откатиться и попробовать иначе».
Я испортил файл и хочу вернуть как было
$ git diff --stat
app.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git restore app.py # вернуть один файл
$ cat app.py
третья версияВернуть всё к последнему коммиту:
git restore .Я удалил файл
$ rm app.py
$ git restore app.py
$ ls
app.pyПока файл был в последнем коммите, он возвращается.
Я закоммитил не то
Коммит ещё не отправлен — отменить коммит, правки оставить:
$ git reset --soft HEAD~1
$ git status --short
M app.pyИзменения на месте, коммита нет. Можно поправить и закоммитить заново.
Коммит уже отправлен — не переписывать историю, а добавить обратный:
$ git revert --no-edit HEAD
[main fba3f8d] Revert "третья версия"
1 file changed, 1 insertion(+), 1 deletion(-)
$ cat app.py
вторая версияrevert создаёт новый коммит, который отменяет старый. История остаётся
правдивой, и ни у кого ничего не ломается.
Надо срочно отложить правки
$ echo "черновик" >> app.py
$ git stash # спрятать и вернуться к чистому состоянию
$ cat app.py
вторая версия
$ git stash pop # достать обратно
$ cat app.py
вторая версия
черновикУдобно, когда ИИ наработал половину, а срочно понадобилось проверить что-то другое.
Я закоммитил секрет
$ git rm --cached .env
$ echo ".env" >> .gitignore
$ git add .gitignore && git commit -m "убрал .env из репозитория"
$ git ls-files
.gitignore
app.pyИз рабочей копии и из будущих коммитов .env ушёл. Но:
$ git log --oneline --all -- .env
1badd12 убрал .env из репозитория
5f9507b добавил .envФайл по-прежнему в истории, и ключ из неё достаётся одной командой. Поэтому порядок действий такой: сначала сменить ключ, а потом уже чистить репозиторий. Если репозиторий публичный — считать ключ скомпрометированным с момента отправки (статья 5.7).
Когда сломалось — а раньше работало
git log --oneline # найти подозрительный коммит
git show <хеш> # посмотреть, что в нём изменилось
git restore --source=<хеш> <файл> # взять версию файла из коммитаЕсли коммитов много и непонятно, какой виноват, — есть git bisect, который
делит историю пополам (приём из статьи 4.4, только автоматический).
Ветки
git switch -c опыт # новая ветка от текущего места
git switch main # вернуться
git merge опыт # влить сделанное
git branch -d опыт # удалить веткуДля работы с ИИ ветка полезна в одном конкретном случае: крупная переделка, про которую заранее неясно, доживёт ли она до конца. В ветке её можно бросить, не убирая за собой.
Я сделал reset --hard и потерял коммит
Коммит, который «пропал» после reset, ещё не удалён. Git помнит, где
стоял HEAD каждый раз:
$ git reset --hard HEAD~1
$ cat app.py
первая версия
$ git reflog
a4d85e7 HEAD@{0}: reset: moving to HEAD~1
c03070e HEAD@{1}: commit: вторая версия
a4d85e7 HEAD@{2}: commit (initial): первая версия
$ git reset --hard HEAD@{1}
$ cat app.py
вторая версияreflog — последняя линия обороны: он хранит записи несколько недель. Но
спасает только закоммиченное. Незакоммиченные правки после reset --hard
не вернуть ничем — ещё один довод коммитить перед каждой задачей для ИИ.
Чего делать не надо
git push --force в общую ветку. Переписывает историю у всех.
git reset --hard без понимания. Выбрасывает и коммиты, и правки.
Коммит «всё подряд» раз в неделю. Такой коммит невозможно откатить частично — а именно это и нужно, когда ИИ сломал одно из двадцати изменений.
Коммит с непрочитанным диффом. git diff перед коммитом — тридцать секунд,
которые ловят случайно оставленный отладочный вывод и забытый ключ.