Git rebase explained in detail with examples


GIT

What is Git Rebase?

Rebasing is nothing more than taking one branch and adding it to the tip of another, where the tip is simply the last commit in the branch. Based on this analogy, git rebase is used to integrate changes from one branch into another

Following image would give you a clear idea of git rebase in realtime:
Git rebase explained in detail with examples

 

Workflow of Git rebase

This is the general workflow of git rebase which we will explore using some examples in the next section:

clone the remote repository

git checkout -b my-feature-branch
>>>..work and commit some stuff..<<<
git rebase master
>>>..work and commit some stuff..<<<
git rebase master
>>>..finish the feature, commit..<<<
git rebase master

git checkout master
git merge my-feature-branch

 

Clone repository to local workstation

I have cloned git_examples project from my gitlab:

Git rebase explained in detail with examples

 

Create backup branch before git rebase

WARNING:
git rebase can be dangerous for repositories with multiple shared branches. As same repo would be used by multiple users, the chances of having rebase conflicts are much higher. Sometimes these conflicts can be very complex and becomes near to impossible to resolve. In such scenarios it is better to perform git pull rather than git rebase.

Anyhow to be on the safer side it is recommended to create a backup branch before performing git rebase so you can reset your base branch from the backup in case of any issues.

Switch to your base branch:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

Create a new backup branch and switch to that branch:

deepak@ubuntu:~/git_examples$ git checkout -b my-feature-branch-backup
Switched to a new branch 'my-feature-branch-backup'

Now you can switch back to your base branch and proceed with the rebasing:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

If you encounter any issues while performing git rebase, you can switch to my-feature-branch:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

And perform a hard reset against the backup branch:

deepak@ubuntu:~/git_examples$ git reset --hard my-feature-branch-backup

Although if you did any changes after creating the backup branch then those will be lost.

 

Example-1: Steps to perform git rebase

Step-1: Checkout to feature branch

We are currently at my-feature-branch:

deepak@ubuntu:~/git_examples$ git branch
  main
* my-feature-branch
  my-feature-branch-backup

Following are the list of commits performed on this repository:

Git rebase explained in detail with examples

 

Step-2: Commit changes in feature branch

This step is optional, just to demonstrate the impact of changes. I will go ahead and make some changes in git_rebase/script2.sh for my-feature-branch:

deepak@ubuntu:~/git_examples$ vim git_rebase/script2.sh

Commit the changes with a proper message:

deepak@ubuntu:~/git_examples$ git commit -m "Added some comment in script2" git_rebase/script2.sh
[my-feature-branch 5313ae5] Added some comment in script2
1 file changed, 1 insertion(+)

Verify the list of commits in my-feature-branch:

deepak@ubuntu:~/git_examples$ git log --oneline
5313ae5 (HEAD -> my-feature-branch) Added some comment in script2
9177492 (origin/main, origin/HEAD, main) Print date in script2
3754278 Print working directory detail
9afa6c2 Added script2.sh under git_rebase
784b0ae Added new script under git_rebase
ddce879 Initial commit

 

Step-3: Commit changes in main branch

Now let us also make some changes under our main branch. This step is again optional and purely to demonstrate the behaviour. So we will switch to our main branch:

deepak@ubuntu:~/git_examples$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

Make some changes to a different script git_rebase/script1.sh:

deepak@ubuntu:~/git_examples$ vim git_rebase/script1.sh
IMPORTANT NOTE:
This point is important that I am making changes to a different script which was untouched in my-feature-branch. If the same file is modified in both the branches then git rebase will cause conflicts which we will analyse in next section of this tutorial.

Commit the changes under main branch:

deepak@ubuntu:~/git_examples$ git commit -m "Added name in script1.sh" git_rebase/script1.sh
[main 4357c59] Added name in script1.sh
1 file changed, 1 insertion(+)

As you can see, the commits from my-feature-branch are not visible here at this stage:

deepak@ubuntu:~/git_examples$ git log --oneline
4357c59 (HEAD -> main) Added name in script1.sh
9177492 (origin/main, origin/HEAD) Print date in script2
3754278 Print working directory detail
9afa6c2 Added script2.sh under git_rebase
784b0ae Added new script under git_rebase
ddce879 Initial commit

 

Step-4: Perform git rebase

Let's switch back to my-feature-branch:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

deepak@ubuntu:~/git_examples$ git branch
main
* my-feature-branch

Now we feel that may be we should rebase our branch with main branch to inline the changes:

deepak@ubuntu:~/git_examples$ git rebase main
Successfully rebased and updated refs/heads/my-feature-branch.

The rebase was successful because different set of files were modified in both the branches. At this stage, the my-feature-branch will have all the changes from main branch:

