История систем контроля версий — от tar.gz до Git
Прежде чем разбираться, как устроен Git, полезно понять, почему он вообще выглядит именно так. Многие странности Git — git add перед git commit, отдельный index, концепт staging area, distributed-модель — это не «странные дизайн-решения», а конкретные ответы на конкретные боли предыдущих систем контроля версий. Если вы знаете, какие боли решал Git, то поведение Git становится логичным.
Этот урок — короткая история контроля версий. Не для развлечения — а потому что Git унаследовал кое-что хорошее (от CVS и Subversion), отказался от кое-чего плохого (от тех же CVS и Subversion), и придумал нечто принципиально новое (distributed модель), что переломило индустрию. К концу урока вы будете понимать, чем Git отличается от того, что было до него.
Эра до VCS: 1970-е — начало 1980-х
Представьте: 1975 год, мейнфрейм, программисты пишут код на FORTRAN или COBOL, в команде 10 человек. Как они работали с версиями?
Координация была голосовой: «Боб, я редактирую файл, не трогай». Бэкапы — ручное копирование с суффиксом .bak, .old, .20-may. Конфликты решались «кто последний сохранил, тот и прав». При смене программиста никто не знал, какие изменения вносил предыдущий — комментарии в коде типа «// Mike: rewrote this on May 12 because date was wrong» были единственной формой истории.
Это, очевидно, не масштабируется. Когда команда больше 10 человек или проект длиннее 6 месяцев — нужна автоматизация.
SCCS и RCS (1972, 1982): первые VCS
В 1972 году в Bell Labs появилась SCCS (Source Code Control System) — первая система контроля версий. Она хранила историю файла как набор delta (изменений) от первой версии до текущей. Файлы версионировались по одному: sccs get foo.c забирало последнюю версию, sccs delta foo.c фиксировало изменения.
В 1982 году в Purdue University появилась RCS (Revision Control System) — улучшенная SCCS, бесплатная, более удобная в использовании. RCS хранила обратные дельты: текущая версия в чистом виде, а старые — как diff от текущей. Это было быстрее при работе с актуальной версией.
Главное ограничение SCCS и RCS — они работали по одному файлу. Если ваше «изменение» затрагивает 5 файлов — нет атомарной операции, которая зафиксирует все 5 как единое целое. Каждый файл версионируется независимо, ревизии не синхронизированы.
Кроме того, для редактирования использовалась модель lock-modify-unlock: программист делал co -l foo.c (checkout с lock), редактировал, делал ci foo.c (commit). Пока файл залочен — другие не могли редактировать. На практике это означало: один человек ушёл в отпуск, не сняв lock, — пять коллег ждут.
CVS (1986-1990): первый коммерческий успех
В 1986 году появилась CVS (Concurrent Versions System) — революция. CVS была надстройкой над RCS, но добавила два критических улучшения:
- Версионирование всего проекта, а не отдельных файлов. Commit фиксирует состояние нескольких файлов одновременно.
- Optimistic concurrency: вместо locks — слияние при конфликте. Несколько программистов могут одновременно редактировать один файл; CVS попытается объединить изменения автоматически, если они в разных строках.
CVS получила огромное распространение. К концу 1990-х CVS был стандартом в open-source: Linux kernel (до Git), FreeBSD, GNU Emacs, gcc — всё на CVS.
Но у CVS были серьёзные проблемы. Главные:
- Атомарность commit не была реальной. Если сервер падал посреди commit — половина файлов записана, половина нет. Никакой транзакционности.
- Переименование файлов не отслеживалось. Если вы переименовали
foo.cвbar.c, CVS воспринимал это как удалениеfoo.cи создание новогоbar.c. Вся история терялась. - Branches и tags были косметикой, реализованной через метки в файлах. Branching был тяжёлой операцией, поэтому в CVS-проектах ветки делались редко.
- Бинарные файлы требовали ручного флага. Без флага CVS пытался применять текстовые diff к бинарникам и портил их.
Subversion (2000): «CVS done right»
В 2000 году CollabNet запустил проект Subversion (SVN) с прямым лозунгом «CVS done right». Цель: исправить все боли CVS, оставив привычную модель клиент-сервер.
SVN был очень популярен в 2000-2010-х. Apache Foundation, многие корпорации, Python project, Ruby on Rails — все использовали SVN. В России и СНГ SVN ещё до начала 2020-х был основной VCS в энтерпрайз-проектах (банки, госконторы), где переход на Git происходил медленно.
Но у SVN остался корневой недостаток CVS: всё крутится вокруг центрального сервера. Если у вас нет интернета — вы не можете сделать commit. Если сервер упал — никто из команды не может работать. Если вы за городом и хотите посмотреть, что изменилось два года назад — нужно скачать diff с сервера.
В то же время в начале 2000-х распределённая работа становилась нормой: open-source проекты разрастались до тысяч контрибьюторов в разных часовых поясах, у которых интернет не всегда стабильный. Назрел переход к распределённым VCS.
Распределённые VCS (2003-2005): революция
В 2003 году появилась Monotone — первая широко известная распределённая VCS (DVCS). В ней не было центрального сервера: каждый клиент имел полную копию репозитория, включая всю историю. Commit был локальной операцией. Обмен изменениями между разработчиками — через push/pull, peer-to-peer.
Monotone была экспериментом, не получила массового распространения. Но идею подхватили:
- Mercurial (2005, Matt Mackall) — DVCS, написана на Python+C, простая в использовании
- Bazaar (2005, Canonical) — DVCS от Canonical (которые делают Ubuntu)
- Git (2005, Linus Torvalds) — DVCS, написана за 2 недели, без оглядки на удобство
Все три появились в 2005 году. Не случайно — это год, когда мир созрел к распределённой модели.
Почему Linus написал Git: BitKeeper drama
История появления Git — это технический детектив, который стоит знать каждому DE. Дело было так.
К 2002 году разработка ядра Linux была настолько масштабной, что CVS и Subversion не справлялись. Linus Torvalds выбрал коммерческую DVCS BitKeeper от компании BitMover. BitKeeper была отличной системой — DVCS, быстрая, надёжная — но коммерческой. Однако автор BitKeeper Larry McVoy сделал бесплатную лицензию для open-source проектов, и Linux kernel её использовал с 2002 по 2005 год.
За месяц Linus написал систему, которая через 10 лет вытеснит всех конкурентов. Как? Потому что у него был очень специфический набор требований:
- Скорость на больших репозиториях. Linux kernel — ~50 тысяч файлов, миллионы commits. CVS и SVN еле справлялись.
- Распределённая модель. Тысячи контрибьюторов в разных часовых поясах должны работать без центральной координации.
- Целостность данных. Никаких полу-применённых commit, никаких порчей файлов. SHA-1 хеши на каждом уровне.
- Branching должен быть дешёвым. В Linux каждая фича — отдельная ветка, и веток сотни одновременно.
- Простая модель данных. Сложные форматы хранения трудно дебажить. У Git — три типа объектов и всё.
Эти требования сформировали Git таким, какой он есть. Каждое странное решение Git — это ответ на одно из этих требований.
Что унаследовал Git, что отбросил
Подытожим. Git — это синтез трёх десятилетий опыта VCS:
Особенности, унаследованные от SCCS/RCS: сама идея версионирования файлов.
От CVS: атомарный commit, охватывающий несколько файлов. Концепт «состояние проекта в целом».
От Subversion: транзакционность операций, отслеживание переименований, инструменты diff.
От Monotone/Mercurial: distributed-модель, peer-to-peer обмен.
Что Git изобрёл сам:
- Content-addressable storage: каждый объект адресуется своим SHA-1 хешом. Из этого следует целостность, immutability, удобный merge.
- Snapshots, не дельты: каждый commit — это полный снимок дерева, а не diff от предыдущего. Из этого — скорость операций.
- Cheap branching: ветка — это просто файл с одной строкой (SHA-1 коммита). Создание ветки стоит наносекунды.
В следующем уроке разберём подробно, чем distributed-модель отличается от centralized, и почему это переломило индустрию.
Unix-философия: do one thing wellПопробуй сам
Если у вас уже установлен Git, попробуйте посмотреть на исторический commit Linus в репозитории самого Git:
mkdir -p ~/git-sandbox/lesson-01-history
cd ~/git-sandbox/lesson-01-history
git clone --depth 1 https://github.com/git/git.git
cd git
git log --reverse --format="%h %ad %s" --date=short | head -5
Если Git ещё не установлен, не страшно — поставим его в модуле 3. Пока просто читайте.