Упрощение работы с ветками в svn при использовании трака
Бессарабов Иван, ivan@bessarabov.ru
3 августа 2009
Оглавление:
Предпосылки
Уже долгое время я используют при создании программных продуктов trac и svn.
При организации работы, которую я практикую, достаточно много рутинных операций, которые можно автоматизировать. В этой статье я кратко описываю каким образом, я немного упросил работу с помощью маленьких perl скриптов.Предупреждение: все описанное в тексте прекрасно работает для меня, но я совершенно не гарантрую, что это будет работать для других. Все данные предоставлены as is и гарантий ничего я не предоставляю.
Структура свн
У меня несколько траков и я использую следующие два алгоритма организации работы:
- Отдельный трак для каждого проекта
- Один трак для нескольких проектов
В том случае, если я использую отдельный трак для одного проекта, то стуктура каталогов в svn у меня следующая:
. |-- branches |-- tags `-- trunk
А в том случае, если у меня в одном траке находится несколько проектов, то структура чуть-чуть сложнее:
.
|-- project_a
| |-- branches
| |-- tags
| `-- trunk
|-- project_b
| |-- branches
| |-- tags
| `-- trunk
`-- project_c
|-- branches
|-- tags
`-- trunk
post-commit-hook
С помощью скрипта trac-post-commit-hook я настроил свои системы таким образом, что если в комментарии к комиту есть специальные слова, то тогда в траке выполняются некоторые дополнительные действия.
Я использую следующие 2 типа комментариев:
svn commit -m "Сделал блок параметров. See #134."
при таком коммите в комментарий тикета #134 в траке будет дописан текст "(In [102]) Сделал блок параметров. See #134."svn rm svn://192.168.1.1/test/project_a/branches/134 -m "Изменения в ветки слиты с транком. Ветку удаляю. Closes #134."
в комментарий тикета #134 будет дописан текст "(In [109]) Изменения в ветки слиты с транком. Ветку удаляю. Closes #134." и этот тикет будет закрыт.
Краткий алгоритм работы
Я организую работу по написанию программного обеспечения следующим образом. В траке в виде тикетом оформляются задачи. Например, вполне может существовать тикет #134 "Сделать программу отображение статистики отгрузок". В примере, которые я буду рассматривать, работа ведется в траке, в котором находится несколько проектов, а в svn trunk находодится /project_a/trunk
В trunk содержится стабильная версия кода.
Для решения тикета #134 необходимо выполнить следующие действия:
- Скопировать транк в ветку. Ветка называется по номеру тикета. В данном случае нужно скопировать trunk в /branches/134
- Переключится на ветку
- Написать весь необходимый код и закомиитить его
- Влить свою работу в транк
- Удалить ветку
- Закрыть тикет
Все перечисленные пункты, кроме третьего — это совершенно рутинные действия, а четко описанную рутину вполне можно автоматизировать.
Детальный алгоритм работы
Вот все комманду, которые нужно ввести, чтобы выполнить все перечисленные выше действиия:
Создать ветку
bessarabov@server:~/wc$ svn cp svn://192.168.1.1/test/project_a/trunk svn://192.168.1.1/test/project_a/branches/134 -m "Создаю ветку для работы над тикетом. See #134." Committed revision 101. bessarabov@server:~/wc$
После выполнение комманды выше создается ветка /branches/134 и в тикет #134 отписывается комментарий "(In [101]) Создаю ветку для работы над тикетом. See #134."
Переключится на ветку
bessarabov@server:~/wc$ svn sw svn://192.168.1.1/test/project_a/branches/134 At revision 101. bessarabov@server:~/wc$
Писать код
Обычно ветка — это личное пространство одного разработчика. В ветке ведетсся работа и коммитятся некоторые этапы. В ветке можно все сломать и это сломленное состояние закоммитить — в этом ничего страного, так как это никак не повлияет на работу остальных разработчиков.
При коммитах в конце комментария полезно добавлять "See #ticket_number.", чтобы на указанный тикет сразу же записывался комментарий.
bessarabov@server:~/wc$ svn commit -m "Сделал блок параметров. See #134. Sending cgi-bin/stat.pl Adding root/parameters.tt Transmitting file data .. Committed revision 102. bessarabov@server:~/wc$
После того, как код полностью говто и проверен, его нужно сливать с транком.
Влить код в trunk
Прекрасное описание объединения веток есть в svnbook .
Ниже — просто пример, без объяснений.
bessarabov@server:~/wc$ sw svn://192.168.1.1/test/project_a/trunk D root/parameters.tt U cgi-bin/stat.pl Updated to revision 107. bessarabov@server:~/wc$ svn log --stop-on-copy svn://192.168.1.1/test/project_a/branches/134 ------------------------------------------------------------------------ r107 | bessarabov | 2009-07-10 16:25:41 +0400 (Fri, 10 Jul 2009) | 1 line Полностью додел программу статистики отгрузок. See #134. ------------------------------------------------------------------------ r106 | bessarabov | 2009-07-10 14:19:09 +0400 (Fri, 10 Jul 2009) | 1 line Дописал код, система статистики отгрузок работает. Осталось лишь дописать инструкцию на странице странице и задача решена. See #134. ------------------------------------------------------------------------ r105 | bessarabov | 2009-07-09 19:03:50 +0400 (Thu, 09 Jul 2009) | 1 line Продолжил работу над системой отгрузки. See #134. ------------------------------------------------------------------------ r102 | bessarabov | 2009-07-09 18:36:17 +0400 (Thu, 09 Jul 2009) | 1 line Сделал блок параметров. See #3469. ------------------------------------------------------------------------ r101 | bessarabov | 2009-07-09 18:01:27 +0400 (Thu, 09 Jul 2009) | 1 line Создаю ветку для работы над тикетом. See #134. ------------------------------------------------------------------------ bessarabov@server:~/wc$ svn merge -r101:107 svn://192.168.1.1/test/project_a/branches/134 --- Merging r101 through r107 into '.': A root/parameters.tt U root/tests.tt bessarabov@server:~/wc$ svn ci -m "Вливаю изменения -r101:107 ветки 134 в транк. See #134." Sending cgi-bin/stat.pl Adding root/parameters.tt Transmitting file data .. Committed revision 108. bessarabov@server:~/wc$
Удалить ветку и закрыть тикет
bessarabov@server:~/wc$ svn rm svn://192.168.1.1/test/project_a/branches/134 -m "Изменения в ветки слиты с транком. Ветку удаляю. Closes #134." Committed revision 109. bessarabov@server:~/wc$
Итак, вот список всех команд, которые нужно набирать, чтобы работать с svn в соответствии с моими инструкциями. Видно, что это тихий ужас — при решении даже элементарного тикета нужно сделать очень много скучной рутинной работы.
Решение
Решение проблмы элементарное: написать крошечные скрипты, которые выполняют всю рутинную работу.
Я достаточно давно придумал, как и что должны делать скрипты, постепененнько их написал и вот, даже отписал небольшую статью с описанием моего решения.Итак, я написал 3 скрипта:
- si
- sc
- sc
si
Сокращение от "svn init".
Скрипту передается один параметр: номер тикета, над которым нужно начинать работу.
Скрипт создает ветку и переключается на нее.
Для того, чтобы скрипт смог понять, транк какого проекта нужно копировать в ветку, нужно запускать скрипт в рабочей копии того прокта, к которому относится тикет.
Пример:
bessarabov@server:~/wc$ si 134 Committed revision 101. At revision 101. bessarabov@server:~/wc$
sc
Сокращение от "svn commit".
Скрипту передается один параметр: текст комментария, с которым нужно коммитить.
Скрипту дописывает в конец комментария текст " See #ticket_number." и коммитит.
Пример:
bessarabov@server:~/wc$ sc "Сделал блок параметров." Sending cgi-bin/stat.pl Adding root/parameters.tt Transmitting file data .. Committed revision 102. bessarabov@server:~/wc$
sd
Сокращение от "svn done".
После того, как работа в ветки завершена и все закомичено, нужно вливать сделанную работу в транк.
Скрипт sd определяет начальную и конечную правку в которой происходили изменения и вливает их в транк и удаляет ветку.
Скрипту не нужно передаванить никакие параметры.
О путях
Я положил эти скрипты в /home/bessarabov/bin . Для того, чтобы я мог запускать скрипты без указания полного пути я прописал в /home/bessarabov/.bash_profile :
PATH=$PATH:/home/bessarabov/bin export PATH
Код скриптов
si
#! /usr/bin/perl
use strict;
use warnings;
# Проверяю входные параметры
unless ($ARGV[0]) {
die "Ошибка: не указана ветка\n";
}
unless ($ARGV[0]=~/^\d+$/) {
die "Ошибка: номер ветки должен быть числом\n";
}
# Переменная в которую я соберу путь к репозиторию
my $rep_path;
# Понимаю, где я нахожусь
$rep_path = `svn info|grep "URL:"`;
chomp ($rep_path);
# Я должен находится в рабочей копии, примем либо в транке, либо в ветке
$rep_path =~ s/(trunk|branches\/\d*)$//;
unless ($1) {
die "Ошибка: текущий путь не рабочая копия тракани и не ветки\n";
}
# Удаляю текст перед путем
$rep_path =~ s/^URL: //;
# Переменная с путем транка
my $trunk = $rep_path . "trunk";
# Переменная с номером тикета
my $ticket = $rep_path . "branches/" . $ARGV[0];
# Переменная в которую я буду сохранять вывод
my $result;
# Создаю ветку с именем номера тикета. В комменатрий пишу "Создаю ветку для работы над тикетом. See #ticket_number."
$result = `svn cp $trunk $ticket -m "Создаю ветку для работы над тикетом. See #$ARGV[0]."`;
print $result;
# Переключаюсь на ветку
$result = `svn sw $ticket`;
print $result;
sc
#! /usr/bin/perl
use strict;
use warnings;
# Проверяю входные параметры
unless ($ARGV[0]) {
die "Ошибка: коментарий не указан\n";
}
# Переменная в которую я сохраню номер ветки в которой я сейчас работаю
# (номер ветки равен номеру тикета)
my $ticket;
# Переменная в которую я буду сохранять вывод
my $result;
# Узнаю имя ветки
$result = `svn info|grep "URL:"`;
chomp ($result);
$result =~ /\/(\d*)$/;
unless ($1) {
die "Ошибка: рабочая копия не ветка";
}
$ticket = $1;
# Создаею ветку с именем номера тикета. В комменатрии пишиет "Создаю ветку для работы над тикетом. See #1234."
my $command = "svn ci -m '$ARGV[0] See #" . "$ticket.'";
$result = `$command`;
print $result;
sd
#! /usr/bin/perl
#Понимаю, где я нахожусь и преключаюсь на транк (т.е. убираю branches/NNN из текущего пути)
$result = `svn info|grep "URL:"`;
chomp ($result);
$result =~ /URL: (.*)branches\/(\d*)$/;
# Переменная с путем ветки, где я работаю
my $branch = "$1" . "branches/" . "$2";
# Переменная с путем транка
my $trunk = "$1" . "trunk";
# Переменная с номером тикета
my $ticket = $2;
my $result;
$result = `svn sw $trunk`;
# В эти переменные запихну минимальный и максимальный номер правки в ветке
my $rmin = 10000000;
my $rmax = 0;
$result = `svn log --stop-on-copy --xml $branch`;
foreach (split(/\n/,$result)) {
if ($_ =~ /revision="(\d*)"/) {
if ($1>$rmax) { $rmax=$1; }
if ($1<$rmin) { $rmin=$1; }
}
}
$result=`svn merge -r$rmin:$rmax $branch`;
print $result;
$result = `svn ci -m "Вливаю изменения -r$rmin:$rmax ветки $ticket в транк. See #$ticket."`;
print $result;
$result = `svn rm $branch -m "Изменения в ветки слиты с транком. Ветку удаляю. Closes #$ticket."`;
print $result;