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
  • Уроки
  • База знаний
  • Собеседование
Часть I — Что такое Git и зачем

$ глава 1 · 30 минут

Зачем нужен контроль версий

Контроль версий - это вторая стихия, которую программист обязан понимать на уровне дыхания. Первая - файловая система. Без файловой системы - нет программ. Без контроля версий - нет совместной работы и нет надёжной истории.

Эта глава - про то, как люди жили до Git, что они делали, какие у них были проблемы, и почему Git устроен именно так. Глава нужна не для общего развития. Понимание контекста объясняет, почему в Git ровно такие команды и почему они себя ведут именно так.

1.1 Жизнь без контроля версий

Любой человек, который писал хоть что-то длиннее одного скрипта, знаком с таким набором файлов:

report.docx
report_v2.docx
report_v2_FINAL.docx
report_v2_FINAL_правки.docx
report_v3_СРОЧНО.docx
report_v3_СРОЧНО_правки_от_босса.docx

Это и есть контроль версий. Примитивный, ручной, но работает. Каждое сохранение - отдельный файл. Можно вернуться. Можно сравнить. Можно отменить.

Проблем у такого подхода ровно три, и все они становятся видны как только проект перерастает одного человека и неделю работы.

Первая. Имена файлов теряют смысл. Через два месяца невозможно понять, какая версия настоящая. «Финальная» - это та, в которой FINAL, или та, в которой _правки_от_босса. Эта проблема не решается дисциплиной. Она усиливается, чем больше людей и чем дольше срок.

Второй. Совместная работа невозможна. Если Маша и Петя одновременно правят report_v3, чьи правки останутся. Чтобы не пересечься, договариваются по очереди: «я работаю, не трогай». Это блокирует параллельную работу.

Третья. Дисковое место. Каждая копия - отдельный полный файл. На офисном документе 200 КБ это незаметно. На репозитории с 50 000 файлов и тысячей версий - это сотни гигабайт мусора.

Все три проблемы решаются автоматизацией: пусть инструмент сам хранит версии, сам помогает с конфликтами, сам экономит место. Так появились системы контроля версий.

1.2 Локальные системы контроля версий

Первое поколение - конец 1970-х, начало 1980-х. Системы вроде SCCS (Source Code Control System, 1972) и RCS (Revision Control System, 1982).

Они работали так. В одной директории - рабочие файлы. В соседней (RCS/) - служебная директория с историей. Каждое сохранение - это патч (diff) между предыдущей версией и новой.

project/
├── main.c
└── RCS/
    └── main.c,v       ← вся история main.c в формате патчей

Чтобы посмотреть версию недельной давности, RCS брал последнюю версию и обратно применял к ней патчи, шаг за шагом. Восстановить старую версию занимало секунды или минуты, в зависимости от размера истории.

Этот подход называется delta-based - основанный на дельтах. Хранится не каждая версия целиком, а только разница между соседними. Экономно по диску. Но плохо масштабируется: для очень старой версии нужно прокрутить все патчи начиная с последнего.

Главное ограничение - локальность. Вся история живёт у одного человека на одной машине. Если жёсткий диск умер - история пропала вместе с проектом.

1.3 Централизованные системы

Второе поколение - конец 1980-х и 1990-е. CVS (Concurrent Versions System, 1986), потом Subversion (SVN, 2000), потом Perforce.

Главная идея: положить историю на отдельный сервер. У сервера - полный архив. У разработчиков - только текущая рабочая копия плюс указание, какую версию они взяли.

       сервер (один на всю команду)
   ┌──────────────────────┐
   │   полная история     │
   │   всех файлов        │
   └──────────────────────┘
        ↑           ↑
        │           │
   ┌────┴────┐ ┌────┴────┐
   │ Маша    │ │ Петя    │
   │ (копия) │ │ (копия) │
   └─────────┘ └─────────┘

Это решило две проблемы из трёх. Совместная работа - пожалуйста: сервер видит, кто что трогает, и предупреждает о конфликтах. Дисковое место у разработчика - экономия: ему не нужна вся история, только текущий снимок.

Но появились свои проблемы.

Зависимость от сервера. Без сети - нет коммитов. Командировка, плохой интернет, упавший сервер - работа парализована. Можно править файлы локально, но история не записывается, ветки не создаются, история не смотрится.

Единая точка отказа. Если сервер сломался без бэкапа - потеряна вся история проекта. У разработчиков на машинах только текущая версия. Всё, что было раньше - пропало.

Ветвление дорогое. В SVN ветка - это копия всей директории на сервере. На большом проекте создание ветки и переключение между ветками занимало минуты, а иногда часы. От этого ветки боялись делать. Долгие feature-ветки превращались в кошмар слияния.

Несмотря на это, SVN был стандартом примерно с 2003 по 2010 год. Многие проекты с того времени до сих пор там и сидят.

1.4 Распределённые системы

Третье поколение - начало 2000-х. BitKeeper (1998), Monotone (2003), Darcs (2003), Mercurial (2005), Git (2005), Bazaar (2005).

