How open-code-review Handles Diff Viewing and Annotation: A Deep Dive into the Go Implementation
open-code-review implements a four-stage pipeline that extracts raw Git diffs, parses them into structured FileDiff and Hunk objects, maps comment positions to exact diff coordinates, and serves an interactive HTTP viewer for inline annotation.
The alibaba/open-code-review project provides a comprehensive solution for diff viewing and annotation across multiple code review platforms. By combining Git command execution with custom parsing logic, the tool transforms unified diff output into navigable data structures that enable precise inline commenting. Understanding how open-code-review handles diff viewing and annotation reveals the architectural patterns that support GitLab, Gerrit, and GitFlic integrations.
The Four-Stage Diff Processing Pipeline
The architecture separates concerns into distinct phases: extraction, parsing, position mapping, and visualization. Each stage operates on concrete Go structs defined in the internal/tool package.
Stage 1: Diff Extraction via Git Commands
The pipeline begins in internal/tool/file_read_diff.go with the loadDiffsByPath function. This utility executes git diff --no-ext-diff --no-textconv against a specified revision range to obtain a raw unified diff. The function captures stdout and handles repository path resolution, ensuring that binary diffs and external diff drivers are excluded for consistent text parsing.
Stage 2: Unified Diff Parsing
Once extracted, the raw diff string feeds into the parseDiff function within the same file. This parser segments the combined diff into per-file sections represented by the FileDiff struct, which tracks OldPath and NewPath metadata. Each FileDiff contains a slice of Hunk structs that record oldStart, newStart, line counts, and the actual text lines. These structures preserve the context lines required for accurate comment positioning.
Stage 3: Annotation Position Mapping
When users create comments, the system must translate file paths and line numbers into platform-specific diff positions. The internal/tool/code_comment.go file implements makeCommentPosition, which walks the parsed FileDiff and Hunk slices to compute the exact coordinates. This mapping accounts for added, removed, and context lines, generating position objects compatible with GitLab's diff position API or Gerrit's comment endpoints.
Stage 4: Interactive Viewer Server
The final stage exposes a local HTTP server defined in internal/viewer/server.go and internal/viewer/handler.go. The opencodereview view command (entry point in cmd/opencodereview/viewer_cmd.go) launches this server, serving HTML diff pages at the /diff endpoint and handling comment submissions via /comment. The viewer renders the parsed hunks with clickable "Add comment" controls that invoke the position mapping logic.
Core Implementation Details
Loading and Parsing Diffs in file_read_diff.go
The LoadDiffsByPath function returns a map[string]*FileDiff, keyed by new file paths for O(1) lookup during annotation. Each FileDiff struct encapsulates the complete change set for a single file, while nested Hunk structs maintain the line-range metadata necessary for mapping absolute line numbers to relative hunk positions.
// Load diffs for a specific revision range
diffsByPath, err := file_read_diff.LoadDiffsByPath("/path/to/repo", "main", "feature-branch")
if err != nil {
log.Fatalf("diff extraction failed: %v", err)
}
// Inspect a specific file's hunks
fd := diffsByPath["src/main.go"]
fmt.Printf("File %s contains %d hunks\n", fd.NewPath, len(fd.Hunks))
Computing Comment Positions in code_comment.go
The MakeCommentPosition function accepts a Comment struct (containing Path and Line) and the diffsByPath map. It iterates through the Hunks of the target FileDiff to locate which hunk contains the specified line number, then calculates the oldLine and newLine values required by the review platform's API.
comment := tool.Comment{
Path: "src/main.go",
Line: 42, // Line number in the new version
}
pos, err := code_comment.MakeCommentPosition(comment, diffsByPath)
if err != nil {
log.Fatalf("position mapping failed: %v", err)
}
fmt.Printf("Diff position: %+v\n", pos)
Serving the Diff Viewer
The viewer server integrates the parsed diff structures with a lightweight HTTP interface. When a browser requests a diff, the handler retrieves the pre-computed FileDiff objects and renders them with line anchors. If a diff cannot be located—such as when a repository is stripped or the revision range is invalid—the system logs a warning and falls back to a plain-text summary, ensuring that no comment data is lost.
Practical Usage Examples
Launch the interactive diff viewer from the command line to inspect changes before submitting a review:
# Start the local diff viewer server
$ opencodereview view --repo . --from origin/main --to HEAD
# Server available at http://localhost:8080/diff
Programmatically process diffs for custom CI integrations:
package main
import (
"log"
"github.com/alibaba/open-code-review/internal/tool"
)
func main() {
// Extract and parse
diffs, err := tool.LoadDiffsByPath(".", "HEAD~1", "HEAD")
if err != nil {
log.Fatal(err)
}
// Process each file's hunks
for path, fd := range diffs {
log.Printf("Processing %s with %d hunks", path, len(fd.Hunks))
}
}
CI Integration and Fallback Handling
The repository includes examples/gitflic_ci/post_review.py, which demonstrates how Python-based CI systems can leverage the Go diff engine. When the diff is unavailable—perhaps due to shallow clones or missing merge-base references—the integration falls back to posting summary comments rather than inline annotations. This graceful degradation ensures that review feedback persists even when the full diff viewing pipeline cannot execute.
Summary
- Diff extraction relies on
internal/tool/file_read_diff.goto execute Git commands and capture unified diff output. - Parsing logic converts raw diffs into
FileDiffandHunkstructs that preserve line-number metadata. - Annotation mapping uses
internal/tool/code_comment.goto translate absolute line numbers into platform-specific diff positions. - Viewer server provides an HTTP interface defined in
internal/viewer/server.gofor interactive diff browsing and commenting. - Fallback mechanisms ensure comments are posted as summaries when diff data is missing, as shown in the GitFlic CI example.
Frequently Asked Questions
How does open-code-review extract diffs from Git repositories?
The tool invokes git diff --no-ext-diff --no-textconv through the LoadDiffsByPath function in internal/tool/file_read_diff.go. This command generates a unified diff for the specified revision range while excluding binary files and external diff drivers, ensuring consistent text output for parsing.
What data structures does open-code-review use to represent diff hunks?
The system defines a FileDiff struct for each changed file, containing OldPath and NewPath fields along with a slice of Hunk structs. Each Hunk records oldStart, newStart, and the line-by-line changes, preserving context lines necessary for accurate comment positioning.
How are comment positions mapped to specific lines in a diff?
The MakeCommentPosition function in internal/tool/code_comment.go accepts a file path and line number, then traverses the Hunk slices within the corresponding FileDiff to calculate the exact diff coordinates. This produces position objects that code review platforms like GitLab or Gerrit can render as inline annotations.
Can open-code-review display diffs without a local Git repository?
No, the current implementation requires a local repository to execute git diff commands. However, if the diff cannot be generated—due to shallow clones or missing references—the system implements a fallback mechanism that posts comments as summary notes rather than inline annotations, ensuring feedback is not lost.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →