воскресенье, 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 — это номер ревизии, предшествующей удалению файла.

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

  1. Проблему 1 решить еще проще. Точнее, ее и возникать не должно.
    Зачем один и тот же файл _добавлять_ в разных ветках?
    Если в одном проекте несколько веток, их создают командой branch.

    ОтветитьУдалить
  2. В теории проблемы возникать не должно. На практике такое все-таки возникает. Самый простой пример: начали проект без файла .bzrignore, создали новую ветку, от нее несколько человек ответвилось, пишет код. И вот каждый независимо от других создает файлик .bzrignore потому что нужда заставила. При последующем merge будут нюансы.

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