python-unidiff
A typed Python library that parses unified diff data into inspectable PatchSet, PatchedFile, and Hunk objects.
Repository Health
Technical Analysis
unidiff is a small, focused Python library for parsing and interacting with unified diff (patch) data. Feed it a diff — from git diff, hg diff, a .diff file, or any file-like/bytes source — and it returns a PatchSet: a list of PatchedFile objects, each a list of Hunk objects, each a list of Line objects, mirroring the actual structure of a diff.
Every level exposes the metadata you’d otherwise have to regex out yourself: per-file added/removed line counts, added/removed/renamed/binary file classification, git file modes and symlink detection, hunk source/target line numbers, and per-line added/removed/context flags with source and target line numbers. A metadata_only mode skips content tracking for faster parsing when you only need the stats.
The library also ships a small CLI (unidiff, or python -m unidiff) that prints a per-file addition/deletion summary from stdin or a file, useful as a quick diff-stat replacement in shell pipelines.
What You Get
PatchSet— parses from a file-like object, bytes, a string (PatchSet.from_string), or a filename (PatchSet.from_filename), and behaves like a list ofPatchedFileobjectsPatchedFile— per-file metadata:is_added_file,is_removed_file,is_modified_file,is_rename,is_binary_file,added/removedcounts, andpath- Git-specific metadata —
source_mode/target_modefile modes (e.g.100644,120000) and anis_symlinkshortcut, plusdiff_line_noto locate hunkless binary entries HunkandLineobjects withsource_start/target_start,source_line_no/target_line_no, andis_added/is_removed/is_contextflags for line-by-line inspection- Round-trip string rendering —
str(patch)reconstructs the original unified diff text from any parsed object - A
unidiffCLI (alsopython -m unidiff) that prints an addition/deletion summary per file, usable directly in agit diff | unidiffpipeline metadata_only=Trueparsing mode that skips content tracking for faster stats-only parsing on large diffsbytesinput support and an explicitnewline='\n'path for diffs containing embedded carriage returns or control characters
Common Use Cases
- Code review tooling - annotate or comment on specific added/removed lines in a pull request diff by walking
Hunk/Lineobjects - Coverage-on-diff / lint-on-diff - restrict a linter or coverage report to only the lines actually changed in a patch
- Patch application scripts - inspect
is_added_file/is_removed_file/is_renamebefore deciding how to apply a hunk to a working tree - Diff-stat CLIs - use the bundled
unidiffcommand, or build a custom summary, fromgit diff | unidiffstyle pipelines - Changelog / release-note generation - walk added and removed lines across a
PatchSetto summarize what changed between two revisions
Under The Hood
Architecture
The library models a unified diff as four nested list subclasses that mirror the diff’s own structure: PatchSet (list of PatchedFile) → PatchedFile (list of Hunk) → Hunk (list of Line). Parsing lives in unidiff/patch.py as a hand-written line-by-line scanner (PatchSet._parse → PatchedFile._parse_hunk) driven entirely by the compiled regexes centralized in unidiff/constants.py (git header, rename, mode-change, hunk-header, no-newline-marker patterns). This keeps pattern matching separate from the object model and from error signaling, which is isolated in unidiff/errors.py as a single UnidiffParseError. A metadata_only flag threaded through the parse path switches between full line-content tracking and a cheaper line-type-only scan, letting the same parser serve both “I need the exact diff back” and “I only need stats” callers without duplicating logic. __init__.py re-exports the public surface (PatchSet, PatchedFile, Hunk, constants) as the sole supported import path.
Tech Stack
Pure Python 3.9+ with zero runtime dependencies — packaging is pyproject.toml/setuptools with a dynamic version pulled from unidiff/__version__.py. Type hints use modern PEP 585/604 generics (list[Line], Union/Optional from typing, from __future__ import annotations) and the package ships a py.typed marker for downstream type-checker consumption. The only extra surface is a stdlib-only argparse-based CLI in unidiff/__main__.py registered via [project.scripts].
Code Quality
Tests (1,125 lines across test_parser.py, test_hunks.py, test_line.py, test_patchedfile.py) actually outweigh the library source (965 lines), run via stdlib unittest, and are checked against a real corpus of diff fixtures under tests/samples/ covering git, hg, bzr, quilt, and debdiff formats plus edge cases (symlinks, renames, embedded CR, quoted filenames). CI runs the suite across five Python versions (3.9–3.13) and layers a separate mypy type-check job over the source tree. Naming is consistent and each public property is documented inline; error handling is explicit via UnidiffParseError rather than silent failure on malformed input.
What Makes It Unique
The object model is deliberately faithful to the diff format itself rather than flattening it into a generic dict/list structure, so callers can walk patch[i][j][k] and get exactly the file/hunk/line addressed by that position. Combined with the metadata_only fast path and the round-trip str() guarantee (parsed output regenerates byte-identical diff text), it functions equally well as a diff introspection library and as a diff-rewriting primitive, without pulling in any external dependency to do either.
Used by 4 apps in this directory
AutoGen
AI Development · Automation
Build autonomous and human-in-the-loop multi-agent AI systems with a layered, event-driven Python and .NET framework pioneered at Microsoft Research.
ClickHouse
Databases · Analytics · Data Engineering
Open-source column-oriented database that delivers real-time analytical queries on petabyte-scale data with millisecond latency.
Sentry
Security · Developer Tools · Monitoring
Developer-first error tracking and performance monitoring platform with AI-powered root-cause analysis across 20+ languages and frameworks.
Timeplus Proton
Data Engineering · Analytics
Single C++ binary SQL engine for real-time stream processing, ETL, and analytics on Kafka, Redpanda, and ClickHouse with sub-millisecond latency.