graphite

graphite

使用 Graphite (gt) 处理堆叠式 PR——创建、导航和管理 PR 堆栈。

8Star
2Fork
更新于 2026/2/18
SKILL.md
readonly只读
name
graphite
description

使用 Graphite (gt) 处理堆叠式 PR——创建、导航和管理 PR 堆栈。

Graphite 技能

使用 Graphite (gt) 创建、导航和管理堆叠式拉取请求。

快速参考

我想... 命令
创建新分支/PR gt create branch-name -m "message"
修改当前分支 gt modify -m "message"
向上导航堆栈 gt up
向下导航堆栈 gt down
跳转到堆栈顶部 gt top
跳转到堆栈底部 gt bottom
查看堆栈结构 gt ls
提交堆栈以供审查 gt submit --no-interactive
将堆栈变基到主干 gt restack
更改分支父级 gt track --parent <branch>
重命名当前分支 gt rename <new-name>
在堆栈中移动分支 gt move

什么是好的 PR?

按重要性大致降序排列:

  • 原子性/封闭性 - 独立于其他更改;能通过 CI 并可以安全地单独部署
  • 狭窄的语义范围 - 仅更改模块 X,或跨模块 X、Y、Z 进行相同更改
  • 小差异 -(启发式)总差异行数少

不要担心创建太多拉取请求。 创建更多 PR 总是比创建更少 PR 更可取。

没有太小的更改: 小型 PR 能让中型/大型 PR 更清晰。

只要它们能独立通过构建,始终支持创建更多 PR。


分支命名约定

在堆栈中命名 PR 时,请遵循以下语法:

terse-stack-feature-name/terse-description-of-change

例如,一个包含 4 个 PR 的堆栈:

auth-bugfix/reorder-args
auth-bugfix/improve-logging
auth-bugfix/improve-documentation
auth-bugfix/handle-401-status-codes

创建堆栈

基本工作流程

  1. 对文件进行更改
  2. 暂存更改:git add <files>
  3. 创建分支:gt create branch-name -m "commit message"
  4. 对堆栈中的每个 PR 重复
  5. 提交:gt submit --no-interactive

处理未跟踪的分支(常见于工作树)

在创建分支之前,检查当前分支是否被跟踪:

gt branch info

如果看到 "ERROR: Cannot perform this operation on untracked branch":

选项 A(推荐):临时跟踪,然后重新设置父级

  1. 跟踪当前分支:gt track -p main
  2. 使用 gt create 正常创建堆栈
  3. 创建所有分支后,将第一个新分支的父级重新设置为 main:
    gt checkout <first-branch-of-your-stack>
    gt track -p main
    gt restack
    

选项 B:暂存更改并从 main 开始

  1. git stash
  2. git checkout main && git pull
  3. 创建新分支并取消暂存:git checkout -b temp-working && git stash pop
  4. 继续使用 gt track -p maingt create

导航堆栈

# 向上移动一个分支(朝向堆栈顶部)
gt up

# 向下移动一个分支(朝向主干)
gt down

# 跳转到堆栈顶部
gt top

# 跳转到堆栈底部(主干之上的第一个分支)
gt bottom

# 查看完整的堆栈结构
gt ls

修改堆栈

修改当前分支

git add <files>
gt modify -m "updated commit message"

重新排序分支

使用 gt move 重新排序堆栈中的分支。这比尝试使用 gt create --insert 更简单。

重新设置堆栈父级

如果你在某个功能分支之上创建了堆栈,但希望它基于 main:

# 转到堆栈的第一个分支
gt checkout <first-branch>

# 将其父级更改为 main
gt track --parent main

# 变基整个堆栈
gt restack

重命名分支

gt rename new-branch-name

将提交重置为未暂存的更改

如果更改已提交,但你想以不同方式重新堆叠:

# 重置最后一次提交,保持更改未暂存
git reset HEAD^

# 重置多个提交(例如,最后 2 次提交)
git reset HEAD~2

# 查看差异以了解你正在处理的内容
git diff HEAD

提交之前

验证堆栈根植于 main

在运行 gt submit 之前,验证第一个 PR 的父级是 main

gt ls

如果第一个分支的父级不是 main

gt checkout <first-branch>
gt track -p main
gt restack

运行验证

