Skill v1.0.2
currentAutomated scan100/1005 files
version: "1.0.2" name: glab-mr description: "Create, view, manage, approve, and merge GitLab merge requests. Use when working with MRs: creating from branches/issues, reviewing, approving, adding comments, resolving discussion threads, checking out locally, viewing diffs, rebasing, merging, or managing state. Triggers on merge request, MR, pull request, PR, review, approve, merge, resolve thread."
glab mr
Create, view, and manage GitLab merge requests.
Quick start
# Create MR from current branchglab mr create --fill# List my MRsglab mr list --assignee=@me# Review an MRglab mr checkout 123glab mr diffglab mr approve# Merge an MRglab mr merge 123 --when-pipeline-succeeds --remove-source-branch
Common workflows
Creating MRs
From current branch:
glab mr create --fill --label bugfix --assignee @reviewer# Create now, merge automatically when checks passglab mr create --fill --auto-merge# Start from an MR template file when your project uses oneglab mr create --fill --template .gitlab/merge_request_templates/default.md
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:
``bash glab mr list --reviewer=@me --state=opened ``
- Checkout and test:
``bash glab mr checkout 123 npm test ``
- Leave feedback:
```bash # Forward command surface for new MR comments/discussions glab mr note create 123 -m "Looks good, one question about the cache logic"
# Automation/status update that should not create a resolvable thread glab mr note create 123 -m "Build status: green" --resolvable=false
# Reply inside an existing discussion thread glab mr note create 123 --reply abc12345 -m "Good catch — updated"
# Native diff comments on the latest MR version glab mr note create 123 --file src/cache.ts --line 42 -m "Please extract this branch" glab mr note create 123 --file src/cache.ts --old-line 17 -m "Why was this removed?"
# List discussion threads and expose note/discussion IDs (experimental) glab mr note list 123 glab mr note list 123 --state unresolved --type diff glab mr note list 123 --output json
# Resolve or reopen a discussion by note/discussion ID (experimental) glab mr note resolve 3107030349 123 glab mr note reopen 3107030349 123 ```
- Approve:
``bash glab mr approve 123 ``
Automated review workflow:
For repetitive review tasks, use the automation script bundled with this skill under scripts/ (paths below are relative to the skill's own directory):
scripts/mr-review-workflow.sh 123scripts/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 123glab 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 create --auto-mergeto request auto-merge up front, orglab mr merge --when-pipeline-succeedson an existing MR
Automation:
- Script:
scripts/mr-review-workflow.shfor automated review + test workflow
Listing and targeting MR discussions
glab mr note list text output includes each note ID and discussion ID, plus both relative and absolute timestamps. Use those identifiers with resolve, reopen, or --reply rather than scraping author/body text.
# Filter the text viewglab mr note list 123 --type diff --state unresolvedglab mr note list 123 --file src/app.ts# Prefer JSON when an automation needs stable IDsglab mr note list 123 --output json \--jq '.[] | {discussion_id: .id, note_ids: [.notes[].id]}'# Act on the verified identifierglab mr note resolve <discussion-or-note-id> 123glab mr note reopen <discussion-or-note-id> 123
--type accepts all, general, diff, or system; --state accepts all, resolved, or unresolved; --file limits results to diff notes on one path. These subcommands remain experimental, so confirm live help when scripting across mixed glab versions.
Native MR note flow (glab mr note create)
glab mr note create is the preferred command surface for posting new MR discussions.
Use native glab mr note create when
# New top-level discussion/commentglab mr note create 123 -m "Please add a regression test"# Non-resolvable note for automation/status outputglab mr note create 123 -m "Build status: green" --resolvable=false# Reply to an existing discussion threadglab mr note create 123 --reply abc12345 -m "Fixed in the latest push"# File-level diff commentglab mr note create 123 --file src/app.ts -m "General concern on this file"# Line comment on the new side of the diffglab mr note create 123 --file src/app.ts --line 84 -m "This branch can return null"# Range comment on the new sideglab mr note create 123 --file src/app.ts --line 84:96 -m "Consider extracting this block"# Comment on a removed line from the old sideglab mr note create 123 --file src/app.ts --old-line 37 -m "Why was this guard removed?"
Flag rules worth remembering from the upstream help/docs:
--replytargets an existing discussion thread instead of starting a new one.--replyaccepts a full discussion ID or a unique prefix of at least 8 characters.- By default, new top-level notes are created as resolvable discussion threads. Use
--resolvable=falsefor bot/status comments that should not block projects requiring all threads to be resolved. --lineand--old-linerequire--fileand cannot be used together.--file,--reply, and--uniqueare mutually exclusive.--resolvable=falsecannot be combined with--reply,--file,--line, or--old-line.- Omit both
--lineand--old-linewhen you want a file-level diff comment.
Keep the helper/script path when
Use the bundled inline-comment helper or raw glab api JSON-body approach when you need stronger anchoring guarantees for automation, especially when:
- you must verify that GitLab created an actual inline discussion rather than silently falling back to a general MR note
- you are posting many comments in batch
- you are targeting tricky diffs (new files, renamed files, complex paths, or line-code fallback cases)
glab mr note create is now enough for most interactive reply and diff-comment workflows. The helper remains valuable for robust automated review pipelines.
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 configtoken = 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 valuesr = 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", # for renamed files, use the diff's actual old_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 generalprint("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 redef get_new_line_number(diff_text, keyword):"""Find the new_file line number of the first added line containing keyword."""new_line = 0for line in diff_text.split("\n"):hunk = re.match(r"@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@", line)if hunk:new_line = int(hunk.group(1)) - 1continueif line.startswith("-") or line.startswith("\\"):continuenew_line += 1if line.startswith("+") and keyword in line:return new_linereturn None# Usagediffs = json.loads(...) # from /merge_requests/{iid}/diffsfor 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 helper bundled with this skill under scripts/ (paths below are relative to the skill's own directory):
# Single commentpython3 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 filepython3 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 diffs, and uses a two-step anchoring strategy:
- Try the normal
position[new_line]inline payload first. - If GitLab rejects it with a
line_codevalidation error, compute the diff anchor and retry withposition[line_range][start/end][line_code].
That retry path is the preferred recovery for failures like:
400 Bad request - Note {:line_code=>["can't be blank", "must be a valid line code"]}
Only if that retry also fails should your broader review workflow fall back to a root MR note that clearly says inline anchoring failed while preserving the exact finding text and reviewer identity.
Filtering discussion threads by resolution
# Show only unresolved discussion threads on an MRglab mr view 123 --unresolved# Show only resolved threadsglab mr view 123 --resolved
Useful for quickly checking which review threads still need attention before merging.
glab mr list filtering flags
glab mr list supports the following filtering and sorting flags:
# Filter by authorglab mr list --author <username># Filter by source or target branchglab mr list --source-branch feature/my-branchglab mr list --target-branch main# Filter by draft statusglab mr list --draftglab mr list --not-draft# Filter by label or exclude labelglab mr list --label bugfixglab mr list --not-label wip# Order and sortglab mr list --order updated_at --sort descglab mr list --order merged_at --sort asc# Date range filteringglab mr list --created-after 2026-01-01glab mr list --created-before 2026-03-01# Search in title/descriptionglab 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
Structured output
glab mr approvers supports --output json / -F json for structured output, which is useful for agent automation.
# View MR approvers with JSON outputglab mr approvers 123 --output jsonglab mr approvers 123 -F json
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- MR discussion commands; useglab mr note createfor new comments, pluslist,resolve, andreopenrebase- Rebase source branchreopen- Reopen merge requestrevoke- Revoke approvalsubscribe/unsubscribe- Manage notificationstodo- Add to-do itemupdate- Update MR metadataview- Display MR details