gitdiff

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package gitdiff shells out to the git CLI to enumerate changed files and load per-file unified diffs for prereview. Shelling out (vs. a Go git library) is deliberate — it respects the user's local git config (rename detection thresholds, autocrlf, etc.) so what prereview shows matches what `git diff` shows in their terminal.

Index

Constants

This section is empty.

Variables

View Source
var HighlightCSS = func() string {
	var buf bytes.Buffer
	if err := chromaFormatter.WriteCSS(&buf, chromaStyle); err != nil {
		return ""
	}
	return buf.String()
}()

HighlightCSS returns the chroma stylesheet for the active style. Computed once at package-init time; main.go serves it as /syntax.css.

Functions

func ChangedCount

func ChangedCount(entries []FileEntry) int

ChangedCount returns how many entries have a non-empty Status — i.e. how many of these files differ from the active base. Useful for the drawer header ("N files · M changed").

func CurrentBranch

func CurrentBranch(repo string) string

CurrentBranch returns the short name of the checked-out branch (e.g. "main", "feature/foo"). Returns "HEAD" in a detached state (which is what `git rev-parse --abbrev-ref HEAD` emits there). Errors return an empty string — callers should display "current branch" or similar generic copy when the result is blank.

func HighlightLine

func HighlightLine(filename, content string) template.HTML

HighlightLine is retained for callers that want to highlight a single line in isolation (currently none in the project — kept for backward compatibility and potential ad-hoc use). For bulk highlighting use HighlightLines, which amortises tokenizer startup across all lines.

func HighlightLines

func HighlightLines(filename string, contents []string) []template.HTML

HighlightLines tokenises every line of `contents` in a single pass and returns the syntax-highlighted HTML per line. Falls back to per-line HTML escape on tokenizer errors. Empty input returns nil.

Joining with '\n' before tokenising lets chroma understand line transitions naturally — string tokens that span lines keep their type, etc. The output is then split back into per-line groups by walking the token stream and splitting tokens on their '\n' bytes.

func IsMarkdownPath

func IsMarkdownPath(path string) bool

IsMarkdownPath reports whether path is a Markdown file by extension.

func IsValidRef

func IsValidRef(repo, ref string) bool

IsValidRef reports whether `ref` resolves to a commit in this repo. Used by the runtime base picker to validate user-typed refs before swapping state.Base. Treats both branch names and commit-ish refs (HEAD~3, abc1234) uniformly via `git rev-parse --verify`.

func ListBranches

func ListBranches(repo string) []string

ListBranches returns the short names of every local branch in the repo, sorted alphabetically. Used to populate the base-picker dropdown — `git for-each-ref` runs in microseconds even on big repos, so calling it from every Mount is fine.

func ListRemoteBranches

func ListRemoteBranches(repo string) []string

ListRemoteBranches returns the short names of every remote-tracking branch (e.g. "origin/main"), sorted. The symbolic "<remote>/HEAD" entries are skipped — they're aliases, not useful review bases. Lets the base picker offer "review since I branched off origin/main" without a freeform input.

Types

type DiffLine

type DiffLine struct {
	OldNum  int    // 0 when the line doesn't exist on the old side
	NewNum  int    // 0 when the line doesn't exist on the new side
	Kind    string // "add" | "del" | "ctx"
	Content string // raw line text, no leading +/-/space, no trailing \n
	// HighlightedContent is Content rendered as syntax-highlighted HTML
	// spans via chroma. Templates render this instead of Content; the
	// raw Content stays around for CSV exports and other consumers that
	// don't want HTML markup.
	HighlightedContent template.HTML
}

DiffLine is one rendered line in the viewer. Exactly one of OldNum / NewNum may be zero (for additions and deletions respectively); for context lines (Kind == "ctx") both are populated.

func CollapseToHunks

func CollapseToHunks(lines []DiffLine, ctx int) []DiffLine

CollapseToHunks turns a full-file line list (every line tagged add/del/ctx, as LoadDiff produces with -U999999) into a true diff view: only changed lines plus `ctx` unchanged lines on each side of a change. Each maximal run of dropped unchanged lines is replaced by a single synthetic fold line:

