Зачем нужен Git и как он устроен
Зачем нужен Git и как он устроен
Любой проект, в котором работает больше одного человека (а часто и один), рано или поздно сталкивается с тремя вопросами: «что изменилось?», «когда это сломалось?» и «кто это сделал?». Система контроля версий отвечает на все три. Git - самая распространённая из таких систем, и именно с ней ты будешь работать каждый день как бэкенд-разработчик.
Без Git невозможны code review, CI/CD, параллельная командная работа, откат неудачного релиза и десятки других вещей, которые считаются стандартом индустрии. Понимание того, как Git устроен внутри, помогает не бояться его команд и быстро разбираться в нетривиальных ситуациях.
Централизованные и распределённые VCS
До Git были системы вроде SVN (Subversion) и CVS. Они работали по модели «один центральный сервер»: вся история хранилась на сервере, а разработчик получал только текущую версию файлов. Если сервер падал - история была недоступна.
Git - распределённая система. Каждый разработчик хранит у себя полную копию репозитория со всей историей. Это даёт несколько преимуществ:
- работа оффлайн - коммиты, просмотр истории, создание веток не требуют сети;
- скорость - почти все операции локальные;
- надёжность - потеря сервера не означает потерю истории, потому что у каждого участника есть полная копия;
- гибкий workflow - ветки стоят дёшево, merge и rebase делаются локально.
В централизованной модели разработчики - тонкие клиенты, а сервер - единственный держатель истории. В Git каждый узел равноправен: твой ноутбук, ноутбук коллеги и сервер GitHub - это три одинаково полноценные копии репозитория. Remote (GitHub/GitLab) - не «главный сервер», а просто общая точка синхронизации.
Объектная модель Git
Git хранит данные не как «список изменений», а как граф объектов. Есть четыре типа объектов:
blob - содержимое одного файла. Git не хранит имя файла в blob, только содержимое. Два файла с одинаковым содержимым - один blob.
tree - аналог директории. Содержит ссылки на blob-ы (файлы) и другие tree (поддиректории), плюс имена и права доступа.
commit - снимок проекта. Содержит ссылку на корневой tree, имя автора, дату, сообщение коммита и ссылку на родительский коммит (или несколько при merge).
tag - именованная ссылка на коммит (обычно для версий: v1.0.0, v2.3.1).
Каждый объект идентифицируется SHA-1 хешем своего содержимого. Это значит, что одинаковое содержимое всегда даёт один и тот же хеш, а любое изменение - другой. Git по сути является content-addressable storage.
Три зоны: working directory, staging area, repository
Git разделяет работу на три зоны, и это ключевая ментальная модель:
Working directory - файлы, которые ты видишь в файловом менеджере. Здесь ты пишешь код.
Staging area (index) - промежуточная зона. Ты выбираешь, какие именно изменения попадут в следующий коммит. Это позволяет коммитить не всё подряд, а логически связанные изменения.
Repository - папка .git/, где хранятся все объекты, ветки, конфиг. Коммит фиксирует то, что было в staging area.
Структура папки .git/
Когда ты делаешь git init, в проекте появляется скрытая папка .git/. Вот что внутри:
DAG: история как граф
История в Git - это направленный ациклический граф (DAG). Каждый коммит указывает на своего родителя (или родителей при merge). Ветки - это просто указатели (ссылки) на определённые коммиты.
В этом примере коммит D - merge-коммит с двумя родителями: C и F. Ветка main указывает на D, ветка feature/login указывает на F. Стрелки в графе всегда смотрят от потомка к родителю - именно так Git ходит по истории при git log: от текущего коммита назад во времени.
Такая модель даёт Git возможность эффективно работать с ветками: создание ветки - это запись 41 байта (SHA-1 хеш + перенос строки), а не копирование файлов.
Зачем VCS бэкенд-разработчику
На первый взгляд, контроль версий - это про «сохранение файлов». Но в реальной работе Git решает задачи, которые без него были бы мучительными:
Откат неудачного деплоя. Релиз ушёл в прод и сломал авторизацию. Без Git - паника, попытки вспомнить, что изменилось. С Git - git revert <hash> или деплой предыдущего тега, и через 2 минуты прод снова работает.
Параллельная работа. Три разработчика одновременно пилят три фичи в одном микросервисе. Каждый в своей ветке, код не мешает друг другу, merge request собирает изменения вместе.
Code review. Merge Request в GitLab/GitHub показывает ровно те строки, которые изменились. Ревьюер читает diff, оставляет комментарии, автор исправляет - и всё это отслеживается.
CI/CD. Pipeline запускается автоматически при push: линтеры, тесты, сборка Docker-образа, деплой. Без Git нет push, без push нет CI.
Аудит. git blame показывает, кто написал каждую строку. git log --follow показывает историю файла, даже если его переименовали. git bisect находит коммит, который сломал тесты, бинарным поиском.
Git ≠ GitHub
В реальной работе бэкенд-разработчика Git участвует в каждом этапе:
- разработка - ветки, коммиты, локальное тестирование;
- code review - merge/pull request на GitLab/GitHub;
- CI/CD - pipeline запускается при push, собирает и деплоит;
- откат - если релиз сломал прод,
git revertили деплой предыдущего тега; - аудит -
git blameиgit logпоказывают, кто, когда и зачем менял код.
Каждый из этих пунктов разберём отдельно дальше по треку. Начинаем с настройки Git: имя, email и SSH-ключ, без которых первый же push упрётся в ошибку.
Мини-задание
- Установи Git и проверь версию:
git --version - Создай пустой репозиторий:
git init test-repo && cd test-repo - Загляни внутрь папки
.git/и найди файл HEAD - посмотри, на что он указывает - Сделай первый коммит и посмотри, как изменилось содержимое
.git/objects/ - Выполни
git cat-file -t <hash>иgit cat-file -p <hash>для любого объекта, чтобы увидеть его тип и содержимое