Честно признаюсь, что долго не понимал, о чем идет речь, когда Subversion ругают за проблемы со слияниями бранчей. До сегодняшнего дня, когда мы на работе огребли довольно объемную проблемку. Хочу поделиться подробностями.
Задачка
Есть у нас средних размеров джанговский проект — "Куда Все Идут". В нем изначально, из-за нежелания плодить без надобности сущности, было одно глобальное приложение под неоригинальным названием "core". Недавно из-за разрастающихся планируемых фич нам захотелось разделить код на три приложения. Два со специфичной логикой — "social" и "content" — и одно с общей для всего проекта логикой — все тот же "core". Причем прямо сейчас часть "content" будет практически пустой, и по сути весь процесс заключается в отпочковывании части "social" от части "core".
При это мы находимся в такой ситуации, когда заморозить текущую разработку на месяц-другой нельзя, и поэтому мы решили сделать это разделение в отдельном бранче.
Бранчи
У нас получается, в итоге, два бранча:
- trunk, в котором идет вполне себе активная разработка очередной версии сервиса
- бранч, провидчески названный "magicsplit", в котором идет разделение: по сути большой рефакторинг с кучей переносов кода, переименований переменных и починкой тестов
Бранчи, безусловно, обязаны сильно разойтись, но ведь на то нам и нужна система контроля версий, чтобы это разруливать.
Слияние
И вот сегодня под вечер, когда magicsplit достиг своего работоспособного состояния, мы решили его влить в транк.
offline/trunk$ svn merge -r1489:HEAD https://.../magicsplit/ .
И накнулись на интересные, но от того не менее больно бьющие по лбу грабли.
Практически первое, что было сделано в magicsplit — это рядом с "core" была создана папка для нового приложения ("social"), в которую тут же был унесен практически весь код, который к этому приложению по логике относился. В частности, некоторые модели данных, практически все view'хи и шаблонные теги переехали в эту новую директорию. Дальше там происходила практически только отладка. А в это время в транке этот же самый код активно правился. Но в старых файлах внутри core.
Так вот, засада заключается в том, что Subversion, хоть и номинально знает, что, например, social/views.py переехал туда из core/views.py, никак эту информацию при слиянии не использует. И поэтому у нас слияние бранча оказалось практически пустым: изменения из транка влились в оставшуюся core/* и никак не слились с бранчевыми изменениями в social/*.
Варианты
Теперь, вот, не знаем, что делать. Варианты пока такие:
- импортировать всю историю до слияния из Subversion в Bazaar, и надеяться, что он полнее поймет смысл операции "переименовать файл"
- вручную заставить Subversion влить изменения из trunk/core/* в magicsplit/social/* (что, на самом деле, сложно, потому что структура не такая упрощенная, как я рассказываю)
- делать слияние вручную за месяц-другой %-)
Что посоветует уважаемое сообщество?
Комментарии: 40
Базаар - весьма глючная и тормозная хрень. Бери git, он специально для такого и создавался (трекает контент вцелом, а не пофайлово, т.е. знает что файл переехал в другую папку и сумеет примерджить изменения).
Или Hg/mercurial (который, вроде, слизан с гита, но, говорят, медленнее и сложнее в использовании).
Олег, у тебя наверное данные примерно годовалой давности. Он сильно изменился с того времени. Кроме того, мне нужна от него вот эта одна конкретная вещь.
Думал про это. Пока меня пугает то, что его надо долго изучать, чтобы начать пользоваться. А нам, по сути, это надо завтра :-). Кроме того, слышал, что ммпортом из svn у него какой-то убогий. Или нет?
Оба утверждения убивают наповал.
Mercurial, как и Bazaar, написан на питоне, поэтому нет ничего странного что git быстрее примерно в два раза. Но велика ли разница длится коммит 1 секунду или 2-е? :)
У меркуриала мне нравится что ссылка на репозитарий и веб-морду одна.
Я бы попробовал на скорую руку посмотреть на результат применения bzr.
Насчет быстродействия: если я правильно помню, то ставка у bzr была именно на юзабилити (и таки да, использовать его из консоли оказалось приятнее чем тот же svn), а потом догонялась скорость (начиная с версии 0.9 она заметно выросла; текущая - 1.6). Но скорость операций для проектов (в моем случае - на rails) меня устраивает более чем.
Насчет скорости - сравнение.
Попробуй git-svn -ом выгрузить весь свой репозитарий и сделать слияние уже гитом. git должен корректно разрулить все перемещения файлов и смердживание бранчей даст то, что нужно.
На всякий случай уточню: git от svn отличается помимо прочего еще и тем, что работает не с "рабочей папкой", а с локальной версией репозитария, т.е. вся история изменений доступна локально без сервера. А git-svn умеет выгружать и загружать все это с/на сервер svn.
Попробуйте у народа с reactos.org спросить, они периодически с бранчами балуются.
В два раза - это вы сами придумали? Тесты говорят совершенно о другом.
Получается, в рамках subversion нет другого способа, кроме как вручную перенести все изменения. Конечно, не совсем вручную ...
Если бы меня заставили заниматься такими вещами, то я бы сделал примерно так:
Перенес все изменения, сделанные в trunk-е, в ветвь. Упростить себе жизнь можно, воспользовавшись svn merge для каждого файла или каталога в отдельности, для чего перейти в каталог branches/magicsplit/social рабочей копии и выполнить в ней svn merge svn://.../trunk/core@START svn://.../trunk/core@END. Это сделает diff для изменении и применит его к текущему каталогу.
Перенес все изменения (вместе с изменениями, сделаннами в ветви) обратно в trunk. Перейдя в trunk рабочей копии, выполнить
svn merge svn://.../branches/magicsplit@START svn://.../branches/magicsplit@HEAD. Здесь START - это номер ревизии, начиная с которой произошло отпочкование.
Я совсем не уверен, что этот способ будет наилучшим или вообще подходящим для вашего случая.
А можно попробовать svn 1.5 — там сильно починили merge-tracking и branch-merging.
Вообще, перемещения файлов должны учитываться в svn при мерже. По крайней мере, у меня учитывались.
Попробуйте поменять trunk так, чтобы по структуре файлов эта ветка максимально походила на новую. Убедитесь, что работает (а лучше зарелизьтесь), а после можно и смержиться в полуавтоматическом режиме.
Могу попробовать сделать это через GIT. Поставил выкачиваться репозиторий :)
Mercurial, на мой взгляд, с задачей вполне справляется.
Я как-то делал слияние веток в SVN под Windows с помощью TortoiseSVN. Там неплохой мануал как это лучше делать: http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-dug-merge.html
Там два способа в зависимости от особенностей организации репозитария (портируются ли изменения транка в бранч). Оба основаны на получении патча с изменениями бранча, применения его к коду транка локально, фикса конфликтов и тестирования, а потом коммита в транк. В TortioseSVN это делается легко, как описано по ссылке. И т.к. пути можно указывать произвольные при создании дифа и при применении патча к локальной копии транка, то можно разбить задачу на части — для каждой перемещенной папки или файла, если их путь поменялся.
Я могу ошибаться, но мне кажется автоматическое отслеживание перемещений директорий и файлов при слиянии веток — это задача программиста а не системы контроля версий и автоматизировать ее нормально невозможно и не нужно. Иначе система теряет примитивность и предсказуемость.
Это смысл любого слияния, как такового. Проблема этого случая в том, что слияния по сути не происходит.
Я бы не согласился с однозначностью вывода. Если система контроля версий может это сделать надежно и предсказуемо, то пусть делает. Никакой особенной rocket science я тут, признаться, не чувствую. Это просто вопрос аккуратного программирования логики.
Собственно, как написал выше ods, Меркуриал с этим справляется, по крайней мере в простых случаях.
Про способ Романа... Я так понимаю, если изменения транка портированы в бранч (один из способов по ссылке), то потом надо сделать дифф между текущим транком и текущим бранчем и применить его к транку, но не диф между точкой отпочкования и текущим бранчем т.к. он будет содержать уже содержащиеся в транке изменения.
На части разбивать, с учетом переноса файлов.
Да, уже почитал, интересно работает ли это в реальной жизни у кого-нибудь.
Предыдущий коммент про способ Романа, это я не въехал, наверно предлагается полностью заменить транк бранчем (если перенести рефакторинг из бранча в транк сложнее, чем перенести новые фичи из транка в бранч). Но тогда второй шаг все равно странно выглядит.
Посмотрите svnmerge.py (http://www.orcaware.com/svn/wiki/Svnmerge.py)).
Или сразу svn 1.5 (у которого сейчас RC8) - там со слиянием намного лучше (уж перемещаемые файлы в бранчах он точно отслеживает), но почти все тоже самое можно делать и с svnmerge.py в 1.4.
Спасибо за замечания. Действительно, предлагалось именно перенести все изменения в бранч, а потом просто заменить trunk. И действительно, второй шаг делает совсем не то, что нужно. Вместо этого ...
или сказать
Мне в свое время, чтобы не попадать в такие ситуации, очень помог совет в конце http://svnbook.red-bean.com/en/1.1/ch04s04.html#svn-ch-4-sect-4.4, когда бранч какой-то фичи долго разрабатывается, то установить в команде период а лучше день, скажем раз в неделю, когда все изменения транка портируются в бранчи. Тогда по завершению жизни бранча вообще нет проблем, сразу шаг 2.
Я боюсь, что нам бы это не помогло. Проблему вызывает не то, что бранч долго разрабатывается (сами изменения кода там как раз не конфликтуют почти), а то, что в бранче основной код переехал в другие файлы. Поэтому как только это переименование случилось — все, никакие изменения из транка автоматически в бранч попадать не будут.
А если рассуждать о том, как надо было, то надо было сделать максимум изменений без двигания структуры файлов, потому на день прекратить разработку в транке, сделать все переносы и тут же слить.
А тут автоматически и не получится в общем случае. Чтобы начать портировать коммит из транка, можно его выгрузить как контекстный дифф для каждого измененного файла, а потом применить полученные патчи к файлам бранча, в которые соответствующий код переехал. Не совсем автоматически, но и не в ручную.
Угу. Вот похоже (после сегодняшних проб), так и придется сделать. Я еще напишу результаты отдельным постом.
Разве не проще было сделать SVN ->