Алгоритмы в Git / Git Internals
Git для большинства — привычный набор команд: commit, merge, push, pull. Но за ними стоит достаточно сложная распределённая система с content-addressable storage, алгоритмами и протоколами обмена данными. Как связаны commit и «патчи» из изменений в коде? Как Git находит общее начало двух веток? Когда изменения можно объединить автоматически, а когда Git честно отказывается угадывать смысл результата? Как клиент и сервер определяют, какие объекты уже известны обеим сторонам, не передавая полную историю?
На курсе мы будем последовательно строить ответы на эти инженерные вопросы, чтобы чёрный ящик под названием Git стал немного прозрачнее. Разберём physical storage: packfiles, .idx, delta compression; посмотрим на используемые алгоритмы и структуры данных: object graph, обход commit DAG и поиск merge base; diff-алгоритмы семейства Myers, а также patience и histogram heuristics; three-way merge и устройство conflict state в index. Увидим, как Git приходит к компромиссам между компактностью и стоимостью чтения, обсудим протокол negotiation между клиентом и сервером через want/have, а также оптимизации и механизмы, которые делают Git применимым к большим репозиториям: commit-graph, Bloom filters, reachability bitmaps, partial clone.
Сквозной проект курса — создание собственного минимального Git-сервера, совместимого с настоящим клиентом Git для clone и fetch. Мы начнём с чтения объектов и refs, затем реализуем базовый протокол, discovery capabilities, negotiation общей истории и генерацию packfile.
Пререквизиты курса:
- хотя бы раз в жизни пользовались Git'ом
- знание основ алгоритмов и структур данных (графы, хеш-таблицы)
Занятия
15 лекций, 8 практикЛекция 1
Версии проекта и устройство Git

Версии проекта и устройство Git
Версии проекта и устройство Git
- Снимок проекта вместо цепочки сохранённых изменений: объекты, граф и структурное переиспользование.
- Различие сохранённого снимка, вычисляемого diff и физической дельты в packfile.
- Адресация по содержимому, канонические байты и первый самостоятельный разбор loose object в .git.
Лекция 2
Точное устройство базы объектов

Точное устройство базы объектов
Точное устройство базы объектов
- Четыре типа объектов Git: blob, tree, commit и аннотированный tag.
- Пути, режимы файлов, родительские связи и особая роль первого родителя.
- Граница между неизменяемыми объектами и изменяемыми ссылками; lightweight и annotated tags.
Практика 1
Один OID, два репозитория

Один OID, два репозитория
Один OID, два репозитория
- SHAttered, chosen-prefix collisions и превентивная защита GitHub без выдуманного рассказа об успешном взломе Git hosting.
- Гипотетическая полная коллизия Git object через обычный push, принудительную передачу, fetch по OID, реплики и общий object pool.
- Почему валидная подпись верхнего commit или tag не восстанавливает утраченную однозначность нижнего OID.
Лекция 3
Изменяемое состояние Git

Изменяемое состояние Git
Изменяемое состояние Git
- Object database, refs, HEAD, index и working tree как разные области состояния.
- Index как будущий снимок; staged, unstaged и untracked состояния.
- Преобразования содержимого и безопасное конкурентное обновление refs через lockfile и compare-and-swap.
Практика 2
Авторизация внутри транзакции ссылок

Авторизация внутри транзакции ссылок
Авторизация внутри транзакции ссылок
- Реальный GitHub mass-assignment incident: успешная аутентификация ключа не заменяет право изменять выбранный ресурс.
- Protected refs, изменение политики во время операции и ошибки TOCTOU.
- Пакетные ограничения, оптимистическая проверка, блокировки и точка линеаризации.
Лекция 4
Граф коммитов и выбор истории

Граф коммитов и выбор истории
Граф коммитов и выбор истории
- История как DAG с несколькими корнями, слияниями и достижимостью по parent edges.
- Обходы графа с frontier и множеством посещённых вершин; DFS, BFS и несколько стартовых точек.
- Алгебра revision sets, A..B, отрицательные вершины и различие множества результата и порядка вывода.
Лекция 5
Общие предки и merge base

Общие предки и merge base
Общие предки и merge base
- Общие и лучшие общие предки как задача над частичным порядком достижимости.
- Criss-cross history и несколько равноправных merge bases.
- Почему время и кратчайший путь не заменяют корректный алгоритм; безопасное отсечение по поколениям.
Лекция 6
Алгоритмы построения diff

Алгоритмы построения diff
Алгоритмы построения diff
- Edit graph, longest common subsequence и сценарий вставок и удалений.
- Алгоритм Myers и его параметрическая сложность O(ND).
- Неоднозначность минимального ответа, а также компромиссы Myers, patience и histogram diff.
Практика 3
bisect на DAG и несовершенный оракул

bisect на DAG и несовершенный оракул
bisect на DAG и несовершенный оракул
- Множество кандидатов, веса достижимости и минимаксный выбор следующей проверки.
- Неравная стоимость тестов, результат skip и неоднозначный виновник.
- Построение адаптивной стратегии на асимметричном DAG и анализ границ её завершения.
Лекция 7
Трёхстороннее слияние и конфликты

Трёхстороннее слияние и конфликты
Трёхстороннее слияние и конфликты
- Base, ours и theirs как два изменения относительно общего состояния.
- Автоматическое объединение независимых правок и основные классы конфликтов содержимого и путей.
- Unmerged index со стадиями 1/2/3, переименования и границы восстановления намерения пользователя.
Лекция 8
От loose objects к packfile

