non-interactive-git-rebase について
GIT_SEQUENCE_EDITOR を使って Git の対話型 rebase をスクリプト化する開発者向けガイドです。コミットの並べ替え、削除、統合、分割、メッセージ変更を扱い、事前バックアップと結果検証を求めます。
主な機能
- 01準備済み todo ファイルによる対話型 rebase
- 02コミットの並べ替え、削除、統合、分割
- 03edit と amend による非対話的なメッセージ変更
- 04--exec による一括編集
- 05バックアップブランチと結果検証の必須化
活用シーン
- 01コミットの並べ替えや削除をスクリプト化
- 02作者やメッセージを一括変更
- 031つのコミットを複数に分割
- 04履歴変更の前後でリポジトリ内容を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ facebook/ hermes --skill non-interactive-git-rebaseインストール後、Claude Code で/non-interactive-git-rebaseを入力して読み込まれたか確認します。
詳細
非対話型 Git リベース
概要
通常、git rebase -i を実行すると、ToDo リストを編集するためのエディターが開きます。GIT_SEQUENCE_EDITOR に、あらかじめ用意したファイルで ToDo ファイルを置き換えるコマンドを設定すると、並べ替え、削除、スカッシュ、編集、修正など、あらゆる対話型リベース操作をスクリプトだけで実行できます。
重要:必ずバックアップブランチを作成する
リベースの前に、必ずバックアップブランチを作成してください:
git branch backup-before-rebase HEAD
リベースが成功し、検証に合格した後も、バックアップブランチを自動で削除しないでください。 ユーザーが自動クリーンアップを明示的に要求した場合を除き、削除する前に必ずユーザーに確認してください。バックアップを残しておくコストは低く、後から問題に気づく可能性もあります。
基本的な手法
GIT_SEQUENCE_EDITOR の仕組み
エディターには ToDo ファイルのパスが $1 として渡されます。このファイルを上書きするコマンドであれば、どれでも機能します:
# Replace the todo with our prepared version
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
Git はデフォルトの ToDo(すべて pick の行)を生成してから、cp /tmp/prepared-todo <todo-file> を呼び出し、ファイルを用意した内容に置き換えます。その後、Git は書き換えられた ToDo を実行します。
ToDo ファイルの準備
実際にはリベースせずにデフォルトの ToDo を取り出すには、ファイルにコピーした後、エディターを失敗させて Git を中断します:
GIT_SEQUENCE_EDITOR='cp $1 /tmp/default-todo && false' git rebase -i <base>
false はゼロ以外のステータスで終了するため、Git はリベースを中断します。ToDo ファイルは手元に残り、リベースは実行されていません。
または、git log から直接作成できます:
git rev-list --reverse <base>..HEAD | while read hash; do
echo "pick $hash $(git log --oneline -1 $hash | cut -d' ' -f2-)"
done > /tmp/prepared-todo
その後、ファイルを編集します。行を並べ替えたり、pick を drop/squash/edit などに変更したりします。
一括修正に --exec を使う
--exec <cmd> フラグを指定すると、ToDo 内の各 pick の後に exec <cmd> の行が挿入されます。GIT_SEQUENCE_EDITOR=:(何もせず成功し、デフォルトの ToDo を変更しないコマンド)と組み合わせると、各コミットの再適用後にコマンドを実行できます。エディターは不要です。
範囲内のすべてのコミットの作成者メールアドレスを変更する:
GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec \
'if [ "$(git log -1 --format=%ae)" != "[email protected]" ]; then git commit --amend --no-edit --author="Name <[email protected]>"; fi'
コミットメッセージのパターンを変更する:
GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec \
'git commit --amend -m "$(git log -1 --format=%B | sed s/old/new/)"'
操作
コミットの並べ替え
ToDo ファイル内の行を並べ替えます。例 — コミット abc123 を先頭に移動する:
# Build todo with abc123 first, then everything else in original order
{
echo "pick abc123 The commit to move first"
git rev-list --reverse <base>..HEAD | while read hash; do
full=$(git rev-parse abc123)
[ "$hash" != "$full" ] && echo "pick $hash $(git log --oneline -1 $hash | cut -d' ' -f2-)"
done
} > /tmp/prepared-todo
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
コミットの削除
ToDo から行を削除するか、pick を drop に変更します。
コミットメッセージの書き換え
ToDo では決して reword を使わないでください。 reword コマンドは新しいメッセージを入力する対話型エディターを開くため、GIT_SEQUENCE_EDITOR ではスクリプト化できません。代わりに edit を使い、-m を指定して修正してください:
# Step 1: Prepare todo with 'edit' on commits to reword
# Step 2: At each stop, amend the message non-interactively:
git commit --amend -m "New subject line
New body text."
# Step 3: Continue to the next stop
git rebase --continue
スカッシュ/フィックスアップ
pick を squash または fixup に変更します。スカッシュの場合は、スカッシュ後に開くコミットメッセージ用エディターにも GIT_SEQUENCE_EDITOR が必要です:
# Squash second commit into first, keep first message
# In the todo: pick aaa, fixup bbb
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
コミットの分割
ToDo で edit が必要です。リベースが停止したら、分割するコマンドを実行してから続行します。
# Step 1: Prepare todo with 'edit' on the target commit
sed 's/^pick TARGET_HASH/edit TARGET_HASH/' /tmp/default-todo > /tmp/prepared-todo
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
# Step 2: Rebase stops at the target commit. Split it:
git reset HEAD~1
# Step 3: Selectively stage and commit pieces.
# If the commit has N hunks in a file and you want hunk 3:
printf 'n\nn\ny\n' | git add -p path/to/file
git commit -m "First piece: just hunk 3"
# Stage the rest
git add .
git commit -m "Second piece: remaining changes"
# Step 4: Continue the rebase
git rebase --continue
git add -p によるハンク選択の自動化: printf 内の各 y/n は、順番に各ハンクに対応します。ハンクが十分に離れている(ファイル内の異なる領域にある)場合は、安定して動作します。ハンクが隣接していると、Git がそれらを 1 つのハンクとして表示する場合があり、その場合はまず s(分割)を入力する必要があります:
# Split first hunk, then accept/reject sub-hunks
printf 's\ny\nn\ny\n' | git add -p path/to/file
別の方法 — パッチを使った分割(複雑なケースでは、こちらのほうが信頼性が高いです):
git reset HEAD~1
git diff path/to/file > /tmp/full.patch
git checkout -- path/to/file
# Apply only the lines you want for commit 1
# (manually prepare /tmp/part1.patch from full.patch)
git apply /tmp/part1.patch
git add path/to/file
git commit -m "First piece"
git apply /tmp/full.patch # won't re-apply already-applied hunks
# Or: git checkout <original-hash> -- path/to/file
git add .
git commit -m "Second piece"
検証
リベース後は、(意図的にコミットを削除した場合を除き)ツリーの内容がリベース前と同一であることを検証します:
git diff backup-before-rebase HEAD --stat
# Empty output = content preserved correctly
よくある間違い
| 間違い | 修正方法 |
|---|---|
ToDo で reword を使う | reword は対話型エディターを開きます。代わりに edit と git commit --amend -m を使います |
| 以前のリベース後に ToDo ファイル内のハッシュが間違っている | git log から再生成します。ハッシュはリベースのたびに変わります |
git add -p で予期しないハンクが選択される | コンテキストが重なるとハンクが結合する場合があります。代わりにパッチを使って分割します |
| バックアップブランチを早まって削除する | 削除する前に必ずユーザーに確認してください。後から問題に気づく可能性があります |
クイックリファレンス
| 操作 | コマンド |
|---|---|
| todo を抽出 | GIT_SEQUENCE_EDITOR='cp $1 /tmp/default-todo && false' git rebase -i <base> |
| 並べ替え/削除 | GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base> |
| メッセージを書き換え | 上記と同様に、todo で edit を指定し、git commit --amend -m "..." と --continue を実行します |
| スカッシュ(メッセージを保持) | 上記と同様に、todo で fixup を指定します |
| スカッシュ(メッセージを編集) | 上記と同様に、squash を指定してからメッセージエディターで編集します |
| 分割 | 上記と同様に、edit を指定してから手動で分割し、--continue を実行します |
| 一括 amend | GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec '<cmd>' |