Add mergeRequestResolveConflicts GraphQL mutation
What does this MR do and why?
Dependencies
This MR now contains only the mergeRequestResolveConflicts GraphQL mutation itself. The shared gates and the read-side sections field it depends on split off into two other MRs:
- !256973 (closed) moves the push-access, mergeability and resolvable-in-UI checks, the usage counter, and the error mapping into
MergeRequests::Conflicts::ResolveService. This MR is stacked on it (retargeted onto its branch) because it needs theServiceResponsethe service returns there. - !256975 (closed) adds the
sectionsfield toMergeRequest.conflictFiles, which is where thesectionIdvalues this mutation's input takes come from. This MR is not stacked on it. One request spec context that reads section ids from that field is left out here until it merges, and will be added back once this MR is rebased onto master.
Adds a mergeRequestResolveConflicts GraphQL mutation: the conflicts controller's resolve_conflicts action for API clients. MergeRequests::Conflicts::ResolveService (see !256973 (closed)) checks that the user can push to the source branch (ListService#can_be_resolved_by?), that the merge request is still unmergeable, and that its conflicts are resolvable in the UI (can_be_resolved_in_ui?), and returns a ServiceResponse that this mutation maps to payload errors. The service produces the same merge commit of the target branch into the source branch the web UI produces.
Per conflicting file the input takes either a side for each conflict section (HEAD = "our changes", ORIGIN = "their changes"), keyed by the section ids from MergeRequest.conflictFiles.sections (!256975 (closed)), or the file's full resolved content. An optional commit message defaults to the UI's Merge branch '…' into '…' message. Resolution and pre-receive errors come back in errors.
Until now conflicts could only be resolved through the web UI, which is session-authenticated. API clients, concretely the GitLab mobile app, which already reads MergeRequest.conflictFiles (added in 19.4 as an experiment) to show conflicts natively, were left with a browser hand-off for the resolution step. With this mutation the app resolves conflicts in place with the same semantics as the web page.
Both the mutation and the conflictFiles.sections field are experiments (milestone 19.5).
Related: MergeRequest.conflictFiles field (19.4 experiment). This is its write counterpart.
Screenshots or screen recordings
None (API-only).
How to set up and validate locally
- Create a merge request whose branches conflict (e.g. the spec fixture branches
conflict-resolvable→conflict-startin the test repository). - Fetch the sections and their ids:
{
project(fullPath: "group/project") {
mergeRequest(iid: "1") {
conflictFiles {
ourPath
theirPath
sections {
sectionId
conflict
lines { type oldLine newLine text }
}
}
}
}
}- Run the mutation with the returned
sectionIdvalues:
mutation {
mergeRequestResolveConflicts(input: {
projectPath: "group/project",
iid: "1",
commitMessage: "Resolve the conflicts",
files: [
{ oldPath: "files/ruby/popen.rb", newPath: "files/ruby/popen.rb",
sections: [{ sectionId: "<sectionId from step 2>", resolution: HEAD }] },
{ oldPath: "files/ruby/regex.rb", newPath: "files/ruby/regex.rb", content: "resolved\n" }
]
}) { errors mergeRequest { id detailedMergeStatus } }
}- The source branch gains a merge commit with both heads as parents; the MR becomes mergeable after the mergeability re-check.
MR acceptance checklist
Evaluated against the MR acceptance checklist.
- Specs:
spec/graphql/mutations/merge_requests/resolve_conflicts_spec.rb,spec/requests/api/graphql/mutations/merge_requests/resolve_conflicts_spec.rb. - Generated: GraphQL reference docs, introspection schemas, fine-grained token GraphQL docs.