Skip to content
GitmoapdocsBeta
Guide· How do I…?

Merge request settings

Overview

Merge request settings are a repository's merge policy: how a merge request lands in git, whether its commits may be squashed, and what else has to be true before the Merge button does anything. Set them on a group and every repository beneath inherits them, so "no unresolved threads and a green pipeline" can be one decision for the whole organisation rather than forty.

There is one set of settings per repository and one per group — nothing to create, nothing to name. You change fields, and the set comes into existence the first time you do. Nothing is copied onto a merge request when it is opened: the policy is read fresh every time somebody asks for the merge check or merges. A change you make now therefore applies to every merge request that is open right now.

Who has to approve a merge request, and how many of them, is not one of these settings. It is set with approval rules, which have their own page and their own way of inheriting.

Before you start

  • Maintainer to change settings, on the repository or group you are configuring, or on any group above it. Maintainer on a top-level group is enough to set the policy for everything underneath.
  • Reporter to read them, on the repository or group you are asking about — with the usual visibility shortcut: a public repository or group answers anyone, signed in or not; an internal one answers anyone signed in; a private one answers only its members. A rule that refuses a merge is not a secret, and contributors need to know what they are being asked for before they open a merge request.
  • A token with the right scope, if you are calling the API or the CLI rather than clicking: mr_settings:read to read, mr_settings:manage to change or reset. Scopes are fixed when a token is minted, so a script that only audits policy should carry mr_settings:read and nothing else.
  • Nothing else needs to exist.A repository with no branches and no merge requests can be configured today; the policy is waiting when the first merge request arrives.
  • Know which knobs are gates and which are defaults.Most settings block a merge. The merge method is a default that whoever merges can override for that one merge — see The merge method is a default, the rest are gates below before you rely on it.

How it works

One set per scope, resolved when you read it

Every repository and every group has at most one set of these settings, and each field in it is in one of two states: set to a value, or unset. false and 0 are values like any other, so a field set to one of them is an override, not an absence; unset is the only thing that means inherit. Reading a repository gives you the resolved answer, with inheritance already applied and every field carrying a concrete value. Reading a group gives you that group's own values only, with unset fields absent or null, never filled in with a default.

That asymmetry is deliberate, and it is the difference between two questions. What will happen to my next merge? is a repository question and deserves a complete answer. What does this group impose on everything below it? is a group question, and folding in what the group inherits from its own parents would hide where a value actually comes from.

The repository's override switch is all or nothing

A group chain merges field by field: walking from the repository's parent group upwards, the nearest group that sets a field wins for that field, and a group that turns its own override switch on stops the walk there. A repository is different. Its stored values count only when override_group_settings is on. With it off — the default — everything the repository has saved is ignored and the whole policy comes from the group chain.

