О Mercurial

2020-01-26

Bitbucket убивает Mercurial. С февраля 2020 нельзя будет создавать Mercurial репозитории. А 1 июня 2020 все существующие Mercurial репозитории будут удалены. Git победил.

removing Mercurial

Если кто не в курсе, Git, Mercurial, а также менее известные Bazaar, Darcs и другие — это распределённые системы контроля версий (DVCS). В них можно коммитить локально, а потом пушить изменения в центральный репозиторий.

Самым популярным центральным репозиторием, или, как они себя сами называют, социальной сетью для разработчиков, уже давно является GitHub. Именно его недавно купила Microsoft. Как следует из названия, GitHub умеет только Git, и только его. И даже более или менее успешный его «форк» под названием GitLab тоже умеет только Git.

А я люблю Mercurial. Это первая DVCS, с которой я начал работать. Git в моей жизни появился позднее. На Mercurial я пересел с SVN. И это было небо и земля. Тогда на Google Code были только Subversion и Mercurial. А GitHub тогда вообще не было.

Google Code окончательно закрылся в 2016. И с тех пор Bitbucket был самым популярным публичным условно бесплатным хостингом с поддержкой Mercurial. И вот теперь Mercurial оттуда уходит. И исчезает последняя серьёзная причина вообще пользоваться Bitbucket, потому что GitHub таки лучше. И там и там имеется неограниченное количество закрытых репозиториев, с ограничениями на количество юзеров в таких репозиториях. А на открытые репозитории ограничений нет.

И что мне теперь делать? Можно прогнуться под тренд и переехать с Mercurial на Git. Какого-то чёрта Bitbucket не предоставил кнопки для этого. Все пути ведут лишь на пошаговые инструкции из 2011 года. На первый взгляд, работающие. Вот только URL репозитория может измениться. Примерно с той же степенью трудоёмкости можно переехать и на GitHub. Ну и зачем мне тогда Bitbucket?

А можно повредничать и принципиально остаться на Mercurial. Но нужно сменить хостинг. Хороший список альтернатив есть на сайте самого Mercurial.

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

  • Helix TeamHub — внезапно, от создателей другой системы контроля версий Perforce.
  • а вот, кажется, и всё. Savannah от GNU выглядит уж больно «традиционно».

Многие хостинги обычных наших виртуалочек предлагают в пару кликов поставить себе крутой Mercurial хостинг. Без волшебной кнопки, немного поработав гаечным ключом и бубном, можно аналогичное поставить на любой хостинг, если он у вас есть. Из таких приложений интересно выглядят:

  • Heptapod — форк GitLab с поддержкой Mercurial. Любопытно, что внутри там таки Git, плюс подпиленная версия hg-git.
  • Phabricator — комбайн на PHP. Есть ненулевой шанс водрузить это на shared хостинг.
  • Kallithea — самая «родная» для Mercurial, простенькая, и на Python.

«Классические» наборы багтрекеров плюс репозиториев, и далеко не только Mercurial, на мой взгляд, слишком страшны, чтобы хотеть их установить у себя. Но они давно есть, и нормально работают:

  • Savane — основа хостинга Savannah, что под крылом GNU.
  • Trac — старый добрый Trac.

На самом деле, чтобы просто дать доступ из браузера к репозиторию Mercurial, хватит и встроенного hg serve или hgweb. Но мне нужна ещё выкладка файлов, багтрекер, вики. Потому и гоняюсь за хостингами.

И общественные хостинги меня уже второй раз кидают. Сначала Google Code. Теперь Bitbucket. Может, действительно, для своих хомячковых проектов пока поднять свой? Что надёжнее, майкрософтовый GitHub или свой хостинг где-нибудь в виртуалочке с невнятными бэкапами?

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

В типичной ежедневной рутине, работая в одну каску, разницы, собственно, никакой и нет. Что git pull → что-то поменять → git addgit commitgit push. Что hg pullhg update → что-то поменять → hg commithg push. Никакой разницы.

Разница проявляется в более продвинутых концепциях.

versus

Ветки.

В Git, хоть все об этом забывают, ветки — лишь указатели на «головы» репозитория. То есть на последние коммиты графа коммитов. Причём указатель «текущей» ветки просто автоматически смещается на новый коммит при, собственно, коммите. Эти указатели живут совершенно отдельно от коммитов. Нет никакой истории перемещения указателей. Их вполне можно (не)удачно похерить. Или переставить не туда.

