Pull to refresh

Сравнение Subversion и Mercurial (HG)

Reading time 7 min
Views 11K
Мое первое знакомство с системой контроля версий было еще в школе. Это был Subversion. В то время меня очень впечатлила его сила и возможности. Но шло время. Были не очень приятные моменты с переименовыванием файлов, каталогов и прочее (да, да здравствует svn 1.5, 1.6 и его вечные папки .svn). И все бы продолжалось в том же духе, если бы однажды в компании не задумались о смене системы контроля версий. Все случилось неожиданно быстро и предо мной возник Mercurial. Пришлось почитать о его особенностях, поспрашивать советов бывалых и вот я уже сам помогал своим коллегам разобраться в поведении и работе нового инструмента. Чем дольше я знакомился с Hg, тем больше он мне нравился, точнее, нравился его децентрализованный подход к контролю версий, Subversion же неизбежно отходил на второй план.
Однако, на новом месте работы мне снова пришлось вспомнить о Subversion, что, честно сказать, меня не обрадовало. К счастью, это не было безоговорочной политикой компании и предложить альтернативу было вполне реально, особенно учитывая, что некоторые сотрудники предпочли Git и успешно с ней работают. Значит, дело за малым – наглядно показать, в чем же преимущества работы с децентрализованными системами контроля версий: Git или Mercurial, но, в силу моего личного опыта, рассказать я решил про Hg. Собственно, эта статья есть краткое содержание круглого стола, проведенного мной с целью сравнения и смены системы контроля версий.


Существует множество мнений на счет того, какая именно система контроля версий лучше. Конечно, у каждого мнения есть свои аргументы за и против. Однако, взять хотя бы самый простой сценарий: над проектом работает 2 и более человек, и проект предполагает дальнейшую поддержку и развитие, скажем, в качестве библиотеки, то хранение его в svn доставит вам куда больше хлопот, чем хранение в Mercurial.
Перейдем же к конкретным особенностям, которые выделяют Mercurial среди остальных.

1. В отличие от svn commit, при команде hg push вы всегда узнаете, если кто-то успел поменять что-то в проекте и зафиксировал эти изменения на сервере.

svn commit — команда для отправки локальных изменений на удаленный сервер. Происходит атомарно. При этом заканчивается неудачей только в том случае, если версия, на которой основаны измененные файлы, уже не является последней на удаленном сервере. То есть если в директории есть два файла a.txt и b.txt и вы изменили только b.txt, а Петя только файл a.txt, команда commit завершится для вас успешно и если вы не выполните команду svn update (обновить все файлы в директории до последней версии), то вы об этом не узнаете. Так же svn поддерживает commit (и checkout) из любой папки проекта. Из-за этого, если вы делаете коммит в одной папке, то об изменених в другой вы, опять же, не узнаете.

hg push — команда для отправки наборов изменений, которые были сделаны раньше командой hg commit. Последняя команда фиксирует текущее состояние репозитория, однако делает это полностью локально. Только при совершении команды hg push — изменения пересылаются на удаленный сервер. Так как это команда для пересылки изменений, то она распространяется целиком на весь репозиторий. Таким образом, при этой команде будет фиксироваться полностью все состояние репозитория. И если кто-то успел сделать push раньше вас, то такая ситуация трактуется как многоловье и по умолчанию hg push завершится ошибкой, которая говорит, что ваше действие сделает новую голову (head) в удаленном хранилище.
Для полноты изложения дадим определение голове (head). Это ревизия, которая сама не является родителем какой-то другой ревизии в этом бранче. Многоголовье — ситуация, когда в одном бранче существует более одной головы.
Вернемся к нашей ситуации, когда нас кто-то опередил. После того, как нам вернули ошибку необходимо сделать команду hg pull, которая затянет нам все недостающие ревизии в наше локальное хранилище. Опять же, стоит отметить, что это действие никак не повлияет на наши файлы. Ни один из них не будет изменен. Эта команда просто добавляет в историю список изменений, которых у нас не было. Таким образом, если посмотреть на визуализацию текущей ситуации в репозитории, то мы увидим, что у нас образовалось многоголовье. Многоголовье в локальном хранилище не страшно. Ведь мы же сами его сделали. Гораздо опаснее, если мы создадим такую же ситуации на удаленном хранилище и кто-то успеет склонировать его к себе. Этот человек уже не сможет понять какая версия самая актуальная и как ему с ними работать. Он начнет бегать, кричать и ругаться, что все это слишком сложно и непонятно. Поэтому нам ни в коем случае нельзя совершать пуш в текущем состоянии (Mercurial нам это даст сделать только с использованием команды hg push --force, что лучше никогда не делать). Таким образом мы должны выполнить merge двух голов и получить одну, которую Mercurial сможет спокойно залить на удаленный сервер.

