— Зачем ты, Белка, летишь за мной, Кабаном?
— Не знаю, Кабан! Приказ Хорька. Как понял? Приём.
— Ни хера не понял! Какого Хорька, Белка? Я Кабан. Кто такой Хорёк? Кто это? Приём.
— Кабан, ты дятел! Как понял? Приём.
— Понял тебя, Белка. Я — Дятел. Повторяю вопрос про хорька. Кто это?
— Кабан, сука, ты всех заманал, лети вперёд молча! Конец связи.
Виктор Шендерович
Как известно, 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.txtSubversion взяла изменения, произошедшие в файле
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. Облегчить её могут небольшие скрипты, написание которых автор оставляет в качестве домашнего задания.
Предваряя холивар на тему «Какая система управления версиями лучше», автор будет благодарен за содержательные комментарии о том, как задача отслеживания переименованных файлов решается в других системах.