среда, 24 марта 2010 г.

Командная строка и bzr: "ленивые" опции

Следующий совет может оказаться полезным для тех, кто использует bzr из командной строки на постоянной основе или даже время от времени. Как вы помните, многие команды bzr имеют встроенные псевдонимы, а также пользователь может задавать свои собственные пользовательские псевдонимы при помощи команды alias. Так вместо status можно использовать st, а вместо commit ci. Однако эти самые команды зачастую имеют обширный набор опций для тонкого управления поведением каждой команды. Все эти опции имеют основную форму вида --имяопции, и могут иметь короткую форму вида -X где X — это одна буква латинского алфавита. Для того, чтобы узнать какая короткая форма у определенной опции, просматривайте информацию об использовании команды. Например, опция --revision имеет короткую форму -r (о чем вы наверняка знаете).

Однако это еще не всё. Движок, используемый для обработки опций и аргументов команд bzr позволяет использовать сокращенную запись для основной формы. Сокращать допускается до того состояния пока bzr сможет однозначно понять, что вы имеете ввиду, обычно достаточно оставить 2-3 первых буквы. Например, опцию --revision можно сокращать до --rev, а опцию --force до --fo. Тогда pull --remember можно вызывать как pull --rem, а push --overwrite как push --over, и т.д. И поскольку не все опции имеют короткую форму из одной буквы, то часто бывает полезно знать эту возможность.

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

C:\work\bzr-day\options-for-lazy>bzr pull --re lp:foo
bzr: ERROR: ambiguous option: --re (--remember, --revision?)

В данном случае bzr не смог понять какую именно опцию вы имели ввиду: --remember или --revision.

Механизм поддержки сокращений для опций работает для всех опций самой команды, но не работает для глобальных опций самого bzr, таких как --no-plugins, --no-aliases и т.п. Полный список глобальных опций можно увидеть запустив команду bzr help global-options.

воскресенье, 21 марта 2010 г.

Использование внешнего редактора для команды bzr shelve

Одной из очень полезных функций в bzr является пара команд shelve/unshelve. Эти команды позволяют временно удалить из рабочих файлов часть изменений, чтобы зафиксировать остальные изменения без них. Часто это бывает нужно для логического разделения разных правок. Например, вы начали реализовывать новую функцию в своей программе и попутно обнаружили и исправили старый баг. Имеет смысл зафиксировать исправление ошибки отдельно и затем продолжить работу над новой функцией. В этом случае помогает shelve/unshelve.
Подобная функциональность присутствует в других системах контроля версий. Так, в git для этого используется команда stash, а в hg для этого используется расширение shelve.
Подробный рассказ об использовании shelve/unshelve заслуживает отдельной статьи. Вкратце процесс отбора изменений для удаления их "на полочку" выглядит следующим образом:
  • вы запускаете команду shelve, опционально указывая список файлов, в которых нужно отобрать изменения.
  • для каждого файла с изменениями bzr вам показывает отдельные блоки изменений (diff hunks), аналогично тому как блоками показывает изменения команда diff.
  • для каждого блока вы указываете: хотите ли вы убрать это изменение "на полочку" либо оставить в рабочей копии.
  • после того, как вы ответите на вопрос по каждому блоку каждого файла с изменениями, отобранные изменения удаляются из рабочей копии и сохраняются в специальном хранилище ("на полочке").
Подробнее об использовании shelve смотрите справку по этой команде.

Одним из неудобств при использовании shelve является гранулярность отбора изменений. Вы не можете так просто удалить одну строку с изменениями из целого блока (hunks). Вам приходится или убирать весь блок или оставлять весь блок. Что было не очень удобно.

Однако, к нашей радости, в bzr 2.1.0 у shelve появилась новая функция: теперь можно использовать внешний редактор для правки и отбора изменений. В качестве редактора имеет смысл использовать программу для просмотра и редактирования изменений в 2х файлах. Например, vimdiff, WinMerge и проч.

