This warning appears when you try to delete a branch with `git branch -d` that contains commits not yet merged into your current branch or its upstream. Git is protecting you from accidentally losing work. You can either merge the branch first, verify the commits exist elsewhere, or force delete with `git branch -D`.
The "branch is not fully merged" warning is Git's safety mechanism to prevent accidental loss of commits. When you run `git branch -d branchname`, Git checks whether all commits on that branch are reachable from either: 1. **HEAD** (your currently checked-out branch) 2. **The upstream branch** (if one is configured) If commits on the branch would become unreachable after deletion, Git warns you and refuses to delete. This is because deleting the branch would make those commits "orphaned" - they'd still exist in the repository temporarily but would eventually be garbage collected and lost forever. The key insight is that Git doesn't check if the branch was merged anywhere - it specifically checks if it's merged into HEAD or the upstream. So you might see this warning even if the branch was properly merged into another branch, just not the one you're currently on. Common scenarios that trigger this warning: - **You're on the wrong branch**: The feature branch was merged into `main`, but you're currently on a different branch - **Squash or rebase merges**: The commits were integrated but with different SHA hashes, so Git doesn't recognize them as "the same" - **Unpushed/unmerged work**: The branch genuinely contains commits that haven't been integrated anywhere - **Stale local state**: Your local `main` hasn't been updated after a merge on the remote
First, understand what Git is comparing against. The -d flag only checks if the branch is merged into HEAD:
# See your current branch
git branch --show-current
# Or see all branches with current highlighted
git branch -vIf you're trying to delete a feature branch that was merged into main, make sure you're actually on main:
git checkout main
git branch -d feature-branchThis often resolves the warning immediately.
If the merge happened on the remote (via pull request), your local branch needs to be updated:
# Fetch all remote changes
git fetch origin
# Update your local main to match remote
git checkout main
git pull origin main
# Now try deleting again
git branch -d feature-branchThe warning appears because your local main doesn't have the merge commit yet. After pulling, Git can see that the feature branch's commits are now part of main.
Before force-deleting, confirm whether the commits are truly safe:
# See commits on the branch you want to delete
git log feature-branch --oneline
# Check if those commits exist in main
git log main --oneline | head -20
# Better: check if the branch is an ancestor of main
git merge-base --is-ancestor feature-branch main && echo "Safe to delete"
# Or see commits in feature-branch but NOT in main
git log main..feature-branch --onelineIf git log main..feature-branch shows no commits, the branch's work is already in main, and it's safe to delete. If it shows commits, those would be lost.
If you use "Squash and merge" on GitHub/GitLab, the original commits get combined into a new single commit with a different hash. Git can't tell they're "the same" work.
Option 1: Force delete after confirming the PR was merged
# If you've verified the PR is merged on GitHub/GitLab
git branch -D feature-branchOption 2: Use the merged branch as reference
# Delete relative to main, not HEAD
git checkout main
git pull origin main
git branch -d feature-branchOption 3: Check the PR status first
# With GitHub CLI
gh pr view feature-branch --json state,mergedAtFor teams that regularly use squash merges, force-deleting with -D after confirming the PR is merged is the standard workflow.
Similar to squash merges, "Rebase and merge" rewrites commits with new hashes. The content is identical, but Git sees different commit IDs.
# After a rebase-merge, you'll need to force delete
git branch -D feature-branch
# To verify the changes made it in, compare trees
git diff main feature-branch
# If the diff is empty or minimal, the changes are integratedThe key insight: after a rebase-merge, your local feature branch and the rebased version on main have the same changes but different history. Git's merge detection doesn't help here.
If you've verified the work is merged (or you intentionally want to discard it), use the uppercase -D flag:
# Force delete - bypasses the merge check
git branch -D feature-branchWhen it's safe to force delete:
- The PR/MR was merged (check on GitHub/GitLab)
- You ran git diff main feature-branch and it's empty
- You intentionally want to abandon this work
- The branch was merged but using squash/rebase strategies
When you should NOT force delete:
- You haven't checked if the work was merged
- The branch contains uncommitted experiments you might want
- Other team members might be depending on this branch
After pull requests are merged, you may have many local branches to clean up:
# First, update all remote tracking info
git fetch --all --prune
# See which local branches are tracking deleted remotes
git branch -vv | grep ': gone]'
# Delete all branches whose upstream is gone
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -DOr use Git's built-in cleanup for merged branches:
# Delete all local branches already merged into main
git checkout main
git branch --merged | grep -v "^*" | grep -v "main" | xargs -n 1 git branch -dNote: This uses -d (lowercase) so truly unmerged branches will be skipped with warnings.
Understanding Git's Merge Detection:
Git's -d safety check works by comparing commit hashes, not content. It asks: "Are all commits reachable from HEAD?" This has important implications:
1. Same changes, different commits = "not merged": Squash merges, cherry-picks, rebases, and manual re-implementations all create new commits. Even if the code changes are identical, Git sees different SHA-1 hashes.
2. Merge direction matters: If you merge main into feature, then delete feature while on main, Git may warn. The feature's commits exist, but the merge commit is on feature, not main.
Why Git Has Two Delete Flags:
git branch -d # --delete (safe)
git branch -D # --delete --force (unsafe)The lowercase -d is a safety net, not a rule. Git's warning is informational: "Hey, these commits might be lost." It's your job to verify whether that matters.
The `--force` Workflow:
Many teams adopt this workflow:
1. Merge PR on GitHub (squash/rebase/merge)
2. Locally: git fetch --prune
3. Locally: git branch -D feature-branch
This is safe because the remote is the source of truth. Once the PR is merged, the local branch is just a stale reference.
Recovering "Lost" Commits:
If you force-deleted a branch and need those commits back:
# Find orphaned commits (within ~30 days)
git reflog
# Look for the branch's last commit
git reflog | grep feature-branch
# Recreate the branch at that commit
git branch feature-branch <commit-hash>Git doesn't immediately delete commits. They remain in the object database until garbage collection runs (typically 30+ days for orphaned commits).
Checking Merge Status Programmatically:
# Is branch A fully merged into branch B?
git merge-base --is-ancestor A B
echo $? # 0 = yes (safe to delete), 1 = no
# Compare the branch to its upstream
git log @{upstream}..HEAD --oneline
# See all branches containing a specific commit
git branch --contains <commit-hash>Remote Branch Deletion:
The local warning doesn't apply to remote branches:
# Delete remote branch (no merge check)
git push origin --delete feature-branch
# Or the older syntax
git push origin :feature-branchRemote branch deletion is always "forced" from Git's perspective. The assumption is you're managing the remote deliberately.
Configuring Upstream Tracking:
The -d check also considers the upstream branch. If your local branch tracks a remote:
# Set upstream
git branch --set-upstream-to=origin/main feature-branch
# Now -d checks against origin/main, not just HEAD
git branch -d feature-branchThis can help in workflows where you want to delete feature branches that are merged into the remote main, regardless of your local HEAD.
warning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server