Главная идея ломает все предыдущие: у каждого разработчика - полная копия истории. Не «снимок», как в SVN. Полная история. Каждый клон репозитория - это полноценный репозиторий.

   ┌─────────┐     ┌─────────┐     ┌─────────┐
   │ Маша    │     │ Петя    │     │ сервер  │
   │ полная  │ ←→  │ полная  │ ←→  │ полная  │
   │ история │     │ история │     │ история │
   └─────────┘     └─────────┘     └─────────┘

Сервер в распределённой системе технически не нужен. Маша и Петя могут синхронизироваться напрямую: «забери у меня изменения», «забери у тебя». На практике сервер всё равно появляется - он удобен как точка встречи и как резервная копия. Но он не главный. Он один из многих.

Что это даёт:

  • Работа без сети. Коммиты, ветки, история, поиск - всё локально. Сеть нужна только чтобы синхронизироваться с другими.
    • Скорость. Локальная операция - это чтение с диска. SVN-клиенту нужно сходить на сервер, дождаться ответа. Git-клиенту - нет.
    • Дешёвые ветки. Создание ветки - это запись одной строки в файле. Об этом подробно в branch. Для сравнения, в SVN это копирование директории.
  • Надёжность. История продублирована у каждого разработчика. Чтобы потерять её, нужно одновременно сломать все клоны.

Цена - учиться чуть дольше. У централизованной системы модель проще: есть сервер, я снимаю с него файлы, правлю, отправляю обратно. У распределённой - нужно понимать, что коммит на моей машине и коммит на сервере - это разные коммиты, пока я не сделал push. Эта разница ловится за неделю практики и потом уже не вспоминается.

1.5 Откуда взялся Git

Git появился в апреле 2005 года. У него был ровно один автор первой версии - Линус Торвальдс, тот же, который сделал ядро Linux.

История такая. С 2002 года ядро Linux использовало систему BitKeeper - коммерческую, но бесплатную для open source. В 2005 году компания BitMover, которая делала BitKeeper, отозвала бесплатную лицензию: один из разработчиков ядра реверс-инжинирил протокол. Линус оказался без системы контроля версий, при этом масштаб проекта был такой, что ни одна из существующих не справлялась с нужной производительностью.

Линус взял и за 10 дней написал первую версию Git. Цели были такие:

  1. Высокая скорость, особенно на больших репозиториях.
  2. Распределённость - никакого центрального сервера.
  3. Сильная защита от повреждений и подделок (отсюда хэши SHA, об этом в главе 3).
  4. Простая модель данных. Под капотом - четыре типа объектов и больше ничего.

Название «git» на британском сленге значит «придурок», «дурацкий человек». Линус пошутил, что назвал систему в свою честь. Шутка прижилась.

Первый коммит самой Git в её собственной истории сделан 7 апреля 2005 года. С тех пор Git разрабатывается коллективно, в основном на mailing list. Текущий ведущий мэйнтейнер - Junio Hamano, он подхватил проект от Линуса через несколько месяцев после старта.

1.5.1 Копнуть глубже: почему Git «выиграл»

В 2005 году конкурентами Git были Mercurial и Bazaar - обе распределённые, обе появились примерно тогда же. У Mercurial интерфейс был чище, у Bazaar - лучше документация. Git выиграл по двум причинам.

Первая - ядро Linux. Самый известный open-source проект сразу переехал на Git, и это создало гравитацию.

Вторая - GitHub. Платформа появилась в 2008 году и сделала работу с Git настолько удобной, что разработчики стали выбирать Git не за технические достоинства, а за GitHub. К 2013 году соотношение Git/Mercurial/Bazaar в популярных проектах было примерно 90 / 8 / 2.

Технически Mercurial был не хуже. Возможно, в чём-то лучше. Но в гонке инструментов техническое качество - лишь один из факторов, и часто не самый сильный.

1.6 Дельты и снимки

Это место - главное идейное отличие Git от всего, что было до него. И это место чаще всего пропускают в учебниках. Зря.

В системах вроде SVN, CVS, Perforce каждая версия файла хранится как разница (delta) от предыдущей версии. Чтобы получить файл в его виде на прошлой неделе, система начинает с текущей версии и накатывает дельты в обратном порядке. Или наоборот: начинает с самой первой версии и накатывает дельты вперёд.

SVN / CVS / Perforce - модель дельт:
версия 1: main.c полностью
версия 2: дельта (3 строки изменены)
версия 3: дельта (1 строка добавлена)
версия 4: дельта (5 строк удалено)
                ↑
        чтобы восстановить версию 4,
        собираем версии 1 + 2 + 3 + 4 цепочкой

Git устроен иначе. Каждая версия - это полный снимок (snapshot) всего проекта. Не разница, а целая фотография. Когда делается коммит, Git запоминает состояние каждого файла на этот момент.

