Rebasing to avoid merge commits
Published: 2019-12-23 21:11:20

---
title: Rebasing to avoid merge commits
published: true
date: 2014-08-15 17:00:10 UTC
tags: 
canonical_url: 
---

Everyone likes to keep the source control history linear, preferring fast-forward merges to merge commits. It’s really not that hard. _Promise._

Git’s commit structure is a _linked list of snapshots_ whose commits point to one or several _ancestors_. This means that you must “base” your work on a previous commit.

### What’s a merge commit?

Merge commits can be useful when merging topic/feature branches together to indicate, in the history, where branches were combined.

When working on a single branch, however, they are ugly, unnecessary, clutter the history, and look like this:

![](https://cdn-images-1.medium.com/max/280/1*jierxlydpVNF-LgiSh134w.png)<figcaption>Here, I worked on master in two places,</figcaption>

This is the result of doing work — not necessarily on the same file just in the _same branch_ — in two places at once. Here, _eada9ae_ and _eac49bb_ are both children of _7036724_, but they contain different snapshots which were merged at 53ad270.

### Rebasing for great success

Let’s start with a history like this:

![](https://cdn-images-1.medium.com/max/509/1*DuATfls-IfdYIFQCi3hJ4g.png)<figcaption>Nice, linear history (so far)</figcaption>

Now, let’s work on the same file in two places at once (and also simulate a merge conflict). To do this, I’ll edit the file locally in the working copy, and I’ll edit it in the BitBucket editor (which gets committed and pushed immediately):

![](https://cdn-images-1.medium.com/max/566/1*ycX1RUN6abhyBu0qoOKPKg.png)<figcaption>BitBucket edit appears in the history.</figcaption>

Now, edit and commit the file locally:

```
msanford@Tenjin:/tmp/rebase-example $ git commit -m “Fourth commit — working copy”
[master 25dd301] Fourth commit — working copy
 1 file changed, 1 insertion(+)
```

Our branches are now out of sync: HEAD in origin/master points to 4ff5d2 whereas it points to 25dd301 in the working copy:

```
msanford@Tenjin:/tmp/rebase-example $ git log --oneline
25dd301 Fourth commit — working copy
e8a2bb8 Third commit — working copy
988eb03 Second commit — working copy
4168ee0 Initial commit — working copy
```

At this point, if you git push from the working copy, you’ll see this familiar message, suggesting that you _git pull_:

```
msanford@Tenjin:/tmp/rebase-example $ git push
To [git@bitbucket.org](mailto:git@bitbucket.org):michaelsanford/rebase-example.git
 ! [rejected] master -> master (fetch first)
error: failed to push some refs to ‘[git@bitbucket.org](mailto:git@bitbucket.org):michaelsanford/rebase-example.git’
hint: **Updates were rejected because the remote contains work that you do**
hint: **not have locally**. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., ‘ **git pull** …’) before pushing again.
hint: See the ‘Note about fast-forwards’ in ‘ **git push --help** ’ for details.
```

Note that the hint also suggests consulting “the fast-forward section of the manpage” which, of course, nobody has ever done. It suggests a more elegant course of action.

```
msanford@Tenjin:/tmp/rebase-example $ **git pull --rebase**
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From bitbucket.org:michaelsanford/rebase-example
 e8a2bb8..4ff45d2 master -> origin/master
**First, rewinding head to replay your work on top of it…**
Applying: Fourth commit — working copy
**Using index info to reconstruct a base tree…**
M README.md
Falling back to patching base and 3-way merge…
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Failed to merge in the changes.
Patch failed at 0001 Fourth commit — working copy
The copy of the patch that failed is found in:
 /private/tmp/rebase-example/.git/rebase-apply/patch

When you have resolved this problem, run “git rebase —continue”.
```

Rebase succeeded (no error messages)? No merge conflicts? You can push right away!

### Handling Merge Conflicts

Oh no, a merge conflict in the file we were working on!

#### README.md:

```
Edited locally in the working copy.
Second edit locally from the working copy.
Third commit locally from the working copy.
<<<<<<< HEAD
Fourth commit from BitBucket.
=======
Fourth commit locally from the working copy.
>>>>>>> Fourth commit — working copy
```

No problem: we can edit the file in the working copy, resolve the conflict, and _rebase continue_.

```
msanford@Tenjin:/tmp/rebase-example $ vi README.md
msanford@Tenjin:/tmp/rebase-example $ git add README.md
msanford@Tenjin:/tmp/rebase-example $ git rebase —continue
Applying: Fourth commit — working copy

msanford@Tenjin:/tmp/rebase-example $ git status
On branch master
Your branch is ahead of ‘origin/master’ by 1 commit.
 (use “git push” to publish your local commits)
```

### What happened?

Git rebase has, as the name suggests, changed the _base_ of our working copy’s commit history to match origin/master’s base. Since both the local working copy and the remote have the _same base commit_ (after fixing the merge conflict) it has allowed us to _fast-forward_ our work as a patch on top of the remote’s commit and maintain a linear history.

This results in a nice, straight line that contains _all of the work done in a semantically-sensible way_ (because the merged changes are topical):

![](https://cdn-images-1.medium.com/max/567/1*DQFoHYpM_4r4iRnXZv37HA.png)

In the first example (with the merge commit) if our working copy held the blue line with _eac49bb_, _git pull rebase_ would pull _eada9ae_, then create a patch and replay the work contained in _eac49bb_ on top of _that commit (_instead of using _7036724_ as the base_)_, [fast-forwarding](http://ariya.ofilabs.com/2013/09/fast-forward-git-merge.html) it and preserving a linear topical history.

### Teams

In this example I have used only one developer, but that is not relevant: you can do this in teams exactly the same way!