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

Версия от 19:05, 18 ноября 2009; VitaliyFilippov (обсуждение | вклад)

Данная статья является очередным сравнением популярных DVCS — Mercurial, Git и Bazaar, с точки зрения нескольких нетривиальных задач.

Do you want to try some new features? By joining the beta, you will get access to experimental features, at the risk of encountering bugs and issues.

Ок Нет, спасибо

Работа с SVN (миграция и синхронизация)

Mercurial: отлично! Есть несколько расширений — hgsubversion, hgsvn, convert, позволяющих работать с Subversion тем или иным образом, и не совместимых друг с другом. Самое вменяемое из них — hgsubversion, хотя и заявлено, что оно ещё сырое, и, к сожалению, не распространяется вместе с Mercurial’ом. Имеет фактически весь необходимый функционал — можно делать и push, и pull в/из Subversion, можно клонировать SVN-репозиторий с сохранением веток и меток (правда, обязательно стандартное их расположение в корневых поддиректориях /trunk, /branches, /tags), эти два метода совместимы, rebase также работает, а граф ветвлений сохраняется. Очень крут тот факт, что ветка, которая создавалась неполным копированием trunk'а, то есть, копированием некоторых его поддиректорий, успешно подцепилась в нужное место графа ветвлений. Ни Bazaar, ни Git этого не смогли.

Остальные два экстенжна «нинужны»: hgsvn — нечто более старое, работает сбоку от общего механизма, тоже позволяет делать push и pull, но не клонирует весь репозиторий, а только извлекает (checkout’ит) последнюю версию, чтобы далее можно было использовать Subversion и Mercurial вместе. Ну и конечно, оно не совместимо с hgsubversion. convert же предназначен для конвертации истории проекта из нескольких различных систем контроля версий в Mercurial, ни черта не совместим ни с hgsvn, ни с hgsubversion и не сохраняет граф ветвлений. Зато, правда, поддерживает возможность использования других имён поддиректорий trunk/branches/tags.

Git: очень хорошо (5-)! git-svn встроен в git и умеет всё, что нужно: клонирование, синхронизация (fetch, pull или merge), фиксация изменений в Subversion-репозиториях (dcommit) и rebase работают с сохранением веток и меток, причём стандартная схема их именования trunk/branches/tags не является обязательной. Ветки SVN импортируются как Remote Tracking Branches, что тоже удобно — можно начинать историю ветки в git’е не с сотворения миров, а с любого момента. Также git-svn поддерживает подключаемые внешние репозитории Subversion (т. н. Externals). Граф ветвлений сохраняется, хоть и не так круто, как в Mercurial’е. В общем, функционал фактически полон.

Bazaar: отлично! Можно импортировать репозиторий командами svn-import или branch, можно делать push и pull в/из Subversion. При импорте можно сохранить все ветки и метки в одном хранилище (если использовать Shared Repository), для этого также требуются стандартные названия trunk/branches/tags. Граф ветвлений Subversion сохраняется. Также существует несколько других расширений для импорта Subversion в Bazaar, но они хуже.

Была любопытная проблема в работе с SVN. Рассмотрим следующую последовательность действий:

  1. Клонируем svn в ветку bzr1.
  2. Клонируем bzr1 в bzr2.
  3. Вносим изменения в bzr2, коммитим.
  4. Вносим другие изменения в svn, коммитим. Лучше несколько раз (создаём несколько ревизий).
  5. Делаем pull из svn в ветку bzr1 — bzr1 снова синхронизирован с svn.
  6. Делаем push из bzr2 в bzr1 — облом: ветки «разошлись». Хорошо, делаем merge из bzr1 в bzr2. Коммитим (фиксируем).
  7. (Необязательно, но интереснее) Снова вносим изменения в bzr2, коммитим.
  8. Теперь хотим протащить изменения из bzr2 в svn. Сначала делаем push из bzr2 в bzr1. Теперь история bzr1 идентична истории bzr2.
    И это не так тривиально, как хотелось бы!
    Потому что теперь импортированные из svn в bzr1 ревизии заменяются одной merge-ревизией, а после неё в истории появляется ревизия с модификацией, импортированная из bzr2. Исходные svn-ревизии «подцепляются» к merge-ревизии. Чтобы увидеть их, нужно сказать не просто bzr log, а bzr log -n0.
  9. Делаем push из bzr1 в svn — облом: в bzr1 есть ревизия, «воткнутая» между уже зафиксированными в репозитории svn-ревизиями. И что теперь делать?
    bzr: ERROR: Operation denied because it would change the mainline history. Set the append_revisions_only setting to False on branch "..." to allow the mainline to change.
    Bazaar предлагает нам либо разрешить менять местами SVN-ревизии, а если разрешить — всё равно обламывается и предлагает использовать rebase, возможно, из-за пункта 7. rebase не помогает — говорит «no revisions to rebase».
  10. Чтобы исправить эту ситуацию, клонируем svn в ветку bzrtmp.
  11. Делаем merge из bzr1 в bzrtmp, коммитим. Теперь в bzrtmp последней ревизией будет merge-ревизия, к которой «подцеплены» ревизии, которые мы так жаждем протащить-таки в SVN.
  12. Теперь мы можем сделать push из bzrtmp в svn, а потом из svn — pull во все остальные ветки, и они придут к согласованному виду…