Kind == "fold", Content == "<N>"  (N = number of skipped lines)

The fold line's OldNum/NewNum are set to the first skipped line's numbers so the template can derive a stable, collision-free data-key (gaps never overlap).

If the input contains no add/del line (an unchanged file — shown via the all-files scope toggle or the clean-tree fallback) there is no diff to collapse, so the input is returned unchanged. Callers treat that as "show the whole file".

type FileDiff

type FileDiff struct {
	Path     string
	Lines    []DiffLine
	IsBinary bool   // true when git reports "Binary files differ" — Lines is nil
	Note     string // e.g. "file added", "file deleted"; informational, may be empty
	// MaxLineChars is the longest Content length (in characters) across
	// all DiffLines. Used by the template to set `.code` width in `ch`
	// units up front; without this hint, the browser would compute
	// `width: max-content` for every line-row to determine the diff
	// container's scrollable extent — expensive on big files (950+
	// rows) and the dominant cost in perceived vertical-scroll lag.
	MaxLineChars int
	// MarkdownBlocks is the rendered-Markdown view of the current
	// (new-side) file: top-level blocks tagged with their source line
	// range. Populated only for .md/.markdown paths; nil otherwise.
	// Computed once per load and cached with the FileDiff.
	MarkdownBlocks []MarkdownBlock
}

FileDiff is the full-file, line-by-line diff for one path.

func LoadDiff

func LoadDiff(repo, base, path string) (*FileDiff, error)

LoadDiff returns the full-file diff for one path against base.

Uses `git diff --no-color -U999999` so every line of the file appears (additions, deletions, context). For files that are pure additions (A) or pure deletions (D) git produces a diff against /dev/null, which the parser handles naturally — every line is "add" or "del" respectively.

Files larger than maxRenderableFileBytes short-circuit with a Note instead of loading content — the viewer renders the note and skips the per-line markup entirely.

type FileEntry

type FileEntry struct {
	Path         string // post-rename / current path
	Status       string // "A","M","D","R","C","T","U"
	Renamed      string // pre-rename path for R/C; empty otherwise
	Added        int    // lines added vs base; -1 means "binary / unknown"
	Deleted      int    // lines deleted vs base; -1 means "binary / unknown"
	CommentCount int    // populated by the controller, not by gitdiff
}

FileEntry is one row in the changed-files list.

func ListFiles

func ListFiles(repo, base string) ([]FileEntry, error)

ListFiles enumerates every file currently in the working tree (tracked + untracked-non-ignored), annotated with diff information vs base where applicable.

This is the foundation of "show all files; diff is an overlay": prereview now always lets the reviewer browse the whole repo, with diff metadata layered on top of the files that happen to differ from base. Files unchanged vs base appear in the list with Status="", Added=0, Deleted=0 — the template renders them without a diff badge.

Deleted files (present in base, absent in the working tree) are intentionally omitted — they don't exist in the repo "right now". Reviewers who care about deletions can flip the base or read git history directly.

base may be "HEAD", a branch name, or any revision spec git accepts. If base is empty, diff annotation is skipped and every entry is bare.

type MarkdownBlock

type MarkdownBlock struct {
	HTML      template.HTML
	StartLine int
	EndLine   int
}

MarkdownBlock is one independently-commentable rendered unit plus the 1-based, inclusive SOURCE line range it came from. The line range is what makes rendered-Markdown commenting line-accurate: a comment placed on a unit anchors to these real source lines, so it round-trips with the raw line view and the CSV — nothing is renumbered.

Single-line nodes (heading, code fence, blockquote, …) are one block. Containers are descended ONE level so review comments can target a single line: a list yields one block per list item, a table one block for the header row plus one per body row, and a paragraph that spans multiple SOURCE lines yields one block per source line (these docs are authored one-sentence-per-line, which CommonMark soft-wraps into a single paragraph — splitting restores per-line commenting). An inline span that crosses a soft line-break degrades to literal text on that one line; it never produces invalid HTML.

func RenderMarkdownBlocks

func RenderMarkdownBlocks(src []byte) []MarkdownBlock

RenderMarkdownBlocks parses src and returns each commentable unit as safe HTML tagged with its source line span. Empty input → nil.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL