Git и Mercurial - сравни и сравни


520

Некоторое время назад я использовал Subversion для своих личных проектов.

Я все больше и больше слышу о Git, Mercurial и DVCS в целом.

Я хотел бы дать повод всему DVCS, но я не слишком знаком с любым вариантом.

Каковы некоторые из различий между Mercurial и Git?

Примечание: я не пытаюсь выяснить, какой из них «лучший» или даже какой, с которого мне следует начать. Я в основном ищу ключевые области, где они похожи, и где они отличаются, потому что мне интересно знать, как они отличаются с точки зрения реализации и философии.



2
Возможный дубликат В чем разница между Mercurial и Git?
Nawfal

Ответы:


451

Отказ от ответственности: я использую Git, следую за разработкой Git в списке рассылки git и даже немного помогаю Git (в основном, gitweb). Я знаю Mercurial из документации и некоторых из обсуждения на IRC-канале #revctrl на FreeNode.

Спасибо всем людям на IRC-канале #mercurial, которые предоставили помощь по Mercurial для этой статьи



Резюме

Здесь было бы неплохо иметь некоторый синтаксис для таблицы, что-то вроде PHPMarkdown / MultiMarkdown / Maruku расширение Markdown

  • Структура репозитория: Mercurial не допускает слияния осьминогов (с более чем двумя родителями) и не помечает объекты без фиксации.
  • Теги: Mercurial использует версионный .hgtagsфайл со специальными правилами для тегов для каждого репозитория, а также имеет поддержку локальных тегов в .hg/localtags; в Git теги - это ссылки, находящиеся в refs/tags/пространстве имен, и по умолчанию они автоматически выбираются при извлечении и требуют явного нажатия.
  • Ветви: В Mercurial основной рабочий процесс основан на анонимных заголовках ; Git использует легкие именованные ветки и имеет особый вид веток (ветки удаленного отслеживания ), которые следуют за ветками в удаленном хранилище.
  • Имена и диапазоны ревизий : Mercurial предоставляет номера ревизий , локальные для репозитория, и основывает относительные ревизии (считая от наконечника, то есть текущей ветви) и диапазоны ревизий на этой локальной нумерации; Git предоставляет способ ссылки на ревизию относительно кончика ветви, а диапазоны ревизий являются топологическими (основаны на графике ревизий)
  • Mercurial использует отслеживание переименования , в то время как Git использует обнаружение переименования для обработки переименований файлов.
  • Сеть: Mercurial поддерживает «умные» протоколы SSH и HTTP, а также статический протокол HTTP; современный Git поддерживает «умные» протоколы SSH, HTTP и GIT и «тупой» протокол HTTP (S). Оба имеют поддержку файлов комплектов для автономного транспорта.
  • Mercurial использует расширения (плагины) и установленный API; Git имеет скриптируемость и установленные форматы.

Есть несколько вещей, которые отличают Mercurial от Git, но есть и другие вещи, которые делают их похожими. Оба проекта заимствуют идеи друг у друга. Например, hg bisectкоманда в Mercurial (ранее расширение bisect ) была вдохновлена git bisectкомандой в Git, в то время как идея git bundleбыла вдохновлена hg bundle.

Структура репозитория, хранение ревизий

В Git есть четыре типа объектов в его объектной базе данных: объекты BLOB- объектов, которые содержат содержимое файла, объекты иерархического дерева, которые хранят структуру каталогов, включая имена файлов и соответствующие части прав доступа к файлам (разрешение на выполнение файлов, являющееся символической ссылкой) , объект коммита, который содержит информацию об авторстве, указатель на снимок состояния репозитория при ревизии, представленной коммитом (через объект дерева верхнего каталога проекта) и ссылки на ноль или более родительских коммитов, и объекты тегов, которые ссылаются на другие объекты и могут быть подписан с использованием PGP / GPG.

Git использует два способа хранения объектов: свободный формат, где каждый объект хранится в отдельном файле (эти файлы записываются один раз, и никогда не изменяются), и упакованный формат, в котором многие объекты хранятся с дельта-сжатием в одном файле. Атомарность операций обеспечивается тем, что ссылка на новый объект записывается (атомарно, используя трюк create + rename) после записи объекта.

Git-репозитории требуют периодического обслуживания с использованием git gc(чтобы уменьшить дисковое пространство и повысить производительность), хотя в настоящее время Git делает это автоматически. (Этот метод обеспечивает лучшее сжатие репозиториев.)

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

Mercurial использует журнал транзакций для обеспечения атомарности операций и использует усечение файлов для очистки после неудачной или прерванной операции. Revlogs только для добавления.

Рассматривая структуру хранилища в Git по сравнению с Mercurial, можно увидеть, что Git больше похож на объектную базу данных (или файловую систему с адресным содержимым), а Mercurial больше похож на традиционную реляционную базу данных с фиксированным полем.

Различия:
в Git объекты дерева образуют иерархическую структуру; В Mercurial файл манифеста имеет плоскую структуру. В объекте Git blob хранят одну версию содержимого файла; в Mercurial filelog хранит всю историю одного файла. (если не учитывать здесь любые сложности с переименованиями). Это означает, что есть разные области операций, где Git будет быстрее, чем Mercurial, все остальные вещи считаются равными (например, слияния или отображение истории проекта), и области, где Mercurial будет быстрее, чем Git (например, применение исправлений или отображение история одного файла).Эта проблема может быть не важна для конечного пользователя.

Из-за структуры с фиксированными записями структуры журнала изменений Mercurial , коммиты в Mercurial могут иметь только до двух родителей ; коммиты в Git могут иметь более двух родителей (так называемое «слияние осьминога»). Хотя вы можете (теоретически) заменить слияние осьминога серией слияний с двумя родителями, это может вызвать сложности при конвертации между репозиториями Mercurial и Git.

