DEV Community

Akinkunmi
Akinkunmi

Posted on

GitDeclutter: know which local Git branches are safe to delete

A merged pull request does not mean the local branch on your laptop is empty.

I kept learning that the hard way. GitHub showed the PR as merged. The remote branch was already gone. git branch --merged looked clean enough. Then I found two commits that had never left my machine; written after I clicked merge, or left behind by a squash.

git branch -D feature/foo deletes that branch. That is all it does. It does not tell you whether the work is gone, and it does not find the other twelve leftovers sitting next to it. You can pass more names, but you still have to choose them.

So I built GitDeclutter: a small Go CLI that finds stale local branches, tells you which ones are safe, and can delete that SAFE set in one pass.

Know what's safe to delete before you delete it.

Official repo: github.com/kunmi02/git-declutter

This is not the older Python project of the same name. That one turns a folder of copied files into a Git repo. This one classifies stale local branches.

The failure mode git branch --merged misses

Squash-and-merge is the usual trap. GitHub records a new commit on main. Your local feature branch still has the original commits. Ancestry no longer matches, so a naive "is this merged?" check is either too aggressive or too timid.

The other trap is quieter:

PR merged at C

A──B──C

Local branch later becomes:

A──B──C──D──E
Enter fullscreen mode Exit fullscreen mode

The pull request is done. Commits D and E may exist only on your laptop. Deleting the branch throws them away.

GitDeclutter treats a merged PR as evidence, not as permission.

What it actually does

git declutter scan never deletes anything. It reads local Git history, remote-tracking state, and — when available — GitHub or GitLab pull request metadata. Then it puts every local branch in one of four buckets:

  • SAFE — strong evidence that deleting it will not lose unique work
  • REVIEW — it looks stale, but the evidence is incomplete
  • KEEP — unique or active work is still there
  • PROTECTED — current branch, default branch, a worktree, or a configured pattern

False negatives are acceptable. False positives are not. If it is unsure, it will not recommend automatic deletion.

A scan looks like this:

$ git declutter scan

GitDeclutter
Repository: payments-api

SAFE TO REMOVE                                    12

✓ feature/oauth
  PR #418 merged · remote deleted · no local changes

NEEDS REVIEW                                      3

⚠ experiment/cache
  Remote deleted · no merged PR found

KEEP                                              4

✕ feature/refunds
  3 commits exist only locally

PROTECTED                                         3

🔒 main
🔒 release/*
Enter fullscreen mode Exit fullscreen mode

If you disagree with a row, ask for the reasons:

git declutter why feature/refunds
Enter fullscreen mode Exit fullscreen mode

You get the signals, not a yes/no from a script you cannot audit.

Cleanup is optional, bulk, and recoverable

git branch -D is a delete command. GitDeclutter is a decide-then-delete command.

When you are ready, git declutter clean lists every SAFE branch and lets you remove them in one pass — interactively, or all at once with --safe-only --yes. That is the bulk path: you are not typing twelve branch names into git branch -D. REVIEW, KEEP, and PROTECTED never go on that path.

Before it deletes, it stores a recovery ref (refs/git-declutter/recovery/...) for 30 days by default. git declutter restore <branch> brings it back. Use --permanent only when you mean it.

It also re-checks the branch immediately before deletion. If the tip moved after the scan, that branch is skipped.

Install

You need Git. You do not need Go after the binary is on your PATH.

go install github.com/kunmi02/git-declutter@latest
git declutter scan
Enter fullscreen mode Exit fullscreen mode

GOBIN (or $(go env GOPATH)/bin) must be on your PATH. Prebuilt macOS, Linux, and Windows archives will live on GitHub Releases once version tags are published.

GitHub token via gh or GITHUB_TOKEN, GitLab via glab or GITLAB_TOKEN. Offline mode exists if you do not want provider calls:

git declutter scan --offline
Enter fullscreen mode Exit fullscreen mode

There is no GitDeclutter account and no telemetry. Provider APIs are used only for repo metadata (owner, branch names, SHAs, PR state) — not for source, diffs, or commit messages.

Why I did not just wrap git branch -D

git branch -D is the right tool when you already know the name and accept the loss. It will not scan the repo, classify leftovers, or give you a recoverable bulk cleanup of only the stale ones.

The gap is the decision, then the list. I wanted something that would rather leave a stale branch sitting there than pretend a squash merged, locally-extended branch was empty and, when the evidence is strong, let you clear the SAFE ones in one go.

If that is a problem you have, start with a scan:

git declutter scan
Enter fullscreen mode Exit fullscreen mode

Source, issues, and install: https://github.com/kunmi02/git-declutter

Top comments (0)