高效管理Git工作树:tree-me工具的妙用

本文介绍了Git工作树(worktree)的优势及其在并行开发中的价值,并详细讲解了一个名为tree-me的实用工具如何简化工作树管理,通过自动化组织目录、集成PR工作流等功能提升开发效率。

我坚信Git工作树是Git中最被低估的功能之一。多年来我一直忽视它们,因为我不理解它们,也不知道如何将其适配到我的工作流程中。

但当我开始使用LLM编码工具进行更多并行工作时,情况发生了变化。能够同时在多个分支上工作是一个游戏规则的改变者。

如果没有Git工作树,在同一仓库中同时处理多个分支是一件痛苦的事情。要么采用串行方法:暂存你的更改,切换上下文,并祈祷你没有破坏任何东西。或者更糟的是,用“WIP”消息提交半成品(说的就是你,过去的我)。或者,你可以拥有同一仓库的多个克隆副本,但这难以管理并且会占用大量磁盘空间。

Git工作树解决了这个问题。它们允许你在不同的目录中同时检出多个分支,这些目录都共享同一个Git数据库(即.git目录)。对我来说,这意味着我可以在一个终端中处理一个功能,在另一个终端中审查PR,让Claude Code在另一个终端中处理另一个功能,并且所有这些都共享相同的Git历史记录。

但问题是:手动创建工作树很繁琐。你需要记住把它们放在哪里、如何命名,以及之后清理它们。此外,默认情况下,除非指定不同的目录,否则Git工作树会在仓库的根目录中创建。

我想要更简单的东西。我想要一个能在任何仓库中工作的工具。无需设置,无需配置文件,只有合理的默认值。

介绍 tree-me

我构建了tree-me,这是一个围绕Git原生工作树命令的最小化包装器。它在让Git处理所有复杂性的同时,增加了组织性的约定。

取代这种繁琐操作:

1
2
3
4
5
# 手动创建工作树
mkdir -p ~/worktrees/my-project
git worktree add ~/worktrees/my-project/fix-bug -b haacked/fix-bug main
cd ~/worktrees/my-project/fix-bug
# 现在为每个仓库重复此操作...

你只需这样做:

1
2
3
tree-me create haacked/fix-bug
# 创建: ~/dev/worktrees/my-project/haacked/fix-bug
# 并自动切换到该目录

工作原理

tree-me使用类似Git的子命令并遵循约定,让你无需思考:

  • 自动从Git远程仓库检测仓库名称
  • 自动检测默认分支(检查origin/HEAD,回退到main)
  • 按仓库组织:$WORKTREE_ROOT/<repo-name>/<branch-name>
  • 将所有的验证、错误处理和边缘情况委托给Git处理
  • PR支持:使用Git的原生PR引用获取GitHub PR(需要gh CLI)
  • 自动切换目录:创建后自动切换到工作树目录
  • 标签补全:在bash/zsh中补全命令和分支名称

命令

1
2
3
4
5
6
7
tree-me create <branch> [base]        # 在工作树中创建新分支
tree-me checkout <branch>             # 检出已存在的分支(别名:co)
tree-me pr <number|url>               # 检出GitHub PR(使用gh CLI)
tree-me list                          # 列出所有工作树(别名:ls)
tree-me remove <branch>               # 删除一个工作树(别名:rm)
tree-me prune                         # 清理过时的工作树文件
tree-me shellenv                      # 输出用于自动切换目录的shell函数

示例

1
2
3
4
5
6
7
tree-me create haacked/fix-bug              # 从main/master创建
tree-me create haacked/fix-bug develop      # 从develop创建
tree-me co existing-feature                 # 检出已存在的分支
tree-me pr 123                              # 检出PR #123
tree-me pr https://github.com/org/repo/pull/456
tree-me ls                                  # 显示所有工作树
tree-me rm haacked/fix-bug                  # 清理(支持标签补全)

约定

tree-me是Git原生命令的最小化包装器。适用于任何仓库、任何语言、任何设置。唯一的约定是工作树的存放位置和命名方式。

希望工作树放在不同的位置?设置WORKTREE_ROOT。需要从develop而不是main创建分支?将其作为参数传递:tree-me create my-feature develop。有约定,也有变通方案。

设置

要启用自动切换目录和标签补全,请将以下内容添加到你的~/.bashrc~/.zshrc

1
source <(tree-me shellenv)

这使得tree-me createtree-me checkouttree-me pr命令自动切换到工作树目录。同时还能启用命令和分支名称的标签补全(试试tree-me rm <TAB>)。

可以在 github.com/haacked/dotfiles/blob/main/bin/tree-me 查看完整实现。

PR工作流

这是它大放异彩的地方。当你正深入处理某个功能时,有人请你审查一个PR:

1
2
3
4
5
6
tree-me pr 123                  # 获取、检出PR,并切换到其目录
# 你现在位于: ~/dev/worktrees/dotfiles/pr-123
# 审查代码,测试它,留下评论
# 完成后,切换回去
tree-me co haacked/my-feature   # 检出并切换回你的功能分支
# 回到你的工作,无需暂存

当你审查完成:

1
tree-me rm pr-123               # 使用标签补全查看可用的分支

完成。清理干净。不会意外地将审查更改提交到你的功能分支。

注意:tree-me使用gh CLI来获取PR。如果你没有安装它,可以使用brew install gh安装。

安装

下载tree-me并将其放在你的PATH环境变量中的某个位置:

1
2
3
# 示例:复制到 ~/bin 或 ~/.local/bin
curl -o ~/bin/tree-me https://raw.githubusercontent.com/haacked/dotfiles/main/bin/tree-me
chmod +x ~/bin/tree-me

然后启用自动切换目录和标签补全(参见上面的设置部分)。

就这些了。除了Git、bash和可选的用于PR检出的gh CLI外,没有其他依赖。

为什么构建这个工具

我每天处理多个仓库——PostHog、我的博客、各种开源项目。我厌倦了记住特定于项目的工作树脚本,并寻找上周创建的工作树。

哲学是:不要重新实现Git已经做得很好的事情。只添加所需的最小约定。

Git已经完美地处理了工作树。我只需要有组织的路径、合理的默认值以及跨所有项目的一致接口。

目录结构

一切都根据仓库名称和分支名称有条理地组织:

1
~/dev/worktrees/<repo-name>/<branch-name>

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
~/dev/worktrees/
├── dotfiles/
   ├── haacked/vim-improvements/
   ├── haacked/git-tools/
   └── main/
├── posthog/
   ├── haacked/feature-flags/
   ├── pr-789-contributor/
   └── main/
└── spelungit/
    └── haacked/performance/

一目了然,你就知道一切在哪里。

它不做什么

tree-me不复制环境文件、安装依赖项或设置项目特定工具。这是故意的。这些问题属于你项目的设置脚本,而不是一个通用的Git工具。

想要自动化环境设置?在你的仓库中添加一个在检出后运行的脚本。想要复制.env文件?把它放在你项目的入门文档中。tree-me只处理Git工作树的仪式性操作。

试试看

如果你经常处理多个分支,请尝试使用工作树。如果你处理多个仓库,请尝试使用tree-me。如果你讨厌它,至少你了解了Git工作树(这可能比脚本本身更有价值)。

可以在 github.com/haacked/dotfiles/blob/main/bin/tree-me 找到tree-me。它是MIT许可的——可以复制、修改、改进它。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计