Некоторое время назад при старте нового проекта было решено попробовать использовать Git вместо Subversion. Через некоторое время коллектив разделился на тех, кто любит Git (программисты), и тех, кто его ненавидит (дизайнеры и художники). Эксперимент по замене Subversion на Git провалился и на горизонте замаячила перспектива возвращения Subversion.
Почесав репу и содрогнувшись от связанных с Subversion воспоминаний мужики решили: «А что, мы же программисты!» и запилили свой Subversion с Git-ом и печеньками. Так родился проект git-as-svn.
Теперь мы можем использовать и Git, и Subversion с одним и тем же репозиторием. Причем доступ через Subversion напрямую использует данные Git-репозитория, в отличие, скажем, от SubGit, где для Subversion используется отдельный репозиторий.
Зачем понадобился еще один велосипед?
Справедливости ради надо отметить, что оба лагеря были по-своему правы. В пользу Git можно сказать следующее:
- У Git-а можно делать по ветке на задачу и спокойно коммитить промежуточные результаты без риска все разломать.
- Git, по сравнению с Subversion, нереально быстрый.
Против Git:
- Более высокий порог вхождения:
- Git объективно сложнее, чем Subversion;
- TortoiseGit далеко не такой удобный как TortoiseSvn;
- многие вещи надо делать через консоль;
- много больше способов отстрелить себе ногу.
- У Git не очень хорошо с клиентами под Windows.
- Для работы в Subversion-стиле (обновил → поработал → залил → поработал → залил...) нужно много больше телодвижений.
- Нет разделения прав внутри репозитория.
- При большом количестве постоянно заливающего народу, люди начинают биться головами (между 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-сервер.
Как работает коммит?
Одна из самых важных деталей системы — сохранение изменений. В общих чертах, идея следующая:
- В момент команды svn commit клиент отправляет на сервер свои изменения. Сервер при этом запоминает их. В этот же момент происходит первая проверка на актуальность клиентских данных.
- Сервер берет голову ветки и начинает формировать новый коммит на базе полученных от клиента данных. В этот момент происходит ещё одна проверка на актуальность клиентских данных.
- Проверяется целостность svn properties для заливаемых данных.
- Сервер пытается консольным Git-клиентом сделать push нового коммита в текущую ветку этого же репозитория. Далее по результату push-а:
— если все хорошо — загружаем последние изменения из git-коммитов и радуемся;
— если не fast forward — загружаем последние изменения из git-коммитов и идём к шагу 2;
— если отбили хуки — сообщаем клиенту;
— если другая ошибка — сообщаем клиенту.
Таким образом, за счёт использования в данной операции консольного Git-а мы избегает гонки с заливкой напрямую через Git, и получаем хуки в качестве приятного бонуса.
Где хранятся svn-данные репозитория?
Для представления Subversion репозитория нужен ряд данных, которые в Git либо отсутствуют (например, данные о том, откуда скопирован файл), либо очень дорого получить (например, номер ревизии, когда файл менялся в последний раз). Чтобы не заниматься их расчетом каждый запуск, они кэшируются в ветках refs/git-as-svn/*. Этот же кэш позволяет не ломаться соответствии Git-коммитов Subversion-ревизиям из-за операций force push. ;-)
Известные проблемы и ограничения
На данный момент приходится мириться со следующими ограничениями:
- нельзя средствами Subversion менять svn properties;
- не сохраняется история копирования файлов.
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 нужно:
- Установить Java 8.
- Скачать последнюю сборку с github.com/bozaro/git-as-svn/releases/latest.
- Распаковать архив.
- Запустить сервер.
java -jar git-as-svn.jar --config config.example --show-config
В результате будет создан Git репозиторий с одним тестовым коммитом, доступный по URL svn://localhost/example/ из под пользователя test с паролем test. В качестве клиента настоятельно рекомендуется использовать клиент для Subversion 1.8+.
Автор: Bozaro