Programming

Git commits are duplicated in the same branch after doing a rebase

27 September 2026 · 16 min read

Git commits are duplicated in the same branch after doing a rebase

Encountering duplicate commits after a Git rebase can be a frustrating experience, especially when you’re trying to maintain a clean and linear project history. This issue often arises from misunderstandings about how Git rebase operates and how it manipulates commit histories. Developers frequently face this problem when collaborating on shared branches or when attempting to integrate changes from a feature branch back into the main branch. The core of the problem lies in Git’s approach to history rewriting, which, while powerful, can lead to unintended duplication if not handled correctly. Understanding the nuances of rebasing, particularly when dealing with shared repositories, is crucial for avoiding these duplicate Git commits. Let’s dive into why Git commits are duplicated in the same branch after doing a rebase and how to resolve this common issue.

Understanding Git Rebase and Commit Duplication

Git rebase is a powerful tool that allows you to rewrite the commit history of a branch. When you rebase a branch, you’re essentially moving the starting point of your branch to a different commit. This creates a cleaner, linear history by reapplying your commits on top of the target branch, such as main. However, this process can also lead to duplicate commits if not managed properly. The most common reason for duplicated commits is that the commits already exist on the target branch. When you rebase, Git attempts to replay the commits from your branch onto the target branch. If some of those commits are already present, Git will perceive them as new changes and create duplicates. This often happens when you’ve previously merged some of the feature branch’s changes into the target branch, or when another developer has already incorporated those changes.

For example, imagine you have a feature branch feature-A and the main branch. You start feature-A from main, make some commits, and then decide to rebase feature-A onto the latest main. If, in the meantime, some of your changes from feature-A have been cherry-picked or merged into main through another process, rebasing feature-A will result in duplicate commits. Git’s rebasing process doesn’t automatically detect and prevent these duplicates. Understanding this behavior is the first step to avoiding the problem.

A common misconception is that rebasing simply “moves” commits. In reality, it creates new commits with the same changes but a different commit hash. This is crucial because Git identifies commits by their unique hash, not by the changes they contain. Therefore, even if the changes are identical, Git treats them as distinct commits. According to the Git documentation [1], rebasing should be used with caution in shared branches, as it rewrites history and can cause confusion for collaborators.

Identifying Duplicate Commits After a Rebase

Detecting duplicate commits can be done through visual inspection of the commit history using tools like git log. Visually inspecting the output is helpful, but can be tedious. You might also notice that the same changes appear twice in the history, or that certain commit messages are repeated. A more programmatic approach involves using Git commands to compare the contents of potentially duplicated commits. You can use the git diff command to compare the changes introduced by two commits. If the diff is empty, it suggests that the commits are indeed duplicates.

Another helpful technique is to use git log –graph –oneline. This command provides a visual representation of the commit history, making it easier to spot duplicated branches or commits. Look for linear sequences of commits that appear to be identical. Furthermore, you can use the –author and –after flags with git log to filter the commit history to a specific author and time period, making it easier to isolate the relevant commits. For example, git log –author=“Your Name” –after=“2023-01-01” will show all commits made by “Your Name” after January 1, 2023. This is a helpful way to pinpoint the commits introduced during the rebase process.

Consider this scenario: After rebasing your feature branch, you notice two commits with identical commit messages, both adding the same feature functionality. Running git diff shows no differences. This confirms that these commits are duplicates. Being able to identify these duplicated commits is the first step in rectifying the issue and cleaning up your branch history. This careful inspection ensures the integrity of your project’s history. According to a Stack Overflow survey [2], managing Git history effectively is a common challenge for developers, highlighting the importance of understanding these diagnostic techniques.

Resolving Duplicate Commits

Once you’ve identified the duplicate commits, there are several approaches you can take to resolve the issue. The best approach depends on whether you’ve already pushed the rebased branch to a remote repository. If you haven’t pushed the changes yet, you can more easily amend the local history. If you’ve already pushed the branch, you’ll need to use more careful techniques to avoid disrupting other collaborators. One common approach is to use git reset to move the branch pointer back to a point before the duplicate commits were introduced. Then, you can selectively cherry-pick the commits you want to keep, ensuring you don’t include the duplicates.

