Run git branch -d branch-name to remove a local branch. The copy on the remote needs its own command, git push origin --delete branch-name.
Neither one touches commits. Only the branch pointer goes away, and the commits sit in the repository until garbage collection prunes them.
The safe -d flag tests a branch against its upstream branch, or against HEAD when no upstream is set (git-scm.com manual, Git 2.56.0). Many guides describe it as checking the current branch alone. Either way, squash-merged work still needs -D, because its original commits are never reachable from the target branch.
How to Delete a Local Branch in Git

Switch to a different branch first, then run git branch -d branch-name. Git removes the pointer only when the commits are already merged.
- List your branches with
git branch. The current one carries an asterisk. - Switch to another branch, for example
git switch main. - Delete the target with
git branch -d feature-login. - Run
git branchagain and confirm the name is gone.
Skipping the switch in step 2 won’t work, because Git refuses to delete the branch that HEAD points at. If the switch is where you get stuck, this walkthrough on switching branches in Git covers both git switch and git checkout.
With -d, the branch has to be fully merged into its upstream branch, or into HEAD when no upstream is set (git-scm.com manual, Git 2.56.0). Fail that test and Git stops with an error instead of deleting.
You can pass several names at once, like git branch -d old-fix spike-auth. The branch’s own reflog is deleted along with it.
Local deletion never touches the remote. The copy on origin stays until you delete it there separately.
When Should You Use git branch -D Instead of -d?
Only when the branch’s unique commits are disposable. -D is shorthand for --delete --force and skips the merge check completely.
You’ll usually land here after a “not fully merged” error, where Git suggests rerunning the command with -D.
The catch is squash and rebase merges. Both write new commits to the base branch, so the original branch tip is never reachable from main.
A squash merge leaves the content on main under different commit hashes. Reachability is what -d tests, so Git reports the branch as unmerged even though the work landed.
Check what is actually unique before forcing anything. git log main..feature-login lists commits on the branch that main lacks.
After a squash merge that list still shows commits, so confirm the pull request state instead of trusting the log.
- If the pull request was squash merged or rebased,
-Dis safe, because the work sits on main under new hashes. - If the branch was never pushed and holds unique commits, stop and merge or push first.
-Dwould leave those commits reachable only through the reflog.
An abandoned experiment is exactly what the flag is for.
Force delete is not a recovery-proof delete. Orphaned commits survive only until garbage collection removes them.
How to Delete a Remote Branch in Git
Run git push origin --delete branch-name and the remote drops the branch. -d works as the short form of --delete.
Git treats deletion as a ref update, which is why it goes through git push instead of git branch. The manual for git push defines --delete as equivalent to prefixing each ref with a colon.
git push origin --delete feature-logingit push origin :feature-login
Both lines do the same thing. An empty source before the colon deletes the destination ref, and the manual’s own example is git push origin :dev.
Order doesn’t matter, since the local and remote branches are separate refs. Delete the local copy first, second, or never, and leave the remote alone if a teammate still needs it.
Sometimes the remote answers with a “remote rejected” error. The git push manual says that is usually caused by a server-side hook, or by the safety options receive.denyDeletes and receive.denyDeleteCurrent.
The web interface works too. As of October 2026, GitHub’s pull request page shows Delete branch only once the pull request is merged or closed and no other open pull request uses the branch (GitHub Docs), and this guide to deleting a branch in GitHub walks through the clicks.
In a standard clone, a successful push also removes the matching remote-tracking reference, origin/feature-login, from your clone. Branches deleted any other way leave that reference behind until you prune it.
How to Clean Up Stale and Merged Branches
Run git fetch --prune to clear stale remote-tracking branches, and git branch --merged main to list local branches that are safe to delete. Squash-merged branches fall outside both and need a manual -D.
Removing stale remote-tracking branches
A remote-tracking branch is a local reference such as origin/feature-login that records where that branch stood on the remote at your last fetch. Git keeps it after the remote branch is deleted, because a plain git fetch only adds and updates references.
git fetch --prune origin deletes the stale references before it downloads anything. git remote prune origin does the same without fetching at all.
To get rid of a single one by hand, use git branch -dr origin/feature-login. The next fetch recreates it if the branch still exists on the remote.
One git config command turns pruning on for every fetch: git config --global fetch.prune true. The per-remote version is remote.origin.prune.
Manual pruning is a habit people drop, so the setting beats the reminder.
One caveat about tags. Pruning follows the remote’s refspec, not branch names, so a refspec such as refs/tags/*:refs/tags/* makes --prune delete local tags that are missing on the remote, including ones you created yourself (git-scm.com manual).
Deleting all merged branches in one command
The git-branch manual says --merged finds branches that can be safely deleted, because their tips are fully contained by HEAD, or by the commit you name.
git branch --merged maingit branch --merged main | grep -v -E '^\*|^ *main$' | xargs git branch -d
The first line is the preview, so run it before anything else. The second deletes everything in that list except the checked-out branch and main. Run it with main checked out, because -d tests against HEAD when a branch has no upstream.
Keep -d in the pipe. Graphite’s local branch cleanup guide pipes every branch except main into -D, which wipes unmerged work without a single warning.
The Git 2.56.0 manual (September 28, 2026) also documents git branch --delete-merged 'origin/*', which deletes local branches whose tip is already reachable from the upstream they track. Add --dry-run to preview the result.
It skips a branch whose upstream ref no longer exists, which is exactly the state a pruned branch is in. For those, fall back to --merged main, and check git branch -vv, which marks such branches as gone.
Squash-merged branches never show as merged. Confirm the pull request state, then use -D on the ones that remain.
Which Delete Command Removes What?
Only git push origin --delete touches the remote. Every other delete command edits refs inside your own clone.
| Command | Removes | Merge check | Reversible? |
|---|---|---|---|
git branch -d | Local branch | Yes | Yes, from a commit hash |
git branch -D | Local branch | No | Yes, until the reflog expires |
git push origin --delete | Branch on the remote | No | Only from a clone or a hosting restore |
git fetch --prune | Stale remote-tracking branches | Not applicable | Not needed, the remote branch is already gone |
git branch -dr | One remote-tracking branch | No | Yes, the next fetch recreates it |
That split is the whole model. Local branches and remote-tracking branches live in your clone, while the branch on origin, the default remote name, lives on the server.
Run git branch -a to see local and remote-tracking entries side by side (git-scm.com manual). A name like feature-login is a local branch, so -d or -D removes it. An entry like remotes/origin/feature-login is a remote-tracking reference, and that one goes with --prune or -dr.
The common mistake is running git branch -dr origin/feature-login and expecting the server copy to disappear. It edits your clone only, so the branch on the server stays.
Sources disagree on one point. Some guides say the reference lingers after a successful git push origin --delete until you prune. In a standard clone it does not. Git’s own push code deletes the matching local remote-tracking reference once the remote confirms the deletion, although the git push manual never says so.
Check with git branch -r right after the push. Deletions made in a web interface, by automatic branch deletion after a merge, or by a teammate leave stale references in your clone until you prune.
Why Git Refuses to Delete a Branch
A delete fails either because the ref is in use or missing in your clone, or because the hosting platform protects it. The error text tells you which.
Errors in the local repository
If Git says it cannot delete a checked-out branch, HEAD points at it, or a linked worktree has it checked out. Switch branches or remove the worktree, then retry.
A “branch not found” error is usually a typo, or a remote-tracking name passed without -r. List candidates with git branch --list 'feature-*', because a pattern needs --list (git-scm.com manual).
When the message says the remote ref does not exist, the branch is already gone from the server, or a stale origin/ entry points at nothing. Run git fetch --prune, then retry the push only if the branch still appears. That error never comes from git branch. Only git push prints it.
Rules set by the hosting platform
The same delete command gets different answers on GitHub and GitLab.
On GitHub, a branch protection rule blocks deletion by default, and that restriction skips admins unless the rule also blocks bypassing. Turning on Allow deletions lets anyone with write access delete the branch, but a locked branch stays undeletable (GitHub Docs, as of October 2026).
GitLab protects the default branch from the start. Maintainers and Owners can delete a protected branch only in the web interface or through the API, never with local Git commands or third-party clients (GitLab Docs, version 19.5).
So git push origin --delete release can succeed on GitHub once deletions are allowed, and it fails on a protected GitLab branch for everyone. The fix differs too.
- On GitHub, edit the branch protection rule, then push the delete again.
- On GitLab, go to Code, Branches, More actions, Delete protected branch, then type the branch name to confirm.
GitHub also wants a new default branch chosen before the old one can be deleted (GitHub Docs). Set the replacement in the repository settings first.
One more trap. Deleting a branch from GitHub’s branch list while a pull request is open closes that pull request, after a warning.
Can You Recover a Deleted Git Branch?
Yes, until garbage collection removes the commits. Deleting a branch removes only its pointer, so recovery means finding the last commit hash and pointing a new branch at it.
- Read the hash from the delete output if it is still on screen, since Git prints
Deleted branch feature-login (was 3f4a5b6). - Otherwise run
git reflogand find the linecheckout: moving from feature-login to main. The entry directly below it holds the branch’s last commit. - Recreate the branch with
git branch feature-login 3f4a5b6. - Check the history with
git log feature-loginbefore doing anything else.
That commit hash is all Git needs, because the commits stay in the repository after the pointer is gone. The HEAD reflog carries it because it records branch switching (git-scm.com manual).
Recreating the branch is ordinary creating a new branch in Git with an explicit start point.
How long recovery stays possible
The defaults in the git-gc manual decide the window. Reflog entries not reachable from the current tip expire after 30 days (gc.reflogExpireUnreachable), while entries that are still reachable last 90 days (gc.reflogExpire).
Separately, gc waits 2 weeks before it prunes loose unreachable objects (gc.pruneExpire). Because gc keeps every object a reflog still references, the 30-day figure sets the window for an unmerged branch, not the two-week one. Count from the day the entry was recorded.
When recovery fails
It fails if the branch was never checked out in this clone, since the HEAD reflog then has no entry. It also fails in a fresh clone, because reflogs stay local, and once the window has passed and the entries and objects are gone.
In the first case, look for origin/feature-login or another clone that has the commits. On GitHub, the head branch of a closed pull request comes back with the Restore branch button at the bottom of the pull request (GitHub Docs, as of October 2026).
Common Questions About Deleting a Branch in Git
Does deleting a branch delete its commits?
No. Git removes only the branch pointer, and the commits stay in the repository until garbage collection prunes them. Commits reachable from another branch are never at risk.
Is git branch -d the same as git branch –delete?
Yes. -d is the short form of --delete, and -D is shorthand for --delete --force (git-scm.com manual). Only the force variant skips the merge check.
How do I list branches before deleting one?
Run git branch for local branches, git branch -r for remote-tracking ones, and git branch -a for both.
git branch --show-current prints the checked-out branch, and prints nothing in a detached HEAD state.
Can I delete a branch in the GitHub or GitLab interface?
Yes. On GitHub, open the branch dropdown, choose View all branches, and delete from the list.
GitLab offers the same under Code, then Branches. A protected branch needs the Delete protected branch action instead.
Automating Branch Cleanup After Every Merge
Deleting a branch in Git stops being a manual chore once the hosting platform removes head branches on merge. On GitHub, an admin enables Automatically delete head branches in the repository’s pull request settings (GitHub Docs, as of October 2026).
That setting removes only the remote branch, so every clone keeps a local branch and a stale origin/ reference. Set fetch.prune to true on each machine to close that gap, then clear any leftovers with --merged and -d.
The setting covers merged pull requests only, and branch protection or repository rules can block the deletion (GitHub Docs). Branches from closed, unmerged pull requests stay until someone deletes them.
The trade-off is speed. The remote branch disappears at merge time, and restoring it means using the Restore branch button on the closed pull request. Verified October 2026 against the Git 2.56.0 manual.
- Notepad++ Android Options: Apps That Come Close - October 21, 2026
- Data Management Services: A Practical Guide for Growing Companies - October 21, 2026
- MongoDB Cheat Sheet - October 20, 2026




