脚本通过提交父节点遍历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
相关产品推荐
相关产品推荐