Для того, чтобы использовать внешний редактор в команде shelve его следует предварительно задать в файле конфигурации bazaar.conf. Для этого в секции [DEFAULT] добавьте строку вида:
change_editor = vimdiff -fo @new_path @old_path
Здесь: @new_path @old_path — это специальные параметры, вместо которых в реальности bzr будет подставлять полный путь к редактируемому файлу (@new_path) и полный путь к файлу с последним зафиксированным состоянием (@old_path).

Так, например, я для использования WinMerge указал следующую строку в bazaar.conf:
change_editor = '"C:/Program Files/WinMerge/WinMergeU.exe" @old_path @new_path'
После того, как вы настроите change_editor, при вызовах shelve в строке выбора для каждого блока изменений вам будет доступен дополнительный выбор ("e"). По нажатию на клавишу "e" (editor) будет запущена указанная вами программа для редактирования текущего файла. Ваша задача: отредактировать @new_path до состояния, в котором вы хотите, чтобы он был на диске. Команда shelve сама определит какие изменения нужно убрать из текущей рабочей копии конкретного файла, чтобы в итоге получилось то, что вы хотите.

Приятной работы!

четверг, 18 февраля 2010 г.

Использование плагина bzr-pipeline для "вечных" локальных правок

Иван Сагалаев описал один из вариантов работы с плагином bzr-pipeline. Иван пишет:
...хочу поделиться bzr-специфичным решением одной практической ситуации, возникающей при групповой работе над проектом.
Работая над общим кодом на локальной машине, иногда бывает нужно делать в нём строго локальные правки: пути к файлам, адреса серверов, отладочное логирование. При этом эти правки никогда не должны попадать в основной бранч проекта.
Часть из них можно и нужно вынести в отдельные локальные конфигурационные файлы, которые просто заигнорировать. Но вот с упомянутым отладочным логированием так не получится — это произвольный код, временно раскиданный по файлам. Ещё один пример такой правки — полное вырезание куска кода, который не критичен для работы, но не даёт проекту завестись с локальном окружении.
В bzr для этой проблемы мне известны два решения: одно прямое, другое — более удобное, с помощью плагина bzr-pipeline. Беда только в том, что в его документации описан несколько другой юзкейс, и я с первого раза вообще не понял, как оно мне поможет. Потом разобрался и решил восполнить пробел.
Читать целиком: http://softwaremaniacs.org/blog/2010/02/18/local-patches-in-bzr-pipeline

среда, 17 февраля 2010 г.

Новая стабильная версия Bazaar 2.1.0

Спустя полгода после выпуска стабильного релиза bzr 2.0.0 разработчики Bazaar представляют новую стабильную версию Bazaar 2.1.0 (февраль 2010 года).

Новая версия содержит в себе много разных улучшений и исправлений дефектов, однако ничего революционного (вроде нового формата репозитория) не несёт. Список отдельных улучшений просто огромен (более 200 пунктов), поэтому рассмотрим только ключевые и заметные для пользователей моменты.
  • Значительно уменьшено потребление памяти для выполнения операций (уменьшение почти в два раза). Это значит, что с ветками с большой историей работать будет быстрее и комфортнее.
  • Добавлен новый хук для объединения файлов. Теперь становится возможным для файлов разных типов определять различные алгоритмы объединения изменений. Для использования этой возможности необходимо будет написать соответствущий плагин.
    В качестве примера такого плагина вместе с bzr поставляется плагин news_merge, который должен облегчить процесс объединения изменений в файле NEWS в исходном коде самого bzr. (Файл NEWS в bzr редактируется очень часто, поэтому конфликты возникают очень часто).
  •  В файле .bzrignore теперь можно задавать шаблоны для отключения игнорирования специфичных файлов. Для этого используйте восклицательный знак (!) в начале шаблона.
    Например, игнорируем все файлы кроме *.py:
    * 
    !*.py
  • Функция shelve: теперь имеется возможность использовать внешний редактор для редактирования изменений, которые должны быть отложены "на полочку". Для этого нужно указать редактор в файле конфигурации как опцию change_editor, например:
    change_editor = vimdiff -fo @new_path @old_path
    
    здесь @new_path и @old_path специальные макросы для подстановки пути к текущей версии файла и зафиксированной его версии.
  • Новая опция unshelve --preview для просмотра отложенных изменений
  • Для checkout теперь можно сделать switch --revision, update --revision
  • При указании ревизии опцией --revision (-r) теперь можно использовать более простой синтаксис в стиле "делай то, что я имею в виду" (DWIM). Т.е. вместо -r tag:foo можно использовать просто -r foo, а вместо -r date:today просто -r today. Префиксы по прежнему можно использовать, и они могут понадобиться в случаях, когда bzr не сможет угадать правильный вариант (например тег 1.1.1 может совпадать с номером ревизии 1.1.1).
  • На Windows в командной строке поддерживаются маски для имен файлов, как на Linux. Раньше такая поддержка существовала в специфичном виде только для команды bzr add, теперь же она реализована в общем виде для всех команд. Поэтому теперь для добавления маски *.obj в файл .bzrignore командой bzr ignore вам нужно позаботиться о том, чтобы аргумент *.obj был взят в кавычки.
  • Поддержка знака тильды (~) как домашнего каталога пользователя в URL вида bzr+ssh://host/~
  • Появился новый раздел в документации: Bazaar System Administrator’s Guide 
  • Значительно переработана документация по плагинам. Кроме основных сведений также содержит автоматически сгенерированную документацию по всем публичным плагинам.
