Subversionで外部定義(svn:externals)を利用したブランチをタグリリースする。

タグを作成するときに、外部定義(svn:externals)を含んでいると、参照先が更新されたときに外部定義部分だけ最新版になっちゃったりする。
外部定義にリビジョンを明示してあげればいいんだけど、外部定義のあるところをいちいち手動で書き換えるのはめんどくさい。
サーバ側のフックスクリプト(pre-commit, post-commit等)でなんとかできないか調べてみたけど難しそうだったので、クライアント側でタグをcommitする際にsvn:externalsに参照するリビジョンを埋め込むスクリプトを組んでみた。<使用方法>

  1. タグリリースするフォルダと同じ階層にスクリプトを置く
  2. Terminalでスクリプトを実行する
  3. タグの元になるフォルダ名を入力する
  4. タグ名を入力する
  5. コミットログを入力する

<スクリプトの内容>

  1. タグの元になるフォルダをsvn copyする
  2. コピーしたフォルダのリポジトリURLの中のbranchesをtagsに置換して、svn switchする(このときにリポジトリのHEADのリビジョン番号を取得)
  3. コピーしたフォルダに含まれるsvn:externalsを検索し、2で取得したリビジョン番号を埋め込む
  4. コピーしたフォルダをcommitする
#!/usr/local/bin/perl

use strict;
use warnings;
use Encode;

my $os  = 'linux';              # windowsで利用する場合はコメントアウトしてください。
#my $os = 'windows';    # linuxで利用する場合はコメントアウトしてください。

# 出力文字コードの選択
my $output_code;
if ($os eq 'windows'){
       $output_code    = 'cp932';
}
else {
       $output_code    = 'utf8';
}

# ブランチをコピーする
       opendir my $current_dir, './';
       print encode($output_code, decode('utf8', "フォルダ一覧:\n"));
       while (my $file = readdir($current_dir)){
               next if ($file =~ /^\./);
               if (-d $file){
                       print "$file\n";
               }
       }
       closedir($current_dir);

       # コピー元フォルダの入力
       my $src;
       while (1){
               print encode($output_code, decode('utf8', "タグリリースする元フォルダ名を入力してください:"));
               $src    = <STDIN>;
               chomp($src);
               if (-d $src){
                       last;
               }
               else {
                       print encode($output_code, decode('utf8', "指定のフォルダが存在しません。\n"));
               }
       }

       # タグ名の入力
       my $dst;
       print encode($output_code, decode('utf8', "タグ名を入力してください           :"));
       $dst    = <STDIN>;
       chomp($dst);

       # copyの前のupdate確認(とりあえずしてない)

       # svn copyの実施
       print encode($output_code, decode('utf8', "svn copyを行います。\n"));
       system("svn copy $src ./$dst");

       # 切り替え先URLの作成
       chdir($dst);
       open my $svninfo, '-|', 'svn info' or die $!;
       my $changed_url;
       while(my $line = <$svninfo>){
               chomp $line;
               if($line =~ /^URL:\s(.*)/){
                       $changed_url    = $1;
                       $changed_url    =~ s/branches/tags/;
                       print encode($output_code, decode('utf8', '次のURLにタグを作成します: ' . $changed_url . "\n"));
               }
       }
       close($svninfo);

       # svn switchの実施
       chdir("../");
       open my $svnswitch, '-|', "svn switch $changed_url $dst" or die $!;
       my $rev;
       while(my $line = <$svnswitch>){
               chomp $line;
               Encode::from_to($line, $output_code, 'utf8');
               if($line =~ /^リビジョン\s([\d]*)\sです。/){
                       $rev = $1;
               }
       }
       print encode($output_code, decode('utf8', 'svn:externalsを次のrevに固定します: ' . $rev . "\n"));
       close($svnswitch);

# externalsを書き換える
       # svn:externalsの取得
       chdir($dst);
       open my $svnpropget, '-|', 'svn propget svn:externals -R' or die $!;
       while(my $line = <$svnpropget>){
               chomp $line;
               if($line =~ /(.*)\s-\s(.*)\s(.*)/){
                       my $target      = $1;
                       my $param       = "$3 -r$rev $2";
                       # 何故か、リビジョン指定なしとリビジョン指定ありで、SRC/DSTの記述順が逆
                       # リビジョン指定なし: SRC DST
                       # リビジョン指定あり: DST -r rev SRC
#                       print '$target: ' . $target . "\n";
#                       print '$param : ' . $param  . "\n";
                       system("svn propset svn:externals \"$param\" $target");
               }
       }
       close($svnpropget);

# リポジトリにコミットする
       chdir("../");
       print encode($output_code, decode('utf8', "コミットログを入力してください: "));
       my $commit_log  = <STDIN>;
       chomp($commit_log);
       system("svn commit $dst -m $commit_log");

__END__

以下、いろいろ参考にさせてもらったところ。(結果的に関係なかったものも含む)



この数の多さが、検討中の迷走ぶりを表している……。