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

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

  1. Why does Git duplicate the commits in this PR while rebasing?
  2. How can I fix the mess am in now?

Kind help please!

@jwnasambu 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.

I follow a somewhat repetitive workflow but it helps;

Pull changes from the Upstream Repository into the local master branch

$ git pull --rebase upstream master    #—On local master branch to pull changes from upstream.`

$ git checkout -b TRUNK-123 master     -b [optional flag / applies to creating a new branch]

$ git rebase -i master     -i [optional for interactive rebase]

If you are on branch X and you do git rebase -i master, it changes branch X, not master, so you would have to push branch X.

Conclusion: Pull upstream changes to the local master branch, then check out your branch and rebase when required (You would have to commit any local changes to that branch first).

You can also rebase on branches you don’t have checked out, e.g.,

git fetch upstream master
git rebase -i upstream/master

Should do the same thing.

@jwnasambu Don’t worry too much about what commits show up in the commits tab on GitHub… This tends to happen when you mix two methods of keeping a branch up to date, e.g., both doing a git merge of master and then a git rebase. Generally, you shouldn’t mix both merges and rebases on a single branch because it gets messy.

If you want to clean things up, then the thing to do is a git rebase --interactive and remove the merge commits. A Git UI can be helpful here…

Merging and rebasing are two mechanisms for keeping a branch in sync with an upstream branch. A merge applies the updates from the upstream branch by creating a new commit (the merge commit) in the current branch that contains the changes and, in the case of conflicts, any changes you’ve made to address those conflicts.

Rebasing applied a different strategy: it takes the point where the two branches (upstream and current) diverge, resets to that point, applies the changes from upstream and then the changes from the current branch. That’s what this diagram is getting at:

C4’ is the same diff as C4, but it’s now applied after C3 instead of C2. With a merge you end up with this:

Where C6 adds C3 and C5 to the master branch plus the merge commit which includes any conflict resolutions that need to be applied.

2 Likes

Thanks so so much for the clear explanation. I now understand where the mess originated from and I now know how to go about it. I was really scared but now I can move with confidence. Once again thanks @ibacher.