スポンサーリンク

SVNで,コミット時にログの入力を強制する (Windows版subversionのサーバ側フックスクリプトの作成方法)


SVNで,コミット時にコメントの入力を強制する方法。


想定するSVNの構成:

  • サーバ側:WindowsでSubversionを使っている。
  • クライアント側:WindowsでTortoise SVN を使っている。


より良いライブラリ管理のために。


応用すれば,コミット時に色々な制約を設けることができる。(ルール違反のコミットを防止できる)


スクリプト作成

まず,SVNコミットをサーバ側でフックするスクリプトを作成

pre-commit.bat (C:\TracLight\projects\svn\<プロジェクト名>\hooks\ 内などに保存)

rem 条件を満たさないコミットをはじくためのスクリプト


rem subversionから渡される引数を格納
set REPOS=%1
set TXN=%2

rem コミットしようとしているトランザクションを検証
cscript //nologo %0\..\validate.js "%REPOS%" "%TXN%"

rem 検証結果をsubversionに返す(0ならコミット可能)
exit %ERRORLEVEL%

なお,TXNとは「transaction」の略語。※http://www.all-acronyms.com/TXN/Transaction/165309



次に,同じフォルダ上に,コミット情報の具体的な検証スクリプトを作成(WSH/JScript)

validate.js

// batから渡される引数を格納
var svn_repos          = WScript.Arguments.Unnamed(0);
var svn_transaction_id = WScript.Arguments.Unnamed(1);

// svnlookから,コミット実行時のログメッセージを取得
var ws = WScript.CreateObject("WScript.Shell");
var cmd_get_comment = "svnlook -t \""
	+ svn_transaction_id
	+ "\" log \""
	+ svn_repos
	+ "\""
;
var proc = ws.Exec( cmd_get_comment );
while( proc.Status == 0 )
{
	WScript.Sleep(100);
}
var svn_comment = proc.StdOut.ReadAll();

// ログメッセージにまともな内容が入力されている事を要求
svn_comment = svn_comment.replace( /(\r|\n|\t| | )/g, "" );
if( svn_comment.length > 0 )
{
	// 正常終了コードを返して終了
	WScript.Quit(0);
}
else
{
	// 標準エラー出力にエラーメッセージを出力(コミット者に通知される文章)
	WScript.StdErr.Write("ログメッセージが記入されていません。");

	// 異常終了コードを返して終了
	WScript.Quit(1);
}


この2つのファイルを保存するだけで,SVNコミットにログメッセージを強制できる。

何か再起動したりする必要はない。



解説:

  • svnlookは,コミットについての情報を参照するためのコマンド。
  • svnlookで, -r リビジョン という指定方法をせずに, -t トランザクション名 という指定方法をしている。
    • リビジョンとは,確定済みの情報の事である。今まさにコミットしようとしている情報は,リビジョンとして取り出してはいけない。トランザクションとして参照する。
    • 逆に,コミットしようとしているタイミングではない場合,もうトランザクションは終了しており,トランザクション名を使って何かの情報を参照することはNG。この場合は,確定済みの情報を参照するためにリビジョン番号を使う。

参考URL:SVNについて


SVNリポジトリの情報をCUIで取得する方法

svnlook コマンド一覧
http://www.ne.jp/asahi/hishidama/home...

  • svnlookコマンドでリポジトリ情報を参照できる
  • サブコマンドとして,log(ログメッセージ),author(コミッタの名前)などがある

第9章 Subversion 完全リファレンス
http://www.caldron.jp/~nabetaro/svn/s...
http://www.caldron.jp/~nabetaro/svn/s...

  • コマンドに -tオプションをつけるとトランザクションID(=コミット中の未確定の情報),-rオプションをつけるとリビジョン番号(コミットが確定済みの情報)を指定できる
  • リビジョン指定を省略した場合、最新リビジョンが対象となる

subversion 第VIII章 開発者の情報:SVNの「トランザクション」とは何か
http://www.exacteye.com/svn/svn.devel...

  • トランザクションを邪魔するもの
    • Subversionのトランザクションという概念は、データベースのトランザクション類似。両方とも、不可分性と、分離性を持つ。
    • Subversionのトランザクションは、ファイルやディレクトリに対するいくつかの修正操作を含む。

コミット前のフックスクリプトの作り方

pre-commitフックを作ってみる。
http://d.hatena.ne.jp/masakas/2005012...

  • tags配下のディレクトリだけにコミットを許可し,trunk内にコミットさせないようにするスクリプト
  • linux上でbashを利用して作成

チケットに関するコメントを必須にする
http://d.hatena.ne.jp/nakaji999/20091...

  • コミット時のログメッセージに,関連するtracのチケット番号を入力必須にするスクリプト
  • Windows上でbash.exeを介してbash + pythonで作成

コミットログの入力を強制させる
http://www.betatechnology.jp/pp/index...

  • コミット時のログメッセージを入力必須とする。空の場合はコミットをはじく。
  • linux上でbashを利用してスクリプトを作成

pre-commit によるコミットの拒否の方法
http://www.asahi-net.or.jp/~iu9m-tcym...

  • その他,サーバ側フックスクリプトの種類
  • start-commit, pre-commit, post-commitの3種類がある

