In our team's repository, we work by submitting PRs and we only merge them into master using github's squash & merge functionality. The problem for me is that I can then no longer see branches merged into master with git branch --merged
, as this will not show branches that have been squashed & merged. My local branches are piling up and it's a pain to review them one by one to see which I can remove. Is there another way to list them?

- 604
- 7
- 16
-
if my or any other answer solved your problem please mark it as accepted so that dev can figure that out and don't waste their time on solved problems. – Vijay Rajpurohit Sep 03 '19 at 11:06
2 Answers
tl;dr; Unfortunately, no. You'll have to check the status of your PR on GitHub and, once it has been merged, forcibly delete your local branches with git branch -D
.
"Squash and merge" Illustrated
The GitHub Squash and merge operation doesn't actually merge your topic branch ― instead, it squashes all the commits from that branch into a single one, and then rebases that commit on top of the target branch.
From the documentation:
When you select the Squash and merge option on a pull request on GitHub, the pull request's commits are squashed into a single commit.
And the most important part:
Pull requests with squashed commits are merged using the fast-forward option.
A fast-forward merge doesn't create a merge commit ― it simply moves the target branch reference forward so that it points to the same commit as the source branch. This can only be done if the source branch points to a commit that is a descendant of the target branch.
So, if your topic branch looks like this in your local repository:
master
v
o--o--o--o
\
A--B--C <- This is the branch you want to merge with a PR
^
topic
When you use Squash and merge, GitHub will rewrite the history of your topic
branch so that it looks like this:
master
v
o--o--o--o
\
S <- This is A, B and C squashed together
^
topic
Then, GitHub will rebase topic
on top of master
. Notice that this will result in a commit that's different from the original squashed commit S
because of the different parent; to distinguish it, we call the rebased commit S'
(S prime):
master
v
o--o--o--o
\
S'
^
topic
Finally, the topic
branch is incorporated into master
with a fast-forward merge:
master
v
o--o--o--o--S'
^
topic
This all happens inside the repository that's hosted on GitHub's servers. Meanwhile, on your machine the repository still looks like this:
master
v
o--o--o--o
\
A--B--C
^
topic
After you've done git pull
on master
, you'll receive the squashed and rebased commit S'
:
master
v
o--o--o--o--S'
\
A--B--C
^
topic
Git has no way of knowing that commit S'
contains the combined changes from A
, B
and C
. That's why it will still report topic
as unmerged.
A Practical Consideration
Linus Torvalds once wrote:
People can (and probably should) rebase their private trees (their own work). That's a cleanup. But never other peoples code. That's a "destroy history".
He then continues:
You must never EVER destroy other peoples history. You must not rebase commits other people did. Basically, if it doesn't have your sign-off on it, it's off limits: you can't rebase it, because it's not yours.
It should be obvious that GitHub's Squash and merge feature destroys other people's history by completely rewriting their PR branches.
I won't sit here and tell you that Squash and merge is intrinsically evil ― I'm sure it has its uses.
However, if your team often finds itself having to deal with stale local branches (like you described), you might want to consider switching to a merge workflow that incorporates the history of your PRs as-is instead of destroying it.

- 56,676
- 12
- 126
- 154
-
1Technically the squash-and-merge button uses the same algorithm as Git's `git merge --squash`: it doesn't build `S` atop the original branch, but rather directly atop the target, so no final rebase is required. The effect is the same, though. – torek Aug 28 '19 at 18:23
I came accross the same problem using PRs and squash merges and made a small command to cleanup the undesired branches, based on the ones that had an upstream branch.
It also avoids main branches deletion like master
, develop
, and release
:
git branch -d $(git branch --format "%(refname:short) %(upstream)" | awk '{if ($2) print $1;}' | grep -vE "release|develop|master") -f
I made it using the answer to this question : List all local branches without a remote

- 3,272
- 11
- 37
- 44

- 6,818
- 2
- 28
- 36