SVN. Базовые команды
по мотивам «Subversion in Action»
- Начало работы
- Основы
- Экскурсия по Subversion
- Начальное получение рабочей копии
- Простой рабочий цикл
- Итоги
Начало работы
Установка редактора
echo $SVN_EDITOR
Основы
Рабочие копии
Рабочая копия Subversion представляет собой обычное дерево каталогов локальной файловой системы, содержащее файлы. Рабочая копия является личным рабочим пространством, и изменения станут доступны другим только в случае их явной публикации. Можно иметь несколько рабочих копий одного и того же проекта.
Публикациия изменений осуществляется с помощью их записывания в репозиторий, а получение - с помощью чтения из репозитория.
Каждая рабочая копия содержит папку с именем .svn, известную как административный каталог (administrative directory). Файлы, находящиеся в административном каталоге, необходимы для понимания Subversion того какие артефакты содержат неопубликованные изменения, а какие являются устаревшими по отношению к актуальной версии.
Обычно, Subversion-репозиторий содержит файлы нескольких проектов, которые находятся в соответствующих дочерних каталогах дерева файловой системы этого репозитория.
Получение рабочей копии поддерева репозитория выполняется следующим образом:
$ svn checkout http://svn.example.com/repos/calc
A calc/Makefile
A calc/integer.c
A calc/button.c
Checked out revision 56.
$ ls -A calc
Makefile integer.c button.c .svn/
Расположение Subversion-репозитория всегда задается в виде URL.
Schema | Способ доступа |
---|---|
file:/// | доступ к репозиторию в файловой системе |
http:// | доступ по протоколу WebDAV к преднастроенному серверу Apache |
https:// | то же, что и http://, но с шифрованием SSL |
svn:// | доступ по внутреннему протоколу к серверу svnserve |
svn+ssh:// | то же, что и svn://, но через SSH-туннель |
Публикация изменений обычно называется фиксацией (committing) и выполняется с помощью команды commit
:
$ svn commit button.c
Sending button.c
Transmitting file data .
Committed revision 57.
Для обновления локальной рабочей копии используется команда update
:
$ svn update
U button.c
Updated to revision 57.
Ревизии
Каждый коммит (commit) в репозитории рассматривается в виде атомарной транзакции - фиксируются либо все изменения, либо ни одного.
При приеме коммита в репозитории создается новое состояние дерева файловой системы, называемое ревизией (revision). Каждой ревизии присваивается уникальный возрастающий целочисленный номер. Начальная ревизия имеет номер 0
и соответствует пустому корневому каталогу.
Номера ревизий в Subversion относятся ко всему дереву, а не отдельному файлу.
Как рабочие копии отслеживают репозиторий
Для каждого файла Subversion поддерживает два основных свойства в административном каталоге .svn/:
- базовая ревизия рабочего файла (рабочая ревизия);
- отметка времени последнего обновления локальной копии из репозитория.
Используя эту информацию Subversion, при взаимодействии с репозиторием, определяет одно из 4-х состояний рабочего файла:
-
неизменен и актуален - файл не изменялся в рабочем каталоге, и репозиторий не содержит для него изменений после рабочей ревизии.
svn commit
иsvn update
выполнятся без внесения изменений. -
изменен локально и актуален - файл был изменен в рабочем каталоге, но репозиторий не содержит для него изменений после рабочей ревизии.
svn commit
опубликует внесенные изменения, аsvn update
выполнится без внесения изменений. -
неизменен и устарел - файл не изменялся в рабочем каталоге, но был изменен в репозитории.
svn commit
выполниться без внесения изменений, аsvn update
затянет (fold) последние изменения в рабочую копию. -
изменен локально и устарел - файл был изменен как локально, так и в репозитории.
svn commit
завершится с ошибкой, т.к. файл должен быть сначала обновлен.svn update
попытается совместить (merge) изменения в репозитории с локальными изменениями. Если автоматическое слияния невозможно, то разрешение конфликта выполняется пользователем.
Смешивание рабочих копий разных ревизий
Одной из особенностей Subversion является возможность иметь рабочую копию включающую файлы и каталоги разных рабочих ревизий.
Обновление и комиты разделены
Операции публикации не влекут обновление, и наоборот. Т.е. при изменении и последующем коммите одного файла в рабочей копии изменится только его рабочая ревизия. Рабочая ревизия всей копии изменится только после svn update
.
Смешивание ревизий - это норма
Закоммиченные файлы будут иметь более поздний номер рабочей ревизии, чем все остальные. Для просмотра состояния рабочих ревизий можно воспользоваться командой svn status --verbose
.
Смешивание ревизий - это полезно
Смешивание ревизий дает возможность переместить определенную часть рабочей копии в истории, позволяя исследовать более ранние изменения.
Смешивание ревизий имеет ограничения
- Невозможно зафиксировать (commit) удаление неактуального файла.
- Невозможно зафиксировать изменения метаданных неактуального каталога. Рабочая ревизия каталога имеет определенный набор свойств, и фиксация изменений свойств неактуального каталога может привести к утрате свойств, неизвестных рабочей копии.
Экскурсия по Subversion
Импорт
Для импортирования нового проекта в Subversion-репозиторий используется команда import
:
$ svn import -m "New import" myproj \
http://svn.red-bean.com/repos/trunk/misc/myproj
Adding myproj/sample.txt
...
Transmitting file data .........
Committed revision 16.
Ревизии: номера, ключевые слова и даты
Для указания ревизии используется ключ --revision
(-r
):
svn --revision REV
Диапазон задается указанием номеров ревизий через двоеточие:
svn --revision REV1:REV2
Номера ревизий
Вновь созданный репозиторий начинается с нулевой ревизии, а каждый последующий коммит увеличивает номер ревизии на единицу.
$ svn commit --message "Corrected number of cheese slices."
Sending sandwich.txt
Transmitting file data .
Committed revision 3.
Ключевые слова ревизий
Для некоторых ревизий Subversion поддерживает ключевые слова (т.е. синонимы), которые можно использовать в качестве аргументов ключа --revision
:
-
HEAD
- последняя (самая молодая) ревизия в репозитории. -
BASE
- номер ревизии артефакта в рабочей копии. Если артефакт был локально изменен, ревизияBASE
ссылается на версию без изменений. -
COMMITTED
- последняя ревизия меньшая или равнаяBASE
, в которой артефакт был изменен. -
PREV
- ревизия предшествующая последней ревизии в которой артефакт был изменен (техническиCOMMITTED - 1
).
Subversion хранит оригинальную (pristine) копию каждого файла в административном каталоге .svn. Номер ревизии оригинальной копии соответствует ключевому слову BASE
.
Данные ключевые слова могут использоваться только при указании локального пути, но не URL.
Примеры использования:
$ svn diff --revision PREV:COMMITTED foo.c
# покажет последние закоммиченные изменения foo.c
$ svn log --revision HEAD
# покажет сообщение лога для последнего коммита в репозитории
$ svn diff --revision HEAD
# сравнит рабочий файл (с локальными изменениями)
# с последней версией в репозитории
$ svn diff --revision BASE:HEAD foo.c
# сравнит оригинальную (pristine) версию foo.c
# с последней версией в репозитории
$ svn log --revision BASE:HEAD
# покажет логи всех коммитов с момента последнего обновления
$ svn update --revision PREV foo.c
# откатит последнее изменение foo.c
# (рабочая версия foo.c уменьшится)
Даты ревизий
Ключ --revision
, также, может принимать в качестве параметра дату. В этом случае Subversion найдет наиболее позднюю ревизию на заданную дату.
$ svn checkout --revision {2002-02-17}
$ svn checkout --revision {15:30}
$ svn checkout --revision {15:30:00.200000}
$ svn checkout --revision {"2002-02-17 15:30"}
$ svn checkout --revision {"2002-02-17 15:30 +0230"}
$ svn checkout --revision {2002-02-17T15:30}
$ svn checkout --revision {2002-02-17T15:30Z}
$ svn checkout --revision {2002-02-17T15:30-04:00}
$ svn checkout --revision {20020217T1530}
$ svn checkout --revision {20020217T1530Z}
$ svn checkout --revision {20020217T1530-0500}
$ svn log --revision {2002-11-28}
------------------------------------------------------------------------
r12 | ira | 2002-11-27 12:31:51 -0600 (Wed, 27 Nov 2002) | 6 lines
...
При указании только даты, например 2002-11-27
, Subversion дополнит ее временем 00:00:00
.
Поиск в диапазоне дат выполняется следующим образом:
$ svn log --revision {2002-11-20}:{2002-11-29}
...
Начальное получение рабочей копии
Извлечение (checkout) репозитория создает его рабочую копию на локальной машине. Созданная рабочая копия соответствует HEAD
-ревизии удаленного репозитория.
$ svn checkout http://svn.collab.net/repos/svn/trunk
A trunk/subversion.dsw
A trunk/svn_check.dsp
A trunk/COMMITTERS
A trunk/configure.in
A trunk/IDEAS
...
Checked out revision 2499.
Каталог для рабочей копии задается следующим образом:
$ svn checkout http://svn.collab.net/repos/svn/trunk subv
A subv/subversion.dsw
A subv/svn_check.dsp
A subv/COMMITTERS
A subv/configure.in
A subv/IDEAS
...
Checked out revision 2499.
В этом случае рабочая копия будет располагаться в каталоге subv вместо trunk.
Простой рабочий цикл
Типичный рабочий цикл выглядит следующим образом:
- Обновление рабочей копии
svn update
- Внесение изменений
svn add
svn delete
svn copy
svn move
- Проверка изменений
svn status
svn diff
svn revert
- Слияние опубликованных изменений с локальной рабочей копией
svn update
svn resolved
- Фиксация изменений
svn commit
Обновление рабочей копии
Обновление рабочей копии до состояния последней ревизии в репозитории выполняется командой svn update
:
$ svn update
U foo.c
U bar.c
Updated to revision 2.
Буква перед артефактом означает действие выполняемое Subversion для обновление рабочей копии.
U foo
- файл foo был обновлен (на основе полученных от сервера изменений).A foo
- файл или каталог foo был добавлен в рабочую копию.D foo
- файл или каталог foo был удален из рабочей копии.R foo
- файл или каталог foo был замещен в рабочей копии, т.е. foo был удален, и артефакт с таким же именем был добавлен. Хотя имя осталось прежним репозиторий считает, что артефакты являются различными объектами с отдельной для каждого историей.G foo
- файл foo получил новые изменения из репозитория, но локальная копия также была отредактирована. Изменения либо не пересекались, либо были идентичными, и Subversion успешно слил (merGed) полученные изменения с локальной версией.C foo
- для файла foo от сервера были получены изменения, напрямую пересекающиеся с локальными. Данный конфликт должен быть разрешен с участием пользователя.
Внесение изменений
Возможные изменения в рабочей копии:
- изменение файлов - Subversion автоматически отслеживает такие изменения.
- изменение дерева - данный тип изменений осуществляется явной пометкой Subversion файлов и каталогов на запланированное удаление, добавление, копирование или перемещение. Хотя данные операции могут оказать прямой эффект на локальную копию, в репозиторий изменения попадут только после коммита.
Основные команды изменения дерева приведены ниже:
svn add foo
- планирует добавление в репозиторий файла, каталога или символьной ссылки. При последующем коммите foo станет дочерним артефактом по отношению к родительскому каталогу. В случае если foo является каталогом все его содержимое также будет запланировано на добавление. Для добавления каталога без содержимого необходимо указать ключ--non-recursive
(-N
).svn delete foo
- планирует удаление из репозитория файла, каталога или символьной ссылки. В случае файла или символьной ссылки удаление в локальной копии происходит сразу. При последующем коммите артефакт будет удален как из рабочей копии, так и из репозитория.svn copy foo bar
- создает новый артефакт bar как копию foo. Артефакт bar автоматически планируется на добавление. При последующем коммите bar добавляется в репозиторий вместе с историей отражающей факт копирования.svn move foo bar
- данная команда эквивалентнаsvn copy foo bar; svn delete foo
.
Изменения, вносимые данными командами, не видны в репозитории до следующего коммита. Для внесения изменений напрямую в репозиторий можно использовать версии команд работающие с URL.
Проверка изменений
Команда svn status
показывает все изменения в файлах и каталогах.
L some_dir # для some_dir svn оставил блокировку в области .svn
M bar.c # содержимое bar.c было изменено локально
M baz.z # только зачения свойств bar.z были изменены
X 3rd_party # 3rd_party является частью внешнего определения
? foo.o # svn не отслеживает foo.o
! some_dir # svn отслеживает артефакт, но он отсутствует или не полон
~ qux # версионируется как файл/каталог/ссылка, но тип был изменен
I .screenrc # svn игнорирует данный артефакт
A + moved_dir # добавлен с историей
M + moved_dir/README # добавлен с историей и имеет локальные изменения
D stuff/fish.c # файл запланирован на удаление
A stuff/loot/bloo.h # файл запланирован на добавление
C stuff/loot/lump.c # текст файла содержит конфликты из-за обновления
C stuff/loot/glub.c # свойства файла содержат конфликты из-за обновления
R xyz.c # файл запланирован на замещение
S stuff/squawk # файл или каталог был переведен в ветку
K dog.jpg # файл заблокирован локально; метка блокирования присутствует
O cat.jpg # файл заблокирован в репозитории другим пользователем
B bird.jpg # файл заблокирован локально, но блокировка имеет дефект
T fish.jpg # файл заблокирован локально, но блокировка перезахвачена
Вывод svn status
включает пять колонок символов, после чего идут несколько пробелов, после чего идет имя файла или каталога.
В первой колонке печатается код статуса файла или каталога и/или их содержимого:
A item
- файл, каталог или символьная ссылка запланированы на добавление в репозиторий.C item
- файл находится в состоянии конфликта, т.е. полученные от сервера изменения пересекаются с локальными изменениями в рабочей копии. Конфликт должен быть разрешен до отправки изменений в репозиторий.D item
- файл, каталог или символьная ссылка запланированы на удаление из репозитория.M item
- содержимое файла было изменено.R item
- файл, каталог или символьная ссылка запланированы на замещение соответствующего артефакта в репозитории, т.е. в рамках одной ревизии данный объект сначала будет удален, а после объект с таким же именем будет добавлен в репозиторий.X item
- каталог не версионируется, и относится к внешнему определению (externals definition) Subversion.? item
- файл, каталог или символьная ссылка не версионируются. Вывод данных файлов можно опустить либо указав ключ--quiet
(-q
) для командыsvn status
, либо с помощью задания свойстваsvn:ignore
для родительского каталога.! item
- файл, каталог или символьная ссылка версионируются, но утрачены или каким-то образом повреждены. Артефакт может быть утрачен по причине удаления без использования команд Subversion. Каталог может быть поврежден в случае прерывания получения рабочей копии или обновления. Вызовsvn update
обновит файл или каталог из репозитория, а вызовsvn revert file
восстановит утраченный файл.~ item
- файл, каталог или символьная ссылка находятся в репозитории в виде, отличающемся от того, что находится в рабочей копии. Например, в репозитории содержится файл, а в локальной копии - каталог с таким же именем, и при этом не использовались командыsvn delete
иsvn add
.I item
- файл, каталог или символьная ссылка не версионируются т.к. Subversion настроен на его игнорирование в ходе выполнения командsvn add
,svn import
иsvn status
. Такие файлы будут выводится только в случае указания ключа--no-ignore
при вызове командыsvn status
.
Вторая колонка показывает статус свойств файла или каталога. В случае изменения свойств будет выведено M
, иначе - пробел.
В случае если Subversion заблокировал рабочую область каталога (.svn) в третьей колонке будет выведено L
, иначе - пробел. Такое возможно, например, в ходе редактирования сообщения коммита. Иначе, в случае если нет операций находящихся в процессе работы, может потребоваться вызов svn clean
.
В случае если файл или каталог запланированы на добавление или изменение с дополнительной сопровождающей историей, в четвертой колонке будет выведен +
, иначе - пробел. Обычно это связано с использованием svn move
или svn copy
.
Пятая колонка может содержать только пробел или S
. Это означает, что файл или каталог был переключен с пути остальной рабочей копии на ветку (используя svn switch
).
Шестая колонка показывает информацию о блокировках (отличных от L
в третьей колонке).
Получение статуса конкрентного артефакта выполняется следующим образом:
$ svn status stuff/fish.c
D stuff/fish.c
Вызов svn status
с ключем --verbose
(-v
) выведет статус всех артефактов в рабочей копии, в том числе не изменившихся.
$ svn status --verbose
M 44 23 sally README
44 30 sally INSTALL
M 44 20 harry bar.c
44 18 ira stuff
44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
44 21 sally stuff/things
A 0 ? ? stuff/things/bloo.h
44 36 harry stuff/things/gloo.c
Смысл первой колонки остается таким же. Вторая колонка показывает номер рабочей ревизии артефакта. Третья и четвертая колонки показывают номер ревизии последнего изменения файла и имя пользователя внесшего эти изменения соответственно.
Все, перечисленные выше, команды работают без обращения к серверу. Для получения информации об актуальности артефактов используется ключ --show-updates
(-u
).
$ svn status --show-updates --verbose
M * 44 23 sally README
M 44 20 harry bar.c
* 44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
A 0 ? ? stuff/things/bloo.h
Status against revision: 46
Звездочки говорят об имеющихся на сервере обновлениях.
Для получения информации о том, какие именно были внесены изменения используется команда svn diff
:
$ svn diff
Index: bar.c
===================================================================
--- bar.c (revision 3)
+++ bar.c (working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>
int main(void) {
- printf("Sixty-four slices of American Cheese...\n");
+ printf("Sixty-five slices of American Cheese...\n");
return 0;
}
Index: README
===================================================================
--- README (revision 3)
+++ README (working copy)
@@ -193,3 +193,4 @@
+Note to self: pick up laundry.
Index: stuff/fish.c
===================================================================
--- stuff/fish.c (revision 1)
+++ stuff/fish.c (working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.
Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h (revision 8)
+++ stuff/things/bloo.h (working copy)
+Here is a new file to describe
+things about bloo.
Вывод команды представлен в универсальном формате (unified diff format).
Соответствующий патч (patch) можно получить вызвав:
$ svn diff > patchfile
Отмена изменений выполняется с помощью команды svn revert
:
$ svn revert README
Reverted 'README'
В этом случае Subversion откатит файл до оригинального состояния находящегося в административной области .svn.
Концептуально, svn revert ITEM
имеет тот же эффект, что и удаление артефакта из рабочей копии с последующим вызовом svn update -r BASE ITEM
, за исключением отсутствия необходимости соединения с сервером.
Все три команды - svn status
, svn diff
и svn revert
- выполняются без взаимодействия с сервером.
Разрешение конфликтов
$ svn update
U INSTALL
G README
C bar.c
Updated to revision 46.
В приведенном выше выводе отметка файла символом C
означает то, что локальные изменения пересекаются (overlapped) с изменениями пришедшими с сервера, и требуется ручное вмешательство.
При возникновении конфликта обычно происходят три вещи:
- Subversion выводит
C
при обновлении и запоминает, что файл находится в состоянии конфликта. - В случае если для файла возможно контекстное, построчное слияния, в содержимое вставляются особые строки - маркеры конфлита (conflict markers), которые отделяют конфликтующие области.
- Для каждого конфликтующего файла Subversion создает три дополнительных неверсионируемых файла в рабочем каталоге:
- filename.mine - файл, содержащий локальные изменения до обновления.
- filename.rOLDREV - файл, в состоянии
BASE
-ревизии, т.е. перед внесением локальных изменений. - filename.rNEWREV - версия файла, полученная от сервера при обновлении (
HEAD
-ревизия репозитория).
Subversion не позволит выполнить commit
при наличии данных файлов.
$ svn commit --message "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
Разрешение конфликта можно выполнить тремя способами:
- совместить конфликтующий текст вручную (при подсказке маркеров конфликта);
- заменить рабочий файл на один из временных;
- выполнить
svn revert <filename>
для отмены всех локальных изменений.
После разрешения конфликта необходимо уведомить об этом Subversion вызвав svn resolved
:
$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'
Данная команда удалит три временных файла, и Subversion больше не будет считать, что файл находится в состоянии конфликта.
Слияние конфликта вручную
$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread
Текст между <<<<<<<
и =======
относится к локальным изменениям, а между =======
и >>>>>>>
к изменениям полученным от сервера.
Замещение рабочего файла
$ svn update
C sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt
Редактирование по-новой
$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt
В случае отката конфликтующего файла вызов svn resolved
не требуется.
Фиксация изменений
Команда svn commit
отправляет все изменения в репозиторий. Для указания комментария к коммиту используется опция --message
(-m
).
$ svn commit --message "Corrected number of cheese slices."
Sending sandwich.txt
Transmitting file data .
Committed revision 3.
Для получения комментария из файла используется ключ --file
:
$ svn commit --file logmsg
Sending sandwich.txt
Transmitting file data .
Committed revision 4.
В случае отсутствия --message
или --file
Subversion запустит редактор по-умолчанию.
В случае наличия в репозитории изменений для закомиченного файла svn commit
завершится с ошибкой:
$ svn commit --message "Add another rule"
Sending rules.txt
svn: Commit failed (details follow):
svn: Out of date: 'rules.txt' in transaction 'g'
В этом случае необходимо запустить svn update
, разрешить возникшие конфликты и повторить вызов.
Итоги
Извлечение рабочей копии
svn co svn+ssh://<host or IP>/repo\\trunk [targetDir]
Обновление репозитория
svn update
svn up
Работа с файлами
svn add src/main/java/Application.java
svn delete src/main/java/Application.java
svn copy src/main/java/Service1.java src/main/java/Service2.java
svn move src/main/java/Service1.java src/main/java/Service2.java
Проверка текущего состояния
svn status
svn status --show-updates
svn diff
Фиксация изменений и отправка на сервер
svn commit -m "PROJ-0001 Description"
Разрешение конфликта
svn resolved src/main/java/Application.java
Наборы изменений
svn changelist PROJ-0001 src/main/java/Appliaction.java \
src/main/java/Utils.java
svn diff --changelist PROJ-0001
svn commit --changelist PROJ-0001 -m "PROJ-0001 Description"
Просмотр истории
svn log | less
svn log -r 100500 --diff | less
Откат изменений
svn merge -c -100500 .