Rebasing Step-by-Step

This is a walk-through of how I recently resolved a mid-rebase conflict. The scenario: At the very beginning of the project, I stubbed out a number of chapter files in the branch drafts. Once I had a few chapters finished, I uploaded the files to the builder server and realised it made for a very ugly document. I separated work-in-progress into per-chapter branches. This made things a lot cleaner. Unfortunately I forgot to delete the stub file for Chapter 10 in the branch drafts. Eventually I got around to it, but in the mean time, I'd also made a bunch of edits to the chapter file in the branch ch10. Bringing ch10 up-to-date via rebasing meant dealing with the conflict whereby the updates in drafts would result in the file being removed from ch10, which was not what I wanted as a final outcome. This piece walks you through how I brought the branch ch10 up-to-date while keeping the latest version of the file ch10.asciidoc (from ch10) in place.

This is a free excerpt from my upcoming book Git for Teams.

Begin Rebasing

Change into the branch which is currently out-of-date from the main project, but which contains new work that hasn't been introduced yet:

$ git checkout ch10

Begin the rebasing process:

$ git rebase drafts

If there are no conflicts, Git will skip merrily through the process and spit you out the other end. However...if there are conflicts...

Mid-Rebase Conflict from a Deleted File

If you run into a conflict mid-rebase, Git will stop the procedure and ask you to deal with it. The following is the output from the first mid-rebase conflict.

First, rewinding head to replay your work on top of it...
Applying: CH10: Stub file added with notes copied from video recording lessons.
Using index info to reconstruct a base tree...
A   ch10.asciidoc
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): ch10.asciidoc deleted in HEAD and modified in CH10: Stub file added with notes copied from video recording lessons.. Version CH10: Stub file added with notes copied from video recording lessons. of ch10.asciidoc left in tree.
Failed to merge in the changes.
Patch failed at 0001 CH10: Stub file added with notes copied from video recording lessons.
The copy of the patch that failed is found in:
   /Users/emmajane/Git/1234000002182/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Exactly as I anticipated: The file ch10.asciidoc is a problem. Git has put me into a detached HEAD state while I resolve the conflict.

The relevant piece of information from this output is:

When you have resolved this problem, run "git rebase --continue".

This tells me that I need to:

  1. Resolve the merge conflict.
  2. Once I think the merge conflict is resolved, run the command git rebase --continue.

Step 1: resolve the merge conflict. I do this by editing the file in question and looking for merge conflict markers.

$ vim ch10.asciidoc

At this point CH10 is an out-of-date version of the file which existed at an earlier point. There are no merge conflict markers in the file, so I proceed to Step 2.

$ git rebase --continue

The following message is returned from Git:

ch10.asciidoc: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

That not very helpful! I just looked at that file and there were no merge conflicts. I'll ask Git what the problem is using the command status.

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add/rm <file>..." as appropriate to mark resolution)

    deleted by us:   ch10.asciidoc

no changes added to commit (use "git add" and/or "git commit -a")

Aha: Unmerged paths and then a little later on deleted by us: ch10.asciidoc. Well I don't want the file to be deleted.

Fortunately Git has given me a useful suggestion this time.

(use "git reset HEAD <file>..." to unstage)

This is useful because Git has told me deleted by us and I know I don't want to delete the file, therefore I need to unstage Git's change. Unstaging a change is effectively saying to Git, "that thing you were planning to do? Don't do it. In fact, forget you were even thinking about doing anything with that file. Reset your HEAD, Git."

$ git reset HEAD ch10.asciidoc

Now, what this command is actually doing is clearing out the staging index, and moving the pointer back to the most recent known commit. As I am knee-deep in a rebase, and in a detached HEAD state as opposed to in a branch, reset simply clears away the staging index and puts me in the most recent state from the rebasing process. In my case, this leaves me with the older version of the file, which is fine. As I proceed through the rebase, I'll replace the contents of the file with the latest version from the ch10 branch.

Still with me? Let's see what Git thinks I should do next:

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (all conflicts fixed: run "git rebase --continue")

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    ch10.asciidoc

nothing added to commit but untracked files present (use "git add" to track)

So I've still got the file (great!), but Git is also confused about what to do, because as far as it's concerned, that file should have been deleted. We need to explicitly add the file back into the repository at this point, which Git tells me Untracked files: (use "git add <file>..." to include in what will be committed) ch10.asciidoc. It's sort of weird formatting for my single file, but if there is a longer list of files, this formatting is lovely.

$ git add ch10.asciidoc

Now at this point, I know that add is just the beginning of a process, and that I'm going to need to commit the file as well, but this is rebasing and the rules are different. I'm going to ask Git what to do next by checking the status again.

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   ch10.asciidoc

Okay, it's saying there are changes to be committed (yup, already knew that), BUT it doesn't tell me to commit them! Instead it tells me to simply continue with the rebasing all conflicts fixed: run "git rebase --continue". I proceed with this command even though add is normally paired with commit to save changes.

$ git rebase --continue

Mid-Rebase Conflict from a Single File Merge Conflict

After re-starting the rebasing process, Git has run into another conflict as it replays the commits. The output is as follows:

Applying: CH10: Stub file added with notes copied from video recording lessons.
Applying: TOC: Adding Chapter 10 to the book build.
Using index info to reconstruct a base tree...
M   book.asciidoc
Falling back to patching base and 3-way merge...
Auto-merging book.asciidoc
CONFLICT (content): Merge conflict in book.asciidoc
Recorded preimage for 'book.asciidoc'
Failed to merge in the changes.
Patch failed at 0002 TOC: Adding Chapter 10 to the book build.
The copy of the patch that failed is found in:
   /Users/emmajane/Git/1234000002182/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Another conflict. You're being high maintenance, Git! Okay, okay, at least it's a different file this time (CONFLICT (content): Merge conflict in book.asciidoc). I take a closer look at the status again to see if Git gives me additional clues.

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

    both modified:   book.asciidoc

no changes added to commit (use "git add" and/or "git commit -a")

Long sigh. Alright, Git. Let's see what the conflict is in this file.

$ vim book.asciidoc

Opening up the file in my favourite editor, I see there is indeed a legit merge conflict in this file (it has rows of angle brackets showing me the files that Git isn't sure how to resolve). The merge conflict markers are in place, and it's easy to clean things up. I save the file, and ask Git if it's happy by using the status command, again.

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

    both modified:   book.asciidoc

no changes added to commit (use "git add" and/or "git commit -a")

The message is a little misleading because I have fixed the conflicts. At this point I opened the file to double check. Nope, no conflicts there. So now I move onto the next group of instructions: unmerged paths: use "git add <file>..." to mark resolution and then both modified: book.asciidoc.

$ git add book.asciidoc

And check the status again (at this point I'm starting to think "if I had a dollar for every time I ran git status I'd be freaking rich!"):

$ git status

The output from Git is as follows:

rebase in progress; onto 6ef4edb
You are currently rebasing branch 'ch10' on '6ef4edb'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   book.asciidoc

Again, kinda weird, but I don't actually run the commit command at this point, instead, Git tells me all conflicts fixed: run "git rebase --continue" so I simply proceed with the rebasing process:

$ git rebase --continue

The output from Git is as follows:

Applying: TOC: Adding Chapter 10 to the book build.
Recorded resolution for 'book.asciidoc'.
Applying: CH10: Outline of GitHub topics

The rebasing procedure has been completed. My branch ch10 is now up-to-date with all changes which had been previously committed to the drafts branch.

Conclusion

There are a few different ways that rebasing can kick up a conflict. Take your time, read the instructions carefully, and if you aren't getting useful information, try using git status to see if there's something more helpful that Git can offer. And, if you are really in a panic about what's happening, you can always abort the process with git rebase --abort. This will return you the state your branch was in right before you started the rebase.