Comments and per-commit diffs — implementation notes
Two collaboration/inspection features that share the existing diff and comment machinery rather than adding parallel copies of it.
Issue and merge-request comments
General (non-line) discussion comments on issues and merge requests.
Data model
- Issues — a dedicated
IssueCommententity /issue_commentstable (V21__issue_comments.sql):id,issue_id(FK,ON DELETE CASCADE),author_id(FK,ON DELETE CASCADE),body,created_at. MirrorsMergeRequestCommentminus the diff-anchor columns. - Merge requests — general comments reuse the existing
merge_request_commentstable.V22__merge_request_discussion_comments.sqldrops theNOT NULLonfile_path; a general comment hasfile_path = nullandold_line = new_line = -1, while a per-line review comment keeps its path and line anchor. This avoids a second MR-comment table and lets one delete endpoint serve both kinds.
Services
IssueCommentService—add/list/delete. Any reader may comment; author, owner, or collaborator may delete (AccessPolicy.canWrite). Empty bodies are rejected withInvalidIssueException(→ 400).MergeRequestCommentService.addGeneral— sibling of the line-anchoredadd, but with no diff-anchor check: it setsfilePath = nulland both line numbers to-1.
Web layer
IssueResource:POST {number}/commentsandPOST {number}/comments/{commentId}/delete; the detail view passes the comment list plusloggedIn/currentUserId/canModeratetoissue.html. The delete control renders when the viewer is the comment author orcanModerate(AccessPolicy.canWrite— owner, collaborator or org member), matching what the service enforces;owner(admin) alone would hide it from collaborators.MergeRequestResource:POST {number}/discussionfor general comments; deletion reuses the existing{number}/comments/{commentId}/delete.detailsplitscommentService.list(mr)into line comments (filePath != null) and discussion (filePath == null) — the line-matching stream now filters on the line-comment sublist, so a general comment's nullfilePathnever reaches the.equals(...)match (which would NPE).
Rendering is shared markup: both issue.html and mergeRequest.html render the
thread with the same comment / comment-head / comment-body classes already
used by per-line comments, so no new CSS is required.
Per-commit diff view
Reuses the merge-request diff core instead of introducing a new differ.
GitMergeService.commitDiff(barePath, commitId)— resolves the commit and its first parent (or anEmptyTreeIteratorfor a root commit), scans the two trees with aDiffFormatter, and runs eachDiffEntrythrough the same privateformatEntryused by branch diffs, returning the existingDiffViewrecord. A well-formed but unknown id (including the all-zero id) surfaces asMissingObjectExceptionduringresolveand is mapped toOptional.empty()(→ 404) rather than a 500.GitBrowseService.commit(barePath, rev)— single-commit metadata (CommitInfo) for the page header, with the sameMissingObjectException→ empty handling.RepositoryResource:GET commit/{id}renderscommit.html(read-only diff, no comment affordances). Rows incommits.htmllink to it. The route prefixcommit/does not collide with the existingcommits/{ref}listing.
The diff is always computed live from git; nothing is duplicated into the database, matching the merge-request diff decision.