Кроме изменений в самом bzr множество изменений в сопутствующих инструментах и плагинах. Так GUI для bzr Bazaar Explorer меньше чем за 9 месяцев доросло до версии 1.0, с чем мы и поздравляем его автора Йена Клэтворси (Ian Clatworthy).

Разработчики bzr объявили, что предыдущая стабильная версия bzr 2.0 (текущий релиз по состоянию на февраль 2010 — это 2.0.4) будет поддерживаться и далее, но настоятельно рекомендуют переходить на 2.1. Серия 2.1 будет поддерживаться в течение года (как минимум), она же будет включена в дистрибутив Ubuntu Lucid.

Сморите также:

воскресенье, 7 февраля 2010 г.

Уникальные идентификаторы файлов в bzr

Как я уже упоминал в статье "одна ветка = один каталог" bzr унаследовал из baz такую особенность как уникальные идентификаторы файлов. В этой статье рассмотрим идентификаторы подробнее.
В примерах использовался bzr версии 2.1.

Для чего нужны идентификаторы файлов

Для каждого файла, каталога или симлинка Bazaar использует внутри некий уникальный идентификатор, аналогично уникальным идентификаторам ревизий. Присвоение каждому версионированному объекту идентификатора упрощает отслеживание переименований/перемещений файлов и каталогов. Таким образом операция получения журнала ревизий для одного файла, а также операция объединения изменений в разных ветках, — эти операции работаю гораздо эффективнее поскольку им не требуется отслеживать все прошлые переименования. Чтобы получить копию файла из прошлой ревизии через команду
bzr cat -rN file
вам не нужно задумываться о том,  были ли переименования и какое имя было у файла в той ревизии: bzr использует идентификатор файла и сам найдет нужные данные.

Уникальные идентификаторы назначаются в момент добавления файлов/каталогов под контроль версий командой add и затем в течении жизни файлов/каталогов уже не меняются.

Уникальные идентификаторы файлов можно посмотреть с помощью команды bzr ls --show-ids:
$ bzr ls --show-ids
bar.txt    bar.txt-20100207052407-e0zzku5zxmzfsso8-1
foo/       foo-20100207052337-rv090vn2o3x0m720-1

Проблемы с идентификаторами файлов

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

Текущая реализация идентификаторов файлов в bzr страдает следующими недостатками:
  1. Один и тот же файл добавленный в разных ветках одного и того же проекта будет иметь разный идентификатор. Что при последующем объединении веток приведет к проблеме: bzr будет считать эти два файла абсолютно разными. В результате вы получите конфликт объединения и вам придется выбрать какой файл оставить, а какой удалить (и соответственно потерять историю изменений). Изменения между версиями из разных веток придется объединять вручную.
  2. Если вы отмените контроль версий для файла командой bzr remove file а затем передумаете и снова добавите его командой bzr add file, то команда add назначит файлу новый уникальный идентификатор, соответственно история файла прервется и начнется с нуля.
  3. Текущая реализация уникальных идентификаторов в bzr препятствует реализации функции создания копий файлов (аналогично svn copy или hg copy).
Озвученные недостатки являются неприятными, но не критическими. Большинство проектов эти недостатки может и не заметить. Однако замалчивать их в этом блоге я не буду.

В теории эти недостатки можно преодолеть, и главные разработчики bzr даже имеют определенные планы на этот счёт. Но по состоянию на версию bzr 2.1 эти планы еще не реализованы и сложно сказать когда они все-таки будут реализованы.

Проблемы 1 и 2 из списка выше возможно решить в текущей версии bzr с помощью определенных техник.

Использование bzr add --file-ids-from

Поскольку bzr назначает уникальный идентификатор во время выполнения команды bzr add, то для частичного преодоления проблемы номер 1 из списка выше (один и тот же файл добавленный в разных ветках будет иметь разные идентификаторы) можно воспользоваться опцией --file-ids-from команды add. Эта опция в качестве своего аргумента принимает путь к другой ветке и для всех новых файлов, имена которых совпадают с именами файлов в той другой ветке, используются уникальные идентификаторы из другой ветки. Таким образом можно избежать расхождения идентификаторов.

Пример:

C:\work\bzr-day\file-ids>bzr init a

C:\work\bzr-day\file-ids>bzr mkdir a/foo
added a/foo

C:\work\bzr-day\file-ids>bzr commit -m1 a
Committing to: C:/work/bzr-day/file-ids/a/
added foo
Committed revision 1.

C:\work\bzr-day\file-ids>echo > a/bar.txt

C:\work\bzr-day\file-ids>bzr add a
adding bar.txt

C:\work\bzr-day\file-ids>bzr ls a --show-ids
a/bar.txt    bar.txt-20100207052407-e0zzku5zxmzfsso8-1
a/foo/       foo-20100207052337-rv090vn2o3x0m720-1

Теперь создадим копию ветки а для ревизии 1 и также попробуем добавить файл bar.txt:

C:\work\bzr-day\file-ids>bzr branch a b
Branched 1 revision(s).

C:\work\bzr-day\file-ids>echo > b/bar.txt

C:\work\bzr-day\file-ids>bzr add b
adding bar.txt

C:\work\bzr-day\file-ids>bzr ls b --show-ids
b/bar.txt    bar.txt-20100207052932-m88m6b14qvdroa2s-1
b/foo/       foo-20100207052337-rv090vn2o3x0m720-1

Как мы видим уникальные идентификаторы для файла bar.txt в разных ветках различаются.

Повторим те же действия, но файл будем добавлять с опцией --file-ids-from:
C:\work\bzr-day\file-ids>bzr branch a c
Branched 1 revision(s).

C:\work\bzr-day\file-ids>echo > c/bar.txt

C:\work\bzr-day\file-ids>bzr add c --file-ids-from a
adding bar.txt w/ file id from bar.txt

C:\work\bzr-day\file-ids>bzr ls c --show-ids
c/bar.txt    bar.txt-20100207052407-e0zzku5zxmzfsso8-1
c/foo/       foo-20100207052337-rv090vn2o3x0m720-1

Теперь файл bar.txt добавлен с тем же самым уникальным идентификатором.

Восстановление контроля версий для файла после команды bzr remove

Выше указывалось, что вторым недостатком уникальных идентификаторов является то, что файл удаленный из-под контроля версий командой bzr remove при последующем добавлении командой bzr add получит новый уникальный идентификатор. Как следствие для такого файла прервется история. Если вы не хотите терять историю, то вместо использования bzr add для восстановления файла вам необходимо использовать команду bzr revert. В этом случае файл будет восстановлен в последнем зафиксированном состоянии с правильным уникальным идентификатором.

Даже если вы удалили файл несколько ревизий назад, то используйте команду bzr revert -rN file, где N — это номер ревизии, предшествующей удалению файла.

пятница, 15 января 2010 г.

Работа с bzr в централизованном стиле

Наш читатель и автор плагина bzr-externals Евгений Тарасенко рассказывает о работе с Bazaar в централизованном стиле (а-ля svn). Передаю ему слово.

