mcpField
S
stack-rebase

stack-rebase

github.com/athola開発34376 回インストール1 日前に更新
リポジトリ

stack-rebase について

stack-rebase は、公開済みの Git ブランチスタックで、基点や途中のスライスが変わった際にリベースを連鎖させるスキルです。競合対応、安全な強制プッシュ、PR のベース更新も扱います。

主な機能

  1. 01リベースの契機を特定
  2. 02--update-refs でリベースを連鎖
  3. 03競合の解決または中止
  4. 04--force-with-lease でブランチをプッシュ
  5. 05PR のベースを更新して確認

活用シーン

  1. 01スタック先頭の PR が master にマージされた後の後続ブランチ更新
  2. 02master の進行に合わせてスタックを更新
  3. 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 の項目を作成します。

  1. stack-rebase:fetch-complete
  2. stack-rebase:trigger-identified
  3. stack-rebase:rebase-complete
  4. stack-rebase:conflicts-resolved
  5. stack-rebase:force-pushed
  6. stack-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 の出力で確認されている

コメント