Что ещё любопытно — с Subversion-репозиториями DVCS работают, как правило, гораздо быстрее самого Subversion, и быстрее всех работает Bazaar. То есть, в принципе, можно вообще жить с Subversion-сервером и Bazaar-клиентом.

Управление патчами

Первое, что приходит на ум — это, конечно, аналоги quilt'а, работающие поверх DVCS: Mercurial Queues, Bazaar Loom, StGIT. Все они очень похожи и друг на друга, и на сам quilt.

quilt вещь банальная, позволяющая автоматизировать тупое накатывание последовательности большого числа патчей и правку патча, который находится где-то в середине: можно откатить N верхних патчей, внести изменения в файлы и сказать refresh, и верхний на данный момент патч (то есть патч откуда-то из середины) обновится, дабы соответствовать внесённым изменениям. quilt создан на основе скриптов человека, который не использует системы контроля версий — Эндрю Мортона (Andrew Morton) — второго по значимости участника разработки ядра Linux после Линуса Торвальдса.

Из реализаций quilt поверх DVCS появился именно MQ и долгое время, судя по всему, был «изюминкой» Mercurial’а, хотя на самом деле неидеален — идея добавления и удаления истории из/в репозиторий всё-таки странновата, а патчи не хранятся в том же репозитории, что и код — то есть, при клонировании исчезают.

Крут ли quilt, нет ли — каждый решает сам для себя. Конечно, поддержке Debian-патчей на какой-нибудь Midnight Commander (их там в районе 60-и) quilt … помогает. Тем не менее, с моей точки зрения, некорректно закладываться на жёстко последовательное применение всех патчей. На самом деле, такие патчи логично организовывать в виде графа (графа зависимостей). Сразу будет видно, что большинство патчей независимы друг от друга, а зависимости окажутся на виду.

Таким образом, задачу управления патчами удобнее всего решать, заводя по отдельной ветке на каждый патч. Причём даже есть расширения Mercurial pbranch (от patch branches) и Git TopGit (от topic branches). Для Bazaar’а, увы, таких расширений нет.

Схема управления рабочими копиями

Что нам предлагают централизованные системы контроля версий? Некий «хардкод»: репозиторий ровно один, рабочих копий много. Что нам предлагают DVCS? Теоретически, полный беспредел, то есть свободу.

На практике — не совсем полную.

Mercurial говорит нам: на ровно 1 рабочую копию ровно 1 репозиторий, и иначе быть не может. Это и удобно — сказал hg up ветка_такая_то, пара файлов поменялась, и опа — ты уже в другой ветке. Это и неудобно — чтобы положить на диск одновременно две разных ветки, нужно обязательно клонировать репозиторий (поддержки checkout нет).

С Git'ом ситуация почти такая же, как и с Mercurial’ом. 1 рабочая копия, 1 репозиторий. Хотя при клонировании данные репозитория можно и не копировать, задавая опцию --shared, но это скорее похоже на Bazaar’овские Stacked Branches, чем на Lightweight Checkout. Идея лёгких рабочих копий (или «идея .gitlink») высказана для GSoC-2007, однако пока так и не реализована.

Bazaar вначале был хуже всех, а потом стал лучше всех. Вначале он говорил нам: на 1 ветку ровно 1 рабочая копия и ровно 1 репозиторий. А когда исправился, стало можно создать Shared Repository с опцией --no-trees, а потом сколько захочется рабочих копий checkout'ами. Переключить рабочую копию с ветки на ветку также можно — командами switch или qswitch.