- •Лабораторная работа№4 «Система версионного контроля Subversion»
- •1. Фундаментальные понятия
- •1.1 Хранилище
- •1.2 Модели версионирования
- •1.2.1 Проблема разделения файлов
- •1.2.2 Модель Блокирование-Изменение-Разблокирование
- •1.2.3 Модель Копирование-Изменение-Слияние
- •1.3 Subversion в действии
- •1.3.1 Url хранилища в Subversion
- •1.3.2 Рабочие копии
- •1.3.3 Url хранилища
- •1.3.4 Правки
- •1.3.5 Как рабочие копии отслеживают хранилище
- •1.3.6 Смешивание правок в рабочих копиях
- •1.3.7 Обновления и фиксации отделены друг от друга
- •1.3.8 Смешивание правок
- •2. Импорт
- •2.1 Путешествие во времени вместе с Subversion
- •2.2 Создание рабочей копии
- •2.3 Простейший рабочий цикл
- •2.4 Обновление рабочей копии
- •2.5 Внесение изменений в рабочую копию
- •2.6 Анализ изменений
- •2.6.1 Svn status
- •2.6.3 Svn diff
- •2.6.4 Svn revert
- •2.7 Разрешение конфликтов (при слиянии с чужими изменениями)
- •2.8 Слияние конфликтов вручную
- •2.9 Копирование файла поверх вашего рабочего файла
- •2.10 Использование svn revert
- •2.11 Фиксация изменений
- •2.12 Анализ истории
- •Svn log
- •Svn diff
- •Анализ локальных изменений
- •Сравнение рабочей копии с хранилищем
- •Сравнение хранилища с хранилищем
- •Svn cat
- •Svn list
- •Заключительное слово об истории
- •3. Ветвление и слияние
- •3.1 Что такое ветка?
- •3.2 Использование веток
- •3.3 Создание ветки
- •3.4 Работа с веткой
- •3.5 Ключевые идеи, стоящие за ветками
- •3.6 Копирование изменений между ветками
- •3.7 Копирование отдельных изменений
- •3.8 Ключевые идеи, стоящие за слиянием
- •3.9 Как правильнее всего использовать слияние
- •3.9.1 Ручной контроль слияния
- •3.9.2 Предварительный просмотр результатов слияния
- •3.9.3 Конфликты при слиянии
- •3.9.4 Учитывать или игнорировать происхождение
- •3.10 Типовые примеры
- •3.10.1 Полное объединение двух веток
- •3.10.2 Отмена изменений
- •3.10.3 Восстановление удаленных элементов
- •3.11 Типовые приемы использования веток
- •3.11.1 Ветки релизов
- •3.11.2 Функциональные ветки
- •3.11.3 Переключение рабочей копии
- •3.12 Метки
- •3.12.1 Создание простой метки
- •3.12.2 Создание комплексной метки
- •3.13 Поддержка веток
- •3.13.1 Структура хранилища
- •3.13.2 Продолжительность жизни информации
3.10.2 Отмена изменений
Еще одной типичной задачей для svn merge является откат ранее сделанных изменений. Предположим, работая в копии /calc/trunk, вы вдруг выясняете, что изменения файла integer.c, сделанные в правке 303, были совершенно ошибочными. Вы можете воспользоваться командой svn merge для «отмены»изменений в своей рабочей копии, после чего зафиксировать локальные изменения в хранилище. Для этого нужно всего лишь указать различия в обратном порядке:
$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U integer.c
$ svn status
M integer.c
$ svn diff
…
# verify that the change is removed
…
$ svn commit -m "Undoing change committed in r303."
Sending integer.c
Transmitting file data .
Committed revision 350.
Правку хранилища можно рассматривать как группу изменений (некоторые системы управления версиями называют это набором изменений). Используя параметр -r, можно попросить svn merge применить к рабочей копии набор изменений или целый диапазон наборов изменений. В нашем примере с отменой изменений мы просим svn merge применить к рабочей копии набор изменений #303 в обратном направлении.
Обратите внимание, что подобный откат изменений ничем не отличается от остальных операций, выполняемых с помощью svn merge. Поэтому необходимо с помощью svn status и svn diff убедиться в том, что результат соответствует ожиданиям, а затем, используя svn commit, отправить финальную версию в хранилище. После фиксации этот конкретный набор изменений больше не будет отражен в правке HEAD.
Давайте, однако, подумаем: отменяется ли на самом деле предыдущая фиксация? Изменения продолжают существовать в правке 303. И если кто-то создаст рабочую копию проекта calc между правками 303 и 349, он ведь все равно получит ошибочные изменения?
Безусловно, это так. Говоря об «удалении» изменений, мы имеем в виду их удаление из правки HEAD. Исходные изменения по-прежнему останутся в истории хранилища. Чаще всего это можно рассматривать даже как положительный момент. В любом случае, большинство пользователей интересует только HEAD-правка проекта. Однако, возможны ситуации, когда необходимо полностью удалить последствия фиксации. (Например, если кто-то случайно зафиксировал конфиденциальный документ.) Сделать это будет непросто, так как архитектура Subversion намеренно исключает возможность потери информации. Правки представляют собой не меняющиеся деревья файлов, основанные друг на друге. Удаление правки из хранилища может вызвать эффект домино, потащив за собой все последующие правки и повредив все рабочие копии. [26]
3.10.3 Восстановление удаленных элементов
Отличным свойством системы управления версиями является то, что информация никогда не теряется. При удалении файла или каталога элемент исключается из правки HEAD, но продолжает существовать в более ранних правках. Новые пользователи очень часто спрашивают: «Как мне вернуть назад свой файл или каталог?»
Для начала было бы неплохо определиться, какой именно элемент мы пытаемся восстановить. Для этого полезно представить все объекты хранилища как точки в двумерной системе координат. Первой координатой будет отдельное дерево правок, в второй — путь в этом дереве. Таким образом, каждая версия файла или каталога может быть задана парой координат.
В отличие от CVS, Subversion не имеет каталога Attic[27], поэтому для определения искомой пары координат восстанавливаемого объекта нужно вызвать svn log. Лучше всего запустить svn log --verbose в каталоге, который содержал удаленный объект. Параметр --verbose покажет для каждой правки список измененных элементов — в этом случае вам останется только найти ту правку, в которой файл или каталог были удалены. Это можно сделать визуально или обработав вывод команды каким-нибудь инструментом (например, утилитой grep или просто запустив поиск в редакторе).
$ cd parent-dir
$ svn log --verbose
…
------------------------------------------------------------------------
r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
D /calc/trunk/real.c
M /calc/trunk/integer.c
Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.
…
В этом примере предполагается, что вы ищете удаленный файл real.c. Просмотрев логи родительского каталога, вы можете заметить, что этот файл был удален в правке 808. Следовательно, самая последняя версия этого файла была в правке, предшествующей удалению. Вывод: необходимо из правки 807 восстановить путь /calc/trunk/real.c.
Поиск — непростая задача. Теперь, когда нам известно, что именно нужно восстановить, есть две варианта действий.
Во-первых, мы можем посредством svn merge применить правку 808 «в обратном направлении». (Как отменять изменения, мы уже рассматривали, см.«Отмена изменений».) Это приведет к повторному добавлению файла real.c в форме локального изменения. Файл будет запланирован для добавления, и после фиксации будет снова присутствовать в HEAD.
Однако, в данном примере это не самое лучшее решение. Повторное применение правки 808 не только добавит файл real.c. Лог-сообщение показывает, что также будут отменены некоторые изменения в integer.c, что весьма нежелательно. Конечно, можно выполнить обратное слияние с правкой 808, а затем отменить (svn revert) локальные изменения в integer.c, но такой подход плохо масштабируется. Что, если в правке 808 изменилось 90 файлов?
При другом, более аккуратном подходе svn merge вообще не используется, а вместо этого применяется команда svn copy. Просто скопируйте объект, заданный «парой координат» (правка и путь), из хранилища в рабочую копию:
$ svn copy --revision 807 \
http://svn.example.com/repos/calc/trunk/real.c ./real.c
$ svn status
A + real.c
$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding real.c
Transmitting file data .
Committed revision 1390.
Знак "плюс" в столбце статуса показывает, что элемент не просто запланирован для добавления, а запланирован для добавления «с историей». Subversion запоминает, откуда он был скопирован. В будущем вызов svn log для этого файла будет пересекать точку восстановления файла и прослеживать всю историю, предшествующую правке 807. Другими словами, новый файл real.c на самом деле не является новым; он является прямым потомком исходного файла, который был удален.
Хотя наш пример демонстрирует только восстановление файла, отметим, что такой же подход будет работать и при восстановлении удаленных каталогов.
