feat(mr): create diff comments

What does this MR do?

Adds --file, --line, and --old-line flags to glab mr note create for placing diff comments on specific files and lines in the latest MR diff version.

  • --file targets a file path in the MR diff
  • --line targets a new-side line (single number or N:M range for multiline)
  • --old-line targets an old-side (removed) line
  • Omitting both --line and --old-line creates a file-level comment

New packages

  • internal/diff — unified diff parser (Parse, FindNewLine, FindOldLine)
  • internal/commands/mr/mrutils/position.goGetLatestDiffVersion, FindFileDiff, BuildDiffPosition, ParseLine

Mutual exclusivity

Flag group Reason
--line / --old-line Cannot target both sides simultaneously
--reply / --file Reply adds to existing thread; diff comment creates new one
--unique / --file Dedup searches flat notes by body, not diff positions

Validation

  • --line or --old-line without --file → error
  • Empty diff → error
  • Line not found in diff → error
  • Multiline range end validated
  • --line 0 or negative values → error

Testing strategy

Unit tests

All new logic is covered by unit tests (make test-changed or go test ./internal/...):

  • internal/diff/parse_test.go — unified diff parser: line mapping, edge cases, empty diffs
  • internal/commands/mr/mrutils/position_test.goBuildDiffPosition, ParseLine, FindFileDiff
  • internal/commands/mr/note/mr_note_create_test.go — flag validation, mutual exclusivity, integration with mocked API

Manual verification

After building (make build), you can post diff comments on this MR to verify end-to-end:

# New-side diff comment on line 17 of a new file
bin/glab mr note create 3171 --file internal/commands/mr/mrutils/position.go --line 17 -m "Test: new-side diff comment"

# New--side range diff comment
bin/glab mr note create 3171 --file internal/commands/mr/mrutils/position.go --line 14:15 -m "Test: new-side range comment"

# Old-side diff comment on a removed line (old line 27)
bin/glab mr note create 3171 --file internal/commands/mr/note/mr_note_create.go --old-line 27 -m "Test: old-side diff comment"

Both commands produce a link to the created note. The comments appear inline in the MR diff view at the expected file and line.

Out of scope

Old-side multiline ranges

--old-line currently accepts a single line number only (no range syntax like --line 10:15). Supporting old-side multiline ranges would require:

  1. Changing --old-line from int to string to accept range syntax (e.g., --old-line 5:10)
  2. Adding a parallel lineCode path that uses Type: "old" and encodes old-side line numbers in the sha1_oldline_0 format
  3. A new LineRange construction with Type: "old" on both start/end positions
  4. Tests for all combinations

The GitLab API supports it (LinePositionOptions has both OldLine and Type: "old"), but the use case is niche — commenting on a range of removed lines. This can be added in a follow-up if there is demand.

Binary file detection

If --file points to a binary file (e.g., an image), the diff content is empty and the error message is "diff for image.png is empty", which does not explain that binary files are unsupported. A follow-up could check the Binary field on the diff object and return a clearer error like "binary files do not support diff comments".

Closes #8285 (closed)

Merge request reports

Loading