Насколько я знаю, у Mercurial нет эквивалента аннотированных тегов (объектов тегов) из Git. Частным случаем аннотированных тегов являются подписанные теги (с подписью PGP / GPG); эквивалент в Mercurial можно сделать с помощью GpgExtension , расширение которого распространяется вместе с Mercurial. Вы не можете пометить объект без фиксации в Mercurial, как вы можете это сделать в Git, но это, я думаю, не очень важно (некоторые репозитории git используют теговый блоб для распространения открытого ключа PGP, который используется для проверки подписанных тегов).

Ссылки: ветки и метки

В Git ссылки (ветки, ветки удаленного отслеживания и теги) находятся вне DAG коммитов (как они должны). Ссылки в refs/heads/пространстве имен ( локальные ветви ) указывают на коммиты и обычно обновляются с помощью «git commit»; они указывают на верхушку (голову) ветви, поэтому такое название. Ссылки в refs/remotes/<remotename>/пространстве имен ( ветви с удаленным отслеживанием ) указывают на фиксацию, следуют за ветками в удаленном репозитории <remotename>и обновляются «git fetch» ​​или эквивалентными. Ссылки в refs/tags/пространстве имен ( теги ) обычно указывают на коммиты (легкие теги) или объекты тегов (аннотированные и подписанные теги) и не предназначены для изменения.

Теги

В Mercurial вы можете дать постоянное имя для ревизии, используя тег ; теги хранятся аналогично шаблонам игнорирования. Это означает, что глобально видимые теги хранятся в .hgtagsфайле с контролем версий в вашем хранилище. Это имеет два следствия: во-первых, Mercurial должен использовать специальные правила для этого файла, чтобы получить текущий список всех тегов и обновить такой файл (например, он читает самую последнюю зафиксированную ревизию файла, а не проверенную версию); во-вторых, вы должны зафиксировать изменения в этом файле, чтобы новый тег был виден другим пользователям / другим репозиториям (насколько я понимаю).

Mercurial также поддерживает локальные теги , хранящиеся в них hg/localtags, которые не видны другим (и, конечно, не передаются)

В Git теги являются фиксированными (постоянными) именованными ссылками на другие объекты (обычно это объекты тегов, которые в свою очередь указывают на коммиты), хранящиеся в refs/tags/пространстве имен. По умолчанию при извлечении или отправке набора ревизий git автоматически выбирает или выдвигает теги, которые указывают на выборку или отправку ревизий. Тем не менее, вы можете в некоторой степени контролировать, какие теги выбираются или отправляются .

Git обрабатывает легкие теги (указывающие непосредственно на коммиты) и аннотированные теги (указывающие на объекты тегов, которые содержат сообщение тега, которое необязательно включает сигнатуру PGP, которая, в свою очередь, указывает на фиксацию), немного по-разному, например, по умолчанию он рассматривает только аннотированные теги при описании фиксирует использование "git description".

Git не имеет строгого эквивалента локальных тегов в Mercurial. Тем не менее, лучшие практики git рекомендуют настроить отдельный общедоступный пустой репозиторий, в который вы помещаете готовые изменения и из которого другие клонируют и извлекают. Это означает, что теги (и ветви), которые вы не нажимаете, являются частными для вашего хранилища. С другой стороны, вы также можете использовать пространство имен, отличное от heads, remotesили tags, например, local-tagsдля локальных тегов.

Личное мнение: По моему мнению, теги должны находиться вне графика ревизий, поскольку они являются внешними по отношению к нему (они являются указателями на график ревизий). Теги должны быть не версионными, но передаваемыми. Выбор Mercurial использования механизма, аналогичного механизму игнорирования файлов, означает, что он должен или обрабатывать .hgtagsспециально (файл в дереве переносим, ​​но обычный он версионен), или иметь теги, которые являются только локальными ( .hg/localtagsне версионными, но непередаваемый).

ветви

В Git локальная ветвь (tip tip или head branch) - это именованная ссылка на коммит, где можно вырастить новые коммиты. Ветвь также может означать активную линию развития, то есть все коммиты, достижимые из кончика ветви. Локальные ветви находятся в refs/heads/пространстве имен, поэтому, например, полное имя ветви 'master' - 'refs /head / master'.

Текущая ветвь в Git (имеется в виду проверенная ветвь и ветвь, куда пойдет новый коммит) - это ветка, на которую ссылается ссылка HEAD. Можно иметь HEAD, указывающий непосредственно на коммит, а не на символическую ссылку; эта ситуация, когда вы находитесь в анонимной неназванной ветви, называется отсоединенной HEAD («git branch» показывает, что вы находитесь в «(без ветви)»).

В Mercurial есть анонимные ветви (главы филиалов), и можно использовать закладки (через расширение закладки ). Такие ветви закладок являются чисто локальными, и эти имена (до версии 1.6) не передавались с помощью Mercurial. Вы можете использовать rsync или scp, чтобы скопировать .hg/bookmarksфайл в удаленный репозиторий. Вы также можете использовать, hg id -r <bookmark> <url>чтобы получить идентификатор ревизии текущей подсказки закладки.

