Изменения

Сравнение DVCS - несколько задач

37 490 байтов добавлено, 15:47, 2 августа 2010
м
Нет описания правки
Данная статья является очередным сравнением популярных [[wikipedia:Distributed revision control|DVCS]] — [http://selenic.com/mercurial/ Mercurial], [http://git-scm.com/ Git] и [http://bazaar-vcs.org/ Bazaar], с точки зрения нескольких нетривиальных задач. Ссылка: [http://yourcmc.ru/DVCS_YAC http://yourcmc.ru/DVCS_YAC] («Yet Another Comparison»). Хотите лаконичный ответ в стиле «ИМХО» на вопрос — кого выбрать из трёх? Ну пожалуйста. Выбирайте Mercurial. А не Git (безумный конгломерат) и не Bazaar (пионерское поделие, унаследованное от Arch’а).
== Работа с SVN (миграция и синхронизация) ==
Почему-то, говоря о недостатках DVCS, никогда не говорят о следующем не очень удобном при переходе с централизованных систем поведении — DVCS отслеживают версию ''цельного'' репозитория, а не отдельных файлов или директорий. Кроме проблем, которые это создаёт при желании извлечь ''часть'' репозитория для работы, а не обязательно весь, это ведёт к тому, что если две ветки «расходятся», становится обязательным слияние, даже если '''конфликтов в изменениях не было''' и даже если '''вообще менялись разные файлы'''. А централизованным хоть бы хны — <tt>svn up</tt> и живи себе дальше. Что ещё любопытно — с Subversion-репозиториями DVCS работают, как правило, быстрее самого Subversion, и быстрее всех работает '''Bazaar'''. То есть, в принципе, можно вообще жить с Subversion-сервером и DVCS-клиентом (если, конечно мириться с постоянными слияниями и rebase’ами). Во всех трёх DVCS при работе с Subversion есть следующее ограничение: невозможно связывать один и тот же репозиторий с несколькими Subversion-источниками — ни в пределах разных веток, ни в пределах разных каталогов. Mercurial честно говорит «repository unrelated», Bazaar — «branches have no common ancestor», Git пытается что-то родить, но запутывается в одинаковых коммитах, имеющих несколько SVN-родителей. А ведь было бы неплохо иметь такие возможности… === Mercurial === '''Mercurial''': почти отлично! Есть несколько расширений — (5-), есть 3 расширения различной степени глюкавости — [http://mercurial.selenic.com/wiki/HgSubversion hgsubversion], [http://pypi.python.org/pypi/hgsvn hgsvn], [http://mercurial.selenic.com/wiki/ConvertExtension convert], из которых позволяющих работать с Subversion тем или иным образом, и не совместимых друг с другом.  Самое вменяемое из них — этих расширений — '''hgsubversion''', и хотя и заявлено, что оно ещё сыроесыровато, иразработка ведётся активно, к сожалениюв мэйллисте постоянно происходит некая жизнь, не распространяется вместе а автор отзывчив. Вместе с Mercurial’омоно не распространяется, нужно ставить отдельно. Тестировал я ревизию 500 из http://bitbucket.org/durin42/hgsubversion. Имеет фактически весь основной необходимый функционал — можно делать и <tt>push</tt>, и <tt>pull</tt> в/из Subversion, можно клонировать SVN-репозиторий с сохранением веток и меток (правда, обязательно стандартное их расположение в корневых поддиректориях <tt>/trunk</tt>, <tt>/branches</tt>, <tt>/tags</tt>), эти два метода совместимы, <tt>rebase</tt> также работает, а граф ветвлений сохраняется. Очень крут тот факт, что ветка, которая создавалась неполным копированием <tt>trunk</tt>'а, то есть, копированием некоторых его поддиректорий, успешно подцепилась в нужное место графа ветвлений. Ни Bazaar, ни Git этого не смогли. Поддержки svn:mergeinfo пока что нет, хотя она близится. Минус расширения заключается в сырости — за время тестирования я нашёл уже несколько неприятных багов:* <tt>push</tt> из Mercurial-клона репозитория без веток и меток (имеющего вид просто одного каталога) в Subversion не работал вообще — [http://groups.google.com/group/hgsubversion/browse_thread/thread/3749eb3cbf007855 обсуждение в группе hgsubversion]. К счастью, меня пропёрло, я потратил час времени и баг этот пофиксил собственноручно. [[hgsubversion-fix-for-singledir-repo.diff|Фикс]] очень простой, на две строчки, и он уже находится в репозитории в виде ревизии r501 — за отзывчивость автору зачёт.* <s>Не работает клонирование репозиториев по протоколу [http://svnbook.red-bean.com/en/1.0/ch06s03.html svnserve] (<tt>svn://host:port/url</tt>). Очень неприятно, особенно, если вы предпочитаете именно <tt>svnserve</tt> по причине его производительности.</s> Уже давно работает.* Течёт память, что при клонировании '''толстых''' SVN-репозиториев (например, [http://svn.wikimedia.org/svnroot/mediawiki/ репозитория MediaWiki]) приводит к ошибкам <tt>Abort: out of memory</tt>, то есть нехватки памяти. Обходится методом клонирования репозитория по частям, делая <tt>hg init</tt>, а потом несколько <tt>hg pull</tt> до победного конца. Причина в дырявости [http://www.swig.org/ SWIG] SVN Python-библиотеки. Забавно, что аналогичная [http://www.swig.org/ SWIG] Perl-библиотека такой текучестью, видимо, не страдает, так как Git этот репозиторий глотал (причём по 10 раз за раз) и не давился. ''Perl, говорите, течёт? Ну хи-хи, хи-хи.'' Кстати, Bazaar течёт точно так же, как <tt>hgsubversion</tt>.* На ревизии 43380 MediaWiki <tt>hgsubversion</tt> свалился с ошибкой <tt>abort: phase3/includes/ConfigurationCache.php@a1335975fba6: not found in manifest!</tt>. [http://groups.google.com/group/hgsubversion/browse_thread/thread/2a2ae0aa680054ec обсуждение в группе hgsubversion]. Причина, вероятно, кроется где-то в районе обработки перемещений каталогов Subversion. Я обошёл данный баг убиением части файла <tt>.hg/svn/rev_map</tt> (можно было и весь убить) и перезапуском <tt>hg pull</tt>. Однако в итоге появилось две ветки вместо одной: <tt>on_wiki-configuration</tt> и <tt>on_wiki-configuration/phase3</tt> (подкаталог ветки…) Итог клонирования MediaWiki-репозитория: успешно с небольшими танцами с бубном — склонировал за несколько перезапусков <tt>pull</tt>'а и с уборкой <tt>rev_map</tt> в середине, все ветки на месте, только одна, перемещённая потом в подкаталог себя, продублировалась: «<tt>on-wiki_configuration</tt>» и «<tt>on-wiki_configuration/phase3</tt>». Репозиторий (то есть директория <tt>.hg</tt>) занимает на диске '''1.63 Гб'''.
Остальные два экстенжна «нинужны»: <tt>hgsvn</tt> — нечто более старое, работает сбоку от общего механизма, тоже позволяет делать push и pull, но не клонирует весь репозиторий, а только извлекает (checkout’ит) последнюю версию, чтобы далее можно было использовать Subversion и Mercurial вместе. Ну и конечно, оно не совместимо с <tt>hgsubversion</tt>. <tt>convert</tt> же предназначен для конвертации истории проекта из нескольких различных систем контроля версий в Mercurial, ни черта не совместим ни с <tt>hgsvn</tt>, ни с <tt>hgsubversion</tt> и не сохраняет граф ветвлений. Зато, правда, поддерживает возможность использования других имён поддиректорий <tt>trunk/branches/tags</tt>.
'''=== Git''': очень хорошо (5-)! <tt>[http://git-svn.yhbt.net/ git-svn]</tt> встроен в git и умеет всё, что нужно: клонирование, синхронизация (<tt>fetch</tt>, <tt>pull</tt> или <tt>merge</tt>), фиксация изменений в Subversion-репозиториях (<tt>dcommit</tt>) и <tt>rebase</tt> работают с сохранением веток и меток, причём стандартная схема их именования <tt>trunk/branches/tags</tt> не является обязательной. Ветки SVN импортируются как [http://www.gitready.com/beginner/2009/03/09/remote-tracking-branches.html Remote Tracking Branches], что тоже удобно — можно начинать историю ветки в git’е не с сотворения миров, а с любого момента. Также <tt>git-svn</tt> поддерживает подключаемые внешние репозитории Subversion (т. н. [http://svnbook.red-bean.com/en/1.0/ch07s03.html Externals]). Граф ветвлений, вроде-бы, сохраняется. На одном из SVN-репозиториев почему-то не сохранился. На тестах же всё получалось… Короче говоря, функционал полон, но возможно, есть баг.===
'''BazaarGit''': хорошо. Можно импортировать репозиторий командами отлично (5-)! <tt>[http://git-svn.yhbt.net/ git-importsvn]</tt> встроен в git и умеет всё, что нужно: клонирование, синхронизация (<tt>fetch</tt>, <tt>pull</tt> или <tt>branchmerge</tt>), можно делать фиксация изменений в Subversion-репозиториях (<tt>pushdcommit</tt> ) и <tt>pullrebase</tt> вработают с сохранением веток и меток, причём стандартная схема их именования <tt>trunk/из Subversionbranches/tags</tt> не является обязательной. При импорте можно сохранить все ветки и метки в одном хранилище (если использовать Ветки SVN импортируются как [http://bazaar-vcswww.orggitready.com/SharedRepositoryTutorial Shared Repositorybeginner/2009/03/09/remote-tracking-branches.html Remote Tracking Branches]). Можно начинать историю ветки в git’е не с сотворения миров, для этого также требуются стандартные названия а с любого момента. Также <tt>trunk/branches/tagsgit-svn</tt>поддерживает подключаемые внешние репозитории Subversion (т. Кроме того, я наткнулся на некоторые проблемы при фиксации изменений в Subversion после нескольких слияний двух Bazaar’овских веток н. [http://svnbook.red-bean.com/en/1.0/ch07s03.html Externals]). Граф ветвлений сохраняется, хоть и пока не разрешил ихтак круто, как в Mercurial’е. Граф ветвлений SubversionВ общем, увыможно сказать, не сохраняетсячто функционал полон. Также существует несколько других расширений для импорта Subversion <tt>git svn fetch</tt> успешно возобновляет операцию клонирование в Bazaar, но все они ещё хужеслучае её падения посредине.
Что ещё любопытно — А теперь поговорим о неприятном: полное клонирование [http://svn.wikimedia.org/ SVN-репозитория MediaWiki] заняло более 3 дней, хотя должно по логике занимать часов 6-8. Причина в том, что Git время от времени не понимает, откуда ответвилась ветка, лезет в глубокую историю и начинает сканировать по новой уже просканированные ревизии. Таким образом, некоторые (а конкректно — ветвлённые не из самого trunk’а, а из его подпапок) ветки не получаются связанными с Subversionисторией trunk’а, старые ревизии для них дублируются. Справедливости ради надо отметить, что для двух таких веток старая история объединяется, а физически никаких дубликатов не хранится. Вообще в итоге Git-репозиториями DVCS работаютклон репозитория MediaWiki (директория .git) занял всего '''845 Мб'''. Кроме того, в процессе клонирования память '''не текла вообще''', на всё про всё уходило мегабайт 80 памяти. Кроме того, как правиловсегда с Git’ом, гораздо быстрее самого есть различные нетривиальности, что-то бывает нужно настроить (''гитара настроена? нас двое, на! играй, на!''), синтаксис не совмещён с обычными командами Git — то есть например для синхронизации копии с Subversionнужно сказать не <tt>git pull</tt>, и быстрее всех работает а <tt>git svn fetch</tt>. === Bazaar === '''Bazaar''': хорошо. То Можно импортировать репозиторий командами <tt>svn-import</tt> или <tt>branch</tt>, можно делать <tt>push</tt> и <tt>pull</tt> в/из Subversion. При импорте можно сохранить все ветки и метки в одном хранилище (если использовать [http://bazaar-vcs.org/SharedRepositoryTutorial Shared Repository]), для этого также требуются стандартные названия <tt>trunk/branches/tags</tt>. Граф ветвлений Subversion сохраняется, но также не отражает копирования подпапок. Также существует несколько других расширений для импорта Subversion в Bazaar, но они хуже. Тест на клонирование репозитория MediaWiki прошёл почти без проблем, но почему-то вместо 59589 ревизий копировал 85567 ревизий, то естьоткуда-то придумал лишних 25978. Возможно, это были ветки — корректного присоединения их истории к <tt>trunk</tt>'у обнаружено после клонирования '''не было вообще''' (!). Причина, скорее всего, в принципетом, что ветки там в основном не совсем честные (как уже было упомянуто в секции про '''Git''') — они создавались не командой <tt>svn cp trunk branches/ветка</tt>, а создавался каталог ветки и в него копировались каталоги <tt>phase3</tt> и <tt>extensions</tt> из <tt>trunk</tt>’а. В итоге изменения, происходившие «до» создания таких «нечестных» веток, в самих ветках вообще не отражены. Память течёт точно так же, как и Mercurial’е и, очевидно, по той же причине (SWIG), и перезапуски процесса клонирования по причине ухода машины в глубокий своп также были необходимы. После клонирования репозиторий изначально занимал аж 16 Гб, однако при ближайшем рассмотрении стало ясно, что большую часть этого пространства занимали каталоги <tt>obsolete_packs</tt> и <tt>upload</tt> со старыми упакованными данными репозитория и некими временными файлами соответственно. Такая же ситуация осталась и после вызова <tt>bzr pack</tt>, о чём есть целый баг: [https://bugs.launchpad.net/bzr/+bug/304320 Bug 304320 — bzr pack should (optionally?) delete obsolete packs]. Все эти лишние данные, по-видимому, можно смело удалить ручками — и обнаружить, что репозиторий занимает '''925 Мб''', то есть, лишь ненамного больше, чем в случае Git, и примерно в 1.75 раза меньше, чем в Mercurial’е. Правда, по ощущению чтение репозитория происходит медленнее и чем в '''Git''', и чем в Mercurial. При выводе истории изменений по одной и той же ветке '''Git''' не напрягается вообще жить , '''Mercurial''' напрягается немного, а '''Bazaar''' напрягается ощутимо. При том, что '''Git''' и '''Mercurial''' выводят не только историю изменений самой ветки, а также историю изменений, происходивших ''до создания ветки'' (помним, что ветки нечестные). Правда, я не исключаю, что под виндой ситуация с Subversionпроизводительностью может быть совсем другая. А теперь снова о неприятном. Есть у Базаара одна небольшая, зато возникающая в достаточно простом случае, проблемка. То есть пусть бы она и появлялась, но очень редко — это можно терпеть. Но тут ситуация не так чтобы редкая. Так вот. Всё начинается с банального — мы сделали изменение в '''Bazaar'''-сервером клонированном SVN-репозитории, а кто-то тем временем сделал изменение '''в самом SVN-репозитории'''… Причём не важно, в каких файлах: даже если конфликтов в наших изменениях нет, проблема возникает всё равно (помним о том, что DVCS управляют «цельным» репозиторием). Что нам теперь остаётся делать? Ну конечно, мержиться. И merge успешно проходит. Но в случае '''Bazaar''' помните — после этого merge нужно сразу делать rebase и push в Subversion! Иначе, если до этого сделать ещё хотя бы один commit в Bazaar, push не пройдёт (он и не должен), а rebase не поможет или вывалится с исключением. Хотите подробнее? Рассмотрим следующую последовательность действий: # Клонируем svn в ветку bzr1.# Клонируем bzr1 в bzr2.# Вносим изменения в bzr2, коммитим.# Вносим ''другие'' изменения в svn, коммитим. Лучше несколько раз (создаём несколько ревизий).# Делаем pull из svn в ветку bzr1 — bzr1 снова синхронизирован с svn.# <strike>Делаем push из bzr2 в bzr1 — облом: ветки «разошлись»</strike>. Хорошо, делаем merge из bzr1 в bzr2. Коммитим (фиксируем).# (важно) Снова вносим изменения в bzr2, коммитим.# Теперь хотим протащить изменения из bzr2 в svn. Сначала делаем push из bzr2 в bzr1. Теперь история bzr1 идентична истории bzr2.#: <font color="red">'''И это не так тривиально, как хотелось бы!'''</font>#: Потому что теперь импортированные из svn в bzr1 ревизии ''заменяются одной merge-клиентомревизией'', а после неё в истории появляется ревизия с модификацией, импортированная из bzr2. Исходные svn-ревизии «подцепляются» к merge-ревизии. Чтобы увидеть их, нужно сказать не просто <tt>bzr log</tt>, а <tt>bzr log -n0</tt>.# <strike>Делаем push из bzr1 в svn — облом: в bzr1 есть ревизия, «воткнутая» между уже зафиксированными в репозитории svn-ревизиями</strike>. Две bzr-ветки здесь необязательны — просто полезны для иллюстрации танцев ревизий. И что теперь делать?<pre>bzr: ERROR: Operation denied because it would change the mainline history.Set the append_revisions_only setting to False on branch "..." to allowthe mainline to change.</pre>Bazaar предлагает нам разрешить менять местами ревизии в SVN-репозитории. Но если разрешить — всё равно обламывается (ну кто ж тебе по [[rupedia:WebDAV|WebDAV]] даст ревизии местами переставить, даа) и предлагает использовать rebase. А rebase — и это из-за пункта 7 — нам не поможет, а скажет «no revisions to rebase». А в некоторых комбинациях — вывалится со змеиным исключением. Чтобы исправить эту ситуацию: # Клонируем svn ещё раз в ветку bzrtmp.# Делаем merge из bzr1 в bzrtmp, коммитим. Теперь в bzrtmp последней ревизией будет merge-ревизия, к которой «подцеплены» ревизии, которые мы так жаждем протащить-таки в SVN.# Теперь мы можем сделать push из bzrtmp в svn, а потом из svn — pull во все остальные ветки, и они придут к согласованному виду… Хотите автоматический сценарий для воспроизведения ошибки? Пожалуйста: [[bzr-rebase-dont-help.sh]]. В конце видны результаты, которые дадут команды rebase и pull. В частности, в результате последней команды rebase bzr скажет «<tt>ERROR: exceptions.AttributeError: 'NoneType' object has no attribute 'get_known_graph_ancestry'</tt>», уйдёт в Python Traceback и оттуда не вернётся. <div style="height: 150px; overflow: scroll">{{:bzr-rebase-dont-help.sh}}</div> А в '''Mercurial''' в точно такой же ситуации rebase замечательно работает. Пример аналогичного скрипта: [[hg-rebase-better-than-bzr-ithelps.sh]]. <div style="height: 150px; overflow: scroll">{{:hg-rebase-better-than-bzr-ithelps.sh}}</div> === Итоги клонирования SVN MediaWiki === Место на диске:* '''Git''' — 845 мб.* '''Bazaar''' — 925 мб.* '''Mercurial''' — 1633 мб. «Нечестные» ветки (<tt>svn cp trunk/phase3 trunk/extensions branches/ветка/</tt>):* '''Mercurial''' — корректно прицепились к trunk’у (!).* '''Git''' — общая история у всех таких веток, но отдельная от trunk’а.* '''Bazaar''' — история «ДО» создания нечестных веток потеряна. Проблемы:* '''Git''' — клонирование SVN идёт очень долго, много данных копируется повторно.* '''Mercurial''' — течёт память, проблема с <tt>rev_map</tt> в середине клонирования.* '''Bazaar''' — течёт память, проблемы с <tt>rebase</tt>.
== Управление патчами ==
ПервоеРассмотрим задачу управления набором патчей: есть некоторый проект, есть набор сторонних (например, ваших) патчей. Некоторые из них нужно накатывать в определённой последовательности, кроме того, что приходит и само ПО, и патчи не стоят на ум — этоместе и могут меняться, и неплохо бы иметь также их историю. Можно, конечно, аналоги [http://savannahпросто создать форк, но в этом случае ваша ветка, скорее всего, серьёзно разойдётся с оригинальной и «выделять» из неё отдельные фичи станет очень тяжело.nongnu.org/projects/quilt quilt]'аКороче говоря, работающие поверх DVCS: [http:если кому-то хочется увидеть наглядный пример из разряда «нафига оно надо», выполните команду <tt>apt-get source mc<//mercurial.selenic.com/wiki/MqExtension Mercurial Queues]tt>, [https://launchpad.net/bzrто есть, скачайте Debian-loom Bazaar Loom], пакеты с исходными кодами для [http://www.procodemidnight-commander.org/stgit/ StGITMidnight Commander]. Все они очень похожи и друг на друга — патчей там чуть меньше, и на сам '''quilt'''чем 50.
'''=== quilt''' вещь банальная, позволяющая автоматизировать тупое накатывание последовательности большого числа патчей и правку патчаMQ, который находится гдеbzr-то в середине: можно откатить N верхних патчейloom, внести изменения в файлы и сказать refresh, и верхний на данный момент патч (то есть патч откуда-то из середины) обновится, дабы соответствовать внесённым изменениям. '''quilt''' создан на основе скриптов человека, который не использует системы контроля версий — Эндрю Мортона (Andrew Morton) — второго по значимости участника разработки ядра Linux после Линуса Торвальдса.StGIT ===
Из реализаций Первое, что приходит на ум — это, конечно, аналоги [http://savannah.nongnu.org/projects/quilt quilt]'а, работающие поверх DVCS появился именно : [http://mercurial.selenic.com/wiki/MqExtension Mercurial Queues] (MQ и долгое время), судя по всему[https://launchpad.net/bzr-loom Bazaar Loom], был «изюминкой» Mercurial’а[http://www.procode.org/stgit/ StGIT]. Все они очень похожи и друг на друга, хотя и на самом деле неидеален — идея добавления сам '''quilt'''. MQ и удаления истории из/в репозиторий всёStGIT достаточно развиты и стабильны. bzr-таки странновата, а патчи не хранятся в том loom же репозитории, что и код — то есть, при клонировании исчезаютпока находится на стадии развития.
Крут ли '''quilt''' — вещь банальная, нет ли — каждый решает сам для себя. Конечнопозволяющая автоматизировать тупое накатывание последовательности большого числа патчей и правку патча, поддержке Debianкоторый находится где-то в середине: можно откатить N верхних патчей , внести изменения в файлы и сказать <tt>refresh</tt>, и верхний на какой-нибудь Midnight Commander данный момент патч (их там в районе 60то есть патч откуда-ито из середины) обновится, дабы соответствовать внесённым изменениям. '''quilt … помогает. Тем не менее, с моей точки зрения, некорректно закладываться ''' создан на жёстко последовательное применение всех патчей. На самом делеоснове скриптов человека, такие патчи логично организовывать в виде графа который не использует системы контроля версий — Эндрю Мортона (графа зависимостейAndrew Morton). Сразу будет видно, что большинство патчей независимы друг от друга, а зависимости окажутся на виду — второго по значимости участника разработки ядра Linux после Линуса Торвальдса.
Таким образомИз реализаций '''quilt''' поверх DVCS первым появился именно '''MQ''' и долгое время, судя по всему, был «изюминкой» Mercurial’а, хотя на самом деле совсем не идеален — идея добавления и удаления истории из/в репозиторий всё-таки странновата, а патчи не хранятся в том же репозитории, что и код — то есть, при клонировании исчезают. Этого недостатка лишены и '''bzr-loom''', и '''StGIT'''. === Подход «по ветке на патч» === Крут ли '''quilt''', нет ли — каждый решает сам для себя. Конечно, поддержке Debian-патчей на какой-нибудь Midnight Commander '''quilt''' очень… помогает. Тем не менее, с моей точки зрения, некорректно закладываться на жёстко последовательное применение всех патчей. На самом деле, такие патчи логично организовывать '''в виде графа''' (графа зависимостей). Сразу будет видно, что большинство патчей независимы друг от друга, а реально существующие зависимости окажутся на виду. Теперь вспомним, что мы рассматриваем DVCS и что они умеют очень хорошо управлять ветками, а ревизии как раз организуются в граф — и поймём, что задачу управления патчами удобнее всего решать, заводя ''по отдельной ветке '' на каждый патч. Причём даже для этого есть даже расширения Mercurial '''[http://arrenbrecht.ch/mercurial/pbranch/ pbranch] ''' (от patch branches) и Git '''[http://repo.or.cz/w/topgit.git TopGit] ''' (от topic branches). Для Bazaar’аподобного расширения, увы, таких нет. ==== Mercurial pbranch ==== '''Mercurial''''овский '''pbranch''': хорошо. Умеет весьма немногое. Самые полезные команды — это <tt>'''pdiff'''</tt>, <tt>'''pgraph'''</tt> и <tt>'''pmerge'''</tt>. <tt>'''pdiff'''</tt> экспортирует текущий патч '''без его родителей''' в стандартном формате. <tt>'''pgraph'''</tt> показывает граф зависимостей патчей, из которого можно, по сути, понять, в какой последовательности их накатывать, если они сами уже экспортированы. Ну и просто вообще граф патчей — это удобно. <tt>'''pmerge'''</tt> объединяет изменения, произошедшие в зависимостях патча, в патч. Другие команды расширения: ;pbackout: отменить текущий патч.;pmessage: посмотреть описание патча.;peditmessage: изменить описание патча.;pemail: отправить патч по почте, имхо — бесполезная вещь.;pnew: создать новый патч из ещё не закоммиченных изменений в текущей ветке.;pstatus: посмотреть статус патча (какие изменения требуются).;reapply: применить некую уже закоммиченную ревизию в ветку патча. Чего не хватает (а хочется): * Легко создавать патчи, зависящие от нескольких других (хотя это можно делать и вручную).* Легко поддерживать ветку, содержащую объединение всех патчей — для развёртывания.* Удалять зависимости патчей. Первые два пункта в pbranch делаются как-то уж совсем нелогично — нужно '''ручками''' прописать зависимость патча в файл <tt>.hg/pgraph</tt> и вызвать команду <tt>hg pmerge</tt>, которая автоматически объединит все пока что не объединённые зависимости в необходимые ветки. Очень странно, что предписано общаться через <tt>.hg/pgraph</tt> при том, что этот файл вообще-то не является никаким «окончательным хранилищем» — его скорее можно классифицировать, как кэш команды <tt>pgraph</tt> — если его удалить, он будет успешно воссоздан, и при клонировании репозитория он также не копируется. Зато, в отличие от TopGit, '''pbranch''''евый репозиторий без проблем клонируется со всеми ветками и информацией о патчах, и документация на '''pbranch''' [http://arrenbrecht.ch/mercurial/pbranch/index.htm весьма вменяема]… ===== Пример использования ===== Лично я, для управления патчами MediaWiki, входящими в состав CustIS’овской сборки MediaWiki, и хранящимися под SVN в виде набора diff-файлов, выбрал именно '''pbranch''' и следующую схему работы:* SVN-репозиторий компании содержит директорию <tt>custisinstall</tt> с конфигурационными файлами и патчами в виде diff’ов и директорию extensions с расширениями, написанными либо сильно модифицированными (по сути «форкнутыми») нами. Без веток, без меток.* Ветка <tt>default</tt> содержит импортируемый <tt>hgsubversion</tt>'ом SVN-репозиторий.* Ветка <tt>mediawiki</tt> содержит дистрибутив оригинальной MediaWiki в том виде, в каком он должен присутствовать в DocumentRoot’е, плюс расширения, слабо модифицированные (патчами) или вообще не модифицированные нами, в поддиректории <tt>extensions</tt>.* На основе ветки <tt>mediawiki</tt> создаются отдельные ветки для независимых друг от друга патчей в код MediaWiki и/или код расширений из ветки mediawiki.* Для патчей, зависящих от других, создаются ветки на основе веток этих патчей.* Ветка <tt>all</tt> содержит объединение всех веток патчей.* Ветка <tt>mergeinstall</tt> содержит объединение веток <tt>all</tt> и <tt>default</tt> и используется для развёртывания.* Все зависимости между ветками автоматически отслеживаются '''pbranch''''ем. Таким образом, для установки нашей сборки MediaWiki через Mercurial надо только клонировать репозиторий и обновиться до ветки mergeinstall — сразу же будут получены все патчи и расширения. При этом все патчи с помощью команды <tt>hg pdiff</tt> экспортируются в diff-файлы и коммитятся в Subversion, поэтому доступен и старый метод установки — Python-скрипт, выкачивающий из svn.wikimedia.org код MediaWiki, из локального Subversion’а — код нужных расширений и патчи, и накатывающий эти патчи на MediaWiki автоматически. ==== TopGit ==== '''TopGit''': тоже весьма хорошо, даже чуть получше (версия на момент тестирования была 0.8). А в будущем станет ещё лучше — по ходу просмотра справки по некоторым командам выводится по одному-два TODO — значит, они, наверное, появятся. Забавно, кстати, что '''TopGit''' явно написан в общем духе git-а — смесь скриптов на bash, perl и программ на C. «Головной» скрипт '''<tt>tg</tt>''' (TopGit) написан на шелле, и команды <tt>tg --help, tg -h, tg help</tt> и т. п. сначала вводят в недоумение — «<tt>no git repository here</tt>». Нет, не подумайте, справку TopGit печатать умеет, но для этого сначала надо зайти в git-репозиторий :) типа ''Чего ты спрашиваешь? У тебя интерес реальный? Ты покупать будешь или нет?..'' '''TopGit''', как уже сказано, умеет побольше '''pbranch''', в частности, умеет 1-ую и 2-ую вышеупомянутые «хотелки» — команда <tt>tg create</tt> может создать новую ветку-патч на основе множества зависимостей, автоматически объединяя их и предлагая разрешать возникающие конфликты, а с помощью <tt>tg update</tt> можно обновлять изменения в зависимостях в ветки патчей, причём рекурсивно. На самом деле и то, и другое можно сделать и в '''pbranch''', и даже вообще без него, но придётся набрать чуть больше команд. Команды расширения: ;create: создать новую ветку-патч на основе одной или нескольких веток.;delete: удалить ветку-патч.;depend: редактировать зависимости.;export: экспортировать историю текущей ветки в виде quilt-серии или новой, «подчищенной», git-ветки.;import: импортировать коммиты в виде патчей.;info: аналог <tt>hg pstatus</tt>, выводит состояние ветки — сколько, чего и откуда надо обновить и т. п.;mail: отправка патчей по почте.;patch: аналоги <tt>hg diff</tt>, выводит текущий патч в виде diff + комментарий вверху.;push: отправляет изменения из tg-шной ветки.;remote: перевести внешние (remote-tracking) ветки под управление tg.;summary: выводит список патчей со статусами и/или граф зависимостей через [[lib:Graphviz|Graphviz]]. В ASCII-виде этот граф, к сожалению, не рисуется.;update: см.выше. Чего не хватает (а хочется): * Возможности клонирования репозитория с учётом TopGit’а.*: С клонированием Git, как известно, выпендривается своими «[http://www.gitready.com/beginner/2009/03/09/remote-tracking-branches.html Remote Tracking Branches]», а TopGit, к сожалению, не полностью основан на стандартных механизмах Git. Поэтому с помощью команд <tt>git clone</tt> или <tt>git pull</tt> скопировать репозиторий с сохранением всей информации TopGit невозможно. Между прочим, весьма серьёзный недостаток.* Удалять зависимости патчей.*: То есть по-настоящему удалять зависимости, а не просто отменять изменения. Но это в TODO перечислено и, вероятно, будет реализовано.
== Схема управления рабочими копиями ==
'''Mercurial''' говорит нам: на ровно 1 рабочую копию ровно 1 репозиторий, и иначе быть не может. Это и удобно — сказал <tt>hg up ветка_такая_то</tt>, пара файлов поменялась, и опа — ты уже в другой ветке. Это и неудобно — чтобы положить на диск одновременно две разных ветки, нужно обязательно клонировать репозиторий (поддержки checkout нет).
С '''Git''''ом ситуация почти такая же, как и с Mercurial’ом. 1 рабочая копия, 1 репозиторий. Хотя при клонировании данные репозитория можно и не копировать, задавая опцию <tt>--shared</tt>, но это скорее похоже на Bazaar’овские [http://doc.bazaar-vcs.org/latest/en/user-guide/stacked.html Stacked Branches], чем на Lightweight Checkout. Идея checkout’ов, или лёгких рабочих копий (или «идея .gitlink») [http://git.or.cz/gitwiki/SoC2007Ideas высказана для GSoC-2007], однако (пока ?) так и не реализована. Управление ветками в '''Bazaar''' вначале было гораздо хуже: на 1 ветку ровно 1 рабочая копия и ровно 1 репозиторий. Однако потом появилось уникальное: <tt>checkout</tt>'ы и [http://bazaar-vcs.org/SharedRepositoryTutorial Shared Repository], имеющий опцию <tt>--no-trees</tt>. Таким образом, стало возможно иметь сколько угодно репозиториев и сколько угодно рабочих копий. В Git и Mercurial этого нет. Переключать легковесную рабочую копию с ветки на ветку Bazaar тоже умеет — командой <tt>switch</tt>, для переключения ветки её надо превратить в легковесную рабочую копию командой <tt>bind</tt>. Тут надо сделать небольшое отступление: Bazaar имеет несколько идеологических моментов. Первый — это идея, гласящая, что '''''директории — не контейнеры веток, а сами ветки'''''. В одной директории, содержащей рабочую копию, не может содержаться нескольких веток. Существуют ещё [http://bazaar-vcs.org/SharedRepositoryTutorial разделяемые репозитории], но они сами по себе не содержат рабочей копии, а содержат директории, которые могут содержать рабочие копии. Идея, с моей точки зрения, далеко не лучшая. Например, из-за неё Bazaar не может, как Git и Mercurial, переключиться на произвольную ревизию и по коммиту автоматически ответвиться в сторону. В Bazaar можно только сделать <tt>revert</tt> («откат») к определённой версии. Рабочие файлы при этом изменятся, а «состояние» рабочей копии — нет. Считается, что рабочая копия ''всегда'' содержит последнюю сохранённую ревизию. Вторая идея — это '''''идея mainline''''', то есть, идея поддержки «центральной» ветки разработки на уровне системы контроля версий. С хвалебной точки зрения про mainline можно почитать в блоге «Базарный день» (части [http://bzr-day.blogspot.com/2009/09/mainline-1.html 1], [http://bzr-day.blogspot.com/2009/09/mainline-2.html 2], и будут ещё). Я же выступлю с критической точки зрения — никакая это не гениальная практика, а просто ещё один хардкод, который можно выразить простыми словами: после синхронизации рабочих копий ''HEAD-ревизия'' (ревизия, не имеющая дочерних — в терминах Mercurial), ''всегда ровно одна''. Все другие ''HEAD’ы'' физически находятся в других директориях (= ветках), ещё не синхронизированных с данной. То есть, Bazaar заставляет пользователя объединять историю при каждой синхронизации с другой веткой. Из ''идеи mainline'' сразу следует определённое неудобство: при синхронизации '''меняется история''' веток (даже твоей, любимой, неприкосновенной) — ревизии, внесённые ранее локально, могут быть перемещены в «под-узлы» ревизии-объединения, которую в свою ветку внесла удалённая сторона. История меняется так, чтобы в конечном счёте история всех веток выглядела одинаково. То есть, «история истории» теряется, а ещё из перестановок ревизий в истории сразу следуют проблемы работы с Subversion. Короче говоря, да, это просто очередной «хардкод». То ли — рассчитанный на то, что если дать людям свободу, они не будут в состоянии достаточно часто объединяться с основной веткой, то ли — просто унаследованный от Arch’а ([[wikipedia:GNU Arch|GNU Arch]] — прародитель Bazaar’а).
И если уж зашла речь о недостатках '''Bazaar''' вначале был хуже всех’а, а потом стал лучше всех. Вначале ещё следует отметить, что он говорил нам: на 1 ветку ровно 1 рабочая копия не умеет показывать консольные ASCII-псевдографические графы ревизий. '''Git''' и '''Mercurial''' умеют — и ровно 1 репозиторийэто очень удобно. А когда исправилсяразработчики Bazaar считают их отсутствие фичей — ревизии, стало можно создать [http://bazaar-vcs.org/SharedRepositoryTutorial Shared Repository] с опцией <tt>--no-trees</tt>типа, а потом сколько захочется рабочих копий <tt>checkout</tt>сортируются, зачем графы? ''(и правда, зачем? стройте графы сами, в голове! как не умеете?!!)''ами. Переключить рабочую копию с ветки на ветку также можно — командами <tt>switch</tt> или <tt>qswitch</tt>.
[[Категория:Разработка]]
[[Категория:Статьи]]