多人团队开发中,通常我们需要对提交的代码进行格式统一化,或者对提交的代码进行预编译,只有编译通过的代码才能上传到服务器。钩子脚本(hooks)正是实现这些功能的工具。
hooks 是什么?
根据特定的 SVN 操作(如提交(commit)、加锁(get-lock)和释放锁(release-lock)等),SVN 会在处理这些操作时调用对应的 hooks。这些 hooks 可以由用户自行编写,实现特定的功能。
hooks 可以在服务器上使用,也可以在每个程序员的客户端上使用。服务器端的 hooks 具有完整的功能,而客户端只有部分功能(Git)或者不能使用钩子(SVN),因此下面主要研究服务端 hooks 的使用。
hooks 脚本
打开一个 SVN 代码管理仓库的服务器文件夹(或者自己新建一个服务器文件夹)可以看到在 hooks 文件夹下有如下 hooks 模板文件:
post-commit.tmpl
post-lock.tmpl
post-revprop-change.tmpl
post-unlock.tmpl
pre-commit.tmpl
pre-lock.tmpl
pre-revprop-change.tmpl
pre-unlock.tmpl
start-commit.tmpl
未来使用这些脚本,在 Windows 系统中需要将后缀修改为.bat
,在 Linux 中需要修改为.sh
。我们可以在这些文件中重新编写脚本,实现特定的功能。
注意:hooks 模板文件是对应的 Linux 系统,模板文件在 Windows 下是不能运行的,比如在 pre-commit.tmpl 中,获取传入参数的命令在 Windows 下应该修改为:
SET REPOS=%1
SET TXN=%2
svnlook 命令
svnlook 命令时 SVN 提供的一个命令行工具,用于查看仓库的信息,如提交日志、修改历史和文件属性等。
常用的 svnlook 命令如下:
- svnlook changed: 显示指定版本的提交的文件和目录的变化。
- svnlook cat: 显示提交文件的具体内容。
为了简单起见,下面只介绍 hooks 最常用的一个脚本:pre-commit。
pre-commit
当客户端执行 commit 时,服务器会调用该脚本,脚本结束时返回 0 则可以提交,返回非 0 则终止此次提交。
输入参数:
- REPOS,对应仓库的路径
- TXN,对应提交的版本号
因为我对 Python 脚本比较熟悉,命令行程序敲起来头疼,所以我就在 bat 文件中调用 Python 脚本实现钩子功能了。
SET REPOS=%1
SET TXN=%2
python path_to_your_python_script.py %REPOS% %TXN%
使用 Python 脚本
使用 Python 脚本实现如下功能:
- 从服务器仓库克隆出仓库的一个完整副本
- 获取提交的目录和文件,将它们更新到副本文件夹中
- 在副本文件夹中执行程序的编译、测试等,成功返回 0,失败则返回非 0(脚本中未实现)
import os, sys
from subprocess import check_output
import shutil
repos = sys.argv[1]
txn = sys.argv[2]
temp_dir = 'E:/temp'
repo_url = ''
def checkout_repo():
if os.path.exists(temp_dir):
shutil.rmtree(directory_path)
cmd = 'svn checkout %s %s' % (repo_url, temp_dir)
check_output(cmd)
def commit_changes():
cmd = ('svnlook', 'changed', repos, '-t', txn)
out = check_output(cmd).splitlines()
for line in out:
line = line.decode()
yield line.split()
checkout_repo()
for status, path in commit_changes():
if path[-1] == "/":
target_dir = os.path.join(temp_dir, path)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
else:
cmd = 'svnlook cat -t %s %s %s' % (txn, repos, path)
file_content = check_output(cmd)
file_content = file_content.decode('utf-8')
target_filename = os.path.join(temp_dir, path)
with open(target_filename, 'w', encodeing='utf-8') as f:
f.write(file_content)
总结
SVN 钩子脚本可以在我们对仓库进行某些操作时调用一些自定义的脚本,扩展了版本管理软件功能,能够有效的提高开发的效率,保证代码质量。