mancuoj

mancuoj

Better late than never.
github
twitter

Three Strategies for Merging PRs on GitHub

Generally speaking, we have a common workflow when creating a PR:

  1. Create a branch, such as feature
  2. Submit the PR
  3. Have someone review it
  4. Make changes based on the review comments
  5. Merge the PR into the main branch

However, the feature branch may have multiple commits, so how to merge it into the main branch becomes a problem. GitHub provides three options:

image

Create a merge commit#

Create a "merge commit" that connects the history of two branches

image

We can simulate this process locally. First, create three commits in the main branch. You can see the commit history using git log --oneline --graph:

* b8472e4 (HEAD -> main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

Then, create two commits in the new branch git checkout -b feature:

* 9770b82 (HEAD -> feature) add e.txt
* 4e65cc4 add d.txt
* b8472e4 (main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

Switch back to the main branch and merge using the command:

git checkout main
git merge --no-ff feature

View the records again:

*   7da24fd (HEAD -> main) Merge branch 'feature' into main
|\  
| * 9770b82 (feature) add e.txt
| * 4e65cc4 add d.txt
|/  
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

As you can see, GitHub uses the default option git merge --no-ff behind the scenes to create a merge commit that connects the history of the two branches.

The advantage of this method is that it preserves all the commit history and individual time points, but the disadvantage is that it can become messy with multiple or long-term branches.

Squash and merge#

Replace all commits in the branch with a single commit before merging

image

Let's simulate it locally again. First, git reset --hard b8472e4 to go back before the merge:

* b8472e4 (HEAD -> main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

Use git merge --squash feature to squash the commits, then write an appropriate commit message.

Finally, the commit history in the main branch is as follows:

* 52c548d (HEAD -> main) add d.txt and e.txt
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

The advantage of this method is that the commit history in the main branch is clear and linear, with one commit solving one feature. However, the disadvantage is that it loses the context information of the merge and individual commits, and it is even impossible to know whether this branch was merged or not.

Another method of Squash#

Use the interactive rebase command for more detailed operations:

git checkout feature
git rebase -i main

Choose squash to merge the subsequent commits into the previous commit:

pick 4e65cc4 add d.txt
squash 9770b82 add e.txt

Write an appropriate commit message. At this point, the commit history in the feature branch becomes:

* f37e60f (HEAD -> feature) add d.txt and e.txt
* b8472e4 (main) add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

Finally, go back to the main branch and merge (of course, you can also use cherry-pick to merge individual or combined commits):

git checkout main
git merge feature

Bonus#

How to recover from these operations that mess up the commit history?

git reflog
# You will see all the git operations you have done, find the previous reset step that messed up and reset to it
git reset HEAD@{index}

Rebase and merge#

All commits will be added and merged to the "top" of the main branch

No image, but you can understand from the description that the final result in the main branch should be:

image

Restore the branch status and then perform the following operations:

git checkout feature
git rebase main

git checkout main
git merge feature

After completion, the commit history in the main branch is as follows:

* 9770b82 (HEAD -> main, feature) add e.txt
* 4e65cc4 add d.txt
* b8472e4 add c.txt
* 2db7159 add b.txt
* b5b52d8 add a.txt

The advantage is that the commit history in the main branch is clear and linear, without unnecessary merge commits, and the commit history of other branches will be directly saved in the main branch records.

The disadvantage is similar to squash merge, and more small commits may affect the attention to the big picture.

Recommendation#

All choices are trade-offs, and you should consider these key factors:

  • Team preference
  • Commit history: complete or clear?
  • Smaller commits or complete feature commits
  • Long-term running branches or not
  • ...

I have only one recommendation: make more commits, write more PRs, don't pile up code, smaller code snippets mean easier code review, simpler unit testing, and code that adheres to the single responsibility principle.

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.