glab mr
Create, view, and manage GitLab merge requests.
Quick start
# Create MR from current branch
glab mr create --fill
# List my MRs
glab mr list --assignee=@me
# Review an MR
glab mr checkout 123
glab mr diff
glab mr approve
# Merge an MR
glab mr merge 123 --when-pipeline-succeeds --remove-source-branch
Common workflows
Creating MRs
From current branch:
glab mr create --fill --label bugfix --assignee @reviewer
From issue:
glab mr for 456 # Creates MR linked to issue #456
Draft MR:
glab mr create --draft --title "WIP: Feature X"
Review workflow
List pending reviews:
glab mr list --reviewer=@me --state=openedCheckout and test:
glab mr checkout 123 npm testLeave feedback:
glab mr note 123 -m "Looks good, one question about the cache logic" # Resolve a discussion thread while adding a note (v1.88.0+) glab mr note 123 --resolve <discussion-id> -m "Fixed, addressed in latest commit." # Reopen a resolved thread glab mr note 123 --unresolve <discussion-id>Approve:
glab mr approve 123
Automated review workflow:
For repetitive review tasks, use the automation script:
scripts/mr-review-workflow.sh 123
scripts/mr-review-workflow.sh 123 "pnpm test"
This automatically: checks out → runs tests → posts result → approves if passed.
Merge strategies
Auto-merge when pipeline passes:
glab mr merge 123 --when-pipeline-succeeds --remove-source-branch
Squash commits:
glab mr merge 123 --squash
Rebase before merge:
glab mr rebase 123
glab mr merge 123
Troubleshooting
Merge conflicts:
- Checkout MR:
glab mr checkout 123 - Resolve conflicts manually in your editor
- Commit resolution:
git add . && git commit - Push:
git push
Cannot approve MR:
- Check if you're the author (can't self-approve in most configs)
- Verify permissions:
glab mr approvers 123 - Ensure MR is not in draft state
Pipeline required but not running:
- Check
.gitlab-ci.ymlexists in branch - Verify CI/CD is enabled for project
- Trigger manually:
glab ci run
"MR already exists" error:
- List existing MRs from branch:
glab mr list --source-branch <branch> - Close old MR if obsolete:
glab mr close <id> - Or update existing:
glab mr update <id> --title "New title"
Related Skills
Working with issues:
- See
glab-issuefor creating/managing issues - Use
glab mr for <issue-id>to create MR linked to issue - Script:
scripts/create-mr-from-issue.shautomates branch + MR creation
CI/CD integration:
- See
glab-cifor pipeline status before merging - Use
glab mr merge --when-pipeline-succeedsfor auto-merge
Automation:
- Script:
scripts/mr-review-workflow.shfor automated review + test workflow
Posting Inline Comments on MR Diffs
The glab api --field Problem
glab api --field position[new_line]=N silently falls back to a general (non-inline) comment
when GitLab rejects the position data. This happens with:
- Entirely new files (
new_file: truein the diff) - Files with complex/encoded paths
- Any nested position field that doesn't survive form encoding
There is no error — GitLab just drops the position and creates a general discussion. You won't know
it failed unless you check the returned note's position field.
The Fix: Always Use JSON Body
Post inline comments via the REST API with a Content-Type: application/json body:
import json, urllib.request, urllib.parse, subprocess
# Get token from glab config
token = subprocess.run(
["glab", "config", "get", "token", "--host", "gitlab.com"],
capture_output=True, text=True
).stdout.strip()
project = urllib.parse.quote("mygroup/myproject", safe="")
mr_iid = 42
# Always fetch fresh SHAs — never use cached values
r = urllib.request.urlopen(urllib.request.Request(
f"https://gitlab.com/api/v4/projects/{project}/merge_requests/{mr_iid}/versions",
headers={"PRIVATE-TOKEN": token}
))
v = json.loads(r.read())[0]
payload = {
"body": "Your comment here",
"position": {
"base_sha": v["base_commit_sha"],
"start_sha": v["start_commit_sha"],
"head_sha": v["head_commit_sha"],
"position_type": "text",
"new_path": "src/utils/helpers.ts",
"new_line": 16,
"old_path": "src/utils/helpers.ts", # same as new_path
"old_line": None # None = added line
}
}
req = urllib.request.Request(
f"https://gitlab.com/api/v4/projects/{project}/merge_requests/{mr_iid}/discussions",
data=json.dumps(payload).encode(),
headers={"PRIVATE-TOKEN": token, "Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req) as resp:
result = json.loads(resp.read())
note = result["notes"][0]
is_inline = note.get("position") is not None # True = inline, False = fell back to general
print("inline:", is_inline, "| disc_id:", result["id"])
Finding the Correct Line Number
Line numbers must point to an added line (+ prefix) in the diff — context lines and removed
lines will cause the position to be rejected:
import re
def get_new_line_number(diff_text, keyword):
"""Find the new_file line number of the first added line containing keyword."""
new_line = 0
for line in diff_text.split("\n"):
hunk = re.match(r"@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@", line)
if hunk:
new_line = int(hunk.group(1)) - 1
continue
if line.startswith("-") or line.startswith("\\"):
continue
new_line += 1
if line.startswith("+") and keyword in line:
return new_line
return None
# Usage
diffs = json.loads(...) # from /merge_requests/{iid}/diffs
for d in diffs:
if d["new_path"] == "src/utils/helpers.ts":
line = get_new_line_number(d["diff"], "safeParse")
print("line:", line)
Reusable Script
For scripted or automated MR reviews, use the bundled helper:
# Single comment
python3 scripts/post-inline-comment.py \
--project "mygroup/myproject" \
--mr 42 \
--file "src/utils/helpers.ts" \
--line 16 \
--body "This returns the wrapper object — use .data instead."
# Batch from JSON file
python3 scripts/post-inline-comment.py \
--project "mygroup/myproject" \
--mr 42 \
--batch comments.json
Batch file format:
[
{ "file": "src/utils/helpers.ts", "line": 16, "body": "Comment 1" },
{ "file": "src/routes/+page.svelte", "line": 58, "body": "Comment 2" }
]
The script auto-reads your token from glab config, fetches fresh SHAs, and reports whether each comment landed inline or fell back to general.
Filtering discussion threads by resolution (v1.88.0+)
# Show only unresolved discussion threads on an MR
glab mr view 123 --unresolved
# Show only resolved threads
glab mr view 123 --resolved
Useful for quickly checking which review threads still need attention before merging.
v1.87.0 Changes: New glab mr list Flags
The following flags were added to glab mr list in v1.87.0:
# Filter by author
glab mr list --author <username>
# Filter by source or target branch
glab mr list --source-branch feature/my-branch
glab mr list --target-branch main
# Filter by draft status
glab mr list --draft
glab mr list --not-draft
# Filter by label or exclude label
glab mr list --label bugfix
glab mr list --not-label wip
# Order and sort
glab mr list --order updated_at --sort desc
glab mr list --order merged_at --sort asc
# Date range filtering
glab mr list --created-after 2026-01-01
glab mr list --created-before 2026-03-01
# Search in title/description
glab mr list --search "login fix"
# Full flag reference (all available flags)
glab mr list \
--assignee @me \
--author vince \
--reviewer @me \
--label bugfix \
--not-label wip \
--source-branch feature/x \
--target-branch main \
--milestone "v2.0" \
--draft \
--state opened \
--order updated_at \
--sort desc \
--search "auth" \
--created-after 2026-01-01
v1.88.0 Changes
glab mr note: Added--resolve <discussion-id>and--unresolve <discussion-id>flags to resolve/reopen discussion threads while adding a noteglab mr view: Added--resolvedand--unresolvedflags to filter displayed discussion threads by resolution status
Command reference
For complete command documentation and all flags, see references/commands.md.
Available commands:
approve- Approve merge requestscheckout- Check out an MR locallyclose- Close merge requestcreate- Create new MRdelete- Delete merge requestdiff- View changes in MRfor- Create MR for an issuelist- List merge requestsmerge- Merge/accept MRnote- Add comment to MRrebase- Rebase source branchreopen- Reopen merge requestrevoke- Revoke approvalsubscribe/unsubscribe- Manage notificationstodo- Add to-do itemupdate- Update MR metadataview- Display MR details