среда, 4 марта 2009 г.

Исправление ошибок при работе с Bazaar

Errare humanum est (человеку свойственно ошибаться) — утверждение, с которым невозможно поспорить. Мудрый подход состоит не в том, чтобы пытаться избегать ошибок, а в том, чтобы научиться их исправлять. Собственно, одно из основных предназначений любой системы контроля версий — это возможность исправления ошибок. Используя систему контроля версий вы быстрее сможете понять почему перестала работать последняя ревизия вашего приложения, изучив изменения от предыдущей ревизии, или даже можете вернутся к старой, работающей, ревизии. Кроме этого, распределенные системы, в отличие от централизованных, отделяют момент фиксации изменений от момента их отправки (в обычном режиме работы), тем самым позволяя внимательнее рассмотреть изменения на предмет ошибок. Плюс ко всему этому, Bazaar предлагает свои подходы и команды для работы над ошибками, которые мы и рассмотрим ниже в приложении к типичным ситуациям. Если вы случайно ошиблись при создании ветки Bazaar из дерева исходного кода (с помощью команды bzr init) и превратили в ветку не то дерево, самое простое, что можно сделать - это удалить появившуюся служебную директорию .bzr. Конечно, здесь нужно быть внимательным чтобы не удалить нужную служебную директорию. Другая типичная ситуация - добавление под контроль версий (с помощью команды bzr add) не того файла. Часто в рабочей директории могут находится временные файлы, журналы тестовых запусков, или другие файлы, которые не нужно хранить под контролем версий. Но в случае выполнения команды bzr add без параметров, под контроль версий попадут все неизвестные файлы (исключая файлы указанные для игнорирования). Эту ситуацию можно исправить с помощью команды bzr remove. При этом команда remove без опций не будет удалять неизвестный, или измененный файл:
$ bzr add
$ bzr remove file.tmp
bzr: ERROR: Can't safely remove modified or unknown files:
added:
    file.tmp
Use --keep to not delete them, or --force to delete them regardless.
Здесь мы можем использовать опцию --keep что бы оставить файл как неизвестный и удалить его только из под контроля версий. Или можно использовать опцию --force для полного удаления файла. Но, если файл уже был зарегистрирован и не изменялся, то команда remove удалит его без необходимости указания дополнительных опций, т.к. файл может быть восстановлен на момент предыдущей ревизии с помощью команды revert, которую мы рассмотрим ниже:
$ bzr add TODO
$ bzr commit -m "Добавлен TODO"
$ bzr remove TODO
В случае, если не использовать команды Bazaar, а просто удалить файл с помощью команды операционной системы, то файл автоматически будет удален и из под контроля версий. Теперь представим, что мы решили попробовать какой-то новый вариант в проекте, изменили несколько файлов, но попытка не удалась и хочется просто вернуться к состоянию на момент последней фиксации изменений. В этом случае мы можем использовать команду revert:
$ bzr revert
Эта команда отменит все изменения в рабочей директории с момента последней фиксации (в том числе будут восстановлены удаленные файлы). В этом случае хорошей практикой является предварительное использование команд bzr status и bzr diff, что бы быть уверенным, что при отмене изменений не пропадет что-то нужное. Команда revert также позволяет указывать имена файлов для которых нужно отменить изменения:
$ bzr revert file.txt
При этом все остальные измененные файлы останутся нетронутыми. Еще одна уникальная для Bazaar функция (по крайней мере я не видел в других системах такого простого интерфейса) для работы надо ошибками — команда uncommit. С ее помощью можно отменить последнюю фиксацию и вернуть рабочее дерево в состояние на момент до выполнения фиксации. Незаменимая вещь на случай опечатки в сообщении о фиксации:
$ bzr commit -m "Очепятка"
$ bzr uncommit
$ bzr commit -m "Опечатка"
Кроме этого, с uncommit можно использовать опцию -r для отмены сразу нескольких ревизий:
$ bzr uncommit -r -3
При этом рабочее дерево останется в том же состоянии что и до выполнения отмененных фиксаций. С командой revert также можно использовать опцию -r для возврата рабочего дерева в состояние до указанной ревизии, но ревизии при этом не удаляются:
$ bzr commit -m "Исправления"
(выпуск релиза)
$ bzr revert -r -2
$ bzr commit -m "Отменено исправление"
(выпуск следующего релиза)
Также рассмотрим исправления, связанные с метками. Если вы поставили для ревизии метку с помощью команды bzr tag, но после этого обнаружили, что ее лучше перенести на более позднюю ревизию, то можно воспользоваться опцией --force:
$ bzr tag 1.0
(еще рано для первой версии, нужно сделать несколько изменений)
$ bzr tag 1.0 --force
Метка также может быть удалена с помощью опции --delete:
$ bzr tag 1.0
(еще рано для первой версии, нам пока не нужна метка)
$ bzr tag 1.0 --delete

3 комментария:

  1. Мы тут под хрипоты порой спорим, нужна ли вообще команда типа "uncommit" для "исправление" неверного коммита в Perforce. В Perforce такой команды нет, и лично я считаю - к лучшему. Технически в Perforce это возможно. Для этого надо иметь административные права, и эта функция обычно никому недоступна. Может для opensource это интересно, потому что changelist можно сделать общедоступным, если он "чист" от опечаток, но для внутренних дел - может только в 1 проценте случаев можно разрешать человеку откатывать свой коммит без сохранения истории.

    ОтветитьУдалить
  2. Я, к сожалению, мало знаю про Perforce, но как я понимаю работа в виде распределенной системы все равно делается через сервер (http://en.wikipedia.org/wiki/Perforce#Distributed_revision_control)?

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

    Соответственно, сделать uncommit на сервере можно только если есть доступ на изменение. Возможно не очень хорошо, что когда есть доступ, то можно делать uncommit на сервере, но я думаю, что на практике это должно решаться в виде договоренности. Все таки доступа на изменение не должно быть у первых встречных. Если хочется большей уверенности, то, думаю, можно сделать на сервере (или даже на другом сервере) копию ветки которая будет прикреплена к основной и служить резервной копией (к ней не должно быть доступа снаружи). В этом случае даже если кто-то делает uncommit на основной ветке сервера резервная копия остается без изменений. Ну и, как я уже сказал, ревизии всегда можно восстановить. Грубо говоря, они удаляются только из списка ревизий, но не из хранилища.

    На практике я достаточно часто пользуюсь uncommit для исправления комментариев к ревизиям, т.к. пытаюсь набирать их быстро и ошибаюсь. :-)

    ОтветитьУдалить
  3. Александр: я думаю к теме uncommit мы еще вернемся. В распределенных системах uncommit возможен и порою нужен. И он не имеет столь деструктивные последствия как правка центрального репозитория Perforce под правами админа ;-)

    ОтветитьУдалить