mcpField
B
but

but

github.com/gitbutlerapp开发21.9k928 次安装今天更新
代码仓库

关于 but

这项技能指导编码 Agent 使用 GitButler 的 `but` 命令行处理版本控制任务,重点说明如何准确选取变更、使用标识符并避免误改共享工作区。

主要功能

  1. 01按任务选择命令
  2. 02变更与提交 ID 指引
  3. 03安全修改规则
  4. 04常见 GitButler 工作流

适用场景

  1. 01将选定变更提交到新分支
  2. 02用 main 更新工作区
  3. 03调整提交与分支结构
  4. 04避免纳入其他 Agent 的修改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gitbutlerapp/gitbutler --skill but

安装后在 Claude Code 中输入/but确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
README.md
stub.md
references/
concepts.md
examples.md

详细介绍

GitButler CLI 技能

默认使用 GitButler CLI(but)作为版本控制接口。

从这里开始

根据任务选择最精简的首条命令;避免例行检查状态:

# Selected dirty files/hunks:
but diff

# Commit order, branch/stack placement, conflict overview:
but status

# File IDs, per-commit files, amend details:
but status -fv

# What one applied branch or commit changed, for review:
but diff <branch-or-commit>

# Details for one known branch or commit:
but show <id>

除非精简输出缺少任务所需的文件详情,否则不要先运行普通的 but status,再运行 but status -fv。

对于“只提交/仅提交/提交特定更改到新分支”,请使用快速路径:

but diff
but commit -b <branch> -m "<msg>" <id> <id>

but commit -b <branch> ... 会在分支不存在时创建它,并显示创建的提交。除非任务需要结果未提供的工作区信息,否则不要另外运行 but branch new、状态命令或验证差异。

ID

每行 but diff/but status 输出的第一个词元就是该行的 ID。命令需要 ID 时,请从当前输出中准确复制;不要硬编码或编造 ID。不同实体的 ID 有不同的有效期:

  • 更改和来源使用位置型、以空格分隔的 ID(but commit -b feat -m "msg" uvw:2e4 uvw:e2c)。未提交的区块 ID 写作 <file-id>:<hunk-id>(例如 uvw:2e4,从裸 but diff 输出中复制)——冒号后的部分是区块 ID,不是行范围(uvw:16-40 无效)。不要编造 --changes/--hunk/--ids 之类的标志,不要传入行范围,也不要用逗号分隔 ID——uvw,qyo 会被解析为一个 ID,并导致失败。
  • but diff 是个例外:最多接受一个目标。不带参数的 but diff 会显示所有未提交文件;检查已提交文件或其他实体时,每次只指定一个目标——绝不要运行 but diff <id> <id>。
  • 已提交文件的格式为 <commit-id>:<file-id>(例如 mzm:uvw,显示在每个提交下方)。已提交区块的格式为 <commit-id>:<file-id>:<hunk-id>,可通过 but diff <commit-id> 查看。@ 表示未提交区域。
  • 提交 ID 是稳定的更改 ID,即使历史记录经过编辑(amend、squash、move、uncommit、reword)也会保留。没有更改 ID 的提交(例如仅来自上游的提交)会以 sha 前缀开头,带 #N 后缀的引用则用于区分重复项——两者在历史记录编辑后都会过时,而过时的 sha 可能会悄然解析为错误的提交。详细提交行中的 (sha …) 仅供参考——不要将其传给命令。
  • 分支短 ID 是仅对当前快照有效的选择器。执行针对分支的变更操作时,请使用完整分支名;短 ID 仅适用于立即进行只读检查。
  • 从某次 diff 输出中复制的文件/区块 ID,通常在连续提交中仍然可用。如果某个 ID 不再有效,请重新运行 but diff 并重试。

