Merge request settings
- Read the policy a repository applies
- Read what a group sets on its own
- Set a repository's own merge policy
- Hold a merge while a reviewer asks for changes
- Block a merge on discussion, work items or a pipeline
- Decide what happens when no pipeline applies
- Change how stacked merge requests behave
- Hand one field back to the group
- Set one policy for every repository in a group
- Reset a repository to the group policy
- Clear a group's 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:readto read,mr_settings:manageto change or reset. Scopes are fixed when a token is minted, so a script that only audits policy should carrymr_settings:readand 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 defaultTurn 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 = trueThe 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
| Setting | Default | What it does |
|---|---|---|
override_group_settings | false | Whether this repository's own values count at all. On a group, it stops that group from inheriting from its own parents |
merge_method | merge_commit | The 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_option | allow | never 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_default | false | What "delete the source branch" defaults to when a merge does not say |
unresolved_threads | false | Every discussion thread on the merge request must be resolved |
block_on_rejection | false | A reviewer who has requested changes blocks the merge until they withdraw or change that verdict. A later push does not clear it |
require_linked_issue | false | The merge request's title or description must reference at least one work item. Any reference counts, closing or not |
require_issue_criteria_green | false | Every 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_success | false | Every 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_pipeline | false | Only while require_pipeline_success is on: whether a merge request that no pipeline applies to may merge. Off, it is blocked |
stack_auto_retarget | true | When 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_order | true | A 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_strategy | signal | signal 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
| When | What is checked then |
|---|---|
| The merge check runs, and again at the merge itself | merge_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 only | squash_option, and delete_source_branch_default as a fallback |
| Immediately after a merge | stack_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.
gitmoap mr-settings get-repo --repo-id <id>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.
gitmoap mr-settings get-group --group-id <id>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.
gitmoap mr-settings update-repo --repo-id <id>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.
{ "override_group_settings": true, "block_on_rejection": true}Open the repository's Settings → Merge requests and turn on Block merging when changes are requested.
gitmoap mr-settings update-repo --repo-id <id>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.
| Field | The merge is blocked while |
|---|---|
unresolved_threads | Any discussion thread on the merge request is unresolved — an inline thread on the diff or a top-level one |
require_linked_issue | The 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_green | A 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_success | A 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 |
{ "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.
{ "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.
gitmoap mr-settings update-repo --repo-id <id>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.
gitmoap mr-settings update-repo --repo-id <id>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.
{ "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.
gitmoap mr-settings update-repo --repo-id <id>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.
gitmoap mr-settings update-group --group-id <id>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.
gitmoap mr-settings delete-repo --repo-id <id>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.
gitmoap mr-settings delete-group --group-id <id>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)
Token scope
mr_settings:read
Send
Path
- repo_iduuidRepository ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Repository not found
Receive
- allow_merge_without_pipelinebooleanWhile
require_pipeline_successis 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)
Token scope
mr_settings:manage
Send
Path
- repo_iduuidRepository ID
Body
- allow_merge_without_pipelineobjectoptionalWhile
require_pipeline_successis on: whether a merge request that no pipeline applies to may merge. Off — the default — blocks it.nullinherits. - 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.
nullinherits. - squash_optionobjectoptional
- stack_auto_retargetobjectoptional
- stack_enforce_merge_orderobjectoptional
- stack_restack_strategyobjectoptional
- unresolved_threadsobjectoptional
Refuses
400Invalid request401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Repository not found409The request conflicts with the resource's current state; `reason` says how
Receive
- allow_merge_without_pipelineobjectoptionalWhile
require_pipeline_successis 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
Token scope
mr_settings:manage
Send
Path
- repo_iduuidRepository ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Repository not found409The 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)
Token scope
mr_settings:read
Send
Path
- group_iduuidGroup ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Group 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_successis 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)
Token scope
mr_settings:manage
Send
Path
- group_iduuidGroup ID
Body
- allow_merge_without_pipelineobjectoptionalWhile
require_pipeline_successis on: whether a merge request that no pipeline applies to may merge. Off — the default — blocks it.nullinherits. - 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.
nullinherits. - squash_optionobjectoptional
- stack_auto_retargetobjectoptional
- stack_enforce_merge_orderobjectoptional
- stack_restack_strategyobjectoptional
- unresolved_threadsobjectoptional
Refuses
400Invalid request401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Group not found409The 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_successis 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
Token scope
mr_settings:manage
Send
Path
- group_iduuidGroup ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Group not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
Limits
| Limit | Value | What happens at it |
|---|---|---|
| Settings per scope | One per repository, one per group | There is nothing to run out of: an update either creates that one set or edits it, and a reset removes it |
| Inheritance depth | Every group above the repository, however deep | The 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-repopasted 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
nullfor 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_optionorstack_restack_strategyis 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
401rather than404, and every change needs a credential whatever the visibility. A signed-in non-member of a private resource gets404
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
404rather than403to 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_settingsis 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
falseis an explicit override meaning not this, whatever the group says- What to do
- Send that field as
nullto 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
nullfor 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_methodtofast_forwardormerge_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_optionisneverand the merge asked to squash- What to do
- Drop the squash flag, or change the policy to
allowif 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_threadsis 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_rejectionis 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_issueis on and nothing is linked- What to do
- Reference a work item in the merge request's title or description —
#412on its own is enough, it does not have to sayCloses
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#412when the item lives outside this repository
400 BAD_REQUESTon merge, naming acceptance criteria that are not green
- Cause
require_issue_criteria_greenis 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
#412link 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_successis 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_pipelineif 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_orderis 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_orderoff 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 failedmerge_methoditem
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_strategyisserver_sidebut the rebase conflicted, or the merge request is two or more levels above the one that merged, orstack_auto_retargetis 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_defaultis 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:
{ "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:
{ "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:
{ "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:
{ "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:
{ "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.
Related
- 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_threadsblocks 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_greenevaluates, and how a pipeline proves one - Pipelinesthe runs
require_pipeline_successwaits for - Groupsthe nesting that decides which policy a repository inherits
- Audit logwho changed the policy, and when