Централизованный стиль

В данной статье я хотел бы обратить внимание на еще один вариант организации работы с системой контроля версий Bazaar, о котором часто забывают в пылу погони за ветками и DVCS. Я говорю о централизованном стиле работы, который используется в Subversion, но в отличии от последнего Bazaar предоставляет намного более гибкий и главное понятный инструмент разработчику. Так в нашем распоряжении оказываются: понятная история ветвлений (попробуйте bzr qlog), локальные фиксации, откат и много других приятных мелочей. Также не стоит забывать что Bazaar расширяемая система и если вам там чего-то не хватает, то можно попробовать написать это самому!

Итак, чем хорош централизованный стиль:
  • во-первых, вся работа ведется в одной директории что очень актуально для многих IDE, которые забывают про мелкие настройки при переходе в другой каталог
  • во-вторых, если вы используете С++ компилятор то количество дополнительных файлов просто съест ваше время и место на жестком диске при каждом новом построении, а при работе с ветками неизбежно придется компилировать и отлаживать сначала ветку, а после слияния и trunk
  • ну и в-третьих, иногда бывает нужно зафиксировать небольшое изменение (одну ревизию) и тут у вас просто меньше лишних телодвижений в отличии от стилей с feature branch или с переключением веток.

Про TortoiseBzr

Для начала, я хотел бы обратить внимание на тот факт, что при установке Bazaar под Windows по умолчанию отключен модуль TortoiseBzr, скорее всего это связано с его некоторой задумчивостью при обновлении статуса иконок файлов, частенько бывает нужно пару раз F5 нажать, чтобы он сообразил что к чему. Поэтому если вы любитель работы из проводника проверьте, что он у вас установлен.

Базовый набор команд

Поскольку Bazaar поддерживает различные псевдонимы (алиасы) для команд, то вы можете выбрать для себя наиболее удобный вариант использования:

checkout, co, qgetnew — извлечь рабочую копию
commit, ci, qcommit, qci — фиксировать изменения
update, up, qupdate, qup — обновить рабочую копию

Я думаю, общая идея понятна, хочу только отметить что префикс "q" обозначает команды с GUI интерфейсом, предоставляемые плагином QBzr.

Извлечение проекта из хранилища

Команда:
bzr checkout bzr://server/project/trunk project
или из контекстного меню проводника: 'Bazaar Checkout/Branch…'



Как вы можете заметить, интерфейс диалога частично переведен, но используемая терминология может ввести в ступор начинающего пользователя, тем не менее, нам нужен пункт "Создать рабочую копию".

Внесение изменений

После внесения локальных изменений (непосредственно программирования) у нас есть два пути.

Путь первый. Если ваши локальные изменения невелики, то вы можете их сразу отправить в хранилище, также как это делает Subversion. Для этого сначала обновляем нашу рабочую копию до последней ревизии находящейся в хранилище:
bzr update
Обратите внимание что слияние ваших изменений и новых ревизий из хранилища произойдет автоматически, без дополнительного bzr merge, что в большинстве случаев является оправданным при правильном распределении задач между разработчиками, иначе придется редактировать конфликты.

И фиксируем наши изменения в хранилище:
bzr qcommit
Путь второй. Если же ваши изменения более значительны и разработка займет какое-то время, что обычно и бывает, то в процесс разработки добавляется новая команда фиксации промежуточных локальных изменений:
bzr qcommit --local
либо через контекстное меню 'Bazaar Commit…' и выбором пункта 'Локальная фиксация'



Команда выполняется столько раз, сколько требует логика разработчика, обычно стараются делать одно функциональное изменение = одна фиксация, но это уже из области best practice.

После окончания разработки и последней локальной фиксации, действуем по схеме 1:

bzr update

И вот тут происходит самое интересное, запустите:

bzr qlog




и вы увидите, что все локальные фиксации ответвились от основной линии, как будто вы вели разработку в отдельной ветке и теперь хотите ее объединить с основным деревом, статус pending merge что нам и нужно.

Тестируем наши объединенные изменения и если все нормально фиксируем в хранилище:

