stack-rebase について
stack-rebase は、公開済みの Git ブランチスタックで、基点や途中のスライスが変わった際にリベースを連鎖させるスキルです。競合対応、安全な強制プッシュ、PR のベース更新も扱います。
主な機能
- 01リベースの契機を特定
- 02--update-refs でリベースを連鎖
- 03競合の解決または中止
- 04--force-with-lease でブランチをプッシュ
- 05PR のベースを更新して確認
活用シーン
- 01スタック先頭の PR が master にマージされた後の後続ブランチ更新
- 02master の進行に合わせてスタックを更新
- 03途中のスライス変更を後続ブランチへ反映
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ athola/ claude-night-market --skill stack-rebaseインストール後、Claude Code で/stack-rebaseを入力して読み込まれたか確認します。
詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
スタックのリベース
ベース PR がマージされた場合、またはアップストリームのベースブランチが変更された場合に、PR スタック全体にわたってリベースを連鎖させます。
使用する場合
次のいずれかの状況で stack-rebase を実行します。
- スタックの最初の PR が
masterにマージされ、2 番目の PR がmasterを直接ターゲットにする必要がある masterが先に進み、スタックのブランチに新しいコミットを取り込む必要がある- スタック途中の PR が変更され、後続のブランチにその変更を取り込む必要がある
使用しない場合
- スタックがまだ公開されていない(
sanctum:stack-pushを使用) - スタックがまだ存在しない(
sanctum:stack-createを使用)
前提条件
- すべてのスライスブランチがローカルに存在する(
git branch --list) - リモートが最新の状態である(
git fetch origin) - ワーキングツリーがクリーンである(
git status) --update-refsの使用には Git 2.38 以降が必要
必須の進捗トラッキング
開始前に TodoWrite の項目を作成します。
stack-rebase:fetch-completestack-rebase:trigger-identifiedstack-rebase:rebase-completestack-rebase:conflicts-resolvedstack-rebase:force-pushedstack-rebase:prs-updated
ステップ 1:リモートの状態を取得する(fetch-complete)
git fetch origin
マージされた PR のブランチがまだリモートに存在する場合は、GitHub ではマージ後もブランチが保持されることに注意してください。 マージされたブランチ自体は、スタックの有効なベースではなくなっています。
ステップ 2:きっかけを特定する(trigger-identified)
何が変更されたかを確認します。
ケース A、ベース PR が master にマージされた場合:
以前の「ルート」だったスライスが master に含まれています。
残りのスライスはすべて master をベースにリベースする必要があります。
# Confirm the merged branch is now in master
git branch -r --merged origin/master | grep "${MERGED_BRANCH}"
ケース B、Master が先に進んだ場合:
スライスは master より遅れていますが、スタックのトポロジーは変わっていません。
ルートスライスを master をベースにリベースします。--update-refs によって、後続のブランチもすべて更新されます。
ケース C、スタック途中の変更: スライスが修正されました。 後続のスライスはすべて、そのスライスをベースにリベースする必要があります。
ステップ 3:連鎖リベースを実行する(rebase-complete)
ケース A および B:ルートスライスを master をベースにリベースする
STACK=stack/my-feature
BASE=master
# Check out the root slice
ROOT_SLICE=$(git branch --list "${STACK}/*" \
| sed 's/^[* ]*//' | sort | head -1)
git checkout "${ROOT_SLICE}"
# Rebase with --update-refs rewrites all stack branches
git rebase --update-refs origin/${BASE}
--update-refs はリフログを調べ、リベース対象のコミットを指すすべてのローカルブランチ参照を更新します。
スタック内のすべてのスライスブランチが、一度の処理で書き換えられます。
ケース C:スタック途中のスライスからリベースする
# Check out the first slice BELOW the amended one
CHILD_SLICE=stack/my-feature/add-api # example
git checkout "${CHILD_SLICE}"
git rebase --update-refs stack/my-feature/add-schema
jj による高速化(利用できる場合)
# jj rebases all descendants automatically on any change
# To rebase the whole stack onto master:
jj rebase -d master \
-r "ancestors(${STACK}/add-ui) & !ancestors(master)"
ステップ 4:競合を解決する(conflicts-resolved)
リベースが競合で一時停止した場合:
# See which file conflicts
git status
# After resolving each file:
git add <resolved-file>
git rebase --continue
リベースが完了するまで繰り返します。 競合が複雑すぎる場合は、中断して調査します。
git rebase --abort
その後、再試行する前に競合しているコミット間の差分を確認します。
ステップ 5:更新したブランチを強制プッシュする(force-pushed)
リベースが成功したら、すべてのスライスブランチをプッシュします。
最後にフェッチしてから行われたリモートの変更で上書きしないよう、--force-with-lease を使用します。
for branch in $(git branch --list "${STACK}/*" \
| sed 's/^[* ]*//' | sort); do
git push --force-with-lease origin "${branch}"
echo "force-pushed: ${branch}"
done
--force は絶対に使用しないでください(リモート変更の保護機能がなくなります)。
jj による高速化(利用できる場合)
jj git push --all --allow-new
ステップ 6:PR のベースを更新する(prs-updated)
ケース A のみ:ルートスライスがマージされ、残りのスライスを master をベースにリベースした後は、スタック内の次の PR のベースが正しくなくなっています。
GitHub CLI を使ってベースを更新します。
NEXT_PR=456 # PR number of the new stack root
gh pr edit "${NEXT_PR}" --base master
スタックのさらに下にある PR では、ベースは引き続き前のスライスブランチのままです。そのブランチは --update-refs によってすでに書き換えられているため、ベースの編集は不要です。
スタック全体に矛盾がないことを確認します。
for branch in $(git branch --list "${STACK}/*" \
| sed 's/^[* ]*//' | sort); do
pr_num=$(gh pr list --head "${branch}" \
--json number,baseRefName \
--jq '.[0] | "#\(.number) base=\(.baseRefName)"')
echo "${branch}: ${pr_num}"
done
競合を防ぐためのヒント
- スライスは小さく保ちます。PR が小さいほど、リベース時に競合が起きる範囲も小さくなります
- 頻繁にリベースします。マージ直後の master をベースに 1 日に 1 回リベースするほうが、1 週間分の差分を解決するより効率的です
- すでにレビュー中のコミットの修正は避けます。代わりに fixup コミットを作成し、マージ時に squash します
注意事項
git rebase --update-refsには Git 2.38 以降が必要です。実行前にgit versionで確認してください--update-refsを使用できない場合は、ルートから先端まで順に各スライスブランチをチェックアウトし、リベースしてください- 強制プッシュの後、GitHub は PR のレビューを自動的に古い状態としてマークします。レビュアーに再承認を依頼してください
stack-pushスキルには、リベースによって PR の SHA が変わった後にスタックの概要コメントを再投稿する方法が記載されています
完了条件
- 6 件すべての TodoWrite 項目(
stack-rebase:fetch-completeからstack-rebase:prs-updatedまで)がリベース開始前に作成され、 順番に完了としてマークされている -
git fetch originが、リベースのトリガーを特定して ケース A、B、C のいずれかに分類する前に完了している - ケース A と B では
git rebase --update-refsが中断せずに 完了している。競合が発生した場合は、続行前にそれぞれ解決されている - すべてのスライスブランチが
--force-with-leaseを指定して強制プッシュされている。--forceは 決して使用されていない - ケース A のみ:
gh pr edit <next-pr> --base masterによって新しいスタックのルートのベースターゲットが更新され、 スタック全体のトポロジーがgh pr listの出力で確認されている