94

Ever since GitHub introduced Squash and Merge, all the cool kids at my workplace are using it when merging pull requests. Is there a way to cleanup "Squash and Merge" branches?

The following command from How can I delete all git branches which have been merged? does not work for "Squash and Merge":

git branch --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d

6 Answers 6

146

Here's a script that will delete all local branches that have been squash merged into master:

git checkout -q master && git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do mergeBase=$(git merge-base master $branch) && [[ $(git cherry master $(git commit-tree $(git rev-parse "$branch^{tree}") -p $mergeBase -m _)) == "-"* ]] && git branch -D $branch; done

If you want to run a dry run, you can instead run this:

git checkout -q master && git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do mergeBase=$(git merge-base master $branch) && [[ $(git cherry master $(git commit-tree $(git rev-parse "$branch^{tree}") -p $mergeBase -m _)) == "-"* ]] && echo "$branch is merged into master and can be deleted"; done

You can then setup an alias like this:

alias gprunesquashmerged='git checkout -q master && git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do mergeBase=$(git merge-base master $branch) && [[ $(git cherry master $(git commit-tree $(git rev-parse "$branch^{tree}") -p $mergeBase -m _)) == "-"* ]] && git branch -D $branch; done'

Source:

https://github.com/not-an-aardvark/git-delete-squashed

Sign up to request clarification or add additional context in comments.

5 Comments

This worked incredibly well for me. It removed a couple hundred branches and left the ones that I either never finished or were experimental. Excellent answer.
This is awesome. Do we need to worry about cleaning up all the commits with the "_" message that this creates?
Also, has anyone figured out how to make this a git alias instead of a bash alias? I can't get the quotes right.
@joshkarges What I ended up doing is making a bash script (.sh) and then calling that from the git alias. See github.com/dougthor42/dotfiles/commit/…
I preferred instead to just install the package from this answer's source, so I have an actual "source" of such a complex script I won't be able to maintain if something else is needed down the road. BTW, this answer looks almost like a copy-paste of the script in the package's readme, but anyway...
9

Update (2023-11-30) git-town prune-branches was sunset as of v10.0.0, and the CHANGELOG now suggests running git-town sync instead.

Update The tool git-delete-merged-branches did not work great for me. I recommend git-town prune-branches now.

The tool git-town offers prune-branches:

git-town prune branches

It then asks for the main development branch. You need to select your main or master branch:

Git Town needs to be configured

? Please specify the main development branch: main

Afterwards, it asks for "perennial" branches, which are branches besides the main branch, should be kept. Examples are branches for next versions etc.

? Please specify perennial branches:

Then, it goes forward with deletion:

[main] git fetch --prune --tags
From github.com:JabRef/jabref

[main] git branch -d remove-sav-file
Deleted branch remove-sav-file (was 33c1a869e1).

[main] git branch -d remove-bibtexml
Deleted branch remove-bibtexml (was d21c11337a).

Alternatives

git-delete-merged-branches

The tool git-delete-merged-branches allows for a convenient deletion of branches. I especially like the interactive mode.

Installation (requires python3):

pip install git-delete-merged-branches

Then execute

git-delete-merged-branches --effort=3 --branch main
  • --effort=3 is important to enable deletion of squashed branches.
  • --branch main is required (as otherwise master is used)

Other alternatives

  • @teppeis/git-delete-squashed: With node.js installed execute npx @teppeis/git-delete-squashed. Supports main branch.
  • git-delete-squashed: Not maintained: Misses functionality for main branch.. @teppeis/git-delete-squashed is based on this.

2 Comments

github.com/lzap/git-xcleaner is shell-only interactive one
FYI: git-town prune-branches was sunset as of v10.0.0, and the CHANGELOG now suggests running git-town sync instead: github.com/git-town/git-town/blob/main/…
2

There is no easy way to automate this, at least not completely. (Some special cases could be handled.) Instead, the best thing to do is to delegate this branch-deleting to the person whose pull request has been squash-merged. There are several good reasons for that:

  1. They are the only ones who can be sure the merge was done correctly.

    Suppose, for instance, that in order to squash-merge a series of six commits, the person who did the squash-merge had to, or chose to, change a few characters somewhere in a line or two, for some reason good or bad. That line or two means that the overall change in the final commit is different from the sum of the six changes in the six commits.

    But is the overall result correct? If you did not do any of the changes yourself, how will you know?

  2. They are the only ones who know if they intend to keep developing on that branch.

    Just because the six commits on feature/tall were squashed into one commit added to devel does not mean that feature/tall is all done. They may have several more commits to add; they may want to rebase feature/tall onto devel again, dropping the six squashed commits in favor of the one six-commit-squash, but keeping another three commits they are about to add.

There are probably some more cases. These may all be rare; they may never occur in your project; but the point here is that branch feature/tall is their branch, not your branch, so they—whoever they are—should be the ones deleting it when it's done.

Note that when you pick up feature/tall you have your own Git rename it to origin/feature/tall (assuming your remote is named origin). If you are experimenting with it, and git checkout feature/tall, your Git makes a copy for you. Once they delete feature/tall and you run git fetch origin --prune, your Git deletes your origin/feature/tall. So now the problem is simpler and can be automated: find branches whose "upstream" is gone, and delete those. (The one line script in this answer has some minor flaws; see the comments; a fancier one would use git for-each-ref and look up each branch's upstream setting with git rev-parse, but that's probably overkill.)

Comments

2

If you prefer an interactive tool with TUI (text-based user interface), I wrote a tool called git xcleaner. It can find merged branches, rebased branches (commits with the same commit message), pruned branches or manually selected branches.

https://github.com/lzap/git-xcleaner

Comments

1

Here is my favorite git alias which deletes squash merged branches if your repo automatically deletes merged branches:

[alias]
    delete-merged = !git branch --format '%(if:equals=gone)%(upstream:track,nobracket)%(then)%(refname:short)%(end)' --omit-empty | xargs -r git branch -D

It is very simple and readable, don't really on awkward parsing of git branch -vv output

1 Comment

This is much better then ` git branch -vv | grep -i ": gone] " | awk '{print $1}' | xargs git branch -D`. (Just repeating it here for those googling for that command)
0

The answer in this post is also useful https://medium.com/opendoor-labs/cleaning-up-branches-with-githubs-squash-merge-43138cc7585e

Adapted slightly to allow any origin name and prevent deletion of popular branches I use this:

git fetch --all
REMOTE=$(git remote)
comm -12 <(git branch | sed 's/ *//g') \
  <(git remote prune $REMOTE | sed 's/^.*$REMOTE//g') \
  | grep -v -e main -e master -e develop \
  | xargs -L1 -J % git branch -D %

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.