【问题标题】:Finding the first commit on a branch with GitPython使用 GitPython 查找分支上的第一个提交
【发布时间】:2013-08-29 05:11:16
【问题描述】:

我正在使用 Python 和 Git-Python 编写一个 git post-receive 钩子,它收集有关推送中包含的提交的信息,然后使用摘要更新我们的错误跟踪器和 IM。在推送创建分支的情况下(即 post-receive 的 fromrev 参数全为零)并且还跨越该分支上的多个提交,我遇到了麻烦。我正在从torev 提交向后遍历父列表,但我不知道如何判断哪个提交是分支中的第一个提交,即何时停止查找。

在命令行我可以做到

git rev-list this-branch ^not-that-branch ^master

这将给我准确的this-branch 中的提交列表,没有其他的。我尝试使用Commit.iter_parents 方法复制它,该方法被记录为采用与 git-rev-list 相同的参数,但据我所知,它不喜欢位置参数,而且我找不到集合有效的关键字参数。

我阅读了 Dulwich 的文档,但不清楚它是否会与 Git-Python 做任何不同的事情。

我的(简化的)代码如下所示。当推送启动一个新分支时,它目前只查看第一个提交然后停止:

import git
repo = git.Repo('.')
for line in input:
    (fromrev, torev, refname) = line.rstrip().split(' ')
    commit = repo.commit(torev)
    maxdepth = 25    # just so we don't go too far back in the tree
    if fromrev == ('0' * 40):
        maxdepth = 1
    depth = 0
    while depth < maxdepth:
        if commit.hexsha == fromrev:
            # Reached the start of the push
            break
        print '{sha} by {name}: {msg}'.format(
            sha = commit.hexsha[:7], user = commit.author.name, commit.summary)
        commit = commit.parents[0]
        depth += 1

【问题讨论】:

    标签: python git git-post-receive gitpython dulwich


    【解决方案1】:

    使用纯Git-Python也可以做到。我还没有找到一种方法来识别一组可以一次性完成的 kwargs。但是可以简单地构造一组 master 分支的 shas,然后在要检查的分支上使用 iter_commits 以找到第一个没有出现在 parent 中的分支:

    from git import *
    
    repo_path = '.'
    repo = Repo(repo_path)
    parent_branch = repo.branches.master
    examine_branch = repo.branches.test_feature_branch
    
    other_shas = set()
    for parent_commit in repo.iter_commits(rev=parent_branch):
        other_shas.add(parent_commit.hexsha)
    for commit in repo.iter_commits(rev=examine_branch):
        if commit.hexsha not in other_shas:
            first_commit = commit
    
    print '%s by %s: %s' % (first_commit.hexsha[:7],
            first_commit.author.name, first_commit.summary)
    

    如果你真的想确保排除所有其他分支上的所有提交,你可以将第一个 for 循环包装在 repo.branches 上的另一个 for 循环中:

    other_shas = set()
    for branch in repo.branches:
        if branch != examine_branch:
            for commit in repo.iter_commits(rev=branch):
                other_shas.add(commit.hexsha)
    
    • Caveat 1:第二种方法显示了第一个没有出现在任何其他分支上的提交,这不一定是这个分支上的第一个提交。如果feat_b 是从来自master 的feat_a 分支出来的,那么这将显示在feat_b 分支之后的第一个提交:feat_a 的其余提交已经在feat_b 上。
    • 警告 2:git rev-list 并且这两种解决方案仅在分支尚未合并回 master 时才有效。您实际上是在要求它列出该分支上的所有提交,而不是另一个。
    • 备注:第二种方法过于矫枉过正,需要更多时间才能完成。更好的方法是将其他分支限制为已知合并分支的列表,如果您拥有的不仅仅是 master。

    【讨论】:

      【解决方案2】:

      我只是在玩德威,也许有更好的方法来做到这一点(使用内置助行器?)。假设只有一个新分支(或多个没有共同点的新分支):

      #!/usr/bin/env python
      import sys
      from dulwich.repo import Repo
      from dulwich.objects import ZERO_SHA
      
      
      def walk(repo, sha, shas, callback=None, depth=100):
          if not sha in shas and depth > 0:
              shas.add(sha)
      
              if callback:
                  callback(sha)
      
              for parent in repo.commit(sha).parents:
                  walk(repo, parent, shas, callback, depth - 1)
      
      
      def reachable_from_other_branches(repo, this_branch):
          shas = set()
      
          for branch in repo.refs.keys():
              if branch.startswith("refs/heads") and branch != this_branch:
                  walk(repo, repo.refs[branch], shas)
      
          return shas
      
      
      def branch_commits(repo, fromrev, torev, branchname):
          if fromrev == ZERO_SHA:
              ends = reachable_from_other_branches(repo, branchname)
          else:
              ends = set([fromrev])
      
          def print_callback(sha):
              commit = repo.commit(sha)
              msg = commit.message.split("\n")[0]
              print('{sha} by {author}: {msg}'
                    .format(sha=sha[:7], author=commit.author, msg=msg))
      
          print(branchname)
          walk(repo, torev, ends, print_callback)
      
      
      repo = Repo(".")
      for line in sys.stdin:
          fromrev, torev, refname = line.rstrip().split(' ')
          branch_commits(repo, fromrev, torev, refname)
      

      【讨论】:

      • 谢谢 - 我没有在德威看到步行者物品,看起来我可以用其中一个很好地做到这一点。稍后将发布一些代码。
      【解决方案3】:

      这样的东西会找到第一个提交:

      x = Repo('.')
      print list(x.get_walker(include=[x.head()]))[-1].commit
      

      (请注意,对于大型存储库,这将使用 O(n) 内存,请使用迭代器来解决此问题)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-12
        • 1970-01-01
        • 1970-01-01
        • 2021-06-18
        • 1970-01-01
        • 1970-01-01
        • 2015-07-28
        • 2012-12-29
        相关资源
        最近更新 更多