git branch --merged does not show all merged branches [SOLVED]

April 5th, 2023


Blog Cover

In git, you can display the list of all branches you have locally with the git branch command. This command also supports a list of options that determines how the result is displayed.

One of those options is --merged:

git branch --merged

By using this option with the branch command, you get a list of all branches that have been merged to HEAD, which is the active branch when the command is run. You can explicitly specify a target branch like this:

git branch --merged main

This will show a list of all branches that have been merged to main. The branches listed would also include main because the changes in main have been merged into main 😅

Instances where you may not get merged branches

TLDR: if the state of the remote branch is different from the local branch, and the remote branch is merged to a target branch, git would not be able to verify if the local branch has been merged to the target branch

Recently, I ran this merged command, but I didn't get the list of merged branches I expected.

My scenario was that I had some local branches which were synced with remote branches. Those remote branches were merged with main. So I pulled main:

git pull origin main

But when I ran git branch --merged, I didn't see the list of local branches which I expected. What I discovered (hours later) was that the state of the local branches were not in sync with the state of the remote branches when the remote branches were merged.

What makes the state of a local branch different from its remote counterpart?

Remote branch history conflicts with local branch history

Something might have happened to the branch on remote which didn't happen locally, and that would put the branches out of sync. If this happened before the remote branch was merged, git would be unable to tell if the branch has been merged locally.

I experienced this while using a "rebase and fast-forward" merge approach.

My setup is like this: for merge requests, I don't do the regular merging which adds a new commit when a branch is merged with another branch. Instead, I use the fast-forward merge which adds only the commits made in the branch I want to merge, without including a "merge commit".

But fast-forwards are only possible if the branch I want to merge (let's call it testing) contains all changes in the target branch (let's call it main). So if I have already made new changes on main since the time testing was created, a fast-forward merge for testing to main won't work.

For a fast-forward merge to work, I'd have to rebase testing with main. And when that works successfully, testing would contain all the changes in main (with the new changes added on top), and a fast-forward merge would work. I can easily perform a rebase on the GitLab UI (without having to do that locally) so that my merge can be successful.

So here is where the git history of the new branch on remote becomes out of sync with the local branch.

By rebasing on the GitLab UI (which I had to do as there were new changes on main) before merging, the state of the remote testing branch changes. The remote branch now contains the new changes made on main. But that's not the case with the local testing branch right?

Because I didn't rebase locally, the local branch only contains changes from main which existed before the branch was created. It doesn't contain new changes on main that are made afterwards.

What do you notice here? The remote testing branch and local testing branch are now in different states.

So, the remote branch is merged, and then you sync the remote main branch with the local main branch (by git pulling).

Now, even if the local main branch now contains the new changes you made in testing, running git branch --merged won't show you testing, because the state of the remote testing branch before it was merged was different from the local testing branch.

However, if you check "remote branches that are merged", you would see the remote testing branch:

git branch -r --merged

The r option shows only remote branches in the result.

So how do you fix this for the local branch?

Fix git branch --merged not showing all merged branches

To solve this, you have to ensure that the local testing branch is synced with the remote testing branch. By doing that, the local branch would have the same state as the remote branch, and then, git can verify that the branch has indeed been merged.

Here are two ways you can ensure the branches are synced.

git rebase locally

Remember that what changed the state of the remote branch was the fact that it was rebased with main. We can repeat that for the local branch. For example:

git checkout testing
git rebase main

This would put the local testing branch in the same state as the remote testing branch.

Now when you run git branch --merged, testing would be listed as a merged branch.

git pull from remote

Instead of rebasing, you can also sync the remote branch with the local branch, by pulling:

git checkout testing
git pull origin testing

But this would throw an error. The reason for this is that your branches are out of sync--they have diverged. You may see a similar error from git like this:

Git showing divergent branches error

The reason for this error, again, is that both branches are in different states. So you can't automatically do a push.

As you see in the hints, git recommends a rebase. So we can rebase the local branch with the remote by using this command:

git rebase origin/testing

The testing remote and local branches are now in the same state, so when you run git branch --merged, you'd see testing.

Squash and Merge approach also affects git branch --merged

This problem does not only occur in fast-forward merges. It can also occur if you're using the squash and merge approach. If all commits in a branch (say, testing) are squashed into one commit during merge, git would not be able to tell if testing was merged with the target branch by executing git branch --merged.

That's because, even though all the content changes in testing exist in the target branch, the histories are different. The target branch has only one commit with all the changes, while testing has multiple commits with the changes. Hence, git would not be able to tell.

As for this, I don't know the solution, but when I discover it, I'll share :)

If you enjoyed or learn anything from this piece, please share with others 😇

Connect with me ✨