- 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.
## Resolve or reopen in another repository
With `--repo`, `mr note resolve` and `mr note reopen` require an explicit MR before the discussion identifier:
```bash
glab mr note resolve 123 abc12345 --repo group/project
glab mr note reopen 123 abc12345 --repo group/project
```
The discussion may be a full ID, a unique prefix of at least eight characters, or a numeric note ID. Without `--repo`, omitting the MR uses the current branch. The binary's usage line lists the operands inconsistently; follow its examples and the implementation: MR first, discussion second. Confirm the target before changing discussion state.
## Checkout, creation, and selection
`glab mr checkout 123 --worktree ../review-mr-123` fetches and checks out the MR in a separate worktree. Reusing that path updates the existing worktree. A branch can be checked out in only one worktree; choose `--branch` for a different local name when needed. Review divergence/reset prompts carefully: worktree isolation does not authorize discarding commits or local changes. Fetching through a matched remote creates the tracking ref.
`mr create --fill` derives title/description from commits and defaults to pushing unless `--push` was explicitly set. Use `glab mr create --fill --push=false` to suppress that push; it still creates an MR and is not a dry run. Ensure the source branch is already available remotely. Draft creation avoids adding a second `Draft:` prefix.
`glab mr update feature-branch --state opened --ready` restricts branch lookup to open MRs. Accepted states are `opened`, `closed`, `merged`, `locked`, and `any` (default); IDs and URLs ignore this selection filter. `--state` does not itself change MR state.
A list filter for exactly one assignee with no reviewers, or exactly one reviewer with no assignees, preserves server ordering. Do not infer the same ordering guarantees for combined multi-user results. `mr view` shows available human-readable spent/estimated time; use JSON for machine consumption.
## Template selection
For `create --template`, a bare name (with optional `.md`) resolves under the repository's `.gitlab/merge_request_templates/` directory. An absolute path or a path containing a separator is read directly; relative paths use the current directory and `~/` is supported. Use `./template.md` for a file in the current directory, rather than a bare template name. Read and approve the file contents before posting them; `--description-file` remains mutually exclusive with `--template`.
## Quick start
```bash
# Create MR from current branch
glab mr create --fill
# Create or update with a multi-line description from a file
glab mr create --title "Fix login bug" --description-file description.md
glab mr update 123 --description-file description.md
# Attach local files to a description
glab mr create --title "Fix login bug" --description "Before and after:" \
--attach ./before.png --attach ./after.png
glab mr update 123 --attach ./latest.png
# 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
```
For automated patch consumption, use `glab mr diff --raw`. The normal rendered diff is intended for people; `--raw` returns an unmodified Git patch with standard unified diff headers.
## Common workflows
### Creating MRs
**From current branch:**
```bash
glab mr create --fill --label bugfix --assignee @reviewer
# Create now, merge automatically when checks pass
glab mr create --fill --auto-merge
# Start from an MR template file when your project uses one
glab mr create --fill --template .gitlab/merge_request_templates/default.md
```
For one-off multi-line descriptions, use `--description-file <path>` or `--description-file -` for stdin. It is mutually exclusive with `--description`; on create, it is also mutually exclusive with `--template`. A file containing exactly `-` is rejected because `--description -` means "open an editor".
Use the experimental `--attach <path>` repeatedly to upload files and append GitLab-provided Markdown references to the description. `--attach -` reads one file from stdin and cannot share stdin with `--description-file -`. On update, attachments append to the existing description unless a replacement description is supplied. For fork merge requests, `mr create` uploads attachments to the target project so the references resolve there.
In a non-interactive environment, an explicit `--title` is sufficient; glab can create the MR with an empty description instead of requiring a TTY or `--description`. Interactive terminals still prompt for a missing description/template. For deterministic automation, pass `--yes` plus any source/target/repository selectors explicitly.
```bash
GLAB_NO_PROMPT=1 glab mr create \
--title "Fix login timeout" \
--source-branch fix/login-timeout \
--target-branch main \
--yes
```
**Without a local Git checkout:**
`glab mr create` can create an MR outside a Git repository when every
remote-dependent input is supplied as a flag. The source branch must already exist on
the selected source project. Do not use `--push`, `--fill`, or `--template`,
because those require local repository state.
```bash
glab mr create \
--repo group/project \
--source-branch feature-branch \
--target-branch main \
--title "Add feature" \
--description "Details..." \
--yes
# Fork source into an upstream target
glab mr create \
--repo upstream/project \
--head your-namespace/project \
--source-branch feature-branch \
--target-branch main \
--title "Add feature" \
--description "Details..." \
--yes
```
**From issue:**
```bash
glab mr for 456 # Creates MR linked to issue #456
```
**Draft MR:**
```bash
glab mr create --draft --title "WIP: Feature X"
```
### Review workflow
1. **List pending reviews:**
```bash
glab mr list --reviewer=@me --state=opened
```
2. **Checkout and test:**
```bash
glab mr checkout 123
npm test
```
3. **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"
# Internal note visible only to project members
glab mr note create 123 --internal -m "Rotating the leaked token now."
# 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?"
# Stage comments in a private pending review, then publish them together
glab mr note draft create 123 --file src/cache.ts --line 42 -m "Please extract this branch"
glab mr note draft list 123 --output json
glab mr note draft publish 123 --reviewer-state requested_changes --yes
# Attach evidence to a new or existing note
glab mr note create 123 -m "Rendered result" --attach ./result.png
glab mr note update 123 456789 --attach ./updated.png
# 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 123 3107030349
glab mr note reopen 123 3107030349
```
For `glab mr note update`, the note ID is always the final positional argument. The help renderer shows required arguments before optional ones, but the command parses the last argument as `<note-id>`; use `glab mr note update <mr> <note-id>` when specifying an MR.
4. **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):
```bash
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:**
```bash
glab mr merge 123 --when-pipeline-succeeds --remove-source-branch
```
Merge output reports the resulting merge request state accurately. Do not assume a successful `glab mr merge --auto-merge` prints `Merged!`; when the MR is only armed for auto-merge, automation should treat the accepted state as pending rather than already merged.
**Squash commits:**
```bash
glab mr merge 123 --squash
```
**Rebase before merge:**
```bash
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.yml` exists 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-issue` for creating/managing issues
- Use `glab mr for <issue-id>` to create MR linked to issue
- Script: `scripts/create-mr-from-issue.sh` automates branch + MR creation
**CI/CD integration:**
- See `glab-ci` for pipeline status before merging
- Use `glab mr create --auto-merge` to request auto-merge up front, or `glab mr merge --when-pipeline-succeeds` on an existing MR
**Automation:**
- Script: `scripts/mr-review-workflow.sh` for automated review + test workflow
## Listing and targeting MR discussions
`glab mr note list` text output includes each note ID and an eight-character discussion ID prefix for every non-system discussion, plus both relative and absolute timestamps. Pass the characters before the ellipsis directly to `--reply`; use JSON when a workflow needs the full discussion ID. Use verified identifiers with `resolve`, `reopen`, or `--reply` rather than scraping author/body text.
```bash
# Filter the text view
glab mr note list 123 --type diff --state unresolved
glab mr note list 123 --file src/app.ts
# Prefer JSON when an automation needs stable IDs
glab mr note list 123 --output json \
--jq '.[] | {discussion_id: .id, note_ids: [.notes[].id]}'
# Extract full discussion IDs
glab mr note list 123 -F json --jq '.[].id'
# Act on the verified identifier
glab mr note resolve 123 <discussion-or-note-id>
glab mr note reopen 123 <discussion-or-note-id>
```
Upstream's generated help demonstrates the same extraction with an external `jq` pipe. This skill set prefers the supported built-in `--jq` flag so filtering stays inside `glab` and avoids a separate shell process.
`--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.
In text output, the default `--type all` includes system notes as well as regular discussions. Use `--type system` when you want only system activity, or select a narrower note type when automation should not parse assignment/status events as user feedback.
## 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
```bash
# New top-level discussion/comment
glab mr note create 123 -m "Please add a regression test"
# Non-resolvable note for automation/status output
glab mr note create 123 -m "Build status: green" --resolvable=false
# Reply to an existing discussion thread
glab mr note create 123 --reply abc12345 -m "Fixed in the latest push"
# File-level diff comment
glab mr note create 123 --file src/app.ts -m "General concern on this file"
# Line comment on the new side of the diff
glab mr note create 123 --file src/app.ts --line 84 -m "This branch can return null"
# Range comment on the new side
glab mr note create 123 --file src/app.ts --line 84:96 -m "Consider extracting this block"
# Comment on a removed line from the old side
glab 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:
- `--reply` targets an existing discussion thread instead of starting a new one.
- `--reply` accepts 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=false` for bot/status comments that should not block projects requiring all threads to be resolved.
- `--line` and `--old-line` require `--file` and cannot be used together.
- `--file`, `--reply`, and `--unique` are mutually exclusive.
- `--resolvable=false` cannot be combined with `--reply`, `--file`, `--line`, or `--old-line`.
- Pending review comments use `glab mr note draft create`, not `mr note create --draft`. Draft creation supports diff targeting, replies, and attachments, but not `--unique`, `--resolvable`, or `--internal`.
- `--internal` creates a note visible only to project members. It cannot be combined with diff targeting (`--file`, `--line`, or `--old-line`), and a new top-level internal note must not explicitly request `--resolvable=true`.
- With `--reply --internal`, glab refuses to post to a public thread. A reply to an internal thread stays internal even when `--internal` is omitted.
- `--attach` can be repeated and may provide the entire note body; it cannot be combined with `--unique` because each upload produces a fresh URL.
- On `mr note update`, attachment-only input appends to the current note body; pairing `--message` with attachments replaces the body and then appends the new references.
- Omit both `--line` and `--old-line` when you want a file-level diff comment.
### Pending review comments
Use pending comments when you want to stage several review findings before making them visible:
```bash
glab mr note draft create 123 --file src/app.ts --line 84 \
-m "This branch can return null"
glab mr note draft create 123 --reply abc12345 \
-m "I reproduced this locally"
# Inspect only your own pending comments before publishing
glab mr note draft list 123 --output json
# Review state is not an approval; use glab mr approve separately when appropriate.
glab mr note draft publish 123 \
--message "A few blockers, see the comments." \
--reviewer-state requested_changes \
--yes
```
`publish` releases only your own pending comments. `--reviewer-state` accepts `requested_changes` or `reviewed`; neither records a formal approval. If you authored the merge request, GitLab ignores `--reviewer-state` unless you are also listed as a reviewer, and glab warns about that condition. `--internal` applies only to the optional summary note and requires `--message`. Non-interactive runs require `--yes`, and publishing fails when no pending comments exist. Attachments are uploaded immediately even while the comment itself remains pending.
`draft create` prints the numeric pending-comment ID. Preserve it for `draft update` or `draft delete`; it is not a published note ID or discussion ID. When an MR is supplied, the parser expects the draft ID last, despite the renderer placing required arguments first:
```bash
# Inspect the selected draft before changing it
glab mr note draft list 123 --output json
glab mr note draft update 123 456 -m "Revised finding"
# Delete only after explicit approval; this affects your pending review
glab mr note draft delete 123 456 --yes
```
`draft update` changes only the body, not its diff position; image-position drafts cannot be updated from the CLI because their position cannot be preserved. Attachment-only updates append to the existing body, while an explicit message replaces the body before attachment references are appended. `draft delete` prompts unless `--yes` is supplied. The draft command group acts only on your own pending comments; other reviewers' drafts are not listed, edited, deleted, or published. Do not use the removed `mr note publish` command: its `--help` falls back to parent help rather than proving that it still exists.
### 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 a root 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
In glab v1.118.0, `glab api --field position[new_line]=N` fails fast when it would create a JSON request body:
```text
a field name containing a bracket is not supported in a JSON request body
```
Do not use bracketed field names to build inline MR discussion bodies. Older form-style position payloads were also fragile with:
- Entirely new files (`new_file: true` in the diff)
- Files with complex/encoded paths
- Any nested position field that doesn't survive form encoding
Use `--input -` with a JSON body for inline discussion creation. Always check the returned note's `position` field so automation can detect a root MR note or failed anchor.
### The Fix: Always Use JSON Body
Post inline comments through `glab api` with a literal JSON request body. Keep authentication inside `glab`; never read, copy, log, or print stored token values:
View on GitHub