SVN merge for dummies

Version control systems
Простыми словами и с большим количеством картинок на примере Eclipse рассказывается, как сделать svn merge. Статья будет полезна тем, у кого выполнение слияния веток еще не стало повседневной частью работы.

Статья рассчитана на использование следующего программного обеспечения:
  • SVN 1.4.4
  • Eclipse 3.4.2 SR2
  • Subversive 0.7.7
  • SVN-коннектор Native JavaHL 1.5.4

Жизненный цикл ветки


Рассмотрим пример жизни обычной ветки, в которой вы, в роли одного из разработчиков, выполняете порученную вам задачу.
В начале в репозитории присутствует только основная часть кода (trunk, далее «транк»), текущая ревизия которой — r10. Состояние транка T1 соответствует версии кода на момент создания ветки (branch, далее «ветка»).
Вы создаете собственную ветку, которая начинается с ревизии r11. Далее начинается параллельная работа над кодом, в течение которой вы вносите в свою ветку объем изменений B1. Одновременно с этим другие разработчики вносят в транк объем изменений T2.
Далее вы включаете в свою ветку последние изменения, сделанные в транке. Состояние вашей ветки в данный момент T1+B1, поэтому вас интересуют только изменения, произошедшие в транке с того момента, когда вы создали свою ветку. Эти изменения (T2) однозначно задаются номерами начальной и конечной ревизии, в нашем случае это r10 и r12. Вы выполняете слияние изменений, сделанных в транке между 10 и 12 ревизиями, с текущей версией вашей ветки и, при необходимости исправив ошибки, коммитите результат слияния в свою ветку (ревизия r14).
svntree1.png
Теперь у вас есть все последние изменения из транка, а состояние вашей ветки равно T1+B1+T2.
Далее разработка продолжается, и вы вносите в свою ветку объем изменений B2, в то время как другие разработчики вносят в транк объем изменений T3.
Вы снова включаете в свою ветку последние изменения из транка. Состояние вашей ветки равно T1+B1+T2+B2, поэтому вам необходим только объем изменений T3. Выполняя слияние, в качестве начальной ревизии вы задаете конечную ревизию предыдущего слияния (r12). Таким образом, вы выполняете слияние изменений, сделанных в транке между 12 и 15 ревизиями, с текущей версией вашей ветки и, при необходимости исправив ошибки, коммитите результат слияния в свою ветку (ревизия r17).
Допустим, что теперь порученная задача вами решена и пришло время внедрить выполненные вами изменения в транк. Операция не должна быть очень сложной благодаря тому, что вы регулярно приводили свою ветку в соответствие с транком и оперативно решали возникающие проблемы. Состояние вашей ветки сейчас равно T1+B1+T2+B2+T3 (плюс исправления B1' и B2', которые вы внесли в процессе слияний). Состояние транка сейчас равно T1+T2+T3. Поэтому вы выполняете слияние изменений, сделанных в вашей ветке между 11 и 17 ревизиями, с текущей версией транка и, при необходимости исправив ошибки, коммитите новую версию транка (r18).
После этого свою ветку рекомендуется удалить и, продолжая свою работу, создать новую ветку.
Далее на конкретном примере рассмотрим процесс слияния.

Исходное состояние репозитория


Рассмотрим следующую ситуацию. Есть набор проектов test_area, в котором есть проект test_project:
repo1.png
Из ревизии 40898 транка был создан бранч, начальная ревизия которого стала равна 40904. После этого были внесены изменения в транк (ревизия 40906) и в бранч (ревизия 40923). Далее рассмотрим процесс слияния бранча с транком на примере включения в бранч последних изменений транка.
svntree2.png
В проекте есть три файла: ChangedByMe.java, ChangedByOther.java и Conflicted.java.
Вы в своей ветке поменяли файл ChangedByMe.java, дописав к значению поля str подстроку "-I changed it". Еще в своей ветке вы поменяли файл Conflicted.java, также дописав к значению поля str подстроку "-I changed it".
Переключившись в транк, вы (или кто-то еще) аналогичным образом поменяли файлы ChangedByOther.java и Conflicted.java, дописав туда подстроку "-Someone changed it".