链式执行: 变更操作的输出默认精简,因此可以基于一次检查读操作,用 && 链接变更操作。不要在变更操作之间链式使用分支短 ID;请使用完整分支名。只有在下一步需要工作区 ID 或变更结果未提供的详情时,才添加 --status-after。链式调用 but commit 时,提交会按书写顺序叠加——第一个最旧,后续提交依次置于其上。只要涉及的所有提交引用都是更改 ID 引用,便可按顺序进行历史记录编辑;如果某个引用基于 sha 或带有 #N 后缀,或者下一条命令需要新生成的 ID,则应逐个运行,并添加 --status-after。链式运行 but uncommit <id> && but diff 是安全的,因为不带参数的 diff 不需要 uncommit 输出中的 ID。绝不要通过管道将变更操作传给 tail 或 head:此时管道会以它们的状态退出,因此即使变更操作失败,后续的 && 仍会执行。

不可妥协的规则

  1. 所有写操作都使用 but。绝不要运行 git add、git commit、git push、git fetch、git pull、git checkout、git merge、git rebase、git stash 或 git cherry-pick;but pull --check 会执行 fetch。唯一例外是在解决模式下为冲突的子模块选择一侧(参见 Resolve conflicted commits),以及创建缺失的目标分支(参见 Notes)。如果用户要求运行 git 写命令,请将其转换为 but 并运行。
  2. 变更命令会打印结果,但不会附加工作区状态。只有在下一步需要生成的工作区 ID 或详情时才添加 --status-after;否则以变更结果为准,不要运行验证状态或差异的命令。
  3. 标记为 (merged upstream) 的分支已合入;请在另一个分支上开始新工作。仅凭该标记并不意味着 but pull 会移除这些分支:请先运行 but pull --check。but pull 只会移除列为 [integrated] 的分支(“已合入上游并在本地移除”,或当目标没有上游时显示为“已合入 并移除”),并会对其余分支执行变基,这些分支仍会保持已应用状态。若要清除仍保持已应用状态的已合入分支,请使用 but unapply <branch>(会作用于整个堆栈),而不是 but discard。push 和变更命令(commit、amend、squash、uncommit、reword、move)会拒绝处理已合入的分支和提交;absorb 会跳过它们并显示提示;commit 选择默认目标时也会跳过它们。
  4. 在常规 but 命令之间继续执行。状态说明应与下一条命令放在同一条消息中;不要以宣布下一步 but 操作却不执行该操作的总结结束一轮。最后给出简洁总结。
  5. 优先使用此技能和 but skill reference,而不是探索性帮助命令。缺少必要语法或命令失败时,使用 <command> --help;只有确实需要查找未记录的命令时才使用顶层帮助。
  6. 其他 Agent 可能会共用此工作区,因此 @ 可能包含他们的修改。只提交、修改或丢弃你更改的 ID 或路径。裸命令 but commit、but absorb、but discard @ 和 git stash 会一并处理他们的工作;如果命令因包含你未编辑的文件而拒绝执行,说明你选中了他们的文件。