bzr qcommit

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

Как отменить merge сделанное в результате bzr update

Хочу предупредить, если после update у вас что-то пошло не так или вы просто передумали объединять ваши изменения, то не спешите использовать команду revert. Эта команда отменит неоконченное объединение и ваши изменения. Ваши локальные ревизии никуда не исчезнут, они останутся в хранилище истории локального дерева файлов. Но вы не вернетесь к своей локальной истории, а напротив останетесь с историей из основной ветки.

Для возвращения к локальной истории вам необходимо узнать идентификатор последней ревизии в вашей локальной истории. Проще всего это сделать при помощи GUI команды qlog, но можно и воспользоваться командой heads из плагина bzrtools.

В случае использования qlog выделите ревизию, помеченую ярлыком Pending Merge и затем в нижнем левом окне в первой строке с версией найдите подстроку вида revid:xxxx, в нашем примере это revid:_-20100111130959-gd9bfn7z2mxxixx3. Выделите ее мышкой и скопируйте в буфер обмена.


Затем закройте qlog и выполните следующие команды:
bzr revert
bzr unbind 
bzr pull . --overwrite -r revid:_-20100111130959-gd9bfn7z2mxxixx3
bzr bind 
Команде pull вы должны передать идентификатор ревизии локальной истории, тот самый, который мы скопировали из окна qlog.

Идентификатор ревизии локальной истории можно также получить при помощи команды heads из плагина bzrtools. Для этого запустите следующую команду:
bzr heads --dead-only
В ответ она выведет информацию следующего вида:
C:\work\bzr-day\Centralised\trunk>bzr heads --dead
HEAD: revision-id: _-20100111130959-gd9bfn7z2mxxixx3 (dead)
  committer: Базарный день
  branch nick: trunk
  timestamp: Mon 2010-01-11 15:09:59 +0200
  message:
    локальная фиксация №2
В строке "HEAD: revision-id:" показан нужный нам идентификатор. Опять же копируем его и запускаем те же действия:

bzr revert
bzr unbind 
bzr pull . --overwrite -r revid:_-20100111130959-gd9bfn7z2mxxixx3
bzr bind 

Даже если вы сделали revert до того, как посмотрели идентификатор локальной ревизии через qlog — не паникуйте, а используйте второй способ с командой heads.
Замечание редакции: если вы используете локальные фиксации, то перед выполнением команды bzr update убедитесь, что все ваши изменения зафиксированы командой commit, либо отложены "на полочку" командой shelve. Иначе вы их легко потеряете при выполнении команды revert. Однако, вместо локальных фиксацией всё-таки более безопасно использовать либо отдельную ветку либо отдельную рабочую копию.

Заключение

Итак, если вы до этого пользовались Subversion и хотите получить небольшой fun от использования системы контроля версий, то смело переходите на Bazaar. Если при этом вам захочется большего, я имею ввиду полный контроль над ветками, то Bazaar умеет и так работать, при этом не придется ломать уже налаженный стиль работы остальных. В общем Bazaar это настоящий швейцарский нож для разработчика с весьма дружественным интерфейсом.

воскресенье, 13 декабря 2009 г.

Модель "одна ветка = один каталог" в bzr

Немного истории

Современная система контроля версий Bazaar имеет свои корни в другой распределенной системе: GNU Arch. У системы Arch было несколько форков (разновидностей), одни из которых также назывался Bazaar (baz). Arch критиковали за излишнюю сложность интерфейса и трудность в освоении. Собственно современный Bazaar (bzr) появился как замена старому baz, и главной целью разработки нового bzr было именно упростить интерфейс пользователя системы, сделать систему удобной и лёгкой в использовании.

По большей части новый bzr добился своей цели (удобства использования). Однако при этом некоторые особенности модели системы Arch перекочевали в том или ином виде в новый Bazaar (bzr). И это накладывает свой отпечаток на работу самой системы и на работу пользователей с системой.

Следует заметить, что старый baz и новый bzr — обе эти системы разрабатывались и разрабатываются в основном за счет финансовой помощи компании Canonical Ltd, широко известной благодаря своему дистрибутиву Linux: Ubuntu.

