Create Merge Request
Create a GitLab merge request for the current branch targeting main.
Steps
Get the first commit message of the current branch (compared to main):
git log main..HEAD --reverse --format="%s" | head -n 1Build the description in a file containing REAL line breaks. Never put
\nor\\ninside a quoted-dargument: the shell passes those characters literally, so GitLab renders them as\ninstead of Markdown line breaks.Use the file tool or a quoted heredoc to create the body, then pass its contents:
glab mr create -t "<TITLE>" -d "$(cat /tmp/mr_body.md)" -b main -yThe body file must contain actual Markdown, for example:
## Summary - Describe the implementation ## Verification - State the checks that passedPick one creation mode; do not mix them:
# A. Explicit title and body. Branch must already be pushed. glab mr create -t "<TITLE>" -d "$(cat /tmp/mr_body.md)" -b main -y # B. Derive title and body from commits. Pushes the branch. glab mr create --fill --fill-commit-body -b main -y
glab flag rules
Check glab mr create --help before reaching for a flag - glab is not gh, and
the installed version may predate a flag that exists upstream.
--fillwith both-tand-dis a hard error:usage of --title and --description overrides --fill. Mode A above drops--fillfor that reason.--description-filedoes not exist in anyglabversion. That isgh pr create --body-file. For a long body use-dwith the file contents, or-d -to open an editor (interactive only, so useless to an agent).--fill-commit-bodyrequires--fill:--fill-commit-body should be used with --fill.--templateis mutually exclusive with-d,--fill, and--related-issue, and is absent from older builds.- Non-interactive runs need either
-tor--fill, otherwise:--title or --fill required for non-interactive mode. --fillimplies--push; mode A does not, so push first.
Attaching a screenshot/image (optional)
A local image can't be embedded in an MR description directly - it must first be uploaded to the project, which returns ready-to-paste markdown.
Upload via the project uploads endpoint. Use raw
curl --form, notglab api(glab api -F file=@pathsends the file contents as a string field → HTTP 400, not a multipart upload):# URL-encode the project path: aircall/foo/bar -> aircall%2Ffoo%2Fbar curl -s --request POST \ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \ --form "file=@/path/to/image.png" \ "https://gitlab.com/api/v4/projects/<URL-ENCODED-PROJECT-PATH>/uploads" \ -o /tmp/upload.json python3 -c "import json; print(json.load(open('/tmp/upload.json'))['markdown'])"The response
markdownfield looks like.Put that markdown in the MR description. For a multi-line body, write it to a file and pass its contents to
-d- there is no--description-file:glab mr create -t "<TITLE>" -d "$(cat /tmp/mr_body.md)" -b main -y # bash/zshIn fish the substitution is
-d (cat /tmp/mr_body.md); simplest is to put the whole invocation in a.shfile and runbash script.sh.
Verify the created description
After creation, fetch the MR and inspect the description before reporting success:
glab mr view <MR_ID> --output json
The returned description must contain real newlines and Markdown markers such as ## and - as separate lines. If it contains literal \n, repair it immediately using a body file with real line breaks:
glab mr update <MR_ID> -d "$(cat /tmp/mr_body.md)" -y
glab mr view <MR_ID> --output json
Do not report the MR as ready until this verification passes. glab mr update supports -d; --description-file is not available.