Add experimental GraphQL mutation for latest diff comments

Summary

  • Adds a new CreateLatestDiffNote GraphQL mutation (marked experiment) that creates diff notes on merge requests with minimal parameters (body, filePath, and newLine/oldLine)
  • Automatically resolves SHAs (baseSha, startSha, headSha), file paths (oldPath, newPath), and positionType from the latest merge request diff
  • Includes a required headSha argument to guard against race conditions
  • Uses experiment: { milestone: '18.1' } so the interface can evolve without being bound by the deprecation policy

Why

The existing CreateDiffNote mutation (and REST discussions API) require callers to provide a full position with 7+ fields (3 SHAs, positionType, oldPath, newPath, line numbers) even though the backend already has all this information from the merge request diff. This new mutation reduces the required arguments to just 3-4 fields.

What's included

File Purpose
app/graphql/mutations/notes/create/latest_diff_note.rb New experimental mutation
app/services/merge_requests/resolve_diff_position_service.rb Service to resolve full position from minimal params
app/graphql/types/mutation_type.rb Mount with experiment: annotation
spec/requests/api/graphql/mutations/notes/create/latest_diff_note_spec.rb 18 mutation specs including parity test
spec/services/merge_requests/resolve_diff_position_service_spec.rb 11 service specs

Usage

mutation {
  createLatestDiffNote(input: {
    noteableId: "gid://gitlab/MergeRequest/1"
    body: "Your comment"
    filePath: "app/models/user.rb"
    newLine: 42
    headSha: "abc123..."
  }) {
    note { id body position { positionType filePath newLine } }
    errors
  }
}

Line arguments

Scenario Arguments Example
Added line (green) newLine only newLine: 42
Removed line (red) oldLine only oldLine: 15
Unchanged/context line (white) Both oldLine and newLine oldLine: 10, newLine: 10

Renamed files

For renamed files, either the old or new path can be used in filePath. The mutation automatically resolves both oldPath and newPath from the merge request diff.

Manual testing

Tested locally against gitlab-org/gitlab-test MR with:

  • A file with added and removed lines (test-file.txt)
  • A renamed file with content changes (CONTRIBUTING.md → Jimmy.md)
  • A pure rename with no content changes (PROCESS.md → RIP.md)
Test case Result
Comment on added line (newLine only)
Comment on removed line (oldLine only)
Comment on context/unchanged line (both lines)
Renamed file: comment on added line
Renamed file: comment on removed line
Renamed file: comment on context line
Renamed file: lookup by old path (CONTRIBUTING.md)
Renamed file: lookup by new path (Jimmy.md)
Pure rename (no diff content): line comment Expected — no diff lines exist (same behavior as existing CreateDiffNote mutation and REST API)
Stale headSha Returns error
Invalid filePath Returns error
Missing line arguments Returns error
Quick action (/label) in body Applied
Parity test vs CreateDiffNote Identical positions
Edited by Marc Shaw

Merge request reports

Loading