git branch -dを叩いた瞬間に赤いエラー。「not fully merged」「checked out at」「Couldn’t find remote ref」「protected branch」——ブランチ削除で遭遇するエラーは実は12種類以上あり、表面的な対処では再発することが多い領域です。
単に-D(強制削除)で突破すると、未マージコミットが本当に必要だったケースで致命傷になります。逆に原因を知っていれば、ほとんどのエラーは3ステップ以内で安全に解決できます。
この記事では、ブランチ削除時に発生する全エラーパターンの原因診断と対処を、エラーメッセージ別の早見表と診断フローで整理。worktree・protected branch・HEAD参照・upstream・submodule・タグ衝突・権限エラーなど深部の原因まで掘り下げ、さらに誤削除からの復旧手順とチーム運用で削除トラブルを未然に防ぐルールまでまとめた完全版です。
ブランチの一般的な削除手順そのものは【Git】ブランチを削除する方法にまとめています。本記事は「削除しようとしたら止められた」シナリオ特化のトラブルシューティング集として使ってください。
この記事で学べること
- ブランチ削除エラーの12パターン完全網羅と診断フロー
- エラーメッセージ別早見表(30秒で原因特定)
- worktree・protected branch・HEAD参照など深部の原因への対処
- GitHub/GitLab のブランチ保護エラーの突破手順
- リモート削除が反映されないときの対処(ローカルのstale参照掃除)
- タグと同名ブランチ・submoduleブランチの削除方法
- 誤削除した場合のreflog/fsck復旧手順
- 削除トラブルを未然に防ぐチーム運用ルール
- エラーメッセージ別 30秒早見表
- 診断フロー:3秒で削除できる状態か判定
- エラー①:Cannot delete branch checked out at …
- エラー②:The branch X is not fully merged
- エラー③:branch ‘X’ not found
- エラー④:Couldn’t find remote ref / remote ref does not exist
- エラー⑤:Protected branch update failed (GitHub / GitLab)
- エラー⑥:リモートのデフォルトブランチは削除できない
- エラー⑦:タグと同名ブランチでreferenceが曖昧
- エラー⑧:submodule由来のbranch削除不可
- エラー⑨:You are on a branch yet to be born
- エラー⑩:Permission denied (403) — 権限不足
- エラー⑪:リモート削除したのにブランチが見える
- エラー⑫:error: some refs could not be updated
- 削除後の掃除:stale参照・reflog・GC
- 誤削除したブランチを復旧する
- 削除トラブルを未然に防ぐチーム運用ルール
- よくある質問
- 関連記事
- まとめ
エラーメッセージ別 30秒早見表
削除時に出る代表的なエラーメッセージから、原因と対処を一発検索できるようにしました。詳細は後続セクションで深掘りします。
診断フロー:3秒で削除できる状態か判定
Q1: そのブランチは今いる場所(current branch)? YES → Q2 NO → Q3 Q2: まず別のブランチへ切り替える git switch main → Q3へ Q3: そのブランチは worktree で開かれている? YES → git worktree remove <path> で解除 NO → Q4へ Q4: ローカル削除 or リモート削除? ローカル → Q5へ リモート → Q7へ Q5: main/currentブランチにマージ済み? YES → git branch -d <name> NO → Q6へ Q6: 本当に不要?(コミット失う覚悟がある?) YES → git branch -D <name>(強制) NO → まずマージ or cherry-pick で救出 Q7: リモート側はBranch protectionで保護されている? YES → Settings→Rules→Allow deletions を許可 NO → git push origin --delete <name>
削除前の必須1コマンド:git log <削除対象> --not main --oneline。これで「mainに取り込まれていないコミット」が一覧で見えます。空なら安全に-d、出てくるなら本当に不要か再確認。
エラー①:Cannot delete branch checked out at …
$ git branch -d feature/login error: Cannot delete branch 'feature/login' checked out at '/home/user/repo'
原因
削除対象のブランチに自分が今いるか、別のworktreeで同じブランチがcheckoutされています。Gitは「使っている最中のブランチを消す」動作を禁止しています。
対処
# 自分が対象ブランチにいる場合 git switch main # 別ブランチへ移動 git branch -d feature/login
# worktree一覧を確認 git worktree list # /home/user/repo a1b2c3d [main] # /home/user/repo-feature d4e5f6c [feature/login] ← これが原因 # 該当worktreeを削除(作業ファイルも消えるので注意) git worktree remove /home/user/repo-feature # その上でブランチ削除 git branch -d feature/login
worktreeを消すとそのディレクトリ配下の未コミット変更も消えます。重要な作業が残っている可能性があるので、git worktree listで確認後、必要なら対象ディレクトリに移動してcommit/stashしてから削除してください。
エラー②:The branch X is not fully merged
$ git branch -d feature/experiment error: The branch 'feature/experiment' is not fully merged. If you are sure you want to delete it, run 'git branch -D feature/experiment'.
原因
対象ブランチに現在のHEADから辿れないコミットが残っています。Gitは「消すと失われるコミットがある」と警告しているわけです。マージしたつもりでもsquashマージやrebaseで別ハッシュになった場合、Gitはマージ済みと認識しません。
対処
# 失われるコミットを一覧(空なら実は安全) git log feature/experiment --not $(git for-each-ref --format="%(refname)" refs/heads/) --oneline # もっとシンプルに:mainに対して未マージ分を見る git log feature/experiment --not main --oneline # 結果が空 → 安全に削除可(squash/rebase経由で取り込まれている) git branch -D feature/experiment # 結果あり → 本当に失ってよいか判断 # 必要なら cherry-pick で救出 git checkout main git cherry-pick a1b2c3d git branch -D feature/experiment
Squash mergeとの関係
GitHubのSquash and mergeでPR統合した場合、元のコミットハッシュは残らないためGitは「未マージ」と判定します。これは正常な挙動で、実質マージ済みなら-Dで強制削除してOK。確認にはgit log --cherry-mark --oneline feature..mainが有効(squash元として既に統合されたコミットに=マークが付きます)。
エラー③:branch ‘X’ not found
$ git branch -d feature/Login error: branch 'feature/Login' not found.
原因と対処
ほぼ100%名前のtypoか大小文字ミス。WindowsとmacOSはデフォルトで大小文字を区別しないファイルシステムですが、Gitの内部refは大小文字を区別します。
# ローカルブランチ一覧(大小文字もそのまま) git branch # リモート含む全ブランチ git branch -a # パターン検索 git branch --list "feature/*" # タブ補完で確実に git branch -d feature/<Tab>
エラー④:Couldn’t find remote ref / remote ref does not exist
$ git push origin --delete feature/old error: unable to delete 'feature/old': remote ref does not exist
原因
リモート側では既に削除済みなのに、ローカルにorigin/feature/oldの古い追跡参照(stale ref)が残っているパターン。多くは他の誰かがWeb UIや別PCから削除した場合に発生します。
対処
# リモートに存在しないローカル参照を一括削除 git fetch --prune # 以降、自動でpruneしたい場合 git config --global fetch.prune true # タグもpruneしたい場合 git fetch --prune --prune-tags
fetch.prune=trueを設定するとgit fetchのたびに自動的にstale参照が掃除されます。複数人のチーム開発ではほぼ必須設定。
エラー⑤:Protected branch update failed (GitHub / GitLab)
$ git push origin --delete release/v1 remote: error: GH006: Protected branch update failed for refs/heads/release/v1. remote: error: Cannot delete a protected branch To github.com:org/repo.git ! [remote rejected] release/v1 (protected branch hook declined)
原因
GitHub/GitLabでBranch protection ruleが設定されており、保護ブランチの削除が禁止されています。設計としては正しい動作で、削除を許可する設定変更が必要です。
対処(GitHub)
# Webでの設定変更 1. Repository Settings を開く 2. Branches or Rules を選択 3. 該当ルールの "Edit" 4. "Allow deletions" にチェック or ルールから該当パターンを外す 5. Save changes # もしくはgh CLIで(Admin権限必須) gh api -X DELETE repos/:owner/:repo/branches/release%2Fv1/protection # 削除後にルールを戻すのを忘れずに(パターン/masterや/main/を再保護)
保護ブランチを一時的に外すのは事故の温床。release系ブランチやmain/masterの削除権限を持つのはレポジトリAdminのみ、作業中はAuditログで誰が何を操作したか記録される運用が理想。可能ならブランチを削除せずアーカイブタグを切るのがベターです。
GitLabでの手順
# GitLab Protected Branches 1. Settings → Repository → Protected branches 2. 該当ブランチで "Unprotect" 3. 削除 → 再設定
エラー⑥:リモートのデフォルトブランチは削除できない
$ git push origin --delete main remote: error: refusing to delete the current branch: refs/heads/main To github.com:org/repo.git ! [remote rejected] main (deletion of the current branch prohibited)
原因
リモートリポジトリのHEADが指しているブランチ(default branch)は削除不可。「現在のブランチ」という概念がリモートにもあり、それを削除すると空のリポジトリになるため拒否されます。master→main移行などで旧default branchを削除したいときによく遭遇します。
対処:default branchを先に変更
# GitHub Web UI で変更 1. Settings → Branches 2. Default branch を main など別のブランチに変更 3. 警告を読んで確認 4. その後 git push origin --delete master
# 現在のdefault branch確認 gh repo view --json defaultBranchRef -q .defaultBranchRef.name # 変更 gh repo edit --default-branch main # 旧default branchを削除 git push origin --delete master
master→main移行時の完全フロー
①mainブランチ作成(masterから)→②リモートにpush→③GitHub/GitLabでdefault branchをmainに変更→④masterをリモート削除→⑤各メンバーがgit fetch --prune+git branch -m master mainでローカル同期。詳細手順は【Git】ブランチ名を変更する方法のmaster→main移行セクションで解説しています。
エラー⑦:タグと同名ブランチでreferenceが曖昧
$ git branch -d v1.0 warning: refname 'v1.0' is ambiguous. error: branch 'v1.0' not found.
原因
同じ名前のタグとブランチが両方存在。Gitが「どちらを消すべきか」判断できず曖昧(ambiguous)として処理。
対処:フルパス指定
# どちらが存在するか確認 git show-ref v1.0 # refs/heads/v1.0 ← ブランチ # refs/tags/v1.0 ← タグ # ブランチだけ削除するには refs/heads/ を使う git update-ref -d refs/heads/v1.0 # タグだけ削除するなら git tag -d v1.0
タグとブランチの同名衝突は運用ルールで未然に防ぐのが本筋。Gitタグはv1.0.0形式(SemVer)、ブランチはrelease/v1.0形式、のように命名体系を分けると混乱しません。詳細は【Git】タグ完全ガイドを参照。
エラー⑧:submodule由来のbranch削除不可
親リポジトリの.gitmodulesで参照中のブランチを削除するとsubmoduleが壊れるため、事前に参照を切り替える必要があります。
# 現在のsubmodule参照を確認 git config -f .gitmodules --get submodule.libs/utils.branch # feature/old # 参照ブランチを変更(例:main) git config -f .gitmodules submodule.libs/utils.branch main git submodule sync git add .gitmodules git commit -m "chore: bump submodule to main" # その上でsubmodule側のブランチを削除 cd libs/utils git push origin --delete feature/old git branch -d feature/old
submoduleのブランチを消すと、そのsubmoduleを参照する他のリポジトリ/ブランチのビルドが壊れる可能性があります。削除前にgit grep "feature/old"でモノレポ内の参照を全件確認を推奨。
エラー⑨:You are on a branch yet to be born
$ git init $ git branch -d master fatal: You are on a branch yet to be born
原因と対処
コミットが1つも無い初期状態では、ブランチは「まだ生まれていない」扱い。削除したい場合は.git/refs/heads/<name>を直接消すか、先に1コミット作って正規ルートで削除します。
# 方法A:初回コミットを作って正規削除 git commit --allow-empty -m "Initial commit" git switch -c main git branch -D master # 方法B:参照ファイルを直接削除(低レベル) rm .git/refs/heads/master # HEADファイルも確認 cat .git/HEAD # ref: refs/heads/master ← masterを指している場合 echo "ref: refs/heads/main" > .git/HEAD
エラー⑩:Permission denied (403) — 権限不足
$ git push origin --delete release/v1 remote: Permission to org/repo.git denied to username. fatal: unable to access ...: The requested URL returned error: 403
原因と対処
GitHub/GitLab側で該当ブランチの削除権限が無い。PAT(Personal Access Token)のスコープ不足、もしくはリポジトリのAdmin権限が無いケース。
# 認証情報の確認 gh auth status # 現在どのユーザー・スコープでログインしているか確認 # PATのscope: repo, delete_repo 等を見直す # → 新しいPAT発行: https://github.com/settings/tokens # SSHキーで接続している場合 ssh -T git@github.com # → 接続できるユーザーが正しいか確認
ロール別の削除権限(GitHub組織リポジトリ)
- Admin:任意のブランチ削除可(保護ブランチ含む、設定による)
- Maintain:一般ブランチは削除可、保護ブランチは不可
- Write:一般ブランチの削除可(推奨ロール)
- Triage以下:削除不可
エラー⑪:リモート削除したのにブランチが見える
# 他の開発者がリモートのbranch/Xを削除済み # しかし自分のローカルでは: $ git branch -r | grep X origin/feature/X ← まだ見える!
原因と対処
ローカルに残ったリモート追跡参照(remote-tracking branch)のキャッシュが古いまま。git fetch単体では更新されるがstale(古い)参照は自動削除されないのがデフォルト動作です。
# 一回だけprune git fetch --prune # 特定リモートだけ git fetch origin --prune # 以降自動化(全remote共通) git config --global fetch.prune true # 個別のstale参照を強制削除 git branch -dr origin/feature/X
関連する「リモートブランチ確認」の完全ガイドは【Git】リモートブランチを確認する方法を参照。branch -r/ls-remote/fetch --pruneの使い分けが詳しく解説されています。
エラー⑫:error: some refs could not be updated
$ git push origin --delete feature/old remote: error: refusing to delete ... To github.com:org/repo.git ! [remote rejected] feature/old (refusing to update checked out branch) error: failed to push some refs to 'github.com:org/repo.git'
原因
複数要因の可能性(保護ブランチ・権限・参照競合・pre-receive hook)。上位に詳細メッセージがあるのでremote:行を全て読むのが原因特定の第一歩。
# 詳細出力(GIT_TRACE) GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin --delete feature/old 2>&1 | less # pre-receive hookに阻まれている場合 # → サーバー管理者(GitHubの場合はorg admin)に確認 # 保護ブランチ以外の原因候補: # - CODEOWNERSで所有者レビュー必須 # - Requires signed commits が有効 # - Rulesetで削除禁止
削除後の掃除:stale参照・reflog・GC
削除したブランチのゴミ参照と孤児コミットを放置すると、リポジトリサイズが膨らみ、clone時間が伸びます。定期メンテナンスで健全に保ちましょう。
# リモート追跡参照のstale掃除 git fetch --prune --prune-tags # 古いreflogエントリを強制期限切れ(90日→30日など) git reflog expire --expire=30.days --all git reflog expire --expire-unreachable=7.days --all # 孤児オブジェクトをGC(通常は自動実行されるが手動も可) git gc --prune=now # マージ済みローカルブランチを一括削除 git branch --merged main | grep -vE "^\*|main$|develop$" | xargs -n 1 git branch -d
git gc --prune=nowとreflog expireを組み合わせると、誤削除ブランチのコミットも完全に消えて復旧不可能になります。「消した後に気づく」ケースを考慮し、個人環境ではreflog保持期間はデフォルト(90日)のままがおすすめ。
誤削除したブランチを復旧する
① ローカルで-Dしてしまった直後
# 削除直前のHEADを探す
git reflog
# a1b2c3d HEAD@{0}: checkout: moving from feature/X to main
# d4e5f6c HEAD@{1}: commit: feat: 機能X実装 ← これが削除対象の最後のcommit
# 新しいブランチとして復旧
git branch feature/X d4e5f6c
# または直接checkout
git checkout -b feature/X d4e5f6c
② 時間が経ってreflogから消えた
# 到達不可能なオブジェクトを探す git fsck --lost-found # dangling commit a1b2c3d... # dangling commit d4e5f6c... # コミット内容を確認 git show d4e5f6c # 使えるものを復旧 git branch restored/feature-X d4e5f6c
③ リモート削除した直後(GitHub)
GitHubの削除ブランチ復旧
GitHubはWeb UIで削除したブランチを一定期間(目安:数分〜1時間)復元可能。Pull Requests → Closed → 該当PRを開く → “Restore branch” ボタン。PR経由で作ったブランチなら復旧しやすい。AdminのAudit logから復旧依頼できる場合もあり。
④ push前に-Dした場合
ローカルのreflogにしか記録が無く、他のPCにcloneしていなければ復旧はreflog+fsckが唯一。重要なブランチはpushを習慣化するのが最良の保険です。
復旧できないケースを減らす最強の運用:①個人ブランチも原則push(普段の作業用origin/以下に置く)、②重要な作業前にgit stashまたは一時commit、③gcの自動pruneを抑制(git config gc.pruneExpire=2.weeks)。
削除トラブルを未然に防ぐチーム運用ルール
エラー対処の知識よりも、そもそもトラブルが起きない運用を整えるのが効率的です。以下はCONTRIBUTING.md / 運用ガイドに組み込むべきルール集。
## ブランチ削除ルール ### 削除OKなブランチ - feature/* : PR marge後に自動削除(GitHub Auto-delete branch有効化) - fix/* : PR merge後に自動削除 - tmp/* : 個人の一時ブランチ、自由に削除可 ### 削除NGなブランチ - main / master : 保護ブランチ、削除不可 - release/* : Branch protectionでdelete禁止、代わりにタグでアーカイブ - hotfix/* : PR merge後に Admin判断で削除 ### 削除前チェックリスト 1. git log <branch> --not main --oneline で未マージコミット有無を確認 2. GitHub/GitLab の Open PR にまだ残っていないかチェック 3. CIビルドが完了してから削除(実行中のCIが失敗する可能性) 4. 他メンバーが参照していないか Slack等で確認 ### Branch protection 推奨設定(mainブランチ) - Restrict deletions: ON - Require pull request reviews: 1+ - Require status checks: CI green必須 - Include administrators: ON(Adminでも保護対象)
GitHubの自動削除設定
Settings → General → Pull Requests → Automatically delete head branchesを有効化すると、PRがmerge/closeされた瞬間にそのfeatureブランチが自動削除されます。ゴミブランチが溜まらず、削除漏れの事故も減るためほぼすべてのリポジトリで有効化推奨。マージ済みhead branchはPRから”Restore branch”ボタンで即復旧できるため安全。
よくある質問
-dはマージ済み確認付き削除、-Dはマージ状態を無視した強制削除。日常はまず-dで試し、エラーで止められたら本当に不要か確認してから-Dに切り替える、が正しいフロー。-Dを手癖にすると、squash mergeと誤解して必要なコミットを失います。git push origin :<name>は「空のrefをpush=削除」という古い書き方、git push origin --delete <name>(Git 1.7以降)は同じ動作の可読性の高い新書式。現在は--delete推奨。コロン記法はエイリアスやスクリプトでたまに見かけますがメンテナンス性のため新規コードでは使わない方が良いです。-dは現在のHEADから辿れるかで判定します。mainにマージしたあと自分がfeatureブランチに戻ってから-dすると、featureからはmainの最新が辿れないので「未マージ」と出ることがあります。git switch mainしてからgit branch -d featureが正解。git push origin --delete <name>がリモート側削除コマンド。git branch -d -r origin/<name>はローカルに残っているリモート追跡参照を消すだけで、リモートサーバー上のブランチは消えません。意図通り使い分けを。詳細は【Git】ブランチを削除する方法参照。git branch --merged main | grep -vE "^\*|main$|develop$" | xargs -n 1 git branch -d事前にgit branch --merged mainだけ実行して対象を確認するのが安全。PowerShellならgit branch --merged main | Where-Object {$_ -notmatch "main$|^\*"} | ForEach-Object { git branch -d $_.Trim() }。git reflogで削除直前のHEAD@{N}を探す。②git checkout -b 復旧名 HEAD@{N}でブランチ再生成。③もしreflogに無ければgit fsck --lost-foundで孤児コミットを探す。④リモートに残っていた場合はgit fetch origin 元の名前:復旧名で取得。詳しくは本記事「誤削除したブランチを復旧する」セクション。git log <branch> --not main --onelineでmainに取り込まれていないコミットが見えます。出力が空なら実質マージ済み(squash経由含む)。出力にコミットがある場合は、cherry-pickで必要分を救出してから-D。安全策としてgit branch --contains <branch>の先端shaでどのブランチにマージされているかも確認できます。関連記事
- 【Git】ブランチを削除する方法 — 削除手順のリファレンス(-d/-D/push –delete/prune)
- 【Git】リモートブランチを確認する方法 — branch -r/ls-remote/fetch –pruneの使い分け
- 【Git】ブランチ名を変更する方法 — master→main移行の完全フロー
- 【Git】ブランチ名変更後にpushできないときの6パターン診断 — 名前変更後の周辺エラー
- 【Git】タグ完全ガイド — タグとブランチの命名体系で衝突を防ぐ
- 【Git】revertとresetの違いと使い分け完全ガイド — 削除前の安全確認/reflog復旧
- 【Git】rebase中のエラー完全復旧ガイド — safety branchによる削除前の保険
- 【Git】よく使うgitコマンド決定版チートシート — 削除コマンドを含む早見表
- 【Git】巨大ファイルを誤ってコミットしたときの削除方法 — 削除と歴史書き換えの区別
まとめ
- 削除エラーは12パターン。エラーメッセージ早見表で30秒以内に原因特定できる
- 最頻出は「checked out at」「not fully merged」「protected branch」の3つ
- 未マージ警告はsquash merge由来のことが多く、
git log feature --not main --onelineで実質マージ済みかを確認 - worktree・submodule・default branch・タグ衝突など、表面的に見えない原因も意外と多い
- リモート削除が反映されない時は
fetch --prune、fetch.prune=trueで自動化 - 誤削除は
reflog→fsck --lost-found→GitHubの”Restore branch”の順で復旧試行 - GitHubのAuto-delete head branches設定で削除漏れを未然防止
- Branch protectionでmainなど重要ブランチの削除を物理禁止+CONTRIBUTING.mdでルール明文化
ブランチ削除エラーは表面的には複雑に見えますが、原因は有限(12パターン以内)。エラーメッセージ早見表で即座に原因特定し、-Dでの安易な強制削除を避ければ、未マージコミットを失う事故は未然に防げます。最強の運用は①Branch protection+②Auto-delete+③fetch.prune自動化+④reflog依存の最終保険。この4つが揃えば、削除トラブルはほぼゼロにできます。

