5.4. Demystifying Rebasing.md - shinokada/gitnotes GitHub Wiki

Demystifying Rebasing

What is rebasing?

Rebasing is replaying a commit or a series of commits from history on top of a different commit in the repository.

Git first rewinds the branch that you’re rebasing – in this case, feature – back to its common ancestor with bugfix. The common ancestor is commit f. Git then replays the patch of each commit from the branch you’re rebasing on top of, in this case, bugfix, and moves the HEAD label along. Finally, one at a time, Git applies the patch of each commit from the branch you’re rebasing, in this case, feature, and moves the HEAD and feature labels along.

Creating your first rebase operation

❯ git checkout wValidator

Next create a new branch named cValidator from wValidator:

❯ git checkout -b cValidator

Open up README.md and add Chris to the end. Now stage your changes:

❯ git add .

And commit your staged changes with an appropriate message:

❯ git commit -m "Added Chris as a new maintainer to README.md"

Switch back to the wValidator branch:

❯ git checkout wValidator

Open README.md and add Chris’s initial C to the end:

# Maintainers
This project is maintained by teamWYXZC:

Stage and commit:

❯ git commit -am "Updated team acronym to teamWYXZC"

Take a quick look at the current state of the repository in graphical form:

❯ git log --oneline --graph --all
* a984d30 (HEAD -> wValidator) Updated team acronym to teamWYXZC
| * bc562c4 (cValidator) Added Chris as a new maintainer to README.md
|/  
* 3574ab3 (origin/wValidator) Whoops—didn't need to call that one twice
...

See a commit in a different branches. (wValidator and cValidator)

To rebase wValidator on top of cValidator, you need to be on the wValidator branch. Since you’re already there now, execute the rebase with the following command:

❯ git rebase cValidator
Successfully rebased and updated refs/heads/wValidator.

Take a look at the history graph

❯ git log --oneline --graph --all
* b4496d6 (HEAD -> wValidator) Updated team acronym to teamWYXZC
* bc562c4 (cValidator) Added Chris as a new maintainer to README.md
* 3574ab3 (origin/wValidator) Whoops—didn't need to call that one twice
...

You see the following linear activity at the top of the graph.

Clean up:

❯ git branch -d cValidator

A more complex rebase

To see the history for just those 2 branches instead of all branches you can pass in the branch names instead of --all:

❯ git log --oneline --graph wValidator origin/xValidator

Use origin/xValidator when you don't have a local copy of the xValidator branch checked out. This uses the origin/xValidator remote tracking branch directly. (Or you can either checkout a local copy of xValidator.)

* b4496d6 (HEAD -> wValidator) Updated team acronym to teamWYXZC
* bc562c4 Added Chris as a new maintainer to README.md
* 3574ab3 (origin/wValidator) Whoops—didn't need to call that one twice
* 43d6f24 check05: Finally, we can return true
| * 8ef01ac (origin/xValidator) Refactoring the main check function
| * 5fea71e Removing TODO
|/  
* bf3753e check04: Checking dia
gonal sums

The common ancestor of both branches is bf3753e.

Begin the rebase operation with the git rebase command, where you indicate which branch you want to rebase your current branch onto. Rebase wValidator on the top of origin/xValidator.

❯ git rebase origin/xValidator
Auto-merging js/magic_square/validator.js
CONFLICT (content): Merge conflict in js/magic_square/validator.js
error: could not apply 43d6f24... check05: Finally, we can return true
...

Let's solve the above merge conflicts.

Resolving errors

Three sections of code between the conflict markers:

<<<<<< HEAD
    Section 1: Xanthe's changes from origin/xValidator
|||||| parent of 43d6f24
    Section 2: Code before changes in the common ancestor
======
    Section 3: Will's changes on wValidator
>>>>>> 43d6f24

In the above chart, note that the first section is the current change from origin/xValidator, the second one is the common ancestor and the last one is from wValidator.

This is because in a rebase situation HEAD was first moved to the tip of the branch you’re rebasing on top of, i.e. the origin/xValidator branch. >>>>>> 43d6f24 tells which commit in the wValidator the confilict occured.

Resolving the conflict

Keep the changes on Xanthe’s branch (first section). After you have done it continue the rebase with the following command:

❯ git rebase --continue
js/magic_square/validator.js: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

Git rebases each of the original commits one at a time, so you need to deal with and add the changes from each commit resolution one at a time.

❯ git add .

Then continue with the rebase:

❯ git rebase --continue
Auto-merging js/magic_square/validator.js
CONFLICT (content): Merge conflict in js/magic_square/validator.js
error: could not apply 3574ab3... Whoops—didn't need to call that one twice

We just dealt with validator.js, but note could not apply 3574ab3... where as the previous conflict was with 43d6f24. This is a new conflict in a different commit.

There is actually an easier way to take the HEAD commit, i.e. Xanthe’s commit verbatim with no changes, while discarding all the changes from the conflicting commit.

Run the following command to completely skip applying the 3574ab3 - Whoops— didn’t need to call that one twice commit:

❯ git rebase --skip
Successfully rebased and updated refs/heads/wValidator.

See the result:

❯ git log --oneline --graph wValidator origin/xValidator
* 88df4df (HEAD -> wValidator) Updated team acronym to teamWYXZC
* 87c95f4 Added Chris as a new maintainer to README.md
* 8ef01ac (origin/xValidator) Refactoring the main check function
* 5fea71e Removing TODO
* bf3753e check04: Checking diagonal sums

Note that 5fea71e and 8ef01ac (both are from Xanthe’s two commits) comes after b37 53e and new commits, 87c95fe and 88df4df comes at the top. Note that the two conflicting commits from wValidator are gone (the commits with short hash 43d6f24 and 3574ab3). We skipped the first by manually keeping only Xanthe’s changes, and we skipped the second one with the git rebase --skip command.

To keep the changes from the HEAD commit verbatim, run git rebase --skip.

If you run git merge origin/xValidator:

*   44194fa (HEAD -> wValidator) Merge remote-tracking branch
'origin/xValidator' into wValidator
|\
| * 8ef01ac (origin/xValidator) Refactoring the main check
function

| * 5fea71e Removing TODO
* | 0e84d2b Updated team acronym to teamWYXZC
* | 7f754d4 Added Chris as a new maintainer to README.md
* | 3574ab3 (origin/wValidator) Whoops—didn't need to call that
one twice
* | 43d6f24 check05: Finally, we can return true
|/
* bf3753e check04: Checking diagonal sums

This is confusing because you’d still see the original 43d6f24 and 3574ab3 commits in the history, which would have essentially been undone in the conflict resolution of the 44194fa merge commit.

Orphaned commits

Even though the skipped commits are no longer shown in the log, you can still find these commits if you still have the original commit hash for them.

❯ git log --oneline -3 3574ab3
3574ab3 (origin/wValidator) Whoops—didn't need to call that one
twice
43d6f24 check05: Finally, we can return true
bf3753e check04: Checking diagonal sums

Those commits are orphaned, or “loose” as Git refers to them. It’s just sitting there until Git does its usual garbage collection, at which point Git will physically delete any loose objects that have been hanging around too long.

Merging vs rebasing

  • Choose to rebase when grouping the changes in a linear fashion makes contextual sense, such as Will’s and Xanthe’s work above that’s contained to the same file.

  • Choose to merge when you’ve created major changes, such as adding a new feature in a pull request, where the branching strategy will give context to the history graph. A merge commit will have the history of both common ancestors, while rebasing removes this bit of contextual information.

  • Choose to rebase when you have a messy local commit or local branching history and you want to clean things up before you push. This touches on what’s known as squashing, which you’ll cover in a later chapter.

  • Choose to merge when having a complex history graph doesn’t affect the day-to- day functions of your team.

  • Choose to rebase when your team frequently has to work through the history graph to figure out who changed what and when. Those merge commits add up over time!

Challenge solution

Your challenge is to rebase the work you’ve done on the wValidator branch on top of Zach's zValidator branch.

  • First checkout zValidator with git checkout zValidator.
  • Now switch back to the wValidator branch with git checkout wValidator.
  • Execute git rebase zValidator; this should complete without conflict.
  • Execute git branch -d zValidator; this should complete without issues.

Note: Instead of checking out and then deleting zValidator you could also have just rebased directly onto origin/zValidator.

Key points

  • Rebasing “replays” commits from one branch on top of another.

  • Rebasing is a great technique over merging when you want to keep the repository history linear and as free from merge commits as possible.

  • To rebase your current branch on top of another one, execute git rebase .

  • You can resolve rebase conflicts just as you do merge conflicts.

  • To resume a rebase operation after resolving conflicts and staging your changes, execute git rebase --continue.

  • To skip rebasing a commit on top of the current branch, execute git rebase --skip.

⚠️ **GitHub.com Fallback** ⚠️