Here’s how to resolve duplicate commits before pushing:

  1. Use git log to identify the commit hash before the duplicate commits.
  2. Run git reset –hard to move the branch pointer back to that commit. This will discard the duplicate commits.
  3. Carefully review the changes you want to keep and cherry-pick them using git cherry-pick .
  4. Verify that the history is clean and free of duplicates using git log –graph –oneline.

If you’ve already pushed the rebased branch, force-pushing is generally discouraged because it can cause problems for other collaborators who have based their work on the old history. In such cases, a safer approach is to create a new commit that reverts the changes introduced by the duplicate commits. This leaves the original commits in the history but effectively undoes their effects. Another method is to use git revert, which creates a new commit that undoes the changes introduced by a specific commit. This is a non-destructive approach that preserves the original commit history while effectively removing the duplicated changes. It’s crucial to communicate with your team before making any changes to a shared branch, especially when force-pushing is involved.

Here’s an example. Suppose you’ve pushed a branch named feature-B with duplicated commits. Instead of force pushing, you can use git revert for each duplicated commit, creating new commits that undo the duplicated changes. This ensures that other developers working on feature-B aren’t negatively affected by a rewritten history. This careful handling is essential for maintaining a smooth and collaborative workflow.

Preventing Duplicate Commits in the Future

Prevention is always better than cure. Several strategies can help you avoid duplicate commits when using Git rebase. Before rebasing, always ensure that your local branch is up-to-date with the target branch. This reduces the likelihood of including commits that have already been merged. Regularly fetching and merging or rebasing from the target branch into your feature branch can help keep your branch synchronized. Moreover, effective communication with your team is crucial. Before initiating a rebase, especially on shared branches, communicate your intentions with your team members. This ensures that everyone is aware of the potential history rewriting and can avoid basing their work on outdated commits.

Here are some key strategies for preventing duplicate commits:

  • Regularly update your feature branch with the target branch (e.g., main).
  • Communicate with your team before rebasing shared branches.
  • Use git fetch and git merge to integrate changes from the target branch instead of rebasing, if appropriate.

Consider adopting a branching strategy that minimizes the need for rebasing. For example, using short-lived feature branches and merging them frequently can reduce the risk of conflicts and duplicate commits. Another useful tip is to examine the commit history of both your branch and the target branch before rebasing. This helps you identify any commits that might already be present in the target branch. Tools like gitk or git log –graph can be helpful for visualizing the commit history. Also, consider using Git’s merge functionality instead of rebase in certain situations, especially when collaborating on shared branches.

Here are some additional best practices:

  • Use descriptive commit messages to easily identify changes.
  • Keep commits small and focused to minimize conflicts.
  • Regularly review your commit history to identify potential issues early.

By proactively managing your Git workflow and communicating effectively with your team, you can significantly reduce the risk of encountering duplicate commits and maintain a clean and organized project history. Remember, a well-maintained Git history is crucial for effective collaboration and long-term project success. According to Atlassian’s Git tutorial [3], a clean commit history simplifies debugging and makes it easier to understand the evolution of the codebase.

Infographic here
FAQ: Duplicate Commits After Rebase -----------------------------------
Why am I seeing duplicate commits after a rebase?
Duplicate commits often occur because the commits you're rebasing have already been merged or cherry-picked into the target branch. Git treats these as new changes and creates duplicates. Git doesn't automatically detect and prevent these duplicates.
Is it safe to force-push after removing duplicate commits?
Force-pushing should be done with caution, especially on shared branches. It can disrupt other collaborators who have based their work on the old history. Communicate with your team before force-pushing.
What's the difference between git rebase and git merge?
git rebase rewrites the commit history by moving the starting point of your branch. git merge creates a new merge commit that combines the changes from two branches without altering the existing history.
How can I visually inspect my commit history for duplicates?
Use commands like git log --graph --oneline to visualize the commit history and look for identical sequences of commits. This helps identify potential duplicates.
The featured snippet paragraph: To avoid duplicate commits, always update your local branch with the target branch before rebasing. This reduces the likelihood of including commits that have already been merged. Regularly fetching and merging or rebasing from the target branch into your feature branch can help keep your branch synchronized, preventing Git from creating duplicates. Remember to communicate with your team before rebasing, especially on shared branches.

Dealing with duplicate commits after a rebase is a common challenge, but with a solid understanding of Git’s rebasing process and the right tools, you can effectively identify and resolve these issues. Remember to prioritize clear communication with your team and to adopt preventative measures to minimize the risk of future occurrences. By carefully managing your Git workflow, you’ll ensure a cleaner, more organized project history, facilitating smoother collaboration and more efficient development. Now that you understand how to tackle this issue, take these strategies and apply them to your workflow, and you’ll be well on your way to mastering Git and maintaining a pristine repository. Consider exploring other advanced Git techniques like interactive rebasing for even finer control over your commit history, or delve into branching strategies like Gitflow for a structured approach to collaboration.

Question & Answer :
I understand the scenario presented in Pro Git about The Perils of Rebasing. The author basically tells you how to avoid duplicated commits:

Do not rebase commits that you have pushed to a public repository.

I am going to tell you my particular situation because I think it does not exactly fit the Pro Git scenario and I still end up with duplicated commits.

Let’s say I have two remote branches with their local counterparts:

origin/master origin/dev | | master dev 

All four branches contains the same commits and I am going to start development in dev:

origin/master : C1 C2 C3 C4 master : C1 C2 C3 C4 origin/dev : C1 C2 C3 C4 dev : C1 C2 C3 C4 

After a couple of commits I push the changes to origin/dev:

origin/master : C1 C2 C3 C4 master : C1 C2 C3 C4 origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit 

I have to go back to master to make a quick fix:

origin/master : C1 C2 C3 C4 C7 # (2) git push master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit origin/dev : C1 C2 C3 C4 C5 C6 dev : C1 C2 C3 C4 C5 C6 

And back to dev I rebase the changes to include the quick fix in my actual development:

origin/master : C1 C2 C3 C4 C7 master : C1 C2 C3 C4 C7 origin/dev : C1 C2 C3 C4 C5 C6 dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master 

If I display the history of commits with GitX/gitk I notice that origin/dev now contains two identical commits C5' and C6' which are different to Git. Now if I push the changes to origin/dev this is the result:

origin/master : C1 C2 C3 C4 C7 master : C1 C2 C3 C4 C7 origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push dev : C1 C2 C3 C4 C7 C5' C6' 

Maybe I don’t fully understand the explanation in Pro Git, so I would like to know two things:

  1. Why does Git duplicate these commits while rebasing? Is there a particular reason to do that instead of just applying C5 and C6 after C7?
  2. How can I avoid that? Would it be wise to do it?

Short answer

You omitted the fact that you ran git push, got the following error, and then proceeded to run git pull:

To <a class="__cf_email__" data-cfemail="17707e6357757e637562747c726339786570" href="/cdn-cgi/l/email-protection">[email protected]</a>:username/test1.git ! [rejected] dev -> dev (non-fast-forward) error: failed to push some refs to '<a class="__cf_email__" data-cfemail="64030d1024060d100611070f01104a0b1603" href="/cdn-cgi/l/email-protection">[email protected]</a>:username/test1.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 

Despite Git trying to be helpful, its ‘git pull’ advice is most likely not what you want to do.

If you are:

  • Working on a “feature branch” or “developer branch” alone, then you can run git push --force to update the remote with your post-rebase commits (as per user4405677’s answer).
  • Working on a branch with multiple developers at the same time, then you probably should not be using git rebase in the first place. To update dev with changes from master, you should, instead of running git rebase master dev, run git merge master whilst on dev (as per Justin’s answer).

A slightly longer explanation

Each commit hash in Git is based on a number of factors, one of which is the hash of the commit that comes before it.

If you reorder commits you will change commit hashes; rebasing (when it does something) will change commit hashes. With that, the result of running git rebase master dev, where dev is out of sync with master, will create new commits (and thus hashes) with the same content as those on dev but with the commits on master inserted before them.

You can end up in a situation like this in multiple ways. Two ways I can think of:

  • You could have commits on master that you want to base your dev work on
  • You could have commits on dev that have already been pushed to a remote, which you then proceed to change (reword commit messages, reorder commits, squash commits, etc.)

