| name | x-rebasing-feature-branch |
| description | フィーチャーブランチをベースブランチ(main/master/developなど)にリベースし、リモートに反映するまでを1タスクとして実行するワークフローを提供します。「リベースして」「mainを取り込んで」「mainに追従して」「rebase」といった依頼、またはフィーチャーブランチがベースブランチから乖離している場合に使用します。 |
| allowed-tools | ["Bash","Read","Glob","Grep","AskUserQuestion","TaskCreate","TaskUpdate","TaskList"] |
フィーチャーブランチのリベース
完了条件
git status -sbでローカルとリモートが同期(ahead/behindなし)するまでは完了したとみなしません。Successfully rebasedの表示やワーキングツリーのクリーン化は途中経過にすぎません。
実行フローの概要
ブランチ確定→状態確認→ベースブランチ最新化→ローカルrebase→[コンフリクト解消ループ→]リモートpush→完了確認
タスク登録(実行開始時に必ず実施)
フローを開始する前に、全ステップをTaskCreateで登録します。各ステップを開始するときTaskUpdateでin_progressへ、完了したときcompletedへ更新します。
| # | subject | blockedBy |
|---|
| 1 | Step 1: ブランチを確定する | — |
| 2 | Step 2: ベースブランチを最新化する | 1 |
| 3 | Step 3: フィーチャーブランチに戻ってリベースする | 2 |
| 4 | Step 4: リモートにプッシュする | 3 |
| 5 | Step 5: 完了確認 | 4 |
実行手順
Step 1: ブランチを確定する
git branch --show-current
フィーチャーブランチの確認
フィーチャーブランチにいることを確認し、ブランチ名をFEATURE_BRANCHとして記憶します。
ベースブランチの確定
ユーザーの最初の指示にブランチ名が含まれている場合(例:「developを取り込んで」「stagingにrebaseして」)は、そのブランチ名を提示してAskUserQuestionで確認します(yesまたは正しいブランチ名を入力してもらいます)。
{
"question": "ベースブランチは「<検出したブランチ名>」でよいですか?別のブランチの場合は入力してください。",
"options": [
{ "label": "はい", "description": "<検出したブランチ名> にリベースします" }
]
}
含まれていない場合は、利用可能なローカルブランチを確認してからAskUserQuestionで確認します。
git branch | grep -v '^\*'
{
"question": "リベース先のベースブランチはどれですか?",
"options": [
{ "label": "main", "description": "mainブランチにリベースします" },
{ "label": "develop", "description": "developブランチにリベースします" },
{ "label": "staging", "description": "stagingブランチにリベースします" }
]
}
ブランチ一覧に応じてoptionsを調整し(存在するブランチのみ提示)、ユーザーが選択した値をBASE_BRANCHとして記憶します。
FEATURE_BRANCHとBASE_BRANCHの両方が確定したら次に進みます。
未コミットの変更がある場合
git status --short
変更がある場合はAskUserQuestionで対応を選択してもらいます。
{
"question": "未コミットの変更があります。どうしますか?",
"options": [
{ "label": "コミットする", "description": "変更をコミットしてからリベースします" },
{ "label": "スタッシュする", "description": "git stashで退避し、リベース後に復元します" }
]
}
ワーキングツリーがクリーンになってから次に進みます。
Step 2: ベースブランチを最新化する
Step 1で確定したBASE_BRANCHを使います。
git checkout <BASE_BRANCH> && git pull origin <BASE_BRANCH>
Step 3: フィーチャーブランチに戻ってリベースする
Step 1で記憶したFEATURE_BRANCHとBASE_BRANCHを使います。
git checkout <FEATURE_BRANCH> && git rebase <BASE_BRANCH>
コンフリクトが発生した場合は「コンフリクト発生時」セクションに進みます。発生しなかった場合はStep 4に進みます。
Step 4: リモートにプッシュする
--force-with-leaseを使うことで、他の人がプッシュした変更を誤って上書きするリスクを軽減します。
git push --force-with-lease origin <FEATURE_BRANCH>
Step 5: 完了確認
git fetch origin <FEATURE_BRANCH> && git status -sb
期待される出力は## <FEATURE_BRANCH>...origin/<FEATURE_BRANCH>のみ(ahead/behindがゼロの状態)です。aheadが残っている場合はpushが失敗または不完全なので原因を調査して再pushします。behindが残っている場合は他の誰かが先にpushしているので、ユーザーに状況を報告して指示を仰ぎます。
完了確認に成功したら、ユーザーに以下を簡潔に報告して終了します。
- ベースブランチ名と取り込んだコミット数
- コンフリクトの発生したファイルがあれば、その解消方針
コンフリクト発生時
リベース中にコンフリクトが発生した場合は、以下の手順A〜Dを繰り返します。すべて解消できてSuccessfully rebased and updated <branch>が表示されたら、メインフローのStep 4に戻ります。
A. コンフリクトの内容を把握する
コンフリクトが発生したファイルを一覧で取得します。
git diff --name-only --diff-filter=U
各ファイルをReadツールで開いて、<<<<<<<〜>>>>>>>のマーカーを全箇所読んでください。この読み取りはスキップ禁止です。merge.conflictstyle diff3が設定されている場合は3区画になります。
| マーカー範囲 | 内容 |
|---|
<<<<<<< HEAD〜` | |
| ` | |
=======〜>>>>>>> | フィーチャーブランチ側の変更内容 |
どちらかにしか存在しない行・型定義・import・設定項目がないかも確認します。
B. mergirafで構造的コンフリクトを自動解消する
まずmergirafが利用可能か確認します。
command -v mergiraf
インストールされていなければこの手順をスキップして手順Cに進みます。
インストールされていれば、コンフリクトが発生しているファイルを確認し、1ファイルずつmergiraf solveを実行します。
git diff --name-only --diff-filter=U
mergiraf solve <ファイルパス>
終了コードで次のアクションを判断します(xargsパイプラインでは終了コードがxargsのものになるため、1ファイルずつ処理します)。
| 終了コード | 意味 | 次のアクション |
|---|
| 0 | 全コンフリクトを解消 | mergirafの変更内容を報告(後述)→git add |
| 2 | 一部のコンフリクトのみ解消 | mergirafの変更内容を報告(後述)→残りを手順Cで手動解消後にgit add |
| 1 | エラー(diff3が設定されていない・対応していないフォーマットなど) | そのファイルは手順Cへスキップ(mergirafによる変更はなし) |
mergirafの変更内容を報告する
終了コードが0または2のファイルは、git addまたは手順Cの手動解消へ進む前に、mergiraf review <マージID>を実行してユーザーへ変更内容を報告します。
mergiraf review <マージID>
報告には次を含めます。
- どの箇所をどう解消したかの要約
- 終了コードが2の場合は、解消できずに残ったコンフリクト箇所
すべてのファイルを処理したあと、残マーカーがないことを確認できれば手順Dへ進みます。
git diff --name-only --diff-filter=U
C. 手動で解消方針をユーザーに確認する
mergirafで解消できなかったファイルを手動で解消します。各コンフリクト箇所の内容と解消後のコード例を提示してAskUserQuestionで承認を得ます。
[!WARNING]
--ours/--theirsは、手順Aの確認で相手側に固有の変更が一切ないと確認できた場合のみ使用できます。確認を省くと有効な変更を無言で破棄します。
--skipは、手順Aの確認でフィーチャーブランチ側(=======〜>>>>>>>)にHEADに含まれない変更が一切ないと判断できた場合のみ使用できます。そうでない場合は--oursで解消して--continueするのが正しい手順です。
{
"question": "<ファイル名> のコンフリクトをどのように解消しますか?\n\n[ベースブランチ側]\n<HEAD側の変更内容>\n\n[フィーチャーブランチ側]\n<フィーチャーブランチ側の変更内容>",
"options": [
{ "label": "両方取り込む", "description": "両方の変更をマージします(解消後のコードを提示して再確認します)" },
{ "label": "ベースブランチ側を優先", "description": "HEADの変更を採用し、フィーチャーブランチ側を破棄します。手順Aでフィーチャーブランチ側に固有の変更がないと確認済みの場合のみ選択できます" },
{ "label": "フィーチャーブランチ側を優先", "description": "フィーチャーブランチの変更を採用し、ベースブランチ側を破棄します。手順AでHEAD側に固有の変更がないと確認済みの場合のみ選択できます" },
{ "label": "このコミットをスキップ(--skip)", "description": "このコミット全体を履歴から削除します。手順Aでフィーチャーブランチ側にHEADに含まれない変更が一切ないと確認済みの場合のみ選択できます" }
]
}
D. コードを修正してリベースを継続する
承認を得たら、コードを修正してコンフリクトマーカーを除去します。リンターが自動で変更する可能性がある場合は、その影響も考慮します。
git add <解消したファイル> && git rebase --continue
「このコミットをスキップ」を選択した場合はgit addを実行せず、次のコマンドだけを実行します。
git rebase --skip
git rebase --continue(または--skip)の結果に応じて分岐します。
Successfully rebased and updated <branch>が表示された場合は、メインフローのStep 4に戻ります。
- 後続のコミットで再度コンフリクトが発生した場合は、手順A〜Cを繰り返します。同じパターンでも再度の承認を得てからコードを修正します。
注意事項
- リベースを使うべきではない場合(共有ブランチ、公開済みコミットなど)は処理を中断して、ユーザーに報告します
--forceではなく--force-with-leaseを使用します
- ベースブランチに直接force pushしません