参考URL:バッチファイルについて

WSHからbat側にエラーコードを返す方法

WSHからコマンドラインにエラーコードを返すには
http://wsh.style-mods.net/topic7.htm

  • wsh側からWScript.Quit(1) でエラーコード1が返る
  • コマンドプロンプト側で %ERRORLEVEL% により,返ってきたエラーレベルを捕捉できる


WSHから標準エラー出力に文字列を出力する方法

Windows Scripting Host 2.0 標準入出力・エラー出力
http://homepage3.nifty.com/aya_js/wsh...

  • WScript.StdErr.Write()


batファイルが標準エラー出力を扱うには

http://www.ne.jp/asahi/hishidama/home...
http://d.hatena.ne.jp/junjun777/20080...

  • 「batファイルそのものから標準エラー出力する」という方法はない。
  • 「標準エラー出力されたものをbatが受け取ってどこかのファイルに吐き出す」事は可能。(ファイル・ディスクリプタ2番を使う)

その他

DOSのバッチファイルでカレントフォルダを取得するには?
http://okwave.jp/qa/q1421822.html

  • %0\.. で,バッチファイルの存在するフォルダを表す事ができる

補足:コマンドプロンプトだけで何とかしようとした努力の残骸


特定のリビジョンのログメッセージを表示するコマンド

svnlook -r リビジョン番号 log リポジトリのパス


同じ事を複雑に実行する方法(結果を変数に格納したかった)

for /F "usebackq" %i in (`svnlook -r リビジョン番号 log リポジトリのパス`) do @echo %i


コマンドプロンプトで,文字列をデリミタ区切りに分解する

for /F "delims=- tokens=1,2,3" %i in ("a-b-c") do echo %i,%j,%k

 -> a,b,c


環境変数をデリミタ区切りで分解する

for /F "delims=\ tokens=1,2" %i in ("%WINDIR%") do echo %i,%j

 -> C:,WINDOWS


コミット実行時に,現在のリビジョン番号をテキストファイルに格納するフックスクリプト

set REPOS=%1
set TXN=%2

for /F "delims=- tokens=1,2" %%i in ("%TXN%") do @set REVISION=%%i

echo REVISION=%REVISION% > C:\hoge.txt

exit 0

補足2

いちおう,コミット内容のソースコードもフックスクリプトから参照できる。

下記は,特定のパスのソースコード内で,特定のキーワードの利用を禁じる例。


// batから渡される引数を格納
var svn_repos          = WScript.Arguments.Unnamed(0);
var svn_transaction_id = WScript.Arguments.Unnamed(1);


// svnlookから,コミットによって生じる予定の差分を取得
var ws = WScript.CreateObject("WScript.Shell");
var cmd_get_diff = "svnlook -t \""
	+ svn_transaction_id
	+ "\" diff \""
	+ svn_repos
	+ "\" | findstr /b \"+\" "
;
var proc = ws.Exec( cmd_get_diff );
while( proc.Status == 0 )
{
	WScript.Sleep(100);
}
var svn_diff = proc.StdOut.ReadAll();
var svn_diff_lines = svn_diff.split( "\r\n" );


// 特定のパスに存在するソースコードが変更されたか
if( svn_diff.match( /\+\+\+.*\/my_project\/app\/models\/.*\.rb/i ) )
{
	// Railsのモデルクラスが更新された場合

	// 全体のdiffを1行ずつ検査
	var looking_model_flag = false;
	for( var i = 0; i < svn_diff_lines; i ++ )
	{
		var line = svn_diff_lines[i];
		
		// モデルに関する差分が始まったところか
		if( line.match( /\+\+\+.*\/my_project\/app\/models\/.*\.rb/i ) )
		{
			looking_model_flag = true;
		}
		else
		// モデル以外のファイルに関する差分が始まったところか
		if( line.match( /\+\+\+.*/i ) )
		{
			looking_model_flag = false;
		}
		
		// モデル内で「params」という語を使っているか
		if(
			looking_model_flag
			&&
			line.match( /^[^#]*params/i )
		)
		{
			// 標準エラー出力にエラーメッセージを出力(コミット者に通知される文章)
			WScript.StdErr.Write("HTTPパラメータの解析はコントローラ層で行なってください。");

			// 異常終了コードを返して終了
			WScript.Quit(1);
		}
	}
}


// 正常終了
WScript.Quit(0);

一応動作するが,コミット量や回数が多い場合に問題が発生する(フックスクリプト実行のプロセスが残ったままになったりする)
ので利用していない。


参考URL:


特定のコミットによって発生した差分をGNU diff形式で表示する

svnlook -r リビジョン番号 diff プロジェクトのパス | findstr /b "+" | findstr /b /v "+++"
  • findstrのオプション:
    • bは行頭の意
    • vは除外の意]
  • diffの仕様:
    • +++ で始まる行はファイル名
    • + で始まる行は更新後のファイル内容

svnlook diff
http://www.caldron.jp/~nabetaro/svn/s...


Version Control with Subversion
http://www.linuxtopia.org/online_book...

特定のコミットによって変更されたファイルの一覧を表示する

svnlook -r リビジョン番号 changed プロジェクトのパス