章
目
录
软件测试的代码覆盖率是衡量测试是否达标的重要标准之一。前段时间,我用fastapi框架重新编写了覆盖率统计服务。这个服务的核心操作,就是先获取全量代码覆盖率,再通过diff操作来统计增量代码覆盖率。而要进行diff操作,就不可避免地要和Git打交道。那么,如何在Python中操作Git呢?今天就来给大家分享一下。
一、认识GitPython库
在Python环境里操作Git,有个超好用的工具——GitPython库。它提供了一系列方法,让我们能轻松访问和操作Git仓库。有了它,原本复杂的Git操作在Python里变得简单许多。
安装GitPython库
安装GitPython很简单,使用pip包管理器就行,在命令行输入下面的命令:
pip install gitpython
这样就把GitPython库安装到我们的开发环境中啦。
二、GitPython库的基础操作
克隆仓库
在使用GitPython操作Git仓库前,得先把远程仓库克隆到本地。下面是示例代码:
from git import Repo
remote_url = 'https://github.com/username/repository.git'
local_path = '/path/to/local/repository'
Repo.clone_from(remote_url, local_path)
在这段代码里,我们先设定好远程仓库的URL和本地存储路径。然后调用Repo.clone_from()
方法,它就会把远程仓库克隆到我们指定的本地路径下。
不过,如果本地仓库已经存在,就不用再克隆了。可以像下面这样加个判断:
remote_url = 'https://github.com/username/repository.git'
local_path = '/path/to/local/repository'
if os.path.exists(local_path):
repo = Repo(local_path)
else:
repo = Repo.clone_from(remote_url, local_path)
这段代码先检查本地路径是否存在仓库,如果存在,直接实例化;不存在的话,再进行克隆操作。
获取分支信息
克隆好仓库后,有时我们需要获取仓库里的分支信息。代码如下:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
branches = repo.heads
for branch in branches:
print(f"Branch: {branch.name}, Commit ID: {branch.commit}")
这里通过repo.heads
获取所有分支,然后遍历这些分支,打印出每个分支的名称和对应的commit ID ,这样就能清楚地知道仓库里都有哪些分支以及它们的提交记录了。
提交更改
在开发过程中,提交代码更改是常有的事。用GitPython提交更改的代码如下:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
repo.index.add(['file1.py', 'file2.py'])
repo.index.commit("Commit message")
这段代码先指定仓库路径并实例化仓库对象,然后用repo.index.add()
方法把需要提交的文件添加到暂存区,最后用repo.index.commit()
方法提交更改,并附上提交信息。虽然在代码覆盖率统计中没用到这个功能,但了解一下还是很有必要的。
查看状态
想知道仓库当前的状态,比如哪些文件被修改了、哪些还没被暂存等,可以用下面的代码:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
repo_status = repo.git.status()
print(repo_status)
这段代码通过repo.git.status()
获取仓库状态,和在命令行里使用git status
命令的效果是一样的,执行后就能看到仓库的详细状态信息了。
切换分支和合并代码
在开发中,切换分支和合并代码也是常见操作。用GitPython实现的代码如下:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
repo.git.checkout('develop')
repo.git.merge('feature-branch')
这里先使用repo.git.checkout('develop')
切换到develop
分支,然后用repo.git.merge('feature-branch')
把feature-branch
分支合并到当前分支。
获取远程仓库的最新变更
为了保证本地代码和远程仓库同步,我们需要获取远程仓库的最新变更,代码如下:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
repo.fetch()
执行repo.fetch()
后,会从远程仓库获取最新的提交记录、分支和文件,但这些变更不会自动合并到本地代码。也就是说,执行完git fetch
后,还需要手动进行合并或者基于远程分支重新开发。
diff操作
在统计增量代码覆盖率时,diff操作非常关键。下面的代码展示了如何使用GitPython进行diff操作:
from git import Repo
repo_path = '/path/to/your/git/repository'
repo = Repo(repo_path)
repo.git.diff(base_branch, current_branch)
git.diff()
方法用来比较base_branch
和current_branch
这两个分支之间的差异,通过它就能知道两个分支的代码有哪些不同。
三、实战案例:统计增量覆盖率的核心代码
下面这段代码是我在统计增量覆盖率时的核心部分:
def get_diff(self, current_branch: str, base_branch: str = "origin/master"):
"""获取版本之间代码差异"""
diff = self.repo.git.diff(base_branch, current_branch).split("\n")
ret = {}
file_name = ""
diff_lines = []
current_line = 0
for line in diff:
if line.startswith("diff --git"):
if file_name != "":
ret[file_name] = diff_lines
file_name = re.findall("b/(\S+)$", line)[0]
diff_lines = []
current_line = 0
elif re.match("@@ -\d+,\d+ +(\d+),\d+ @@", line):
match = re.match("@@ -\d+,\d+ +(\d+),\d+ @@", line)
current_line = int(match.group(1)) - 1
elif line.startswith("-"):
continue
elif line.startswith("+") and not line.startswith("+++"):
current_line += 1
diff_lines.append(current_line)
else:
current_line += 1
ret[file_name] = diff_lines
return ret
这个get_diff
方法接收current_branch
和base_branch
两个参数,current_branch
表示当前分支,base_branch
表示基准分支,默认是origin/master
。
方法内部先调用self.repo.git.diff(base_branch, current_branch)
获取两个分支之间的代码差异,并按行拆分成列表。然后用一个字典ret
来存储差异信息,键是文件名,值是差异的行号列表。
在遍历差异行时,会根据行的内容进行不同处理:
- 遇到以
diff --git
开头的行,说明开始处理新文件的差异,这时就提取文件名。 - 匹配到以
@@ -\d+,\d+ +(\d+),\d+ @@
格式的行,提取新版本代码的起始行号。 - 以
-
开头的行,表示该行在基准分支中有但当前分支中没有,直接忽略。 - 以
+
开头且不以+++
开头的行,代表当前分支新增的行,记录行号并添加到差异行列表。 - 其他情况,行号递增。
最后把最后一个文件的差异行信息添加到字典并返回,这样就能得到两个版本之间详细的代码差异信息了。
GitPython真的给Python操作Git带来了极大的便利,让我顺利实现了增量代码覆盖率的统计工作。这里展示的只是我在工作中用到的部分GitPython方法,它还有很多其他实用的功能,大家可以去官方文档(gitpython.readthedocs.io/en/stable/)深入了解。希望今天的分享能帮助到大家哦!