gitui — Rust 写的 git TUI,libgit2 直连让启动比 lazygit 快一个量级
是什么
gitui 是 Stephan Dilly(GitHub: extrawurst)用 Rust 写的 git 终端图形界面,把 status / stage / log / branches / stash 这些 git 日常动作放进一个多面板单屏 TUI。它和 lazygit 长得像、操作也类似,但底层走的是完全不同的路线:lazygit 每次都 fork 一个 git 子进程跑,gitui 直接用 libgit2 的 Rust 绑定读写仓库——两条路线的工程代价和性能差异就是这篇笔记的主题。
日常类比:
- lazygit 是装了菜单的快递员——每次你点一下,他还是要跑去 git 那家店替你下单
- gitui 是把店搬进自己家——所有 git 数据结构(object database / index / refs)直接在内存里读,不出门
数据上看:解析 Linux 内核 90 万 commit 的 log,gitui 用 24 秒、占 0.17GB 内存、10MB 二进制;同样的事 lazygit 要 57 秒、2.6GB 内存、25MB 二进制。截至 2026-05,v0.28.1,22k stars。
为什么重要
不理解 gitui 和 lazygit 的分歧,下面这些事都没法解释:
- 为什么 TUI 工具的真正瓶颈是子进程开销,不是终端渲染——这个洞见可以泛化到很多 CLI 包装层
- 为什么 gitui 故意不支持 git-LFS / sparse-checkout——这不是没做完,是 libgit2 路线的结构性代价
- 为什么”Rust + 直接绑定 C 库”这条工程模式正在大量复制(ripgrep 直接读文件不调 grep,delta 直接做 diff 渲染不调 less)
- 为什么同一个品类可以有两个明星项目并存——它们其实在解不同的问题
核心要点
gitui 的设计可以拆成 4 件事:
-
libgit2-rs 直连仓库:libgit2 是把 git 内部结构(commit 对象、tree、blob、refs、index)写成 C 库的实现,gitui 通过 Rust 绑定
git2-rs直接调它的函数,不 fork 任何子进程。读 status 就是遍历内存里的 index,看 log 就是顺着 commit 父指针走 DAG——这是它快一个量级的根本原因。 -
异步 git 操作 + 单线程 UI:耗时操作(fetch / 大仓 commit graph 构建 / blame)扔进后台线程,UI 线程只画屏幕、收键盘。哪怕仓库在拉远端,你按方向键还是立即响应。lazygit 也做了类似异步,但子进程本身就是开销。
-
面板 + 单键键位:默认面板 Status / Files / Log / Stashes / Branches,Tab 切换,每个面板内 vim 风格 hjkl 移光标,单字母触发动作(
sstage、uunstage、ccommit、ppush)。设计哲学和 lazygit / btop 同源——多面板上下文 + 单键操作减少手指移动。 -
MIT 协议 + Rust 单二进制:
cargo install gitui一行装好,10MB 静态可执行,无运行时依赖。这种”装一个就能跑”的体验和 ripgrep / fzf / bat 共享同一个 Rust CLI 时代的红利。
实践案例
案例 1:在 90 万 commit 的大仓库里看 log
# 先克隆 Linux 内核(约 6GB,含 .git)git clone https://github.com/torvalds/linuxcd linux
# 用 lazygit 打开 → Log 面板加载 ~57s,进度条卡住lazygit
# 用 gitui 打开 → Log 面板加载 ~24s,过程中可继续切其他面板gitui差别不是渲染——两边都是终端文字——而是 lazygit 每滚一屏 commit 都要再 fork 一次 git log,gitui 是已经把 commit graph 加载到内存里直接遍历。
案例 2:行级 stage(和 lazygit 一样但更快)
gitui# 进 Files 面板(按 2),光标移到一个改动的文件# 回车进入文件 diff 视图# 在某行按 s → 只 stage 这一行;按 hunk 按 S → stage 整个 hunk# 退回后看 Status,只 stage 的那一行已经在 staged 区操作语义和 lazygit / delta 加 git add -p 一致,但 gitui 的 stage 动作直接改 index 文件、不调 git add。
案例 3:什么时候 gitui 帮不了你
# 仓库里有 LFS 大文件git lfs installgitui# → checkout 时 LFS pointer 不会自动 smudge,文件依然是 140 字节的 metadata
# 仓库是 sparse-checkout 模式git sparse-checkout init --conegitui# → 看不到 sparse 配置,可能错把不在 sparse 范围的文件展示出来这两个是 libgit2 跟不上 git 主仓的典型病症——遇到这类仓库要 fallback 回 lazygit 或 git CLI。
案例 4:tmux + gitui 的常见工作流
# tmux 三窗格:左编辑器、右上 gitui、右下 shelltmux new-session \; split-window -h \; split-window -v# 在右上窗格跑 gitui,因为它启动 < 100ms,所以可以 Ctrl-c 关掉再开都不卡这种”频繁开关”工作流是 gitui 比 lazygit 体验拉开最大的场景——lazygit 启动要等几百毫秒,gitui 几乎瞬间就回来。
踩过的坑
-
HTTPS 推拉要显式配 credential.helper:lazygit 直接复用你 shell 里的 git 配置;gitui 通过 libgit2,不会自动读 osxkeychain / wincred,第一次 push 容易卡死要手动
git config credential.helper。具体的修法是在仓库根跑git config credential.helper osxkeychain(macOS)或manager(Windows),改完 gitui 才能复用 keychain 里的 token。 -
大仓首次打开有 cold start:24 秒的 90 万 commit 数字是热路径,首次构建 commit graph 内部缓存要更久。在 Linux 内核这种仓库上等 30 秒+ 是正常的。后续打开因为缓存命中快很多。
-
键位和 lazygit 不通用:都是单字母 vim 风,但具体绑定不一致——比如 lazygit 的
s是 squash,gitui 的s是 stage。两个都用要分别记,建议把不常用那个的快捷键打印出来贴显示器。 -
不能完全替代 git CLI:作者在 README 明确写”this is not a full replacement for the git shell”——rebase 冲突解决、复杂 reflog 恢复、submodule 一些边角操作还得回到 CLI。当 daily 主力工具,不当唯一工具。
-
theme 配置藏得深:默认主题对暗色终端不一定友好,要在
~/.config/gitui/theme.ron用 RON 格式(不是 YAML / TOML)写颜色,文档里只有几行示例。新人改主题前最好直接 fork 一份社区的 dark / light theme 当模板。
适用 vs 不适用场景
适用:
- 大仓库 daily 操作(看 log / stage / commit / branch 切换)——速度优势最明显
- 对启动延迟敏感的工作流(频繁开关 git 客户端、tmux 里来回切窗口)
- 喜欢 vim 键位 + Rust 单二进制无依赖的洁癖
- 已经在用 ripgrep / bat / fzf / delta 这套 Rust CLI 生态——风格统一
- CI / Docker 镜像里要塞个 git TUI 但又想控制镜像大小(10MB vs 25MB 的差距在容器里也算钱)
不适用:
- 仓库重度用 LFS / sparse-checkout / partial clone——libgit2 跟不上
- 团队有大量交互式 rebase 冲突场景——gitui 的冲突解决体验不如 lazygit 成熟
- 完全不习惯键盘驱动 TUI、想要鼠标点击——这两个工具都不适合,去看 GitKraken / Sourcetree
- 对 GPG / SSH 签名链路敏感——gitui 支持 GPG 但和你 shell 配置可能行为不一致
- 需要插件生态(自定义命令、第三方扩展)——gitui 还没开放插件接口,lazygit 的 customCommands 灵活得多
历史小故事(可跳过)
- 2019 年:Stephan Dilly 因为大仓库下 lazygit 启动慢起念头自己写一个 Rust 版。当时 Rust 的 TUI 库还很原始,他先用 tui-rs(后来改名 ratatui)打磨了几个月底层
- 2020 年初:v0.1 发布,定位明确”就是要比 lazygit 快”。第一周就上了 HN 头版,star 数过千
- 2021–2024:随 Rust TUI 生态(ratatui)成熟,UI 体验持续打磨。期间最大的两次架构调整是异步任务系统重写、commit graph 内部缓存格式升级
- 2026 年:v0.28,22k stars,已经是 Rust CLI 生态里仅次于 ripgrep / fzf / bat 的常驻项目
它的存在本身证明了一件事:同一品类不需要赢家通吃——lazygit 和 gitui 解的是不同程度的痛点,前者强在 git 兼容性和 rebase 体验,后者强在性能和无依赖。决定用哪个不是看哪个”更好”,是看你的仓库规模和你最常做什么操作。
学到什么
- CLI 包装层的瓶颈不在 UI,在子进程——这个洞见可以拷贝到很多场景:ripgrep 不调 grep,fd 不调 find,delta 不调 less。每多一次 fork+exec 就多一次进程启动 + 内存拷贝 + 文件描述符建立的开销,TUI 这种”几十毫秒就要刷一次”的场景被放大很多倍
- 直接绑定 C 库(libgit2 / libpng / libssl)有性能红利,也有跟不上主线的代价——是个永恒的权衡。git 主仓加了 sparse-checkout v2,libgit2 跟进可能要半年到一年
- TUI 工具的横向对比:不只是看 feature 列表,还要看底层架构选择——lazygit / gitui / btop 都是多面板单键,但工程路线完全不同
- Rust CLI 时代的复利:单二进制、无 runtime、cargo install 一行装好——这套体验本身就是采用率的引擎,对比 npm install 一堆 peerDependency 就能看出来差距
延伸阅读
- 项目主页:extrawurst/gitui(README 里有完整 keybinding 表)
- 性能对比文章:gitui vs lazygit benchmarks(Linux 内核仓库的实测数字就是从这里来的)
- libgit2-rs 文档:git2-rs docs(理解 gitui 的底层 API;自己写 git 工具也用这个)
- 作者博客:extrawurst.com(Stephan 写过几篇 gitui 设计回顾,讲为什么不走 git CLI 路线)
- lazygit —— 同品类 Go 实现,对照阅读
- ratatui —— gitui 用的 Rust TUI 框架(可能未建档)