关于 but
面向使用 GitButler CLI(`but`)的 Agent,提供工作区模式下的代码变更与版本管理流程指导。重点说明多分支栈操作、精确提交以及保护其他会话的变更。
主要功能
- 01同步并检查工作区状态
- 02创建与管理并行或堆叠分支
- 03按文件或代码块提交变更
- 04整理提交历史
- 05推送并创建 Pull Request
适用场景
- 01在 GitButler 工作区开始新任务
- 02将不同变更分配到对应分支
- 03提交特定文件或代码块
- 04处理合并后的拉取与工作区同步
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gaboe/ opencode-gitbutler --skill but安装后在 Claude Code 中输入/but确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cheatsheet.mdconcepts.mdexamples.mdreference.mdtutorial.md详细介绍
GitButler CLI 技能
帮助用户在工作区模式下使用 GitButler CLI(but 命令)。
新会话工作流
每个涉及代码更改的新 Agent 会话都必须遵循此流程:
- 先同步 → 执行
but pull获取最新上游更改(避免冲突和基线过时) - 检查状态 → 执行
but status查看现有分支和未暂存的更改 - 决定分支 →
- 如果有与任务匹配的现有分支 → 复用该分支(更改已应用)
- 如果是新工作 → 执行
but branch new <task-name>(例如:feat/add-auth、fix/login-bug) - 如果需要恢复尚未应用的工作 → 执行
but apply <branch>
- 进行更改 → 按需编辑文件
- 暂存并提交 → 执行
but commit <branch> -m "message" --changes <id>,<id> - 完善 → 使用
but absorb或but squash清理历史记录 - 准备就绪后推送 → 执行
but push <branch> - 创建 PR → 执行
but pr new <branch> -t(使用默认目标分支)
分支命名: 使用约定前缀:feat/、fix/、chore/、refactor/
尽早提交,经常提交。 不要犹豫,放心创建提交——GitButler 让编辑历史记录变得轻而易举。之后可以随时将更改 squash、reword 或 absorb 到现有提交中。小型原子提交优于大量未提交的更改。
使用写入/编辑工具后
准备提交时:
- 运行
but status --json查看未提交的更改并获取其 CLI ID - 直接提交相关文件:
but commit <branch> -m "message" --changes <id>,<id>
可以批量编辑多个文件后再提交,无需每次更改后都提交。
核心概念:工作区模型
GitButler ≠ 传统 Git
- 传统 Git:一次处理一个分支,使用
git checkout切换 - GitButler:在一个工作区中同时使用多个栈,并将更改分配给各个栈
这意味着:
- ❌ 不要使用
git status、git commit、git checkout - ✅ 使用
but status、but commit、but命令 - ✅ 可以使用只读的 git 命令(
git log、git diff)
严格安全规则(必须遵守)
- 绝不丢弃由你之外的人创建的更改。
zz(未分配)中通常包含其他会话、Agent 或用户的工作。- 如果存在无关更改,请保持原样,并在执行任何丢弃操作前征求许可。
- 工作结束时,绝不要将你自己的更改留在
zz中。- 编辑后,运行
but status --json,并通过but stage或but commit --changes将文件/代码块 ID 移至正确的分支。
- 编辑后,运行
- 提交前验证分支归属。
- 确认每个已更改的文件/代码块都属于预期分支/任务,然后只提交相应 ID。
- 尊重跨会话的分支归属。
- 在多 Agent 环境中,分支可能属于其他 Agent 会话。绝不要重写提交信息、重命名或推送你在本会话中未创建的分支。
- 如果需要修改其他会话的分支,请先征求用户许可。
快速开始
安装:
curl -sSL https://gitbutler.com/install.sh | sh
but setup # Initialize in your repo
but skill install --path <path> # Install/update skill (agents use --path with known location)
给 AI Agent 的注意事项:
- 以编程方式安装或更新此技能时,始终使用
--path指定准确的安装目录。如果存在多个安装项,--detect标志需要用户交互。 - 所有命令都使用
--json标志,以获取结构化且可解析的输出。这对于使用but status --json可靠地解析工作区状态尤为重要。
核心工作流:
but status --json # Always start here - shows workspace state (JSON for agents)
but branch new feature # Create new stack for work
# Make changes...
but commit <branch> -m "…" --changes <id>,<id> # Commit specific files by CLI ID
but push <branch> # Push to remote
核心命令
有关详细的命令语法和所有可用选项,请参阅 references/reference.md。 有关实践学习指南,请参阅 references/tutorial.md。 有关单页速查表,请参阅 references/cheatsheet.md。
AI Agent 须知: 为所有命令添加 --json 标志,以获取结构化、可解析的输出。
了解状态:
but status --json- 概览(从这里开始,Agent 始终使用 --json)but status --json -f- 包含完整文件列表的概览(需要查看所有已更改文件时使用)but show <id> --json- 有关提交/分支的详细信息but diff <id>- 显示差异
标志说明:
--json- 输出结构化 JSON,而不是人类可读的文本(Agent 始终使用)-f- 在状态输出中包含详细的文件列表(与 --json 组合使用:but status --json -f)
组织工作:
but branch new <name>- 独立分支but branch new <name> -a <anchor>- 堆叠分支(有依赖关系)but stage <file> <branch>- 预先将文件分配到分支(可选,用于提交前整理)
进行更改:
but commit <branch> -m "msg" --changes <id>,<id>- 提交指定的文件或代码块(推荐)but commit <branch> -m "msg" -p <id>,<id>- 同上,使用短标志but commit <branch> -m "msg"- 将所有未提交的更改提交到分支but commit <branch> --only -m "msg"- 仅提交预暂存的更改(不能与 --changes 组合使用)but amend <file-id> <commit-id>- 将文件修改纳入指定提交(显式控制)but absorb <file-id>- 将文件修改纳入自动检测到的提交(智能匹配)but absorb <branch-id>- 将暂存到某个分支的所有更改纳入提交but absorb- 纳入所有未提交的更改(谨慎使用)
获取 --changes 所需的 ID:
- 文件 ID:
but status --json- 提交整个文件 - 代码块 ID:
but diff --json- 提交单个代码块(当一个文件有多处更改时,用于精细控制)
编辑历史记录:
but rub <source> <dest>- 通用编辑操作(暂存/修正/压缩/移动)but squash <commits>- 合并提交but reword <id>- 更改提交消息/分支名称
远程操作:
but pull- 与上游同步but push [branch]- 推送到远程but pr new <branch>- 推送并创建拉取请求(自动推送,无需先行推送)but pr new <branch> -m "Title..."- 内联 PR 消息(第一行为标题,其余为描述)but pr new <branch> -F pr_message.txt- 从文件读取 PR 消息(第一行为标题,其余为描述)- 对于堆叠分支,自定义消息(
-m或-F)仅适用于所选分支;依赖分支使用默认消息
核心概念
有关工作区模型、依赖跟踪和设计理念的深入介绍,请参阅 references/concepts.md。
CLI ID:每个对象都有一个短 ID(例如,提交的 c5、分支的 bu)。将这些 ID 用作参数。
并行分支与堆叠分支:
- 并行:彼此独立的工作
- 堆叠:有依赖关系的工作,一个功能建立在另一个功能之上
but rub 基元操作:核心操作,根据组合对象的不同而执行不同操作:
- 文件 + 分支 → 暂存
- 文件 + 提交 → 修正
- 提交 + 提交 → 压缩
- 提交 + 分支 → 移动
工作流示例
有关完整的分步工作流和实际场景,请参阅 references/examples.md。
开始独立工作:
but status --json
but branch new api-endpoint
but branch new ui-update
# Make changes, then commit specific files to appropriate branches
but status --json # Get file CLI IDs
but commit api-endpoint -m "Add endpoint" --changes <api-file-id>
but commit ui-update -m "Update UI" --changes <ui-file-id>
提交指定代码块(精细控制):
but diff --json # See hunk IDs when a file has multiple changes
but commit <branch> -m "Fix first issue" --changes <hunk-id-1>
but commit <branch> -m "Fix second issue" --changes <hunk-id-2>
整理提交:
but absorb # Auto-amend changes
but status --json # Verify absorb result
but squash <branch> # Squash all commits in branch
解决冲突:
but resolve <commit> # Enter resolution mode
# Fix conflicts in editor
but resolve finish # Complete resolution
管理工作区:
but config target origin/test # Set default PR target (requires unapply all branches first)
but unapply <branch> # Remove branch from workspace (keeps commits)
but apply <branch> # Bring branch back into workspace
but teardown # Exit GitButler mode → normal git
but setup # Re-enter GitButler mode
but discard <ids> # Discard unstaged changes
PR 合并后的流程
在 GitHub 上 squash 合并 PR 后,请严格按以下顺序操作:
but unapply <merged-branch> # MUST do BEFORE pull - prevents orphan branch errors
but pull # Pull merged changes from remote
重要:如果在取消应用已合并的分支之前执行 but pull,GitButler 会因孤立分支冲突而报错。务必先取消应用。
如果 but unapply 失败(使用 --delete-branch 远程删除后,分支已从工作区消失),but pull 也可能因内部仍存在幽灵堆栈而出现“resolution mismatch”错误。此时可以使用 GitButler 桌面应用处理——告知用户从 GUI 运行 but pull。或者,使用 but teardown → but setup → but config target origin/<branch> 重置。
执行 but teardown → but setup 后:目标配置会重置。请再次运行 but config target origin/<branch>。
安全使用 --no-hooks
当预提交钩子因与更改无关的既有错误而失败时,可以使用 --no-hooks。但这也会跳过格式化程序:
bun run format # Format FIRST
but commit <branch> -m "msg" --changes <ids> --no-hooks # Then commit without hooks
或者,正常提交并吸收格式化修复:
but commit <branch> -m "msg" --changes <ids> # Commit (hooks may fix formatting)
but absorb # Absorb any auto-formatted changes
已知问题和解决方法
| 问题 | 发生情况 | 解决方法 |
|---|---|---|
but resolve 丢失目标配置 | 进入 resolve 模式后,but config target 会重置为“未设置” | 在 but resolve finish 后再次运行 but config target origin/<branch>。如果 finish 失败,则执行 git checkout gitbutler/workspace → but teardown → but setup |
but absorb 区块锁定 | 如果某个区块被另一个分支上的提交锁定,Absorb 会将其分配给错误的提交 | 使用 but amend <file> <commit> 进行明确控制,而不是使用 absorb |
but pr new 没有 --base 标志 | 始终针对默认目标创建 PR | 先设置目标:but config target origin/<branch> |
but config target 需要先取消应用 | 有已应用的分支时无法更改目标 | 对所有分支运行 but unapply → 更改目标 → 运行 but apply |
but config forge auth 是交互式的 | 无法在非交互式 Agent 模式下运行 | 用户必须在终端中运行,并在 GitHub 上授予组织访问权限 |
but commit 的预提交钩子失败 | 钩子因与所做更改无关的既有错误而失败 | 如果错误并非由你的更改导致,则使用 but commit --no-hooks。务必先运行 bun run format,因为 --no-hooks 会跳过格式化程序 |
but branch delete 删除最后一个片段 | 如果删除后会留下匿名片段,则无法删除 | 使用 but unapply,而不是 delete |
but stage 前缀匹配 | 分支名可以缩写 | but stage <id> ch 可用于 chore/gitbutler-setup |
but discard 区块范围错误 | 丢弃文件级更改时,有时会出现区块范围错误 | 对文件级更改使用 git checkout -- <file>,而不是 but discard |
but teardown + but setup 重置目标 | teardown/setup 周期结束后,目标配置会丢失 | setup 后再次运行 but config target origin/<branch> |
Lefthook 的 pre-commit.old 不断累积 | Lefthook 会创建 pre-commit.old 备份,并在下次安装时产生冲突 | 在 package.json 的 prepare 脚本中添加 rm -f .git/hooks/pre-commit.old |
在取消应用前运行 but pull | 仍有已合并分支处于应用状态时拉取,会导致孤立错误 | 运行 but pull 前,务必先运行 but unapply <merged-branch> |
远程分支删除后运行 but unapply | 远程删除分支时(例如合并时使用 --delete-branch),but unapply 会因“branch not found”失败,随后 but pull 会因“resolution mismatch”失败 | 使用 GitButler 桌面应用拉取,或执行 but teardown → but setup → but config target origin/<branch> |
拆分区块的文件卡在 zz 中 | 文件包含锁定到不同分支上提交的区块——GitButler 会将 stack_id 设为 None,插件则根据锁定引用将文件视为“已处理” | 手动提交每个区块:运行 but diff --json 获取区块 ID,然后针对每个区块运行 but commit <branch> -m "msg" --changes <hunk-id> |
| 插件自动清理未清除空分支 | ge-branch-* 清理失败率约为 12%;用户命名的空分支永远不会自动清理 | 运行 /b-branch-gc 命令,或手动运行 but unapply <branch-id> |
| 通知未送达 Agent | 插件通知送达率约为 55%(观察到的会话中,259 条已排队,142 条已送达) | 务必使用 but status --json 验证状态——不要只依赖 <system-reminder> 通知 |
| 工作区投影不匹配 | but status 显示分支/堆栈,但 but unapply <branch> 或 GUI stack_details 表示在工作区中找不到该堆栈 | 将其视为 GitButler 元数据损坏,而不是 Git 历史问题。停止在原处进行修改,收集日志、项目和提交图以供支持排查,然后执行 but teardown → 备份 .git/gitbutler → but setup。如果问题仍然存在,请使用全新克隆/工作树。 |
多次 resolve/rebase 后出现许多 (no changes) 提交 | 堆栈累积了与上游补丁等效或重放重复的合成提交 | 不要继续在原处进行 resolve。冻结引用,使用 git cherry -v 找出补丁等效的提交,并从逻辑基点重建干净分支,而不是保留损坏的历史。 |
| 应用一个堆栈时自动取消应用另一个堆栈 | GitButler 表示,为了应用所请求的堆栈,它必须取消应用另一个堆栈 | 这些堆栈在内容或祖先关系上有重叠。先取消应用当前已激活的冲突堆栈,或者重建预期的堆栈边界,确保一次只应用一条逻辑链。 |
投影错误导致 but push 受阻 | 工作区/堆栈元数据已损坏,但原始 Git 引用正常 | 如果目标是修复远程祖先关系,请使用原始 Git 验证分支引用,并在万不得已时使用 git push --force-with-lease。之后返回 gitbutler/workspace,并使用 but setup 重新初始化元数据。 |
诊断卡在 zz 中的文件
当文件卡在 zz(未分配)中且无法自动恢复时:
-
找出原因:
but status --json -f # Look for files in zz with [LOCKED] markers but diff --json # Get hunk-level IDs and see lock targets -
如果代码块被锁定到不同分支(拆分代码块场景):
- 每个代码块都必须单独提交到其锁定的分支
- 对每个代码块执行
but commit <branch> -m "msg" --changes <hunk-id> - 或执行
but rub <hunk-id> <commit-id>,将其修改并入已锁定的提交
-
如果文件没有锁定,但仍在
zz中:- 插件的
after-edit可能静默失败了——请手动暂存 - 执行
but stage <file-id> <branch>,或使用--changes提交
- 插件的
-
如果执行
but cursor stop后有许多文件卡住:- 对每个文件执行
but rub <file-id> <branch-id>,强制分配 - 这是最可靠的恢复方法
- 对每个文件执行
关键要点: 自动恢复无法修复锁定到多个分支的文件。如果在 zz 中看到 [LOCKED],就需要手动处理。
对损坏的堆栈进行精准修复
如果堆栈损坏过于严重,无法通过常规 but resolve 快速收敛——例如:
- 反复出现
conflicted提交, git cherry -v显示大量补丁等效的重复项,- GitButler 显示许多标记为
(no changes)的提交, - 或单个提交带入了
.auto-resolution/**或其他明显无关的快照文件,
请使用精准修复,而不是尝试保留损坏的历史。
适合进行精准修复的情况
- 原始 Git 图易于理解,但 GitButler 元数据/历史重放难以理解。
- 即使当前分支历史已无法清晰反映,你仍能明确识别出这项工作的预期逻辑层次。
- 一个混合提交包含真正的功能文件,以及明显意外混入的无关内容。
外科式修复工作流
-
先冻结所有内容
- 为当前损坏的分支创建备份引用。
- 暂存工作区中的未提交改动和钩子刷新产生的噪声。
-
找到干净的逻辑基点
- 使用
git cherry -v <base> <branch>区分补丁等效的重复提交和真正独有的提交。 - 使用
git log --reverse <base>..<branch>按顺序查看分支的提交层次。
- 使用
-
从干净的基点重建,而不是沿用损坏的分支历史
- 从预期的基点创建临时分支。
- 只挑选干净的提交。
- 如果提交受到污染(例如添加了
.auto-resolution/**),则只从该提交或现有分支顶端对预期路径执行文件级检出,然后创建新的干净提交。
-
独立验证每个重建层
- 使用
git diff --name-only <base>..<layer>比较各层之间的文件差异。 - 在替换引用前,在重建后的提交栈上运行项目验证(
bun run check、构建/测试/分析器等)。
- 使用
-
只有在确认干净的提交栈无误后才替换引用
- 将实际分支引用移至重建后的临时引用。
- 只有在确认祖先关系和检查结果后,才使用
--force-with-lease强制推送。
为什么实际操作中这样更快
外科式修复听起来比反复解决冲突更费力,但一旦提交栈陷入重复重放、(no changes) 或元数据损坏的境地,重建逻辑差异通常比保留所有历史产物更快、更安全。
关键安全规则
- 绝不要丢弃你没有创建的改动。
zz中未分配的改动可能属于其他 Agent、会话或用户。对任何你无法确认的改动,运行but discard或git checkout --前,务必先询问用户。在 GitButler 工作区中,多个参与者会并行工作——把改动视为“过时”或“已合并”并将其丢弃,是一种有破坏性的假设。 - 始终立即将你的改动分配到分支。 不要将编辑内容留在
zz(未分配)中。编辑文件后,使用but stage <file-id> <branch>将其暂存到你的工作分支,或直接使用--changes提交。
指南
- 始终先运行
but status --json了解当前状态(Agent 应始终使用--json)。 - 每个独立的工作主题都创建一个新的提交栈。
- 使用
--changes直接提交指定文件,无需先暂存。 - 尽早并频繁提交——不要等到尽善尽美。与传统的 Git 不同,GitButler 使用
absorb、squash和reword可以轻松修改历史。先创建小型、原子化的提交,之后再完善,胜过积累大量未提交改动。 - 作为 Agent 运行时,所有命令都使用
--json标志——这样会提供结构化、可解析的输出,而不是面向人的文本。 - 不确定时使用
--dry-run标志(push、absorb)。 - 频繁运行
but pull——在会话开始时、创建分支前以及推送前都要运行。工作区过时会导致合并冲突。 - 更新此技能时,使用
but skill install --path <known-path>以避免出现提示。 - 完成工作前检查带锁的
zz文件。 运行but status --json -f,查找zz中带有[LOCKED]标记的文件。这些文件不会自动恢复——你必须使用--changes <hunk-id>将每个代码块手动提交到正确的分支。详情请参阅 references/concepts.md — 区块锁定。 - 不要只相信通知——插件通知的送达率约为 55%。在推断分支分配或提交状态前,始终使用
but status --json核实工作区状态。
插件自动行为(幕后操作)
GitButler 插件会自动执行一些操作。你无需亲自执行这些操作——但你应该了解它们会发生,以免重复操作或对意外的状态变更感到困惑。
| 行为 | 触发条件 | 执行内容 |
|---|---|---|
| 文件自动分配 | 每次文件编辑后 | 查找拥有该文件的分支,并运行 but cursor after-edit 或 but rub 来分配文件 |
| 自动提交 | 会话空闲时(Agent 停止编辑) | 运行 but cursor stop,将所有未提交的更改提交到其分配的分支 |
| LLM 提交信息改写 | 自动提交后 | 根据实际差异,使用 Claude Haiku 改写通用提交信息 |
| 分支重命名 | 自动提交后 | 根据用户的提示词,将 ge-branch-* 分支重命名为描述性名称 |
| 空分支清理 | 自动提交后 | 移除提交数为 0 的 ge-branch-* 分支(成功率约为 88%) |
| 会话标题同步 | 自动提交后 | 根据分支名称更新会话标题 |
| 上下文注入 | 每条 Agent 消息发送前 | 注入包含工作区通知(分支已创建、提交已完成等)的 <system-reminder> |
这对你意味着什么
- 不要手动重命名
ge-branch-*分支——插件会在空闲后重命名 - 不要改写自动生成的提交信息——插件会使用 LLM 改写
- 如果看到意外的提交——检查插件是否在空闲事件期间自动提交
- 通知送达率约为 55%——并非所有注入的通知都会送达给你。请始终使用
but status --json验证状态,而不要依赖通知 - 空分支清理可能失败——如果看到过时的
ge-branch-*分支,请运行/b-branch-gc