Выполнение слияния


Шаг 1. Подготовка

На данном шаге следует для своей ветки выполнить следующие операции:
  • сделать update;
  • устранить все ошибки компиляции;
  • сделать commit.

updatecommit.png
Обязательно убедитесь, что ваш проект нацелен на нужную ветку:
verifylocation.png
Слияние удобней всего выполнять в перспективе «Team Synchronizing». Переключитесь в эту перспективу и откройте следующие виды: «Synchronize», «History», «Console», «SVN Repositories», «Project Explorer».

Шаг 2. Определение начальной и конечной ревизии транка для слияния

В качестве начальной ревизии для первого слияния следует брать ревизию транка, из которой была создана ветка. В нашем случае это 40898. Определить этот номер можно, изучая историю корневой папки своей ветки и используя кнопку «Stop On Copy».
history1.png
Для большей наглядности рекомендуется при создании ветки писать комментарий вида:
Branched from trunk@40898
В качестве конечной ревизии следует брать последнюю ревизию транка — 40906. Этот номер можно увидеть в виде «SVN Repositories» или в диалоге выбора URL диалога Merge.
При последующих слияниях в качестве начальной ревизии транка следует брать конечную ревизию транка, указанную в предыдущем слиянии. Чтобы не забыть ее номер, при коммите результатов слияния следует записать комментарий следующего вида:
Merged with trunk@40898-40906

Шаг 3. Заполнение диалога Merge

Делаем видимым вид «Console».
В виде «Project Explorer» нажимаем на нашем проекте правую кнопку и выбираем Team->Merge. Двигаем диалог Merge так, чтобы была видна консоль.
В URL выбираем путь к транку: .../test_area/trunk/test_project
В Revisions задаем: 40898-40906
merge1.png
Нажимаем кнопку Preview и контролируем адекватность результатов, появляющихся в консоли и в окне Merge Preview.
merge2.png
Нажимаем кнопку OK.
В этот момент автоматически произойдут следующие действия:
  1. Выполнится команда svn merge.
  2. Все локальные файлы, которые были изменены в транке, но не были изменены в вашей ветке (ChangedByOther.java), будут заменены версией из транка. Таким образом, у вас на диске будет файл из транка, а предыдущая версия этого файла, с которой вы работали, будет лежать в svn в вашей ветке.
  3. Все локальные файлы, которые были изменены и в транке, и в вашей ветке (Conflicted.java), будут заменены diff-файлами вида:

public class Conflicted {
<br><<<<<<< .working
<br> String str = "Conflicted-I changed it";
<br>=======
<br> String str = "Conflicted-Someone changed it";
<br>>>>>>>> .merge-right.r40906
<br>}


Для каждого конфликтного файла в папке проекта будут созданы три служебных файла:
[имя_файла].merge-left.r[номер_ревизии] — содержит исходную версию файла (которая была до ваших и чужих изменений)
[имя_файла].merge-right.r[номер_ревизии] — содержит версию файла с внешними изменениями (из транка)
[имя_файла].working — содержит версию файла с вашими изменениями

В виде «Synchronize» будут отображены результаты выполнения команды svn merge:
sync1.png

Шаг 4. Обработка внешних изменений

Сначала обработаем обновившиеся файлы, которые не были изменены нами. Поскольку наша задача — загрузить в свою ветку изменения из транка, имеет смысл нажать кнопку Accept All Incoming Changes.
При необходимости принять отдельное решение относительно каждого файла, дважды кликаем на него — откроется Compare Editor. В левой части окна будет отображена версия файла из транка, а в правой — версия из вашей ветки.
compare1.png
Чтобы отказаться от изменений, следует в Compare Editor включить режим Two-Way Compare (кнопка должна быть нажата) и нажать кнопку Copy All Non-Conflicting Changes from Right to Left, после чего сохранить файл (Ctrl-S). После сохранения файл исчезнет из вида «Synchronize».
Чтобы принять изменения, следует в виде «Synchronize» нажать на файле правую кнопку и выбрать Accept. Файл исчезнет из вида «Synchronize».

