Mercurial: MQ в сравнении с Git

Перевод статьи Steve Losh: A Git User’s Guide to Mercurial Queues


Последнее время я всё больше и больше использую Mercurial Queues. Благодаря словам Brendan Cully во время недавнего спринта Mercurial я внезапно понял, что MQ в Mercurial это некая улучшенная версия index из git.

Я захотел написать о схожести этих двух концепций, чтобы пользователи git смогли лучше понять расширение MQ для Mercurial и увидеть, на сколько эта концепция ушла далеко вперёд по сравнению с git index.

Эта статья не является руководством по командам MQ. Это лишь попытка объяснить принципы работы MQ и дать сравнение с git. Полное описание команд вы можете найти в специальной главе hg book.

Основы GIT

Позвольте мне кратко описать, как работает git, чтобы всем было понятно, о чём дальше пойдёт речь.

Когда вы работаете с git, у вас есть три «уровня»:

  • Рабочая директория
  • Index
  • Репозиторий git

Когда вы выполняете команду git add – изменения из рабочей директории сохраняются в index. Когда вы выполняете команду git commit, изменения, сохранённые в index, перемещаются в репозиторий:

Это очень удобная модель, потому что она позволяет вам собирать изменения в changeset по кусочкам в процессе работы, а потом сделать один commit, когда всё будет готово.

Основы Mercurial

Работая в Mercurial, вы имеете только два «уровня»:

  • Рабочая директория
  • Репозиторий Mercurial

Когда вы делаете команду hg commit, изменения из рабочей директории сохраняются в репозиторий:

Эта модель не даёт такой гибкости в создании changeset’ов, как git. Немного больше возможностей может добавить record extension, но это всё же не то…

Итак, посмотрим, как расширение MQ предоставит нам всю гибкость git index и даже больше!

MQ и один патч

Основной процесс работы с MQ выглядит как создание некоего патча командой hg qnew NAME. Чтобы записать изменения из рабочей директории в этот патч, нужно выполнить команду hg qrefresh (или hg qrecord). Когда вы готовы сделать commit в репозиторий, необходимо выполнить команду hg qfinish:

Диаграмма очень похожа на то, что мы видели в git, не так ли? MQ предоставляет «промежуточный» уровень для сохранения изменений, аналогично git index.

MQ и два (или более) патчей

Итак, в git мы имеем только один «промежуточный» уровень (index). В большинстве случаев этого достаточно. Однако, если вам нужно больше возможностей, посмотрим на Mercurial MQ.

MQ не зря называется Mercurial Queues (queue = очередь на англ). Вы можете создавать более одного патча в очереди, таким образом, одновременно может существовать несколько «промежуточных» уровней при необходимости.

Например, вы хотите изменить что-то в интерфейсе, но это требует изменений и в API проекта. Вам хотелось бы сделать один commit, который вносит изменения в API, и отдельный commit, который будет содержать изменения интерфейса. Это можно сделать, создав два патча: hg qnew api-changes; hg qnew interface-changes

Вы можете перемещаться между этими патчами с помощью команд hg qpop и hg qpush. Например, если вы работаете над интерфейсной частью и внезапно поняли, что забыли сделать необходимые изменения в API:

  • Находясь в interface-changes patch нужно сделать команду hg qpop, чтобы переместиться к api-changes patch.
  • Делаем изменения в API
  • Сохраняем изменения в API в api-changes командой hg qrefresh
  • Возвращаемся к работе над интерфейсом командой hg qpush

Использование нескольких патчей организованных в очередь — это как цепочка из нескольких git index, в которых можно накапливать различные изменения перед commit.

Несколько очередей патчей

А что если вам нужно поработать над двумя фичами одновременно, каждая из которых состоит из двух патчей? Вы могли бы создать 4 патча – 2 для первой фичи и 2 для второй, причем вторые два патча будут находиться в очереди поверх первых двух. Но это не самая хорошая идея.

Начиная с версии Mercurial 1.6 появилась команда qqueue, которая позволяет создавать несколько очередей (patch queues). Это значит, что вы можете создать независимые очереди для каждой фичи: hg qqueue –c NAME

Переключение между очередями осуществляется командой hg qqueue NAME. Это можно представить как несколько наборов «промежуточных» уровней (аля несколько наборов git index). Важно понимать, что MQ – это инструмент, который даёт вам множество возможностей, а понадобится ли они на практике и как организовать свою работу с контролем версий решать вам.

Версионирование очередей патчей

Версионирование очередей может быть сложным моментом для понимания, но как только вы разберётесь, вам откроется вся сила MQ!

Для начала, давайте посмотрим, каким же образом патчи хранятся в MQ?

Когда вы создаёте новый патч командой hg qnew interface-changes, Mercurial создаёт директорию patches внутри директории .hg вашего проекта. Каждый новый патч сохраняется в виде файла в этой директории (в т.ч. в эту же директорию patches сохраняются некоторые метаданные MQ):

