lesson ── git-labs ── ~22 мин ── 8 шагов
Цель - пройти три способа «отмотать назад» и понять, когда какой
применять. reset переписывает историю - подходит для своей
локальной ветки. revert создаёт компенсирующий коммит - безопасен
на shared ветке. reflog спасает, когда уже всё сломал.
интерактивный sandbox
Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.
stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя
cd /home/student/work
mkdir -p undo-lab && cd undo-lab
git init -b main
for i in 1 2 3 4 5; do
echo "line $i" >> file.txt
git add file.txt
git commit -m "line $i"
done
git log --oneline
Пять коммитов, файл содержит 5 строк. Это полигон для отмен.
✓ Пять коммитов на месте.
--soft снимает коммит, но оставляет содержимое в index.
cd /home/student/work/undo-lab
git reset --soft HEAD~1 # --soft = только сдвиг ветки, index и working не трогать
git log --oneline # лог короче на 1
git status # содержимое снятого коммита - в index
Лог теперь 4 коммита. Index содержит line 5 - готов
перекоммитить с другим сообщением.
✓ Коммит снят, изменение в index.
--mixed (дефолт) также сбрасывает index. Working tree не трогает.
cd /home/student/work/undo-lab
git reset HEAD~1 # без флага = --mixed: ветка + index, working не трогать
git log --oneline
git status # изменения в working как unstaged
Лог - 3 коммита. Index пустой. Working содержит line 4 и line 5
как unstaged изменения. Полезно, когда коммит был сделан с
ошибочными файлами в index.
✓ Index сброшен. Изменения в working.
cd /home/student/work/undo-lab
git reset --hard HEAD~1 # --hard = ветка + index + working, всё в HEAD~1
git log --oneline
cat file.txt # только 2 строки - всё что было сверху, ушло
git status # working tree clean
Лог - 2 коммита. file.txt содержит только 2 строки. Working
tree clean. Все «потерянные» изменения - теперь не видны через
log, но...
Опасно? Да. Но reflog ещё помнит.
✓ Хард-reset сделан. История 'кажется' потерянной.
reflog - локальный журнал перемещений HEAD. Все «удалённые»
коммиты ещё там.
cd /home/student/work/undo-lab
git reflog # локальный лог всех перемещений HEAD (90 дней)
Видишь записи HEAD@{0}, HEAD@{1}, и т.д. Это твои недавние
позиции. Найди ту, где было 5 коммитов:
# HEAD@{n} = "где HEAD был n шагов назад" по refloggit reset --hard HEAD@{4}git log --oneline # все 5 коммитов обратно
Все 5 коммитов вернулись. reflog держит записи 90 дней по умолчанию.
✓ Пять коммитов восстановлены. reflog спас.
Допустим коммит line 3 плохой, и он уже запушен другим в
shared. Reset тут опасен - переписывание истории. Используй revert.
cd /home/student/work/undo-lab
# awk '{print $1}' = первая колонка `log --oneline`, это short-shaSHA=$(git log --oneline | grep 'line 3' | awk '{print $1}')git revert --no-edit $SHA # --no-edit = взять дефолтное сообщение "Revert ..."
git log --oneline
cat file.txt # line 3 пропала, остальные сохранены
Лог стал длиннее на коммит (Revert "line 3"). История сохранена,
обоим видно, что произошло. file.txt теперь содержит
line 1, line 2, line 4, line 5 - без line 3.
✓ Revert создал обратный коммит. История не переписана.
Запомни различие:
cd /home/student/work/undo-lab
git log --oneline --graph
Видишь линейную историю из 6 записей: 5 оригинальных + revert. Никаких "пропавших" SHA.
✓ Шесть коммитов. История целая, ошибка отменена.
Сделай коммит:
cd /home/student/work/undo-lab
echo "extra" >> file.txt
git add file.txt && git commit -m "add extra"
git log --oneline | head -1
Запомни SHA. Сделай amend:
git commit --amend -m "add extra (corrected)" # заменяет HEAD новым коммитом
git log --oneline | head -1 # SHA другой, сообщение новое
SHA коммита другой. Старая версия есть в reflog:
git reflog | head -5 # верхние 5 записей: видны и commit, и commit (amend)
Видны обе версии: одна commit:, другая commit (amend):. Если
хочешь откатить amend - git reset --hard HEAD@{1}. Финал урока:
ты знаешь, как откатить почти всё, что сделал случайно.
✓ Amend и reflog отработали. Большинство «потерянного» в Git можно вернуть.
reset двигает указатель ветки назад (3 уровня агрессивности).
revert создаёт коммит, обратный по содержимому. reflog хранит
все позиции HEAD за последние 90 дней - оттуда восстанавливаются
даже «удалённые» коммиты.
команды
git reset --soft HEAD~1снять коммит, оставить indexgit reset --hard <sha>переехать на коммит и стереть всёgit revert <sha>создать обратный коммитgit reflogлог всех позиций HEADgit reset --hard HEAD@{n}восстановить состояние из reflogконцепции