Продолжение, см. также: Mercurial: введение в распределённые системы контроля версий, Mercurial: основы.
Несмотря на то, что Меркуриалом можно полноценно пользоваться, используя только встроенную функциональность, существует достаточно большое количество различных расширений, которые значительно увеличивают круг возможностей и решаемых задач. Немалая их часть входит в комплект поставки hg — все описанные, кроме TortoiseHG и QCT, так что начало их использования требует всего лишь включения в .hgrc
(или Mercurial.ini в случае Windows) в таком виде:
[extensions]
hgext.<имя-расширения>=
alias
Особенно описывать тут нечего — позволяет делать псевдонимы для команд. Я вот пользуюсь пятью, для сокращения вывода:
[alias]
slog = log --template '{date|shortdate} [{rev}:{node|short}] {author|person}: {desc|firstline}\n' -l 10
sin = incoming --template '{date|shortdate} [{rev}:{node|short}] {author|person}: {desc|firstline}\n'
sout = outgoing --template '{date|shortdate} [{rev}:{node|short}] {author|person}: {desc|firstline}\n'
sheads = heads --template '{date|shortdate} [{rev}:{node|short}] {author|person}: {desc|firstline}\n'
sglog = glog --template '[{rev}:{node|short}] by {author|person} \n{desc|fill68}\n\n'
О шаблонах подробнее можно прочесть в предыдущей статье.
bisect
Перевод слова bisect — разрезать, делить пополам. Именно этим расширение и занимается — и, кстати, после версии 1.0 это больше не расширение, а встроенная команда — уж очень популярна. :-)
Использование крайне просто — мы обнаруживаем в текущей версии кода какую-то ошибку, которой раньше не наблюдалось (регрессию). И хорошо помнится, что вот 100 ревизий назад её точно не было. Помечаем ревизии как плохую и хорошую соответственно:
hg bisect -b
hg bisect -g -100
После этого bisect
обновляет рабочую копию до состояния ровно посредине между наличием ошибки и её отсутствием. Мы проверяем, в каком состоянии находится код в данный момент, и вызываем hg bisect
соотстветственно с флагами -b
(bad) или -g
(good). За несколько прыжков мы точно получаем ревизию, в которой была внесена ошибка.
patchbomb
Расширение, которое автоматизирует отправку патчей по email’у. Спрашивает описание, которое шлёт в первом письме серии (помеченном как [PATCH 0 of N], где N — кол-во патчей), и потом каждый патч — отдельным письмом (помеченное как [PATCH M of N], где M — номер патча). Может также вместо текстовых диффов слать бандлы — в моём предыдущем проекте вообще используется изменённая версия, которая одновременно шлёт бандл — для точного применения, и текстовый патч — для просмотра. Несмотря на своё название, расширение добавляет команду email
.
graphlog и view
Два расширения, занимающихся практически одним и тем же — демонстрацией списка ревизий. От hg log
они отличаются тем, что показывают ещё и граф изменений (со всеми ветками). hg glog
— это консольная команда, hg view
— это вызов внешней программы hgk
, распространяющейся вместе с меркуриалом. Программа написана на Tcl/Tk и является прямым потомком gitk
, аналогичной программы для git’а. Из возможностей кроме демонстрации графа ревизий и изменений в каждой из них может показывать изменения, произошедшие между двумя любыми ревизиями (и сгенерировать соответствующий патч).
record
Аналог commit
, позволяющий зафиксировать отдельные кусочки изменений — полезно, когда есть большой файл с изменениями, которые неплохо бы разнести в две различных ревизии. При запуске hg record
задаётся вопрос по каждому файлу — внести его в ревизию полностью, частично или пропустить. При выборе второго варианта будет задан о включении в ревизию по каждому кусочку (chunk) изменений в файле.
transplant
Другое название операции, которую выполняет transplant — cherry-pick: перенос изменения из другой ветки, из другой именованной ветки (named branch), из другого репозитория (в том числе репозитории могут быть не связанными друг с другом историей). Используется в тех случаях, когда какое-то исправление из нестабильной ветки должно быть применено в стабильной.
rebase
Тут я немного схитрил — на самом деле это расширение появится лишь в версии 1.1.0 (есть надежды, что появится в ближайшие несколько недель), а сейчас доступно лишь в ветке разработки, которая станет новой версией. rebase
— результат Google Summer of Code. Суть команды в том, что при разветвлении репозитория она позволяет «переместить» ветку на голову другой, т.е. позволяет избежать ревизий, где происходит слияние двух веток. На деле полезность этого каждый определяет сам для себя — к примеру, многие пользователи git’а часто используют эту команду вместо слияния, и я в принципе ждал её реализации (честно говоря, я сам подумывал ей заняться одно время ;), но вот она вышла и оказалось, что я этой командой не пользуюсь вообще.
С другой стороны, в git’е я ею пользуюсь, но исключительно потому, что там нет встроенного аналога mq (mercurial queues — о них будет ниже). Использование довольно простое:
piranha@gtv ~/test>hg sglog
@ [3:01be576a255c] by Alexander Solovyov
| upstream changes
|
| o [2:ef01a8e462c3] by Alexander Solovyov
|/ local changes
|
o [1:458e57460db8] by Alexander Solovyov
| next
|
o [0:15cb0004cf51] by Alexander Solovyov
initial
piranha@gtv ~/test>hg rebase -s ef01 -d 01be
saving bundle to /home/piranha/test/.hg/strip-backup/ef01a8e462c3-temp
adding branch
adding changesets
adding manifests
adding file changes
added 2 changesets with 3 changes to 3 files
rebase completed
piranha@gtv ~/test>hg sglog
@ [3:bc22405c957a] by Alexander Solovyov
| local changes
|
o [2:01be576a255c] by Alexander Solovyov
| upstream changes
|
o [1:458e57460db8] by Alexander Solovyov
| next
|
o [0:15cb0004cf51] by Alexander Solovyov
initial`</pre>
convert
Название говорит само за себя — расширение, которое занимается конвертированием репозиториев. В настоящее время поддерживает несколько входящих форматов — Mercurial, CVS, Darcs, git, Subversion, Monotone, Arch, и два исходящих — Mercurial и Subversion. Позволяет указывать маски файлов, которые необходимо включить или исключить из результирующего репозитория, потому иногда им пользуются для конвертирования из hg в hg с исключением каких-то файлов из истории. :)
Web
Основной интерфейс меркуриала к вебу — hgweb. Он достаточно небольшой — 100 килобайт (это вместе с языком шаблонов и прочей обвязкой) кода на питоне, юзабельный — можно его использовать и как cgi, и как wsgi-приложение (а значит, и FastCGI), имеет нормальные человекопонятные адреса (вида /file/tip/README
), и предоставляет множество удобных вещей. Например, скачивание архива любой ревизии или отрисованный граф (аналог glog
и view
) прямо в вебе.
В то же время есть отличный плагин к trac’у — TracMercurial, насколько я знаю, на текущий момент лучший плагин для VCS, кроме родного траку Subversion. Для Trac 0.10 он достаточно простенький (т.е. основная функциональность работает, но на то она и основная), для trac 0.11 он уже может больше — именованные ветки, теги в адресе, поддержка annotate
и возможно что-то ещё.
GUI
Для Mercurial существует графический клиент — TortoiseHg. В основном он ориентирован на Windows, но есть кое-какая интеграция с GNOME (точнее с Nautilus’ом). Умеет клонировать, мержить, показывать лог ревизий и т.п. — я им не пользуюсь, потому подробно описать не могу.
Ещё есть qct (Qt Commit Tool) — умеет не только hg, но и другие VCS (git, monotone, bazaar, subversion, cvs). Довольно приятный интерфейс, когда надо выбрать файлики для коммита (я им иногда пользуюсь ;), в том числе может выбирать только часть изменений из файла (т.е. делать то же самое, что делает hg record
).
mq
И на десерт я оставил самое сладкое, самое большое расширение меркуриала — mercurial queues. Я здесь не буду раскрывать эту тему до мельчайших подробностей — всё-таки она достаточно обширна, заняла целых две главы в hgbook (раз и два), но об основах расскажу.
Основной сценарий работы с mq: существует какая-то программа, которую хочется улучшить. При работе, например, с svn — просто исправляются файлы в рабочей копии, а потом отправляется авторам по почте diff рабочей копии и последней ревизии. Но такой механизм имеет кучу неудобств, первым из которых является невозможность нормально разделять два (и больше) исправления. В меркуриале, конечно, можно просто закоммитить изменения локально, и отправить их автору, но всегда есть вероятность, что какой-то из патчей исправят, а какой-то, возможно, не примут. И тогда получится, что в репозитории сидят по соседству две почти идентичных ревизии, либо новая ветка, где одна из ревизий пропущена.
И тут на помощь приходит mq. :) Он помечает ревизии (не сам помечает, конечно, ему нужно указывать ;) особым образом и может их «выдёргивать» из репозитория (сами патчи хранятся в .hg/patches/
). Работает он только с сериями патчей (последовательно расположенными ревизиями), при этом на патч нельзя поместить обычную ревизию (иначе получается не совсем понятно, что же делать с ревизией, когда патч выдёргивают из репозитория). Все эти патчи легко изменяются, но вместо объяснения я лучше продемонстрирую, как это выглядит.
У нас есть репозиторий с несколькими ревизиями:
piranha@gtv ~/test>hg slog
2008-10-02 [1:458e57460db8] Alexander Solovyov: next
2008-10-02 [0:15cb0004cf51] Alexander Solovyov: initial
Мы делаем какие-то изменения и создаём новый патч:
piranha@gtv ~/test>echo "some changes" > c
piranha@gtv ~/test>hg add c
piranha@gtv ~/test>hg qnew -f -m "our patch" our.patch
Команда qnew
создаёт новый патч с названием our.patch
, описанием “our patch”
и заносит туда все изменения рабочей копии (-f
):
piranha@gtv ~/test>hg log -r tip
changeset: 2:fef5d05adfa0
tag: qtip
tag: our.patch
tag: tip
tag: qbase
user: Alexander Solovyov
date: Thu Oct 02 12:54:18 2008 +0300
summary: our patch
piranha@gtv ~/test>ls .hg/patches
our.patch series status
Как видно, mq дополнительно устанавливает несколько тегов на свои ревизии, где qbase
— первая из применённых ревизий, qtip
— последняя из применённых ревизий.
Кстати, о способе хранения патчей. В директории .hg/patches/
видно ещё два файла. Файл series
содержит список (по порядку) патчей, которые у нас есть, а status
— список патчей, применённых к репозиторию. При этом порядок патчей можно поменять простым редактированием файла series
(желательно, чтоб в этот момент они не были применены к репозиторию ;).
Предположим, в апстриме произошли какие-то изменения, и нам надо адаптировать патч под них. Для этого мы выдёргиваем патч, до или после вытягивания изменений (это не важно):
piranha@gtv ~/test>hg qpop
Patch queue now empty
piranha@gtv ~/test>hg pull -q -u ../upstream
piranha@gtv ~/test>hg qpush
applying our.patch
Now at: our.patch
piranha@gtv ~/test>hg slog
2008-10-02 [3:4b1960ecfa1a] Alexander Solovyov: our patch
2008-10-02 [2:3189f2f2bdb8] Alexander Solovyov: third revision
2008-10-02 [1:458e57460db8] Alexander Solovyov: next
2008-10-02 [0:15cb0004cf51] Alexander Solovyov: initial
В чём же суть? В том, что мы в любой момент можем изменить патч — этим он и отличается от обычной ревизии. Изменили что-нибудь? Обновим патч:
piranha@gtv ~/test>hg diff
diff --git a/c b/c
--- a/c
+++ b/c
@@ -1,1 +1,1 @@
-some changes
+some change - yeah
piranha@gtv ~/test>hg qref
piranha@gtv ~/test>hg slog -r our.patch -p
2008-10-02 [3:9c80951d796e] Alexander Solovyov: our patch
diff --git a/c b/c
new file mode 100644
--- /dev/null
+++ b/c
@@ -0,0 +1,1 @@
+some change - yeah
qref
— это сокращение от qrefresh
, команда, которая обновляет патч. Изменять она может всё — пользователя (-u
), дату (-d
), описание (-m
— из команды, -e
— интерактивно, -l
— из файла), включать/исключать файлы (либо по именам, либо с помощью -I
и -X
).
Можно добавить ещё один патч:
piranha@gtv ~/test>echo "something new" > s
piranha@gtv ~/test>hg add s
piranha@gtv ~/test>hg qnew -fm "second patch" second.patch
piranha@gtv ~/test>hg log -r qtip:qbase
changeset: 4:e82cd0bf259d
tag: qtip
tag: second.patch
tag: tip
user: Alexander Solovyov <<a href="mailto:piranha@piranha.org.ua">piranha@piranha.org.ua</a>>
date: Thu Oct 02 14:21:07 2008 +0300
summary: second patch
changeset: 3:9c80951d796e
tag: our.patch
tag: qbase
user: Alexander Solovyov <<a href="mailto:piranha@piranha.org.ua">piranha@piranha.org.ua</a>>
date: Thu Oct 02 14:16:26 2008 +0300
summary: our patch
А потом, если вдруг окажется, что два патча — это излишество, их можно слить в один:
piranha@gtv ~/test>hg qpop
Now at: our.patch
piranha@gtv ~/test>hg qfold -m "merged patch" second.patch
piranha@gtv ~/test>hg slog -r qtip
2008-10-02 [3:9412b2fed86f] Alexander Solovyov: merged patch
Ну и, в конце концов, отправить патчи на рассмотрение:
piranha@gtv ~/test>hg email -r qbase:qtip -t author@project.com
Естественно, на этом возможности mq не заканчиваются, стоит лишь посмотреть на hg help mq
— самые заметные, про которые я не упоминал, это стражи (guards, возможность автоматически применять или не применять патч в зависимости от условий) и версионирование патчей (тут всё просто, .hg/patches/
становится обычным репозиторием). Но про них лучше читать в hgbook’е, благо там формат позволяет куда более подробные описания.