воскресенье, 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) имеет как достоинства, так и недостатки. Однако очень часто значимость как плюсов, так и минусов, не абсолютна, а относительна, и зависит от конкретного проекта и предпочитаемого стиля работы. И часто одни недостатки компенсируются другими достоинствами.

пятница, 11 декабря 2009 г.

Уборка мусора: команда bzr clean-tree

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

И вот когда приходит время удалять это барахло, что вы делаете? Удаляете вручную?

Есть более удобный способ: использовать команду clean-tree (начиная с версии bzr 1.13 эта команда доступна в самом bzr, ранее эта команда жила в плагине bzrtools). Эта команда показывает вам список неизвестных (неверсионированных) файлов и предлагает их удалить. Просто и без затей.

Также эта команда имеет удобный ключ командной строки: --ignored. С этим ключом clean-tree будет удалять только игнорируемые файлы. Такая функция тоже бывает полезной, особенно если у вас нет make clean.

Приятной уборки!

вторник, 8 декабря 2009 г.

Управление кодировкой файла при работе с QBzr (GUI для bzr)

QBzr — это кросс-платформенный GUI плагин для bzr, использующий библиотеку Qt4/PyQt4. Мы рассмотрим вопрос управления кодировкой файлов для их корректного отображения в окнах QBzr.

QBzr и кодировки

Поскольку [Py]Qt4 ориентирована на использование unicode для отображения текстовых данных, то возникает небольшая проблема с файлами, содержащими неанглийские тексты (например, русский или японский). Для корректного отображения таких текстов QBzr необходимо знать кодировку, в которой был сохранен текст.

По умолчанию, QBzr предполагает, что текст имеет кодировку UTF-8 (с автоматическим переключением на Latin-1, если текст невозможно отобразить как UTF-8). Кодировка UTF-8 довольно популярна и используется на многих современных дистрибутивах Linux и Mac. Однако на Windows часто используется кодировка ANSI (например, cp1251 для русского текста), а на старых платформах — исторически сложившиеся кодировки (например, koi8-r для русского текста). Поэтому некоторым пользователям приходится указывать кодировку вручную одним из 3х способов:
  • указать кодировку в командной строке
  • указать кодировку в конфигурационном файле
  • указать кодировку непосредственно в диалоговом окне с отображаемым текстом
Рассмотрим эти методы подробнее.

Опция командной строки

Большинство команд, непосредственно отображающих содержимое файлов, таких как qdiff, qannotate, qcat и qviewer, имеют опцию командной строки --encoding. Эта опция позволяет напрямую задать кодировку файла из набора поддерживаемых библиотеками Python (полный список). Часто используемые кодировки для русского языка:
  • cp1251 (Windows ANSI)
  • cp866 (DOS/OEM)
  • koi8-r (Unix)
Для украинского языка аналогично: cp1251, cp866, koi8-u.

Настройки в конфигурационном файле

Кодировка, указанная опцией --encoding, либо выбранная непосредственно (см. ниже), сохраняется в конфигурационном файле текущей ветки (файл .bzr/branch/branch.conf) как опция:
encodng = xxx
Сохраненная кодировка будет использоваться по умолчанию при следующем запуске соответствующей команды QBzr.

Если вы постоянно работаете с одной и той же кодировкой (например, cp1251), то ее можно указать как кодировку по умолчанию для всех веток, прописав в главном конфигурационном файле bazaar.conf:
[DEFAULT]
email = Vasya Pupkin <vasya@pupkin.ru>
encoding = cp1251
Более тонкой настройки можно добиться при использовании конфигурационного файла locations.conf.

Эти настройки будут использоваться для отображения всех файлов. К сожалению, по состоянию на декабрь 2009, bzr и qbzr не имеют возможности сохранять выборочные настройки для отдельных файлов. Однако, такая функциональность может появиться в будущем.

Непосредственное управление

Благодаря Наоки Инада (Naoki INADA) в QBzr 0.17 появилась возможность управлять кодировкой файла прямо из соответствующих диалоговых окон. В окнах для отображения diff, annotate, cat появился элемент (выпадающий список), позволяющий выбрать нужную кодировку. Это очень полезное улучшение, особенно для пользователей TortoiseBzr и Bazaar Explorer, поскольку эти программы никак не передают данные о кодировках в диалоговые окна QBzr.

Diff:




Annotate:





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

