2014-02-08

О Mercurial

Я люблю Mercurial.

Вообще-то это про кроссовки.
Из систем контроля версий я имел дело с Visual SourceSafe (VSS, в его старой реинкарнации в виде файлопомойки на сетевом диске), с Perforce (P4), Subversion (SVN), Mercurial (Hg) и Git.

Про VSS и P4 вообще вспоминать не буду. Эти серверные блокировки — тот еще ад при совместной разработке. А вот Subversion был первым действительно удобным и крутым инструментом контроля версий. Я сразу же поднял репозиторий на localhost и засунул в него все свои локальные проекты (типа исходников сайтов).


Потом наступила эра распределенных DVCS. Я как раз подумывал о том, что выкладывать финальные сборки ПО на сайт в архивах — это некошерно, нужно заводить публичные репозитории. И захотел попробовать распределенные системы и найти хостинг. Sourceforge уже тогда выглядел загибающимся. GitHub тогда еще не существовал. А на гуглокоде поддерживались только Subversion и Mercurial. Так я стал пользоваться Mercurial. И до того он мне понравился, что локальный Subversion репозиторий быстренько превратился в кучку меркуриаловых (тогда это было не так тривиально). С тех пор публичные мои проекты так и живут на гуглокоде, а более менее приватные наброски обосновались на Bitbucket. Почти все на Mercurial (хотя немного грешу и GitHubом).


Чем для меня Mercurial оказался лучше, чем Subversion? Ну тут все сводится к вопросу, чем распределенные системы контроля версий лучше централизованных. Чтобы не совсем капитанить, скажу, чего мне не хватает из Subversion.

Не хватает трекинга каталогов. Ни Git, ни Mercurial этого не умеют. В результате, если где-то нужен какой-нибудь пустой каталог, в нем появляется какой-нибудь readme, в котором объясняется, зачем этот каталог тут есть и почему он пустой. Как в старом добром CVS.

Не хватает чудесных и мощнейших properties. Иметь произвольный набор метаданных (включая, в конце концов, mime type) для каждого файла — это круто. Жаль, что никто из DVCS этого не сделал. Тогда бы не понадобились странные файлы .gitignore и .hgignore.

Теги — это все же (постоянные) указатели на ревизии. В Subversion это так (как мы помним, копия какого-нибудь trunk в подкаталог в tags). В Git это тоже ссылка (refs). А вот в Mercurial зачем-то изобрели .hgtags. Это хорошо в том смысле, что все изменения тегов (ежели такие имеют место быть) записаны в историю (изменений этого файла). Я, правда, не совсем понимаю, как быть с изменением этого файла в разных ветках. Получается так, что, находясь в разных ветках, будешь видеть разный набор тегов?


Таки чем же Mercurial (и любая DVCS) лучше Subversion (и любой централизованной VCS)?

Оффлайновые коммиты. Это действительно очень-очень удобно, когда ты можешь делать коммиты независимо от доступа к интернетам. Можно забуриться в какой-нибудь гондурас и спокойно работать, коммитя тогда, когда нужно, а не тогда, когда проклюнется доступ к центральному репозиторию.

Коммиты отделены от публикации изменений. Коммитить, когда нужно, очень важно. Не тогда, когда фича вылизана и сборка не падает. А когда запилил очень важный класс, родил его за несколько часов. Теперь нужно спокойно закоммитить, затем сделать перерыв, попить чаю. Потом вернуться и начать править тесты, что выльется еще в несколько коммитов и исправлений. Потом, когда все будет вылизано и разложено по полочкам маленьких, четких, коротких и понятных коммитов, можно сделать и пуш. И не обязательно пуш в центральный репозиторий. Если работаешь над фичей совместно с Васей, и Васе нужен твой код, но он еще сырой, можно запушить и только Васе. Система контроля версий не запутается, в конце концов все сольется, запушится и попадет куда надо.

Оффлайновый доступ к истории. История хранится локально. И лазать по ней, делать bisect, искать, когда была внесена ошибка можно офигенно быстрее, чем в Subversion. И опять таки, без интернетов.

Надежность. Это больше для сисадминов. Больше не нужно нежно оберегать Subversion репозиторий, делать его бэкапы. Ибо полная (ну почти) история проекта есть на машине каждого разработчика. Потерять что-то становится почти нереально. Правда, возрастает риск "утечек", если кто-то что и унесет, то унесет всю историю, включая все фейлы и случайно закоммиченные пароли. От форка не отмажешься.

Локальный репозиторий. Сделать его для чего угодно — одна команда. В Subversionе же нужно еще мучаться с file:// urlами.


Чем Mercurial лучше, чем Git? Буду холиварить.

Команды Mercurial гораздо более понятны. Они делают ровно одну вещь и выдают вполне внятные и краткие сообщения об ошибках. А в Git тот же pull на самом деле делает fetch и merge. А вот pull --rebase делает совсем другое: fetch и rebase. А банальная попытка сделать clone локального Git репозитория в соседний каталог, с последующим пушем в него (что вполне обычный кейс для Mercurial), выдала целый экран предупреждений и ошибок, оказывается, не bare репозиторий в Git по умолчанию не принимает пуши и требуется явное его конфигурирование.

Хотел сказать, что у Mercurial лучше документация. У Mercurial есть своя книга (как и у Subversion). Но Git тоже обзавелся книгой. Ничья.

Из GUI самым лучшим является черепашка (TortoiseSVN, TortoiseGit, TortoiseHg). Вот только черепашка для Subversion и Git — только под Windows (для Git это вообще самый безболезненный способ установки и использования под Windows). Нормального полноценного GUI для Git для Linux я так и не нашел. Есть неплохие штуки для коммитов и просмотра истории, но это далеко не весь функционал. А вот TortoiseHg (спасибо кросплатформенным Python и Qt) — функционален, удобен и одинаков на всех платформах.

Ветки. Тому, что в Mercurial называется branch нет аналогов в других системах контроля версий. И это мощная штука. В Mercurial название ветки является свойством каждого коммита. В результате целая группа коммитов, неважно, сколько над ними работало разработчиков и как переплелась история, является объединенной в ветку. В Git такого нет. В Git ветка — это лишь указатель на голову репозитория. Лишь одну голову. В результате возникает практика использования rebase, чтобы искуственно выровнять естественно запутанную историю. Чтобы уменьшить количество ветвлений и голов. Потому что иначе, при наличии лишь указателей на верхушку, нельзя понять, к чему относится коммит посередине. Rebase же — это изменение истории. Пусть и локального репозитория. Изменение нескольких коммитов в прошлом. Меня это пугает. Ибо сам смысл существования систем контроля версий заключается в том, что история зафиксирована навсегда. К тому же в Git эти указатели на головы/ветки/теги являются метаинформацией где-то сбоку от основной истории изменения файлов. Сам видел однажды, как они тупо терялись, и репозиторий превращался в лапшу. Ветки Mercurial надежно зашиты в историю, в каждый коммит, и это хорошо.

Не понимаю, почему Git является гораздо более популярным, чем Mercurial. Неужели мнение Линуса так ценно? И ведь в Mercurial даже пошли навстречу: добавили bookmarks — полный аналог указателей-веток в Git.

Кстати, Mercurial — наиболее универсальная система контроля версий, он умеет работать и с Git репозиториями, и с Subversion. А еще Mercurial (спасибо Python), имеет кучу разных расширений. А еще в Mercurial есть встроенный http сервер, что бывает полезно для расшаривания репозитория в локальной сети.