GitPython:获取尚未应用的远程提交列表

4 投票
2 回答
3489 浏览
提问于 2025-04-17 07:11

我正在写一个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 个回答

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}')

1

我发现提交的树结构总是这样的:一个提交有两个父提交,而这两个父提交又有一个共同的父提交。这就意味着,第一个提交有两个父提交,但只有一个祖父提交。

所以,写一个自定义的迭代器来遍历这些提交,包括那些分叉的树结构,并不是太难。它的样子是这样的:

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个),以避免遍历到仓库的尽头。我还会提前检查本地仓库是否领先于远程仓库。除此之外,一切运行得很好!现在我可以提醒所有提交的作者,他们的更改正在被合并。

撰写回答