linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it works
  • Уроки
  • База знаний
  • Собеседование
Cluster

← все кластеры

Объекты, SHA, packfile, working tree

Вопросы про объектную модель Git: четыре типа объектов, SHA как ключ, как лежит история на диске и почему ветки в Git дешёвые. Это база для всех остальных кластеров - без неё про rebase, reflog и filter-repo бесполезно говорить.

7 вопросов · ~35 мин чтения

Questions

На этой странице

  1. 01Четыре типа объектов в Git. Что хранит каждый и как они связаны?
  2. 02Git хранит snapshot или delta? Объясни как есть.
  3. 03Что физически происходит при `git add file.txt`?
  4. 04Почему SHA-1, а не MD5? Что с переходом на SHA-256?
  5. 05Working tree, index, HEAD. Чем они отличаются и кто что хранит?
  6. 06Что физически меняется на диске при `git commit -m "msg"`?
  7. 07Как Git достаёт содержимое объекта из packfile? Что такое base+delta цепочка?

#git-objects-four-types

juniorчасто

Четыре типа объектов в Git. Что хранит каждый и как они связаны?

Что отвечать

`blob` - содержимое одного файла, без имени и прав. `tree` - список записей одной директории: права, тип, SHA, имя. Запись tree указывает либо на blob (файл), либо на другой tree (поддиректория) - получается рекурсивное дерево. `commit` - SHA корневого tree плюс метаданные: родитель(и), автор, коммиттер, дата, сообщение. `tag` - именованный указатель на коммит, обычно с подписью.

Что хотят услышать

Senior должен: - назвать что Git это key-value store: ключ это SHA, значение это zlib-сжатый объект в `.git/objects/` - объяснить рекурсию tree: дерево директорий это дерево tree-объектов - сказать что blob не знает имени файла - имя живёт в tree, который на blob ссылается - объяснить почему ветка это просто файл с одним SHA в `.git/refs/heads/<name>`, а HEAD это файл с указателем на ветку - различить annotated tag (объект в `.git/objects/`) и lightweight tag (просто запись в `.git/refs/tags/`)

Подводные камни

  • ✗ Сказать «commit хранит изменения» - на самом деле commit хранит полный snapshot через tree, diff Git вычисляет на лету
  • ✗ Сказать «blob знает имя файла» - не знает, имя добавляется tree-объектом
  • ✗ Перепутать tag-объект с веткой - оба ref, но tag обычно не двигается, ветка двигается

Follow-up

  • ? Что покажет `git cat-file -p HEAD^{tree}`?
  • ? Где физически лежит ветка `feature/x`?
  • ? Чем lightweight tag отличается от annotated в плане объектов?

Глубина в базе знаний

  • Blob
  • Tree
  • Commit
  • Tag
  • SHA-1 в Git
tags: internals, objects, modelbook: edu/Git_book/03-object-model.md · how.git.work.bytebytego.pdf

#git-snapshot-vs-delta

intermediateиногда

Git хранит snapshot или delta? Объясни как есть.

Что отвечать

Git **хранит snapshot**, передаёт и упаковывает - delta. Каждый commit указывает на полный tree, а tree через blob - на полное содержимое файла. Это «логическая модель». Когда Git упаковывает объекты в packfile (при `git gc`, при push, при clone), он применяет delta-сжатие: похожие blob'ы заменяются на diff от выбранного «base»-объекта. Восстанавливается обратно при `cat-file`.

Что хотят услышать

Senior должен: - чётко разделить логическую модель (snapshot) и физический формат хранения (loose object + packfile с delta) - сказать что в loose-форме каждый объект полный, в packfile - может быть base+delta-цепочка - объяснить почему snapshot-модель: атомарность коммита, простота восстановления, простота compare двух деревьев - назвать `git verify-pack -v <pack>` как способ посмотреть какие объекты ушли в delta и от какого base

Подводные камни

  • ✗ Сказать «Git хранит дельты как Subversion» - неверно, логически snapshot, дельта только в packfile
  • ✗ Сказать «packfile это формат для пересылки» - packfile это и формат хранения внутри `.git/objects/pack/`
  • ✗ Думать что delta идёт всегда между соседними коммитами по времени - heuristic выбирает base по похожести, не по порядку

Follow-up

  • ? Что произойдёт с repository size если коммитить один и тот же файл слегка по-разному 100 раз?
  • ? Что делает `git gc --aggressive` с pack'ами?
  • ? Зачем нужен `.git/objects/info/packs`?

Глубина в базе знаний

  • Снимки против дельт
  • Packfile
  • Blob
tags: internals, packfile, deltabook: edu/Git_book/03-object-model.md · how.git.work.bytebytego.pdf