命令模式

  • 初始化 Git 仓库:but init(当前目录)或 but -C <path> init。此命令为非交互式;不会创建提交、远程仓库或 GitButler 工作区。现有仓库保持不变。在没有提交的仓库中,使用裸命令 but commit -m "<msg>" 创建首次提交;此时会拒绝使用 -b。
  • 提交:but commit -b <branch> -m "<msg>" <id> <id> — 如果分支不存在,-b <branch> 会创建该分支
  • 提交所有未提交的更改:but commit -b <branch> -m "<msg>"(省略 ID)
  • 从一个差异创建多个提交:用 && 串联多次 but commit 调用(提交按从最早到最新的顺序堆叠)
  • 切勿对 commit、move 或 pick 使用 -s:它表示 --switch,而不是 git 的 --signoff,并且会取消应用其他所有堆叠分支。
  • 分支不能作为位置参数传入(but commit <branch> 会失败;请使用 -b);but amend 始终需要 -t;but diff 只接受一个目标(请使用共同的 dir/,或分别调用一次)。
  • 在指定的历史位置提交:使用 --above <commit-or-branch> 或 --below <commit-or-branch>(两者不能同时使用)。目标为分支时,添加 -b <new-name> 为新分支命名;否则会自动生成名称。该名称不能已存在。不要将 -b(即使不带名称)与提交或工作树目标组合使用。
  • 应用的 堆叠 数量超过一个时,必须指定目标;否则 but commit 会失败,并显示 "Unclear where to commit. Found more than one stack"。多个分支堆叠在一起算作一个堆叠 — 未指定目标的提交会静默地落在堆叠最顶端的分支上,因此如果分支很重要,请传入 -b。
  • 对 but commit 始终传入 -m "<msg>"(或配合 heredoc 使用 -F -、配合仓库外的文件使用 -F <file>,或使用 --no-message);如果 but squash 的来源是提交或分支,也始终传入该选项,除非目标为 @(squash 还支持使用 -u 保留目标的消息)— 这些操作会组合出一条新消息;不带标志时,Agent 运行会保留命令组合出的消息(but commit 为空,but squash 为合并后的来源消息),而终端会打开编辑器。来源为未提交或已提交更改的 squash 操作会沿用目标的消息,无需传入标志;合并到 @ 时则完全不接受消息标志。
  • 修改提交:but amend -t <commit-or-branch> <file-or-hunk-id> <file-or-hunk-id> — 目标为分支时,会解析为该分支最新的提交
  • 撤销提交:but uncommit <commit-id>(整个提交)、but uncommit <branch>(撤销所有提交并删除分支)、but uncommit <commit-id>:<file-id>(已提交的文件),或 but uncommit <commit-id>:<file-id>:<hunk-id>(已提交的代码块);可以混合已提交的文件和代码块,但它们必须都来自同一个提交
  • 复制提交(来源保持不变):but pick <sha> [<sha>...] -b <new-branch> 会将副本放到一个从目标位置创建的独立新分支上;-b <existing-branch> 会将副本放到该已应用分支的最新提交上;--above <branch> -b <new-name>(或 --below)则会将副本放到在该位置堆叠的新分支上。pick 按给定顺序应用来源,因此请按从最早到最新的顺序传入。对于未应用的分支,请使用 git log --reverse <target>..<branch> 列出 SHA,并用 git show 或 but show <sha> 检查;but diff 和 but branch show 为其打印的 ID 均不可用。
  • 将已提交的更改移入现有提交或 @:对于已提交的文件,使用 but squash <commit-id>:<file-id> -t <commit-id-or-@>;对于已提交的代码块,使用 but squash <commit-id>:<file-id>:<hunk-id> -t <commit-id-or-@>;所有来源必须来自同一个提交
  • 将已提交的更改移入所选位置的新提交:对于已提交的文件,使用 but move <commit-id>:<file-id> --above <commit-or-branch> -m "<msg>";对于已提交的代码块,使用 but move <commit-id>:<file-id>:<hunk-id> --above <commit-or-branch> -m "<msg>"(也支持 --below、--branch 和 --unstack)
  • but split 和对已提交更改执行的 but move 也需要通过 -m 或 -F 指定新提交的消息;两者都不传入时,提交消息为空。对于整个提交或分支来源,but move 不接受 -m/-F。
  • 将提交移至分支顶端:but move <commit-id> -b <branch>
  • 将提交或已提交的更改移至命名的新分支:but move <source> --above <branch> -b <new-name>(也支持 --below);要移至独立分支,请使用 but move <source> --unstack -b <new-name>。省略 -b 会自动生成名称。堆叠或取消堆叠分支来源时不支持命名。
  • 丢弃:but discard <id> [<id>...] — 接受分支、提交、已提交的更改、未提交的更改,或表示丢弃所有未提交更改的 @
  • 推送:but push <top-branch> — 推送所选分支及其祖先;要更新一个堆叠,只需选择一次其顶端分支,切勿循环执行。以非交互方式运行时,不带参数的 but push 会推送所有尚未推送的工作 — 每个堆叠推送一次(其最顶端的未推送分支,包含其祖先),输出会列出它推送的每个分支。任何堆叠推送失败时,命令都会以非零状态退出;已推送的堆叠会保持已推送状态,修复失败原因后可安全地重新运行(已是最新的堆叠会跳过)。but push 和 but pr new 会替换远程分支:如果其他人可能已推送到该分支,请先引入他们的提交(参见“引入推送到分支自身远程仓库的提交”)。
  • 创建 PR:but pr new <branch-name> [-m "Title..."] [-F - | -F <file>] [-t] [--draft] — 会先自动推送;不要在此之前运行 but push

