Объектная модель и история — Примеры

Пример 1 — Коммит — это снимок, а не диф

Коллега считает, что коммит хранит лишь изменившиеся строки, и боится, что переключение на старый коммит наполовину применит патч; команды ниже выполняются в черновом репозитории с тремя коммитами. Скажите, что они доказывают и что переписывание делает с хешами.

git log --oneline
# a1b2c3 третий коммит
# 9d8e7f второй коммит
# 4c5d6e первый коммит

git checkout 9d8e7f
ls
# readme.md  src/          <- весь проект, а не патч

git switch -c reworded 4c5d6e
git commit --amend -m "первый коммит, переписанный"
git log --oneline
# 7f8a9b первый коммит, переписанный   <- новый хеш для того же tree
# 9d8e7f второй коммит

Решение

переключение на 9d8e7f восстановило полный tree, значит коммит — это
проект в тот момент, а не набор изменённых строк.

amend 4c5d6e дал 7f8a9b: содержимое коммита включает его родителя
и его сообщение.

Коммит указывает на полный tree, поэтому checkout полон, а не частичный патч, а диф, который показывает log, вычисляется для отображения из двух снимков. Изменённый коммит получает новый хеш, потому что его содержимое включает родителя и сообщение, поэтому переписывание старого коммита в общей ветке вынуждает меняться все последующие хеши. Ловушка — читать новый хеш как изменение файла: здесь tree идентичен.

Пример 2 — Восстановление работы после неудачного reset

Разработчик делает жёсткий reset, чтобы отбросить то, что казалось тупиком, а потом понимает, что два коммита были нужны. Скажите, где ещё находятся эти коммиты, и приведите команды, которые их вернут.

git reset --hard HEAD~3
# HEAD is now at 4c5d6e первый коммит

git log --oneline
# 4c5d6e первый коммит       <- три коммита исчезли из log
git reflog
# 4c5d6e HEAD@{0}: reset: moving to HEAD~3
# a1b2c3 HEAD@{1}: commit: третий коммит
# 9d8e7f HEAD@{2}: commit: второй коммит

Решение

коммиты всё ещё объекты; reset лишь передвинул указатель ветки,
а reflog записал, где этот указатель был.

git switch -c rescued a1b2c3      # назовите старую вершину снова
git log --oneline                 # все три коммита вернулись

Reset двигает ссылку, а объекты, на которые она указывала, остаются нетронутыми, пока сборка мусора не удалит недостижимое, поэтому старая вершина всё ещё адресуема по хешу, который сохранил reflog. Ловушка — полагать, что работа умерла вместе с именем ветки: объекты без ссылок живут некоторое время, и reflog — стандартный путь назад.