Рефакторинг и параллельное развитие веток - PullRequest
28 голосов
/ 28 октября 2008

Допустим, у вас есть несколько веток обслуживания для существующих выпусков вашего программного обеспечения. Некоторые разработчики вносят прямые изменения в ветки обслуживания и периодически объединяются в магистраль. Теперь идет обширный рефакторинг в коде магистрали, запланированный на предстоящий основной выпуск. Но это делает ветки обслуживания принципиально несовместимыми с кодом в транке, так как они могут зависеть, например, от кода, который больше не существует.

Как вы справляетесь с этой ситуацией на практике?

Ответы [ 14 ]

22 голосов
/ 21 февраля 2009

Я бы посчитал обязанностью разработчика по обслуживанию филиалов объединить соответствующие изменения с текущим состоянием магистрали. Есть несколько возможностей:

  1. Код в транке не изменился, и патч применяется без конфликтов.
  2. Код в транке изменился, и патч применяется, но требуется ручное слияние.
  3. Код в транке полностью изменился, и патч не может быть применен. Разработчик должен оценить, существует ли такой же дефект в соединительной линии, и при необходимости применить эквивалентное исправление.

Варианты 1 и 2 - это обычные пути разработки обслуживания. Случай 3 - это рассматриваемый вами случай, когда транковый код не может принимать исправление обслуживания в любой форме. Если разработчик сам не может определить, существует ли такая же проблема в соединительной линии, он должен ввести проблему в систему отслеживания ошибок. Эта проблема может побудить разработчиков соединительных линий рассмотреть причину исправления в ветке обслуживания и определить, существует ли такой же дефект. Ввод новой проблемы для возможного дефекта в соединительной линии должен быть последним средством для разработчика обслуживания.

Одним из преимуществ того, что разработчики ПО пытаются применить исправления к обновленной соединительной линии, является повышение их знакомства с новой базой кода. В конце концов, у них закончатся работы по техническому обслуживанию, и им нужно будет работать с новым стволом. По крайней мере, базовый уровень знакомства будет очень полезен.

3 голосов
/ 22 февраля 2009

Это, в конечном счете, вопрос о командном общении, а не простой вопрос ветвления / объединения.

Первым шагом, как и во всех подобных случаях, является понимание того, что у вас есть проблема. Это то, что вы сделали.

Затем вам нужно предупредить всю команду о проблеме.

Как только вы это сделаете, я думаю, что есть два разных пути:

  1. Если ветки обслуживания используются нечасто, скажем, выпущенный код является достаточно зрелым и не содержит ошибок, вы можете решить заморозку кода. Каждый разработчик должен закончить то, над чем он / она работает, к 32 октября и объединить эти изменения обратно в магистраль. Ветви должны быть либо закрыты, либо заморожены. Затем работа в магистрали может продолжиться, и новое программное обеспечение может быть выпущено.

  2. Если в филиалах происходят частые или срочные изменения и исправления, эта проблема усложняется. Все еще нужно заморозить код, иначе магистраль будет засорена несколько раз. Но здесь разработчикам все еще нужно что-то исправлять и передавать их клиентам. Я предлагаю, чтобы каждое изменение в ветвях после замораживания кода соединительной линии регистрировалось в базе данных отслеживания ошибок (обязательно в каждой ситуации) со специальным указанием того, что это было исправлено в ветви N, но еще не объединено со стволом. Это требует тщательной регистрации, чтобы помнить каждую важную деталь.

    После того, как ствол подвергся рефакторингу, но до того, как он очищен, помещен в буфер, помечен и выпущен, просмотрите базу данных об ошибках, особенно элементы, исправленные в ветвях, но не ствол. Они все еще актуальны? Сейчас самое время изменить код еще раз, если это необходимо. Это может означать двойную работу на короткое время, но, надеюсь, код теперь гораздо более удобен в обслуживании.

    Как только все известные проблемы устранены, новая версия может быть выпущена, а старые ветви могут быть закрыты.

2 голосов
/ 27 февраля 2009

Учитывая, что большая часть затрат на исправление ошибки заключается в воспроизведении проблемы и тестировании исправления. Можете ли вы написать автоматический тест, который будет работать во всех ветвях, даже если исправление кода должно быть сделано по-разному для каждой ветви?

2 голосов
/ 22 февраля 2009

На практике вам, возможно, придется проделать дополнительную работу, чтобы сделать новые изменения обратно совместимыми.

  • Шаг 1: Начать рефакторинг компонента. С каждым шагом сохраняйте старый интерфейс, но он переносит вызовы в новую реализацию. Обратите внимание, что это можно сделать в несколько этапов по мере создания нового интерфейса / API. Модульные тесты должны быть в состоянии проверить, что миграция со старого на новый работает правильно, но этот шаг, скорее всего, все еще повлечет за собой издержки тестирования / QA.

  • Шаг 2: Новая версия запущена в производство; убедитесь, что все знают об этом. На этом этапе новые функции не добавляются в старую версию, и все новые (или измененные) абоненты используют новую версию.

  • Шаг 3: Найдите все (используйте инструменты, чтобы сделать это), который вызывает старый интерфейс, и измените все, чтобы вызвать новый интерфейс. Это, вероятно, влечет за собой много затрат на тестирование / контроль качества. Каждый вызывающий может быть зафиксирован / освобожден по одному за раз.

  • Шаг 4. На данный момент новая версия активна, и не осталось абонентов, которые получают доступ к старой версии. Удалите его безопасно.

Обратите внимание: если API общедоступен, а вы не контролируете людей, которые его называют (например, такие компании, как Microsoft), вы никогда не сможете пройти шаг № 2.

