?

Log in

No account? Create an account
Mercurial Queues - Scala, Java, JVM и другое [entries|archive|friends|userinfo]
Scala, Java, JVM и другое

[ website | Об этом блоге ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Mercurial Queues [Mar. 1st, 2009|03:03 am]
Scala, Java, JVM и другое

levin_matveev

[stepancheg]
[Tags|]

Mercurial мне нравится всё больше и больше, и вот сейчас я освоил расширение MQ, о чём сейчас расскажу.

Расширение позволяет управлять патчами. Единицей хранения самого репозитория является файл. Единицей хранения MQ является патч к репозиторию.

MQ хранит стек патчей текстовыми файлами в папке .hg/patches. Этот стек патчей может быть приложен до некоторого состояния, например, только первые два патча из пяти. Расширение позволяет прозрачно работать с этими патчами. Например, оно позволяет отредактировать некоторый патч — вы применяете стек патчей до последнего нужного вам патча, просто редактируете исходник программы, говорите hg qrefresh, и MQ сохраняет отредактированный патч. Фишка в том, что вы редактируете патч, наложенный поверх других патчей.

Самое очевидное применение MQ — хранение небольшого набора изменений к внешнему репозиторию (для значительного количества изменений удобнее делать свою ветку, т. е. хранить сами изменения, а не патчи).

Но есть и другое применение MQ — работа со своим репозиторием.

Есть хорошее правило работы с системами контроля версий — различные изменения должны идти разными комитами. Т. е. на рефакторинг двух частей системы и на реализацию двух фич должно идти четыре комита. Это нужно для того, чтобы коллегам было удобнее делать code review, и чтобы было проще откатывать некоторые изменения. Кроме того, настоятельно рекомендуется писать осмысленные, подробные commit messages.

И вот эта проблема очень хорошо решается с помощью MQ. Я пишу фичу, которая назвается charset from collation. Я вызываю команду

hq qnew charset-from-collation

И программирую. Спустя полчаса я понимаю, что мне было бы гораздо удобнее, если бы в класс DataType поле options имело бы тип DataTypeOptions, а не Seq[DataTypeOption]. Я делаю

hg qrefresh # Чтобы MQ зафиксировал патч
hg qpop # снимаем патч
hg qnew data-type-options

Я редактирую класс DataType. Можно было бы сразу закомитить, но ещё не написан тест, и я сомневаюсь, правильно ли названы поля в классе DataTypeOption. Так что я

hg qrefresh
hg qpush

Теперь сверху патч в charset-from-collation, а под ним — рефакторинг класса DataType. Я продолжаю работать над патчем. Когда мне надо порефакторить DataType дальше, я делаю

hg qrefresh
hg qpop
... редактирую DataType
hg qrefresh
hg qpush

И продолжаю работать над основной задачей. Потом мне срочно понадобится сделать другую задачу по проекту, когда работа над charset from collation ещё не доведена ни до какой промежуточной точки. Или ещё что-то. MQ мне поможет аккуратно со всем этим разобраться.

Того, что я перечислил уже достаточно для того, чтобы полюбить MQ. Но у MQ есть другие киллер-фичи.

Во-первых, сами патчи могут храниться в репозитории. Т. е. можно посмотреть, как этот патч выглядел вчера, если я сегодня его случайно испортил. Это обычный репозиторий Mercurial, просто его корень находится в папке .hg/patches. Этот репозиторий можно точно так же скачивать, закачивать, мёрджить, бранчевать.

Во-вторых, есть команда qfinish, которая одним махом комитит патч в основной репозиторий и удаляет патч из репозитория патчей.

И в-третьих, и это самое клёвое, у команды qrefresh есть ключ -e, который позволяет отредактировать описание патча. А при комите командой qfinish, это описание становится commit message. Если я долго работаю над каким-то патчем, например, делаю какой-то большой рефакторинг, я могу по ходу рефакторинга командой qrefresh -e описывать произведённые изменения (а не по окончании рефакторинга глядя на многокилобайтный diff).

И расскажу ещё про одно применение MQ. Когда разработка ведётся по шаблону feature branch, часто бывает удобно редактировать патч в ветке all (т. е. в ветке, в которой сведены изменения из всех веток). С помощью MQ можно писать патчи в основной ветке, а после этого обновляться до нужной ветки, и применять патч там командой hg qfinish. Например, у нас есть своя сборка lighttpd, и там все доработки начинаются с голого trunk, в котором неправильная сборка пакета deb, не сгенерирован configure и не настроен .hgignore. Поэтому прямо в ветке писать и компилировать код очень сложно, а делать патч поверх all как раз очень удобно.

U: MQ хорошо работает начиная с версии 1.1 (в старых версиях приходится писать больше команд)
LinkReply

Comments:
[User Picture]From: dieash
2009-03-04 01:26 pm (UTC)
с MQ не работал, но заинтересовался. возник вопрос:

например, только первые два патча из пяти (1)
оно позволяет отредактировать некоторый патч (2)

а что случится с патчами 4 и 5, если будет изменена заплатка, например, под номером 3? конфликтов с последующими патчами быть не может по определению? а если возможны, то кто будет проверять совместимость правки третьего патча с патчами 4 и 5 и вносить правки в них?
(Reply) (Thread)
[User Picture]From: stepancheg
2009-03-04 03:28 pm (UTC)
Совместимость между патчами 3 и 4 при редактировании патча 3 не проверяется — всё отдаётся на откуп пользователю. Т. е. патч 4 при qpush просто применится с конфликтом, и этот конфликт надо будет вручную разрешить. Не помню как — то ли команда merge запустится, и в файле будут уголки, то ли создастся файл .rej с неприменёнными частями патча.
(Reply) (Parent) (Thread)
[User Picture]From: dieash
2009-03-04 03:30 pm (UTC)
ясно, спасибо
(Reply) (Parent) (Thread)
[User Picture]From: svetlyak40wt
2009-03-12 10:57 am (UTC)
Ух! Еще немного, и hg приблизится к git по функциональности. Хотя, на мой взгляд, этот MQ — весьма корявая замена нормальным локальным веткам гита :-D
(Reply) (Thread)
[User Picture]From: stepancheg
2009-03-12 02:56 pm (UTC)
Локальные ветки в Mercurial, конечно же, есть. MQ — это альтернативный веткам подход, который иногда использовать гораздо удобнее, чем локальные ветки.
(Reply) (Parent) (Thread)
[User Picture]From: bik_top
2010-03-02 01:07 am (UTC)

Ветвление в Mercurial

Буду признателен, если растолкуете пару вопросов.

1) Вопрос насчёт общепринятой практики ветвления в Mercurial.
В каких случаях для вынесения ветви разработки рекомендуется использование клонов центрального репозитория (стратегия one branch per repository), а в каких случаях — внутрирепозиторные именованные ветви?