Git - модель снимков:
коммит 1: снимок проекта целиком
коммит 2: снимок проекта целиком
коммит 3: снимок проекта целиком
коммит 4: снимок проекта целиком
                ↑
        чтобы посмотреть коммит 4,
        просто берём снимок 4

Кажется, что это расточительно. Если в файле изменилась одна строка, зачем сохранять весь файл заново. Так и есть, и Git использует две хитрости, чтобы не раздувать диск.

Дедупликация по содержимому. Каждый файл хранится по хэшу его содержимого. Если файл не менялся между коммитами - его хэш тот же, и Git не создаёт новую копию. Он переиспользует существующую. Получается, что хранится не «снимок проекта целиком», а «снимок каталога с указателями на файлы». Те файлы, которые не менялись,

  • это одни и те же записи на диске.

Сжатие в packfiles. Раз в какое-то время или при синхронизации Git собирает накопленные объекты в один сжатый файл - packfile

  • и применяет внутри него обычное delta-сжатие zlib. То есть «дельты» в Git тоже есть, но не как фундаментальная модель данных, а как оптимизация хранения. Логически - всё это снимки.

Эта разница - фундамент всего, что Git делает. Это объясняет, почему ветки дешёвые: ветка - это просто указатель на снимок. Почему git checkout мгновенный: он не пересобирает версию из дельт, он берёт готовую. Почему git log быстрый: он идёт по списку снимков, а не реконструирует историю заново.

1.6.1 Подводный камень: это не означает, что Git не показывает дельты

Когда выполняется git diff или открывается коммит на GitHub - видны строки + и -, как разница. Это вид, а не способ хранения. Git показывает разницу между двумя снимками, потому что разработчику удобнее смотреть на изменения. Но внутри хранятся два полных снимка, и Git каждый раз вычисляет разницу на лету.

Эта путаница встречается часто: люди говорят «Git хранит дельты», потому что видят дельты в выводе команд. Хранит он не их.

1.7 Что осталось от старых систем

В Git встречаются термины и идеи, которые легче понять, зная их происхождение.

  • Patch (патч) - текстовый файл с изменениями. Из RCS, потом из patch(1)/diff(1). Git умеет работать с патчами: git format-patch, git apply, git am. Это нужно при работе с проектами по mailing list, например ядром Linux.
    • Trunk, branch, tag - словарь SVN. Trunk в Git называется main (раньше master), но концепция «основной линии разработки» та же.
    • Commit, checkout - оба понятия пришли из централизованных систем. Checkout в SVN означало «забрать рабочую копию с сервера». В Git это слово используется в трёх разных смыслах: переключиться на ветку, восстановить файл, отделить HEAD. Эта путаница исправлена в 2019 году добавлением git switch и git restore, но старая команда никуда не делась.

Резюме

  • Системы контроля версий автоматизируют то, что раньше делалось ручными копиями файлов.
  • Эволюция шла в три этапа: локальные (RCS), централизованные (SVN), распределённые (Git).
  • В централизованной системе вся история - на сервере. В распределённой - у каждого разработчика своя полная копия.
  • Главная техническая особенность Git - модель снимков, а не дельт. Каждый коммит - это фотография проекта целиком.
  • Экономия достигается за счёт дедупликации по хэшу содержимого.
  • Git появился в 2005 году благодаря тому, что Linux-сообщество осталось без BitKeeper.

Контрольные вопросы

  1. Чем модель снимков отличается от модели дельт? Назови по одному примеру системы каждого типа.

    Показать ответ

    В модели дельт хранится разница между соседними версиями (так работают RCS, CVS, SVN, Perforce). Чтобы получить старую версию файла, система прокручивает цепочку патчей. В модели снимков каждая версия хранится целиком (так работает Git). Для экономии места одинаковые файлы дедуплицируются по хэшу содержимого.

  2. Почему распределённая система не зависит от центрального сервера? Что произойдёт, если сервер пропадёт?

    Показать ответ

    Каждый клон репозитория содержит полную историю. Сервер - это одна из равноправных копий, удобная точка встречи, но не единственный источник истины. Если сервер пропал, любой разработчик может развернуть новый сервер из своего клона - история не потеряется.

  3. В чём была проблема SVN с ветками, и почему в Git её нет?

    Показать ответ

    В SVN ветка - это копия всей директории проекта на сервере. Создание ветки требует копирования тысяч файлов; переключение - обновления рабочей копии через сервер. На больших проектах это медленно. В Git ветка - это файл из одной строки (40-символьный SHA) в .git/refs/heads/. Создание и переключение - мгновенные.

  4. Если файл не изменился между двумя коммитами, занимает ли он в Git место дважды?

    Показать ответ

    Нет, неверно. Git хранит полные снимки. Когда выполняется git diff, Git берёт два снимка и вычисляет разницу на лету. Дельты

    • это способ показа, не способ хранения. Дельты внутри packfile - это оптимизация места, не модель данных.
← Предыдущая00-prefaceСледующая →02-first-repo
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки