Git не сохраняет копию проекта: что лежит в папке .git
Git не сохраняет копию проекта: что лежит в папке .git
Каждый коммит выглядит как снимок проекта, но Git не копирует все файлы заново. Как объекты, хеши и pack-файлы хранят историю внутри .git? Разбираемся на простых примерах.
Представим проект из тысячи файлов. Мы изменили одну строку, выполнили git commit — и получили новую версию. Если Git каждый раз сохраняет полную копию проекта, после сотни коммитов репозиторий должен занимать в сто раз больше места.
Но этого не происходит. Git действительно воспринимает каждый коммит как снимок проекта, однако одинаковые файлы повторно не записывает. Вместо папок с копиями он хранит небольшую базу объектов, связанных хешами.
Папка .git и есть репозиторий. Рабочие файлы рядом с ней — лишь одна из версий проекта, которую Git восстановил для нас.
Файл превращается в blob
Когда мы выполняем git add, содержимое файла попадает в объект типа blob. В нём нет имени файла, даты создания или пути к папке. Только данные.
Git вычисляет для объекта хеш и использует его как идентификатор. Если содержимое файла не изменилось, хеш тоже останется прежним. Создавать ещё одну копию не нужно — новый коммит просто сошлётся на уже существующий объект.
Допустим, в проекте есть неизменённый файл logo.png. Даже если он присутствует в тысяче коммитов, Git может хранить его содержимое только один раз. Все соответствующие снимки будут указывать на тот же blob.
Откуда Git узнаёт имена файлов
Если внутри blob нет имени, возникает логичный вопрос: как Git понимает, что конкретный объект нужно восстановить как src/main.py?
Для этого существует объект tree. Он похож на описание папки: хранит имена файлов, права доступа и ссылки на blob или другие объекты tree.
Структуру небольшого проекта можно представить так:
tree проекта ├── README.md → blob a8f2… ├── logo.png → blob 03c1… └── src → tree 7bd4… └── main.py → blob e921…
Если в следующем коммите изменится только main.py, Git создаст новый blob для этого файла и новые объекты tree для затронутых каталогов. Неизменённые данные останутся прежними.
Что тогда хранит commit?
Объект commit обычно довольно маленький. В нём находится ссылка на корневой tree, информация об авторе, время, сообщение коммита и ссылка на предыдущий коммит.
Самих файлов внутри него нет. Коммит лишь связывает метаданные с конкретным состоянием дерева проекта.
Посмотреть содержимое текущего коммита можно обычными командами Git:
git cat-file -t HEAD git cat-file -p HEAD
Первая команда покажет тип объекта — commit. Вторая выведет его содержимое. Там можно увидеть поля tree, parent, author и committer.
Следующая команда откроет корневое дерево проекта:
git cat-file -p HEAD^{tree}
Так привычная история коммитов превращается в цепочку объектов: commit указывает на tree, tree — на blobs и вложенные trees, а поле parent связывает один commit с предыдущим.
Ветки почти ничего не весят
Ветка в Git — не отдельная копия проекта. Это всего лишь имя, указывающее на определённый коммит.
Когда мы создаём новую ветку, Git не дублирует файлы и историю. Он создаёт ещё один указатель. По мере появления новых коммитов указатель выбранной ветки двигается вперёд.
Файл HEAD сообщает, какая ветка сейчас активна. Сами ссылки обычно находятся внутри .git/refs, хотя со временем Git может упаковать их в другой служебный файл.
Именно поэтому создать ветку можно мгновенно даже в большом проекте. Копировать гигабайты данных не приходится.
Зачем нужны pack-файлы
На первых этапах Git может хранить каждый объект отдельно в каталоге .git/objects. Объекты уже сжаты, но в крупном репозитории отдельных файлов становится слишком много.
Во время обслуживания Git объединяет их в pack-файлы. Похожие версии файлов могут дополнительно храниться в виде разницы относительно другого объекта. Например, одна версия большого текстового файла записывается полностью, а для другой сохраняются только изменившиеся фрагменты.

Здесь есть важный нюанс. Логически Git работает со снимками проекта, а дельта-сжатие появляется на уровне физического хранения. Поэтому объяснение «Git всегда хранит только разницу между коммитами» тоже не совсем верное.
Проверить состояние объектной базы можно командой:
git count-objects -v
Она покажет количество отдельных объектов, pack-файлов и занимаемое ими место.
Что ещё находится внутри .git?
Помимо базы объектов и ссылок на ветки, здесь хранится файл index. Это staging area — промежуточное состояние между рабочей папкой и будущим коммитом. Команда git add подготавливает именно его.
В .git/config находятся локальные настройки репозитория, а в .git/logs — журналы перемещения ссылок, на основе которых работает reflog. Там же могут появляться сведения о подключённых удалённых репозиториях, незавершённых слияниях и других операциях.
Удалять или редактировать эти файлы вручную без причины не стоит. Git предоставляет команды для работы почти со всеми внутренними данными, а случайное изменение хеша или ссылки может сделать часть истории недоступной.
Почему это полезно понимать?
После знакомства с объектами многие команды перестают казаться магией. Ветка — указатель. Коммит — описание снимка. Файл — blob. Папка — tree. Переключение версии означает, что Git находит нужные объекты и восстанавливает из них рабочую директорию.
Становится понятнее и другая неприятная особенность: если однажды добавить в репозиторий огромный архив или секретный ключ, обычное удаление файла не уберёт его из старых объектов. Он может продолжить жить в истории, пока ссылки на соответствующие коммиты не будут переписаны, а ненужные объекты — очищены.
Оцените материал:
Похожие записи
RoboParty представила RP1 — первого гуманоидного робота с открытым исходным кодом
19.10.2026
Kling 4.0 вышла: как пользоваться и снять видео по ключевым кадрам
18.10.2026
Нет записи о покупке — нет покупки? Как реальные данные изменили постановку задачи
18.10.2026Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
