Как SVN использует mergeinfo?
Я давно слышал о проблемах svn в конфликте слияний.
Я почувствовал облегчение, подумав, когда узнал, что svn пару выпусков назад реализовал функцию под названием mergeinfo
, Казалось, что его введение позволит svn иметь достаточно информации для решения проблем слияния всякий раз, когда они появляются. Пока я не попал в следующую ситуацию:
Скриптовый пример приведенного выше графика:
SVN=${SVN:-svn}
SVNADMIN=${SVNAMDIN:-svnadmin}
rm -rf repo wc
$SVNADMIN create repo
$SVN co file://$PWD/repo wc
cd wc
# r1
$SVN mkdir trunk branches
$SVN ci -m 'structure'
$SVN up
# r2
echo 2 > trunk/t.txt
$SVN add trunk/t.txt
$SVN ci -m 'add t.txt'
$SVN up
# r3
$SVN cp trunk branches/A
$SVN ci -m 'create branch A'
$SVN up
# r4
echo 4 > branches/A/a.txt
$SVN add branches/A/a.txt
$SVN ci -m 'add a.txt'
$SVN up
# r5
$SVN cp trunk branches/B
$SVN ci -m 'create branch B'
$SVN up
# r6
echo 6 > branches/B/b.txt
$SVN add branches/B/b.txt
$SVN ci -m 'add b.txt'
$SVN up
# r7
$SVN merge ^/branches/B branches/A
$SVN ci -m 'merge branch B into A'
$SVN up
# r8
echo 8 > branches/A/d.txt
$SVN add branches/A/d.txt
$SVN ci -m 'add d.txt'
$SVN up
# r9
$SVN merge ^/branches/A branches/B
Если SVN хранит историю того, откуда происходит каждая ветка, почему он не может понять, что b.txt
остался нетронутым @ branch A
?
если он не может понять это, то, что фактическое использование делает SVN mergeinfo
?
Если svn не в состоянии справиться с этой проблемой, разве не удастся создать инструмент, который поможет мне (а именно, автоматически решит этот тип простых проблем...) в этом направлении? Я думаю, может быть, один уже существует?
Спасибо
3 ответа
В Subversion есть два типа слияния:
- Трехточечные слияния
- Двухточечные слияния (слияние с реинтеграцией)
Допустим, вы работаете над транком, и у вас есть особая функция, которую нужно сделать. Вы создаете ветку Feature A. Когда вы работаете над этой функцией, вы хотите, чтобы работа, которую вы выполняете на внешней линии, была включена в функцию А, просто чтобы вы могли идти в ногу с тем, что делают все остальные. Subversion будет использовать трехточечное слияние.
Subversion рассмотрит разницу между магистралью и ответвлением Feature A с момента возникновения ответвления. Самый последний общий предок. Затем он рассматривает все изменения в компоненте А, которые были сделаны (к которым вы не хотите прикасаться), а также изменения в коде, выполненном в транке.
Subversion затем объединит изменения в стволе, не перезаписывая изменения, сделанные в ветви. Стандартная процедура слияния, и Subversion делает это довольно хорошо.
Где же svn:mergeinfo
заходи? Вы не хотите объединять дважды одинаковые изменения, поэтому Subversion отслеживает изменения с svn:mergeinfo
имущество. Если Subversion увидит, что изменения в стволе из Revision 5 уже объединены, это изменение не произойдет. Это очень хорошо с этим.
Теперь вы закончили с вашей функцией и хотите, чтобы эти изменения были объединены обратно в транк. Вы выполняете последнее объединение ствола с ветвью, фиксируете эти изменения и теперь сливаетесь из ветви Feature обратно в ствол.
Вот небольшая проблема. Мы отслеживали то, что мы слили из транка в ветку Feature через svn:mergeinfo
, Однако, поскольку мы не слились из ветви Feature в транк, нет svn:mergeinfo
там. Если мы попытаемся выполнить обычное трехточечное слияние из ветви Feature в магистраль, то предполагается, что все изменения в ветви Feature должны быть объединены обратно в магистраль. Однако многие из этих функций на самом деле являются объединенными изменениями соединительных линий.
По правде говоря, на данный момент мы хотим сделать двухточечное слияние. Мы хотим, чтобы и ствол, и ветвь Feature совпали точно после того, как мы осуществим слияние. В конце концов, мы регулярно объединяем транк в ветвь Feature. То, что мы хотим сделать, это включить эти функции обратно в транк. Таким образом, ствол будет таким же, как функция ветви.
До Subversion 1.8 вы должны были бы принудительно слить реинтеграцию, запустив svn merge --reintegration
, Теперь Subversion рассмотрит историю слияния и выяснит, когда следует выполнить слияние с реинтеграцией.
Теперь вот сложная часть. Внимательно посмотрите на номера ревизий. Это будет очень, очень важно!
- Редакция 10: я внес свои последние изменения в Trunk, и мне нужно объединить их в ветку Feature.
- Редакция 11: Я объединяю ствол с веткой Feature.
svn:mergeinfo
покажет, что весь ствол от ревизии 1 до ревизии 10 находится в ветви Feature. Поскольку последнее изменение в багажнике - Revision 10, это имеет смысл. - Редакция 12: Я объединяю Редакцию 11 ветки функций в ствол. Это слияние реинтеграции. После этого то, что находится на ветке Feature, и то, что находится в транке, должно быть полностью согласовано.
Теперь вот кикер!
- Редакция 13: я делаю еще одно изменение в багажнике.
Теперь я хочу объединить это с моей веткой Feature (создание Revision 14). Теперь, что делает svn:mergeinfo
на ветке признак говоришь? В нем говорится, что транк из Revision 1 в Revision 10 был объединен с ветвью Feature. Однако Revision 12 и Revision 13 багажника не было. Поэтому Subversion захочет объединить редакцию 12 и редакцию 13 обратно в ветку Feature.
Но подождите секунду!
Версия 12 на стволе была моим слиянием всех изменений в моей ветке Feature обратно в ствол! Таким образом, редакция 12 уже содержит все изменения редакции, которые я сделал в своей ветке Feature. Если я сливаю редакцию 12 обратно в свою ветвь Feature, я буду говорить, что все эти изменения в Revision 12 в стволе (которые действительно были изменениями, внесенными в ветку Feature и объединенными в ствол), должны быть объединены с веткой Feature. Но эти изменения также были сделаны в ветке Feature. Можете ли вы сказать, конфликт слияния? Я знал, что ты мог!
Есть два способа справиться с этим:
- Рекомендуемый способ: после того, как вы вернете свою ветвь функций обратно в транк, удалите ветку. Закрой. Никогда не используйте это снова. Не трогай! Это не так плохо, как кажется. После слияния реинтеграции ваш ствол и эта ветвь функции будут совпадать в любом случае. Удаление и воссоздание ветки из ствола не будет таким уж плохим.
- Хитрый способ, который работает: нам нужно обмануть Subversion, заставив думать, что редакция № 12 (или изменения слияния реинтеграции) уже объединена с нашей веткой Feature. Мы могли бы возиться с
svn:mergeinfo
имущество. И я использую, чтобы сделать это. Где это говоритtrunk:1-11
Я бы вручную поменял наtrunk:1-12
,
Это сложно, но слишком сложно и рискованно, потому что Subversion уже дает вам возможность манипулироватьsvn:mergeinfo
без изменения вручную.
Это называется записью только слияния.
$ svn co svn://branches/feature_a
$ cd feature_a
$ svn merge --record-only -c 12 svn://trunk
$ svn commit -m "Adding in the reintegration merge back into the feature branch."
Это меняет svn:mergeinfo
в ветви функций, не влияя на фактическое содержание файлов. Никакого реального слияния не сделано, но Subversion теперь знает, что редакция 12 транка уже находится в ветви Feature. Как только вы это сделаете, вы можете повторно использовать ветку функций.
Теперь посмотрите на диаграмму: когда вы слили ветвь B с веткой A, вы слили все изменения из B в A, и svn:mergeinfo
отследил это. Когда вы сливаете ветвь B обратно в ветвь A, у вас уже есть все изменения из ветки B в ветке A, и вы не хотите, чтобы эти изменения возвращались в ветку B. Вы должны были использовать слияние реинтеграции:
$ cd $branch_a_working_dir
$ svn merge $REPO/branches/B
$ svn commit -m "Rev 7: All of my changes on Branch B are now in A"
$ vi d.txt
$ svn add d.txt
$ svn commit -m"Rev 8: I added d.txt"
$ cd $branch_b_working_dir
$ svn merge --reintegrate svn://branch/A # Note this is a REINTEGRATION merge!
$ svn commit -m"Rev 9: I've reintegrated Branch A into Branch B
Теперь, если мы хотим продолжить использовать ветку A для дальнейших изменений:
$ cd $branch_a_working_dir
$ svn merge -c 9 --record-only $REPO/branches/b
$ svn commit -m"I've reactivated Branch A and can make further changes"
Я надеюсь, что это немного объясняет, как svn:mergeinfo
работает, почему вы должны знать, используете ли вы обычное трехточечное слияние по сравнению с двухточечным слиянием реинтеграции, и как иметь возможность реактивировать ветвь после того, как вы сделали слияние реинтеграции.
Пока вы помните об этом, слияние Subversion работает довольно хорошо.
Ответ David W.. довольно хороший, но я собираюсь дать свой собственный ответ, который ответит на это более современным подходом. То, что Дэвид говорит вам, является правдой, и полезно понимать его ответ при чтении моего ответа, поэтому вам следует прочитать его первым, если вы этого еще не сделали. Мой ответ дает более современное понимание проблемы и решения проблем, на которые указывает Дэвид.
Поэтому прежде всего простой ответ заключается в том, что ситуация, которую вы представляете, работает отлично, если вы используете Subversion 1.8. Пример сценария, который я добавил к вопросу, не конфликтует при запуске с использованием Subversion 1.8. Только клиент должен быть обновлен, чтобы получить эту функциональность.
Более длинный ответ здесь заключается в том, что с более старыми версиями Subversion вам нужно знать, когда использовать плохо названный --reintegrate
Вариант (1.8 рассчитывает это для вас). Несмотря на название --reintegrate
это не только когда вы реинтегрируете ветку обратно в ствол.
Одна из повторяющихся проблем, с которыми сталкиваются люди, заключается в том, что они хотят объединить ветвь функций обратно в свою ствол и затем продолжить использовать ветвь. Как показывает ответ Давида, в прошлом было два метода решения этой проблемы. Прежде всего удалите ветку, а затем сделайте ее свежей. Во-вторых, делайте запись только слияния. Джулиан Фоад, один из моих коллег-разработчиков Subversion, при работе над 1.8 понял, что ни один из этих двух методов не был необходим. Об этом он рассказал в своей презентации по слиянию на конференции Subversion Live 2012. Слайды для его презентации доступны онлайн здесь (обратите внимание, что это PDF всех слайдов для конференции, поэтому он не совсем маленький), часть об этих проблемах начинается на стр. 123.
Подводя итог его презентации, типичный рабочий процесс ветви ветки состоит в том, чтобы создать ветку вне магистрали. Вносите изменения в свою ветвь функций и периодически объединяйте ее из ствола в ветку вашей функции, чтобы синхронизировать ее с тем, что находится в стволе, например svn merge ^/trunk $BRANCH_WC
, Затем, когда вы готовы объединиться с магистралью, вы делаете svn merge --reintegrate ^/branches/myfeature $TRUNK_WC
, Теперь вы дошли до того, что традиционная мудрость заключалась в том, что вам пришлось удалить свою ветвь объектов или выполнить слияние только записей. Вместо этого Джулиан обнаружил, что вы можете просто продолжать использовать ветку, если будете следовать этим правилам.
Каждый раз, когда вы объединяетесь в том же направлении, что и последнее объединение между двумя ветвями, вы НЕ используете
--reintegrate
вариант. Рассматривайте создание ветви как объединение с этим местом создания.Каждый раз, когда вы меняете направления, которые вы объединяете между двумя ветками, используйте
--reintegrate
для этого слияния.
В вашем конкретном случае вы меняете направления, которые вы объединяете. r7 сливается из ветви B в ветку A. r9 сливается из ветви A в ветку B. Поэтому, когда вы собираетесь выполнить слияние r9, вам нужно использовать --reintegrate
, Эта логика - именно то, что Subversion делает для вас с 1.8.
Наконец, я не рекомендую объединяться между ветвями братьев и сестер, как вы делаете здесь. Многие простые случаи, например, то, что вы здесь делаете, будут работать хорошо. Однако, если вы, например, разбить файл (svn cp file file2
, удалите часть file1, а другую часть из file2), после чего вы столкнетесь с проблемами при попытке объединить последнюю из двух функциональных ветвей обратно в транк (при условии, что вы слили разделение с обеими ветвями). Лучше всего ограничить слияние между двумя ветвями (дочерней и родительской). Вы можете создавать ветви из других веток и сливаться с их родителями, прежде чем переходить к родителям родителей и так далее, и все будет в порядке. Мы бы хотели, чтобы в будущем даже такие вещи работали должным образом, но это ситуация, которая сейчас стоит в версии 1.8 (намного лучше, чем в прошлом, но не так хорошо, как хотелось бы).
Вы неправильно поняли значение и использование mergeinfo - он содержит только уже (для этого узла) слитые ревизии и источники слияния, чтобы не слить ревизию дважды, ничего о контенте