mcpField
G
gh-stack

gh-stack

github.com/arize-ai開発12.1k59 回インストール今日更新
リポジトリ

gh-stack について

依存関係のあるブランチとプルリクエストを管理する GitHub CLI 拡張 `gh stack` の利用スキルです。コーディングエージェント向けに、スタックの作成、移動、同期、レビューを非対話形式で案内します。

主な機能

  1. 01依存ブランチスタックの作成と整理
  2. 02スタック各層間の移動
  3. 03ブランチのリベースと同期
  4. 04スタック型プルリクエストの提出
  5. 05スタック状況の確認と再構成

活用シーン

  1. 01大きな変更をレビュー可能な PR に分割
  2. 02依存する作業の下位ブランチを更新
  3. 03ブランチスタックの同期と提出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/arize-ai/phoenix --skill gh-stack

インストール後、Claude Code で/gh-stackを入力して読み込まれたか確認します。

詳細

gh-stack

gh stack は、スタックされたブランチとプルリクエストを管理するための GitHub CLI 拡張機能です。スタックとは、各ブランチがその下のブランチを基に構築される、順序付けられたブランチのリストであり、トランクブランチ(通常はリポジトリのデフォルトブランチ)を起点とします。各ブランチは、その下のブランチをベースとする1つのPRに対応するため、レビュー担当者にはその階層の差分だけが表示されます。

main (trunk)
 └── auth-layer     → PR #1 (base: main)            - bottom (closest to trunk)
  └── api-endpoints → PR #2 (base: auth-layer)
   └── frontend     → PR #3 (base: api-endpoints)   - top (furthest from trunk)

スタックの下部はトランクに最も近いブランチで、上部はトランクから最も遠いブランチです。各ブランチは、その下のブランチを引き継ぎます。移動コマンド(up、down、top、bottom)はこのモデルに従います。up はトランクから遠ざかり、down はトランクに近づきます。

このスキルを使用する場面

ユーザーが次のことを行いたい場合に、このスキルを使用します。

  • 大きな変更を、レビューしやすい小さなPRの連鎖に分割する
  • 依存関係のあるブランチのスタックを作成、リベース、プッシュ、または同期する
  • ブランチスタック内の階層間を移動する
  • スタックされたPRのステータスを確認する
  • ブランチを削除、並べ替え、または名前変更するために、スタックを解体して再構築する

前提条件

GitHub CLI(gh)v2.0以降がインストールされ、認証済みである必要があります。次のコマンドで拡張機能をインストールします。

gh extension install github/gh-stack

gh stack を使用する前に、対話型プロンプトが表示されないように git を設定します。

git config rerere.enabled true           # remember conflict resolutions (skips prompt on init)
git config remote.pushDefault origin     # if multiple remotes exist (skips remote picker)

エージェントのルール

すべての gh stack コマンドは、非対話型で実行する必要があります。 すべてのコマンド呼び出しには、プロンプト、TUI、インタラクティブメニューを回避するために必要なフラグと位置引数を含めてください。コマンドが入力を求める場合、無期限にハングします。

  1. init、add、checkout には、必ずブランチ名を位置引数として指定してください。引数なしでこれらのコマンドを実行すると、インタラクティブなプロンプトが表示されます。ブランチ名は指定どおりに使用され、前置されたり変換されたりすることはありません。そのため、gh stack add refactor/foo を実行すると、ブランチ名は refactor/foo になります。
  2. PR のタイトルを自動生成するには、必ず gh stack submit に --auto を指定してください。--auto がないと、submit は新しい PR ごとにタイトルを求めるプロンプトを表示します。
  3. gh stack view には、必ず --json を指定してください。--json がないと、エージェントが操作できないインタラクティブな TUI が起動します。ほかに適切なフラグはありません。必ず --json を渡してください。
  4. 複数のリモートが設定されている場合は --remote <name> を使用するか、あらかじめ git config remote.pushDefault origin を設定してください。これを行わないと、push、submit、sync、link、checkout でインタラクティブなリモート選択画面が表示されます。
  5. 複数のスタックで共有されているブランチは避けてください。ブランチが複数のスタックに属している場合、コマンドはコード 6 で終了します。まず、共有されていないブランチにチェックアウトしてください。
  6. コードを書く前に、依存関係の順序に基づいてスタックのレイヤーを計画してください。基盤となる変更(モデル、API、共有ユーティリティ)は下位のブランチに、依存する変更(UI、利用側)は上位のブランチに配置します。gh stack init を実行する前に、依存関係の連鎖を検討してください。
  7. ステージングとコミットには、標準の git add と git commit を使用してください。これにより、各ブランチに含める変更を完全に制御できます。ショートカットの -Am も使用できますが、デフォルトの方法にはしないでください。スタック型 PR は、各ブランチに意図の明確な論理的変更一式が含まれる場合に最も効果的です。
  8. 下位のレイヤーを変更する必要がある場合は、スタックを下に移動してください。フロントエンドのブランチで作業中に API の変更が必要だとわかった場合、現在のレイヤーで無理に対処しないでください。適切なブランチ(gh stack down、gh stack checkout、または gh stack bottom)に移動し、そこで変更を行ってコミットしてから、gh stack rebase --upstack を実行し、上に戻って作業を続けてください。
  9. 外部ツールのワークフローには gh stack link を使用してください。ブランチを外部ツール(jj、Sapling など)で管理している場合は、gh stack link branch-a branch-b を使用してください。link はローカルの追跡状態に依存せず、API を通じた PR とスタックの管理を目的としています。スタックを作成または更新するには、少なくとも 2 つのブランチまたは PR を指定してください。または、既存のスタックの最上部にブランチまたは PR を追加するには、スタック番号の後に追加するブランチまたは PR を指定してください(例:gh stack link 7 branch-c)。

以下の操作は絶対に行わないでください。いずれもインタラクティブなプロンプトまたは TUI が起動し、ハングします。

  • ❌ gh stack view または gh stack view --short — 必ず gh stack view --json を使用してください
  • ❌ --auto を付けずに gh stack submit を実行する — 必ず gh stack submit --auto を使用してください
  • ❌ ブランチ引数なしで gh stack init を実行する — 必ずブランチ名を指定してください
  • ❌ ブランチ名なしで gh stack add を実行する — 必ずブランチ名を指定してください
  • ❌ 引数なしで gh stack checkout を実行する — 必ず PR 番号またはブランチ名を指定してください
  • ❌ 別のローカルスタックがすでにそのブランチに存在する状態で gh stack checkout <pr-number> を実行する — 回避できない競合解決プロンプトが表示されます。まず gh stack unstack を使用してローカルスタックを削除してから、チェックアウトを再試行してください

スタック構造を考える

スタック内の各ブランチは、個別にレビューできる独立した論理的な作業単位を表す必要があります。各ブランチ内の変更には一貫性があり、ひとまとまりの PR として妥当なものである必要があります。

依存関係の連鎖

積み重ねたブランチは依存関係の連鎖を形成し、それぞれのブランチはその下のブランチを基盤にします。つまり、基盤となる変更は下位(先行)のブランチに入れ、それに依存するコードは上位(後続)のブランチに入れる必要があります。

コードを書く前に、レイヤーを計画してください。 たとえば、フルスタック機能は次のように構成できます(ここにある汎用的な名前ではなく、実際のタスクに合ったブランチ名を使ってください)。

main (trunk)
 └── data-models    ← shared types, database schema
  └── api-endpoints ← API routes that use the models
   └── frontend-ui  ← UI components that call the APIs
    └── integration ← tests that exercise the full stack

これは説明用の例です。ブランチ名とレイヤーの境界は、取り組んでいる内容に合わせて選んでください。重要な原則は、あるレイヤーのコードが別のレイヤーのコードに依存する場合、その依存先のコードが同じブランチか、それより下位のブランチにある必要があることです。

ブランチの命名

各レイヤーの内容がわかる、明確で説明的なブランチ名を選んでください(例:auth、api-routes、frontend)。ブランチ名は init と add に指定したとおりに使われ、先頭に何かが追加されたり、変換されたりすることはありません。スラッシュも使用でき、名前の一部として扱われます(例:gh stack add refactor/foo は refactor/foo というブランチを作成します)。

変更を意図的にステージングする

git add と git commit を直接使う主な理由は、どの変更をどのブランチに入れるかを制御するためです。作業ツリーに複数のファイルがある場合、現在のブランチ用の一部をステージングしてコミットし、その後、新しいブランチを作成して残りをステージングできます。

# You're on data-models with several new files in your working tree.
# Stage only the model files for this branch:
git add internal/models/user.go internal/models/session.go
git commit -m "Add user and session models"

git add db/migrations/001_create_users.sql
git commit -m "Add user table migration"

# Now create a new branch for the API layer and stage the API files there:
gh stack add api-routes # created & switched to the api-routes branch
git add internal/api/routes.go internal/api/handlers.go
git commit -m "Add user API routes"

これにより、各ブランチを一つの関心事に集中させられます。ブランチごとに複数のコミットを作成しても問題ありません。重要なのは、ブランチ内のすべてのコミットが同じ論理的な関心事に関連しており、別の関心事に属する変更は別のブランチに入っていることです。

新しいブランチを作成するタイミング

これまでの作業に依存する別の関心事に着手するときは、新しいブランチ(gh stack add)を作成してください。新しいブランチを作るタイミングの目安は次のとおりです。

  • バックエンドからフロントエンドの作業に切り替えるとき
  • コアロジックからテストやドキュメントに移るとき
  • 次の変更群のレビュー担当者が異なるとき
  • 現在のブランチの PR がすでにレビューに十分な規模になっているとき
一つのスタックに一つのストーリー

レビュー担当者の視点でスタックを考えてください。PR のスタックは、機能やプロジェクトについて一貫したストーリーを伝えるものであるべきです。レビュー担当者が PR を順に読み、それぞれが全体の小さく論理的な一部分となっている変更の進展を理解できるようにしてください。

単一のスタックを使う場合: すべてのブランチが同じ機能、プロジェクト、または密接に関連する取り組みの一部である場合です。作業が複数の関心事(モデル、API、フロントエンド)にまたがっていても、すべてが同じ目標に向かっているなら問題ありません。

別のスタックを作成する場合: 現在のスタックとは無関係の作業、つまり別の機能、無関係な領域のバグ修正、独立したリファクタリングに取り組む場合です。たまたま並行して作業しているという理由だけで、無関係な作業を一つのスタックに混ぜないでください。取り組みごとに gh stack init で新しいスタックを開始するか、既存のスタックに gh stack checkout で切り替えてください。