任务配方

从 main 更新工作区

对于“获取 main 的最新内容”“更新/同步此工作区”“变基到 main 上”或“拉取 main”:

  1. but pull — 一条命令即可;无需预先检查。其输出会报告更新后的状态,且 but undo 可以撤销更新。与更新冲突的未提交更改会显示冲突标记,并标为 {conflicted};请按照“未提交文件中的冲突”一节解决。
  2. 如果提交出现冲突,请按照“解决冲突提交”一节中的 but resolve conflicts <branch> 和 but resolve apply 循环逐个分支解决。当 HEAD 为 gitbutler/workspace 时,but pull 会将 but resolve <commit> 打印为下一步;这是编辑模式,仅用于运行代码以完成冲突解决。

but pull --check 会回答“是否会产生冲突?”,但不会更新。不要将它用作例行的预检查;只有在用户要求预览、需要 fetch、仓库策略要求执行此操作、其他 Agent 的分支可能发生变化,或已应用的分支刚刚合并到上游时才使用(参见规则 3)。

将已应用的分支变基到最新目标上,就是执行 but pull — 绝不要使用 move、config target、unapply 或原始的 git pull/git rebase。状态中显示的基准是最后一次 FETCHED 的状态:当 git log 显示远程目标(例如 origin/main)领先于该状态时,这正是 but pull 会 fetch 并应用的更新 — 目标设置并未过时,重新指定目标绝不是解决办法。Pull 会保留未提交更改;没有 stash,因此不要手动还原文件。

引入推送到分支自身远程仓库的提交

but pull 只会变基到目标上。有人推送到你分支远程仓库的提交会显示在 but status 的 (upstream: on <remote>/<branch>) 下方、该分支自身提交的上方;如果状态可能已过时,请运行 but pull --check 以 fetch。

  1. 在对方推送到的分支上运行一次 but branch update <branch>。
  2. 如果目标也发生了变化,再运行 but pull。先 update,再 pull。如果该分支自上次推送后已被变基(例如先前运行过 but pull),update 会留下共享提交的空副本 (no changes)。如果只有一个分支,用其 #N 引用执行 but discard 丢弃该副本。如果是在堆栈中,则用 but undo 撤销 update 并告知用户,因为 update 也可能将提交移到较低的分支。即使 (upstream: …) 仍列出提交,也不要再次 update,因为第二次 update 会重复提交。
提交选定的文件或代码块
  1. but diff — 显示未提交更改的文件 ID 和代码块 ID。不要先运行不带参数的 but status。
  2. 如果整个文件都要纳入提交,请使用文件 ID;如果只提交文件的一部分,请使用代码块 ID(<file-id>:<hunk-id>)。省略不想提交的 ID。对于整个文件,也可以使用仓库根目录相对路径或 dir/ 前缀(but commit -b feat -m "msg" src/ui/ README.md;不要使用 ./ 或通配符),因此不要通过管道将 but diff 传给 grep 来构建 ID 列表。
  3. but commit -b <branch> -m "<msg>" <id1> <id2> — 如果分支不存在,会自动创建,因此无需事先运行 but branch new。
  4. 如果任务要求确认还剩哪些更改,请添加 --status-after;否则,创建提交后的结果就足够了。