Let’s better understand what happened—here is an example:

You have a repository:

2a2e220 (HEAD, master) C5 ab1bda4 C4 3cb46a9 C3 85f59ab C2 4516164 C1 0e783a3 C0 

Initial set of linear commits in a repository

You then proceed to change commits.

git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing 

(This is where you’ll have to take my word for it: there are a number of ways to change commits in Git. In this example I changed the time of C3, but you be inserting new commits, changing commit messages, reordering commits, squashing commits together, etc.)

ba7688a (HEAD, master) C5 44085d5 C4 961390d C3 85f59ab C2 4516164 C1 0e783a3 C0 

The same commits with new hashes

This is where it is important to notice that the commit hashes are different. This is expected behaviour since you have changed something (anything) about them. This is okay, BUT:

A graph log showing that master is out-of-sync with the remote

Trying to push will show you an error (and hint that you should run git pull).

$ git push origin master To <a class="__cf_email__" data-cfemail="46212f3206242f322433252d233268293421" href="/cdn-cgi/l/email-protection">[email protected]</a>:username/test1.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '<a class="__cf_email__" data-cfemail="5a3d332e1a38332e382f39313f2e7435283d" href="/cdn-cgi/l/email-protection">[email protected]</a>:username/test1.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 

If we run git pull, we see this log:

7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1 ba7688a C5 44085d5 C4 961390d C3 2a2e220 (origin/master) C5 85f59ab C2 ab1bda4 C4 4516164 C1 3cb46a9 C3 0e783a3 C0 

Or, shown another way:

A graph log showing a merge commit

And now we have duplicate commits locally. If we were to run git push we would send them up to the server.

To avoid getting to this stage, we could have run git push --force (where we instead ran git pull). This would have sent our commits with the new hashes to the server without issue. To fix the issue at this stage, we can reset back to before we ran git pull:

Look at the reflog (git reflog) to see what the commit hash was before we ran git pull.

070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy. ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master ba7688a HEAD@{3}: rebase -i (pick): C5 44085d5 HEAD@{4}: rebase -i (pick): C4 961390d HEAD@{5}: commit (amend): C3 3cb46a9 HEAD@{6}: cherry-pick: fast-forward 85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~ 2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master 2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master 2a2e220 HEAD@{10}: commit: C5 ab1bda4 HEAD@{11}: commit: C4 3cb46a9 HEAD@{12}: commit: C3 85f59ab HEAD@{13}: commit: C2 4516164 HEAD@{14}: commit: C1 0e783a3 HEAD@{15}: commit (initial): C0 

Above we see that ba7688a was the commit we were at before running git pull. With that commit hash in hand we can reset back to that (git reset --hard ba7688a) and then run git push --force.

And we’re done.

But wait, I continued to base work off of the duplicated commits

If you somehow didn’t notice that the commits were duplicated and proceeded to continue working atop of duplicate commits, you’ve really made a mess for yourself. The size of the mess is proportional to the number of commits you have atop of the duplicates.

What this looks like:

3b959b4 (HEAD, master) C10 8f84379 C9 0110e93 C8 6c4a525 C7 630e7b4 C6 070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1 ba7688a C5 44085d5 C4 961390d C3 2a2e220 C5 85f59ab C2 ab1bda4 C4 4516164 C1 3cb46a9 C3 0e783a3 C0 

Git log showing linear commits atop duplicated commits

Or, shown another way:

A log graph showing linear commits atop duplicated commits

In this scenario we want to remove the duplicate commits, but keep the commits that we have based on them—we want to keep C6 through C10. As with most things, there are a number of ways to go about this:

Either:

  • Create a new branch at the last duplicated commit1, cherry-pick each commit (C6 through C10 inclusive) onto that new branch, and treat that new branch as canonical.
  • Or run git rebase --interactive $commit, where $commit is the commit prior to both the duplicated commits2. Here we can outright delete the lines for the duplicates.

1 It doesn’t matter which of the two you choose, either ba7688a or 2a2e220 work fine.

2 In the example it would be 85f59ab.

TL;DR

Set advice.pushNonFastForward to false:

git config --global advice.pushNonFastForward false