Rebase vs Merge
Overview
Rebasing and merging are two methods of integrating changes from one branch into another branch or from remote branch into local branch. Both methods are used to keep the target branch up to date with the source branch's changes. Both have different way of doing this, their own advantages and disadvantages and the choice between them depends on the specific use case and the team's workflow.
Let's take a look at each method and see how they work.
Rebasing
What is Rebasing?
It integrates changes from one branch into another by reapplying commits from the source branch onto the target branch resulting in a linear history where the target branch's commits are placed on top of the source branch's commits. It keeps the target branch up to date with the source branch's changes without creating merge commits.
A merge commit is created when merging two branches. It is used to combine the changes from one branch into another branch, and it typically has two parent commits, one for each branch that is being merged. The merge commit contains a summary of the changes made in both branches, and it allows developers to easily track and revert any conflicts or issues that may have occurred during the merge process.
Rebasing is a method of integrating changes from one branch into another branch by reapplying commits on top of the target branch, rather than merging the branches together. When a branch is rebased, the commits from the source branch are replayed one by one in a new linear sequence on top of the target branch. This results in a new history for the branch, where the source branch's commits appear to have been made after the target branch's commits.
Because rebasing replays commits instead of merging branches, it does not create a merge commit. Instead, it creates a new set of commits that are identical to the original commits from the source branch, but with different SHA-1 hash values. This can make it difficult to trace the original history of the branch and any conflicts that may have occurred during the rebase process.
Example:
- A team is working on a website and the main branch is called
master. - A developer creates a new branch called
feature/contact-formfrom themasterbranch. - The developer works on the contact form and tests it on their local environment.
- Meanwhile, other developers make changes to the
masterbranch by merging another feature branch into themasterbranch. - When the developer is ready to merge their changes into the
masterbranch by creating a pull request, they discover that theirfeature/contact-formbranch is out of date with themasterbranch. - The
feature/contact-formbranch does not have the latest changes from themasterbranch. - Developer needs to update their
feature/contact-formbranch with the latest changes from themasterbranch. - So, they to switch to the
masterbranch and pull the latest changes from the remote repository. - Then, they switch back to the
feature/contact-formbranch and rebase their changes on top of themasterbranch's commits using thegit rebasecommand. - The
feature/contact-formbranch now has the latest changes from themasterbranch and can be easily merged into themasterbranch. - Now, one can create a pull request to merge the
feature/contact-formbranch into themasterbranch. - The code is reviewed by other team members and if no issues are found, it is merged into the
masterbranch. - The website now has the new feature, the contact form, available on the live website.
Commands:
Scenario 1 - Developer is working on a new feature branch, and need to integrate the latest changes from the master branch into their feature branch.
When the developer is ready to merge their changes into the master branch by creating a pull request, developer discovered that feature/feeback-form branch is out of date with the master branch. And local master branch is also not up-to-date with the remote master branch.
We can do this using rebasing by two different ways.
Method 1 - Using git rebase command
User is currently on the feature/feeback-form branch.
For this, we need to update local master branch with the latest changes from the remote master branch and then update local feature/feeback-form branch with the latest changes from the local master branch.
git checkout master
git pull
git checkout feature/feeback-form
git rebase master
Method 2 - Using git rebase origin/master command
User is currently on the feature/feeback-form branch.
For this, we need to update local feature/feeback-form branch with the latest changes from the remote master branch.
git fetch
git rebase origin/master
git fetch will fetch the latest changes from the remote repository and git rebase origin/master will rebase the current branch on top of the remote master branch.
Why we need to git fetch before git rebase origin/master?
Because, git rebase origin/master will rebase the current branch on top of the remote master branch. But if we don't fetch the latest changes from the remote repository, then git rebase origin/master will not rebase the current branch on top of the remote master branch.
origin/branch-name vs branch-nameorigin/branch-name is the remote branch
branch-name is the local branch
Both branches can have different commits. When we run git fetch, it will fetch the latest commits from the remote repository, and put then in origin/branch-name. But it will not update the local branch.
Scenario 2 - Developer is working on a new feature branch, another developer made changes to the feature branch, and need to integrate the latest changes from the remote feature branch into their local feature branch.
When second developer makes changes to the feature/feeback-form branch, the first developer needs to update local feature/feeback-form branch with the latest changes from the remote feature/feeback-form branch.
User is currently on the feature/feeback-form branch.
For this, we need to update local feature/feeback-form branch with the latest changes from the remote feature/feeback-form branch.
git pull --rebase
Merging
What is Merging?
Merging is a method of integrating changes from one branch into another by creating a new commit that combines the changes from both branches. This results in a non-linear history where the commits from both branches are preserved. This method is used to create a clear record of when the changes from one branch were integrated into another branch.
Example:
- A team is working on a website and the main branch is called
master. - A developer creates a new branch called
feature/feeback-formfrom themasterbranch. - The developer works on the feedback form and tests it on their local environment.
- Meanwhile, other developers make changes to the
masterbranch by merging another feature branch into themasterbranch. - When the developer is ready to merge their changes into the
masterbranch by creating a pull request. - But before creating a pull request, they must first merge the latest changes from the
masterbranch into theirfeature/feeback-formbranch. - So, they switch to the
masterbranch and pull the latest changes from the remote repository. - Then, they switch back to the
feature/feeback-formbranch and merge themasterbranch into theirfeature/feeback-formbranch using thegit mergecommand. - The
feature/feeback-formbranch now has the latest changes from themasterbranch and can be easily merged into themasterbranch. - Now, one can create a pull request to merge the
feature/feeback-formbranch into themasterbranch. - The code is reviewed by other team members and if no issues are found, it is merged into the
masterbranch. - The website now has the new feature, the feedback form, available on the live website.
Commands:
Scenario 1 - Developer is working on a new feature branch, and need to integrate the latest changes from the master branch into their feature branch.
When the developer is ready to merge their changes into the master branch by creating a pull request, developer discovered that feature/feeback-form branch is out of date with the master branch. And local master branch is also not up-to-date with the remote master branch.
We can do this using merging by two different ways.
Method 1 - Using git merge command
User is currently on the feature/feeback-form branch.
For this, we need to update local master branch with the latest changes from the remote master branch and then update local feature/feeback-form branch with the latest changes from the local master branch.
git checkout master
git pull
git checkout feature/feeback-form
git merge master
Method 2 - Using git merge origin/master command
User is currently on the feature/feeback-form branch.
For this, we need to update local feature/feeback-form branch with the latest changes from the remote master branch.
git fetch
git merge origin/master
git merge master vs git merge origin/mastergit merge master will merge the local master branch into the current branch.
git merge origin/master will merge the remote master branch into the current branch.
Scenario 2 - Developer is working on a new feature branch, another developer made changes to the feature branch, and need to integrate the latest changes from the remote feature branch into their local feature branch.
When second developer makes changes to the feature/feeback-form branch, the first developer needs to update local feature/feeback-form branch with the latest changes from the remote feature/feeback-form branch.
User is currently on the feature/feeback-form branch.
For this, we need to update local feature/feeback-form branch with the latest changes from the remote feature/feeback-form branch.
git pull
Differences between Rebasing and Merging
Both rebasing and merging are used to integrate changes from one branch into another branch. But there are some differences between them. Let's discuss them one by one.
History
Rebasing changes the history of the target branch by placing the target branch's commits on top of the source branch's commits. This results in a linear history where the target branch's commits are placed on top of the source branch's commits. This method is used to keep the target branch up to date with the source branch's changes without creating merge commits.
Merging does not change the history of the target branch. This results in a non-linear history where the commits from both branches are preserved. This method is used to create a clear record of when the changes from one branch were integrated into another branch.
Commits
Rebasing changes the commit hashes of the target branch's commits. And in merging, the commit hashes of the target branch's commits remain the same. This is because rebasing rewrites the target branch's commits by placing them on top of the source branch's commits. But in merging, the target branch's commits are not rewritten.
Merge Conflicts
Rebasing is a destructive process. It rewrites the target branch's commits by placing them on top of the source branch's commits. This means that if there are any merge conflicts, they must be resolved before rebasing. Otherwise, the rebase process will fail.
Merging is a non-destructive process. It does not rewrite the target branch's commits. This means that if there are any merge conflicts, they can be resolved after merging. The merge process will not fail if there are any merge conflicts.
When to use Rebasing
Rebasing should be preferred over merging in the following scenarios:
- When working on a personal branch that is not shared with other team members, rebasing can be used to keep the branch up to date with the main branch without creating unnecessary merge commits.
- When working on a feature branch that will eventually be merged back into the main branch, rebasing can be used to keep the branch up to date with the main branch and make it easier to resolve conflicts.
- When working with a linear history is important
- When working with a small team, rebasing can be used to keep the codebase up to date and resolve conflicts quickly and efficiently.
When to use Merging
Merging should be preferred over rebasing in the following scenarios:
- When working on a shared branch that is used by multiple team members, merging can be used to create a clear record of when the changes from one branch were integrated into another branch.
- When working on a long-term project where the commit history needs to be preserved, merging can be used to preserve the commits from both branches and create a non-linear history that can be useful for understanding the relationships between branches and the evolution of the codebase over time.
- When working with a large team merging can be used to create a clear record of when changes were made and by whom, making it easier for team members to understand the relationships between branches and the changes that have been made.
- When working on a project with a strict version control requirements, merging can be used to create a clear record of when changes were made and by whom, making it easier to comply with auditing and traceability requirements.
Merging and Rebasing with Local and Remote Branches
Merging and rebasing are not just use when you want to integrate changes from one branch to another, but also when you want to pull the latest changes from the remote repository, and merge it with your local branch, or rebase it with your local branch.
Imagine a scenario, two developers are working on one feature, first developer created a branch, and start working on it, and pushed it to the remote repository, and second developer pulled the latest commit from the remote repository, and committed some changes, and pushed it to the remote repository. Now, the first developer wants fetch the latest changes from the remote repository, and integrate it with own branch, and there are two ways to do it:
git pull origin feature-branchgit pull origin feature-branch --rebase
git pull origin feature-branch
It is combination of git fetch and git merge, which means it will fetch the latest changes from the remote repository and merge it with the local branch.
In this scenario, if the second developer committed some changes, and pushed it to the remote repository, and the first developer wants to pull the latest changes from the remote repository, and merge it with his branch, then it will create a merge commit, and the commit history will look like this:
* 2c0b9b1 (HEAD -> feature-branch) Merge branch 'feature-branch' of
|\
| * 1c0b9b1 (origin/feature-branch) second developer commit
* | 1b0b9b1 first developer commit
|/
* 0b0b9b1 initial commit
You can see that 2c0b9b1 is a merge commit, and it has two parents, 1c0b9b1 and 1b0b9b1, and 1c0b9b1 is the commit from the second developer, and 1b0b9b1 is the commit from the first developer. So, it is a non-linear commit history.
git pull origin feature-branch --rebase
It is combination of git fetch and git rebase, which means it will fetch the latest changes from the remote repository and rebase it with the local branch.
In this scenario, if the second developer committed some changes, and pushed it to the remote repository, and the first developer wants to pull the latest changes from the remote repository, and rebase it with his branch, then it will not create a merge commit, and the commit history will look like this:
* 2c0b9b1 (HEAD -> feature-branch) second developer commit
* 1b0b9b1 first developer commit
* 0b0b9b1 initial commit
You can see that 2c0b9b1 is not a merge commit, and it has only one parent, 1b0b9b1, and 1b0b9b1 is the commit from the first developer. So, it is a linear commit history.
In the first scenario, the commit history is non-linear, and in the second scenario, the commit history is linear. So, it depends on the project requirements, which one to use.
So, merging and rebasing are also used when you want to integrate the latest changes from remote repository to your local branch.
Conclusion
In this article, we learned about the differences between rebasing and merging in Git, and when to use each one. We also learned about the advantages and disadvantages of each approach, and how to use them in practice.
In general, it is recommended to strive for a linear history, because it is easier to figure out what happened than with a non-linear history. Non-linear history becomes exceedingly unreadable when a lot of branches forked off and merged across a longer commit range.
Solve this practice problem to test your knowledge of rebasing and merging in Git.