GitLab Merge Requests for Infra Changes

Creating MRs, reviewing infra diffs, approval rules, responding to comments, and merging safely.

Why MRs matter for infrastructure

For infrastructure code, the MR is not just a review nicety — it is a safety control. A bad Ansible change can take down a service or misconfigure dozens of machines. The MR gives:

Creating an MR

After pushing your branch:

  1. Go to the project in GitLab
  2. Click Merge Requests → New merge request
  3. Select your source branch and main as the target
  4. Fill in the title and description
  5. Assign a reviewer
  6. Click Create merge request

GitLab often shows a banner on the project page when you push: "Create merge request for branch/name" — click that for a shortcut.

# After pushing, GitLab shows a direct link in the terminal output:
git push -u origin feature/add-ntp-server
# To create a merge request for feature/add-ntp-server, visit:
#   https://gitlab.example.com/infra/repo/-/merge_requests/new?merge_request[source_branch]=feature/add-ntp-server

Writing a useful MR description

A good MR description for an infrastructure change answers:

## What
Add internal NTP server to chrony configuration for all production hosts.

## Why
Ticket INF-2431 — external NTP access is being blocked by firewall policy.

## Affected hosts
All hosts in inventories/production/ (via group_vars/all.yml)

## Testing
Ran `ansible-playbook site.yml --check --diff -i inventories/production/hosts.ini`
Output shows only chrony.conf changing — diff attached to ticket.

## Rollback
Revert this MR commit. chrony will sync from external servers again on next run.

Reviewing an infra diff

When reviewing someone else's Ansible MR, look at:

Check which files changed

Use the Changes tab in the MR. The file list tells you the scope of the change before you read any code.

YAML / variable file changes

Task file changes

Template changes

Checking pipeline status in an MR

The pipeline status is shown in the MR view — a green tick (passed), orange circle (running), or red X (failed).

Click the pipeline status to see individual job results. Click a failed job to read its log.

If the pipeline is failing on a lint rule you disagree with:

# Skip a specific rule on a task
- name: Run legacy script that we cannot replace yet
  ansible.builtin.shell: /opt/legacy/setup.sh
  # noqa: command-instead-of-shell

If the pipeline passes but you are not confident in the dry-run result, ask the author to paste the --diff output in the MR description.

Approval rules

Protected branches can require approvals before merge. Common setups:

If your MR is blocked on approvals, the MR page shows "Approvals: 0/1". Ask the designated reviewer to approve. You cannot approve your own MR.

Responding to review comments

When a reviewer leaves a comment requesting a change:

  1. Make the change in your local branch
  2. Add and commit: git add . && git commit -m "Address review: fix variable name"
  3. Push: git push — the MR updates automatically
  4. Reply to the comment in GitLab explaining what you changed, or resolve it if the reviewer left instructions you followed exactly
Do not force-push during review. It makes it hard for the reviewer to see what changed since their last look. Add commits instead — you can squash them before or after merge.

Squash commits

If you made many small commits during development ("fix typo", "another fix", "address review"), you can squash them into one clean commit at merge time.

Enable Squash commits in the merge dialog. GitLab combines all commits in your branch into one commit on main. The commit message is the MR title by default — edit it to something descriptive.

A squashed commit makes git log on main readable: one line per feature/fix, not a trail of work-in-progress commits.

Merging — what happens next

When all of these are satisfied:

Click Merge. GitLab merges your branch into main and (if configured) triggers a new pipeline on main — which may automatically deploy.

If a deploy pipeline runs after merge, watch it. If it fails, you need to act quickly — main now has code that does not deploy cleanly.

When an MR is stuck

# Bring your branch up to date with main
git fetch origin
git rebase origin/main

# If conflicts during rebase
git status             # shows conflicting files
# edit files, resolve markers
git add resolved-file.yml
git rebase --continue

# Push the rebased branch (requires force)
git push --force-with-lease

--force-with-lease is safer than --force — it fails if someone else has pushed to your branch since you last pulled, preventing accidental overwrite.

Create MR from git push

You can open a GitLab MR directly from the git push command using push options (-o). GitLab prints the MR URL in the push output.

# Push branch and create an MR targeting main
git push -o merge_request.create origin feature/add-firewalld-rules

# Set a specific target branch
git push \
  -o merge_request.create \
  -o merge_request.target=main \
  origin feature/add-firewalld-rules

# Set MR title at push time
git push \
  -o merge_request.create \
  -o merge_request.target=main \
  -o merge_request.title="Add firewalld rules for web tier" \
  origin feature/add-firewalld-rules

# Assign to a reviewer immediately
git push \
  -o merge_request.create \
  -o merge_request.assign=alice \
  origin feature/add-firewalld-rules

GitLab outputs something like: remote: View merge request for feature/add-firewalld-rules: https://gitlab.internal/infra/config/-/merge_requests/42 — you can share this URL immediately without opening a browser.

Draft MRs

A Draft MR signals that the work is in progress and must not be merged yet. GitLab blocks the merge button until the Draft status is removed.

Create a Draft MR

# Method 1: Add "Draft:" prefix to the MR title in the UI

# Method 2: Create as Draft via push option
git push \
  -o merge_request.create \
  -o merge_request.title="Draft: Refactor nginx upstream config" \
  origin feature/nginx-upstream-refactor

When to use Draft MRs

Removing Draft status

In the GitLab UI, click Mark as ready at the top of the MR page. This removes the Draft prefix and enables the merge button (subject to any approval rules).

Best practice: Always open MRs as Draft when your branch is freshly pushed. This prevents a teammate from merging before you've had a chance to review the pipeline results and add a proper description.

CODEOWNERS file

A CODEOWNERS file in your repo root (or .gitlab/) automatically assigns reviewers based on which files are changed in an MR. This ensures the right team is always looped in for critical paths.

File location

# One of these locations (GitLab checks in order):
CODEOWNERS
docs/CODEOWNERS
.gitlab/CODEOWNERS

Syntax

# Pattern                                   Owner(s)
# ──────────────────────────────────────────────────────────────
# All files (catch-all)
*                                            @team-infra

# Specific directory — all files inside
/ansible/roles/                              @team-infra

# Specific file type in any directory
*.tf                                         @team-terraform

# Files in a specific directory by extension
/ansible/group_vars/*.yml                    @team-infra @team-security

# A specific file
/ansible/inventories/production/hosts.ini   @team-infra-leads

# Group syntax (configure groups in GitLab Admin → Groups)
/deploy/                                     @infra/senior-ops

How CODEOWNERS interacts with approval rules

In GitLab, enable Settings → Merge requests → Code Owners → Require approval from code owners to make CODEOWNERS approval mandatory before merge. Without this setting, CODEOWNERS only adds reviewers automatically without blocking the merge.