You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

脚本通过提交父节点遍历Git历史时重复访问同一提交的问题

问题分析与解决方案

首先,咱们先搞清楚为什么会出现重复访问同一提交的情况——这是Git提交历史的DAG(有向无环图)结构导致的,具体来说是合并提交的多个父节点共享祖先节点,而你的遍历逻辑没有做去重处理。

举个简单的例子:假设你有一个合并提交M,它的两个父节点是A和B,而A和B又共同以C作为父节点。当你的脚本处理M时,会把A和B加入revisions列表。先处理A的时候,会遍历到C,把C加入列表并标记为已访问。之后处理B的时候,B的父节点是C,你的代码又会把C追加到revisions列表里。等后续从列表中取出C时,它已经在visited集合里了,断言自然就触发了。

然后回答你的第二个疑问:“通过提交父节点可遍历完整历史”的假设是完全正确的,Git的提交历史确实是通过父节点串联起来的DAG,只要遍历所有父节点就能覆盖完整历史——但前提是你要处理DAG中可能出现的重复节点,避免把已经访问过(或者已经在待遍历队列里)的节点再次加进去。

修复你的脚本

要解决重复访问的问题,只需要在添加父节点到revisions列表前,先检查该节点是否已经被访问过,或者已经在待遍历队列里:

import subprocess

def parents(rev):
    args = ['git', 'rev-list', '--parents', '-n', '1', rev]
    output = subprocess.check_output(args, stderr=subprocess.PIPE).decode()
    items = output.split()
    return items[1:]  # First SHA is the ID of the revision that we passed into the command

revisions = parents('HEAD')
visited = set()
while revisions:
    rev = revisions.pop()
    assert rev not in visited, rev
    visited.add(rev)
    print(rev)
    # TODO: Do check on commit
    # 只添加未访问且不在待遍历队列的父节点
    for parent in parents(rev):
        if parent not in visited and parent not in revisions:
            revisions.append(parent)

更简单的替代方案

既然你期望的输出和git rev-list HEAD一致,其实完全可以直接调用这个命令来获取所有提交,Git已经帮你处理了DAG的遍历、去重和顺序问题,比自己实现更可靠:

import subprocess

def get_all_revisions(rev):
    args = ['git', 'rev-list', rev]
    output = subprocess.check_output(args, stderr=subprocess.PIPE).decode()
    # 分割成单个提交SHA,过滤空行
    return [sha for sha in output.strip().split('\n') if sha]

for rev in get_all_revisions('HEAD'):
    # TODO: Do check on commit
    print(rev)

内容的提问来源于stack exchange,提问作者Niklas R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:12:53