边缘情况:如果想要和不想要的编辑位于同一个 diff 代码块中,GitButler 无法按 ID 拆分该代码块。仅当任务要求保留该代码块中的部分内容为未提交状态时,才临时编辑工作树以隔离想要的行,提交这些 ID,然后恢复剩余行,使其保持未提交状态。

Amend 到现有提交中
  1. 运行 but status -fv 获取目标提交 ID 和文件 ID;如果只纳入文件的一部分,则运行 but diff 获取代码块 ID。
  2. but amend -t <commit-id> <id> <id> — 每个目标提交运行一条命令。对于多个目标提交,如果每个目标都是 change-ID 引用,请用 && 串联 amend 命令;否则逐个运行,并使用 --status-after 获取最新引用。
拆分现有提交

对于二路拆分,可将选定的已提交文件或代码块直接移入一个新提交:

  1. but diff <source-commit-id> — 读取已提交文件和代码块的 ID。
  2. but split <committed-file-or-hunk-id> [<committed-file-or-hunk-id>...] -m "<message>" — 源可以混用文件和代码块,但必须来自同一个提交。该命令会立即在源提交上方创建指定的提交,并将未选中的更改和原消息留在源提交中。新提交无需后续执行 reword。
  3. 只有当下一步需要工作区 ID 或变更结果未提供的详情时,才添加 --status-after。