Также следует отметить, что разработка нового bzr была задумана в ноябре 2004 года и официально началась в февраля 2005 года, на несколько месяцев ранее начала работ над git и hg. До первой половины 2006 года bzr развивался совершенно самостоятельно, как продолжатель baz. После стали очевидны успехи новых систем git и hg, особенно в плане показателей скорости, в результате чего внутренние алгоритмы bzr и форматы хранения данных в репозитории стали постепенно оптимизировать и улучшать в плане скорости работы. Однако несколько фундаментальных особенностей, унаследованных из baz/Arch, остались и до сегодняшнего дня. Понимание этих особенностей поможет вам более эффективно использовать bzr.

Унаследованные особенности

Основные особенности, унаследованные из Arch:
  • модель "каждая ветка живет в отдельном каталоге",
  • доступ к веткам возможен без специального сервера,
  • использование уникальных идентификаторов для файлов внутри системы.
Об уникальных идентификаторах файлов мы поговорим в другой статье, сейчас же рассказ пойдёт о модели "ветка=каталог", о её достоинствах и недостатках.

Каждая ветка живет в отдельном каталоге

Основная модель работы с ветками в bzr — это размещение каждой ветки в отдельном каталоге. У такого метода есть ряд плюсов и ряд минусов.

К плюсам можно отнести следующее. Каждая ветка однозначно присутствует как объект файловой системы, поэтому с ней можно оперировать средствами самой файловой системы, т.е. переименовывать, перемещать или удалять. Каждая ветка однозначно идентифицируется своим расположением, поэтому для доступа к ней достаточно использовать простой путь (для локального доступа) либо URL (для удаленного доступа), т.е. нет необходимости указывать URL + собственно имя ветки. Присутствие одной ветки в одном месте позволяет эффективно реализовать идею mainline. Каждая ветка хранит полную копию истории и полный набор рабочих файлов, что позволяет легко сравнивать и одновременно работать с несколькими ветками, которые содержат незафиксированные изменения.

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

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

Рассмотрим внимательно минусы. Они известны давно и для их нивелирования существует ряд методик эффективного использования bzr. Все указанные проблемы (каждая ветка занимает много места, долгое создание новой ветки, разделение одной рабочей копии) эффективно решаются при использовании разделяемого (или общего) репозитория (shared repository). Таким образом указанные минусы нивелируются использованием shared repository и остается лишь маленький недостаток: по умолчанию разделяемый репозиторий не создается сам по себе, его нужно создавать явно, командой init-repository.

Будем предельно честными: bzr позволяет эффективно работать с ветками, но по умолчанию создает обычные ветки. Это недостаток, который особенно сильно влияет на восприятие системы новичками, пришедших в bzr из других систем (git/hg), в которых всегда ветки живут в едином каталоге и представляют собой лишь виртуальные имена для разных линий истории проекта.

Из-за непонимания этого факта bzr много и не всегда оправданно критикуют.

Эффективное использование bzr: разделяемые репозитории

Что такое разделяемый репозиторий (shared repository)?

Для того, чтобы понять, что такое shared repository (разделяемый или общий репозиторий), сначала рассмотрим внутреннее устройство bzr-ветки. Каждая ветка содержит указатель на последнюю зафиксированную ревизию, что однозначно определяет всю историю ветки, поскольку история имеет вид направленного графа. Каждая ветка должна иметь репозиторий, в котором хранятся зафиксированные ревизии и состояние всех файлов ветки для каждой ревизии. Также ветка может иметь рабочую копию (а может и не иметь).

Для обычных веток репозиторий находится в том же каталоге, что и сама ветка. Однако это не обязательно. Репозиторий может находиться и за пределами каталога с веткой, в одном из родительских каталогов. Такой отдельный репозиторий может хранить зафиксированные ревизии и состояния файлов для нескольких веток, находящихся внутри репозитория. А поскольку история веток по большей части совпадает, то в результате место расходуется гораздо более экономно, чем в случае когда каждая ветка хранит полную копию всего репозитория. Каждая ветка, создаваемая внутри разделяемого репозитория, автоматически находит и начинает использовать этот репозиторий.

