hg-git核心架构剖析:覆盖层与仓库子类化完整指南
【免费下载链接】hg-gitmercurial to git bridge, pushed to directly from the hg-git plugin in Hg项目地址: https://gitcode.com/gh_mirrors/hg/hg-git
hg-git是一款连接 Mercurial(Hg)与 Git 两大版本控制系统的桥接插件,让你能用一条hg push/hg pull命令直接与 Git 服务器同步。它之所以能实现"无损双向转换",全靠两大核心机制支撑:overlay 覆盖层(hggit/overlay.py)与仓库子类化(hggit/hgrepo.py)。本文带你从零读懂它们的设计思想与协作方式。
一图看懂 hg-git 的工作哲学 💡
在深入代码前,先记住三条底层原则(来自 DESIGN.txt):
- 数据只以 Hg 原生格式存储,Git 目录只是"可丢弃的缓存",随时可
hg gclear清空后无损重建; - 映射表是灵魂:
git-mapfile记录 Git SHA 与 Hg SHA 的一一对应,保证两边节点 ID 可互相推导; - 纯 Python 实现,依赖 Dulwich 库而非 Git 命令行,因此无需本机安装 Git。
理解了"Git 只是缓存、映射表是核心",后面两个机制就都好懂了。
仓库子类化机制:不改源码的"热补丁"
为什么要给仓库"套壳"?
Mercurial 的仓库对象(localrepository)定义了pull、push、tags等核心行为。hg-git 不想侵入式修改这些方法,而是选择动态生成一个子类,再悄悄替换掉仓库实例的类,从而劫持关键行为。
generate_repo_subclass 的运行时魔法
插件在 hggit/init.py 的reposetup钩子里执行了两行关键代码:
klass = hgrepo.generate_repo_subclass(repo.__class__) repo.__class__ = klass它拿到仓库原本的类,动态派生出hgrepo子类,然后把实例的__class__直接指向它。这个"套壳"后的仓库,在 hggit/hgrepo.py 中重写了四组行为:
| 重写方法 | 作用 |
|---|---|
pull/push | 把 Hg 的拉取/推送转发给 Git 处理器 |
findoutgoing | 计算推送时本地领先远端的提交 |
_findtags/tags | 把 Git 远端分支、标签"伪装"成 Hg 标签 |
githandler | 缓存式的 Git 操作入口(见下) |
githandler:通往 Git 世界的唯一入口 🚪
子类里最关键的成员是githandler属性(hggit/hgrepo.py#L60-L66),它按需创建一个GitHandler实例(hggit/git_handler.py#L98)。整个插件的导入、导出、推送、书签同步,都汇聚到这个处理器里。可以说,仓库子类化负责"接线",GitHandler 负责"干活"。
overlay 覆盖层:一个"虚仓库"的障眼法
为什么需要覆盖层?
当你执行hg pull或预览 incoming 时,Git 的提交已经下载下来、却还没写入 Hg。此刻 Hg 原生的 changelog / manifest 里根本没有这些提交,直接展示会"看不见"它们。overlay 覆盖层(hggit/overlay.py)就是为了解决这个"时间差"——它构造一个看似包含这些提交、实则只读的虚拟仓库视图。
overlayrepo:统一视图的总入口
overlayrepo(hggit/overlay.py#L331)是覆盖层的门面。它持有两个核心结构:
- revmap / nodemap:把尚未导入的 Git 提交"续编号"到 Hg 现有版本号之后,让它们获得临时的 rev 号;
- changelog / manifest:分别指向
overlaychangelog与overlaymanifestlog,提供虚拟的日志与清单。
overlayrevlog:伪造"修订日志"
overlayrevlog(hggit/overlay.py#L253)重写了parents、ancestor、node、rev等方法。它的套路是:能查到的走 Git 对象,查不到的就回落到真实的 Hg 仓库(self.base)。这种"查新回旧"的委托策略,让虚拟提交与真实提交可以无缝地混在同一条历史里。
overlaymanifest 与 overlaychangectx
overlaymanifest(hggit/overlay.py#L20):把 Git 的 tree 对象递归展开成"路径 → blob SHA"的字典,模拟 Hg 的 manifest 行为;overlaychangectx(hggit/overlay.py#L174):继承自 Hg 的changectx,把一个 Git commit 包装成"一次 Hg 变更集",暴露description()、parents()、manifest()等接口。
这两者让 Mercurial 的通用工具(diff、incoming 预览)在完全不知情的情况下,就能操作那些尚未落库的提交。
两大机制如何分工协作 🔗
它们并非平行,而是各司其职:
| 使用场景 | 触发机制 | 说明 |
|---|---|---|
hg pull/hg push | 仓库子类化 | 子类把请求转给githandler |
| 预览 incoming(未导入提交) | overlay 覆盖层 | 构造overlayrepo虚拟视图 |
| SHA 互相推导 | git-mapfile | 双向映射表 |
具体地,GitHandler.getremotechanges(hggit/git_handler.py#L390-L408)在拉取远端后,正是b = overlayrepo(self, commits, refs)这一行把覆盖层接进了 Hg 的 incoming 流程——子类化负责"何时调用",overlay 负责"看到什么"。
关键文件速查
- 覆盖层实现:hggit/overlay.py
- 仓库子类工厂:hggit/hgrepo.py
- 核心处理器:hggit/git_handler.py
- Git 对端仓库(peer):hggit/gitrepo.py
- 插件装配入口:hggit/init.py
- 设计文档:DESIGN.txt
总结
hg-git 的优雅之处在于**"不修改、只包装"**:
- 仓库子类化在运行时给仓库"套壳",把 push/pull/tags 等关键行为劫持到 GitHandler;
- overlay 覆盖层用一个只读的虚拟视图,填补了"Git 已下载、Hg 未落库"的时间差,让标准 Hg 工具能直接预览这些提交;
- 两者以
git-mapfile映射表为纽带,共同实现 Git ↔ Hg 的无损双向同步。
读懂这两个机制,你就掌握了 hg-git 架构的骨架 🎯。
【免费下载链接】hg-gitmercurial to git bridge, pushed to directly from the hg-git plugin in Hg项目地址: https://gitcode.com/gh_mirrors/hg/hg-git
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考