Git and GitLab Commit & Merge Convention
GitLab Work-Item
For GitLab a work-item is normally responsible for one or more branches. Each branch can have a merge request created or not using GitLab GUI.
Work on a Branch
A developer works locally on a branch and makes commits. Before a push, the commits are squashed and combined into one or commits.
Using Semantic Conventional Commits
All commits have semantic conventional commit messages. Squashing of commits is a delicate matter since commits can have changes which are maybe undone in another commit in the same squash due to development efforts taking a wrong turn and recovering from it. Those commits should be avoided since they can be confusing.
Semantic conventional commits are used to describe the changes made in a commit. They consist of a type, a scope, and a subject. The type describes the type of change made, the scope describes the area of the codebase affected, and the subject describes the change in more detail.
Merging a Branch with Squashing
When GitLab squashes all commits before a merge, all commit messages are lost. GitLab does not squash commit messages involved in the squashed commits. The one doing the manual merge must compose a new commit message for the squashed commits reflecting the changes made in the branch. The developer should also ensure that the branch is up-to-date with the latest changes in the main branch before merging. If there are any conflicts, the developer resolves them locally before merging.
Question: Is this the right way to merge a branch? This requires manual merging and manual commit message composition, tricky and error-prone and not to forget time-consuming.
Merging a Branch without Squashing
Before each push of the branch to GitLab the developer must be sure the commit message is or messages are correct.
This asks a lot of developers to form correct commit messages. Once the branch is pushed, there is no possibility to fix an erroneous commit message.
AI Analysis
Recommended Approaches for Processing and Merging GitLab Merge Requests
The "smartest" way to process and merge branches in GitLab depends on your team's priorities regarding git history clarity, developer cognitive load, and automated semantic release compatibility.
Below are the most effective workflows, structured by merge method and automated tooling.
1. The Industry Standard: Semi-Linear Merge with GitLab Squash & Merge
This approach provides the optimal balance between clean history and low developer overhead.
Workflow Overview
- During Development (Freedom to Iterate):
- Developers commit frequently on their feature branch without worrying about messy intermediate commits (e.g.,
fix typo,WIP,undo previous debug).
- Developers commit frequently on their feature branch without worrying about messy intermediate commits (e.g.,
- Merge Request Configuration:
- Merge Request Title: Follows Conventional Commits format (e.g.,
feat(core): add cross-platform toolchain runner). - MR Description: Contains bullet points or details explaining key changes and linking issue references (e.g.,
Closes #123).
- Merge Request Title: Follows Conventional Commits format (e.g.,
- Merge Action:
- Enable Squash commits when merging in GitLab project settings.
- GitLab automatically populates the squashed commit message from the MR Title (as the subject line) and the MR Description (as the commit body), or allows customizing it directly in the merge widget.
- Merge using Fast-forward merge or Merge commit with semi-linear history (
rebase before merge).
Advantages
- Clean Main History: Main branch contains a linear, 1-commit-per-MR history where each commit is atomic, buildable, and bisectable.
- Low Overhead: Developers don't waste time locally interactive-rebasing or squashing intermediate commits before every push.
- Tooling Friendly: Works seamlessly with tools like
semantic-releaseor standard changelog generators that parse conventional commit messages from themainbranch.
2. The Multi-Commit / Fine-Grained Workflow: Local Rebase with Semi-Linear Merge
Use this if a single Merge Request logically contains multiple atomic, distinct steps that each deserve their own entry in the project history (e.g., refactoring prerequisite followed by a feature addition).
Workflow Overview
- Local Clean-up (Interactive Rebase):
- Developer runs
git rebase -i mainlocally before marking the MR as ready. - Combine WIP commits, remove dead ends, and format each remaining commit with a Conventional Commit message.
- Developer runs
- Rebase on Target Branch:
- Update branch against target (
git rebase mainor click "Rebase" in GitLab).
- Update branch against target (
- Merge Action:
- Set project merge method to Fast-forward merge or Semi-linear history.
- Disable squashing for this specific MR so individual commits are preserved.
Advantages
- Preserves logical, step-by-step history for large architectural changes.
- Enables granular
git revertandgit bisectper individual commit.
3. Comparison of Merge Methods in GitLab
| Merge Method | History Cleanliness | Developer Effort | Traceability & Bisectability |
|---|---|---|---|
| Squash & Merge (Linear / Semi-linear) | ⭐⭐⭐⭐⭐ (Very high) | ⭐⭐⭐⭐⭐ (Low) | High (1 MR = 1 atomic commit) |
| Rebase & Merge (No squash, Fast-forward) | ⭐⭐⭐⭐ (High) | ⭐⭐⭐ (Requires local rebase hygiene) | Very High (Multiple fine-grained commits) |
| Standard Merge Commit (No squash, non-fast-forward) | ⭐⭐ (Messy branch topology) | ⭐⭐⭐⭐ (Low) | Moderate (Branch clutter, merge commit bubbles) |
4. Best-Practice GitLab Project Configuration
To enforce a streamlined and predictable merge process, configure these project settings under Settings > Merge requests:
- Merge method:
- Select Fast-forward merge or Merge commit with semi-linear history (forces branches to be up-to-date with target branch and tested before merging).
- Squash commits options:
- Set to Encourage or Require.
- Set default squash commit message template to
%{title}and%{description}in GitLab MR template settings.
- Merge checks:
- Enable Pipelines must succeed.
- Enable All threads must be resolved.
- Enable Skipped pipelines are considered successful (depending on CI rules).
- Automated Title / Commit Linting (CI):
- Add a lightweight CI job using
commitlint(or a regex check) that validates the MR title (when squashing) or individual commit messages (when not squashing) against the Conventional Commits specification.
- Add a lightweight CI job using
Summary Recommendation
- For 90% of feature/bugfix branches: Use Squash and Merge using the MR title for the conventional commit
header. This offloads commit-squashing complexity from developers while guaranteeing a clean
mainbranch. - For complex, multi-stage refactoring: Perform a local
git rebase -ito craft clean atomic commits, then merge without squashing using Semi-linear history.
Preservation of Review Discussions and Code Context During Squashing
Are discussions and code links lost when squashing?
No, discussions and their links to code are not lost.
In GitLab, all code review comments, threads, and discussions are stored at the Merge Request level in GitLab's database, completely independent of whether the underlying Git commits are squashed when merged into the target branch.
How GitLab Preserves Discussions and Code Context
- Diff Versioning and Snapshots:
- Every time developers push new commits to an MR branch, GitLab creates and stores a new diff version.
- When a reviewer comments on a line of code, that discussion is attached to the specific diff version active at that moment.
- If a subsequent commit alters or reverts that code, GitLab flags the discussion as an "Outdated diff" or collapses it in the MR overview, but the original code snippet and the entire conversation thread remain intact and readable.
- Source Branch Deletion Does Not Delete MR History:
- Even if you enable "Delete source branch" upon merge, GitLab retains the entire Merge Request record.
- You can navigate back to the merged MR at any time to inspect:
- All comment threads and resolution status.
- The exact code diff at the time each comment was written.
- The historical progression across different pushes via the Changes > Compare versions dropdown.
- Traceability Between
git logand GitLab Discussions:- When GitLab performs a squash-and-merge, the squashed commit on the target branch (e.g.,
main) is linked to the Merge Request (e.g.,See merge request !123or referenced via the commit UI). - Anyone viewing
git logorgit blameonmaincan navigate directly to the MR to see all original debates, discarded alternatives, and code reviews.
- When GitLab performs a squash-and-merge, the squashed commit on the target branch (e.g.,
Summary of What Is Kept vs. What Is Squashed
| Element | After Squash & Merge | Where to Find It |
|---|---|---|
Git Commit History on main |
Squashed into a single clean commit | git log / repository branch history |
| Intermediate Git Commits | Discarded from Git branch history | Replaced by the squashed commit |
| Review Comments & Discussions | Preserved completely | GitLab Merge Request Overview tab |
| Code snippets tied to comments | Preserved as historical/outdated diffs | GitLab Merge Request Overview & Changes tabs |
| Diff History between iterations | Preserved across all push versions | GitLab MR Changes tab version selector |
Convention on Git and GitLab Merge-Requests
GitLab Work-Item
For GitLab a work-item is normally responsible for one or more (feature-)branches. Each branch can have a merge-request or not created using the GitLab GUI/Webapp.
Semantic Conventional Commits
Essentially, all commits have semantic conventional commit messages.
Semantic conventional commits are used to describe the changes made in a commit. They consist of a type, a scope, and a subject. The type describes the type of change made, the scope describes the area of the codebase affected, and the subject describes the change in more detail.
Work on a Branch
A developer works locally on a branch and makes regular commits. Intermediate pushes of commits to a feature branch are recommended to enable co-workers to review and provide feedback. Before a push to GitLab, some commits can be combined (squashed) to simplify the timeline.
Merge of a Merge Requests
Note: the branch used for releases is here named
mainand the feature branch is namedfeaturefor the purpose of this example.
A merge of a GitLab merge-request into the main or release branch is prepared locally and needs the following steps:
- When not available yet, 'checkout' the feature branch.
git checkout feature
- When available, check if the tree is clean and up to date
git statusgit pull(get any changes from the remote)
- Get the latest changes from the main branch.
git fetch origin main
- Update the feature branch with changes made in the main branch.
- Option A: with a rebase onto the release branch.
Only when feature changes sofar are minor and no conflicts are expected.
git rebase origin/maingit push origin feature --force-with-lease
- Option B: with a merge of main into the feature branch.
Preferred when feature changes so far are major and others are working on it as well.
git merge origin/main- Resolve any conflicts that may arise during the merge.
git add <resolved-files>git commit -m "chore(git): merge main into feature branch"
- Option A: with a rebase onto the release branch.
Only when feature changes sofar are minor and no conflicts are expected.
- Squash the commits before a merge of a merge-request on GitLab.
- Back up the feature branch to save commit messages.
git branch feature-branch-backup
- Create a Markdown file to save the commit messages for the commit later.
git log origin/main..HEAD --pretty=format:"## Commit: %h%n### From%n- Author: %an <%ae>%n- Date: %ai%n### Message%n%n%B%n---" > commit-messages.mdgit log origin/main..feature-branch-backup --pretty=format:"## Commit: %h%n### From%n- Author: %an <%ae>%n- Date: %ai%n### Message%n%n%B%n---" > commit-messages.md
- Soft-reset the feature branch to 'origin/main'.
This aligns your branch pointer with the 'main' branch while keeping all your uncommitted code modifications staged.
git reset --soft origin/main
- Commit all changes made in the feature branch using a commit message based on the
commit-messages.mdfile.git commit -m "feat(scope): summary of changes"
- Back up the feature branch to save commit messages.
- Check if the feature merge with the 'main' branch is possible.
When only a hash is returned - exit-code is zero - the merge is possible.
git merge-tree --write-tree origin/main feature
- Push the feature branch to the remote repository on GitLab.
git push origin feature --force-with-lease
- Merge the feature branch into the release branch on GitLab.
- The commit message for the merge-request can be the same as the commit message used for the squash commit.
- The commit message footer references the GitLab work-item(s) and the Authors who are responsible for the changes.
Note: The semantic merge commit messages determine the next version for a release based on the selected merge-commit. A product owner selects the merge-commit to be released and automatically determines the version number of the release. Add the
commit-messages.mdfile to the.gitignorefile. Just incase.