Skip to main content

Crate vcs_diff

Crate vcs_diff 

Source
Expand description

vcs-diff — the shared git-format unified-diff model and parser for the vcs-toolkit-rs workspace.

git diff and jj diff --git emit byte-identical git-format unified diffs, so vcs-git and vcs-jj share one model and one parser here rather than each carrying a copy that could silently drift. This is the foundational crate both depend on: std only, no async, no subprocess — pure data types and pure functions over text the wrapper crates obtained elsewhere.

§The surface

  • parse_diff — the entry point. Turns git-format diff text into one FileDiff per file; the same call serves git diff and jj diff --git output alike. Pure and total: arbitrary CLI bytes in, never a panic.
  • FileDiff — one file’s entry: its ChangeKind, the forward-slash-normalised path (and old_path for a rename), the Hunks, and the verbatim raw section for callers that display text.
  • Hunk — a single @@ … @@ block: the old/new line ranges, the section heading, and a body of **DiffLine**s (Context / Added / Removed), each with its leading marker and line terminator stripped.
  • ChangeKind — how the file changed: Added / Modified / Deleted / Renamed.
  • DiffStat — the aggregate files_changed/insertions/deletions shape both git diff --shortstat and jj diff --stat parse into.
  • Version + parse_dotted_version — a numeric major.minor.patch (it Ords, so a caller can gate on a minimum) read tolerantly from a <tool> --version banner.

The wrapper crates re-export these (e.g. vcs_git::FileDiff, vcs_git::parse_diff, vcs_git::GitVersion), so consumers rarely name this crate directly.

§Recipes

Parse a one-file modify diff and read the structured result — pure, so this runs as written:

use vcs_diff::{parse_diff, ChangeKind, DiffLine};

let text = "\
diff --git a/f b/f
--- a/f
+++ b/f
@@ -1,2 +1,2 @@ fn main()
 ctx
-old
+new
";
let files = parse_diff(text);
assert_eq!(files.len(), 1);
assert_eq!(files[0].change, ChangeKind::Modified);
assert_eq!(files[0].path, "f");

let hunk = &files[0].hunks[0];
assert_eq!((hunk.old_start, hunk.new_start), (1, 1));
assert_eq!(hunk.section, "fn main()");
assert_eq!(hunk.lines, vec![
    DiffLine::Context("ctx".into()),
    DiffLine::Removed("old".into()),
    DiffLine::Added("new".into()),
]);

§Features

Structs§

DiffStat
Aggregate line/file counts from a diff stat (git diff --shortstat, jj diff --stat).
FileDiff
One file’s entry in a parsed git-format unified diff (git diff or jj diff --git).
Hunk
A single @@ … @@ hunk within a FileDiff.
Version
A parsed CLI version (major.minor.patch). Ord compares numerically, so a caller can gate a feature on a minimum version; Hash lets it key a map (e.g. a per-version capability cache).

Enums§

ChangeKind
How a file changed in a unified diff.
DiffLine
One line inside a Hunk, tagged by its role. The stored text excludes the leading /+/- marker and the line terminator — a CRLF-origin diff’s trailing \r is stripped along with the \n, so reconstruct exact bytes from FileDiff::raw, not from these lines.

Functions§

parse_diff
Parse a git-format unified diff into one FileDiff per file. Works on git diff and jj diff --git output alike. Public so a consumer can parse diff text it obtained by other means.
parse_dotted_version
Find the first N.N[.N…] token in raw and return its leading three numeric components (a missing patch reads as 0). Each component is the token’s leading digits, so 0-dev or 1.windows trailers don’t break parsing — this handles git version 2.54.0.windows.1, jj 0.42.0, 2.41.0-rc1, etc.