気づいた誤字の修正など、小規模で付随的な修正は、簡単な内容であれば現在のスタックに含めてもかまいません。ただし、変更が独立したプロジェクトとして大きくなった場合は、専用のスタックを作成してください。

クイックリファレンス

タスクコマンド
スタックを作成するgh stack init auth
複数のブランチでスタックを作成するgh stack init auth api frontend
既存のブランチを取り込むgh stack init existing-branch-a existing-branch-b
カスタムのトランクを設定するgh stack init --base develop branch-a
ブランチをスタックに追加するgh stack add api-routes
ブランチを追加し、すべてをステージしてコミットするgh stack add -Am "message" api-routes
ブランチをリモートにプッシュするgh stack push
特定のリモートにプッシュするgh stack push --remote origin
ブランチをプッシュしてドラフト PR を作成するgh stack submit --auto
レビュー可能な状態で PR を作成するgh stack submit --auto --open
同期する(フェッチ、リベース、プッシュ)gh stack sync
特定のリモートと同期するgh stack sync --remote origin
同期してマージ済みのブランチを削除するgh stack sync --prune
スタック全体をリベースするgh stack rebase
上位のスタックのみをリベースするgh stack rebase --upstack
トランクを除外してリベースするgh stack rebase --no-trunk
コンフリクト後に続行するgh stack rebase --continue
リベースを中止するgh stack rebase --abort
スタックの詳細を表示する(JSON)gh stack view --json
スタック内で上下のブランチに切り替えるgh stack up [n] / gh stack down [n]
最上位/最下位のブランチに切り替えるgh stack top / gh stack bottom
スタック番号でチェックアウトするgh stack checkout 7
PR でチェックアウトするgh stack checkout 42
ブランチでチェックアウトする(ローカルのみ)gh stack checkout feature-auth
現在のスタックを解体して再構成するgh stack unstack
番号を指定して特定のスタックを解体するgh stack unstack 7

ワークフロー

最初からスタックを作成する:一連の手順
# 1. Initialize a stack with the first branch
gh stack init auth
# → creates auth and checks it out

# 2. Write code for the first layer (auth)
cat > auth.go << 'EOF'
package auth

func Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // verify token
        next.ServeHTTP(w, r)
    })
}
EOF

# 3. Stage and commit using standard git commands
git add auth.go
git commit -m "Add auth middleware"

# You can make multiple commits on the same branch
cat > auth_test.go << 'EOF'
package auth

func TestMiddleware(t *testing.T) {
    // test auth middleware
}
EOF
git add auth_test.go
git commit -m "Add auth middleware tests"

# 4. When you're ready for a new concern, add the next branch
gh stack add api-routes
# → creates api-routes

# 5. Write code for the API layer
cat > api.go << 'EOF'
package api

func RegisterRoutes(mux *http.ServeMux) {
    mux.HandleFunc("/users", handleUsers)
}
EOF
git add api.go
git commit -m "Add API routes"

# 6. Add a third layer for frontend
gh stack add frontend
# → creates frontend

cat > frontend.go << 'EOF'
package frontend

func RenderDashboard(w http.ResponseWriter) {
    // calls the API endpoints from the layer below
}
EOF
git add frontend.go
git commit -m "Add frontend dashboard"

# ── Stack complete: auth → api-routes → frontend ──

# 7. Push everything and create PRs (drafts by default)
gh stack submit --auto

# 8. Verify the stack
gh stack view --json

ショートカット: より速い手順にしたい場合は、gh stack add -Am "message" branch-name を使うと、ステージ、コミット、ブランチの作成を 1 つのコマンドで実行できます。これは単一コミットのレイヤーには便利ですが、意図的にステージする手順は省略されます。

スタック途中の変更

これはエージェントにとって重要なワークフローです。上位レイヤーで作業中に、下位レイヤーで何かを変更する必要があると気づいた場合(たとえば、フロントエンドコンポーネントの作成中に API エンドポイントの追加が必要になった場合)は、適切なブランチに移動してそこで変更を加え、リベースしてください。

# You're on frontend but need to add an API endpoint

# 1. Navigate to the API branch
gh stack down
# or: gh stack checkout api-routes

# 2. Make the change where it belongs
cat > users_api.go << 'EOF'
package api

func handleGetUser(w http.ResponseWriter, r *http.Request) {
    // new endpoint the frontend needs
}
EOF
git add users_api.go
git commit -m "Add get-user endpoint"

# 3. Rebase everything above to pick up the change
gh stack rebase --upstack

# 4. Navigate back to where you were working
gh stack top
# or: gh stack checkout frontend

# 5. Continue working — the API changes are now available

重要な理由: フロントエンドブランチで API の変更を行うと、その変更は誤った PR に含まれます。API の PR には反映されず、フロントエンドの PR には無関係な API の差分が混在します。変更は必ず、論理的に適切なブランチで行ってください。

スタック途中のブランチを変更して同期する

初回の作成後にブランチを再度見直す必要がある場合(たとえば、レビューのフィードバックに対応する場合):

# 1. Navigate to the branch that needs changes
gh stack bottom
# or: gh stack checkout auth
# or: gh stack checkout 42  (by PR number)

# 2. Make changes and commit
cat > auth.go << 'EOF'
package auth
// updated implementation
EOF
git add auth.go
git commit -m "Fix auth token validation"