创建每个 PR 后,运行适当的 lint、构建和测试:

  1. 参考项目的 CLAUDE.md 获取具体命令
  2. 如果验证失败,修复问题,暂存更改,然后使用 gt modify

提交和更新 PR

提交堆栈

gt submit --no-interactive

更新 PR 描述

提交后,使用 gh pr edit 设置正确的标题和描述。

重要: 永远不要使用 Bash heredoc 编写 PR 描述——shell 转义会破坏 Markdown 表格、代码块等。相反:

  1. 使用 Write 工具创建 /tmp/pr-body.md,包含完整的 Markdown 内容
  2. 使用 gh pr edit 配合 --body-file
gh pr edit <PR_NUMBER> --title "stack-name: description" --body-file /tmp/pr-body.md

PR 描述必须包含:

  • 堆栈上下文:这个堆栈的更大目标是什么?
  • 什么?(对于小更改可选):非常简洁,关注是什么而不是为什么
  • 为什么?:是什么促使了更改?为什么是这个解决方案?它如何融入堆栈?

示例(对于一个 3 个 PR 堆栈中添加警告功能的 PR):

## 堆栈上下文

此堆栈在用户绕过 GitHub 规则集时,在合并按钮上添加警告。

## 为什么?

能够绕过规则集的用户(通过组织管理员或团队成员身份)目前看不到任何提示
表明他们正在规避分支保护。此 PR 将绕过数据从服务器传递到
前端警告(PR 2)以显示它。

故障排除

问题 解决方案
"Cannot perform this operation on untracked branch" 先运行 gt track -p main
堆栈父级指向错误的分支 使用 gt track -p main 然后 gt restack
需要重新排序 PR 使用 gt move
restack 期间冲突 解决冲突,然后 git rebase --continue
想要拆分 PR 重置提交(git reset HEAD^),选择性重新暂存,创建新分支
需要删除分支(非交互式) gt delete <branch> -f -q
gt restack 遇到无关冲突 改用有针对性的 git rebase <target>(见下文)
变基在冲突中中断 检查文件是否已解决但未暂存,然后 git add + git rebase --continue

高级:复杂堆栈中的精确变基

在深度嵌套且包含许多兄弟分支的堆栈中,gt restack 可能有问题:

  • 它会变基所有需要变基的分支,而不仅仅是你的堆栈
  • 可能在完全不相关的分支上遇到冲突
  • 是全有或全无的——难以精确操作

何时使用 git rebase 而不是 gt restack

在以下情况下使用直接的 git rebase

  • 你只想更新堆栈中的特定分支
  • gt restack 在无关分支上遇到冲突
  • 你需要在变基期间跳过过时的提交

针对性变基工作流程

# 1. 检出要变基的分支
git checkout my-feature-branch

# 2. 变基到目标(例如,更新后的父分支)
git rebase target-branch

# 3. 如果遇到冲突:
#    - 解决文件中的冲突
#    - 暂存它:git add <file>
#    - 继续:git rebase --continue

# 4. 如果某个提交已过时且应跳过:
git rebase --skip

# 5. 变基后,使用 gt modify 同步 graphite 的跟踪
gt modify --no-edit

从中断的变基中恢复(上下文重置)

如果变基被中断(例如,Claude 会话上下文耗尽):

  1. 检查状态:

    git status
    # 查找 "interactive rebase in progress" 和 "Unmerged paths"
    
  2. 读取 "unmerged" 文件——它们可能已经解决(没有冲突标记)

  3. 如果已解决,只需暂存并继续:

    git add <resolved-files>
    git rebase --continue
    
  4. 如果仍有冲突标记,先解决它们,然后暂存并继续

从堆栈中删除分支

# 删除分支(非交互式,即使未合并)
gt delete branch-to-delete -f -q

# 同时删除所有子分支(上堆栈)
gt delete branch-to-delete -f -q --upstack

# 同时删除所有祖先分支(下堆栈)
gt delete branch-to-delete -f -q --downstack

标志:

  • -f / --force:即使未合并或关闭也删除
  • -q / --quiet:隐含 --no-interactive,最小化输出

删除中间分支后,子分支会自动变基到父分支。如果需要手动更新跟踪:

gt checkout child-branch
gt track --parent new-parent-branch