連絡先 トップに戻る 最初に戻る 前に戻る 次へ進む
$Date: 2017-05-06 22:21:48 +0900 (2017/05/06 (土)) $
$Revision: 1117 $

pre-commit によるコミットの拒否の方法

概要

例えば ncb ファイルや obj ファイルなどのビルド時に生成される一時ファイルなど Subversion にコミットしたくないファイルをコミットされるのを防ぎたい場合に サーバー側で拒否するための仕組みを説明する。

参考リンク

設定方法の流れ

フックスクリプト

Subversion はコミットを行う前および後にユーザー (Subversion サーバー管理者) が 実行させたい処理をカスタマイズできるようにフックスクリプトという仕組みを用意 している。

コミット関連のフックスクリプトの種類

呼び出し順に以下のスクリプトが用意されている。
  1. start-commit
  2. pre-commit
  3. post-commit

コミット関連のフックスクリプトの動き

  1. start-commit hook スクリプト が呼ばれる
  2. start-commit が終了コード 0 を返す
  3. Subversion がコミット用のトランザクションを作成する
  4. pre-commit hook スクリプト が呼ばれる
  5. pre-commit が終了コード 0 を返す
  6. Subversion がコミットを実行する
  7. post-commit hook スクリプト が呼ばれる
  8. post-commit が終了する (コミットは完了しているのでここで何をしてもコミットは拒否できない。バックアップとかメール通知とかをする)

pre-commit

コミットされようとしている変更内容 (トランザクション) を調べて変更内容の 中にコミットしたくない修正が含まれていない場合に 0 以外の終了コードを 返すことでコミットを中断させる。 今回の目的ではこのスクリプトを使う。

pre-commit の前提知識

実際の例1

スクリプトの先頭のコメントにあるような pre-commit フックスクリプトを別に用意するか または Unix 系の OS の場合、スクリプトを pre-commit という名前で保存する必要がある。

check-commit-changed.pl
#!/usr/bin/perl

###############################################################################
#
#	check-commit-changed.pl
#
#	svnlook changed を利用して意図していないファイル/ディレクトリのコミットを
#	拒否するスクリプト。pre-commit (pre-commit.bat) から呼び出すことが前提
#
#	pre-commit.bat の例
#		set REPOS=%1
#		set TXN=%2
#
#		set PATH=C:\Program Files\Subversion\bin
#		c:\Perl\bin\perl.exe %REPOS%\hooks\check-commit-changed.pl %REPOS% %TXN%
#		exit %ERRORLEVEL%
#
#	pre-commit の例 (for UNIX)
#		#!/bin/sh
#		REPOS=%1
#		TXN=%2
#		
#		set PATH=/usr/bin
#		/usr/bin/perl $REPOS/hooks/check-commit-changed.pl $REPOS $TXN || exit 1
#
#	スクリプトの動作原理
#		1. svnlook を以下のコマンド引数で呼び出す
#			svnlook changed -t トランザクション名 リポジトリパス
#		2. svnlook の出力を解析する
#		3. 意図していない修正があった場合 STDERR にメッセージを出力して終了コード1 を返す
#
###############################################################################


# !!! 適宜書き換えて !!!
# svnlook の絶対パスを指定する
# または呼び出し側の pre-commit または pre-commit.bat で環境変数 PATH に
# svnlook のパスを設定しても OK
$SVNLOOK = "svnlook";


# pre-commit (pre-commit.bat) からの引数
$REPOS   = $ARGV[0];
$TXN     = $ARGV[1];

# debug オプション
$debug   = 0;

# svnlook のコマンドライン引数
$svnlook_cmd = "$SVNLOOK changed  -t $TXN $REPOS";

# svnlook を(パイプで)実行
open IN, "$svnlook_cmd |";