对于两个以上的替换提交,可使用剩余源提交中的已提交更改重复执行 but split ... -m "<message>";如果要重建整个提交,则使用下面的取消提交并重新提交工作流:

  1. 需要源提交、分支名称或放置锚点时,执行 but status -fv。
  2. 在同一次 shell 调用中执行 but uncommit <source-commit-id> && but diff,即可显示提交的更改并打印生成的文件和代码块 ID。
  3. 从该未提交的差异中选择替换内容,而不是从旧的已提交差异中选择。
  4. 根据用户描述确定所需的时间顺序,然后从最早的提交开始,串联执行 but commit 命令来创建替换提交。不要为了匹配按最新到最旧顺序显示提交的 but status 而颠倒顺序。-b <branch> 会将每个新提交放到该分支的顶部——如果拆分的提交上方原本有其他提交,替换提交现在会位于这些保留提交上方。
  5. 如果该分支中的提交必须保留在替换提交上方,请将保留的提交块移回顶部,不要试图调整锚点。 不要使用 --above <top>/--below <top> 作为锚点(每次插入都会重写历史,使 sha/#N 锚点失效)。将整个提交块一起移动,以保持其内部顺序不变:在提交命令链末尾追加 && but move <preserved-id> [<preserved-id>...] -b <branch>。第 1 步中的 Change-ID 引用仍然有效;如果任何保留的引用基于 sha 或以 #N 结尾,请先等待新输出。
  6. 将不需要的更改保留为未提交状态。按从最早到最晚的顺序创建替换提交后,它们会在状态中按从最新到最早的顺序显示,这是正确的;不要重新排序。需要检查最终顺序时,在最后一个变更命令中添加 --status-after。
重新排序提交

but status 按从最新/顶部到最早的顺序显示提交,而任务说明通常按从最早到最晚的顺序列出历史记录——移动前请先转换顺序。

  1. 执行一次 but status 获取提交 ID(只有还需要文件详情时才使用 -fv)。
  2. but move <source> --below <target-commit> 会将源提交放到目标提交正下方,即在从最早到最晚的历史中更早的位置。--above 会将其放到目标正上方,即更新的位置。but move <source> -b <branch> 会将其移至分支顶部/最新位置。
  3. 对于相邻的提交块,只执行一次移动,并任选一种方式指定锚点:but move <block-id> <block-id> --below <following-commit-id> 或 but move <block-id> <block-id> --above <preceding-commit-id>。选择提交块之外的锚点;源的顺序无关紧要,提交块会保持内部顺序。需要检查最终顺序时添加 --status-after;不要再次移动锚点或提交块中的成员。
  4. 其他重新排序操作应尽量减少移动次数。
压缩提交
  1. 执行 but status 获取提交 ID 和顺序。
  2. 按位置指定源提交,并使用 -t 指定结果/目标提交:but squash <source> [<source>...] -t <target> -m "<new message>"。
  3. 要将整个分支压缩为一个提交,只需传入分支,不使用 -t:but squash <branch> -m "<new message>"。
  4. 可基于一次状态读取,依次处理多个互不相关的分组(目标会保留其 Change-ID 引用);优先处理较新的/顶部的分组。只有当引用基于 sha 或以 #N 结尾时,才重新获取引用。
  5. 需要检查最终历史时,在最后一次 squash 命令中添加 --status-after;不要再单独执行 status 进行复核。
堆叠现有分支

要让一个现有分支依赖另一个分支:but move <child-branch-name> --above <parent-branch-name>(使用完整分支名称;调整提交顺序时使用提交 ID)。要取消堆叠:but move <branch-name> --unstack。

不要通过 uncommit + branch delete + branch new --above 来堆叠,也不要使用 but undo 来取消堆叠。

在关联工作树中工作

当任务需要单独的检出目录时,请使用工作树,例如,在主检出目录继续进行其他工作时,构建或测试某个分支。

  1. but worktree new <branch-name> 会在新目录中检出该分支;如果分支不存在,则会创建该分支(现有分支必须未在工作区中使用),并输出该目录。添加 -A <commit> 可让分支从该提交开始,而不是从工作区基准开始。由其他工具创建的工作树必须位于仓库目录之外,或列入 .git/info/exclude;否则,在主检出目录中直接运行 but commit 会将其记录到分支中。
  2. 在该目录中运行 but,或使用 but -C <path>。在该目录中,直接运行 but commit -m "<msg>" 或 but diff 会使用该工作树中的更改;直接指定路径会指向其中的文件,提交也会进入工作树所在的分支。如果 but 提示工作树“is not managed by GitBut”,则表示该工作树已归档:运行 but worktree unarchive <name>。but undo 会拒绝还原已被工作树检出的分支,因此应在该工作树中使用相反命令(uncommit、reword)来撤销错误操作。
  3. 从任何检出目录运行 but status,都会将工作树显示为独立的一列,并且各处显示的 ID 相同。@ 始终表示主工作树中未提交的更改;<worktree>:@ 可从任何位置指代关联工作树中的更改。
  4. 像处理任何分支一样推送并创建 PR(but pr new <branch-name>)。but pull 也会变基工作树分支。如果这导致某个工作树提交显示为 {conflicted},则无法通过分支名称找到它:从 but status 获取其提交 ID,并运行 but resolve conflicts <commit-id>,然后运行 but resolve apply <path>:<N> --commit <commit-id>。
  5. 完成后,运行 but worktree remove <name> 删除目录并保留分支。如果工作树中有未提交的更改,该命令会拒绝执行;-f 会丢弃这些更改。
创建或管理拉取请求

but pr new <branch-name> 会推送所选分支及其祖先,然后一步创建 PR,无需事先运行 but push。使用 heredoc 提供 -F -,或使用 -F <file>(第一行作为标题)、-t,或使用包含实际换行符的 -m(zsh/bash:-m $'Title\n\nBody'),这样就不会打开编辑器。不要将消息写入仓库内的文件,否则下一次 but commit 会将其提交。如果缺少 forge 身份验证,请让用户在终端中运行 but config forge auth;该命令在非交互运行时会拒绝执行。

如果要为堆叠分支创建 PR,请使用 but pr,不要使用 gh pr create(只有 but pr 会设置 PR 基准和堆叠元数据;gh pr create 会破坏这些设置)。要发布整个堆叠:but pr new <top-branch-name> -t。使用 but pr auto-merge|set-draft|set-ready <selector> 进行管理;选择器可以是分支名称、当前分支/堆叠 CLI ID 或数字审查 ID。详情请参阅 but skill reference。

与另一个分支存在依赖冲突

基于另一个分支的提交所做的更改,不能提交到独立分支。but commit 和 but amend 会原子性失败(“Cannot commit: N changes could not be applied”),并指出每项被拒绝的更改所依赖的分支和提交——不会提交任何内容,也不会创建 -b 分支。

仅当你的更改基于该分支的代码时,才应使用分支堆叠。拒绝也可能意味着你的代码块仅与该分支修改过的行相邻(例如文档行、锁定文件):此时,请在你的修改与对方修改之间保留一行未更改的行,或者先不要提交该代码块,等该分支合并后再提交其余内容。切勿为了绕过拒绝而丢弃或重写实际工作;请询问用户。

如果只有一个依赖分支,错误中的 Hint 会给出确切的恢复命令:若要将现有分支堆叠在其依赖分支之上,运行 but move <your-branch> --above <dependency-branch>;若目标分支尚不存在,运行 but branch new <name> --above <dependency-branch>。运行该命令后,再重试原始命令。如果错误列出的依赖项没有 Hint(存在多个依赖分支,或者依赖项就在目标分支上),请运行 but status -fv,查看相关提交位于何处,再决定如何放置。

如果恢复命令失败,切勿尝试用 uncommit、squash 或 undo 作为变通方法——请重新运行 but status -fv,确认两个分支都存在且已应用,然后使用准确的分支名称重试。

解决冲突提交(拉取、移动或重新排序后)

解决冲突时,绝对不要使用 git add、git commit、git checkout --theirs/--ours 或任何 Git 写入命令。 只能使用 but resolve 命令和直接编辑文件。以下所述的编辑模式中,冲突子模块是唯一例外。

在 GitButler 中,冲突不会中断操作:变基总会完成,发生冲突的提交会在 but status 中标记为 {conflicted}。可以从历史编辑命令(move、discard……)打印的警告、but pull 摘要或 but status 中找到这些提交。一次解决一个冲突,不要进入任何模式。每次处理一个分支,按提交从旧到新的顺序进行——流程如下:

  1. but resolve conflicts <branch> — 显示该分支最旧的冲突提交中的冲突,并按文件编号。每个冲突包含三个部分:ours(该提交变基到的新基底)、base(共同祖先)和 theirs(提交自身的版本)。如果不指定分支,它会选择第一个有冲突的分支并告知你所选分支;输出还会说明是否存在其他冲突提交。
  2. 每条命令应用一种解决方案:
    • 合并/混合内容(最常见的情况——结合双方的意图):通过 stdin(heredoc)或传入 --file <f>,将内容传给 but resolve apply <path>:<N>。内容会替换整个冲突区域;绝不要包含冲突标记。
    • 完全采用一方:使用 but resolve apply <path>:<N> --ours 或 --theirs(仅传入 <path> 会将该文件中的所有冲突都采用这一方)。
    • 将一个冲突委托给已配置的 AI 模型:使用 but resolve apply <path>:<N> --ai — 如果你掌握了上下文,请优先自行合并。
    • apply 的默认目标提交与 conflicts 相同;如果多个分支存在冲突,请使用 --commit <branch> 指定目标。
  3. 返回步骤 1,直到报告没有冲突为止。提交 ID 在这里并不稳定——每次 apply 都会重写提交;但分支名称稳定,因此请始终按分支处理所有内容,绝不要记录提交 ID。可以分步完成:仍有未解决冲突的提交会保持 {conflicted} 状态,且只包含那些冲突。

错误的解决方案可通过 but undo 撤销。

but resolve <commit-id> --ai 会使用已配置的 AI 模型一次性解决整个提交;不带提交的 but resolve --ai 会按从旧到新的顺序解决所有冲突提交——这可以作为备用方案,但通常你掌握的上下文比该模型更多,因此请优先使用 apply 自行解决冲突。

只有在必须针对解决结果运行代码时(例如在该提交上构建/测试),才使用编辑模式。 编辑模式需要 gitbutler/workspace:当仅有一个分支处于检出状态时,but resolve <commit-id> 会拒绝执行,因此请使用 apply。

  1. but resolve <commit-id> — 进入解决模式并打印冲突区域。
  2. 编辑文件,删除所有冲突标记——<<<<<<<、|||||||(共同祖先部分)、======= 和 >>>>>>>——并保留正确内容。不要跳过此步骤;不要对有冲突的提交使用 but amend。
    • 冲突子模块没有标记,且在你选择其一方之前,finish 会拒绝执行:请参阅 but skill concepts 中的 Conflict Resolution Mode。
  3. but resolve finish 会报告遗留标记、尚未提交且仍存在的更改、所有仍有冲突的提交,以及当前准确的 but resolve <id> 命令。只有当任务需要完整的最终工作区时,才在你预计会清除最后一个冲突的 finish 命令中添加 --status-after。当它提示没有剩余的冲突提交时,请停止;不要运行验证状态命令。也可以改用 but resolve cancel --force 取消,此命令会丢弃你的解决编辑;如果不带 --force,在冲突标记已检出后它会拒绝执行。
  4. 按从旧到新的顺序,对剩余的冲突提交重复上述步骤——完成较早的提交会使其上方的提交重新变基。
未提交文件中的冲突

but status 会将存在未解决合并冲突的未提交文件标记为 {conflicted};这些文件不会计入可提交的更改,也不属于 but resolve 模式的处理范围。编辑文件,使其包含所需内容(或删除文件),然后运行 but resolve <path>...,以当前状态将其标记为已解决;之后它会显示为普通的未提交更改。

Git 与 But 对照表

gitbut
git statusbut status:查看分支/堆栈/提交概览;but status -fv:查看文件详情;but diff:查看选定的脏更改
git add + git commitbut commit -b <branch> -m ... <ids>
git checkout -b + commitbut commit -b <new-branch> -m ... <ids>
git pushbut push <branch-name>
git rebase -ibut move、but squash、but reword
git rebase --ontobut move <branch> --above <new-base>
git checkout -- <file> / git restorebut discard <path>,或使用 but diff 中的块 ID
git cherry-pickbut pick
git fetchbut pull --check(会获取内容,但不会更改分支)
在其他人已推送过内容的分支上运行 git pull先运行 but branch update <branch>,然后运行 but pull
git worktree add -b <branch>but worktree new <branch>
git worktree removebut worktree remove <name>
gh pr createbut pr new <branch-name> -m "Title..."

备注

  • 在 Git 仓库中,but 无需设置。HEAD 要么是一个已检出的分支(在 but status 中标记为 [HEAD]),要么是合并所有已应用堆栈的 gitbutler/workspace。应用第二个堆栈,或在目标分支已检出时应用一个分支,都会切换到 gitbutler/workspace;取消应用,使其只剩一个堆栈后,则会重新检出该分支。无论哪种情况,命令和 ID 都相同。它确实需要一个跟踪远程默认分支的本地分支:如果命令因 "No target branch is configured" 而失败,请运行 git branch --track main origin/main(将 main 替换为默认分支的名称),不要对自己的分支使用 --set-upstream-to,否则该分支会成为目标分支。
  • but undo 会恢复整个快照,因此撤销操作之后所做的未提交编辑会丢失(but redo 可以恢复这些编辑)。请先提交这些更改。
  • 允许使用只读的 Git 检查命令(git log、git blame、git show --stat)。
  • 如果 but 显示 AGENT ACTION REQUIRED 技能警告,请运行建议的命令一次,然后重新加载/使用 GitButler 技能。如果警告再次出现,请报告问题,而不要重试。
  • 查看命令语法和标志:but skill reference
  • 查看工作区模型:but skill concepts
  • 查看工作流示例:but skill examples

评论

支持 Markdown