mcpField
R
recomposing-commits

recomposing-commits

github.com/nur-zaman開発937 回インストール15 週間前に更新
リポジトリ

recomposing-commits について

recomposing-commitsは、プルリクエスト前に機能ブランチのコミットを整理するスキルです。隔離したworktreeで履歴を再構成し、元のブランチへの適用前にレビューと明示的な承認を求めます。

主な機能

  1. 01保護ブランチや変更中の作業ツリーを拒否
  2. 02変更を分析してコミット単位に分類
  3. 03隔離したworktreeで履歴を再構成
  4. 04適用前のレビューを必須化
  5. 05適用前に内容差分を検証

活用シーン

  1. 01プルリクエスト前のWIPコミット整理
  2. 02無関係な変更のコミット分割
  3. 03曖昧なコミットメッセージの改善
  4. 04機能ブランチのコミット順序変更や分割

インストール

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

$npx skills add https://github.com/nur-zaman/git-recompose-skill --skill recomposing-commits

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

詳細

コミットの再構成

概要

整理されていないコミットを分析 → ワークツリーに隔離 → ユーザーにレビューしてもらう → 元のブランチに適用します。

基本原則: ユーザーが新しい履歴を明示的に承認するまで、元のブランチは決して変更しません。ワークツリーによる隔離とレビューの承認ゲートは必須です。「そのまま実行して」や「信頼しているから、レビューは省略して」とユーザーに言われた場合も同様です。

開始時に伝える内容: 「recomposing-commits スキルを使って、ブランチの履歴を整理します。」


安全性チェック — 最初に実行

git rev-parse --abbrev-ref HEAD

