[Python] 多目录 SVN 更新工具

作者 huhamhire,暂无评论,2014年5月11日 19:52 程序实践

前一阵子遇到了一个关于 SVN 的问题,因为公司代码在权限管理上的需要,开发项目目录下的各模块分支都做了权限限制。这种情况下,在获得完整的高层级目录权限以前,同步更新多个工作目录下多个模块代码是件略麻烦的事情,特别是在除项目目录以外还有其他目录内容需要获取的时候,即使使用 svn up * 也不能很好的解决一次性更新的问题。

正所谓想着办法要偷懒是码农的天性,于是我就写了下面这样的一个 Python 脚本用来实现对于多个 SVN 本地目录的一次性更新。


 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 __author__ = 'me@huhamhire.com'
 5 
 6 import re
 7 import sys
 8 import subprocess
 9 
10 base_dir = "~/SVN/"
11 repos = ["./*", "code/*", "code/project/*"]
12 
13 for repo in repos:
14     repo_dir = base_dir + repo
15     retry = 5
16     while retry:
17         print("> Update: " + repo_dir)
18         process = subprocess.Popen(
19             ["svn", "up", repo_dir],
20             stderr=subprocess.PIPE
21         )
22         err = process.communicate()[1]
23         # error handling
24         if process.returncode:
25             sys.stderr.write(err)
26             # cleanup locked repos
27             lck_pattern = re.compile("svn: E155004: '(.+)' is already locked")
28             lck_repos = lck_pattern.findall(err)
29             for lck_repo in lck_repos:
30                 print("> Clean up: " + lck_repo)
31                 subprocess.Popen(["svn", "cleanup", lck_repo])
32             retry -= 1
33         else:
34             retry = 0

从上面的脚本中就可以看到,实现一个简单的 SVN 更新工具并不复杂。在系统已经安装了 SVN 的情况下,直接向控制台发命令即可。需要额外考虑的是,在更新过程中要能够正确处理版本库被锁等异常情况。

在实现了上面的这个最基础版本以后,我又有了新的想法。因为在实际进行 SVN 版本库更新的过程中,往往仅需要更新所使用的几个版本库目录,所以就要加上通过参数对目录进行选择的功能。同时,为了能够方便后续的进一步功能改进,我对于上面的建议脚本又进行了如下的改进:


 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 __author__ = 'me@huhamhire.com'
 5 
 6 import getopt
 7 import os
 8 import re
 9 import sys
10 import subprocess
11 
12 
13 class SvnUpdate(object):
14 
15     def __init__(self, base_dir, repo_tree):
16         self.base_dir = base_dir
17         self.repo_tree = repo_tree
18         self.branches = []
19         self._set_branches()
20 
21     def _set_branches(self):
22         self.branches = sys.argv[1:]
23 
24     def _recursive_path(self, path_tree, level=0):
25         paths = []
26         for sub_root, sub_trees in path_tree.iteritems():
27             if not level and self.branches and sub_root not in self.branches:
28                 continue
29             for sub_tree in sub_trees:
30                 if isinstance(sub_tree, dict):
31                     for sub_path in self._recursive_path(sub_tree, level+1):
32                         path = os.path.join(sub_root, sub_path)
33                         paths.append(path)
34                 elif isinstance(sub_tree, str):
35                     path = os.path.join(sub_root, sub_tree)
36                     paths.append(path)
37                 else:
38                     raise(Exception, "Wrong repository")
39         return paths
40 
41     @staticmethod
42     def __error_cleanup_locks(err):
43         lck_pattern = re.compile("svn: E155004: '(.+)' is already locked")
44         lck_repos = lck_pattern.findall(err)
45         for lck_repo in lck_repos:
46             print("> Clean up: " + lck_repo)
47             subprocess.Popen(["svn", "cleanup", lck_repo])
48 
49     def update(self):
50         for repo in self._recursive_path(self.repo_tree):
51             repo_dir = self.base_dir + repo
52             retry = 5
53             while retry:
54                 print("> Update: " + repo_dir)
55                 process = subprocess.Popen(
56                     ["svn", "up", repo_dir],
57                     stderr=subprocess.PIPE
58                 )
59                 err = process.communicate()[1]
60                 # error handling
61                 if process.returncode:
62                     sys.stderr.write(err)
63                     self.__error_cleanup_locks(err)
64                     retry -= 1
65                 else:
66                     retry = 0
67 
68 
69 if __name__ == "__main__":
70     base = "~/SVN/"
71     dir_tree = {
72         ".": ["*"],
73         "code": ["*", {"project": ["*"]}]
74     }
75     up = SvnUpdate(base, dir_tree)
76     up.update()

上面的这两段代码各有优劣。第一段代码发扬了 Python 简洁明快的传统,不过在功能上略有不足;第二段代码虽然功能上通过类的封装进行了改进,并且具有更好的扩展性,但是实现起来显然就不够简洁。当然,人的口味也各有不同,所以我还是把这两段都分享出来。

关键词:Python , SVN , 工具 DIY
登录后进行评论