А теперь почему это может быть важным. Если по каким-то причинам другой разработчик изменил какой-то общий компонент (логику, интерфейс или еще что-то), а вы об этом не знали, то в случае с svn, на удаленном репозитории может оказаться неверный или даже некомпилирующийся код. При этом обнаружить первый такой косяк можно довольно быстро (все же мы делаем время от времени svn update и запускаем компиляцию, не правда ли?), а вот обнаружение другого косяка может затянуться до тестов. Логика работы Mercurial позволяет сразу узнать об изменении и сделать все проверки сразу при merge. Конечно, если вы не поинтересовались тем, что же было изменено параллельно с вашей работой, то вы рискуете попасть в такую же ситуацию, что и с svn, однако с одним отличием — в первом случае вы даже не знали о таком, а во втором забили.
Можно возразить, что, если человек не делает перед командой svn commit команду svn update и не проверяет код, то он сам виноват и никакого плюса тут нет, но это ошибочное мнение. Связка команд update/commit в svn не атомарна, а hg push — атомарная операция, что сильно упрощает жизнь.

2. При командах pull, merge вы не теряете свое состояние, которое вы еще не успели передать на удаленный сервер, в отличие от svn update.

Об этом уже упоминалось — при pull вы просто получаете набор неизвестных ранее изменений в историю без изменения файлов. А так как merge в Mercurial делается локально и с локальными ревизиями, то эти ревизии никуда не теряются. И при желании всегда можно удалить результат мержа и сделать его заново, либо полностью откатиться на ту версию репозитория, которая была до всех pull/merge. В svn же это нереально. После выполения svn update из вашей рабочей копии получается солянка из неизмененных, новых, автоматически смерженных файлов, файлов с конфликтами и обычных измененных вами файлов. При этом невозможно вернуть репозиторию состояние, которое у вас было до svn update. Никак.

3. Другая небольшая плюшка — откат на любую версию репозитория в Mercurial за считанные секунды. Единственное ограничение — ваша локальная копия должны быть чистой (без незафиксированных изменений). Это можно сделать с помощью hg update и указания нужной ревизии. При этом не требуется соединение с удаленным сервером, ведь вся история есть у вас локально. В svn это можно сделать только с удаленным сервером и только выкачав репозиторий заново в нужной ревизии.

4. Очень часто бывает, что нужно срочно что-то сделать с приложением. Например, к вам прибежали и срочно хотят получить билд (так как я занимаюсь мобильной разработкой, то такое бывает, хотя есть билдсервер). А у вас проект полностью разваленный, так как вы пилите какую-то супер фичу. В svn вам пришлось бы либо заново выкачивать все сорци с последней ревизии, либо пытаться откатить код до более-менее работающего состояния, либо сказать, что вы не можете этого сделать сейчас. Все варианты так себе и занимают обычно много времени. В любой же децентрализованной системе контроля версий вы можете сделать локальный commit, после чего откатиться на любую работающую версию кода и собрать проект. При этом после этого вы вернетесь ровно в то состояние репозитория, в котором вы были до того, как вас потревожили!

5. Другое частое явление — развитие проекта-библиотеки. И если вас неожиданно попросили собрать проект, который писался довольно давно и использовал библиотеку, которая была подключена svn external, то у вас могут быть большие проблемы. Он может просто не собраться, так как использовал старую библиотеку, которую успели переписать, переродить и так далее. Единственным выходом будет искать ревизию, с которой этот проект работал, и копипастить библиотеки. Вариант с небольшим допиливанием проекта для поддержки новой версии библиотеки не рассматриваем, так как вероятность такой возможности уменьшается экспоненциально c ростом времени. В Mercurial есть понятие подрепозитория, который ссылается не только на сам репозиторий, но и на его конкретную версию. Таким образом вы всегда знаете с использованием какой версии библиотеки работает проект и можете не бояться ее изменений.

6. Я уже упоминал про branch в hg. Стоит напомнить то, как ветки реализованы в svn. Это просто папки с полной копией репозитория. Начиная с версии 1.5 появилась история таких файлов, однако все же это просто папки и файлы. Для работы с ними нужно придерживаться специальных правил именования и содержания. В mercurial же ветки — именованое состояние репозитория и его изменения. И при работе с ветками все ровно также, как и при работе в одной ветке. Это гораздо проще.

7. Аналогично пункту 6 можно говорить и про tags.

8. Напоследок хочется сказать про историю коммитов. Если в svn она чисто прямолинейная и невозможно сказать, что вы на самом деле основывались на ревизии 2, а не 10 и понятия не имел о том, что происходит с 3 по 10, хотя ваш коммит следует под номером 11… В Mercurial будет явно видно, что вы работали с репозиторием версии 2 и сделали коммит 3' и только после этого смержили свои изменения 3' с ревизией 10. Такие данные важны, если хочется проследить некоторые важные события.

Подводя итог статьи, я хочу заметить, что за все время работы с Mercurial я не могу назвать его минусов. Некоторые грешат на его скорость, некоторые на сложность команд commit/push, чтобы закоммитить изменения на сервер. Для меня же это не является проблемой, так как особых задержек в скорости я не замечал (были, конечно, при огромных размерах репозитория, но это мелочи жизни), а второе — дело привычки.

Вообще, некоторые перечисленные преимущества Mercurial могут быть перенесены и на другие децентрализованные системы контроля версий. И этой статьей я просто хотел бы собрать все основные плюсы использования таких систем, опираясь на Mercurial, как пример, с которым я сам имел опыт работы. Надеюсь, эта статья будет полезной, если вам придется испытать муки выбора.

p.s. В итоге решили переходить на Git =)
Tags:
Hubs:
+7
Comments 9
Comments Comments 9

Articles