Git/Revision walk / topo order

让提交历史先展示后代,再展示祖先

Git 的拓扑历史遍历记录尚未输出的子提交数量;只有相关子提交都已处理,父提交才进入可输出集合。

v2.46.0图处理与编译核验 2026-10-09
相关概念

工程问题#

提交时间不一定严格反映父子关系;仅按时间排序可能在一个分支的后代尚未显示时就先显示共同祖先,使历史关系难以阅读。

实际实现#

topo_walk 用 exploration 和 indegree 两个队列发现图并统计边,输出提交后递减其父提交的计数。这里计数采用加一编码:1 表示已经发现且没有未处理子提交,0 用于未初始化或已输出状态。降到 1 的父提交被加入 topo_queue。

处理流程#

  1. 解析当前遍历范围内的提交及父边,准备入度计数
  2. 选择当前可输出的提交
  3. 输出后递减各父提交的未处理子边计数
  4. 计数降到可输出状态后,把父提交加入候选集合

源码片段#

固定版本 · 原文摘录
revision.cv2.46.0 · L3867–L3880
查看原文件
3867		generation = commit_graph_generation(parent);3868		if (generation < info->min_generation) {3869			info->min_generation = generation;3870			compute_indegrees_to_depth(revs, info->min_generation);3871		}38723873		pi = indegree_slab_at(&info->indegree, parent);38743875		(*pi)--;3876		if (*pi == 1)3877			prio_queue_put(&info->topo_queue, parent);38783879		if (revs->first_parent_only)3880			return;
expand_topo_walk

当探索走到更低 generation 时补足计数,然后递减父提交入度;计数等于 1 而非 0 时入候选队列,源于该实现的加一编码。

片段是遍历循环中的父边处理部分;计数初始化、候选队列模式和 limited 分支在同一文件中。

收益与取舍#

工程分析

带来的收益

  • 保证所遍历提交中父提交不会先于其相关子提交输出
  • 可结合 generation number 分阶段探索,避免总是先完整展开历史

付出的代价

  • 合并提交、历史过滤和 first-parent 会改变本次遍历涉及的边,必须按同一规则计数
  • 不能把整体实现简单计为教科书 FIFO 队列的 O(V+E):对象解析、优先队列与历史探索均有额外成本

可比较的选择

只按提交时间降序实现与理解较直接,但时间戳不保证与提交依赖关系一致。

版本与适用范围#

对应 Git v2.46.0 在非 limited 的 topo_order 遍历中使用的 topo_walk 路径,边按子提交指向父提交理解。topo_queue 在默认图顺序模式是栈式行为,在日期排序模式有比较器,不能统一称为 FIFO 或堆。

参考来源#

源码、官方文档与原始论文
01
git · init_topo_walk / indegree_walk_step / expand_topo_walk

revision.c · init_topo_walk / indegree_walk_step / expand_topo_walk

源码v2.46.0
02官方文档v2.46.0

相关实现与比较#

4 个相关案例
5 个匹配案例