От loose objects к packfile
От loose objects к packfile
- Последовательное развитие хранения: отдельные файлы, fanout-каталоги, packfile и несколько источников объектов.
- Индекс .idx: fanout table, отсортированные OID и поиск OID -> offset -> pack entry.
- Полный путь чтения объекта и независимые уровни CRC, checksum, OID, грамматики и связности.
Практика 4
Зелёный CI для уже не того кода

Зелёный CI для уже не того кода
Зелёный CI для уже не того кода
- tj-actions/changed-files и Ultralytics: mutable tag, poisoned cache и привилегированный release workflow.
- Три оси зелёного статуса: просмотренный код, фактические входы исполнения и выданные полномочия.
- Exact-state evidence, инвалидирование поздних результатов и условие публикации именно проверенного состояния.
Лекция 9
Дельта-сжатие внутри packfile

Дельта-сжатие внутри packfile
Дельта-сжатие внутри packfile
- Delta как ограниченная программа из инструкций copy/insert, а не история файла.
- REF_DELTA, OFS_DELTA, реальные байты и безопасное восстановление недоверенного входа.
- Цепочки баз, бюджеты ресурсов и компромисс между размером pack и стоимостью случайного чтения.
Лекция 10
Достижимость и жизненный цикл объектов

Достижимость и жизненный цикл объектов
Достижимость и жизненный цикл объектов
- Корни удержания, reflog и различие unreachable, dangling, recoverable и физически удалённого объекта.
- git gc, repack, prune, льготные периоды и гонка обслуживания с конкурентным писателем.
- Cruft packs и multi-pack-index как физическое хранение и поиск без изменения логических OID.
Практика 5
Планировщик физического хранения

Планировщик физического хранения
Планировщик физического хранения
- Representation DAG, кандидаты на базы дельт, глубина цепочки и обратные дельты.
- Совместная оптимизация размера хранения, стоимости чтения и обслуживания.
- Контрпример жадному выбору, CVE-2024-32020/32021 и публикация независимого проверенного поколения хранения.
Лекция 11
Путь fetch по сети

Путь fetch по сети
Путь fetch по сети
- Transport, discovery, запрос, согласование, передача pack и проверка результата как разные слои.
- Pkt-line и управляющие пакеты как явные границы сообщений и фаз.
- Protocol v2, capabilities и sideband: разделение данных, прогресса и ошибок на недоверенной границе.
Лекция 12
Согласование общей истории при fetch

Согласование общей истории при fetch
Согласование общей истории при fetch
- Сообщения want, have, ACK, ready и done как распределённый поиск общей части DAG.
- Negotiation frontier и безопасное отсечение только подтверждённых общих предков.
- Компромисс между числом раундов, работой графа и объёмом pack; корректное поведение при исчерпании бюджета.
Практика 6
Иерархические квоты без блокировки push

Иерархические квоты без блокировки push
Иерархические квоты без блокировки push
- Иерархия organization/project/repository, изменяемая видимость и раздельный учёт public/private данных.
- Reservations, ancestor counters, single writer, escrow, settlement и восстановление после сбоя.
- Построение автомата квот, сохраняющего жёсткий предел при параллельных push и переносе объёма между областями.
Лекция 13
Серверная сторона fetch

Серверная сторона fetch
Серверная сторона fetch
- Политика допустимых want: знание OID и физическое наличие объекта не дают права чтения.
- Типизированное относительное замыкание, pack-objects, thin pack и допустимые базы дельт.
- Проверка результата, shallow boundaries, filters, promisor objects и публикация remote-tracking refs.
Лекция 14
Три дорогих запроса и три индекса

Три дорогих запроса и три индекса
Три дорогих запроса и три индекса
- Почему обход предков, история пути и перечисление объектов требуют разных производных структур.
- Commit-graph и generation data, changed-path Bloom filters и reachability bitmaps.
- Точные гарантии каждого индекса и обязательный переход к object database при его отсутствии или неприменимости.
Практика 7
Миграция недоверенного проекта

Миграция недоверенного проекта
Миграция недоверенного проекта
- GitLab Bulk Import RCE и Direct Transfer: импортируемые данные, historical identity и current authority.
- Quarantine, allowlist полей, бесправные placeholder identities, отключённая automation и полное object closure.
- Импорт фиксированного экспорта: проверка полноты истории и условие публикации без переноса чужих полномочий. Миграция работающей системы остаётся резервом.
Лекция 15
Масштабирование больших репозиториев

Масштабирование больших репозиториев
Масштабирование больших репозиториев
- Sparse checkout и sparse index как управление материализацией путей и размером index.
- Partial clone, filters, promisor remotes и отложенная загрузка отсутствующих объектов.
- Независимые состояния видимости, наличия и достижимости; сквозной путь файла через хранение, протокол и локальную материализацию.
Практика 8
Merge queue как спекулятивный планировщик

Merge queue как спекулятивный планировщик
Merge queue как спекулятивный планировщик
- FIFO, optimistic retry и speculative prefix при ограниченном числе исполнителей.
- Устаревшие эпохи, инвалидация результатов, справедливость и потраченная впустую работа.
- Сравнение планировщиков по времени завершения, ожиданию и безопасности точного протестированного OID.