deepak@ubuntu:~/git_examples$ git log --oneline
812ceb5 (HEAD -> my-feature-branch) Added some comment in script2
4357c59 (main) Added name in script1.sh
9177492 (origin/main, origin/HEAD) Print date in script2
3754278 Print working directory detail
9afa6c2 Added script2.sh under git_rebase
784b0ae Added new script under git_rebase
ddce879 Initial commit

Here is a flow diagram explaining what we did above:

Git rebase explained in detail with examples

 

Let's switch to main branch:

Git rebase explained in detail with examples

As the above message says, the main branch is ahead of my-feature-branch. This is because we have not yet merged my-feature-branch into main branch:

Git rebase explained in detail with examples

Let's make some more changes into the main branch:

deepak@ubuntu:~/git_examples$ vim git_rebase/script1.sh

deepak@ubuntu:~/git_examples$ git commit -m "Modified script1 before merging with my-feature-branch" git_rebase/script1.sh
[main 0d2ce16] Modified script1 before merging with my-feature-branch
1 file changed, 1 insertion(+)

List the commits in main branch:

deepak@ubuntu:~/git_examples$ git log --oneline
0d2ce16 (HEAD -> main) Modified script1 before merging with my-feature-branch
4357c59 Added name in script1.sh
9177492 (origin/main, origin/HEAD) Print date in script2
3754278 Print working directory detail
9afa6c2 Added script2.sh under git_rebase
784b0ae Added new script under git_rebase
ddce879 Initial commit

 

Step-5: Merge feature branch into main branch

Now let's merge my-feature-branch into main branch:

NOTE:
The user can choose to continue working on my-feature-branch and push the same to gitlab repository. But here we will merge this into main branch and push all the changes into main branch itself.
deepak@ubuntu:~/git_examples$ git merge my-feature-branch
Merge made by the 'recursive' strategy.
git_rebase/script2.sh | 1 +
1 file changed, 1 insertion(+)

Get the list of commits. Now we have all the changes from my-feature-branch also merged into main branch.

deepak@ubuntu:~/git_examples$ git log --oneline
fa16ae6 (HEAD -> main) Merge branch 'my-feature-branch'
0d2ce16 Modified script1 before merging with my-feature-branch
812ceb5 (my-feature-branch) Added some comment in script2
4357c59 Added name in script1.sh
9177492 (origin/main, origin/HEAD) Print date in script2
3754278 Print working directory detail
9afa6c2 Added script2.sh under git_rebase
784b0ae Added new script under git_rebase
ddce879 Initial commit

 

Step-6: Push commits to remote repository

Let's push these changes to our remote gitlab repository under main branch:

Git rebase explained in detail with examples

 

How to solve git rebase conflicts

A conflict arises when your current branch and the branch to be merged have diverged, and there are commits in your current branch that aren’t present in the other branch, and vice versa. Git isn’t able to determine which changes to keep, so it raises a conflict to ask the user to review the changes. The last common commit between the two branches—which is also the point where they diverged—is called the base commit.

 

Step-1: Commit changes in feature branch

Let's switch to my-feature-branch and make some changes to one of the scripts in our repo:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

Make some changes to git_rebase/script2.sh script:

deepak@ubuntu:~/git_examples$ echo "##Adding in script2 under my-feature-branch" >> git_rebase/script2.sh

deepak@ubuntu:~/git_examples$ git commit -m "Updated script2.sh" git_rebase/script2.sh
[my-feature-branch 7bb4ce4] Updated script2.sh
 1 file changed, 2 insertions(+)

List the commit changes, 7bb4ce4 is the latest commit ID for our latest change under my-feature-branch:

Git rebase explained in detail with examples

 

Step-2: Commit changes in main branch

To intentionally create a git rebase conflict, we will modify the same file under main branch:

deepak@ubuntu:~/git_examples$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

Now since we are under main branch, let's modify the same file with some new content:

deepak@ubuntu:~/git_examples$ echo "##Adding in script2 under main-branch" >> git_rebase/script2.sh   

deepak@ubuntu:~/git_examples$ git commit -m "Updated script2.sh in main branch" git_rebase/script2.sh 
[main 6297a37] Updated script2.sh in main branch
 1 file changed, 1 insertion(+)

You can verify the list of commits. Here 6297a37 is the latest commit ID under main branch:

Git rebase explained in detail with examples

 

Step-3: Perform git rebase to main branch

Now switch to my-feature-branch:

deepak@ubuntu:~/git_examples$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'

Try to perform git rebase with main branch:

deepak@ubuntu:~/git_examples$ git rebase main
Auto-merging git_rebase/script2.sh
CONFLICT (content): Merge conflict in git_rebase/script2.sh
error: could not apply 7bb4ce4... Updated script2.sh
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 7bb4ce4... Updated script2.sh

The same behaviour is explained in this image:

Git rebase explained in detail with examples

 

Step-4: Fix the conflicting files

As expected, the git rebase has failed with conflicts. You can check the output of above command which claims that 7bb4ce4 commit ID from my-feature-branch is having a conflict with main branch changes. So we must manually fix this conflict.

Check the content of the conflicted file:

deepak@ubuntu:~/git_examples$ cat git_rebase/script2.sh
#!/bin/bash

echo "This is script2"
echo "Date: `date`"
## Some comment
<<<<<<< HEAD
##Adding in script2 under main-branch
=======

##Adding in script2 under my-feature-branch
>>>>>>> 7bb4ce4 (Updated script2.sh)
  • <<<<<<<: Indicates the start of the lines that had a merge conflict.
  • =======: Indicates separation of the two conflicting changes.
  • >>>>>>>: Indicates the end of the lines that had a merge conflict.
NOTE:
You need to resolve a conflict by manually editing the file. You also need to delete the ‘<<<<<<<’, ‘=======’, and ’>>>>>>>’ in the file.

So based on the indicated lines, we know the content added as part of main and my-feature-branch. Here you must decide the content which you wish to keep or delete. Let us delete the content added as part of my-feature-branch. Here is our final content of this file after removing the extra data:

deepak@ubuntu:~/git_examples$ cat git_rebase/script2.sh
#!/bin/bash

echo "This is script2"
echo "Date: `date`"
## Some comment
##Adding in script2 under main-branch

 

Step-5: Add conflicting files to staging environment

Add the files to the stage after you have fixed the conflict:

deepak@ubuntu:~/git_examples$ git add git_rebase/script2.sh

 

Step-6: Perform git rebase --continue

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.

deepak@ubuntu:~/git_examples$ git rebase --continue
Successfully rebased and updated refs/heads/my-feature-branch.

This time the git rebase was successful. Here is the diagram flow explaining the fix:

Git rebase explained in detail with examples

 

Verify the list of commits, you can see that the changes done as part of commit ID 7bb4ce4 is not there as we had manually removed those changes to fix the conflict:

Git rebase explained in detail with examples

 

Step-7: Perform git merge to main branch

Now we can merge this to our main branch. Again this is optional and if you wish to continue working on my-feature-branch then you can ignore this step.

Switch to main branch:

deepak@ubuntu:~/git_examples$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

Merge my-feature-branch into main branch

deepak@ubuntu:~/git_examples$ git merge my-feature-branch

Here is an image demonstrating this scenario:
Git rebase explained in detail with examples

 

What are the main differences between merge and rebase

  • merge executes only one new commit. rebase typically executes multiple (number of commits in current branch).
  • merge produces a new generated commit (the so called merge-commit). rebase only moves existing commits.

 

When should you use git rebase

Rebase "lifts off" your changes and puts all the changes of the rebased branch into your current branch and then puts your changes on top of it. It therefore changes the history of your branch.

  • no one else is working on the same content (as you are) on a different branch
  • you want your to see all your changes at one point together when merging back to the source branch
  • you want to avoid the auto-generated "merged .." commit messages

I said "you want to see all your changes at one place" because sometimes a merge operation puts all your changes together in one commit (some: merged from ... message). Rebase makes your change look like you made your commits all after each other with no one else doing something in between. This makes it easier to see, what you changed for your feature.

You can also use git merge feature-branch --ff-only to make sure there are no conflicts creating a single commit when you are merging your feature back to main/master.

 

Why git rebase is better than git merge

The reason why a rebase is then better than a merge is that:

  • You rewrite your local commit history with the one of the master (and then reapply your work, resolving any conflict then)
  • The final merge will certainly be a "fast forward" one, because it will have all the commit history of the master, plus only your changes to reapply.

Use rebase whenever you want to add changes of a base branch back to a branched out branch. Typically, you do this in feature branches whenever there's a change in the main branch.

 

What are the problems of using git rebase

  • Because a rebase moves commits (technically re-executes them), the commit date of all moved commits will be the time of the rebase and the git history loses the initial commit time. So, if the exact date of a commit is needed for some reason, then merge is the better option. But typically, a clean git history is much more useful than exact commit dates.
  • If the rebased branch has multiple commits that change the same line and that line was also changed in the base branch, you might need to solve merge conflicts for that same line multiple times, which you never need to do when merging. So, on average, there's more merge conflicts to solve.

 

Summary

Rebasing commits is the one concept in Git that has no counterpart inside the traditional version control world. Using git rebase, you can rewrite the history of a repository in a variety of ways. It is one of the most powerful commands in Git, which makes it one of the most dangerous.

In this tutorial, we covered how to:

  • clone from a remote repository
  • create, update and merge branches
  • keep a local repository updated
  • send the changes from a local repository to a remote
  • manage conflicts during rebase

 

References

Git workflow and rebase vs merge questions
When do you use Git rebase instead of Git merge?

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment