graphite

graphite

使用 Graphite (gt) 處理堆疊式 PR——建立、導覽與管理 PR 堆疊。

8星標
2分支
更新於 2026/2/18
SKILL.md
readonlyread-only
name
graphite
description

使用 Graphite (gt) 處理堆疊式 PR——建立、導覽與管理 PR 堆疊。

Graphite 技能

使用 Graphite (gt) 建立、導覽與管理堆疊式 Pull Request。

快速參考

我想... 指令
建立新分支/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
將堆疊 rebase 到主幹 gt restack
變更分支的父分支 gt track --parent <branch>
重新命名目前分支 gt rename <new-name>
在堆疊中移動分支 gt move

什麼是好的 PR?

大致依重要性遞減排列:

  • 原子性/封閉性 - 獨立於其他變更;能通過 CI 且可單獨安全部署
  • 語意範圍狹窄 - 僅修改模組 X,或跨模組 X、Y、Z 的相同變更
  • 差異小 - (經驗法則) 總差異行數少

不要擔心建立太多 Pull Request。 建立較多 PR 永遠比建立較少 PR 更理想。

沒有任何變更是太小而不值得開 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

處理未追蹤分支 (常見於 worktree)

在建立分支前,檢查目前分支是否已被追蹤:

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

# 對整個堆疊執行 rebase
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):

## Stack Context

This stack adds a warning on the merge button when users are bypassing GitHub rulesets.

## Why?

Users who can bypass rulesets (via org admin or team membership) currently see no indication
they're circumventing branch protection. This PR threads the bypass data from the server to
enable the frontend warning (PR 2) to display it.

疑難排解

問題 解決方案
「Cannot perform this operation on untracked branch」 先執行 gt track -p main
堆疊的父分支錯誤 使用 gt track -p maingt restack
需要重新排序 PR 使用 gt move
restack 時發生衝突 解決衝突,然後 git rebase --continue
想要拆分 PR 重設提交 (git reset HEAD^),重新選擇性暫存,建立新分支
需要刪除分支 (非互動模式) gt delete <branch> -f -q
gt restack 遇到無關的衝突 改用針對性的 git rebase <target> (見下方)
Rebase 在衝突中中斷 檢查檔案是否已解決但未暫存,然後 git add + git rebase --continue

進階:在複雜堆疊中進行外科手術式 Rebase

在深度巢狀且有多個兄弟分支的堆疊中,gt restack 可能會有問題:

  • 它會 rebase 所有需要 rebase 的分支,而不只是你的堆疊
  • 可能在完全無關的分支上遇到衝突
  • 是全有全無——難以進行外科手術

何時使用 git rebase 而非 gt restack

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

  • 你只想更新堆疊中的特定分支
  • gt restack 在無關分支上遇到衝突
  • 你需要在 rebase 過程中跳過過時的提交

針對性 Rebase 工作流程

# 1. 切換到你要 rebase 的分支
git checkout my-feature-branch

# 2. Rebase 到目標分支 (例如更新後的父分支)
git rebase target-branch

# 3. 如果遇到衝突:
#    - 解決檔案中的衝突
#    - 暫存它:git add <file>
#    - 繼續:git rebase --continue

# 4. 如果某個提交已過時且應跳過:
git rebase --skip

# 5. Rebase 完成後,使用 gt modify 同步 graphite 的追蹤
gt modify --no-edit

從中斷的 Rebase 中恢復 (情境重置)

如果 rebase 被中斷 (例如 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