Поэтому такой репозиторий (shared repository) является общим для нескольких веток, либо можно сказать, что несколько веток разделяют (совместно используют) один и тот же репозиторий.

Следует запомнить фундаментальную разницу между ветками и репозиториями в bzr и репозиториями в git/hg. В bzr под репозиторием в большинстве случае понимают именно разделяемый репозиторий, каждая ветка внутри него — это отдельный каталог. В git/hg в одном каталоге живет и репозиторий, и ветки, и присутствует рабочая копия.

Локальная работа с ветками в репозитории

Для локальной работы рекомендуется создавать разделяемый репозиторий командой:
bzr init-repo PATH
После чего создавать копию главной ветки с сервера внутри репозитория:
cd PATH
bzr branch bzr://server/project/trunk
А уже для конкретной работы над новыми функциями создавать новую ветку на основе локальной копии trunk:
bzr branch trunk new-feature
cd new-feature
При такой работе каждая ветка будет иметь собственную рабочую копию, что позволит легко переходить между разными ветками и объединять изменения между ними. Такая методика работы подробно описывалась в наших предыдущих статьях (базовый набор команд и работа с ветками, см. оглавление).

Использование репозитория на сервере

Для хранения веток на сервере также полезно использовать разделяемые репозитории. Но зачастую сервер выступает просто хранилищем ревизий, поэтому рабочие копии для веток создавать не требуется. Это достигается командой:
bzr init-repo --no-trees URL
Опция командой строки --no-trees указывает, что по умолчанию рабочая копия для веток создаваться не будет.

Остальные операции с ветками можно производить как обычно.

Разделение одной рабочей копии между несколькими ветками

Как уже упоминалось выше в git/hg по умолчанию несколько веток живут в одном каталоге, который называют репозиторий, и в этом же каталоге присутствует рабочая копия, которую совместно используют все ветки. В каждый конкретный момент рабочая копия отражает состояние только текущей активной ветки. Будем называть такую модель работы "git-стиль".

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

Для этого нужно создать отдельный репозиторий с указанием флага --no-trees, чтобы локальные ветки в этом репозитории не создавали ненужные рабочие копии. А затем нужно создать отдельную легковесную рабочую копию (lightweight checkout), не содержащую истории. Такая легковесная рабочая копия может быть создана даже за пределами репозитория.
bzr init-repo --no-trees PROJECT
bzr init PROJECT/trunk
bzr checkout --lightweight PROJECT/trunk work
В результате выполнения этой последовательности команд bzr создаст репозиторий в каталоге PROJECT, внутри репозитория создаст ветку trunk, и затем создаст рабочую копию в каталоге work.

Для создания новой ветки и одновременного переключения рабочей копии на нее можно использовать команду:
bzr branch --switch trunk new-feature
Переключение рабочей копии между ветками осуществляется командой switch:
bzr switch trunk
При переключении можно указать опцию --create-branch (-b), чтобы автоматически создать новую ветку:
bzr switch -b bugfix-123
Для получения списка веток в репозитории используйте команду:
bzr branches PROJECT
из плагина bzrtools.

Более подробный рассказ о нюансах работы с bzr в git-стиле будет дан в виде отдельной статьи.

Заключение

В этой статье я попытался описать как и почему в bzr используется модель "одна ветка в одном каталоге", привести доводы за и против такого подхода, а также показать пути эффективного использования bzr.

Как вы могли убедиться, bzr — это очень гибкая система, и она позволяет достичь намного большего при умелом использовании. Однако требует несколько большего времени на освоение.

Недостатком можно назвать лишь то, что по умолчанию bzr работает в не самом оптимальном режиме, поэтому для достижения эффективности требуется сделать несколько больше телодвижений. Следует ли это считать смертельным недостатком? Уверен, что нет.

Впрочем, каждому решать самому. Помните лишь, что каждая из современных распределенных систем (git/hg/bzr) имеет как достоинства, так и недостатки. Однако очень часто значимость как плюсов, так и минусов, не абсолютна, а относительна, и зависит от конкретного проекта и предпочитаемого стиля работы. И часто одни недостатки компенсируются другими достоинствами.