Rebasing branches

With the git rebase command you can also modify the story of branches; one of the things you do more often inside a repository is to change - or better to say - move the point where a branch started, bringing it to another point of the tree. This operation makes it possible to keep low the level of ramifications that would instead be generated using the command git merge, which we will see later.

In order to better understand this, let me give you an example.

Let's imagine that to the commit where the orange was added, a branch nuts was created in the past, to which a walnut was added.

At this point, let's imagine that we want to move this branch above, to the point where it is now master as if this branch had been created starting from there and not from the orange commit.

Let's see how this can be achieved using the git rebase command.

Let's start by creating a new branch that points to commit 0e8b5cf, the orange one:

[1] ~/grocery (master)
$ git branch nuts 0e8b5cf

This time I used the git branch command followed by two arguments, the name of the branch and the commit where to stick the label. As a result, a new nuts branch has been created:

[2] ~/grocery (master)
$ git log --oneline --graph --decorate --all
* 6409527 (HEAD -> master) Add a grape
* 603b9d1 Add a peach
| * a8c6219 (melons) Add a watermelon
| * ef6c382 (berries) Add a blackberry
|/
* 0e8b5cf (nuts) Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

Move HEAD to the new branch with the git checkout command:

[3] ~/grocery (master)
$ git checkout nuts
Switched to branch 'nuts'

Okay, now it's time to add a walnut; add it to the shoppingList.txt file:

[4] ~/grocery (nuts)
$ echo "walnut" >> shoppingList.txt

Then do the commit:

[5] ~/grocery (nuts)
$ git commit -am "Add a walnut"
[master 3d3ae9c] Add a walnut
 1 file changed, 1 insertion(+), 1 deletion(-)

Check the log:

[6] ~/grocery (nuts)
$ git log --oneline --graph --decorate --all
* 9a52383 (HEAD -> nuts) Add a walnut
| * 6409527 (master) Add a grape
| * 603b9d1 Add a peach
|/
| * a8c6219 (melons) Add a watermelon
| * ef6c382 (berries) Add a blackberry
|/
* 0e8b5cf Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

As you can see, the graph is now a little bit more complicated; starting from the orange commit, there are three branches: the berries, the master, and the nuts ones.

Now we want to move the nuts branch starting point, form an orange commit to a grape commit, as if the nuts branch is just one commit next to the master.

Let's do it, rebasing the nuts branch on top of master; double-check that you actually are in the nuts branch, as a rebase command basically rebases the current branch (nuts) to the target one, master; so:

[7] ~/grocery (nuts)
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Add a walnut
Using index info to reconstruct a base tree...
M   shoppingList.txt
Falling back to patching base and 3-way merge...
Auto-merging shoppingList.txt
CONFLICT (content): Merge conflict in shoppingList.txt
Patch failed at 0001 Add a walnut
The copy of the patch that failed is found in: .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".

error: Failed to merge in the changes.

Okay, don't be scared: the rebase failed, but that's not a problem. In fact, it failed because Git cannot merge differences between shoppingList.txt file versions automatically.

Read the message: Now you have three choices:

  1. Fix the merge conflicts and then continue, with git rebase -continue.
  2. Skip this step, and discard the modification using git rebase -skip.
  3. Abort the rebase, using git rebase -abort.

We will choose the first option, but I want to tell you something about the second and third ones.

While rebasing, Git internally creates patches and applies them to the commits we are moving; in fact, while rebasing a branch you actually move all its commits on top of another commit of choice, in this case the last commit on the master branch.

In this case, the nuts branch has only one commit, so Git compared the destination commit (the grape commit on master) with a walnut commit on the nuts branch. At the end, only one comparing and patching step will be necessary (this is the meaning of the REBASE 1/1 message on the console: you are rebasing commit 1 of 1 total commits to rebase).

That being said, you can now understand what git rebase --skip means: if you find the current patching step not useful nor necessary, you can skip it and move on to the next one.

Finally, with git rebase --abort you simply stop the current rebase operation, backing to the previous pre-rebase situation.

Now, back to our repository; if you open the file with Vim, you can see the generated conflict:

[8] ~/grocery (nuts|REBASE 1/1)
$ vi shoppingList.txt

Walnut has been added at line 4, but in the master branch, that line is occupied by the peach, and then there's a grape.

I will fix it adding the walnut at the end of the file:

Now, the next step is to git add the shoppingList.txt file to the staging area, and then go on with the git rebase --continue command, as the previous message suggested:

[9] ~/grocery (nuts|REBASE 1/1)
$ git add shoppingList.txt

[10] ~/grocery (nuts|REBASE 1/1)
$ git rebase --continue
Applying: Add a walnut

[11] ~/grocery (nuts)
$

As you can see, after the git rebase --continue command, the rebase ends successfully (no errors and no more REBASE message in shell prompt at step [11]).

Now take a look at the repo using git log as usual:

[12] ~/grocery (nuts)
$ git log --oneline --graph --decorate --all
* 383d95d (HEAD -> nuts) Add a walnut
* 6409527 (master) Add a grape
* 603b9d1 Add a peach
| * a8c6219 (melons) Add a watermelon
| * ef6c382 (berries) Add a blackberry
|/
* 0e8b5cf Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

Well done! The nuts branch is now just a commit beyond the master one.

Okay, now to keep the simplest and most compact repository, we cancel the walnut commit and put everything back in place as it was before this little experiment, even removing the nuts branch:

[13] ~/grocery (nuts)
$ git reset --hard HEAD^
HEAD is now at 6409527 Add a grape

[14] ~/grocery (nuts)
$ git checkout master
Switched to branch 'master'

[15] ~/grocery (master)
$ git branch -d nuts
Deleted branch nuts (was 6409527).

Well done.

Rebasing is a wide and fairly complex topic; we would need another entire chapter (or book) to tell everything about it, but this is basically what we need to know about rewriting history.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.202.167