Начиная с 1.6, закладки можно нажимать / тянуть. На странице BookmarksExtension есть раздел « Работа с удаленными репозиториями» . Разница в том, что в Mercurial имена закладок являются глобальными , в то время как определение 'remote' в Git описывает также сопоставление имен веток из имен в удаленном репозитории с именами локальных ветвей удаленного отслеживания; Например, refs/heads/*:refs/remotes/origin/*отображение означает, что можно найти состояние ветки 'master' ('refs /head / master') в удаленном хранилище в ветке удаленного отслеживания origin / master ('refs / remotes / origin / master').

Mercurial также имеет так называемые именованные ветви , где имя ветви внедряется в коммит (в наборе изменений). Такое имя является глобальным (передается при получении). Эти имена ветвей постоянно записываются как часть метаданных набора изменений. С современным Mercurial вы можете закрыть «именованную ветку» и остановить запись названия ветки. В этом механизме наконечники веток рассчитываются на лету.

По моему мнению, «именованные ветви» Mercurial следует называть коммит-метками , потому что это то, что они есть. Существуют ситуации, когда «именованная ветвь» может иметь несколько подсказок (несколько бездетных коммитов), а также может состоять из нескольких непересекающихся частей графа ревизий.

В Git нет эквивалента этих «встроенных веток» Mercurial; Более того, философия Git заключается в том, что хотя можно сказать, что ветвь включает в себя некоторую фиксацию, это не означает, что коммит принадлежит какой-то ветке.

Обратите внимание, что документация Mercurial по-прежнему предлагает использовать отдельные клоны (отдельные репозитории), по крайней мере, для долгоживущих веток (одна ветвь на рабочий процесс репозитория), то есть ветвление путем клонирования .

Отводы в толкании

Mercurial по умолчанию толкает все головы . Если вы хотите выдвинуть одну ветвь ( одну головку ), вы должны указать ревизию наконечника ветви, которую вы хотите подтолкнуть. Вы можете указать подсказку ветки по номеру ревизии (локально для репозитория), по идентификатору ревизии, по имени закладки (локально по отношению к репозиторию, не передается) или по встроенному имени ветки (именованная ветвь).

Насколько я понимаю, если вы отправите ряд ревизий, которые содержат коммиты, помеченные как находящиеся в некоторой «именованной ветви» на языке Mercurial, у вас будет эта «именованная ветвь» в репозитории, в который вы нажимаете. Это означает, что имена таких встроенных веток («именованные ветви») являются глобальными (по отношению к клонам данного репозитория / проекта).

По умолчанию (в зависимости от push.defaultпеременной конфигурации) "git push" или "git push < remote >" Git будет выдвигать совпадающие ветви , то есть только те локальные ветви, у которых уже есть эквивалент в удаленном репозитории, в который вы помещаете. Вы можете использовать --allопцию git-push ("git push --all"), чтобы протолкнуть все ветви , вы можете использовать "git push < remote > < branch >", чтобы протолкнуть данную отдельную ветку , и вы можете использовать "git push < remote > HEAD ", чтобы нажать текущую ветку .

Все вышеперечисленное предполагает, что Git не настроен, какие ветви проталкивать через remote.<remotename>.push переменные конфигурации.

Филиалы в получении

Примечание: здесь я использую терминологию Git, где «выборка» означает загрузку изменений из удаленного репозитория без интеграции этих изменений с локальной работой. Это то, что делает " git fetch" и " hg pull".

Если я правильно понимаю, по умолчанию Mercurial извлекает все заголовки из удаленного репозитория, но вы можете указать ветку для извлечения через " hg pull --rev <rev> <url>" или " hg pull <url>#<rev>", чтобы получить одну ветку . Вы можете указать <rev>, используя идентификатор ревизии, имя «именованной ветви» (ветка, встроенная в журнал изменений) или имя закладки. Однако имя закладки (по крайней мере, на данный момент) не передается. Все ревизии «именованных веток», которые вы получаете, принадлежат к переводу. «hg pull» хранит кончики веток, которые он выбирает как анонимные безымянные головы.

В Git по умолчанию (для пульта 'origin', созданного "git clone", и для удаленных, созданных с помощью "git remote add") " git fetch" (или " git fetch <remote>"), получает все ветви из удаленного репозитория (из refs/heads/пространства имен) и сохраняет их в Пространство refs/remotes/имен. Это означает, например, что ветвь с именем 'master' (полное имя: 'refs /heads / master') в удаленном 'origin' будет сохранена (сохранена) как ветвь удаленного отслеживания 'origin / master' (полное имя: 'refs / пультов ДУ / происхождение / мастер ').

Вы можете получить одну ветку в Git, используя git fetch <remote> <branch>- Git будет хранить запрошенную ветку (и) в FETCH_HEAD, которая похожа на Mercurial безымянные головы.

Это всего лишь примеры стандартных сценариев мощного синтаксиса refspec Git: с помощью refspecs вы можете указать и / или настроить, какие ветви нужно выбрать, и где их хранить. Например, по умолчанию регистр «извлекать все ветви» представлен символом подстановки «+ refs /heads / *: refs / remotes / origin / *», а «выборка одной ветви» является сокращением для «refs /heads / <branch>:». , Refspecs используются для отображения имен ветвей (refs) в удаленном репозитории на локальные имена refs. Но вам не нужно много знать о refspecs, чтобы иметь возможность эффективно работать с Git (в основном благодаря команде "git remote").

Личное мнение: Лично я считаю, что «именованные ветви» (с именами ветвей, встроенными в метаданные набора изменений) в Mercurial представляют собой неверный дизайн с его глобальным пространством имен, особенно для распределенной системы контроля версий. Например, давайте возьмем случай, когда у Алисы и Боба есть «именованная ветвь» с именем «for-joe» в своих репозиториях, ветвях, которые не имеют ничего общего. Однако в хранилище Джо эти две ветви будут рассматриваться как одна ветвь. Таким образом, вы как-то придумали соглашение, защищающее от столкновений имен веток. Это не проблема с Git, где в репозитории Joe для for-joe ответвление от Алисы будет «alice / for-joe», а от Bob это будет «bob / for-joe».

В «ветках закладок» Mercurial в настоящее время отсутствует встроенный механизм распространения.

Различия:
эта область является одним из основных различий между Mercurial и Git, как сказали Джеймс Вудьятт и Стив Лош в своих ответах. Mercurial, по умолчанию, использует анонимные легкие кодовые строки, которые в его терминологии называются «заголовками». Git использует легкие именованные ветви с инъективным отображением для отображения имен ветвей в удаленном репозитории на имена ветвей удаленного отслеживания. Git «вынуждает» вас называть ветви (ну, за исключением одной безымянной ветви, ситуация, называемая отделенным HEAD), но я думаю, что это лучше работает с тяжелыми ветвями, такими как тематический ветвь, то есть несколько ветвей в одной парадигме репозитория.

Имена ревизий

В Git есть много способов присвоения имен ревизиям (например, описано на странице руководства git rev-parse ):

  • Полное имя объекта SHA1 (40-байтовая шестнадцатеричная строка) или его подстрока, которая является уникальной в репозитории.
  • Символическое имя ссылки, например, «master» (ссылается на ветку «master»), или «v1.5.0» (ссылается на тег), или «origin / next» (ссылается на ветку удаленного отслеживания)
  • Суффикс ^к параметру ревизии означает первого родителя объекта коммита, ^nозначает n-го родителя коммита слияния. Суффикс ~nпараметра ревизии означает n-го предка коммита в прямой строке первого родителя. Эти суффиксы могут быть объединены, чтобы сформировать спецификатор ревизии, следующий по пути из символической ссылки, например, 'pu ~ 3 ^ 2 ~ 3'
  • Вывод «git description», т. Е. Ближайшего тега, за которым, возможно, следует тире и несколько коммитов, за которыми следует тире, «g» и сокращенное имя объекта, например, v1.6.5.1-75- g5bf8097.

Существуют также спецификаторы ревизий, включающие reflog, не упомянутые здесь. В Git каждый объект, будь то коммит, тег, дерево или блоб, имеет свой идентификатор SHA-1; Существует специальный синтаксис, например, «следующий: Документация» или «следующий: README» для ссылки на дерево (каталог) или BLOB-объект (содержимое файла) в указанной ревизии.

В Mercurial также есть много способов именования наборов изменений (описанных, например, в hg manpage):

  • Простое целое число рассматривается как номер редакции. Необходимо помнить, что номера ревизий являются локальными для данного репозитория ; в другом хранилище они могут быть разными.
  • Отрицательные целые числа обрабатываются как последовательные смещения от наконечника, где -1 обозначает наконечник, -2 обозначает исправление до наконечника и так далее. Они также локальны для хранилища.
  • Уникальный идентификатор ревизии (40-значная шестнадцатеричная строка) или его уникальный префикс.
  • Имя тега (символическое имя, связанное с данной ревизией), или имя закладки (с расширением: символическое имя, связанное с данным заголовком, локальное для репозитория), или «именованная ветвь» (метка коммита; ревизия, заданная «именованной ветвью», tip (бездетная фиксация) всех коммитов с данным ярлыком коммитов, с наибольшим номером ревизии, если таких подсказок более одного)
  • Зарезервированное имя «tip» - это специальный тег, который всегда идентифицирует самую последнюю редакцию.
  • Зарезервированное имя «ноль» указывает на нулевую ревизию.
  • Зарезервированное имя "." указывает на рабочий каталог родителя.

Различия
Как вы можете видеть, сравнивая приведенные выше списки, Mercurial предлагает номера ревизий, локальные для репозитория, а Git - нет. С другой стороны, Mercurial предлагает относительные смещения только от 'tip' (текущей ветви), которые являются локальными для репозитория (по крайней мере, без ParentrevspecExtension ), в то время как Git позволяет указать любой коммит, следующий из любой подсказки.

Самая последняя редакция называется HEAD в Git и «tip» в Mercurial; в Git нет нулевой ревизии. И у Mercurial, и у Git может быть много root (может быть несколько коммитов без родителей; обычно это результат присоединения ранее отдельных проектов).

Читайте также: Много разных статей о спецификациях ревизий в блоге Элайджи (для новичков).

Личное мнение: я думаю, что номера ревизий переоценены (по крайней мере, для распределенной разработки и / или нелинейной / ветвистой истории). Во-первых, для распределенной системы контроля версий они должны быть либо локальными по отношению к репозиторию, либо требовать особого подхода к некоторому репозиторию в качестве центрального органа нумерации. Во-вторых, более крупные проекты с более длинной историей могут иметь количество ревизий в диапазоне 5 цифр, поэтому они предлагают лишь незначительное преимущество по сравнению с сокращенными до 6-7 символьными идентификаторами ревизий и подразумевают строгое упорядочение, в то время как ревизии упорядочены только частично (я имею в виду, что ревизии n и n + 1 не обязательно должны быть родительскими и дочерними).

Ревизионные диапазоны

В Git диапазоны ревизий являются топологическими . Обычно встречающийся A..Bсинтаксис, который для линейной истории означает диапазон ревизий, начинающийся с A (но исключая A) и заканчивающийся на B (т. Е. Диапазон открыт снизу ), является условным ^A Bобозначением («синтаксический сахар»), для которого команды обхода истории означают все Коммиты, достижимые из B, исключая те, которые достижимы из A. Это означает, что поведение A..Bдиапазона полностью предсказуемо (и весьма полезно), даже если A не является предком B: A..Bозначает, что диапазон ревизий от общего предка A и B (объединение базы ) в редакцию Б.

В Mercurial диапазоны редакций основаны на диапазоне номеров редакций . Диапазон указывается с использованием A:Bсинтаксиса и, в отличие от Git, действует как закрытый интервал . Также диапазон B: A - это диапазон A: B в обратном порядке, что не относится к Git (но см. Примечание о A...Bсинтаксисе ниже). Но такая простота имеет свою цену: диапазон ревизий A: B имеет смысл, только если A является предком B или наоборот, т.е. с линейной историей; в противном случае (я предполагаю, что) диапазон непредсказуем, и результат является локальным для репозитория (поскольку номера ревизий являются локальными для репозитория).

Это исправлено в Mercurial 1.6, который имеет новый диапазон топологических ревизий , где «A..B» (или «A :: B») понимается как набор ревизий, которые являются потомками X и предками Y. , Я думаю, эквивалентно '--ancestry-path A..B' в Git.

Git также имеет обозначения A...Bдля симметричной разницы ревизий; это означает A B --not $(git merge-base A B), что означает все коммиты, достижимые из A или B, но исключая все коммиты, достижимые из них обоих (достижимые от общих предков).

переименовывает

Mercurial использует отслеживание переименования для обработки переименований файлов. Это означает, что информация о том, что файл был переименован, сохраняется во время фиксации; в Mercurial эта информация сохраняется в виде «расширенной разницы» в метаданных filelog (file revlog). Следствием этого является то, что вы должны использовать hg rename/ hg mv... или вы должны помнить, чтобы запустить, hg addremoveчтобы сделать обнаружение переименования на основе сходства.

Git является уникальным среди систем контроля версий в том, что он использует обнаружение переименования для обработки переименований файлов. Это означает, что тот факт, что файл был переименован, обнаруживается в тот момент, когда это необходимо: при выполнении слияния или при отображении diff (если запрошено / настроено). Это имеет то преимущество, что алгоритм обнаружения переименования может быть улучшен и не замораживается во время фиксации.

И Git, и Mercurial требуют использования --followопции для отслеживания переименований при отображении истории одного файла. Оба могут следовать за переименованиями при отображении построчной истории файла в git blame/ hg annotate.

В Git git blameкоманда может следить за перемещением кода, а также перемещать (или копировать) код из одного файла в другой, даже если перемещение кода не является частью правильного переименования файлов. Насколько я знаю, эта функция уникальна для Git (на момент написания, октябрь 2009 г.).

Сетевые протоколы

И Mercurial, и Git поддерживают выборку и передачу в репозитории в одной и той же файловой системе, где URL-адрес репозитория - это просто путь файловой системы к репозиторию. Оба также поддерживают выборку из комплекта файлов .

Mercurial поддерживает выборку и передачу по SSH и HTTP протоколам. Для SSH требуется доступная учетная запись оболочки на конечном компьютере и копия hg, установленная / доступная. Для доступа по HTTP hg-serveтребуется запуск скрипта или Mercurial CGI, и Mercurial должен быть установлен на сервере.

Git поддерживает два вида протоколов, используемых для доступа к удаленному репозиторию:

  • «умные» протоколы , которые включают доступ через SSH и через собственный протокол git: // (by git-daemon), требуют наличия git, установленного на сервере. Обмен в этих протоколах состоит из согласования клиентом и сервером того, что у них общего, а затем генерации и отправки файла пакета. Modern Git включает поддержку «умного» протокола HTTP.
  • «тупые» протоколы , которые включают HTTP и FTP (только для извлечения) и HTTPS (для передачи через WebDAV), не требуют, чтобы git был установлен на сервере, но они требуют, чтобы хранилище содержало дополнительную информацию, сгенерированную git update-server-info(обычно запускаемую из ловушки) ). Обмен состоит из того, что клиент проходит цепочку коммитов и загружает свободные объекты и файлы пакетов по мере необходимости. Недостатком является то, что он загружает больше, чем строго требуется (например, в угловом случае, когда имеется только один пакетный файл, он будет загружен целиком, даже при получении только нескольких ревизий), и что для завершения может потребоваться много соединений.

Расширение: скриптируемость против расширений (плагинов)

Mercurial реализован на Python , а некоторый основной код написан на C для повышения производительности. Он предоставляет API для написания расширений (плагинов) как способ добавления дополнительных функций. Некоторые функции, такие как «ветки закладок» или подписывание ревизий, предоставляются в расширениях, распространяемых с Mercurial, и требуют их включения.

Git реализован на C , Perl и в скриптах оболочки . Git предоставляет много команд низкого уровня ( сантехника ), подходящих для использования в скриптах. Обычный способ введения новой функции - это написать ее как Perl или скрипт оболочки, а когда пользовательский интерфейс стабилизируется, переписать его в C для производительности, переносимости, а в случае сценария оболочки, избегая угловых случаев (эта процедура называется встроенной ).

Git опирается на форматы [репозитория] и [сетевые] протоколы. Вместо языковых привязок есть (частичные или полные) повторные реализации Git на других языках (некоторые из них являются частично повторными реализациями и частично обертывают команды git): JGit (Java, используемый EGit, Eclipse Git Plugin), Grit (Ruby) , Далвич (Python), git # (C #).


TL; DR


32
Что можно добавить, так это то, что hg очень старается препятствовать переписыванию истории (это можно сделать только с помощью расширений: mq, histedit, rebase), в то время как git делает это «из коробки» (и выглядит как часть сообщества даже поощряет это).
Тонфа

80
Я думаю, что «переписывание истории» звучит излишне негативно. То , что я поощряем мерзавец это люди , чтобы рассмотреть историю их публикации. Другие люди должны потреблять эту историю. Никто (даже вы) не заинтересован во всех ваших фиксациях "упс, забыли файл". Никого не волнует ряд входящих слияний, через которые вы прошли, отслеживая ветвь вверх по течению и работая над новой функцией. Такие вещи значительно усложняют понимание истории (и связанных с ней инструментов) и не представляют никакой ценности.
Дастин

5
@Jakub: именованные ветви - это то, чего нет в git. Это просто поле в описании cset (и это часть истории, поэтому оно неизменно, если вы не измените хеши и т. Д.). Что-то вроде веток git - это закладки («именованные головы»), но в настоящее время они не могут передаваться удаленно (вы не импортируете удаленные закладки при извлечении). stevelosh.com/blog/entry/2009/8/30/… очень хорошо это объясняет.
Тонфа

28
«Изначально Mercurial поддерживал только одну ветвь для каждого рабочего процесса хранилища, и это видно». Нет Mercurial изначально не поддерживал именованные ветки, но вы всегда могли иметь столько анонимных веток, сколько душе угодно в одном репо. Сравните это с мерзавцем, который делает анонимное ветвление огромной болью. Вы в значительной степени должны придумать имя для каждой маленькой ветки, если хотите что-то сделать (и не собирать мусор).
Стив Лош

17
@SteveLosh: вы, кажется, думаете, что иметь много анонимных веток в Mercurial - это хорошо, но мне это кажется ужасным. Как вы расскажете им все по отдельности? И вы, кажется, думаете, что присвоение имен веткам в Git - это огромная трудность, но если у вас есть цель создать ветку, то у вас есть готовое имя. Если у вас нет цели, не переходите. Я не вижу, как Mercurial предлагает какую-либо выгоду здесь. Я вижу только боль и растерянность.
иконоборчество

57

Я думаю, что вы можете почувствовать, на что похожи эти системы или чем они отличаются, пролистав эти два видео:

Линус Торвальдс на Git ( http://www.youtube.com/watch?v=4XpnKHJAok8 )
Брайан О'Салливан на Mercurial ( http://www.youtube.com/watch?v=JExtkqzEoHY )

Оба они очень похожи по дизайну, но сильно отличаются по реализациям.

Я использую Mercurial. Насколько я понимаю, Git отличается от git тем, что он отслеживает содержимое файлов, а не сами файлы. Линус говорит, что если вы переместите функцию из одного файла в другой, Git расскажет вам историю этой отдельной функции в процессе перемещения.

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

Git лучше работает как толстый клиент SVN, чем Mercurial. Вы можете тянуть и толкать против сервера SVN. Эта функциональность все еще находится в разработке в Mercurial

И у Mercurial, и у Git есть очень хорошие решения для веб-хостинга (BitBucket и GitHub), но Google Code поддерживает только Mercurial. Кстати, у них есть очень подробное сравнение Mercurial и Git, которое они сделали для того, чтобы решить, какой из них поддерживать ( http://code.google.com/p/support/wiki/DVCSAnalysis ). В нем много полезной информации.


8
Я бы рекомендовал прочитать все комментарии на этой кодовой странице Google. Информация кажется несколько предвзятой и не соответствует моему опыту. Я как рт.ст., и использовал его экстенсивно в течение года или около того . Я использую GIT почти исключительно сейчас. Есть вещи, которые мне нужно выполнить, чтобы сделать git проще, а hg - почти невозможно (хотя некоторые любят называть это «усложнением»). Базовый git так же прост, как и базовый hg.
Дастин

11
Дастин, может быть, перечислите некоторые из этих случаев "Git Easy, HG не так много"?
Грегг Линд

1
@ knittl нет, это не так. Главным образом потому, что им было бы неудобно развертывать его, так как в git отсутствует интеллектуальный протокол http (большинство внешних интерфейсов Google основаны на http).
Тонфа

2
@tonfa: в настоящее время разрабатывается интеллектуальный протокол HTTP для Git (например, в списке рассылки git есть исправления, и они находятся в 'pu' = ветке предлагаемых обновлений в репозитории git.git).
Якуб Наребски

4
На данный момент Google Code также поддерживает Git.
Андрей Киреŭŭ

30

Я недавно написал в блоге о моделях ветвления Mercurial и включил сравнение с моделью ветвления в git. Возможно, вам будет интересно: http://stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/


@Steve Losh: Я хотел прокомментировать эту запись в блоге (о неназванной ветке, известной как отсоединенная HEAD, и о git-fetch, извлекающей все ветви, а не одну), но я получил ошибку 500 на сервере.
Якуб Наребски

1
@Jakub Narębski Бьюсь об заклад, проблема в вашем имени не-ASCII. Я почти уверен, что столкнулся с той же проблемой на другом сайте, и оказалось, что привязка Python Askimet задыхается от Unicode. Я взгляну.
Стив Лош

@Steve Losh: Спасибо за информацию, после «unidecoding» моего имени я смог оставить комментарий. Очень хорошее описание ветвления в Mercurial (но я все еще думаю, что оно уступает ;-))
Якуб Наребски

@ SteveLosh Я призываю вас расширить этот ответ для более полного обзора Mercurial. Прямо сейчас, главным ответом, к сожалению, является в основном реклама для git, потому что его автор не использовал Mercurial широко и не понимает, как его эффективно использовать. Было бы хорошо, если бы другой ответ дал, так сказать, меркуриальную точку зрения.
Уоррен Дью

25

Я использую оба довольно регулярно. Основное функциональное различие заключается в способе ветвления имен Git и Mercurial в репозиториях. В Mercurial имена ветвей клонируются и извлекаются вместе с их наборами изменений. Когда вы добавляете изменения в новую ветвь в Mercurial и отправляете ее в другой репозиторий, имя ветки переносится одновременно. Таким образом, имена ветвей более или менее глобальны в Mercurial, и вы должны использовать расширение Bookmark, чтобы иметь только локальные облегченные имена (если вы хотите; Mercurial, по умолчанию, использует анонимные облегченные кодовые строки, которые в его терминологии таковы: называется "головы"). В Git имена веток и их инъективное отображение на удаленные ветки хранятся локально, и вы должны управлять ими явно, что означает знание того, как это сделать.

Как другие заметят здесь, есть много и много мелких различий. Дело в ветвях - это большая разница.


2
См. Также этот пост для хорошего объяснения о четырех видах веток в Mercurial: stevelosh.com/blog/entry/2009/8/30/…
Мартин Гайслер,


11

Mercurial почти полностью написан на python. Ядро Git написано на C (и должно быть быстрее, чем Mercurial), а инструменты написаны на sh, perl, tcl и используют стандартные утилиты GNU. Таким образом, необходимо доставить все эти утилиты и интерпретаторы вместе с системой, которая их не содержит (например, Windows).

Оба поддерживают работу с SVN, хотя поддержка AFAIK svn для git в Windows не работает (может быть, я просто невезучий / хромой, кто знает). Также есть расширения, которые позволяют взаимодействовать между git и Mercurial.

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

Их базовые наборы команд очень похожи (init, clone, add, status, commit, push, pull и т. Д.). Итак, основной рабочий процесс будет таким же. Также есть TortoiseSVN-подобный клиент для обоих.

Расширения для Mercurial могут быть написаны на python (не удивительно!), А для git они могут быть написаны в любой исполняемой форме (исполняемый бинарный файл, скрипт оболочки и т. Д.). Некоторые расширения сумасшедшие мощные, как git bisect.


9
Ядро Mercurial написано на C слишком к вашему сведению (но, вероятно, оно меньше, чем git).
Тонфа

1
Я использую Git-SVN на Windows без каких-либо проблем. Это использует Cygwin (единственный правильный способ использовать git на Windows, если вы спросите меня). Не могу говорить за msysgit.
Дэн Молдинг

@Dan Molding: Да, у меня возникли проблемы с msysgit. Может быть, нужно попробовать порт Cygwin (у меня был плохой опыт использования Cygwin ранее, поэтому я избегал его). Спасибо за совет!
elder_george

Мне лично не нравится вторжение cygwin в реестр для хранения пользовательских данных. Это PITA, чтобы заставить его работать с USB-накопителя и синхронизировать локальную копию диска c: \, когда я хочу работать быстрее, чем мой USB-накопитель. : - /
Крис К

1
Я использую плагин Git для Visual Studio, упомянутый выше, и производительность текущей версии хорошая. Он выполняет инструменты командной строки для выполнения работы, поэтому я не думаю, что это значительно снизит производительность в больших проектах.
Стюарт Эллис

11

Если вам нужна хорошая поддержка Windows, вы можете предпочесть Mercurial. TortoiseHg (плагин для проводника Windows) может предложить простой в использовании графический интерфейс для довольно сложного инструмента. Как указано здесь, у вас также будет плагин для Visual Studio . Однако, в прошлый раз, когда я пытался, интерфейс SVN не работал так хорошо в Windows.

Если вы не возражаете против интерфейса командной строки, я бы порекомендовал Git. Не по технической причине, а по стратегической. Скорость принятия мерзавца намного выше. Посмотрите, сколько известных проектов с открытым исходным кодом переключаются с cvs / svn на Mercurial и сколько переходят на Git. Посмотрите, сколько поставщиков хостинга кода / проекта вы можете найти с поддержкой git по сравнению с хостингом Mercurial.


Существует также TortoiseGit, если вам не нравится использование командной строки. (Но для этого нужно установить msysgit.)
Бен Джеймс

2
Наша компания в конечном итоге выбрала git из- за своей великолепной поддержки Windows - ознакомьтесь с Git Extensions . Я предвзят, потому что я теперь участник, но я не был, когда мы начали использовать это.
Джейкоб Стэнли

11

После прочтения, что Mercurial проще (что я до сих пор считаю, после того, как интернет-сообщество придерживается мнения), когда я начал работать с Git и Mercurial, я почувствовал, что Git относительно проще для меня адаптироваться (я начал с Mercurial с TortoiseHg) при работе из командной строки, главным образом потому, что команды git были названы соответствующим образом, как мне кажется, и их меньше. Mercurial имеет разные названия для каждой команды, которая выполняет отдельную работу, в то время как команды Git могут быть многофункциональными в зависимости от ситуации (например,checkout). В то время, когда Git был сложнее, сейчас разница вряд ли существенная. YMMV .. С хорошим клиентом с графическим интерфейсом, таким как TortoiseHg, правда, с Mercurial работать было намного проще, и мне не нужно было запоминать слегка запутанные команды. Я не буду вдаваться в подробности, как менялись все команды для одного и того же действия, но вот два исчерпывающих списка: 1 с собственного сайта Mercurial и 2-й из вики .

╔═════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════╗
║           Git               ║                Mercurial                                                                       ║
╠═════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════╣
║ git pull                    ║ hg pull -u                                                                                     ║
║ git fetch                   ║ hg pull                                                                                        ║
║ git reset --hard            ║ hg up -C                                                                                       ║
║ git revert <commit>         ║ hg backout <cset>                                                                              ║
║ git add <new_file>          ║ hg add <new_file> (Only equivalent when <new_file> is not tracked.)                            ║
║ git add <file>              ║ Not necessary in Mercurial.                                                                    ║
║ git add -i                  ║ hg record                                                                                      ║
║ git commit -a               ║ hg commit                                                                                      ║
║ git commit --amend          ║ hg commit --amend                                                                              ║
║ git blame                   ║ hg blame or hg annotate                                                                        ║
║ git blame -C                ║ (closest equivalent): hg grep --all                                                            ║
║ git bisect                  ║ hg bisect                                                                                      ║
║ git rebase --interactive    ║ hg histedit <base cset> (Requires the HisteditExtension.)                                      ║
║ git stash                   ║ hg shelve (Requires the ShelveExtension or the AtticExtension.)                                ║
║ git merge                   ║ hg merge                                                                                       ║
║ git cherry-pick <commit>    ║ hg graft <cset>                                                                                ║
║ git rebase <upstream>       ║ hg rebase -d <cset> (Requires the RebaseExtension.)                                            ║
║ git format-patch <commits>  ║ hg email -r <csets> (Requires the PatchbombExtension.)                                         ║
║   and git send-mail         ║                                                                                                ║
║ git am <mbox>               ║ hg mimport -m <mbox> (Requires the MboxExtension and the MqExtension. Imports patches to mq.)  ║
║ git checkout HEAD           ║ hg update                                                                                      ║
║ git log -n                  ║ hg log --limit n                                                                               ║
║ git push                    ║ hg push                                                                                        ║
╚═════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════╝

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

Что я пропускаю в Hg - это субмодуль Git. У Hg есть подпункты, но это не совсем подмодуль Git.

Экосистема вокруг них также может влиять на выбор: Git должен быть более популярным (но это тривиально), Git имеет GitHub, в то время как Mercurial имеет BitBucket , Mercurial имеет TortoiseHg, для которого я не видел аналога, столь же хорошего для Git.

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


8

Проверьте почту Скотта Чакона некоторое время назад.

Я думаю, что git имеет репутацию «более сложной», хотя, по моему опыту, она не сложнее, чем должна быть. IMO, модель git намного проще для понимания (теги содержат коммиты (и указатели на ноль или более родительских коммитов) содержат деревья, содержащие BLOB-объекты и другие деревья ... готово).

Это не только мой опыт, что мерзавец не больше сбивает с толку, чем меркуриал. Я бы порекомендовал еще раз прочитать этот пост от Скотта Чакона по этому вопросу.


1
Модель Mercurial на самом деле практически идентична: журнал изменений указывает на манифест, указывает на файл revisions / blob ... done. Если вы сравнивали формат на диске, вы, вероятно, не учитывали файл пакетов, который более сложно объяснить, чем простой формат revlog из hg.
Тонфа

Что ж, эта упрощенная модель игнорирует теги, что на практике значительно более неудобно в hg (хотя я и утверждаю, что тег git немного сбивает с толку, поскольку по умолчанию он не создает объект тега). Формат на диске был особенно дорогим для обоих проектов, которые имели историю множества имен файлов.
Дастин

1
Я не думаю, что модель игнорирует тегирование: тегирование в Mercurial является тривиальным - как вы знаете, это просто файл, который дает имена хэшам SHA-1. Нет никаких догадок относительно того, как метки вращаются в системе: они движутся вместе с толчками и толчками. И если есть конфликт тегов, то это тоже тривиально: вы решаете его как любой другой конфликт. В конце концов, это просто строка в текстовом файле. Я думаю, что простота этой модели очень приятная особенность.
Мартин Гайслер

Дастин: Да, пользователей часто смущает тот факт, что вы не можете видеть тег 1.0, .hgtagsкогда вы извлекли версию 1.0. Однако вам не нужно заглядывать внутрь, .hgtagsи вы обнаружите, что hg tagsвсе еще перечислены все теги. Кроме того, такое поведение является простым следствием хранения тегов в файле с управлением версиями - опять же модель легко понять и очень предсказуема .
Мартин Гейслер

1
Мартин Гейслер: Я бы сказал, что правила для тегов в Mercurial необходимы, потому что он использует контролируемый по версии файл для транспорта, а слой со специальными правилами делает теги не версионными, понять их совсем не просто.
Якуб Наребски

5

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

Во-первых, оба являются распределенными системами контроля версий. Распределенные системы контроля версий требуют изменения мышления по сравнению с традиционными системами контроля версий, но на самом деле они намного лучше работают во многих случаях, когда их понимают. По этой причине я считаю, что и Git, и Mercurial намного превосходят Subversion, Perforce и т. Д. Разница между распределенными системами контроля версий и традиционными системами контроля версий намного больше, чем разница между Git и Mercurial.

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

Mercurial проще в освоении. Я дошел до того, что мне редко приходилось ссылаться на документацию или заметки после нескольких недель использования Mercurial; Мне все еще приходится регулярно обращаться к своим заметкам с помощью Git, даже после того, как я пользуюсь им в течение года. Git значительно сложнее.

Это отчасти потому, что Mercurial просто чище. Вам редко приходится переходить вручную в Mercurial; Mercurial создаст для вас анонимную ветку автоматически, если и когда вам это нужно. Ртутная номенклатура более интуитивна; вам не нужно беспокоиться о разнице между «fetch» ​​и «pull», как в Git. Mercurial немного менее глючит. Существуют проблемы с чувствительностью к регистру имен файлов, которые раньше вызывали проблемы при переносе проектов на платформы с Git и Mercurial; это было исправлено в Mercurial некоторое время назад, хотя они не были исправлены в Git в последний раз, когда я проверял. Вы можете сообщить Mercurial о переименовании файлов; с помощью Git, если он не обнаружит переименование автоматически - по моему опыту, это очень удачное или неправильное предложение - переименование вообще не может быть отслежено.

Другая причина дополнительного усложнения Git, однако, состоит в том, что большая часть этого необходима для поддержки дополнительных функций и мощности. Да, в Git сложнее обрабатывать ветвления, но, с другой стороны, когда у вас есть ветки, не так уж сложно работать с ветками, которые практически невозможны в Mercurial. Перебазирование ветвей - это одна из этих вещей: вы можете переместить свою ветку так, чтобы ее основа вместо состояния ствола, когда вы разветвлялись, теперь была состоянием ствола; это значительно упрощает историю версий, когда над одной и той же кодовой базой работает много людей, поскольку каждый из переходов к транку может выглядеть последовательным, а не переплетенным. Точно так же гораздо проще объединить несколько коммитов в вашей ветке в один коммит,

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

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


4

Одно отличие совершенно не связано с самими DVCS:

Git, кажется, очень популярен среди разработчиков C. Git является де-факто репозиторием для ядра Linux, и это может быть причиной его популярности среди разработчиков на Си. Это особенно верно для тех, кто может работать только в мире Linux / Unix.

Разработчики Java, похоже, предпочитают Mercurial Git. Возможно, для этого есть две причины: одна из них заключается в том, что в Mercurial размещено несколько очень крупных Java-проектов, включая сам JDK. Другое - то, что структура и чистая документация Mercurial обращаются к людям, прибывающим из лагеря Java, тогда как такие люди считают Git несовместимым с именами команд и отсутствием документации. Я не говорю, что на самом деле это правда, я говорю, что люди привыкли к чему-то из их обычной среды обитания, а затем они склонны выбирать DVCS из этого.

Я предполагаю, что разработчики Python почти исключительно предпочитают Mercurial. На самом деле для этого нет разумной причины, кроме того факта, что Mercurial основан на Python. (Я тоже использую Mercurial, и я действительно не понимаю, почему люди суетятся из-за языка реализации DVCS. Я не понимаю слова Python, и если бы не тот факт, что он где-то указан, он основан на Python, тогда я бы не знал).

Я не думаю, что вы можете сказать, что одна DVCS соответствует языку лучше, чем другой, поэтому вам не следует выбирать из этого. Но на самом деле люди выбирают (частично) в зависимости от того, с какой DVCS они больше всего сталкиваются как часть своего сообщества.

(Нет, у меня нет статистики использования для поддержки моих утверждений выше ... все это основано на моей собственной субъективности)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.