#git-add-physically

intermediateчасто

Что физически происходит при `git add file.txt`?

Что отвечать

Git читает содержимое файла, считает SHA-1 от `blob <len>\0<content>`, пишет blob в `.git/objects/<2>/<38>` (zlib-сжато). Затем обновляет `.git/index` - бинарный файл со списком записей: путь, mode, SHA blob'а, stat-метаданные. Working tree не трогается. `git commit` затем берёт index, строит из него tree-объекты, пишет commit и двигает ветку.

Что хотят услышать

Senior должен: - назвать три места «состояния»: working tree, index (stage), и `.git/objects/` + ref'ы - объяснить что blob создаётся уже на `git add` - даже без коммита в `.git/objects/` лежит копия - сказать что index это плоский список путей, не дерево, дерево собирается на коммите через `git write-tree` - упомянуть что повторный `git add` после правки файла создаёт новый blob, но старый остаётся в objects (потом подберёт `git gc`)

Подводные камни

  • ✗ Сказать «git add просто маркирует файл для коммита» - на самом деле уже создаёт blob на диске
  • ✗ Думать что index хранит patch'и - там полный snapshot для каждого файла через SHA
  • ✗ Сказать «git commit читает working tree» - читает index, не working tree

Follow-up

  • ? Что покажет `git ls-files --stage`?
  • ? Чем `git add -p` отличается от `git add`?
  • ? Что произойдёт если правишь файл после `git add` и делаешь commit без повторного add?

Глубина в базе знаний

  • git add
  • Working tree
  • Blob
  • Commit
tags: internals, index, add

#git-sha1-collision-sha256

intermediateиногда

Почему SHA-1, а не MD5? Что с переходом на SHA-256?

Что отвечать

MD5 окончательно сломан с 2004, SHA-1 был выбран в 2005 как разумный баланс «достаточно длинный + быстрый». В 2017 Google показал коллизию SHA-1 (SHAttered). Для Git это менее страшно: хэш используется для адресации, не для подписи, и префикс `blob <len>\0` усложняет атаку. С 2018 идёт миграция на SHA-256, сейчас Git поддерживает оба формата, но новые репозитории по умолчанию всё ещё SHA-1.

Что хотят услышать

Senior должен: - различить «коллизию найти теоретически» и «коллизию подобрать под нужный prefix» - вторая дороже на порядки - сказать что миграция на SHA-256 включается флагом `git init --object-format=sha256` и пока несовместима с старыми репозиториями (нет прозрачного транзита) - объяснить что в Git хэш и для контента, и для целостности цепочки - подмена одного коммита ломает SHA всех последующих - упомянуть SHAttered как конкретный референс - две PDF с одинаковым SHA-1

Подводные камни

  • ✗ Сказать «Git уже на SHA-256 по умолчанию» - нет, переход не завершён
  • ✗ Думать что SHA-1 коллизия моментально ломает Git - не ломает, Git живёт с этим через дополнительные проверки (`core.commitGraph`, fsck-checks)
  • ✗ Перепутать длину: SHA-1 это 40 hex-символов, SHA-256 это 64

Follow-up

  • ? Что покажет `git rev-parse HEAD` в репозитории с `--object-format=sha256`?
  • ? Как Git защищается от SHAttered-сценария на практике?
  • ? Почему MD5 в Git никогда не рассматривался?

Глубина в базе знаний

  • SHA-1 в Git
  • Blob
tags: internals, sha, securitybook: edu/Git_book/03-object-model.md

#git-working-tree-index-head

intermediateчасто

Working tree, index, HEAD. Чем они отличаются и кто что хранит?

Что отвечать

Working tree - файлы как ты их видишь в редакторе. Index (stage) - бинарный файл `.git/index` с тем, что попадёт в следующий коммит: записи путь+mode+SHA. HEAD - указатель на «текущую позицию», обычно через ветку (`ref: refs/heads/main`), иногда напрямую на коммит (detached). `git status` показывает diff между этими тремя состояниями: working vs index и index vs HEAD.

Что хотят услышать

Кандидат должен: - назвать три состояния и связать их с командами: `add` двигает working → index, `commit` двигает index → HEAD, `reset` двигает HEAD назад с разными режимами - объяснить `git diff` (working vs index), `git diff --cached` (index vs HEAD), `git diff HEAD` (working vs HEAD) - сказать что index не дерево, а плоский список путей со слэшами - дерево соберётся только при коммите - упомянуть что детачнутый HEAD не «сломан» - это валидное состояние, просто без имени ветки

