This article covers how git merge
works with GitHub pull requests. We will focus on the use case where developers want
to keep their feature branches updated with the main branch. After completing the feature work, developers create a pull
request to merge their feature branch into the main branch.
What is a merge in version control?
Git is a distributed version control system that allows multiple developers to work on the same codebase. When developers work on different branches, they must merge their changes into the main branch. A merge is the process of combining changes from one branch into another branch, resulting in a single branch that contains the changes from both branches.
git merge
The standard git merge
command takes each commit from one branch and applies it to another. The final commit has two
parent commits: one from the current branch and one from the merged branch.
In the following example, we have a branch
that we want to merge into the main
branch:
The git log
of the main
branch shows the commit history:
commit e493ac8fea4e0efe125a561b9014313bec41a489 (HEAD -> main, origin/main)
Author: Victor on Software <>
Date: Sat Jun 22 17:31:12 2024 -0500
m3
commit b79e810cb86405061dc979ce4fc05fe36a724256
Author: Victor on Software <>
Date: Sat Jun 22 17:29:41 2024 -0500
m2
commit eaccad9476b472dbfb3cdfbd17088425be75b7b1
Author: Victor on Software <>
Date: Sat Jun 22 17:26:55 2024 -0500
m1
commit 4265c0a30b7b6f03d93331bc112261393c97ee1d
Author: Victor on Software <>
Date: Sat Jun 22 17:25:40 2024 -0500
first commit
And the git log
of the branch
shows the commit history:
commit 2afb078875a84095327ab2ef7c83711534c5eef8 (HEAD -> branch, origin/branch)
Author: Victor on Software <>
Date: Sat Jun 22 17:30:20 2024 -0500
b2
commit 9fc53c2ca9a58637a5d433de4c6150b832d4d275
Author: Victor on Software <>
Date: Sat Jun 22 17:28:25 2024 -0500
b1
commit eaccad9476b472dbfb3cdfbd17088425be75b7b1
Author: Victor on Software <>
Date: Sat Jun 22 17:26:55 2024 -0500
m1
commit 4265c0a30b7b6f03d93331bc112261393c97ee1d
Author: Victor on Software <>
Date: Sat Jun 22 17:25:40 2024 -0500
first commit
We merge the branch
into the main
branch:
git checkout main
git merge branch
The resulting commit history shows all the commits from both branched as well as the final empty merge commit pointing
to the two parent commits: e493ac8 2afb078
:
commit 09d569bd079162643462dde112246f4167f14889 (HEAD -> main)
Merge: e493ac8 2afb078
Author: Victor on Software <>
Date: Sat Jun 22 21:03:01 2024 -0500
Merge branch 'branch'
commit e493ac8fea4e0efe125a561b9014313bec41a489 (origin/main)
Author: Victor on Software <>
Date: Sat Jun 22 17:31:12 2024 -0500
m3
commit 2afb078875a84095327ab2ef7c83711534c5eef8 (origin/branch, branch)
Author: Victor on Software <>
Date: Sat Jun 22 17:30:20 2024 -0500
b2
commit b79e810cb86405061dc979ce4fc05fe36a724256
Author: Victor on Software <>
Date: Sat Jun 22 17:29:41 2024 -0500
m2
commit 9fc53c2ca9a58637a5d433de4c6150b832d4d275
Author: Victor on Software <>
Date: Sat Jun 22 17:28:25 2024 -0500
b1
commit eaccad9476b472dbfb3cdfbd17088425be75b7b1
Author: Victor on Software <>
Date: Sat Jun 22 17:26:55 2024 -0500
m1
commit 4265c0a30b7b6f03d93331bc112261393c97ee1d
Author: Victor on Software <>
Date: Sat Jun 22 17:25:40 2024 -0500
first commit
The result would be the same if instead we merged the main
branch into the branch
branch:
git checkout branch
git merge main
except the final merge commit would be slightly different:
commit 06713a38ed38c12f599c6e810ee50d4cacfe2de7 (HEAD -> branch)
Merge: 2afb078 e493ac8
Author: Victor on Software <>
Date: Sat Jun 22 17:32:01 2024 -0500
Merge branch 'main' into branch
The merged changes on branch
can be pushed to the remote repository without issues because the remote branch can be
fast-forwarded to the new commit.
git push origin branch
What is a fast-forward merge?
A fast-forward merge is a merge where the base branch (target branch) has no new commits. In this case, git moves the target branch to the commit of the source branch. It is a fast-forward merge because the target branch is moved forward to the new commit.
A fast-forward merge does not lose any history – it is always possible to undo a fast-forward merge.
git push
does not, by default, allow a merge that is not a fast-forward. Use the’- force’ option to enable a merge
that is not a fast-forward.
Undo a git merge
The above merge can be undone by resetting the branch to the commit before the merge, which is one of the parent commits of the merge commit.
This command resets the branch
to the commit before the merge:
git reset --hard 2afb078875a84095327ab2ef7c83711534c5eef8
git rebase
Another way to combine changes from one branch into another is to use git rebase
. This command applies the changes
from the source branch to the target branch by reapplying the commits from the source branch to the target branch.
The git rebase
command will modify the commit history of the source branch. In our pull request examples, we will use
git merge
instead of git rebase
to preserve all the commit histories.
Pull request after a merge
When working on a feature branch, developers often want to update their branch with the latest changes from the main to
make sure their feature works with the newest code. We start this process with the above-described git merge
command,
where we merge the main
branch into the branch
branch.
After the merge, the developer can create a GitHub pull request to merge the branch
into the main
branch.
Note that the commit history only shows the commits from the branch
and the merge commit. The main
commits are not
shown in the pull request.
GitHub shows a few options for merging the pull request:
- Create a merge commit: This option creates a new merge commit combining the changes from the
branch
and themain
branches. This is the default option. - Squash and merge: This option combines all the commits from the
branch
into a single commit and merges that commit into themain
branch. - Rebase and merge: This option applies the changes from the
branch
onto themain
branch by rebasing the commits from thebranch
onto themain
branch.
Selecting Create a merge commit results in the following commit history:
The last two commits are both merge commits.
commit 1eb0af8c0e9ad16a0267d8abd1ce667f125ab7e8 (HEAD -> main, origin/main)
Merge: e493ac8 22d2107
Author: Victor Lyuboslavsky <******>
Date: Sun Jun 23 07:35:57 2024 -0500
Merge pull request #1 from getvictor/branch
My pull request
commit 22d2107b49bca56e67b7d4e800d93f93378a0956 (origin/branch, branch)
Merge: 2afb078 e493ac8
Author: Victor on Software <>
Date: Sat Jun 22 21:25:39 2024 -0500
Merge branch 'main' into branch
The PR merge commit points to the previous merge commit and the last commit on main
.
Updating a protected feature branch
We have two branches in this example: main
and feature
. Both branches are protected, meaning that changes to them
must be made through a pull request. We want to update the feature
branch with the latest changes from the main
branch.
We can do this by merging the main
branch into the feature
branch, creating a new branch, and creating a pull
request.
git checkout feature
git merge main
git checkout -b feature-update
git push origin feature-update
And create a pull request to merge feature-update
into feature
.
This pull request shows all the commits from the main
branch and the merge commit. This commit history is problematic
because the PR may trigger a code review from the
code owners
of the files that were already reviewed in previous pull requests to the main
branch.
After the merge, the feature
branch commit history looks like:
Now, we create a pull request to merge the update feature
branch into the main
branch.
After the merge, the main
branch commit history looks like:
The last three commits are merge commits.
commit 59caaf1cc5103099f850c32f1729c5ffe3525404 (HEAD -> main, origin/main)
Merge: e493ac8 dcbc117
Author: Victor Lyuboslavsky <******>
Date: Sun Jun 23 08:56:53 2024 -0500
Merge pull request #3 from getvictor/feature
Feature
commit dcbc117e5811e683f1074d947bc25da21b5fa5f6 (origin/feature)
Merge: 2afb078 373dd82
Author: Victor Lyuboslavsky <******>
Date: Sun Jun 23 08:45:44 2024 -0500
Merge pull request #2 from getvictor/feature-update
Feature update
commit 373dd82672a4879bfcf3b29c4feb97004359adfe (origin/feature-update, feature-update, feature)
Merge: 2afb078 e493ac8
Author: Victor on Software <>
Date: Sun Jun 23 08:03:22 2024 -0500
Merge branch 'main' into feature
Merging a pull request with Squash and merge
If the final pull request is merged with Squash and merge, the commit history will look like:
The last commit is a single commit that combines all the changes from the feature
branch. The merge commits and all
other commits are eliminated.
The downside of Squash and merge is that the commit history is lost. The commit history is useful for debugging, understanding the changes made, and keeping ownership of the changes when multiple developers work on the same feature branch.
Further reading
Recently, we covered why GitHub code review process is broken for our organization.
Previously, we explained how to find the minimum required code owner approvers for a pull request.
Watch how git merge works with GitHub pull requests
Note: If you want to comment on this article, please do so on the YouTube video.