Git: How do I quickly remove local branches?

Published on May 27, 2026

·
TILgitbashgithubgitlab

Software engineers will strive for the most minimal clean desk 🧹 setup while counterbalancing that with the linearly increasing ancient history 📈 of merged remote branches still existing on their local, like a showcase of their contributions.

👉 If you want to be rescued and detangle that local git mess, this miniblog should serve the purpose (thanks to my digital OCD triggers).

Before starting, please run this command in your repository to find out how many local branches you are keeping (mine were 60 at the time of writing this article 🤫):

git branch | wc -l

Cleanup based on GitHub workflows

A simple Google search gives you a multitude of articles that promise to cure your maladies once and for all.

What is the catch you might ask?

All these solutions depend on the particular GitHub merge workflows used by you or your organization. We would be classifying our solution based on these workflows, and while different platforms (github, gitlab etc.) might expose them differently, they all work similarly under the hood.

It is Recommended to read up on upstream tracking required for linking remote branch to local.

NameMerge strategyUpstream trackingRemote deletionSolution
Forbidden fruitMerge commitThere are a number of blogs which help with this since it is easiest to deal with but also rarely practically deployed workflow. Rishabh’s blog that provides a concise solution
Layman’s solutionSquash merge/Rebase mergeJohnson’s blog is applicable in this case but only if both of the requirements are fulfilled.

Meeting halfway to a layman’s solution

Now, to reach a state in the codebase where the layman's solution is applicable, we want to perform some steps, and it depends on where you are in terms of your setup.

Git local stale workflow

Hacky script (above Figure step a) to sync remote and local branches and output the ones not existing on remote (merged, deleted, and branches not yet pushed to remote). Furthermore, it gives the provision to the user to delete all the branches after reviewing the output.

#!/usr/bin/env bash

set -uo pipefail

# Update remote references and remove deleted ones
git fetch --prune

branches=()
while IFS= read -r branch; do
  if ! git show-ref --quiet "refs/remotes/origin/$branch"; then
    branches+=("$branch")
  fi
done < <(git for-each-ref --format='%(refname:short)' refs/heads/)

if [[ ${#branches[@]} -eq 0 ]]; then
  echo "No local branches missing on origin."
  exit 0
fi

echo "Local branches missing on origin:"
echo "----------------------------------"
for branch in "${branches[@]}"; do
  echo "• $branch"
done
echo

read -r -p "Delete all ${#branches[@]} branch(es) listed above? [y/N] " confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
  echo "Aborted. No branches deleted."
  exit 0
fi

current_branch=$(git branch --show-current)
deleted=0
skipped=0

for branch in "${branches[@]}"; do
  if [[ "$branch" == "$current_branch" ]]; then
    echo "Skipping '$branch' (currently checked out)"
    skipped=$((skipped + 1))
    continue
  fi

  if git branch -D "$branch"; then
    deleted=$((deleted + 1))
  else
    echo "Failed to delete '$branch'"
    skipped=$((skipped + 1))
  fi
done

echo
echo "Done. Deleted: $deleted, skipped: $skipped"

Voila! You’ve streamlined a portion of your developer workflow, which can be a minor inconvenience or a huge pain in the ass.


Footnotes