geektimes

Git as Subversion

  • среда, 22 октября 2014 г. в 03:10:53
http://habrahabr.ru/company/mailru/blog/241095/

Некоторое время назад при старте нового проекта было решено попробовать использовать Git вместо Subversion. Через некоторое время коллектив разделился на тех, кто любит Git (программисты), и тех, кто его ненавидит (дизайнеры и художники). Эксперимент по замене Subversion на Git провалился и на горизонте замаячила перспектива возвращения Subversion.

Почесав репу и содрогнувшись от связанных с Subversion воспоминаний мужики решили: «А что, мы же программисты!» и запилили свой Subversion с Git-ом и печеньками. Так родился проект git-as-svn.

Теперь мы можем использовать и Git, и Subversion с одним и тем же репозиторием. Причем доступ через Subversion напрямую использует данные Git-репозитория, в отличие, скажем, от SubGit, где для Subversion используется отдельный репозиторий.

Зачем понадобился еще один велосипед?

Справедливости ради надо отметить, что оба лагеря были по-своему правы. В пользу Git можно сказать следующее:
  1. У Git-а можно делать по ветке на задачу и спокойно коммитить промежуточные результаты без риска все разломать.
  2. Git, по сравнению с Subversion, нереально быстрый.

Против Git:
  1. Более высокий порог вхождения:
    • Git объективно сложнее, чем Subversion;
    • TortoiseGit далеко не такой удобный как TortoiseSvn;
    • многие вещи надо делать через консоль;
    • много больше способов отстрелить себе ногу.
  2. У Git не очень хорошо с клиентами под Windows.
  3. Для работы в Subversion-стиле (обновил → поработал → залил → поработал → залил...) нужно много больше телодвижений.
  4. Нет разделения прав внутри репозитория.
  5. При большом количестве постоянно заливающего народу, люди начинают биться головами (между pull и push успевает кто-то вклиниться).

И самое главное: Git не даёт никаких преимуществ и создаёт дополнительные проблемы, если файлы не поддаются мержу.

Когда стало ясно, что эксперимент по замене Subversion на Git провалился, начали поиск решения из сложившейся ситуации. В процессе поиска были обнаружены:
  • Поддержка Subversion на GitHub.
    Можно приобрести в комплекте с GitHub Enterprise за сотни нефти.
  • Стремный проект SubGit.
    SubGit пытается через хуки поддерживать в синхронном состоянии Git и Subversion репозитории. Это архитектурное решение выглядит очень шатким, хотя, надо отметить, что реализация кажется рабочей.
  • Люто тормозящая python-реализация Subversion-фронтэнда для Git-репозитория.
    Самое главное в этом проекте — его небольшой объем. После этого задача реализации Subversion-фронтенда для Git-репозитория стала казаться просто безумной, а не абсолютно безумной, как было ранее. :)

Что это и как оно работает

Данный проект представляет собой реализацию Subversion-фронтэнда для Git-репозитория. Он позволяет работать с репозиторием при помощи, как минимум:
  • консольного Subversion-клиента;
  • TortoiseSVN;
  • SvnKit.

На данный момент поддерживается:
  • svn checkout, update, switch, diff
  • svn commit (!)
  • svn log
  • svn cat, ls
  • svn replay (svnsync)
  • partial checkout
  • sparse working copy (svn --depth/--set-depth)
  • git submodules
  • аутентификация через LDAP

По производительности работа не просто сопоставима, но местами даже превосходит родной Subversion-сервер.

Как работает коммит?


Одна из самых важных деталей системы — сохранение изменений. В общих чертах, идея следующая:
  1. В момент команды svn commit клиент отправляет на сервер свои изменения. Сервер при этом запоминает их. В этот же момент происходит первая проверка на актуальность клиентских данных.
  2. Сервер берет голову ветки и начинает формировать новый коммит на базе полученных от клиента данных. В этот момент происходит ещё одна проверка на актуальность клиентских данных.
  3. Проверяется целостность svn properties для заливаемых данных.
  4. Сервер пытается консольным Git-клиентом сделать push нового коммита в текущую ветку этого же репозитория. Далее по результату push-а:
    — если все хорошо — загружаем последние изменения из git-коммитов и радуемся;
    — если не fast forward — загружаем последние изменения из git-коммитов и идём к шагу 2;
    — если отбили хуки — сообщаем клиенту;
    — если другая ошибка — сообщаем клиенту.

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

Где хранятся svn-данные репозитория?


Для представления Subversion репозитория нужен ряд данных, которые в Git либо отсутствуют (например, данные о том, откуда скопирован файл), либо очень дорого получить (например, номер ревизии, когда файл менялся в последний раз). Чтобы не заниматься их расчетом каждый запуск, они кэшируются в ветках refs/git-as-svn/*. Этот же кэш позволяет не ломаться соответствии Git-коммитов Subversion-ревизиям из-за операций force push. ;-)

Известные проблемы и ограничения

На данный момент приходится мириться со следующими ограничениями:
  1. нельзя средствами Subversion менять svn properties;
  2. не сохраняется история копирования файлов.

Svn properties


Основная беда svn properties в том, что некоторые свойства надо поддерживать в синхронном состоянии между Git и Subversion. Чтобы эти данные не расходились, svn properties генерируются на базе содержимого файлов из Git-репозитория (например, svn:ignore генерируется на основании .gitignore). При коммите же происходит проверка сохраняемых свойств данным репозитория. Это накладывает важное ограничение: нужно корректно формировать svn:auto-props, иначе при добавлении файлов пользователю придётся приводить их руками к виду, который ожидает сервер.

Самое злобное свойство: svn:eol-style. Основная беда в том, что поведение Git-а по умолчанию в контексте eol-ов сломано и соответствует файлу .gitattributes с содержимым:
* text=auto eol=native 

То есть, с настройками по-умолчанию Git меняет содержимое текстовых файлов.

После долгих страданий было найдено решение проблемы eol-ов: в корень Git-репозитория нужно добавить файл .gitattributes, начинающийся со строки:
* -text

Это потребует от Git не трогать окончания строк у файлов до тех пор, пока его об этом явно не попросят.

И что в итоге?

На данный момент проект находится в неторопливой разработке и эксплуатируется в одной из команд Mail.Ru Group. В результате, люди вольны использовать подходящий для их нужд инструмент: дизайнеры используют TortoiseSvn и заливают им изменения напрямую в master, а программисты пользуются Git-ом и живут в своих уютненьких веточках. Обстановка в целом стала здоровее, и у коллег перестали сжимать кулаки, когда кто-то рядом произносит слова Git или Subversion.

Что нужно сделать, чтобы потыкать в проект палочкой?

Чтобы запустить git-as-svn нужно:
  1. Установить Java 8.
  2. Скачать последнюю сборку с github.com/bozaro/git-as-svn/releases/latest.
  3. Распаковать архив.
  4. Запустить сервер.
    java -jar git-as-svn.jar --config config.example --show-config

В результате будет создан Git репозиторий с одним тестовым коммитом, доступный по URL svn://localhost/example/ из под пользователя test с паролем test. В качестве клиента настоятельно рекомендуется использовать клиент для Subversion 1.8+.