Подводные камни

  • ✗ Сказать «index это патч» - нет, index содержит snapshot через SHA blob'ов
  • ✗ Думать что `git diff` всегда показывает working vs HEAD - по умолчанию это working vs index
  • ✗ Перепутать `git reset --soft` и `--hard` - soft двигает только HEAD, hard двигает HEAD+index+working

Follow-up

  • ? Что покажет `git diff --cached`?
  • ? Чем `git reset --mixed` отличается от `--soft`?
  • ? Как посмотреть содержимое index без сторонних утилит?

Глубина в базе знаний

  • Working tree
  • git status
  • git diff
  • Detached HEAD
tags: internals, index, head

#git-commit-what-happens

seniorиногда

Что физически меняется на диске при `git commit -m "msg"`?

Что отвечать

Git берёт index, рекурсивно собирает tree-объекты (по одному на директорию), вычисляет их SHA и пишет в `.git/objects/`. Затем создаёт commit-объект: SHA корневого tree, SHA родителя (из текущей ветки), author/committer, сообщение - всё это zlib-жмётся и пишется в `.git/objects/`. В конце обновляется `.git/refs/heads/<branch>` на SHA нового коммита, и пишется запись в `.git/logs/HEAD` и `.git/logs/refs/heads/<branch>` - это будущий reflog.

Что хотят услышать

Senior должен: - назвать порядок: write-tree → commit-object → update-ref → reflog-entry - объяснить что коммит без изменений в index возможен через `--allow-empty`, но без флага Git откажется - сказать что merge-коммит отличается двумя (или больше) SHA родителя в commit-объекте - упомянуть hook-точки: `pre-commit` бежит до write-tree, `prepare-commit-msg` - перед редактором, `commit-msg` - после написания сообщения, `post-commit` - после update-ref

Подводные камни

  • ✗ Думать что `git commit` пишет diff - пишет полные tree+commit-объекты
  • ✗ Сказать «новые tree не создаются если ни одна папка не менялась» - корневой tree создаётся всегда, потому что его SHA зависит от вложенных tree (но неизменённые поддеревья переиспользуются)
  • ✗ Забыть про обновление reflog - именно из-за него `reset --hard` потом восстанавливается через `git reflog`

Follow-up

  • ? Что произойдёт если `pre-commit` hook вернёт ненулевой exit?
  • ? Зачем нужен `git commit --allow-empty`?
  • ? Что в commit-объекте у merge-коммита отличается от обычного?

Глубина в базе знаний

  • Commit
  • Tree
  • git reflog
tags: internals, commit, hooks

#git-packfile-delta-chain

seniorредко

Как Git достаёт содержимое объекта из packfile? Что такое base+delta цепочка?

Что отвечать

Packfile (`.git/objects/pack/*.pack`) хранит объекты подряд, иногда как полные snapshot'ы (тип `OBJ_BLOB`/`OBJ_TREE`/...), иногда как delta от другого объекта (типы `OBJ_OFS_DELTA` или `OBJ_REF_DELTA`). Index-файл (`.idx`) даёт O(log n) поиск SHA → смещение. При запросе Git идёт по цепочке: если объект delta - находит base, восстанавливает base рекурсивно, накладывает delta. Длина цепочки ограничена `pack.depth` (default 50).

Что хотят услышать

Senior должен: - назвать `.pack` + `.idx` как пару файлов, и сказать что `.idx` нужен для поиска без сканирования всего pack - различить `OFS_DELTA` (смещение в том же pack) и `REF_DELTA` (SHA base-объекта, может быть в другом pack или loose) - объяснить почему длинные delta-цепочки замедляют чтение - надо восстановить всё base'ы от корня цепочки - назвать `git verify-pack -v` для отладки и `pack.window`/`pack.depth` как настройки heuristic'и упаковки - упомянуть что `git gc` ребалансирует delta-цепочки

Подводные камни

  • ✗ Думать что delta всегда между «соседями по времени» - heuristic выбирает base по похожести имени и размера, не по дате
  • ✗ Сказать «delta это git diff» - не diff, это бинарный copy/insert-формат на уровне байтов, не строк
  • ✗ Не знать про `OFS_DELTA` vs `REF_DELTA` - на собесе на Platform-роль спросят разницу

Follow-up

  • ? Что выведет `git verify-pack -v .git/objects/pack/*.idx | head`?
  • ? Чем плох default `pack.depth=50` для очень больших binary-blob'ов?
  • ? Когда Git создаёт reachability-bitmap и зачем он нужен?

Глубина в базе знаний

  • Packfile
  • Снимки против дельт
  • git cat-file
tags: internals, packfile, performancebook: how.git.work.bytebytego.pdf
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки