GitPython:获取尚未应用的远程提交列表
我正在写一个Python脚本,目的是获取即将通过git pull
操作应用的提交列表。使用优秀的GitPython库是个不错的开始,但git内部的细微运作让我感到困惑。现在,我手头有的代码(简化并注释过的版本)如下:
repo = git.Repo(path) # get the local repo
local_commit = repo.commit() # latest local commit
remote = git.remote.Remote(repo, 'origin') # remote repo
info = remote.fetch()[0] # fetch changes
remote_commit = info.commit # latest remote commit
if local_commit.hexsha == remote_commit.hexsha: # local is updated; end
return
# for every remote commit
while remote_commit.hexsha != local_commit.hexsha:
authors.append(remote_commit.author.email) # note the author
remote_commit = remote_commit.parents[0] # navigate up to the parent
基本上,这段代码获取了下一个git pull
中将要应用的所有提交的作者信息。这部分运行得不错,但也存在以下问题:
- 当本地提交比远程的更新时,我的代码只会打印出所有提交到第一个提交。
- 一个远程提交可能有多个父提交,而本地提交可能是第二个父提交。这意味着我的代码永远找不到远程仓库中的本地提交。
我可以处理远程仓库落后于本地仓库的情况:只需同时查看相反方向(从本地到远程),虽然代码会变得复杂,但能正常工作。但最后这个问题让我很头疼:我需要在一个(可能是无限的)树结构中找到与本地提交匹配的提交。这可不是理论上的问题:我最近的更改是一个仓库合并,这正好出现了这个问题,所以我的脚本无法正常工作。
如果能像repo.iter_commits()
那样获取远程仓库中有序的提交列表,那将大有帮助。但我在文档中没有找到如何做到这一点。我能否直接获取远程仓库的Repo对象?
有没有其他方法可以解决这个问题,还是我在用锤子钉螺丝呢?
2 个回答
我知道这个问题已经存在很久了,但我最近在做一个项目时不得不处理这个事情……
head = repo.head.ref
tracking = head.tracking_branch()
return tracking.commit.iter_items(repo, f'{head.path}..{tracking.path}')
(如果你想知道还有多少本地的提交需要推送,只需反过来做一下:head.commit.iter_items(repo, f'{tracking.path}..{head.path}')
)
我发现提交的树结构总是这样的:一个提交有两个父提交,而这两个父提交又有一个共同的父提交。这就意味着,第一个提交有两个父提交,但只有一个祖父提交。
所以,写一个自定义的迭代器来遍历这些提交,包括那些分叉的树结构,并不是太难。它的样子是这样的:
def repo_changes(commit):
"Iterator over repository changes starting with the given commit."
number = 0
next_parent = None
yield commit # return the first commit itself
while len(commit.parents) > 0: # iterate
same_parent(commit.parents) # check only one grandparent
for parent in commit.parents: # go over all parents
yield parent # return each parent
next_parent = parent # for the next iteration
commit = next_parent # start again
这个函数 same_parent()
会在有两个父提交和多个祖父提交时发出警报。现在,遍历那些未合并的提交就变得简单了:
for commit in repo_changes(remote_commit):
if commit.hexsha == local_commit.hexsha:
return
authors.append(remote_commit.author.email)
为了让内容更清晰,我省略了一些细节。我最多只返回预设数量的提交(在我的例子中是20个),以避免遍历到仓库的尽头。我还会提前检查本地仓库是否领先于远程仓库。除此之外,一切运行得很好!现在我可以提醒所有提交的作者,他们的更改正在被合并。