Этот процесс может быть медленным, и он требует много дисциплины, общения и тестирования. Но в тех случаях, когда альтернатива играет в догонялки / интеграцию навсегда, это может быть разумным вариантом.

2 голосов
/ 21 февраля 2009

Единственный ответ, который мне удалось придумать, - это создать ветвь для обслуживания непосредственно перед началом рефакторинга. Вы поддерживаете эту новую ветвь, как если бы она была стволом, объединяя изменения в ветки выпуска и из них, как обычно. В дальнейшем вы должны быть осторожны при смешивании изменений из старых и новых исходных баз.

Другая альтернатива - попробовать что-то вроде MolhadoRef ( статья в блоге о MolhadoRef и SCM с поддержкой рефакторинга ), если вы можете найти готовую к использованию эквивалентную систему, твои нужды. Теоретически это контроль с учетом рефакторинга. Я давно этого не изучал, но в последний раз вспоминаю, что это было довольно далеко от чего-то большего, чем исследовательская работа и подтверждение концепции.

1 голос
/ 27 февраля 2009

Я вижу два разных способа решения этой проблемы:

1

Значительные изменения в стволе (например, крупный рефакторинг) не должны выполняться в стволе. Они должны быть сделаны в ветке и объединены обратно в ствол, когда они достаточно стабильны.

Периодически изменения в магистрали должны объединяться с другими ветвями обслуживания. Причина объединения рефакторинга в транк только тогда, когда он стабилен, заключается в том, что они затем будут объединены с ветвями обслуживания. Однако, если нет возможности сделать эти изменения стабильными, лучше использовать вариант 2.

После внесения изменений в ветви обслуживания они могут быть объединены обратно в магистраль.

2

Создать ветку веток обслуживания (по одной ветке на каждую). Это будет использоваться для объединения магистрали с каждой ветвью обслуживания. (Обратите внимание, что использование внешних SVN или его эквивалентов должно использоваться для ограничения количества ветвей обслуживания).

Сделайте весь свой рефакторинг в стволе и объедините его с ветвями веток обслуживания. Когда вы освобождаете или думаете, что магистраль стабильна, тогда объедините эти ветви выпусков обслуживания обратно в их соответствующие ветви. Затем они могут быть в свою очередь объединены обратно в багажник.

Фактически каждая ветвь обслуживания становится «вспомогательной магистралью».

Обратите внимание, что этот сценарий подчеркивает компромисс между будущим обслуживанием и предварительным обслуживанием. Чем больше различий и различий в вашем коде, тем больше требуется предварительное обслуживание. Хорошая часть заключается в том, что поэтапное обслуживание намного проще.

1 голос
/ 24 февраля 2009

Это может быть очень трудоемким предложением, но первое, что приходит мне в голову, это объединить все обратно в магистраль. Все изменения объединяются обратно в ствольную копию и сохраняют их все вместе. Затем рефакторинг на стволе, как вы хотите. Теперь у вас есть работающий багажник со всеми исправлениями вместе взятыми.

К сожалению, это будет означать, что любые исправления в ветвях сопровождения должны быть объединены в центральный ствол. Я понимаю, что это было бы много работы, но я думаю, что это позволило бы все реорганизовать, и любые улучшения в ветвях сопровождения будут принадлежать основной ветке. Я могу быть наивным по этому поводу, но я действительно не работал над производственным проектом, а также не знаю точно, что находится в ветвях сопровождения. Я полагаю, что это сделает полностью обновленный ствол, и все ваши улучшения обслуживания будут интегрированы в ствол.

Я полагаю, что если вы сделаете это таким образом, вы добьетесь максимального качества всех ваших веток, и ваш рефакторинг будет распространен на все ветви, которые вы разветвите после рефакторинга. Это был бы хороший способ объединить вашу команду для всего слияния.

1 голос
/ 28 октября 2008

Создайте ветку обслуживания и сделайте ее буфером между стволом и ветвями версий.

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

Хотя я не думаю, что есть серебряная пуля. По мере того, как ветви будут расходиться все больше и больше, они станут несовместимыми, и вам придется подумать, как долго вы будете их поддерживать. В противном случае вы можете исправить ошибки более одного раза, но по-разному для разных веток.

1 голос
/ 28 октября 2008

В тот момент, когда ваши ветви обслуживания больше не совместимы с основной магистралью, пришло время создавать новые ветви для этой цели. Таким образом, в начале большого проекта вы должны убедиться, что все ваши разработчики знают, что в основной ствол поступает новая функциональность, чтобы они могли лучше выбирать, где внедрять исправления. Предположительно, если изменения кода, происходящие в основной магистрали, настолько значительны, что делают обслуживание неподдерживаемым, то обслуживание должно быть включено в основную магистраль.

0 голосов
/ 28 апреля 2009

Как и Грег указал Есть несколько возможных сценариев.

Я бы добавил случай (2.5), где требуется ручное объединение, но, поскольку вы переместили метод из его исходного местоположения, а затем применили некоторые изменения, объединение становится сложным, особенно если «базовый» код также был изменен в ветке "обслуживание". Это не так редко, как кажется, на самом деле перемещение метода в другое место и применение небольшого исправления довольно распространено.

Мы разработали инструмент под названием Xmerge (перекрестное слияние), который является первым шагом к объединению с учетом фактора. Это еще не автоматический, но он помогает справляться с трудными объединениями, включающими перемещенный код. Здесь описано и уже интегрировано в Plastic SCM 2.7.

Мы работаем над: автоматическим обнаружением перемещения, а также возможностью «перекрестного слияния» с несколькими целевыми файлами (вы перемещаете код в другой файл, что также довольно распространено).

...