現在のブランチが次のいずれかの場合は、直ちに拒否してください: main、master、dev、develop、development、staging、production、release、または release/* や hotfix/* に一致するブランチ。

⛔ Recomposing commits on '<branch>' is not allowed.
This skill only works on feature branches.
Please switch to your feature branch first.

HEAD がデタッチ状態の場合も拒否してください(上記のコマンドでブランチ名の代わりに HEAD が出力されます):

⛔ You're in a detached HEAD state, not on a branch.
This skill needs a real branch to reset back onto at the end. Check out a branch first.

ほかの作業を行う前に、作業ツリーに未コミットの変更がないかも確認してください:

git status --porcelain

何か出力された場合は、停止してください:

⛔ You have uncommitted changes. These won't be included in the recompose,
and they'll be at risk when the original branch is reset at the end.
Please commit or stash them first.

回避用のブランチを作成してはいけません。上記のいずれかに該当する場合は作業を続行してはいけません。拒否して停止してください。


使用する場面

  • コミットが WIP/雑然としていて、PR を出す予定がある
  • 複数の無関係な変更が同じコミットに混在している
  • コミットメッセージが不明瞭(「fix」、「WIP」、「stuff」など)
  • コミットを分割、統合、または並べ替える必要がある

次の場合は使用しないでください:

  • 履歴がすでに整理されている
  • 保護されたブランチ/共有ブランチを使用している(代わりに拒否してください)
  • 再構成するコミットが1つだけである
  • 対象範囲にマージコミットが含まれている(ステップ3を参照 — 線形履歴のみ対応)

ワークフロー(ステップ1~8)

ステップ1:現在の履歴を表示する
git log --oneline

出力をユーザーに表示してください。

ステップ2:開始 SHA を尋ねる
Which commit should be the start of the recompose?
Provide the SHA of the first commit you want to reorganize.
(All commits from that SHA through HEAD will be recomposed.)

Enter SHA:

続行する前に検証してください:

# Must actually be an ancestor of HEAD
git merge-base --is-ancestor <user-sha> HEAD || echo "NOT AN ANCESTOR"

NOT AN ANCESTOR と出力された場合は、その SHA が現在のブランチの履歴に含まれていないことをユーザーに伝え、もう一度尋ねてください。

ルートコミットの例外: <user-sha> がリポジトリの最初のコミットの場合、親コミットはありません:

git rev-parse <user-sha>^ 2>&1   # will error: "unknown revision" if it's the root commit

該当する場合は、このケースは自動化されていないことを伝えて停止してください。リポジトリのルートコミットからの再構成には、このスキルでは安全に対応できない手動作業が必要です。ルートコミットより後の開始地点を選ぶか、ルートコミットの履歴を手動で処理するようユーザーに依頼してください。

それ以外の場合は、通常どおりベースを計算します:

BASE=$(git rev-parse <user-sha>^)
ステップ3:再構成するコミットを分析する
git log --oneline $BASE..HEAD
git diff $BASE HEAD

すべての差分を確認してください。論理的なグループを特定してください。複数の論理グループに登場するファイル(重複するファイル — ステップ6を参照)に印を付けてください。

対象範囲にマージコミットが含まれている場合は拒否してください:

git log --merges --oneline $BASE..HEAD

何か出力された場合:

⛔ This range contains a merge commit. This skill only handles linear history —
recomposing across a merge isn't supported. Pick a start point after the merge,
or restructure the branches involved separately.

複数の作成者がいるか確認してください(再構成すると、作成者とコミット日時は実行者のものにリセットされます):

git log --format='%ae' $BASE..HEAD | sort -u

複数のメールアドレスが表示された場合は、続行する前にユーザーに伝えてください:

⚠️  Commits in this range have multiple authors. Recomposing will reset every
new commit's author and date to yours. Continue anyway?
ステップ4:隔離されたワークツリーを作成する

必ず絶対パスを使用してください。 .git/recompose/feat/HISBA-mastra-ai のような相対パスは、ブランチ名にスラッシュが含まれる場合や、ツール呼び出し間でシェルの状態が保持されない場合に、気付かないうちに機能しなくなります。

BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
REPO_ROOT=$(git rev-parse --show-toplevel)
WORKTREE_PATH="$REPO_ROOT/.git/recompose/$BRANCH_NAME"
WORKTREE_BRANCH="recompose/$BRANCH_NAME"

以前の実行や中断された実行の残りを、まずクリーンアップしてください — そうしないと、既存のパスやブランチが原因で worktree add/-b が失敗します:

if git worktree list | grep -q "$WORKTREE_PATH"; then
  git worktree remove "$WORKTREE_PATH" --force
fi
if git rev-parse --verify "$WORKTREE_BRANCH" >/dev/null 2>&1; then
  git branch -D "$WORKTREE_BRANCH"
fi

git worktree add "$WORKTREE_PATH" -b "$WORKTREE_BRANCH"

以降の Git 操作はすべて $WORKTREE_PATH 内で行います。 元のブランチには変更を加えません。

ステップ5:コミットをステージング前の状態に戻す

絶対パスを指定して git -C を使用してください — cd に頼ってはいけません。Bash ツールを呼び出すたびに新しいシェルが起動するため、前回の呼び出しで実行した cd は引き継がれません。

git -C "$WORKTREE_PATH" reset --mixed $BASE

これで、すべての変更がワークツリー内でステージングされていない状態になり、論理的に分けて再ステージングできるようになります。

ステップ 6:論理グループごとにステージしてコミットする

すべてのコマンドで git -C "$WORKTREE_PATH" を使用してください。シェルがワークツリー内に cd していると決めつけないでください。

1 つの論理コミットに明確に属するファイルの場合:

git -C "$WORKTREE_PATH" add <file> <file2>
git -C "$WORKTREE_PATH" commit --no-verify -m "type: description"

複数の論理コミットにまたがるファイルの場合:

# git add -p requires a TTY — run it inside a single shell block
cd "$WORKTREE_PATH" && git add -p <file>   # stage hunk by hunk: y/n/s/e
git -C "$WORKTREE_PATH" commit --no-verify -m "type: first concern"
cd "$WORKTREE_PATH" && git add -p <file>
git -C "$WORKTREE_PATH" commit --no-verify -m "type: second concern"

複数のコミットにまたがる新規/未追跡ファイルの場合、git add -p だけではハンクの選択肢が表示されません。これは追跡対象のファイルでのみ機能します。まず、追加予定としてマークしてください:

git -C "$WORKTREE_PATH" add -N <new-file>   # tracks it for diff purposes, stays unstaged
cd "$WORKTREE_PATH" && git add -p <new-file>

git add -i や git rebase -i は絶対に使用しないでください。これらのコマンドは対話型 TTY 入力を必要とするため、このコンテキストでは停止したり失敗したりします。

ステップ 7:レビューゲート — 必須、妥協不可

ワークツリーですべてのコミットを作成した後、必ず次の内容をユーザーに提示してください:

Recomposition complete in isolated worktree.

To review:
  cd .git/recompose/<branch-name>
  git log --oneline

Original branch is UNCHANGED. Nothing will be applied until you approve.

Note: commits were created with --no-verify (hooks skipped) — run your
linter/formatter after applying. Authorship and commit dates on the new
commits reflect whoever ran this, not the original commits.

Does the new history look good?
  [yes]       → Apply to original branch
  [no/edit]   → Describe what to change, I'll update the worktree
  [cancel]    → Discard worktree, original branch stays as-is

ユーザーから明示的な返答があるまで待ってください。 返答なしにステップ 8 に進んではいけません。

ユーザーが以前に「レビューなしで、そのまま実行して」と言っていた場合: それでもこのレビューゲートを提示する必要があります。次のように説明してください:「レビュー手順は、このスキルの安全性を保証するためのものです。変更は分離してあります。レビューには 30 秒かかりますが、元のブランチを安全な状態に保てます。新しい履歴は次のとおりです:」

ステップ 8:ユーザーの返答に対応する

[yes] — 反映する:

元のブランチに触れる前に、差分が同一であることを確認してください(下記の「検証」セクションを参照)。「差異が見つかりました」という結果が出た場合は先に進まないでください。

まずリモートの upstream を確認します:

git -C "$REPO_ROOT" remote -v
git -C "$REPO_ROOT" branch -vv

ブランチがリモートを追跡している場合:

⚠️  This branch has a remote upstream. Applying recomposed commits will
   require a force-push. Do you want to proceed?

確認を待ってから、次を実行します:

# Safety net: tag where the original branch was, in case anything needs to be undone
git -C "$REPO_ROOT" tag "backup/$BRANCH_NAME-$(date +%s)" "$BRANCH_NAME"

# Confirm we're actually resetting the branch we think we are
CURRENT=$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD)
if [ "$CURRENT" != "$BRANCH_NAME" ]; then
  echo "⛔ Expected to be on $BRANCH_NAME but found $CURRENT — aborting."
  exit 1
fi

# DO NOT run `git checkout $BRANCH_NAME` — you are already on it in the main worktree.
# git checkout will fail with "already used by worktree". Skip it entirely.
git -C "$REPO_ROOT" reset --hard "recompose/$BRANCH_NAME"

# Verify it worked
git -C "$REPO_ROOT" log --oneline -5

# Clean up — worktree FIRST, then branch
git -C "$REPO_ROOT" worktree remove "$WORKTREE_PATH"
git -C "$REPO_ROOT" branch -D "recompose/$BRANCH_NAME"

上記でリモートの upstream が確認できた場合は、プッシュします:

git -C "$REPO_ROOT" push --force-with-lease

--force よりも --force-with-lease を優先してください。ほかの人がその間にリモートブランチへプッシュしていた場合、作業を黙って上書きするのではなく、安全に失敗します。

バックアップタグ(backup/$BRANCH_NAME-<timestamp>)をユーザーに伝えてください。必要に応じて再構成前の状態に戻す方法がわかるようにし、結果に満足したら削除して問題ないことも伝えてください。

[no/edit] — 修正する:

rebase -i を使わずに、ワークツリーですでに作成したコミットを調整するには、そのコミットの直前まで折りたたみを戻し、そこからステップ 6 をやり直してください:

# Find the SHA of the commit right before the one you need to change
git -C "$WORKTREE_PATH" log --oneline

# Uncollapse everything from that point forward back to unstaged
git -C "$WORKTREE_PATH" reset --mixed <sha-before-the-commit-to-fix>

その後、ステップ 6 の手順に従って再ステージし、再コミットしてから、レビューゲート(ステップ 7)を再提示してください。

[cancel] — 破棄する:

git worktree remove "$WORKTREE_PATH"
git branch -D "recompose/$BRANCH_NAME"

元のブランチには変更を加えていません。


重複するファイルへの対処方針

状況コマンド
ファイルの変更が異なるセクションに属するgit add -p <file> → ハンクごとに y/n
1 つのハンクに 2 つの関心事が混在しているs で分割してから y/n
関心事が行単位で実際に入り混じっているe でハンクの差分を手動編集する
ファイル全体が 1 つのコミットに属するgit add <file>(-p は不要)
新規/未追跡ファイルが複数のコミットにまたがるまず git add -N <file>、次に git add -p <file>

検証(完了を宣言する前に)

# 1. Confirm new clean history
git -C "$WORKTREE_PATH" log --oneline $BASE..HEAD

# 2. Full content diff — stats alone are NOT enough
# Stats can match while content differs. Always do a byte-for-byte comparison.
diff \
  <(git -C "$REPO_ROOT" diff $BASE HEAD) \
  <(git -C "$WORKTREE_PATH" diff $BASE HEAD) \
&& echo "IDENTICAL — diffs match exactly" || echo "DIFFERENCES FOUND — investigate before proceeding"

統計情報だけでは不十分です。 66 files changed, 8393 insertions が一致していても、内容が一致する証明にはなりません。完全な差分比較だけが証明になります。必ず diff <(...) <(...) 形式を使い、レビューゲートを提示する前に「IDENTICAL」を確認してください。

「DIFFERENCES FOUND」と表示された場合は停止してください。 レビューゲートを提示したり、変更を反映したりしてはいけません。ステップ 5/6 に戻り、見落としや割り当てミス(誤ったコミットにステージされたハンク、含め忘れたファイルなど)を確認して、「IDENTICAL」と表示されるまでこのチェックを再実行してください。


クイックリファレンス

ステップ操作
0ブランチを確認し、detached HEAD と作業ツリーの変更を確認 — いずれかに問題があれば拒否します
1git log --oneline → 開始 SHA を尋ねます
2SHA が祖先であることを検証し、ルートコミットの例外ケースに対処します
3git diff $BASE HEAD — 分析し、マージコミットがあれば拒否し、複数の作成者がいる場合はフラグを立てます
4古いワークツリー/ブランチをクリーンアップしてから、git worktree add を実行します
5git reset --mixed $BASE — まとめます
6論理グループごとにステージングしてコミットします(追跡されていないファイルに注意)
7レビューゲートを提示 — 必須。フックと作成者情報がリセットされることを記載します
8差分が完全に一致することを確認 → バックアップにタグを付ける → 元に反映 → 強制プッシュ(--force-with-lease) → クリーンアップ

よくある誤り/もっともらしい言い訳の一覧

言い訳実際のところ
「git rebase -i はこれを行う標準的なツールです」git rebase -i には対話型 TTY が必要なため、このコンテキストではハングします。git reset --mixed と選択的なステージングを使用してください。
「ユーザーがレビュー不要と言ったので、その意向を尊重します」レビューの関門は譲れません。所要時間は 30 秒で、元のブランチを保護できます。必ず提示してください。
「元のブランチを直接変更すれば、もっと速く済みます」譲れないルールです。すべてのコミットは作業ツリー内でのみ行います。承認されるまで元のブランチには手を加えません。
「クリーンアップ用のブランチを使えば、同じ結果が得られます」保護対象のブランチチェックに対する回避策は認められません。main/master/dev にいる場合は、拒否して停止してください。
「git add -i は git add -p と似ています」git add -i には対話型 TTY が必要です。git add -p のみを使用してください。
「作業ツリーのクリーンアップは、ユーザーのレビュー後でも構いません」ステップ 8 の一環としてクリーンアップしてください。作業ツリーを残すと、リポジトリが散らかります。
「相対パスの WORKTREE_PATH でも構いません。パスは明らかです」ブランチ名にスラッシュが含まれていると、相対パスは機能しません。必ず REPO_ROOT=$(git rev-parse --show-toplevel) を使い、絶対パスを指定してください。
「作業ツリーに cd して、そこで git コマンドを実行します」Bash ツールの呼び出し間でシェルの状態は保持されません。すべてのコマンドで git -C "$WORKTREE_PATH" を使用してください。
「リセット前に git checkout $BRANCH_NAME が必要です」そのチェックアウトは失敗します。ブランチはすでにメインの作業ツリーで使用中です。省略して、git -C "$REPO_ROOT" reset --hard を直接実行してください。
「差分の統計が一致しているので、内容も同じです」統計(66 ファイル、8393 件の挿入)が一致していても、内容が異なる場合があります。必ず diff <(git diff ...) <(git -C worktree diff ...) で確認してください。
「未コミットの変更がいくつかありますが、問題ありません」未コミットの変更は再構成から除外され、最後の reset --hard によって失われる危険があります。まず git status --porcelain を確認し、変更があれば停止してください。
「git add -p なら、新しいファイルも他のファイルと同じように扱えます」git add -p は、未追跡ファイルに対するハンク選択を提供しません。まず git add -N <file> を使用してください。
「作業ツリーにすべてあるので、バックアップタグは省略します」クリーンアップ時に作業ツリーは削除されます。後から元に戻す唯一の方法は、リセット前の元のブランチの SHA にバックアップタグを作成することです。必ず作成してください。
「強制プッシュは暗黙に含まれているので、実際に実行する必要はありません」ユーザーが確認し、ブランチにアップストリームがある場合は、プッシュ(--force-with-lease)を実行してください。説明するだけで停止してはいけません。
「範囲内にあるのはマージコミットが 1 つだけなので、回避できます」範囲内のマージコミットには対応していません。独自に対処するのではなく、拒否して別の開始地点を選ぶようユーザーに依頼してください。

危険信号 — STOP

以下のいずれかを考えたら、作業を止めてスキルに従ってください。

  • 「git rebase -i を使おう」
  • 「ユーザーがレビューを省略するように言ったので、省略しよう」
  • 「そのほうが速いので、元のブランチで直接作業しよう」
  • 「ワークツリーの代わりにバックアップブランチを作ろう」
  • 「ここでは保護ブランチのことは関係ない」
  • 「すっきりするので、重複するファイルに git add <file> を実行してもいい」
  • 「git -C より簡単なので、ワークツリーに cd しよう」
  • 「統計情報が一致しているので差分に問題はない。内容の確認は省略しよう」
  • 「ブランチをリセットする前に、そのブランチをチェックアウトする必要がある」
  • 「作業ツリーが少し汚れているが、おそらく続行しても問題ない」
  • 「範囲内にマージコミットがあるが、そのままリセットして進めよう」
  • 「バックアップタグは不要だ。何も問題は起きない」
  • 「強制プッシュが必要だと言及したので、実行したのと同じだ」

コメント