Новая версия QBzr 0.17 с показанным элементом выбора кодировок будет выпущена в середине декабря 2009. Однако уже сейчас эти улучшения доступны в ветке trunk2a.

пятница, 4 декабря 2009 г.

Строгий контроль со стороны bzr

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

Ситуация

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

Собрав всю волю в кулак, вы находите подозрительные места, дорабатываете код, добавляете новый модуль, фиксируете изменения, запускаете, проверяете и ура? Нет, снова что-то не так... ах вот же она опечатка. Последние изменения, всё работает. И вы запускаете команду bzr push и передаете свою работу на сервер.

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

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

Ошибка №1: забыли добавить новые модули под контроль версий

Достаточно частая ситуация, когда фиксируются изменения в коде старых файлов и при этом забывают добавить новые файлы под контроль версий командой bzr add. В результате зафиксированное состояние дерева файлов и реальный набор файлов в рабочем каталоге будут отличаться. Что приведет к частичной или полной неработоспособности вашего кода.

В большинстве случаев подобного рода ошибки можно поймать внимательным просмотром вывода команд bzr status (до и/или после фиксации) и bzr diff. Но в пылу сражения может случиться всякое, особенно если вы фиксируете только часть изменений.

commit --strict

В таких ситуациях bzr может помочь если вы будете использовать опцию --strict для команды commit. Данная опция делает невозможным успешную фиксацию, если в рабочем каталоге присутствуют неизвестные файлы, которые не помещены под контроль версий командой add, и не соответствуют маскам игнорируемых файлов.
C:\work\bzr-day\strict>bzr ci --strict -m foo
Committing to: C:/work/bzr-day/strict/
aborting commit write group: StrictCommitFailed()
bzr: ERROR: Commit refused because there are unknown files in the working tree.
При использовании пользовательских псевдонимов вы можете включить эту опцию по умолчанию:
bzr alias commit="commit --strict"
bzr alias ci="commit --strict"

Ошибка №2: последние изменения не зафиксированы, push передает на сервер неокончательный код

С этой ситуацией более-менее понятно. Push работает на уровне зафиксированной истории ветки, поэтому состояние рабочих файлов для него проверять не обязательно.

Опять же внимательный просмотр вывода команды bzr status поможет поймать такого рода ошибки.

push --strict

К счастью, начиная с версии bzr 1.17 команда push автоматически проверяет рабочее дерево файлов на наличие незафиксированных изменений, включая неоконченное объединение. И отказывается работать, если таковые обнаружатся:
C:\work\bzr-day\strict>bzr push ../foo
bzr: ERROR: Working tree "C:/work/bzr-day/strict/" has uncommitted changes (See bzr status). Use --no-strict to force the push.
Как видно, эту проверку можно отключать, указывая опцию --no-strict.

push --no-strict

Если подобная проверка вам почему-то не по душе, то не спешите заводить новый пользовательский псевдоним для команды push, а используйте опцию push_strict в файле конфигурации bazaar.conf или branch.conf. Например:
push_strict = False

Заключение

Мы рассмотрели две встроенные функции bzr для контроля и предотвращения некоторых распространенных ошибок при выполнении операций commit и push. Как указывалось, такие ошибки можно отловить при внимательном анализе вывода команды status. Но в том-то и дело, что подобные ошибки случаются когда смотрят не внимательно или не смотрят вообще.

Дополнительные проверки, специфичные для конкретного проекта, можно осуществлять через функции-хуки, например хуки для событий pre_commit или pre_change_branch_tip. В качестве примера pre_commt хука см. плагин checkeol, который может осуществлять проверку концовок строк в фиксируемых файлах.

среда, 2 декабря 2009 г.

Статьи про bzr в блоге Kash Farooq

Kash Farooq (Каш Фарук), .NET разработчик из Великобритании, опубликовал в своем блоге ряд статей про bzr.

Затрагиваемые темы: работа с Subversion при помощи плагина bzr-svn, описания настроек внешних GUI утилит для объединения изменений и решения конфликтов, операции объединения и переименования в bzr.
__________________

Я считаю, что будет полезным делать обзоры и маленькую рекламу блогам, которые более-менее регулярно пишут про работу с bzr (чуть по более, чем заметка в стиле "я вчера поставил bzr и мне понравилось").
Если вы знаете такие блоги или сами регулярно публикуете материалы на эту тему, то оставляйте ссылки в комментариях либо присылайте электропочтой.