Pull to refresh

Subversion: cлияние переименований файлов

Reading time3 min
Views3.4K
— Зачем ты, Белка, летишь за мной, Кабаном?
— Не знаю, Кабан! Приказ Хорька. Как понял? Приём.
— Ни хера не понял! Какого Хорька, Белка? Я Кабан. Кто такой Хорёк? Кто это? Приём.
— Кабан, ты дятел! Как понял? Приём.
— Понял тебя, Белка. Я — Дятел. Повторяю вопрос про хорька. Кто это?
— Кабан, сука, ты всех заманал, лети вперёд молча! Конец связи.
Виктор Шендерович

Как известно, Subversion не умеет отслеживать переименования файлов. Согласно документации, команда svn move равносильна svn copy с последующим svn delete. Такое поведение вызывает большие проблемы при слиянии веток. Рассмотрим способы их решения.

В примерах вместо полного пути к ветке используется переменная $source, которую можно создать так:

export source=https://example.com/svn/trunk

Файл переименован в текущей ветке


Пусть в текущей ветке файл foo.txt был переименован в bar.txt. Попробуем перенести изменения из ветки $source, где файл всё ещё называется foo.txt:

svn merge $source -r 10:20
Skipped missing target: 'foo.txt'


Subversion не смогла применить изменения, т. к. не нашла файл foo.txt в рабочей копии. Что делать? Явно указать новое имя файла:

svn merge $source/foo.txt -r 10:20 bar.txt
U bar.txt


Subversion взяла изменения, произошедшие в файле foo.txt с 10 по 20 ревизию, и применила к файлу bar.txt, чего мы и добивались.

Файл переименован в исходной ветке


Пусть в ветке $source файл foo.txt был переименован в bar.txt. В текущей ветке файл по-прежнему называется foo.txt. В обеих ветках у файлов поменялось содержимое. Попробуем склеить изменения:

svn merge $source -r 10:20
A bar.txt
D foo.txt


Что произошло? Subversion удалила файл foo.txt и добавила копию файла bar.txt из ветки $source. При этом все изменения в файле foo.txt, произведённые в текущей ветке, оказались утеряны.

Что делать? Во-первых, отменить разрушительные действия svn merge:

svn revert foo.txt bar.txt
Reverted 'foo.txt'
Reverted 'bar.txt'
rm bar.txt


Во-вторых, локально переименовать файл foo.txt в bar.txt:

svn move foo.txt bar.txt
A bar.txt
D foo.txt


В-третьих, наложить на него изменения, произошедшие с файлом в ветке $source:

svn merge $source/foo.txt@10 $source/bar.txt@20 bar.txt
U bar.txt


Обратите внимание: мы берём разницу между файлом foo.txt десятой ревизии и файлом bar.txt двадцатой ревизии и накладываем её на локальный bar.txt.

Результат: файл переименован и содержит изменения из обеих веток.

Файл переименован в обеих ветках


Пусть в ветке $source файл foo.txt получил название bar.txt, а в текущей — baz.txt. В обеих ветках у файлов поменялось содержимое. Попробуем склеить изменения:

svn merge $source -r 10:20
A bar.txt
Skipped missing target: 'foo.txt'


Subversion скопировала файл bar.txt из ветки $source, а вот удалить foo.txt не смогла. Как и в предыдущем случае, возвращаем всё назад:

svn revert bar.txt
Reverted 'bar.txt'
rm bar.txt


Дальнейшие действия зависят от того, какое имя мы считаем правильным: текущее (baz.txt) или приехавшее из ветки $source (bar.txt). В первом случае достаточно наложить разницу между foo.txt и bar.txt на baz.txt:

svn merge $source/foo.txt@10 $source/bar.txt@20 baz.txt
U baz.txt


Во втором случае необходимо сначала переименовать baz.txt в bar.txt и уже потом накладывать разницу:

svn move baz.txt bar.txt
A bar.txt
D baz.txt

svn merge $source/foo.txt@10 $source/bar.txt@20 bar.txt
U bar.txt


Заключение


Как видно, слияние переименований файлов — непростая задача в Subversion. Облегчить её могут небольшие скрипты, написание которых автор оставляет в качестве домашнего задания.

Предваряя холивар на тему «Какая система управления версиями лучше», автор будет благодарен за содержательные комментарии о том, как задача отслеживания переименованных файлов решается в других системах.

Ссылки по теме

Tags:
Hubs:
Total votes 55: ↑52 and ↓3+49
Comments21

Articles