2) Смежный вопрос насчёт continuous integration.
Если проект находится под управлением системы непрерывной интеграции (CruiseControl.NET), то как собирать несколько внутрирепозиторных именованных ветвей? Ведь рабочая папка репозитория всего одна. Предполагается, что на CI-машине клон репозитория всего один, и CI-сервер умеет switch'ить рабочую папку? Или лучше склонировать репозиторий на несколько копий, чтобы рабочая папка каждого имела родителем разные внутрирепозиторные ветви?
(Reply) (Parent) (Thread)
[User Picture]From: stepancheg
2010-03-02 07:37 am (UTC)

Re: Ветвление в Mercurial

1. В первую очередь стоит делать так, как удобно именно вам и вашему коллективу.

Если ветки предполагается мёрджить, то ветки стоит делать именованными, т. к. в противном случае после слияния будет непонятно, что с чем слили. Если ветки небольшие, и делаются часто, я думаю, нет смысла заморачиваться с отдельным репозиторием.

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

— когда есть разработчики, которых нельзя пускать в главный репозиторий (чтобы они что-нибудь левого не накомитили в главную ветку)
— когда у вас нет права комитить в основной репозиторий
— когда вы делаете fork, который вряд ли (или нескоро) будет сливаться назад

2. Если нужно собирать разные ветки, то стоит сделать несколько разных рабочих копий репозитория — незачем при каждой сборке свичить файлы взад-вперёд, параллельная сборка становится возможна, меньше потенциальных проблем и т. п. Я не знаю, как CruiseControl.NET, а TeamCity, которым мы пользуемся, сам управляет локальными копиями, в ч. с. сам делает локальную копию под каждую конфигурацию.
(Reply) (Parent) (Thread)
[User Picture]From: bik_top
2010-03-02 07:58 am (UTC)

Re: Ветвление в Mercurial

Большое спасибо!

Ещё один вопрос.

