Mercurial — у истоков

Mercurial (hg) — это распределенная система контроля версий.
Сейчас это мощный продукт, третий по популярности среди VCS (после Subversion и Git).

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

История

Первая версия (v0.1) была выпущена в 2005 году. Она была написана на Python всего за пару недель. В ней всего 600 строчек кода. Кстати, первая версия Git вышла в том же месяце, дней на 10 раньше.
changed files
Начинался проект Mercurial как proof-of-concept распределенной системы контроля версий и целью было создать минимальную распределенную VCS. Первая версия тестировалась на исходных кодах ядра Linux. Вернемся в 2005 год и посмотрим как же выглядел тогда Mercurial

Исходные коды

Итак, mercurial-0.1. Внутри кроме документации есть два важных файла:

  • hg — обертка для использования в командной строке
  • revfile.py — модуль для работы с репозиторием

Начинаем работу

Создадим свой новый репозиторий (внимание, с того времени синтаксис команд изменился, поэтому воспринимайте это как исторические факты, а не как учебник).

Раньше был «hg create», сейчас это «hg init»

$ mkdir myproject
$ cd myproject
$ /path/to/hg create

Вот весь список команд, которые умел делать Mercurial в своем первом релизе:

  • hg create — создать новый репозиторий в текущей папке
  • hg checkout [rev] — обновить репозиторий до ревизии (если не указана ревизия — то используется «tip», аналог HEAD’а)
  • hg add file1 file2 … — добавить файлы в репозиторий
  • hg delete file1 file2 … — удалить файлы из репозитория
  • hg commit — закоммитить изменения
  • hg history — просмотреть историю изменений
  • hg merge — произвести слияние (merge) двух версий файла

Добавим в наш репозиторий один файлик:

$ /path/to/hg add main.c
$ /path/to/hg commit
$ /path/to/hg history
0: -1 -1 9cb30b658726942b68289f047b063ae4e4718052
manifest nodeid: 29bed52b5b7cfd38d294508ace8e0498093cedf4
user: serge@thinkpad
changed files:
 main.c
description:
commit

На этом этапе разработчики решили, что заниматься синхронизацией репозиториев hg не будет, вместо этого нужно использовать сторонние утилиты типа «rsync» ну или самого обычного «cp». Также использовались сторонние утилиты для слияния (merge). По-моему это здравый подход.

А что внутри?

Обертка для пользовательского интерфейса находится в файле hg.
А весь основной код описан в файле revfile.py.

В нем есть такие классы:

  • revlog — файлы *.i и *.d
  • filelog — открывает лог связанный с файлом в репозитории
  • manifest — работа с manifest.i и manifest.d
  • changelog — работа с chanelog.i и changelog.d
  • repository — работа с репозиторием (все что внутри папки .hg)

Сейчас структура кода Mercurial похожа на изначальную — существует Repository Level, Storage Layer и UI layer.
Отдельно есть еще Utility Layer, связанный с вызовом отдельных платформо-зависимых компонентов.

Как же работает база репозитория?

Каждый коммит идентифицируется с помощью NodeId — 160-битного числа. Его значение зависит от содержимого файла в момент коммита и его положения в иерархии коммитов. Значение вычисляется как хэш SHA1. Кстати, в текущих версиях можно использовать сокращенный NodeId — первые 6 байт хэша (12 шестнадцатеричных символов).

Revlog — это фактически база изменений одного файла, в которой хранятся в запакованном виде (zip) изменения файла по ревизиям. Т.е. вместо полного файла в базе хранятся только его измененные части начиная с момента создания.

Процесс воссоздания файла по его revlog’у — это на удивление быстрая операция. Вычислительная сложность еще с первой версии составляла O(1). Для каждого revlog есть также файл с индексами, в котором на каждую ревизию хранится запись фиксированной длины содержащая NodeId ревизии, NodeId её родительских ревизий, размер данных в ревизии, смещение относительно начала базы и другие данные. Благодаря индексу поиск отдельной ревизии в базу выполняется быстрее.

Поскольку revlog работает только с одним файлом из репозитория, то существует специальный файл 00manifest.d (тоже по своей природе revlog), в котором хранится информация по всем файлам в данной ревизии (получается именно за счет манифеста и существуют атомарные коммиты).

И вот мы видим, что Changeset — это наш коммит, с которым связан манифест и все файлы описанные в манифесте. Changeset хранятся в changelog.d и changelog.i.

В чем же отличие между Changeset и Manifest? Все просто: Manifest — это список состояний файлов в репозитории на момент коммита, а changeset — это список измененных файлов с момента предыдущего коммита. Т.е. если какой-то файл входит в состав скажем 4-й ревизии, но не менялся с момента 1-й ревизии, то в manifest 4-й ревизии он войдет, а в changeset — нет. Кроме того в changeset хранятся автор коммита, сообщение коммита, дата и часовой пояс автора.

Каждый новый коммит создает новую запись в Changeset, в манифесте и в revlog’е каждого файла.

Настоящее будущее

Конечно, с момента выхода первого релиза многое изменилось, но основная идея и общая структура базы сохранилась. Честно, для меня нынешний Mercurial — это отличный пример приложения с простой архитектурой, простым кодом, его просто изучить, просто использовать.

Ну и напоследок — что почитать.

Если вы еще не знакомы с Mercurial — http://hginit.com
Если выбираете между Mercurial и Git: http://stevelosh.com/blog/2010/01/the-real-difference-between-mercurial-and-git/
Если хотите знать больше — http://mercurial.selenic.com/wiki

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s