关于 but
这项技能指导编码 Agent 使用 GitButler 的 `but` 命令行处理版本控制任务,重点说明如何准确选取变更、使用标识符并避免误改共享工作区。
主要功能
- 01按任务选择命令
- 02变更与提交 ID 指引
- 03安全修改规则
- 04常见 GitButler 工作流
适用场景
- 01将选定变更提交到新分支
- 02用 main 更新工作区
- 03调整提交与分支结构
- 04避免纳入其他 Agent 的修改
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gitbutlerapp/ gitbutler --skill but安装后在 Claude Code 中输入/but确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
AGENTS.mdREADME.mdstub.mdreferences/concepts.mdexamples.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:此时管道会以它们的状态退出,因此即使变更操作失败,后续的 && 仍会执行。
不可妥协的规则
- 所有写操作都使用
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并运行。 - 变更命令会打印结果,但不会附加工作区状态。只有在下一步需要生成的工作区 ID 或详情时才添加
--status-after;否则以变更结果为准,不要运行验证状态或差异的命令。 - 标记为
(merged upstream)的分支已合入;请在另一个分支上开始新工作。仅凭该标记并不意味着but pull会移除这些分支:请先运行but pull --check。but pull只会移除列为[integrated]的分支(“已合入上游并在本地移除”,或当目标没有上游时显示为“已合入 并移除”),并会对其余分支执行变基,这些分支仍会保持已应用状态。若要清除仍保持已应用状态的已合入分支,请使用but unapply <branch>(会作用于整个堆栈),而不是but discard。push和变更命令(commit、amend、squash、uncommit、reword、move)会拒绝处理已合入的分支和提交;absorb会跳过它们并显示提示;commit选择默认目标时也会跳过它们。 - 在常规
but命令之间继续执行。状态说明应与下一条命令放在同一条消息中;不要以宣布下一步but操作却不执行该操作的总结结束一轮。最后给出简洁总结。 - 优先使用此技能和
but skill reference,而不是探索性帮助命令。缺少必要语法或命令失败时,使用<command> --help;只有确实需要查找未记录的命令时才使用顶层帮助。 - 其他 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”:
but pull— 一条命令即可;无需预先检查。其输出会报告更新后的状态,且but undo可以撤销更新。与更新冲突的未提交更改会显示冲突标记,并标为{conflicted};请按照“未提交文件中的冲突”一节解决。- 如果提交出现冲突,请按照“解决冲突提交”一节中的
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。
- 在对方推送到的分支上运行一次
but branch update <branch>。 - 如果目标也发生了变化,再运行
but pull。先 update,再 pull。如果该分支自上次推送后已被变基(例如先前运行过but pull),update 会留下共享提交的空副本(no changes)。如果只有一个分支,用其#N引用执行but discard丢弃该副本。如果是在堆栈中,则用but undo撤销 update 并告知用户,因为 update 也可能将提交移到较低的分支。即使(upstream: …)仍列出提交,也不要再次 update,因为第二次 update 会重复提交。
提交选定的文件或代码块
but diff— 显示未提交更改的文件 ID 和代码块 ID。不要先运行不带参数的but status。- 如果整个文件都要纳入提交,请使用文件 ID;如果只提交文件的一部分,请使用代码块 ID(
<file-id>:<hunk-id>)。省略不想提交的 ID。对于整个文件,也可以使用仓库根目录相对路径或dir/前缀(but commit -b feat -m "msg" src/ui/ README.md;不要使用./或通配符),因此不要通过管道将but diff传给 grep 来构建 ID 列表。 but commit -b <branch> -m "<msg>" <id1> <id2>— 如果分支不存在,会自动创建,因此无需事先运行but branch new。- 如果任务要求确认还剩哪些更改,请添加
--status-after;否则,创建提交后的结果就足够了。
边缘情况:如果想要和不想要的编辑位于同一个 diff 代码块中,GitButler 无法按 ID 拆分该代码块。仅当任务要求保留该代码块中的部分内容为未提交状态时,才临时编辑工作树以隔离想要的行,提交这些 ID,然后恢复剩余行,使其保持未提交状态。
Amend 到现有提交中
- 运行
but status -fv获取目标提交 ID 和文件 ID;如果只纳入文件的一部分,则运行but diff获取代码块 ID。 but amend -t <commit-id> <id> <id>— 每个目标提交运行一条命令。对于多个目标提交,如果每个目标都是 change-ID 引用,请用&&串联 amend 命令;否则逐个运行,并使用--status-after获取最新引用。
拆分现有提交
对于二路拆分,可将选定的已提交文件或代码块直接移入一个新提交:
but diff <source-commit-id>— 读取已提交文件和代码块的 ID。but split <committed-file-or-hunk-id> [<committed-file-or-hunk-id>...] -m "<message>"— 源可以混用文件和代码块,但必须来自同一个提交。该命令会立即在源提交上方创建指定的提交,并将未选中的更改和原消息留在源提交中。新提交无需后续执行 reword。- 只有当下一步需要工作区 ID 或变更结果未提供的详情时,才添加
--status-after。
对于两个以上的替换提交,可使用剩余源提交中的已提交更改重复执行 but split ... -m "<message>";如果要重建整个提交,则使用下面的取消提交并重新提交工作流:
- 需要源提交、分支名称或放置锚点时,执行
but status -fv。 - 在同一次 shell 调用中执行
but uncommit <source-commit-id> && but diff,即可显示提交的更改并打印生成的文件和代码块 ID。 - 从该未提交的差异中选择替换内容,而不是从旧的已提交差异中选择。
- 根据用户描述确定所需的时间顺序,然后从最早的提交开始,串联执行
but commit命令来创建替换提交。不要为了匹配按最新到最旧顺序显示提交的but status而颠倒顺序。-b <branch>会将每个新提交放到该分支的顶部——如果拆分的提交上方原本有其他提交,替换提交现在会位于这些保留提交上方。 - 如果该分支中的提交必须保留在替换提交上方,请将保留的提交块移回顶部,不要试图调整锚点。 不要使用
--above <top>/--below <top>作为锚点(每次插入都会重写历史,使 sha/#N锚点失效)。将整个提交块一起移动,以保持其内部顺序不变:在提交命令链末尾追加&& but move <preserved-id> [<preserved-id>...] -b <branch>。第 1 步中的 Change-ID 引用仍然有效;如果任何保留的引用基于 sha 或以#N结尾,请先等待新输出。 - 将不需要的更改保留为未提交状态。按从最早到最晚的顺序创建替换提交后,它们会在状态中按从最新到最早的顺序显示,这是正确的;不要重新排序。需要检查最终顺序时,在最后一个变更命令中添加
--status-after。
重新排序提交
but status 按从最新/顶部到最早的顺序显示提交,而任务说明通常按从最早到最晚的顺序列出历史记录——移动前请先转换顺序。
- 执行一次
but status获取提交 ID(只有还需要文件详情时才使用-fv)。 but move <source> --below <target-commit>会将源提交放到目标提交正下方,即在从最早到最晚的历史中更早的位置。--above会将其放到目标正上方,即更新的位置。but move <source> -b <branch>会将其移至分支顶部/最新位置。- 对于相邻的提交块,只执行一次移动,并任选一种方式指定锚点:
but move <block-id> <block-id> --below <following-commit-id>或but move <block-id> <block-id> --above <preceding-commit-id>。选择提交块之外的锚点;源的顺序无关紧要,提交块会保持内部顺序。需要检查最终顺序时添加--status-after;不要再次移动锚点或提交块中的成员。 - 其他重新排序操作应尽量减少移动次数。
压缩提交
- 执行
but status获取提交 ID 和顺序。 - 按位置指定源提交,并使用
-t指定结果/目标提交:but squash <source> [<source>...] -t <target> -m "<new message>"。 - 要将整个分支压缩为一个提交,只需传入分支,不使用
-t:but squash <branch> -m "<new message>"。 - 可基于一次状态读取,依次处理多个互不相关的分组(目标会保留其 Change-ID 引用);优先处理较新的/顶部的分组。只有当引用基于 sha 或以
#N结尾时,才重新获取引用。 - 需要检查最终历史时,在最后一次 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 来取消堆叠。
在关联工作树中工作
当任务需要单独的检出目录时,请使用工作树,例如,在主检出目录继续进行其他工作时,构建或测试某个分支。
but worktree new <branch-name>会在新目录中检出该分支;如果分支不存在,则会创建该分支(现有分支必须未在工作区中使用),并输出该目录。添加-A <commit>可让分支从该提交开始,而不是从工作区基准开始。由其他工具创建的工作树必须位于仓库目录之外,或列入.git/info/exclude;否则,在主检出目录中直接运行but commit会将其记录到分支中。- 在该目录中运行
but,或使用but -C <path>。在该目录中,直接运行but commit -m "<msg>"或but diff会使用该工作树中的更改;直接指定路径会指向其中的文件,提交也会进入工作树所在的分支。如果but提示工作树“is not managed by GitBut”,则表示该工作树已归档:运行but worktree unarchive <name>。but undo会拒绝还原已被工作树检出的分支,因此应在该工作树中使用相反命令(uncommit、reword)来撤销错误操作。 - 从任何检出目录运行
but status,都会将工作树显示为独立的一列,并且各处显示的 ID 相同。@始终表示主工作树中未提交的更改;<worktree>:@可从任何位置指代关联工作树中的更改。 - 像处理任何分支一样推送并创建 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>。 - 完成后,运行
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 中找到这些提交。一次解决一个冲突,不要进入任何模式。每次处理一个分支,按提交从旧到新的顺序进行——流程如下:
but resolve conflicts <branch>— 显示该分支最旧的冲突提交中的冲突,并按文件编号。每个冲突包含三个部分:ours(该提交变基到的新基底)、base(共同祖先)和theirs(提交自身的版本)。如果不指定分支,它会选择第一个有冲突的分支并告知你所选分支;输出还会说明是否存在其他冲突提交。- 每条命令应用一种解决方案:
- 合并/混合内容(最常见的情况——结合双方的意图):通过 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>指定目标。
- 合并/混合内容(最常见的情况——结合双方的意图):通过 stdin(heredoc)或传入
- 返回步骤 1,直到报告没有冲突为止。提交 ID 在这里并不稳定——每次
apply都会重写提交;但分支名称稳定,因此请始终按分支处理所有内容,绝不要记录提交 ID。可以分步完成:仍有未解决冲突的提交会保持{conflicted}状态,且只包含那些冲突。
错误的解决方案可通过 but undo 撤销。
but resolve <commit-id> --ai 会使用已配置的 AI 模型一次性解决整个提交;不带提交的 but resolve --ai 会按从旧到新的顺序解决所有冲突提交——这可以作为备用方案,但通常你掌握的上下文比该模型更多,因此请优先使用 apply 自行解决冲突。
只有在必须针对解决结果运行代码时(例如在该提交上构建/测试),才使用编辑模式。 编辑模式需要 gitbutler/workspace:当仅有一个分支处于检出状态时,but resolve <commit-id> 会拒绝执行,因此请使用 apply。
but resolve <commit-id>— 进入解决模式并打印冲突区域。- 编辑文件,删除所有冲突标记——
<<<<<<<、|||||||(共同祖先部分)、=======和>>>>>>>——并保留正确内容。不要跳过此步骤;不要对有冲突的提交使用but amend。- 冲突子模块没有标记,且在你选择其一方之前,
finish会拒绝执行:请参阅but skill concepts中的 Conflict Resolution Mode。
- 冲突子模块没有标记,且在你选择其一方之前,
but resolve finish会报告遗留标记、尚未提交且仍存在的更改、所有仍有冲突的提交,以及当前准确的but resolve <id>命令。只有当任务需要完整的最终工作区时,才在你预计会清除最后一个冲突的finish命令中添加--status-after。当它提示没有剩余的冲突提交时,请停止;不要运行验证状态命令。也可以改用but resolve cancel --force取消,此命令会丢弃你的解决编辑;如果不带--force,在冲突标记已检出后它会拒绝执行。- 按从旧到新的顺序,对剩余的冲突提交重复上述步骤——完成较早的提交会使其上方的提交重新变基。
未提交文件中的冲突
but status 会将存在未解决合并冲突的未提交文件标记为 {conflicted};这些文件不会计入可提交的更改,也不属于 but resolve 模式的处理范围。编辑文件,使其包含所需内容(或删除文件),然后运行 but resolve <path>...,以当前状态将其标记为已解决;之后它会显示为普通的未提交更改。
Git 与 But 对照表
| git | but |
|---|---|
git status | but status:查看分支/堆栈/提交概览;but status -fv:查看文件详情;but diff:查看选定的脏更改 |
git add + git commit | but commit -b <branch> -m ... <ids> |
git checkout -b + commit | but commit -b <new-branch> -m ... <ids> |
git push | but push <branch-name> |
git rebase -i | but move、but squash、but reword |
git rebase --onto | but move <branch> --above <new-base> |
git checkout -- <file> / git restore | but discard <path>,或使用 but diff 中的块 ID |
git cherry-pick | but pick |
git fetch | but pull --check(会获取内容,但不会更改分支) |
在其他人已推送过内容的分支上运行 git pull | 先运行 but branch update <branch>,然后运行 but pull |
git worktree add -b <branch> | but worktree new <branch> |
git worktree remove | but worktree remove <name> |
gh pr create | but 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