![](https://habrastorage.org/files/400/5bb/7d5/4005bb7d597c44beb0d20bd40917b6f1.png)
Как Git хранит коммиты
Репозиторий Git использует простое хранилище типа ключ-значение, где в роли ключа выступает хеш SHA-1, а значение представляет собой контейнер одного из трех типов: описание коммита, описание дерева файлов или содержимое файла. Существуют даже низкоуровневые служебные команды (plumbing) для работы с этим хранилищем как с базой данных:
echo 'test content' | git hash-object -w --stdin
Эта архитектурная особенность породила мутное высказывание, что Git отслеживает переименование по содержимому файла. При переименовании объект «коммит» будет содержать ссылку на объект «содержимое файла», но если содержимое не изменилось, то это будет ссылка на объект, уже имеющийся в хранилище.
![](https://habrastorage.org/files/ce0/d20/3e3/ce0d203e31ee4c70a5e043a6aa4898fd.png)
По умолчанию Git хранит содержимое файлов целиком: если мы поменяли строчку в 100-килобайтном исходнике, то в хранилище будет добавлен объект со всеми 100 килобайтами, сжатыми с помощью zlib. Чтобы репозиторий излишне не распухал, в Git предусмотрен garbage collector, который запускается при выполнении команды push, при этом объекты переупаковываются в pack-файл, который содержит разницу между исходным файлом и следующей ревизией (diff).
Когда коммиты умирают
![](https://habrastorage.org/files/3f0/786/75b/3f078675b55b4451877cf6ed8ad5af23.png)
Но «ненужные» коммиты случаются не только при использовании команды reset. К примеру, популярная операция rebase просто копирует информацию о коммитах, оставляя в хранилище «оригинал», который никому уже не потребуется. Чтобы такие «потерянные» объекты не копились, в Git предусмотрен механизм сборки мусора — уже упомянутый выше garbage collector, автоматически вызываемый при выполнении команды push либо вызываемый вручную.
Garbage collector ищет объекты, на которые больше нет ссылок, и удаляет их из хранилища. Огромную роль при этом играет журнал операций reflog: ссылки в нем имеют ограниченный срок жизни, по умолчанию 30 дней для объекта без ссылок и 90 дней для объекта со ссылками. Garbage collector сначала удаляет из журнала reflog все ссылки с истекшим «сроком годности», а затем удаляет из хранилища объекты, на которые больше нет ссылок. Такая архитектура дает разработчику 30 дней, чтобы восстановить “ненужный” коммит, который в противном случае будет окончательно удален из репозитория по истечении этого срока.
Что же произошло на GitHub?
![](https://habrastorage.org/files/a9a/706/d54/a9a706d54f644f1c98f6b73fbfbd6105.png)
Надеюсь, этот небольшой экскурс во внутренности Git сэкономит кому-нибудь ценное время при поиске «пропавших коммитов», на которые ссылается, к примеру, баг-трекер. Если я где-то ошибся или есть замечания — с удовольствием пообщаюсь в комментах.