gitgitlab

What does GitLab actually do under the hood while creating a merge commit?


I'm trying to understand how GitLab makes merge commit. Pure git has different behaviour and it's not so clear why merge commits are required in GitLab

Let's say we have empty repository and perform first commit named direct commit to branch. Then we create a branch b-4 with two commits. Let's merge b-4 branch into main.

Note: keep in mind we use Merge commit method with squash option. Read more at Merge methods.

After merge is done we see the following history in GitLab web-interface (not a screenshot, sorry):

Merge branch 'b-4' into 'main'  d08bf37f
Squashed commit                 ebc509c2
direct commit to branch         34b29e2b

I expect to see something like that in git tree when I run git log --graph --decorate command:

*   commit d08bf37ff7157d48354c001ac066a6a44aece33e (HEAD -> main, origin/main)
|   Author: author_name <author_name@example.com>
|   Date:   Fri Jun 14 16:30:27 2024 +0300
|  
|       Merge branch 'b-4' into 'main'
|  
|       Squashed commit
|  
|       See merge request author_name/repo_name!1
|  
*   commit ebc509c25c945b63fbc3f387bb308a54b1006e90
|   Author: author_name <author_name@example.com>
|   Date:   Fri Jun 14 16:30:27 2024 +0300
|
|       Squashed commit
|
*   commit 34b29e2b89d38439299feef0ac64b926ba9d5d38
    Author: author_name <author_name@example.com>
    Date:   Fri Jun 14 16:29:09 2024 +0300
   
        direct commit to branch

But actually I observe the following:

*   commit d08bf37ff7157d48354c001ac066a6a44aece33e (HEAD -> main, origin/main)
|\  Merge: 34b29e2 ebc509c
| | Author: author_name <author_name@example.com>
| | Date:   Fri Jun 14 16:30:27 2024 +0300
| |
| |     Merge branch 'b-4' into 'main'
| |
| |     Squashed commit
| |
| |     See merge request author_name/repo_name!1
| |
| * commit ebc509c25c945b63fbc3f387bb308a54b1006e90
|/  Author: author_name <author_name@example.com>
|   Date:   Fri Jun 14 16:30:27 2024 +0300
|
|       Squashed commit
|
*   commit 34b29e2b89d38439299feef0ac64b926ba9d5d38
    Author: author_name <author_name@example.com>
    Date:   Fri Jun 14 16:29:09 2024 +0300
   
        direct commit to branch

Article How does GitLab merge request with squash work under the hood? doesn't answer my question.


Solution

  • Creating a squash commit, and then another merge commit, barely makes sense, because the merge commit adds no informational value to the resulting graph. One possible way it does make sense though is for continuity with having the Merge Commit contain the MR number and branch names information, and presumably this is why GitLab offers this option.

    In order for this to work, the merge commit, by definition, must have two parents. So the actual graph that you see in your repository is correct. I suspect the only reason you expected to see a linear graph is because the GitLab documentation is currently wrong. In this image the right most commit cannot be a merge commit:

    enter image description here

    I assume if someone notifies GitLab of the error they will fix it so that the resulting image would show the merge commit with two parents, E and the squash commit. While they're fixing that, they might as well fix the sentence to clearly state this is the Merge commit option, combined with Squash commit.

    Regarding how the squash and merge commit works behind the scenes, it's probably something like this:

    # create a temp branch called feature-squash starting from main:
    git switch -c feature-squash main
    
    # Begin squash merge of the feature branch:
    git merge feature --squash
    
    # Complete squash merge of the feature branch
    git commit -m "Squash feature branch into a single commit"
    
    # Checkout main:
    git switch main
    
    # Force a merge commit on main for the squashed commit:
    git merge feature-squash --no-ff -m "Force merge commit for Squash Merge"
    

    Side Note: When squashing, you could choose the alternative method of Squash Merge, and then you simply get the single linear squashed commit, without the extra (unnecessary except maybe commit message meta-data) merge commit along with it. However, when you are not squashing and you have more than 1 commit, I personally recommend always forcing a merge commit even if fast-forward is possible, so that the entire history of that feature can be discerned from the commit history.