# 3. Rebase everything above this branch
gh stack rebase --upstack

# 4. Push the updated stack
gh stack push
マージ後の定期的な同期
# Single command: fetch, rebase, push, sync PR and stack state
gh stack sync

# Sync and automatically clean up local branches for merged PRs
gh stack sync --prune

エージェント向けの注意: 非対話型環境では、削除確認のプロンプトは表示されません。マージ済み PR のローカルブランチを削除するには、--prune を明示的に指定してください。

エージェント向けの注意: sync は GitHub 上のスタックもローカルに反映します。github.com で PR がスタックに追加されている場合、そのブランチは自動的に取得され、ローカルのスタックに追加されます。ローカルのスタックとリモートのスタックが分岐している場合(ローカルのスタックを変更した一方で、リモートのスタックも異なる形で変更されている場合)、対話型ターミナルでのみ同期時に解決方法を尋ねることができます。非対話型環境では同期が中止され(何もプッシュまたは更新されず)、ℹ Sync aborted が表示されて正常終了します。分岐を解決するには、スタックを解体して再作成してください。

スカッシュマージからの復旧

GitHub で PR がスカッシュマージされると、元のブランチのコミットはトランクの履歴に存在しなくなります。gh stack はこれを自動的に検出し、git rebase --onto を使って残りのコミットを正しく再適用します。

# After PR #1 (auth) is squash-merged on GitHub:
gh stack sync
# → fetches latest, detects the merge, fast-forwards trunk
# → rebases api-routes onto updated trunk (skips merged branch)
# → rebases frontend onto api-routes
# → pushes updated branches
# → reports: "Merged: #1"

# Verify the result
gh stack view --json
# → auth shows "isMerged": true, "state": "MERGED"
# → api-routes and frontend show updated heads

この処理中に sync でコンフリクトが発生した場合は、すべてのブランチをリベース前の状態に戻し、コード 3 で終了します。解決手順については、リベースのコンフリクトに対処するを参照してください。

リベースのコンフリクトに対処する(エージェントのワークフロー)
# 1. Start the rebase
gh stack rebase

# 2. If exit code 3 (conflict):
#    - Parse stderr for conflicted file paths
#    - Read those files to find <<<<<<< / ======= / >>>>>>> markers
#    - Edit files to resolve conflicts
#    - Stage resolved files:
git add path/to/resolved-file.go

# 3. Continue the rebase
gh stack rebase --continue

# 4. If another conflict occurs, repeat steps 2-3

# 5. If unable to resolve, abort to restore everything
gh stack rebase --abort
--json 出力の解析
# Get stack state as JSON
output=$(gh stack view --json)

# Check if any branch needs a rebase, and rebase if so
needs_rebase=$(echo "$output" | jq '[.branches[] | select(.needsRebase == true)] | length')
if [ "$needs_rebase" -gt 0 ]; then
  echo "Branches need rebase, rebasing stack..."
  gh stack rebase
fi

# Get all open PR URLs
echo "$output" | jq -r '.branches[] | select(.pr.state == "OPEN") | .pr.url'

# Find merged branches
echo "$output" | jq -r '.branches[] | select(.isMerged == true) | .name'

# Get the current branch
echo "$output" | jq -r '.currentBranch'

# Check if the stack is fully merged (all branches merged)
echo "$output" | jq '[.branches[] | .isMerged] | all'
スタックを再構成する(ブランチの削除、並べ替え、名前変更)

unstack でスタックを解体し、構造を変更してから、再度初期化します。

# 1. Remove the stack (locally and on GitHub)
gh stack unstack

# 2. Make structural changes — e.g. delete a branch, reorder, rename
git branch -m old-branch-1 new-branch-1

# 3. Re-create the stack with the new structure
gh stack init --base main new-branch-1 new-branch-2 new-branch-3

コマンド

スタックを初期化する — gh stack init

新しいスタックを作成します。位置引数として、ブランチ名を少なくとも1つ必ず指定してください。ブランチ引数を指定せずに実行すると、エージェントでは使用できない対話型プロンプトが表示されます。

gh stack init [flags] <branches...>
# Create a stack with a new branch
gh stack init auth
# → creates auth and checks it out

# Create a stack with new branches
gh stack init branch-a branch-b branch-c

# Use a different trunk branch
gh stack init --base develop branch-a branch-b

# Adopt existing branches into a stack (handled automatically if the branches exist)
gh stack init branch-a branch-b branch-c
フラグ説明
-b, --base <branch>トランクブランチ(デフォルトはリポジトリのデフォルトブランチ)

動作:

  • ブランチ名は指定どおりに作成されます(スラッシュも使用でき、そのまま保持されます)
  • まだ存在しないブランチを作成します(トランクブランチから分岐します)
  • 既存のブランチは自動的に取り込まれ、存在しないブランチはトランクから作成されます
  • リスト内の最後のブランチにチェックアウトします
  • git rerere を有効にし、リベース時の競合解決を記憶します。リポジトリで初めて実行すると、確認プロンプトが表示されることがあります。これを避けるには、事前に git config rerere.enabled true を設定してください

ブランチを追加する — gh stack add

現在のスタックの最上位に新しいブランチを追加します。最上位のブランチ上で(または、スタックにまだブランチがない場合はトランク上で)実行する必要があります。ブランチ名を必ず指定してください。指定せずに実行すると、対話型プロンプトが表示されます。

gh stack add [flags] <branch>

推奨ワークフロー — ブランチを作成してから、標準の git コマンドを使用する:

# Create a new branch and switch to it
gh stack add api-routes

# Write code, stage deliberately, and commit
git add internal/api/routes.go internal/api/handlers.go
git commit -m "Add user API routes"

# Make more commits on the same branch as needed
git add internal/api/middleware.go
git commit -m "Add rate limiting middleware"

ショートカット — ステージング、コミット、ブランチ作成を1つのコマンドで行う:

# Create a new branch, stage all changes, and commit
gh stack add -Am "Add API routes" api-routes

# Create a new branch, stage tracked files only, and commit
gh stack add -um "Fix auth bug" auth-fix
フラグ説明
-m, --message <string>このメッセージでコミットを作成します
-A, --all未追跡ファイルを含むすべての変更をステージングします(-m が必要)
-u, --update追跡対象のファイルのみをステージングします(-m が必要)

動作に関する注意:

  • -A と -u は同時に指定できません。
  • 現在のブランチにコミットがない場合(たとえば init の直後)、add -Am は新しいブランチを作成せず、現在のブランチに直接コミットします。
  • ブランチ名は指定どおりに使用されます。 gh stack add refactor/foo は refactor/foo という名前のブランチを作成します。名前に接頭辞が付加されたり、変換されたりすることはありません。ブランチ名を指定せずに -m を指定すると、コミットメッセージから日付+スラッグ形式(例:03-24-add_api_routes)の名前が自動生成されます。
  • スタックの最上位ではないブランチから実行すると、コード5で終了し、"can only add branches on top of the stack" と表示されます。まず gh stack top を使用して切り替えてください。
  • 未コミットの変更: -Am を指定せずに gh stack add branch-name を実行すると、作業ツリー内の未コミットの変更(ステージング済み、未ステージングを問わず)は新しいブランチに引き継がれます。これは標準の git の動作であり、作業ツリーは変更されません。新しいブランチをクリーンな状態で開始したい場合は、add の実行前に現在のブランチで変更をコミットするか、スタッシュしてください。

ブランチをリモートにプッシュする — gh stack push

スタック内のすべてのブランチをリモートにプッシュします。

gh stack push [flags]
# Push all branches
gh stack push

# Push to specific remote
gh stack push --remote upstream
フラグ説明
--remote <name>プッシュ先のリモート(複数のリモートがある場合に使用)

動作:

  • アクティブな(マージされていない)すべてのブランチをアトミックにプッシュします(--force-with-lease --atomic)
  • プルリクエストは作成も更新もしません。その場合は gh stack submit を使用してください

出力(stderr):

  • Pushed N branches の概要
ブランチを送信して PR を作成する — gh stack submit

スタック内のすべてのブランチをプッシュし、GitHub で PR を作成します。必ず --auto を指定してください。指定しないと、submit は新しいブランチごとに PR タイトルの入力を求めます。

# Submit and auto-title new PRs (required for non-interactive use)
gh stack submit --auto

# Submit and create PRs as ready for review (not drafts)
gh stack submit --auto --open
フラグ説明
--autoプロンプトを表示せずに PR タイトルを自動生成します(非対話型での使用には必須)
--open新規および既存の PR をレビュー可能な状態にします
--remote <name>プッシュ先のリモート(複数のリモートがある場合に使用)

動作:

  • 有効な(マージされていない)ブランチをすべてアトミックにプッシュします(--force-with-lease --atomic)
  • PR がない各ブランチに新しい PR を作成します(ベースには、マージされていない最初の祖先ブランチが設定されます)
  • PR の作成後、GitHub 上でそれらを スタック としてリンクします(リポジトリでスタックが有効になっている必要があります)
  • スタック内のすべての PR がすでにマージされている場合、スタックは完了しており、拡張できません。submit は、マージされていないブランチを自動的にフォークして、トランクを起点とする新しいスタックを作成し、GitHub に登録します。マージ済みのスタックはそのまま残ります。
  • スタックを利用できない場合(終了コード 9)、リポジトリでスタック型 PR が有効になっていません。対話型モードでは、submit は代わりに通常の(スタック化されていない)PR を作成するかどうかを確認します。非対話型モードでは、終了コード 9 で終了します。
  • すでに PR があるブランチの PR メタデータを同期します

PR タイトルの自動生成(--auto):

  • ブランチ上のコミットが 1 つ → コミットのサブジェクトを PR タイトルに、コミット本文を PR 本文に使用します
  • ブランチ上のコミットが複数 → ブランチ名を人が読みやすい形式に変換(ハイフン/アンダースコアをスペースに変換)してタイトルにします

出力(stderr):

  • 新しく作成した PR ごとに Created PR #N for <branch>
  • 既存の PR ごとに PR #N for <branch> is up to date
  • Pushed and synced N branches の概要

ローカル追跡なしでブランチをスタックとしてリンクする — gh stack link

ローカルに追跡状態を作成せずに、GitHub 上で PR をスタックとしてリンクします。jj、Sapling、git-town などの他のツールでスタック型ブランチを管理していて、API を通じて GitHub のスタック型 PR を作成するだけでよい場合に推奨される方法です。

gh stack link [flags] <stack-number | branch-or-pr> <branch-or-pr> [...]
# Link branches into a stack (pushes, creates PRs, creates stack)
gh stack link branch-a branch-b branch-c

# Use a different base branch and mark PRs as ready for review
gh stack link --base develop --open branch-a branch-b branch-c

# Link existing PRs by number
gh stack link 10 20 30

# Add branches to an existing stack of PRs
gh stack link 42 43 feature-auth feature-ui

# Append to the top of an existing stack by its stack number
# (7 is a stack number; only the new PRs/branches are listed)
gh stack link 7 48 feature-auth

最初の引数がスタック番号の場合、残りの引数はそのスタックの最上部に追加されるため、現在の PR を再度列挙する必要はありません。スタックにすでに含まれている引数はスキップされ、別のスタックにある引数は拒否されます。数値の最初の引数は、既存のスタックと一致する場合にのみスタックとして扱われます。一致しない場合は、PR またはブランチとして扱われます。

フラグ説明
--base <branch>スタックの最下部のベースブランチ(デフォルト:main)
--open新規および既存の PR をレビュー可能な状態にします
--remote <name>プッシュ先のリモート(複数のリモートがある場合に使用)

動作:

  • 引数はスタックの順序(下から上)で指定します
  • 各引数にはブランチ名または PR 番号を指定できます。数値の引数は最初に PR 番号として扱われ、その番号の PR が存在しない場合はブランチ名として扱われます
  • ブランチの引数は自動的にリモートへプッシュされます(強制プッシュなし、アトミック)
  • オープン中の PR がないブランチには、自動生成したタイトルと正しいベースブランチの連鎖を使って新しい PR が作成されます(最初のブランチには --base を使用し、それ以降のブランチには直前のブランチを使用します)
  • ベースブランチが想定される連鎖と一致しない既存の PR は、自動的に修正されます
  • PR がまだスタックに含まれていない場合は、新しいスタックが作成されます。一部の PR がすでにスタックに含まれている場合は、スタックが更新されます(追加のみ。既存の PR が削除されることはありません)
  • ローカルの状態は作成も変更もしません

出力(stderr):

  • Pushing N branches to <remote>...
  • 既存の PR があるブランチごとに Found PR #N for branch <name>
  • 新しく作成した PR ごとに Created PR #N for <branch> (base: <base>)
  • ベースブランチを修正した場合に Updated base branch for PR #N to <base>
  • Created stack with N PRs または Updated stack to N PRs

スタックを同期する — gh stack sync

フェッチ、リベース、プッシュ、PR の状態同期を、1 つのコマンドで実行します。日常的な同期に推奨されるコマンドです。

gh stack sync [flags]
フラグ説明
--remote <name>フェッチ元およびプッシュ先のリモート(複数のリモートがある場合に使用)
--pruneマージ済み PR のローカルブランチを削除します

実行内容(順番):

  1. リモートから最新の変更をフェッチします
  2. リモートスタックを調整します — GitHub 上のスタックをローカルに反映します。GitHub 上でスタックに PR が追加されている場合、そのブランチを取得し、ローカルスタックの末尾に追加します。ローカルとリモートのスタックが分岐している場合、非対話型ターミナルでは同期を中止します。対話型ターミナルでは、分岐を解決するための選択肢(ローカルスタックをリモート版に置き換える、GitHub 上のスタックを削除して再作成できるようにする、またはキャンセルする)を提示します。
  3. トランクをファストフォワードしてリモートに合わせます(すでに最新の場合はスキップし、分岐している場合は警告します)
  4. すべてのスタックブランチを更新後の親ブランチにカスケードリベースします(トランクが移動した場合のみ)。マージ済み PR は自動的に処理されます。競合が検出されると、すべてのブランチがリベース前の状態に復元され、コマンドはコード 3 で終了します — 解決手順についてはリベースの競合を処理するを参照してください
  5. アクティブなブランチをすべてアトミックにプッシュします
  6. GitHub から PR の状態を同期し、各 PR のステータスを報告します
  7. スタックオブジェクトを同期します — GitHub 上で、開いている PR をスタックにリンクします。PR がまだスタックに含まれていない場合は新しいスタックを作成し、すでにスタックに含まれている PR がある場合は更新します(追加のみ)。PR が 2 件以上ある場合にのみ実行されます。同期によって PR が作成されることはありません — PR の作成には gh stack submit を使用してください
  8. プルーン — 対話型ターミナルでは、マージ済み PR のローカルブランチを削除するかどうかを確認します。確認を省略するには --prune を使用してください。非対話型環境では、--prune が明示的に渡された場合のみプルーンが実行されます

出力(stderr):

  • ✓ Fetched latest changes from origin
  • Pulling N new branches from the remote stack ... の後に ✓ Pulled N new branches into the stack from the remote(リモートスタックが先行している場合)
  • ⚠ Your local stack has diverged from the stack on GitHub(スタックが分岐している場合。Local: / Remote: のチェーンを含む)
  • ℹ Sync aborted — no changes were made(同期がキャンセルされた場合)
  • ✓ Trunk main fast-forwarded to <sha> または ✓ Trunk main is already up to date
  • ✓ Rebased <branch> onto <base>(ベースが移動した場合、ブランチごと)
  • ✓ Pushed N branches
  • ✓ PR #N (<branch>) — Open(ブランチごと)
  • Merged: #N, #M(マージ済みブランチ)
  • ✓ Stack created on GitHub with N PRs / ✓ Stack updated on GitHub with N PRs / ✓ Linked to the existing stack on GitHub(PR が 2 件以上ある場合)
  • ✓ Pruned <branch> (merged)(プルーンが実行された場合、削除したブランチごと)
  • GitHub 上のスタックオブジェクトがローカルに合わせて作成または更新された場合は ✓ Stack synced、ブランチのみが同期された場合(PR が 2 件未満、またはスタック型 PR が利用できない場合)は ✓ Branches synced

スタックをリベースする — gh stack rebase

リモートからプルし、スタックのブランチを順番にリベースします。sync で競合が報告された場合や、スタックの一部だけをリベースするなど、より細かく制御する必要がある場合に使用します。

gh stack rebase [flags] [branch]
# Rebase the entire stack
gh stack rebase

# Rebase only branches from trunk to current branch
gh stack rebase --downstack

# Rebase only branches from current branch to top
gh stack rebase --upstack

# Rebase stack branches without pulling from or rebasing with trunk
gh stack rebase --no-trunk

# After resolving a conflict: stage files with `git add`, then:
gh stack rebase --continue

# Abort and restore all branches to pre-rebase state
gh stack rebase --abort
フラグ説明
--downstackトランクから現在のブランチまでのブランチのみをリベースします
--upstack現在のブランチから最上位までのブランチのみをリベースします
--no-trunkトランクをスキップし、スタックのブランチ同士のみをリベースします(フェッチせず、トランクのリベースもしません)
--continue競合を解決した後に続行します
--abort中止してすべてのブランチを復元します
--remote <name>フェッチ元のリモート(複数のリモートがある場合に使用)
引数説明
[branch]対象のブランチ(デフォルトは現在のブランチ)

競合の処理: 解決のワークフロー全体については、ワークフローのセクションにあるリベースの競合を処理するを参照してください。

マージ済み PR の検出: ブランチの PR が GitHub でマージされている場合、リベースでは --onto モードを使用して自動的に処理し、マージ先の上にコミットを正しく再適用します。

Rerere(競合の記憶): init によって git rerere が有効になるため、以前に解決した競合は今後のリベースで自動的に解決されます。

トランクをスキップするモード: --no-trunk を使用すると、リモートからのフェッチとトランクブランチを使ったリベースをスキップします。ブランチ間のリベースのみが行われます(ブランチ 2 をブランチ 1 の上に、ブランチ 3 をブランチ 2 の上にリベースするなど)。アップストリームの変更を取り込まず、スタックのブランチ同士だけを揃えたい場合に便利です。


スタックを表示する — gh stack view

現在のスタックのブランチ、PR の状態、最近のコミットを表示します。必ず --json を指定してください。 指定しない場合、このコマンドはエージェントが操作できない対話型 TUI を起動します。

# Always use --json
gh stack view --json
フラグ説明
--jsonスタックデータを JSON として標準出力に出力します(非対話型での使用では必須)

--json の出力形式:

{
  "trunk": "main",
  "currentBranch": "api-routes",
  "branches": [
    {
      "name": "auth",
      "head": "abc1234...",
      "base": "def5678...",
      "isCurrent": false,
      "isMerged": true,
      "needsRebase": false,
      "pr": {
        "number": 42,
        "url": "https://github.com/owner/repo/pull/42",
        "state": "MERGED"
      }
    },
    {
      "name": "api-routes",
      "head": "789abcd...",
      "base": "abc1234...",
      "isCurrent": true,
      "isMerged": false,
      "needsRebase": false,
      "pr": {
        "number": 43,
        "url": "https://github.com/owner/repo/pull/43",
        "state": "OPEN"
      }
    }
  ]
}

ブランチごとのフィールド:

  • name — ブランチ名
  • head — 現在の HEAD SHA
  • base — 最後に同期した時点での親ブランチの HEAD SHA
  • isCurrent — このブランチがチェックアウト中かどうか
  • isMerged — PR がマージされているかどうか
  • needsRebase — ベースブランチが祖先ではないかどうか(非線形の履歴)
  • pr — PR のメタデータ(PR が存在しない場合は省略)。state は "OPEN" または "MERGED" です。

スタック内を移動する

ブランチ名を覚えていなくても、ブランチ間を移動できます。これらのコマンドは完全に非対話型です。

gh stack up          # Move up one branch (further from trunk)
gh stack up 3        # Move up three branches
gh stack down        # Move down one branch (closer to trunk)
gh stack down 2      # Move down two branches
gh stack top         # Jump to the top of the stack (furthest from trunk)
gh stack bottom      # Jump to the bottom (first non-merged branch above trunk)

移動先がスタックの範囲外にならないよう制限されます。アクティブなブランチから移動する場合、マージ済みのブランチはスキップされます。


スタックをチェックアウトする — gh stack checkout

プルリクエスト番号またはブランチ名を指定してスタックをチェックアウトします。必ず引数を指定してください。 引数なしで gh stack checkout を実行すると、対話型の選択メニューが表示されます。

gh stack checkout <pr-number | branch>
# By PR number (pulls from GitHub)
gh stack checkout 42

# By branch name (local only)
gh stack checkout feature-auth

PR 番号(例:123)を指定すると、コマンドは GitHub 上のスタックを取得し、ブランチをプルして、スタックをローカルに設定します。スタックがすでにローカルに存在し、一致する場合は、そのブランチに切り替えます。

