Diff API Reference¶
diff
¶
Unified diff parser, patch applicator, and three-way merge — zero dependencies, stdlib only, Python 3.10+.
Part of zerodep: https://github.com/Oaklight/zerodep Copyright (c) 2026 Peng Ding. MIT License.
Provides structured parsing of unified diffs, patch application/reversal,
and three-way merge with conflict detection. Built entirely on the
standard library difflib module.
Generate and round-trip a patch::
from diff import make_diff, parse_patch, apply_patch, reverse_patch
a = "hello\nworld\n"
b = "hello\nbrave new world\n"
patch_text = make_diff(a, b)
patch = parse_patch(patch_text)
assert apply_patch(a, patch) == b
rev = reverse_patch(patch)
assert apply_patch(b, rev) == a
Three-way merge::
from diff import merge3
result = merge3(base, ours, theirs)
if result.has_conflicts:
print("Conflicts found!")
print(result.content)
DiffError
¶
PatchParseError
¶
Bases: DiffError
Raised when patch text has invalid or malformed format.
Attributes:
| Name | Type | Description |
|---|---|---|
line_no |
1-based line number where the error was detected. |
|
detail |
Human-readable description of the issue. |
Source code in diff/diff.py
PatchApplyError
¶
Bases: DiffError
Raised when a patch cannot be applied to the given source.
Attributes:
| Name | Type | Description |
|---|---|---|
hunk_index |
0-based index of the failing hunk. |
|
expected |
The line content expected by the patch. |
|
actual |
The line content found in the source. |
|
source_line_no |
1-based line number in the source. |
Source code in diff/diff.py
Hunk
dataclass
¶
A single contiguous changed region in a unified diff.
Attributes:
| Name | Type | Description |
|---|---|---|
src_start |
int
|
1-based starting line in the source file. |
src_len |
int
|
Number of source lines covered by this hunk. |
tgt_start |
int
|
1-based starting line in the target file. |
tgt_len |
int
|
Number of target lines covered by this hunk. |
lines |
list[tuple[str, str]]
|
Sequence of |
Source code in diff/diff.py
PatchedFile
dataclass
¶
All hunks for a single file in a patch.
Attributes:
| Name | Type | Description |
|---|---|---|
source_file |
str | None
|
Source filename ( |
target_file |
str | None
|
Target filename ( |
hunks |
list[Hunk]
|
Ordered list of :class: |
Source code in diff/diff.py
Patch
dataclass
¶
A collection of :class:PatchedFile instances parsed from unified diff text.
Supports len(), iteration, and indexing.
Source code in diff/diff.py
ConflictRegion
dataclass
¶
A region where concurrent edits conflict in a three-way merge.
Attributes:
| Name | Type | Description |
|---|---|---|
base_start |
int
|
0-based start index in the base lines. |
base_end |
int
|
0-based exclusive end index in the base lines. |
ours |
list[str]
|
Lines from the ours side. |
theirs |
list[str]
|
Lines from the theirs side. |
Source code in diff/diff.py
MergeResult
dataclass
¶
Result of a three-way merge.
Attributes:
| Name | Type | Description |
|---|---|---|
content |
str
|
The merged text. When conflicts exist the text includes
conflict markers ( |
has_conflicts |
bool
|
|
conflicts |
list[ConflictRegion]
|
List of :class: |
Source code in diff/diff.py
make_diff(a, b, filename_a='a', filename_b='b', context=3)
¶
Generate a unified diff between two strings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
str
|
Original text. |
required |
b
|
str
|
Modified text. |
required |
filename_a
|
str
|
Label for the original file in the diff header. |
'a'
|
filename_b
|
str
|
Label for the modified file in the diff header. |
'b'
|
context
|
int
|
Number of context lines around each change. |
3
|
Returns:
| Type | Description |
|---|---|
str
|
A unified diff string, or an empty string if a and b are identical. |
Source code in diff/diff.py
parse_patch(patch_text)
¶
Parse unified diff text into a structured :class:Patch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
patch_text
|
str
|
The unified diff text. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
Patch
|
class: |
Raises:
| Type | Description |
|---|---|
PatchParseError
|
If the diff text is malformed. |
Source code in diff/diff.py
apply_patch(source, patch)
¶
Apply a parsed patch to source text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The original text to patch. |
required |
patch
|
Patch | PatchedFile
|
A :class: |
required |
Returns:
| Type | Description |
|---|---|
str
|
The patched text. |
Raises:
| Type | Description |
|---|---|
PatchApplyError
|
If a context or deletion line does not match the source. |
DiffError
|
If a :class: |
Source code in diff/diff.py
reverse_patch(patch)
¶
Create a reversed copy of patch.
Applying the reversed patch to the target reproduces the original source::
assert apply_patch(b, reverse_patch(parse_patch(make_diff(a, b)))) == a
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
patch
|
Patch
|
The patch to reverse. |
required |
Returns:
| Type | Description |
|---|---|
Patch
|
A new :class: |
Source code in diff/diff.py
merge3(base, ours, theirs, label_ours='ours', label_theirs='theirs')
¶
Perform a three-way merge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
str
|
The common ancestor text. |
required |
ours
|
str
|
Text from the first branch. |
required |
theirs
|
str
|
Text from the second branch. |
required |
label_ours
|
str
|
Label for conflict markers on the ours side. |
'ours'
|
label_theirs
|
str
|
Label for conflict markers on the theirs side. |
'theirs'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
MergeResult
|
class: |
Source code in diff/diff.py
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | |