git merge command: conflict resolve, rebase squash differences, merge strategies
In this article we'll cover the git merge
method. From how to merge to resolving conflicts, merge commit features, merge strategies, differences from rebase and squash, and other options for the git merge
command.
1. How to merge a branch
The git merge
command is used to merge one branch into another.
In the git branch post I mentioned that the branch name is the last commit on that branch.
By the same token, merging branches ends up creating a new commit that has all the changes from the last commit of both branches,
You can also think of it as a new commit to the working branch where you ran the git merge
command.
The git merge
command always performs the merge commit in the branch where it is created.
For example, if you were merging the feature-a
branch from the main
branch, you would use a command like this
$ git switch main
switched to branch 'main'
$ git merge feature-a
Merge made by the 'ort' strategy
In the example above, you can see that we used the merge-ort
strategy, which is Git's latest default merge strategy, changed in 2022.
1.1. ort strategy
Before moving on to the next section, let's take a quick look at Git's newest merge algorithm, the ort
strategy.
The ort
stands for "Ostensibly Recursive's Twin", meaning that it was created to replace the old default algorithm, the recursive
strategy.
Compared to the recursive
strategy, it is said to reduce conflicts and take care of renamed files.
It is basically based on the 3-way merge algorithm, which compares changes from three sources: the same ancestor commit from both branches, the most recent commit from both branches, and the most recent commit from both branches, and then performs a
merge. When merging two branches, we will mainly use the ort
strategy, because it performs better than the previously used resolve
and recursive
strategies.
Other strategies worth knowing are the octopus
strategy for merging two or more branches, or the subtree
strategy for merging into a subdirectory.
For now, let's just get familiar with the names.
These strategies can be specified with the -s
or --strategy
option, as follows.
$ git merge -s recursive feature-a
2. How to resolve a merge conflict
A merge conflict occurs when the same line in the same file on two different branches has different content. To preserve data, Git will never arbitrarily delete something unless you give it an option in the strategy. Instead, it will pause the merge and give us a choice.
Let's see this in action.
First, we modified the 1.md
file with different content in the main
and develop
branches.
Then, when we go to the main
branch and try to merge the develop
branch, we get a merge conflict.
If you're new to the -a
option of the git commit
command, see git commit post.
At this point, Git will add symbols like <<<<<<<
HEAD
, =======
, >>>>>>>
branch-name
to the contents of the conflicting files to let you know exactly what needs to be fixed.
Let's open the 1.md
file.
This is the 1.md
file opened in the Neovim editor. We just need to keep what we want in this part, remove the symbols Git added, and proceed with the commit.
Merging is even easier in an editor that supports Git, like VSCode. The highlighting is intuitive, and pressing the button for the four sentences above <<<<<<< HEAD
will automatically remove the excluded content and Git's symbols.
After modifying the content, complete the merge with the git commit
command like a normal commit.
3. Difference from rebase
The git merge
and git rebase
commands have something in common: they are both ways of merging two branches and resolving merge conflicts.
However, there are differences in the way they work, and it's important to understand them so you can choose the right one for your needs.
The git merge
command creates a new merge commit with the last commit of the two branches as its parent, as shown above.
As a result, there are commits in the commit history that correspond to two branches together.
The feature-a
, feature-b
, etc. paths come and go on the main path, called the main
branch.
The git rebase
command merges the two branches so that there are no dead ends in the commit history.
How this works is easier than you might think. Let's take a look at the image below.
Assuming you want to merge the feature-a
branch into the main
branch, which diverged from a common ancestor commit A
, the git rebase
command creates a new commit with the same changes as the commit in feature-a
.
In the figure, these are commits C
and D
.
These two commits are then appended to B
, the last commit on the main
branch.
This creates a single commit history, but with the result that all changes in the feature-a
branch can be merged into the main
branch.
The history is also simpler, since we don't have to create a new merge commit.
However, it's not easy to tell at a glance whether commits C
and D
are merged commits from different branches, or commits that were previously in the main
branch.
So from a history perspective, you're losing information.
For this reason, you should use merge and rebase in appropriate situations, depending on the direction your team is taking.
For more information on the git rebase
command, see this post.
4. Difference from squash
Squash is a concept in Git that compresses multiple commits into one.
It does not exist as a separate command, but can be used as an option to the merge or rebase commands.
When the git merge
command is used with the --squash
option, it is called a squash merge.
Let's look at the image below to understand squash merge.
In the current commit 8c12
, the main
and develop
branches are split.
I tried a squash merge on the main
branch, and it went through without a hitch.
The result of the git status
command below shows that all commits from the develop
branch have been staged on the main
branch.
If you check the history since the commit, you'll see that the develop
branch remains unmerged, but its changes have been registered as new commits in main
.
This is what a squash merge does.
The squash
option can also be used during the rebase process, with the difference that the history of the develop
branch is not left as above.
We'll cover this again in the rebase post.
5. Final thoughts
I think merge conflicts are one of the first things Git beginners get stuck on. However, merging and merge conflict resolution are at the core of Git collaboration, so it's important to understand and master them. I hope this post helps you get started.