root group unresolved_threads = true, block_on_rejection = true
child group merge_method = fast_forward
repository override_group_settings = true, unresolved_threads = false
effective for the repository:
unresolved_threads = false (the repository's own value wins)
merge_method = fast_forward (from the child group — the repository leaves it unset)
block_on_rejection = true (from the root group)
everything else = its default

Turn that switch off and the same repository resolves differently, even though nothing else changed:

repository override_group_settings = false, unresolved_threads = false (stored, dormant)
effective for the repository:
unresolved_threads = true (the root group's value — the repository's false is ignored)
merge_method = fast_forward
block_on_rejection = true

The repository's saved values are not deleted when the switch goes off; they sit there as a draft and come back the moment you turn it on. This is why a repository can look configured and behave as though it is not — and why the first thing to check when a change has no effect is that switch.

false is an override, null hands a field back

Setting a switch to false means not this, whatever the group says. It is a value like any other and it wins over an ancestor's true. To stop overriding a single field and inherit it again, send that field as null. To hand back every field at once, reset the whole set.

The same is true of a value that happens to equal the default. To escape a group's require_pipeline_success: true for one repository, turn its override switch on and set require_pipeline_success: false — leaving it unset would inherit the true straight back.

The merge method is a default, the rest are gates

merge_method is the method a merge uses when the merge request does not name one, and whoever merges may name one. Setting fast_forward on the repository is how you get a linear history by default; it does not stop a Maintainer from asking for a merge commit on a particular merge. If a method must be the only method, protect the target branch as well so that fewer people can merge at all.

The squash policy is not a gate either, and it is not symmetrical. never is the only value that refuses anything, and only when a merge explicitly asks to squash. require squashes whatever the merge asked for, without complaint. allow and encourage decide nothing but which way the box starts.

The rest of the list splits three ways. override_group_settings and delete_source_branch_default refuse nothing at all. stack_auto_retarget and stack_restack_strategy decide what happens after a merge, never whether it happens. Everything else — block_on_rejection, the discussion, work-item and pipeline gates, and stack_enforce_merge_order — refuses the merge outright.

What each setting does

SettingDefaultWhat it does
override_group_settingsfalseWhether this repository's own values count at all. On a group, it stops that group from inheriting from its own parents
merge_methodmerge_commitThe default method: merge_commit writes a commit onto the target rather than fast-forwarding, even when a fast-forward would do; fast_forward adds no merge commit and needs the source to be directly ahead of the target; rebase_merge is refused — by the merge check and again at the merge
squash_optionallownever rejects a merge that asks to squash, allow offers it off by default, encourage offers it on by default, require squashes every merge whatever the merge asks for
delete_source_branch_defaultfalseWhat "delete the source branch" defaults to when a merge does not say
unresolved_threadsfalseEvery discussion thread on the merge request must be resolved
block_on_rejectionfalseA reviewer who has requested changes blocks the merge until they withdraw or change that verdict. A later push does not clear it
require_linked_issuefalseThe merge request's title or description must reference at least one work item. Any reference counts, closing or not
require_issue_criteria_greenfalseEvery required acceptance criterion of the work items this merge request closes must be satisfied. One a pipeline proves is read at the head commit, and no evidence at that commit is a failure rather than a pass; one somebody ticks by hand just has to be ticked
require_pipeline_successfalseEvery pipeline that applies to the merge request's head commit must have succeeded — a run that succeeded with warnings counts as success. A pipeline still running, waiting to start, or unable to start blocks the merge
allow_merge_without_pipelinefalseOnly while require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Off, it is blocked
stack_auto_retargettrueWhen a merge request in a stack merges, the open merge requests sitting on top of it are re-pointed at its target automatically
stack_enforce_merge_ordertrueA merge request cannot merge while an open merge request still sources from its target branch. Off, the merge check says nothing about order at all and you may merge in any sequence
stack_restack_strategysignalsignal reports that a branch needs rebasing and leaves it to the author. server_side rebases the branch and force-updates it. Neither happens unless stack_auto_retarget is on

Where each one is checked

WhenWhat is checked then
The merge check runs, and again at the merge itselfmerge_method, unresolved_threads, block_on_rejection, require_linked_issue, require_issue_criteria_green, require_pipeline_success with allow_merge_without_pipeline, stack_enforce_merge_order
At the merge onlysquash_option, and delete_source_branch_default as a fallback
Immediately after a mergestack_auto_retarget, and stack_restack_strategy underneath it

squash_option appears only at the merge: the merge check has no squash item at all, because nothing has said whether this merge wants a squash until the merge itself asks.

Nothing is snapshotted onto a merge request when it is opened. Turning unresolved_threads on blocks every open merge request that has an open thread, from the next merge check onwards, and turning it off unblocks them just as quickly. Approval rules behave the same way.

How to

Read the policy a repository applies

This is the answer to what will happen to my next merge — every field resolved to a concrete value, whether that value came from this repository or from a group four levels up. The response does not say which. override_group_settings tells you only whether the repository's own values count at all, and even when it is on, every field the repository leaves unset still comes from the groups. Reading the groups is how you find out where a value lives.

Open the repository, choose Settings, then the Merge requests tab. While the repository is inheriting, the fields are replaced by a note telling you so and no values are shown at all. Turn Override parent group settings on and the form appears, pre-filled with the policy that is in force, ready to edit.

Terminalbash
gitmoap mr-settings get-repo --repo-id <id>
curlbash
curl -X GET https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Read what a group sets on its own

A group's settings come back raw — only what this group declares, with everything it leaves alone absent or null rather than shown as a default. That is what you want when you are tracing where a value in a repository came from: walk up the groups until one of them names the field.

Open the group, choose Settings, then the Merge requests tab. The form always shows every field, filling in the ones this group does not set with a value it never chose — so the form cannot tell you set from unset, and the stacked-merge-request switches in particular read as off while they are unset. The API answer can: it omits what the group leaves alone. A repository's own view is where you see what is actually in force.

Terminalbash
gitmoap mr-settings get-group --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Set a repository's own merge policy

Send only the fields you want to change — the rest keep the values they had. Nothing here needs the repository to have merge requests, branches or commits.

If the repository sits inside a group that sets a policy, remember the switch: without override_group_settings: true in the same call, everything else you send is stored and ignored.

Open the repository's Settings → Merge requests, turn on Override parent group settings, and set Merge method, Squashing and Delete source branch by default. Save applies the change immediately; the save bar tells you there are unsaved changes until you do.

Terminalbash
gitmoap mr-settings update-repo --repo-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Note

Saving this form writes every field on it, not only the one you touched, so from then on the repository sets all of those fields itself rather than inheriting them. To keep inheriting the rest, change the individual fields through the CLI or the API instead, and send null for anything you want handed back to the group.

Two combinations are worth knowing before you pick a method. fast_forward refuses a merge whenever the source branch is behind the target, so contributors have to rebase before they can land — that is the point of it, but it is a workflow change and not just a history preference. fast_forward together with squash_option: require gives the cleanest possible history: one commit per merge request, no merge commits at all.

Hold a merge while a reviewer asks for changes

block_on_rejection makes a reviewer's "changes requested" verdict hold the merge until that reviewer withdraws it or changes it to an approval, however many approvals arrive in the meantime. A later push does not clear it — somebody has to look again.

json
{
"override_group_settings": true,
"block_on_rejection": true
}

Open the repository's Settings → Merge requests and turn on Block merging when changes are requested.

Terminalbash
gitmoap mr-settings update-repo --repo-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

How many approvals a merge request needs, whose approvals count, whether the author or a committer may approve, and whether approvals survive a push are set with approval rules, not here.

Block a merge on discussion, work items or a pipeline

These four are the gates that tie a merge to the rest of the work rather than to the code review.

FieldThe merge is blocked while
unresolved_threadsAny discussion thread on the merge request is unresolved — an inline thread on the diff or a top-level one
require_linked_issueThe merge request's title and description name no work item. Any reference counts: #412 on its own is enough, it does not have to be a closing one
require_issue_criteria_greenA required acceptance criterion on a work item this merge request closes is not satisfied. One a pipeline proves must be green at the head commit, and no evidence at that commit counts as a failure; one somebody ticks by hand just has to be ticked
require_pipeline_successA pipeline that applies to the head commit has not succeeded yet — it is running, waiting to start, could not start, or failed — or no pipeline applies and allow_merge_without_pipeline is off
json
{
"override_group_settings": true,
"unresolved_threads": true,
"require_pipeline_success": true
}

require_pipeline_success is off until somebody turns it on, and deliberately so: a merge is allowed only when every check has passed, so a CI gate that defaulted on would make every repository without a pipeline unmergeable. Turn it on once the repository has a pipeline that runs on merge requests, not before.

Every pipeline that applies has to pass, not just one. A pipeline applies to a merge request when one of its triggers matched the merge request — its branch, its paths and its actions — or when a run of it tested the head commit, such as a push to the source branch. Each one is judged by its latest run for the head, so retrying a failed run, or approving an untrusted one, replaces the result. A pipeline that matched but could not start — its monthly runs ran out, its definition does not resolve, the merge result conflicts — blocks the merge and the merge check says why. Reading the merge request's pipelines lists each one with where it stands.

Right after a push the gate waits. Until the platform has decided which pipelines apply to the new head, the merge check reports that it is waiting for the pipelines to be scheduled, and the merge is refused. This takes seconds, and it is what stops a merge slipping through between the push and its pipelines starting.

Switching the gate on also switches on a second, non-blocking report. A merge request's run records the target branch tip it was tested against, and if the target has moved since, the merge check says so — the run passed, but it no longer describes what will actually land. That report does not stop a merge. Run the merge request's pipelines again if you want the green to still mean something.

Decide what happens when no pipeline applies

Some merge requests have no pipeline at all: the repository defines none yet, or every pipeline filters itself out — a documentation change in a repository whose CI runs only when code changes. With require_pipeline_success on, allow_merge_without_pipeline decides what that means. Off — the default — the merge is blocked, because the repository said a green pipeline is required and there is none to show. On, a merge request that no pipeline applies to merges as if the gate were satisfied.

json
{
"override_group_settings": true,
"require_pipeline_success": true,
"allow_merge_without_pipeline": true
}

Turning it on does not weaken the gate for merge requests a pipeline does apply to: those still wait for every one of them to pass, and a head the platform is still evaluating is never treated as having no pipeline. It has no effect while require_pipeline_success is off.

require_issue_criteria_green reads against the merge request's head commit, not the default branch. Push a fix and the criteria a pipeline proves are re-evaluated against the new head; a pass recorded at an older commit does not carry forward. A criterion somebody ticks by hand is not tied to a commit and stays ticked. require_linked_issue is not commit-scoped at all — it asks only whether the merge request names a work item.

Open Settings → Merge requests on the repository and use Require all threads resolved, and, under Issue requirements, Require a linked issue and Require linked issues' acceptance criteria to pass. Under Pipelines, use Require pipelines to pass and Allow merging when no pipeline applies. The second is unavailable while the first is off, because it has no effect then.

Terminalbash
gitmoap mr-settings update-repo --repo-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Change how stacked merge requests behave

A stack is a chain of merge requests, each targeting the branch below it. Three settings decide what happens to the ones above when one of them lands.

stack_auto_retarget is on by default and is what keeps a stack coherent: when a merge request merges, its open dependents are re-pointed at the branch it merged into. Turn it off and they keep pointing at a branch that has just been merged and possibly deleted, which is almost never what you want.

stack_enforce_merge_order is also on by default and refuses to merge a merge request while an open one still sources from its target branch — that other merge request is below it in the stack and has to land first. Turn it off and the merge check drops the item entirely, so you may merge in any sequence; the stack view is then the only place the chain is still visible.

stack_restack_strategy decides who does the rebasing a retarget can leave behind — most visibly after a squash merge, which puts the same diff on the target under a new commit. signal reports it on the merge check, without blocking the merge, and leaves the author to rebase and force-push; the report appears only when the branch is genuinely behind. server_side rebases the dependent branch onto its new base as part of the retarget and force-updates it, every time, whether or not anyone would have noticed.

Both of those ride on the retarget, which is the one that has to happen first. The whole post-merge cascade runs only when stack_auto_retarget is on, so server_side with auto-retarget off does nothing at all. If you want server-side restacking, leave auto-retarget on.

Warning

server_side rewrites contributors' branches without asking. A force-update replaces the commits on the dependent branch, so anybody with that branch checked out has to reset to the new head, and any local work that has not been pushed has to be rebased by hand. It reaches direct dependents only — a merge request two levels up is reported rather than rebased — and a rebase that conflicts is abandoned cleanly and falls back to reporting. Existing branches are never touched retroactively: turning it on affects future merges only.

Open Settings → Merge requests on the repository and scroll to Stacked merge requests: Auto-retarget children when a parent merges, Enforce stack merge order, and the Restack strategy picker.

Terminalbash
gitmoap mr-settings update-repo --repo-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Hand one field back to the group

Turning a switch off is not the same as letting the group decide it. false is a value, and it overrides an ancestor's true exactly as true would. To stop overriding one field and inherit it again, send that field as null. The repository keeps its override on everything else.

json
{
"unresolved_threads": null
}

The same call works on a group and means the same thing: the group stops declaring that field, and the groups above it decide again.

The form has no per-field "inherit this one" control — it writes every field it shows. Turning Override parent group settings off hands all of them back at once, which is the web app's version of the same idea. Handing back a single field is a CLI or API action.

Terminalbash
gitmoap mr-settings update-repo --repo-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Set one policy for every repository in a group

This is the setting worth reaching for first. A group's policy reaches every repository beneath it at any depth, including repositories created next week, and each repository inherits it field by field unless it overrides.

Open the group, choose Settings, then Merge requests, set the fields you want as the baseline, and Save. The change reaches every repository in the group and its subgroups at once, including any whose next merge is already open.

Terminalbash
gitmoap mr-settings update-group --group-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/groups/{group_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Note

Saving a group's form writes every field on it, which means the group now sets all of those fields itself and stops inheriting them from the groups above. On a nested group that is usually not what you meant. To set one field and leave the rest inheriting, send just that field through the CLI or the API.

Turning override_group_settings on for a group does something different from a repository: it caps the walk. The group's own values are used, and any field still unset once the walk reaches it falls to its default rather than to what a group above says. Groups between it and the repository are unaffected and still supply whatever they set, so this isolates a subtree from an organisation-wide baseline without freezing the groups inside it. There is no switch for it on the group's form, so set it through the CLI or the API.

Reset a repository to the group policy

Warning

Resetting deletes the repository's whole set of settings in one action — every switch, every number, the override flag and all — and there is no undo and no record of what it contained. The repository immediately picks up whatever its groups say, which may be stricter or looser than it was a moment ago. Note down what you had before you reset: nothing else stores it.

Resetting is how you go back to inheriting everything. It is not a way to switch policy off — a repository with no settings of its own inherits the whole group chain. To genuinely depart from a group's policy, turn override_group_settings on and set the fields explicitly instead. Resetting is idempotent: resetting a repository that never had settings succeeds and changes nothing.

The Merge requests tab has no reset control. Turning Override parent group settings off returns the repository to the group's policy and keeps its stored values as a dormant draft, which is what most people want. To clear those values for good, use the CLI or the API.

Terminalbash
gitmoap mr-settings delete-repo --repo-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/repositories/{repo_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Clear a group's settings

Warning

Clearing a group's settings removes the baseline from every repository beneath it that was relying on it — in one action, with no undo. A repository that was refusing merges with open threads a minute ago may now accept them, and a repository that overrides the field is unaffected, so the outcome is uneven by design. Read a repository or two before you clear, and write down what the group declared.

Repositories below the group do not lose their own settings; they lose what they were inheriting. Each field the group used to declare now falls through to the nearest group above that declares it, and to its default if none does — from the next merge check onwards.

The group's Merge requests tab has no clear control. Use the CLI or the API, then check a repository beneath the group to confirm what it resolves to now.

Terminalbash
gitmoap mr-settings delete-group --group-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/groups/{group_id}/mr-settings \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Reference

Repository

3 endpoints

GET/repositories/{repo_id}/mr-settingsmr_settings:read

Get effective MR settings for a repository (resolved with group inheritance)

get_repo_mr_settingsGET /repositories/{repo_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:read

Send

Path
  • repo_iduuidRepository ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Repository not found

Receive

  • allow_merge_without_pipelinebooleanWhile require_pipeline_success is on: whether a merge request that no pipeline applies to — none is defined, or none matched its branch, paths or actions — may merge. Off by default, which blocks it.
  • block_on_rejectionboolean
  • delete_source_branch_defaultboolean
  • merge_methodfast_forwardmerge_commitrebase_merge
  • override_group_settingsboolean
  • require_issue_criteria_greenboolean
  • require_linked_issueboolean
  • require_pipeline_successbooleanBlock the merge until every pipeline that applies to the merge request's head has succeeded. Off by default.
  • squash_optionallowencourageneverrequire
  • stack_auto_retargetboolean
  • stack_enforce_merge_orderboolean
  • stack_restack_strategyserver_sidesignal
  • unresolved_threadsboolean
PATCH/repositories/{repo_id}/mr-settingsmr_settings:manage

Update MR settings for a repository (upsert, partial update)

update_repo_mr_settingsPATCH /repositories/{repo_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:manage

Send

Path
  • repo_iduuidRepository ID
Body
  • allow_merge_without_pipelineobjectoptionalWhile require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Off — the default — blocks it. null inherits.
  • block_on_rejectionobjectoptional
  • delete_source_branch_defaultobjectoptional
  • merge_methodobjectoptional
  • override_group_settingsobjectoptional
  • require_issue_criteria_greenobjectoptional
  • require_linked_issueobjectoptional
  • require_pipeline_successobjectoptionalBlock the merge until every pipeline that applies to the merge request's head has succeeded. null inherits.
  • squash_optionobjectoptional
  • stack_auto_retargetobjectoptional
  • stack_enforce_merge_orderobjectoptional
  • stack_restack_strategyobjectoptional
  • unresolved_threadsobjectoptional

Refuses

  • 400Invalid request
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Repository not found
  • 409The request conflicts with the resource's current state; `reason` says how

Receive

  • allow_merge_without_pipelineobjectoptionalWhile require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Absent inherits.
  • block_on_rejectionobjectoptional
  • delete_source_branch_defaultobjectoptional
  • merge_methodobjectoptional
  • override_group_settingsobjectoptional
  • require_issue_criteria_greenobjectoptional
  • require_linked_issueobjectoptional
  • require_pipeline_successobjectoptionalBlock the merge until every pipeline that applies to the merge request's head has succeeded. Absent inherits.
  • squash_optionobjectoptional
  • stack_auto_retargetobjectoptional
  • stack_enforce_merge_orderobjectoptional
  • stack_restack_strategyobjectoptional
  • unresolved_threadsobjectoptional
DELETE/repositories/{repo_id}/mr-settingsmr_settings:manage

Delete MR settings for a repository

delete_repo_mr_settingsDELETE /repositories/{repo_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:manage

Send

Path
  • repo_iduuidRepository ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Repository not found
  • 409The request conflicts with the resource's current state; `reason` says how

Receive

No body.

Group

3 endpoints

GET/groups/{group_id}/mr-settingsmr_settings:read

Get MR settings for a group (raw values, no inheritance resolution)

get_group_mr_settingsGET /groups/{group_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:read

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

The same shape update_repo_mr_settings returns — 13 fields. Go to it

Show it here
  • allow_merge_without_pipelineobjectoptionalWhile require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Absent inherits.
  • block_on_rejectionobjectoptional
  • delete_source_branch_defaultobjectoptional
  • merge_methodobjectoptional
  • override_group_settingsobjectoptional
  • require_issue_criteria_greenobjectoptional
  • require_linked_issueobjectoptional
  • require_pipeline_successobjectoptionalBlock the merge until every pipeline that applies to the merge request's head has succeeded. Absent inherits.
  • squash_optionobjectoptional
  • stack_auto_retargetobjectoptional
  • stack_enforce_merge_orderobjectoptional
  • stack_restack_strategyobjectoptional
  • unresolved_threadsobjectoptional
PATCH/groups/{group_id}/mr-settingsmr_settings:manage

Update MR settings for a group (upsert, partial update)

update_group_mr_settingsPATCH /groups/{group_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:manage

Send

Path
  • group_iduuidGroup ID
Body
  • allow_merge_without_pipelineobjectoptionalWhile require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Off — the default — blocks it. null inherits.
  • block_on_rejectionobjectoptional
  • delete_source_branch_defaultobjectoptional
  • merge_methodobjectoptional
  • override_group_settingsobjectoptional
  • require_issue_criteria_greenobjectoptional
  • require_linked_issueobjectoptional
  • require_pipeline_successobjectoptionalBlock the merge until every pipeline that applies to the merge request's head has succeeded. null inherits.
  • squash_optionobjectoptional
  • stack_auto_retargetobjectoptional
  • stack_enforce_merge_orderobjectoptional
  • stack_restack_strategyobjectoptional
  • unresolved_threadsobjectoptional

Refuses

  • 400Invalid request
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found
  • 409The request conflicts with the resource's current state; `reason` says how

Receive

The same shape update_repo_mr_settings returns — 13 fields. Go to it

Show it here
  • allow_merge_without_pipelineobjectoptionalWhile require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Absent inherits.
  • block_on_rejectionobjectoptional
  • delete_source_branch_defaultobjectoptional
  • merge_methodobjectoptional
  • override_group_settingsobjectoptional
  • require_issue_criteria_greenobjectoptional
  • require_linked_issueobjectoptional
  • require_pipeline_successobjectoptionalBlock the merge until every pipeline that applies to the merge request's head has succeeded. Absent inherits.
  • squash_optionobjectoptional
  • stack_auto_retargetobjectoptional
  • stack_enforce_merge_orderobjectoptional
  • stack_restack_strategyobjectoptional
  • unresolved_threadsobjectoptional
DELETE/groups/{group_id}/mr-settingsmr_settings:manage

Delete MR settings for a group

delete_group_mr_settingsDELETE /groups/{group_id}/mr-settingsOpen in the API reference

Token scope

mr_settings:manage

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found
  • 409The request conflicts with the resource's current state; `reason` says how

Receive

No body.

Limits

LimitValueWhat happens at it
Settings per scopeOne per repository, one per groupThere is nothing to run out of: an update either creates that one set or edits it, and a reset removes it
Inheritance depthEvery group above the repository, however deepThe walk stops early only where a group turns its own override switch on

None of these calls counts against a plan quota.

Troubleshooting

400 BAD_REQUESTmentioning repo_id or group_id
Cause
The id in the URL is not a well-formed id — usually a path such as my-group/my-repo pasted where an id belongs
What to do
Use the id. A repository's or group's Settings → General tab shows it, and the by-path lookups take a path when a path is all you have
400 BAD_REQUESTon an update: At least one field must be provided, or Missing request body
Cause
You sent {}, or no body at all, so there is nothing to apply
What to do
Send at least one field. Sending null for a field is a change — it hands that field back to inheritance — but an empty object is not
400 BAD_REQUESTon an update: Invalid JSON: unknown variant …, expected one of …
Cause
A value for merge_method, squash_option or stack_restack_strategy is not one of the allowed ones, or is capitalised differently
What to do
Copy one of the lowercase values the message lists: merge_commit, rebase_merge, fast_forward; never, allow, encourage, require; signal, server_side
400 BAD_REQUESTon an update: Invalid JSON: unknown field …
Cause
The body names something that is not a setting — a typo, or a field name borrowed from somewhere else
What to do
Send only the names the update card lists. An unrecognised name is refused rather than skipped, so nothing was written and nothing is half-applied
400 BAD_REQUESTon an update: Invalid JSON: unknown field naming approvals_required, prevent_author_approval, prevent_committer_approval or reset_approvals_on_push
Cause
Approval requirements are no longer merge request settings
What to do
Define an approval rule for the count, and set the approval policy for who may not approve and whether approvals reset on a push. Values you had set are already there, as a rule named Approvals and the policy on the same group or repository
401 UNAUTHORIZED
Cause
No credential was sent, or the one sent is invalid or expired
What to do
Sign in, or send a valid token. An anonymous read succeeds only on a public repository or group; an internal or private one answers 401 rather than 404, and every change needs a credential whatever the visibility. A signed-in non-member of a private resource gets 404
403 FORBIDDEN: PERMISSION_REQUIRED: policies.manage is needed here
Cause
You can read the repository or group but you are not Maintainer on it
What to do
Ask an Owner or Maintainer to raise your level or make the change. Maintainer on any group above the repository is enough
403 INSUFFICIENT_SCOPE
Cause
The token is valid but was minted without the scope this call needs
What to do
Mint a new token carrying mr_settings:manage. Scopes are fixed at mint time and cannot be added to an existing token
403 FORBIDDEN: Access token is not authorized for this resource
Cause
The token is bound to one repository or one group subtree, and this resource is outside it
What to do
Use a token bound to this resource, or an unbound one. A repository-bound token cannot configure the group above it
404 NOT_FOUNDon a repository or group you are sure exists
Cause
Either it is private and you are not a member, or the id is wrong
What to do
A private resource answers 404 rather than 403 to a non-member so that its existence stays private. Check your membership, then check the id
The settings saved but merges behave exactly as before
Cause
override_group_settings is off, so the repository's stored values are ignored and the group's policy applies
What to do
Turn the switch on in the same call, or in the web app before you save. The values you saved are still there and take effect the moment it goes on
A group's policy does not reach one repository
Cause
That repository overrides it — its switch is on and it sets the field itself
What to do
Read the repository's settings: they come back resolved, so what you see is what applies. Clear the repository's field with null, or reset the repository entirely
Turning a switch off on the repository did not restore the group's value
Cause
false is an explicit override meaning not this, whatever the group says
What to do
Send that field as null to inherit it again, or reset the repository's whole set to inherit everything
A nested group stopped inheriting from the group above it after one edit
Cause
Saving the form in the web app writes every field on it, so that group now sets them all
What to do
Send only the fields you want through the CLI or the API, with null for the ones that should inherit. Reset the group to start again from pure inheritance
400 BAD_REQUESTon merge: Rebase merge method is not yet supported
Cause
The effective method is rebase_merge, or the merge asked for it
What to do
Set merge_method to fast_forward or merge_commit. For linear history, have authors rebase the branch themselves and merge fast-forward
400 BAD_REQUESTon merge: Squashing is not allowed for this repository
Cause
squash_option is never and the merge asked to squash
What to do
Drop the squash flag, or change the policy to allow if squashing should be available here
400 BAD_REQUESTon merge: Merge blocked: naming a rule, M of N approval(s)
Cause
An approval rule that applies to the target branch does not have enough approvals that count
What to do
Run the merge check: it lists every approval with whether it counts and why not. See Approval rules
400 BAD_REQUESTon merge: Merge blocked: N unresolved discussion thread(s) must be resolved before merge
Cause
unresolved_threads is on and threads are open
What to do
Resolve them on the merge request. Every thread counts, not only the ones on the diff
400 BAD_REQUESTon merge: Merge blocked: N reviewer(s) have rejected this merge request
Cause
block_on_rejection is on and a reviewer has requested changes
What to do
The named reviewer withdraws or changes their verdict. A new push does not clear a rejection — somebody has to re-review
400 BAD_REQUESTon merge: Merge blocked: this merge request must reference at least one work item
Cause
require_linked_issue is on and nothing is linked
What to do
Reference a work item in the merge request's title or description — #412 on its own is enough, it does not have to say Closes
The same refusal although the description does name #412
Cause
A link is written only when both the merge request's author and whoever last touched it can see the item. A reference to an item that does not exist, or that either of them cannot read, is dropped with no link and no error. A body naming more than 50 items also loses the tail
What to do
Open the number yourself and confirm it resolves to a work item. Reference an item in a repository both of them can read, and write group/repo#412 when the item lives outside this repository
400 BAD_REQUESTon merge, naming acceptance criteria that are not green
Cause
require_issue_criteria_green is on and a required criterion is not satisfied at the head commit
What to do
Run the pipeline that reports the criterion against this head, or tick it if it is a manual criterion. A pass recorded at an older commit does not count, and only the work items this merge request closes are read — a bare #412 link is not one of them
409 PIPELINES_NOT_GREENon merge: Waiting for this merge request's pipelines to be scheduled
Cause
The head was pushed moments ago and the platform is still deciding which pipelines apply, or the change that moved the head never reached it
What to do
Wait a few seconds and run the merge check again. If it stays like this, run the merge request's pipelines, which decides them for the current head
409 PIPELINES_NOT_GREENon merge: No pipeline applies to this merge request, and the repository requires one to pass
Cause
require_pipeline_success is on and no pipeline's triggers match this merge request — none is defined, or they filter on branches or paths this change does not touch
What to do
Add a pipeline trigger that runs for merge requests, or turn on allow_merge_without_pipeline if merge requests with nothing to build should merge
409 PIPELINES_NOT_GREENon merge: Pipelines are still running or Waiting for pipelines to start
Cause
A pipeline that applies has not finished
What to do
Wait for it. The merge check names each one, and reading the merge request's pipelines gives each one's run
409 PIPELINES_NOT_GREENon merge: Pipelines did not pass: naming one with a status
Cause
A run that tested this head failed, timed out or was cancelled. Only a succeeded run — with or without warnings — passes
What to do
Fix what the run reported and push, or retry the run once the cause is external. Every pipeline that applies has to pass, not just the latest one
409 PIPELINES_NOT_GREENon merge: naming a pipeline that could not start
Cause
The pipeline applies but no run started — its monthly runs are used up, its definition does not resolve, a connection it needs is missing, or the merge result conflicts
What to do
Fix the cause the message names, then run the merge request's pipelines again. A conflicting merge result clears once the conflicts are resolved and the branch is pushed
403 FORBIDDENon merge: Merge blocked: downstack merge request !N must be merged first
Cause
stack_enforce_merge_order is on — it is on by default — and this merge request targets the source branch of another open one
What to do
Merge the one it names first, then this one. If a stack is deliberately being landed out of order, turn stack_enforce_merge_order off for the repository
409 CONFLICTon merge: Fast-forward not possible: source branch must be rebased onto target
Cause
The method is fast_forward, which adds no merge commit and needs the source directly ahead of the target — so any commit on the target the source does not have refuses the merge
What to do
Rebase the source branch onto the target and push, then merge. If that is too strict for this repository, change the method to merge_commit. The merge check shows the same thing ahead of time as a failed merge_method item
A settings change had no effect on one open merge request
Cause
The merge check is computed when you ask for it, so a page loaded before the change is showing the old answer
What to do
Reload the merge request or run the merge check again. There is no snapshot to invalidate
A merge used a method the repository does not set
Cause
The merge request named a method for that merge, which wins over the repository's default
What to do
Restrict who may merge by protecting the target branch. The merge method is a default, not a constraint
The merge check shows a failed item but the merge still goes through
Cause
Two reports are non-blocking on purpose: the one saying the target branch moved since the pipeline ran, and the one saying a stacked branch needs rebasing
What to do
Read the item's own text. Re-run the pipeline, or rebase the branch, to clear it — neither is stopping the merge, and neither is a policy you can switch off separately
A dependent merge request in a stack was never rebased
Cause
stack_restack_strategy is server_side but the rebase conflicted, or the merge request is two or more levels above the one that merged, or stack_auto_retarget is off and the cascade never ran
What to do
Rebase and force-push it by hand. Server-side restacking needs auto-retarget on, covers direct dependents only, and abandons a rebase that conflicts
The source branch was not deleted after a merge
Cause
Either the merge asked for something else explicitly — delete_source_branch_default is only the fallback — or the automatic retarget of a dependent merge request did not complete, which cancels the delete so nothing is left pointing at a branch that has gone
What to do
Retarget or merge the dependents, then delete the branch from the branches list. The delete is best-effort and a failure at that step is not reported on the merge, so check the list rather than assuming

Examples

An organisation baseline on the root group — resolved discussion, no outstanding change requests and a green pipeline everywhere, with squashing encouraged. Pair it with an approval rule on the same group for the review itself:

json
{
"merge_method": "merge_commit",
"squash_option": "encourage",
"delete_source_branch_default": true,
"unresolved_threads": true,
"block_on_rejection": true,
"require_pipeline_success": true
}

One repository beneath it that holds only documentation and has no pipeline, keeping everything else the organisation asks for:

json
{
"override_group_settings": true,
"require_pipeline_success": false
}

The same repository later gets a pipeline and wants the group's gate back, without giving up the override on anything else:

json
{
"require_pipeline_success": null
}

An organisation whose pipelines run only when code changes, so that a merge request touching nothing but documentation has no pipeline — and should merge anyway:

json
{
"require_pipeline_success": true,
"allow_merge_without_pipeline": true
}

A repository that wants the cleanest possible history — one commit per merge request, no merge commits:

json
{
"override_group_settings": true,
"merge_method": "fast_forward",
"squash_option": "require"
}

FAQ

Does changing a setting affect merge requests that are already open?

Yes, immediately. Nothing is copied onto a merge request when it is opened, so the next merge check is evaluated against the policy as it stands. Turning a gate on blocks open merge requests that do not pass it; turning it off unblocks them.

Can somebody bypass the policy by choosing a different merge method?

For the method, yes — a merge may name its own, and the repository's value is only the default. The gates are different: each is checked at the merge itself and not merely in the merge check, so nothing merges past one. If the method matters, restrict who may merge with a branch rule.

Who can see my merge policy?

Anyone who can see the repository or group. On a public one that includes people who are not signed in; on an internal one, everybody signed in; on a private one, its members. Changing the settings needs Maintainer.

Which pipelines does a merge request wait for?

Every pipeline that applies to its head commit: each one whose triggers matched the merge request, and each one a run of which tested that commit, such as a push to the source branch. A run another merge request started for the same commit does not count — it tested a merge into a different target. A pipeline you started by hand counts once its run tested the head.

Is there a per-merge-request override?

Three, all given on the merge call: the merge method, the squash flag and whether to delete the source branch. The method always wins, and so does an explicit delete-the-branch answer — the setting is only the fallback when the merge does not say. The squash flag is negotiated with squash_option instead: never refuses a merge that asks to squash, require squashes regardless of what the flag says, and allow and encourage let the flag decide.

How do I tell which group a value came from?

Read the repository for the resolved answer, then read each group up the chain — a group's read shows only what that group declares, so the first one that names the field is the one that owns it.

Is a settings change recorded anywhere?

Yes. Every change and every reset is recorded in the audit log, with who made it and when. Reads are not — see Audit log for how to read the trail back.

  • Merge requestsopening one, the merge check, merging, and stacks
  • Approval ruleshow many approvals a merge request needs, whose count, and who may not approve
  • Reviews and approvalsrequesting review, approving, rejecting and withdrawing a verdict
  • Merge request discussionthreads and resolving them, which is what unresolved_threads blocks on
  • Repository rulesbranch protection and push rules; the merge threshold that decides who may merge at all lives there
  • Proof of Donethe acceptance criteria require_issue_criteria_green evaluates, and how a pipeline proves one
  • Pipelinesthe runs require_pipeline_success waits for
  • Groupsthe nesting that decides which policy a repository inherits
  • Audit logwho changed the policy, and when