В общем случае, в Git нет возможности узнать, какой ветке принадлежал некий произвольный коммит в прошлом. Ветка — лишь указатель на «голову». Можно выяснить, что данный коммит является предком этой «головы». Но точно неизвестно, прямой это предок или был когда-то вмержен.

Когда с репозиторием работает много человек одновременно, особенно в одной ветке, граф коммитов естественным образом превращается в «косичку» из непрерывных ветвлений и слияний. Так как в Git невозможно понять, были эти слияния в рамках одной ветки или нет, там принято «спрямлять» историю. Rebase, чтобы «косичка» ветки стала прямой. Squash, чтобы «схлопнуть» ветку вообще в один коммит.

merge vs rebase

Это страшные команды переписывания истории. Хотя вроде как сам смысл существования систем контроля версий в том, чтобы всегда хранить всю историю в неизменном виде.

Указатели-ветки в локальном Git репозитории и удалённом репозитории на том же GitHub могут отличаться, и часто отличаются. Их надо синхронизировать. Именно этим занимаются pull (на самом деле fetch) и push. И при этом нужно ещё помнить, какие локальные ветки каким удалённым веткам соответствуют. Эти команды синхронизируют не только граф коммитов, но и местоположение указателей. В Git появляется разница в «происхождении» коммитов и веток. Всегда нужно указывать, какую ветку, из какого репозитория, мы подразумеваем. И слишком часто это происходит неявно.

А ещё в Git есть традиция «отстреливать» «головы» репозитория, которые оказались безымянными. То есть остались без указателя. Такие «головы» могут появиться случайно, но потом до них почти невозможно добраться. Это будут коммиты-призраки, которые потом будут тихо и незаметно уничтожены.

HG branches

В Mercurial всё значительно проще и мощнее. Имя ветки — это свойство коммита. «Текущая» ветка — это просто имя, которое будет записано в следующий коммит. И всё.

Всегда ясно, к какой ветке принадлежит эта «косичка» пересливающихся коммитов. Всегда ясно, какие ветки слились в этом мерже. Нет нужды в «спрямлении» истории, и так всё понятно.

Коммиты из других репозиториев, удалённых или локальных, по команде hg pull просто появляются у вас. У ветки появляется ещё одна «голова». Вы можете их игнорировать до поры до времени, а вмержить, когда вам будет угодно. Все коммиты равнозначны.

Да, в Mercurial есть официальный способ ветвления в виде простого копирования каталога с репозиторием локально. И между этими каталогами вполне нормально делать pull и push. То есть вполне нормально работать с двумя ветками одновременно. Попробуйте сделать это в Git, попробуйте переключить ветки без предварительного коммита.

Если какая-то голова стала не нужна, в Mercurial можно её явно закрыть. Не потерять, а потом втихушку удалить безвозвратно. А явно закрыть за ненадобностью. Специальным коммитом.

Mercurial значительно бережнее относится к истории коммитов. И никогда не предлагает переписать историю. Потому что это опасно. Потому что это противоречит сути контроля версий. Mercurial — молодец.

tools

Инструменты.

Mercurial написан на православном Python. Даже Python 3, с последних версий. Git как был месивом из C, и скриптов на Bash, так им и остался. Хотя хуки и там и там можно писать на чём угодно.

Git бесит меня непредсказуемым набором команд. Тут нет никакой системы. Одни и те же параметры в разных командах могут задаваться разными ключами. Никак не могу всего этого запомнить, приходится гуглить. Некоторые ключи могут полностью изменить суть команды. Некоторые команды делают сразу несколько разных вещей. git pull --rebase, да. А учитывая, что ключи можно задавать в конфиге, то это всё становится вообще плохо предсказуемым.

Mercurial изначально делали для людей. Набор команд чётко определён. Документация понятна. Команда делает ровно одно действие с предсказуемым результатом. Получение изменений из удалённых репозиториев — это hg pull. Применение полученных изменений к рабочей копии, если не требуется мерж, — это hg update. Если мерж требуется, то и нужно сделать явно hg merge, результаты которого нужно явно зафиксировать через hg commit.

А теперь быстро скажите мне, чем отличается git fetch && git merge от git pull, и git merge от git merge --no-ff?

Если вам нужен GUI, то, пожалуйста, есть TortoiseHg. Под все платформы. И везде удобный и полнофункциональный. Потому что Python. А вот тоже прекрасный TortoiseGit — только под Windows. А под Linux, извините, только разные клоны убогого gitk.

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

Я считаю Mercurial правильной системой контроля версий. А популярность Git считаю недоразумением. Стоит ради этой принципиальности упороться с хостингом? Или прогнуться под Git и не мучаться?