@svnlook_output = ();
$veto_files = 0;
while( $data = <IN> )
{
	push @svnlook_output, $data;

	#	svnlook changed の出力フォーマット
	#
	#	参考: http://svn.apache.org/repos/asf/subversion/trunk/subversion/svnlook/main.c : print_changed_tree
	#	参考: http://svn.apache.org/repos/asf/subversion/trunk/subversion/svnlook/main.c : print_changed_tree
	#
	#	XYZ filepath     (from node:rXXX)
	#
	#	X = "A", "D", "U", "_"
	#	Y = " ", "U"
	#	Z = " ", "+"
	#
	#	" " は "_" で表すとする
	#		1. A__ filepath
	#		2. A_+ filepath     (from copynode:rXXX)
	#		3. D__ filepath
	#		4. U__ filepath
	#		5. UU_ filepath
	#		6. _U_ filepath
	#	
	#		1列目
	#			A → ファイル or ディレクトリの追加
	#			D → ファイル or ディレクトリの削除
	#			U → ファイル or ディレクトリの更新
	#
	#		2列目
	#			U → 属性の更新
	#
	$file = $data;
	$file =~ s!^....!!;			# 先頭の4文字を消す
	$file =~ s!\(from .+\)!!;	# (from copynode:rXXX)を消す
	$file =~ s!\s+$!!;			# 末尾の空白を消す

	if( $data =~ /^D/ )
	{
		# ファイル or ディレクトリ削除は許可
		next;
	}

	# ファイルパスを元にコミットの可否を判断する
	if( !is_allowed_to_commit( $file ) )
	{
		print STDERR "We are NOT allowed to commit temporary files ($file)\n";
		$veto_files++;
	}
}
close IN;

if( $veto_files > 0 )
{
	print STDERR "You tried to commit $veto_files files or dirs which are NOT allowed to.\n";
	
	if( $debug )
	{
		print STDERR "svnlook output is\n";
		print STDERR @svnlook_output;
	}
	exit 1;
}

exit 0;


############################################################################3
#
#	ファイルパスを元にコミットを許可するか拒否するか判断する関数
#
#	戻り値
#		1: コミットを許可する
#		0: コミットを拒否する
#
#	!!! 適宜書き換えて !!!
############################################################################3

sub is_allowed_to_commit
{
	my $file = shift;

	if( $file =~ /obj(fre|chk)_(\w+)/ )	# object dir check
	{
		return 0;
	}
	elsif( $file =~ /build(fre|chk)_(\w+)\.(log|err|wrn)/ )	# build log
	{
		return 0;
	}
	elsif( $file =~ /(\w+)\.(\w+)\.user$/ )	# build log
	{
		return 0;
	}
	elsif( $file =~ /_objects\.mac$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.ncb$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.opt$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.plg$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.suo$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.obj$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.res$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.exp$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.idb$/ )
	{
		return 0;
	}
	elsif( $file =~ /\.ilk$/ )
	{
		return 0;
	}
	elsif( $file =~ /mt\.dep$/ )
	{
		return 0;
	}
	elsif( $file =~ /(vc(\d+))\.pdb$/ )
	{
		return 0;
	}
	elsif( $file =~ /BuildLog\.htm$/ )
	{
		return 0;
	}
	return 1;
}

コマンド出力例

E:\tmp\pre-commit\work>svn ci -m "test"
追加しています              test test test.ncb
追加しています              test.ncb
追加しています              test.obj
追加しています              test.opt
追加しています              test.plg
追加しています              test.suo
ファイルのデータを送信しています ...................................................................................................
svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: Commit blocked by pre-commit hook (exit code 1) with output:
We are NOT allowed to commit temporary files (trunk/test test test.ncb)
We are NOT allowed to commit temporary files (trunk/test.ncb)
We are NOT allowed to commit temporary files (trunk/test.obj)
We are NOT allowed to commit temporary files (trunk/test.opt)
We are NOT allowed to commit temporary files (trunk/test.plg)
We are NOT allowed to commit temporary files (trunk/test.suo)
You tried to commit 6 files or dirs which are NOT allowed to.