⚠️ エージェントへの警告: ローカルとリモートのスタックでブランチ構成が異なる場合、このコマンドはフラグで回避できない対話型の競合解決プロンプトを表示します。これを避けるには、先に gh stack unstack を実行して競合するローカルスタックを削除してから、gh stack checkout <pr-number> を再実行してください。

ブランチ名を指定した場合、コマンドはローカルで追跡されているスタックのみを対象にその名前を解決します。非対話型での使用では常に安全です。

スタックを解除する — gh stack unstack

スタックを解体して、ブランチの削除、順序の変更、名前の変更、その他の大きな変更ができるようにします。スタックを解除した後、gh stack init を使用して、目的の構成でスタックを作り直します。

引数を指定しない場合、このコマンドはアクティブなスタック、つまり現在チェックアウトされているブランチを含むスタックを対象とし、GitHub 上でスタックを解除して、ローカルの追跡情報を削除します。

特定のスタックを GitHub 上で解除するには、スタック番号を指定します。スタックがローカルでチェックアウトされているかどうかにかかわらず、リポジトリ内のどこからでも実行できます。番号を指定した場合、GitHub API を直接介してスタックが解除されます(gh stack link と同様に、ローカルの追跡情報は必要ありません)。そのスタックがローカルでも追跡されている場合は、ローカルの追跡情報も削除されます。

gh stack unstack [<stack-number>] [flags]
# Tear down the current stack (locally and on GitHub), then rebuild
gh stack unstack
gh stack init --base main branch-2 branch-1 branch-3 # reordered

# Unstack a specific stack by its number, from anywhere in the repo
gh stack unstack 7

# Only remove local tracking (keep the stack on GitHub)
gh stack unstack --local
フラグ説明
--localローカルでのみスタックを削除します(GitHub 上では保持します)。GitHub には接続しません

エージェント向けの注意: gh stack unstack <number> はリモート優先の API ラッパーです。リポジトリ内のどこからでも、ローカルで追跡されているかどうかにかかわらず、番号を指定して GitHub 上のスタックを解除でき、非対話モードでも安全に使用できます。--local は GitHub に接続しません。ローカルで追跡されていない番号と --local を組み合わせると、エラーになります。不明なスタック番号を指定すると、「GitHub 上に見つかりません」というエラーが返されます(終了コード 2)。


出力規約

  • ステータスメッセージは、絵文字のプレフィックスとともに標準エラー出力に出力されます:成功は ✓、エラーは ✗、警告は ⚠、情報は ℹ です。
  • データ出力(例:view --json)は、標準出力に出力されます。
  • 出力をパイプに渡す場合、データ出力のみが必要であれば 2>/dev/null を使用してステータスメッセージを抑制します。

終了コードとエラーからの復旧

コード意味エージェントの対応
0成功通常どおり続行します
1一般的なエラー詳細を標準エラー出力で確認します。コミットまたはプッシュの失敗を示している可能性があります
2スタック内にいないまず gh stack init を実行してスタックを作成します
3リベースの競合競合しているファイルパスを標準エラー出力から特定して競合を解決し、gh stack rebase --continue を実行します
4GitHub API の失敗gh auth status を確認し、コマンドを再試行します
5無効な引数コマンドの呼び出し方を修正します(フラグと引数を確認します)
6対象の特定が必要ブランチが複数のスタックに属しています。まず gh stack checkout <specific-branch> を実行して、共有されていないブランチに切り替えます
7リベースがすでに進行中競合を解決してから gh stack rebase --continue を実行するか、最初からやり直すには gh stack rebase --abort を実行します
8スタックがロックされている別の gh stack プロセスがスタックファイルを書き込み中です。ロックは 5 秒後にタイムアウトするため、待ってから再試行します
9スタックされた PR を利用できないリポジトリでスタックされた PR が有効になっていません。対話モードでは、submit によって通常の(スタックされていない)PR の作成が提案されます

既知の制限事項

  1. スタックは厳密に直線的です。 分岐するスタック(1 つの親に複数の子を持つ構成)はサポートされていません。各ブランチが持つ親は厳密に 1 つで、子は最大 1 つです。並行する作業の流れが必要な場合は、別々のスタックを使用してください。
  2. スタックの曖昧さは解消できません。 現在のブランチが複数のスタックのトランクである場合、コマンドはコード 6 でエラーになります。まず、共有されていないブランチをチェックアウトしてください。
  3. リモートが複数ある場合は --remote または設定が必要です。 リモートが複数設定されている場合は、push、sync、rebase の実行前に --remote <name> を指定するか、git config で remote.pushDefault を設定してください。
  4. PR のマージ: CLI からスタックされた PR をマージする機能は、まだサポートされていません。PR をマージするには、ブラウザーで PR の URL を開くようユーザーに案内してください。
  5. リモートスタックのチェックアウトには PR 番号が必要です。 ブランチ名を指定した checkout が機能するのは、ローカルで追跡されているスタックのみです。GitHub からスタックを取得するには、PR 番号(例:gh stack checkout 123)を使用してください。
  6. PR のタイトルと本文は自動生成されます。 submit の実行時にカスタムの PR タイトルや本文を設定するフラグはありません。タイトルと本文は、コミットメッセージとフッターから生成されます。作成後に PR のタイトルと本文を変更するには、gh pr edit を使用してください。

コメント