Шаг 5. Обработка конфликтов

Поскольку наша задача — загрузить в свою ветку последние изменения из транка, то следует модифицировать конфликтные файлы таким образом, чтобы сохранить там все изменения, выполненные в транке. Если вы считаете эти изменения неудачными — обсудите это с их автором, модифицируйте транк и повторите процесс слияния.
Обрабатывая каждый файл, дважды кликаем на него — откроется Compare Editor. Имеет смысл выбрать конкретное изменение в верхней панели редактора, тогда в левой части окна будет отображен конфликтный фрагмент из транка, а в правой — конфликтный фрагмент из вашей ветки.
compare2.png
Чтобы отказаться от изменений, следует в Compare Editor включить режим Two-Way Compare (кнопка должна быть нажата), выбрать в верхней панели редактора элемент «Compilation Unit» и нажать кнопку Copy All Non-Conflicting Changes from Right to Left, после чего сохранить файл (Ctrl-S). Далее следует в виде «Synchronize» нажать на файле правую кнопку и выбрать Mark As Merged. Файл исчезнет из вида «Synchronize».
Чтобы принять изменения, следует в виде «Synchronize» нажать на файле правую кнопку и выбрать «Edit Conflicts» — откроется Compare Editor в режиме «Edit Conflicts». В нем (сюрприз!) ваша версия файла будет отображена в левой части окна, версия из транка будет отображена в правой части. После этого следует в Compare Editor включить режим Two-Way Compare (кнопка должна быть нажата) и нажать кнопку Copy All Non-Conflicting Changes from Right to Left, после чего сохранить файл (Ctrl-S). Кроме этого, файл можно отредактировать вручную в левой части окна. Далее следует в виде «Synchronize» нажать на файле правую кнопку и выбрать Mark As Merged. Файл исчезнет из вида «Synchronize».
compare3.png

Шаг 6. Откат неудачных изменений

Если случайно сделали что-то не так, например, приняли изменения вместо того, чтобы отказаться (или наоборот), то всегда все можно легко исправить.
Для отката к версии файлов из вашей ветки в виде «Project Explorer» нажмите правую кнопку на вашем проекте и выберите Team->Revert.
Удалить служебные файлы, созданные в процессе слияния можно либо вручную, либо нажав в виде «Project Explorer» правую кнопку на вашем проекте и выбрав Team->Synchronize with Repository. После этого в виде «Synchronize» следует на вашем проекте нажать кнопку «Override and Update...» и потом обновить (F5) вид «Project Explorer».
Для повторного запуска слияния нет необходимости снова вызывать диалог Merge. Достаточно нажать F5 в виде «Synchronize». При необходимости, следует сначала выбрать нужный экран синхронизации, нажав левую крайнюю кнопку тулбара в виде «Synchronize»:
sync2.png

Шаг 7. Коммит в свою ветку

После того, как принято решение по каждому из файлов, следует для своей ветки выполнить следующие операции:
  • устранить возникшие ошибки компиляции;
  • сделать коммит проекта, обязательно написав комментарий вида:
    Merged with trunk@40898-40906

Итак, вы загрузили в свою ветку последние обновления из транка. Регулярное выполнение этой операции позволит уменьшить объем возникающих при слиянии проблем.
PS. За рамки статьи сознательно было вынесено рассмотрение различий между SVN 1.4 и SVN 1.5, и описание способов разрешения более сложных конфликтных ситуаций.
Tags:svneclipsemergesubversive
Hubs: Version control systems
+16
25.9k 80
Comments 8

Popular right now

Wireless Systems Engineer
from 100,000 to 200,000 ₽ON SemiconductorСанкт-Петербург
Senior Java Developer
from 150,000 to 250,000 ₽Reliable systemsСаратовRemote job
Android-разработчик
from 170,000 to 230,000 ₽ENJOY PROСанкт-ПетербургRemote job
IOS-разработчик
from 190,000 to 300,000 ₽ENJOY PROСанкт-ПетербургRemote job
Machine Learning developer
from 150,000 to 200,000 ₽NZT GroupМосква

Top of the last 24 hours