この記事ではPythonでpython-svnを用いてSubversionのコミットログを解析する方法について解説する。
ここでは2.7でおこなっているが、Python 3.3, 3.2 と2.7のライブラリが提供されている。
インストール
以下のいずれかの方法でバイナリ―をインストールする
Unix系
sudo apt-get install python-svn
Windows、MACOS
下記からダウンロード
http://pysvn.tigris.org/project_downloads.html
ログの取得のサンプルと説明
以下のサンプルは指定のSubversionのレポジトリの履歴を表示するものである。
import pysvn
import time
import sys
from collections import defaultdict
class SvnRepController:
def __init__(self,url_or_path,user,passwd):
self.client = pysvn.Client()
self.url_or_path = url_or_path
self.user = user
self.passwd = passwd
self.client.callback_get_login = self.get_login
def get_login(self, realm, username, may_save):
return True, self.user,self.passwd, False
def log(self):
return self.client.log(self.url_or_path, discover_changed_paths=True)
if __name__ == '__main__':
argvs = sys.argv
argc = len(argvs)
if(argc != 4):
sys.stderr.write( 'Usage:\n python %s url_or_path user pass' % argvs[0] )
quit()
url_or_path = argvs[1]
user = argvs[2]
passwd = argvs[3]
client = SvnRepController(url_or_path,user,passwd)
logs = client.log()
for log in logs:
print ("RevNo:%d" % (log.revision.number))
print ("Author:%s date:%s" % (log.author, time.ctime(log.date)))
print (log.message)
for p in log.changed_paths:
print (" %s" % dict(p))
以下のようにして実行できる。
python svnlog.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "user名" "パスワード"
手順を説明する。
(1) pysvn.Client()を生成する。
(2)client.callback_get_loginに認証用の関数を指定する。
この関数は認証が必要になった場合にコールバックされるものである。
戻り値に認証情報を指定する必要がある。
(3)client.log()を行うことでPySvnLogの配列が取得できる。
(4)PySvnLogはPysvnDictBaseを継承している。PysvnDictBaseはDirectoryと同様に操作できる。つまり次のようにすれば必要なプロパティが取得できる。
logs = client.log()
for log in logs:
dict(log)
ここで取得できた主な項目の説明を下記に示す。
PySvnLogの内容:
名称 | 説明 |
---|---|
revision.number | リビジョン番号 |
author | 作者 |
date | コミット時刻.数値で表現されている。 |
message | コミットメッセージ |
changed_paths | PysvnLogChangedPathの一覧 |
PysvnLogChangedPathの内容:
名称 | 説明 |
---|---|
action | 操作の種別をあらわす http://svnbook.red-bean.com/en/1.7/svn.ref.svn.c.update.html |
path | 操作を行ったパス |
copyfrom_path | コピー元のパス |
copyfrom_revision | コピー元のリビジョン |
(5)あとは必要な集計を行う。
応用
以下に応用例を示す
ファイルの更新履歴から同時に更新される頻度の高いファイルのパターンを分析することも可能である。
以下の例では単純な出現頻度であるファイルを修正した場合に発生する影響度を分析している。
import pysvn
import time
import sys
from collections import defaultdict
class FilePair:
def __init__(self,path1,path2):
self.path1 = path1
self.path2 = path2
self.count = 0
self.reliability1 =0
self.reliability2 =0
class SvnRepController:
def __init__(self,url_or_path,user,passwd):
self.client = pysvn.Client()
self.url_or_path = url_or_path
self.user = user
self.passwd = passwd
self.client.callback_get_login = self.get_login
def get_login(self, realm, username, may_save):
return True, self.user,self.passwd, False
def log(self):
return self.client.log(self.url_or_path, discover_changed_paths=True)
def getSimpleLogicalCoupling(self):
files = defaultdict(int)
ret = defaultdict(FilePair)
logs = self.log()
for log in logs:
for i in range(0,len(log.changed_paths)-1):
path1 = log.changed_paths[i]
if path1.action != "M":
# 変更以外の場合は無視
continue
files[path1.path] += 1
for j in range(i+1,len(log.changed_paths)-1):
path2 = log.changed_paths[j]
if path2.action != "M":
# 変更以外の場合は無視
continue
if path1.path == path2.path:
continue
key = "%s %s" % (path1.path , path2.path)
if( ret.has_key(key) == False ):
key = "%s %s" % (path2.path , path1.path)
if( ret.has_key(key) == False ):
ret[key] = FilePair(path1.path,path2.path)
ret[key].count += 1
for k,v in ret.items():
v.reliability1 = float(v.count) / files[v.path1]
v.reliability2 = float(v.count) / files[v.path2]
return ret
if __name__ == '__main__':
argvs = sys.argv
argc = len(argvs)
if(argc != 6):
sys.stderr.write( 'Usage:\n python %s url_or_path user pass min_count min_reliability' % argvs[0] )
quit()
url_or_path = argvs[1]
user = argvs[2]
passwd = argvs[3]
min_count = argvs[4]
min_reliablility = float(argvs[5])
client = SvnRepController(url_or_path,user,passwd)
list = client.getSimpleLogicalCoupling()
print '"Path A","Path B","Count","Count/count of A","Count/count of B","reliability"'
for k,v in sorted(list.items(), key=lambda x:x[1].count, reverse=True):
if (v.reliability1 > float(min_reliablility) or v.reliability2 > float(min_reliablility)) and v.count > int(min_count):
print '"%s","%s","%d","%f","%f","%f"' % (v.path1,v.path2,v.count,v.reliability1,v.reliability2,v.reliability1 if v.reliability1>v.reliability2 else v.reliability2)
SampleProjectのリポジトリに対してUser:admin,パスワードadminでログインをして、発生頻度が5回以上で、該当ファイルを変更する場合、最低、80%の確率で更新されているファイルの組み合わせを取得するには以下のようにすればよい。
python svnSimpleLogicalCoupling.py http://127.0.0.1/svn/SampleProject admin admin 5 0.8 > out.csv
また次のような事ができると思われる。
・コミットログを解析してどのような修正があるか分析する。
・RedmineとTracのチケット番号と関連づいている変更履歴を抜き出し、ファイル毎のバグの発生率を求める。
・人物毎のコミットの頻度
以上、変更履歴を解析することでさまざまな情報が取得できるものと期待できる。