yourproject/
|
+-- .hg/
|   |
|   +-- patches/
|   |   |
|   |   +-- api-changes
|   |   +-- interface-changes
|   |   `-- ... other MQ-related files ...
|   |
|   `-- ... other Mercurial-related files ...
|
`-- ... your project's files ...

Когда вы создаёте новую очередь патчей командой hg qqueue –c some-feature, Mercurial создаёт отдельную директорию patches-some-feature внутри .hg:

yourproject/
|
+-- .hg/
|   |
|   +-- patches/
|   |   |
|   |   +-- api-changes
|   |   +-- interface-changes
|   |   `-- ... other MQ-related files ...
|   |
|   +-- patches-some-feature/
|   |   |
|   |   +-- api-changes
|   |   +-- interface-changes
|   |   `-- ... other MQ-related files ...
|   |
|   `-- ... other mercurial-related files ...
|
`-- ... your project's files ...

Это обычные директории в файловой системе. Патчи внутри – простые текстовые файлы.

Следовательно, вы можете сделать удивительную вещь: создать для них репозиторий с помощью того же Mercurial и отслеживать изменения в патчах!

«В рот мне ноги!» — скажете вы, ибо теперь можно:

  • Не только создавать несколько наборов «промежуточных» уровней, но и применять к ним версионный контроль!
  • Обмениваться очередями MQ с другими людьми посредствам стандартных pull/push команд.
  • Работать совместно с другими людьми в рамках этих «промежуточных» уровней и объединять изменения с помощью merge.
  • Выложить свои патчи в общий доступ командой hg serve, чтобы коллеги смогли посмотреть на ваш код, перед тем, как вы сделаете финальный commit в репозиторий проекта.

Версионирование очередей патчей можно изобразить следующей диаграммой:

Чтобы упросить работу с версионным контролем MQ ко всем командам была добавлена опция --mq, которая сообщает Mercrual, что действие нужно выполнить над репозиторием MQ, а не над основным репозиторием проекта (т.о. не нужно переходить в директорию patches).

Репозитории MQ – это невероятно мощная концепция! Она может вам никогда и не понадобится, но если вдруг возникнет необходимость – знайте о её существовании.

Кстати, BitBucket имеет специальную поддержку версионирования MQ.

Недостатки MQ

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

Итак, если вы хотите стать героем или хотя бы начать использовать MQ, вот что вы должны знать:

  • Нет простого способа сделать pull из MQ патча.
  • Нет простого способа разделить один MQ патч на два (текущее решение — это очистить патч, сделать hg qrecord для первой части и затем hg qnew для второй части).
  • Нельзя сделать pop патча, если в рабочей директории были сделаны изменения после последнего hg qrefresh. Приходится сначала делать shelve изменений, затем pop и unshelve.

В дополнение, было бы неплохо отрефакторить record extension и переместить его в ядро Mercurial, таким образом, hg qrecord можно будет использовать без дополнительного расширения.

Я буду бесконечно признателен тому человеку, который найдёт время заняться этими вопросами. А пока у нас есть очень-мощный-но-иногда-неуклюжий интерфейс MQ.

Надеюсь, я смог показать пользователям git и Mercurial насколько мощным может быть MQ. Если у вас есть какие либо вопросы, вы можете найти меня в Twitter.

Реклама

Mercurial: MQ в сравнении с Git: 10 комментариев

  1. я недавно работаю с Hg (около двух месяцев, команда из 4х программистов), и мы с командой потратили пару дней что бы разобраться как тут всё работает, и видимо я чегото ещё не понимаю.
    По моему опыту hg commit вызывает действие аналогичное git add — локально создаёт changeset, сохраняет версию, и это можно делать много раз, в процессе работы, «собирая по кусочкам рабочую версию». И только hg push отправляет все накопившиеся changesetы в репозитарий.
    А здесь описана другая схема работы. Чего я не понимаю?

    • Как я понял, у вас есть опыт работы с git…
      Тогда объяснить commit / push в Mercurial очень просто — работает так же как в git!

      hg commit создаёт локальный ченджсет, hg push отправляет все накопившиеся changeset’ы в чужой репоизторий — как вы правильно и написали.
      Однако, что вы не верно истолковали, так это «hg commit вызывает действие аналогичное git add» — это совсем разные команды.
      hg commit — это полный аналог git commit, команда создаёт новый ченджест!
      git add, напротив, не создаёт ченджсет, а лишь запоминает сделанные изменения в некоторой промежуточной области, называемой «index». В базовых командах Mercurial нет аналогичной функциальности (нет промежуточной области «index»).

      Данная статья как раз раскрывает это различие между git и Mercurial.

  2. Мне кажется, что сравнение mq с гитовым индексом не вполне корректно. Как обычно строится работа с git: создается ветка под фичу, в нее вносятся коммиты (~очередь патчей), которые дальше сливаются в один с помощью git rebase -i и мержатся в апстрим.

    В Mercurial же нет полноценной поддержки исправления существующих коммитов, поэтому проблема решается с помощью новой сущности под названием «mq».

    Поправьте, если я что-то неправильно сказал.

  3. hg pull

    По умолчанию Mercurial не обновляет рабочий каталог после pull’а.
    Это означает, что, хотя в хранилище в настоящее время содержатся подтянутый набор изменений, содержимое файлов в рабочем каталоге осталось таким же как и до pull’а. А Git меняет?

  4. >Нет простого способа разделить один MQ патч на два
    1. заходи в Shelve
    2. выбираем нужный патч
    3. выбираем нужный файл
    4. отмечаем нужные чанки
    5. нажимаем на ‘Move selected chunks’
    6. закрываем shelve
    7. коммитимпатч

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s