何が起きたのか
作業完了後のクリーンアップとして git branch -d feature/my-feature を実行したところ、Gitが拒否しました:
error: The branch 'feature/my-feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/my-feature'.
feature/my-feature 上のコミットが現在のブランチ(通常は main や develop)から到達できない状態です。今削除してしまうと、Gitがいずれガベージコレクションで消去します。作業内容が失われます。
*「いずれ」*というのがポイントです。コミットは今この瞬間まだ存在しています。何か悪いことが起きる前に、調査する時間はあります。
なぜGitが削除をブロックするのか
git branch -d(小文字)は安全なバリアントです。ブランチ上のすべてのコミットが、上流ブランチまたは HEAD からすでに到達可能かどうかをチェックします。到達できないコミットが1つでもあれば、拒否します。
このエラーが発生する状況は4つあります:
- ブランチが スクワッシュマージ または リベース でマージされた — 元のコミットSHAは
main上に存在せず、新たにスクワッシュまたはリベースされたバージョンのみが存在する。 - ブランチが、現在チェックアウトしているブランチとは 別のブランチ にマージされた。
- プルリクエストが マージされずにクローズ され、実際の作業内容を失おうとしている。
- 誰かがリモートでマージしたが、まだフェッチしていない。
ステップ1 — 実際に何がマージされていないか確認する
-D を使う前に、30秒だけ確認してください:
# mainにはないfeatureブランチのコミット
git log main..feature/my-feature --oneline
何も出力されなければ、そのブランチは main に対して実質的に空です — 強制削除しても安全です。コミットが表示された場合は、メッセージを読んでみてください。別のハッシュで main にすでに取り込まれた作業のように見えますか?
# 参照用:mainに最近取り込まれたコミット
git log --oneline main | head -20
マージ済みPRの後にリモートがすでにクリーンアップしていないかも確認する価値があります:
git fetch --prune
git branch -r
シナリオA — スクワッシュまたはリベースでマージされた(最も一般的)
十中八九、これが原因です。GitHubのデフォルト「Squash and merge」ボタンとGitLabの「Squash commits」オプションはどちらもコミット履歴を書き換えます — PRは新しいSHAを持つ単一の新しいコミットとして main に取り込まれるため、Gitは元のブランチのコミットをマージ済みと認識しません。
簡単なサニティチェック:
# 作業がmainにあれば空になるはず
git diff main...feature/my-feature
差分が空?コードは安全に main にあります。ローカルブランチを強制削除してください:
git branch -D feature/my-feature
シナリオB — 別のベースブランチにマージされた
main にいる状態で、featureが develop に取り込まれていた可能性があります。正しいブランチと照合して確認してください:
git log develop..feature/my-feature --oneline
空でしたか?そのブランチに切り替えて安全な削除を使ってください:
git checkout develop
git branch -d feature/my-feature
作業がそこにあることを確認できたら、チェックアウトをスキップしても構いません:
git branch -D feature/my-feature
シナリオC — 作業が本当にマージされていない
git log main..feature/my-feature --oneline で main には存在しないコミットが表示された場合は、立ち止まってください。まだ削除しないでください。選択肢は3つあります:
オプション1 — 今すぐマージする:
git checkout main
git merge feature/my-feature
git branch -d feature/my-feature
オプション2 — 必要なコミットだけをチェリーピックする:
# git logの出力からハッシュを取得
git cherry-pick <commit-hash>
オプション3 — 削除前にタグを付ける(セーフティネット):
git tag backup/feature-my-feature feature/my-feature
git branch -D feature/my-feature
タグを付けることでコミットに無期限でアクセスできます。後で復元するには:git checkout -b feature/my-feature backup/feature-my-feature
最終手段 — そして実際に問題ない場合
git branch -D feature/my-feature
大文字の -D はマージチェックを完全にスキップします。使用するのは以下の場合です:
git diff main...feature/my-featureを実行して、固有の差分が何も返ってこなかった。- PRがスクワッシュマージまたはリベースマージされており、古いローカル参照を整理しているだけ。
- 意図的に破棄したい使い捨ての実験ブランチだった。
盲目的に使わないでください。このエラーはGitが「必要かもしれない作業を消す前に2秒だけ考えて」と求めているサインです。
リモートトラッキング参照もクリーンアップする
ローカルブランチを削除しても、リモートトラッキング参照は残ります。こちらもクリーンアップしてください:
# サーバーにリモートブランチがまだ存在する場合は削除
git push origin --delete feature/my-feature
# サーバーがすでに削除済みの場合は古いリモート参照を整理
git fetch --prune
修正を確認する
# ローカルにブランチが存在しないことを確認
git branch | grep feature/my-feature
# (出力なし = OK)
# コミットがmainにあることを確認
git log main --oneline | head -10
# 宙に浮いたリモート参照を確認
git remote prune origin --dry-run
クイックリファレンス
# 診断
git log main..feature/my-feature --oneline # mainにないものは?
git diff main...feature/my-feature # 内容の差分はあるか?
# 安全な削除(確認後)
git branch -D feature/my-feature
# マージ前の作業を削除する前のセーフティネット
git tag backup/feature-my-feature feature/my-feature
git branch -D feature/my-feature