У меня не очень большой опыт ветвления/слияния (как в Subversion, так и, тем более, в Mercurial). Какие есть преимущества у механизмов (внутрирепозиторного) ветвления Mercurial по сравнению с таковыми у Subversion 1.6? Я читал hgbook, туториалы и обзоры, определённое представление получил, но затрудняюсь его чётко и по пунктам сформулировать.


Edited at 2010-03-02 08:18 am (UTC)
(Reply) (Parent) (Thread)
[User Picture]From: stepancheg
2010-03-02 09:35 am (UTC)

Re: Ветвление в Mercurial

Слияние (merge), вопреки расхожему мнению, в SVN делать удобнее, чем в Mercurial: можно сливать отдельные комиты, и файлы внутри комитов. В Mercurial можно только сливать комиты. Ещё в Mercurial есть transplant для слияния отдельных комитов, но он не такой мощный, как слияние в SVN.

С другой стороны, Mercurial работает быстрее (в частности update/switch), и при слиянии не добавляет атрибуты к каждому файлы.

В общем, нельзя говорить, что внутрирепозиторное ветвление в Mercurial лучше, или хуже, чем в SVN. Надо всё в комплексе сравнивать.
(Reply) (Parent) (Thread)
[User Picture]From: stepancheg
2009-03-12 02:58 pm (UTC)
Если ты намекал на rebase, то rebase в hg тоже уже давно есть.
(Reply) (Parent) (Thread)
[User Picture]From: akademic
2009-09-02 09:03 am (UTC)
Доброе время суток.
Сейчас подбираю себе новую систему для контроля версий.
Позволит ли MQ решать следующую проблему?
Есть мой проект.
И в этом проекте я использую сторонний код, доступный из репозитория стороннего разработчика.
Только есть нюанс: просто так сразу я не могу использовать его код, нужно внести некоторые правки, что успешно и делается.
Сторонний код постоянно обновляется его разработчиком, соответственно, я тоже хочу обновляться. Но если я обновлюсь, то мне придётся снова адаптировать код. Так вот хотелось бы применять те изменения, которые я сделал без лишних телодвижений(предполагается, что эти изменения работоспособны), ну и хранить историю.
(Reply) (Thread)
[User Picture]From: stepancheg
2009-09-02 11:45 am (UTC)
Мы делали по-другому — некоторорые комиты в upstream-репозитории объявляли базовыми, и после этого патчи прикладывали к этому базовому комиту — писали, или переносили с прошлого базового комита командой transplant. Получалось много веток, начинающихся с базового комита, и после этого уже все эти ветки мёрджились.

Да, это можно сделать с помощью MQ, но в случае, если изменений много, или если комиты в upstream репозиторий ломают патчи, то с MQ будет путаница.

Если изменений немного, они простые, и с большой вероятностью будут закомичены в upstream, то MQ использовать проще.
(Reply) (Parent) (Thread)
[User Picture]From: akademic
2009-09-06 06:54 pm (UTC)
Спасибо за ответ, буду экспериментировать.

Интересует ещё один аспект использования.
Настроил сейчас сервер mercurial на базе apache2 по доке:
http://mercurial.selenic.com/wiki/HgWebDirStepByStep

С предварительно созданным на сервере репозиторием работает всё хорошо.
А вот удалённо создать новый репозиторий не вышло.

Пробовал вот так:
hg clone . http://my.domain.com/hg/new_repo
Выдаёт:
abort: cannot create new http repository

Хотелось бы также hg clone http://my.domain.com/hg/old_repo http://my.domain.com/hg/new_repo

В документации ничего не нашёл.

Это возможно? Или как-то по-другому можно давать возможность создать репозиторий, не давая прямого доступа к файловой системе сервера?

Пожалуй, это единственное препятствие для перехода на mercurial.
(Reply) (Parent) (Thread)
[User Picture]From: stepancheg
2009-09-06 08:00 pm (UTC)
Удалённо нельзя создавать новые репозитории используя стандартные инструменты Mercurial.

Возможно, есть какие-то сторонние приложения или скрипты для решения этой задачи, я не знаю.

Может быть, стоит использовать коммерческий хостинг Mercurial, например, bitbucket, там можно создавать репозитории через веб-интерфейс, а также навегда перестать думать о настройках и бекапе.
(Reply) (Parent) (Thread)
(Deleted comment)
[User Picture]From: stepancheg
2009-10-04 08:03 pm (UTC)
С ssh вообще нет никаких проблем.

ssh remote.host "rm -rf /"
(Reply) (Parent) (Thread)