ganzua diff
Usage: ganzua diff [OPTIONS] OLD NEW
Compare two lockfiles.
The OLD and NEW arguments must each point to an uv.lock or poetry.lock file,
or to a directory containing such a file.
There is no direct support for comparing a file across Git commits,
but it's possible to retrieve other versions via git show.
Here is an example using a Bash redirect to show non-committed changes in a lockfile:
ganzua diff <(git show HEAD:uv.lock) uv.lock
Options:
--format [json|markdown]Choose the output format, e.g. Markdown. [default: json]--helpShow this help message and exit.
Examples
Can show JSON diffs:
$ ganzua diff corpus/old-uv-project corpus/new-uv-project
{
"stat": {
"total": 2,
"added": 1,
"removed": 0,
"updated": 1
},
"packages": {
"annotated-types": {
"old": null,
"new": {
"version": "0.7.0",
"source": "pypi"
}
},
"typing-extensions": {
"old": {
"version": "3.10.0.2",
"source": "pypi"
},
"new": {
"version": "4.14.1",
"source": "pypi"
},
"is_major_change": true
}
}
}
Can also show the diff as Markdown:
$ ganzua diff corpus/old-uv-project corpus/new-uv-project --format=markdown
2 changed packages (1 added, 1 updated)
| package | old | new | notes |
|-------------------|----------|--------|-------|
| annotated-types | - | 0.7.0 | |
| typing-extensions | 3.10.0.2 | 4.14.1 | (M) |
* (M) major change
Here's the same diff in reverse, which now shows a (D) downgrade notice:
$ ganzua diff corpus/new-uv-project corpus/old-uv-project --format=markdown
2 changed packages (1 updated, 1 removed)
| package | old | new | notes |
|-------------------|--------|----------|---------|
| annotated-types | 0.7.0 | - | |
| typing-extensions | 4.14.1 | 3.10.0.2 | (M) (D) |
* (M) major change
* (D) downgrade
Test cases for demonstrating how the (M)/is_major_change note works:
| package | old | new | notes |
|---|---|---|---|
| epoch-changed | 1.2.3 | 1!1.2.3 | (M) |
| epoch-zero | 1.2.3 | 0!1.2.3 | |
| existence-added | - | 1.2.3 | |
| existence-removed | 1.2.3 | - | |
| major | 1.2.3 | 2.1.0 | (M) |
| minor | 1.2.3 | 1.3.4 | |
| validity-invalid-to-invalid | foo | bar | (M) |
| validity-invalid-to-valid | foo | 1.2.3 | (M) |
| validity-valid-to-invalid | 1.2.3 | foo | (M) |
| zerover-change | 0.1.2 | 0.2.0 | (M) |
| zerover-same | 0.1.2 | 0.1.3 |
Test cases for demonstrating how the (D)/is_downgrade note works:
| package | old | new | notes |
|---|---|---|---|
| downgrade | 1.3.4 | 1.0.1 | (D) |
| upgrade | 1.0.1 | 1.3.4 |
The Markdown diff can show notices when the source of a package changes. When multiple entries have the same note, their IDs are deduplicated:
$ ganzua diff corpus/sources-poetry corpus/sources-uv --format=markdown
6 changed packages (1 added, 5 updated)
| package | old | new | notes |
|--------------------|-------|-------|-------|
| click | 8.3.0 | 8.3.0 | (S1) |
| click-example-repo | 1.0.0 | 1.0.0 | (S2) |
| colorama | 0.4.6 | 0.4.6 | (S1) |
| idna | 3.11 | 3.11 | (S1) |
| propcache | 0.4.1 | 0.4.1 | (S1) |
| sources-uv | - | 0.1.0 | |
* (S1) source changed from default to pypi
* (S2) source changed from <git+https://github.com/pallets/click.git@309ce9178707e1efaf994f191d062edbdffd5ce6#subdirectory=examples/repo> to <git+https://github.com/pallets/click.git@f67abc6fe7dd3d878879a4f004866bf5acefa9b4#subdirectory=examples/repo>
If there are no notes, the entire column is omitted:
$ ganzua diff --format=markdown corpus/new-uv-project corpus/minor-uv-project
1 changed packages (1 updated)
| package | old | new |
|-------------------|--------|--------|
| typing-extensions | 4.14.1 | 4.15.0 |
When a there are no changes, only the summary is shown. The Markdown output omits the table:
$ ganzua diff corpus/new-uv-project corpus/new-uv-project
{
"stat": {
"total": 0,
"added": 0,
"removed": 0,
"updated": 0
},
"packages": {}
}
$ ganzua diff corpus/new-uv-project corpus/new-uv-project --format=markdown
0 changed packages
The input paths may point to directories or lockfiles. The following invocations are all equivalent:
$ ganzua diff corpus/old-uv-project corpus/new-uv-project$ ganzua diff corpus/old-uv-project corpus/new-uv-project/uv.lock$ ganzua diff corpus/old-uv-project/uv.lock corpus/new-uv-project$ ganzua diff corpus/old-uv-project/uv.lock corpus/new-uv-project/uv.lock
output for the above commands
{
"stat": {
"total": 2,
"added": 1,
"removed": 0,
"updated": 1
},
"packages": {
"annotated-types": {
"old": null,
"new": {
"version": "0.7.0",
"source": "pypi"
}
},
"typing-extensions": {
"old": {
"version": "3.10.0.2",
"source": "pypi"
},
"new": {
"version": "4.14.1",
"source": "pypi"
},
"is_major_change": true
}
}
}
JSON Schema
Download: schema.diff.json
Properties:
type DiffStat
Properties:
total: intadded: intremoved: intupdated: int
type DiffEntry
Properties:
-
old: LockedPackage | null -
new: LockedPackage | null -
is_major_change?: bool
True if there was a major version change.This doesn't literally mean "the SemVer-major version component changed", but is intended to highlight version changes that are likely to have breakage.
-
is_downgrade?: bool
True if the version was downgraded. -
is_source_change?: bool
True if the package source changed.
type LockedPackage
Properties:
version: stringsource:pypi|default|other| SourceRegistry | SourceDirect
type SourceRegistry
The package is sourced from a third party registry.
Properties:
registry: string
URL or path to the registry.
type SourceDirect
The package is sourced from a specific URL or path, e.g. a Git repo or workspace path.
Properties:
direct: string
URL or path to the package (directory or archive).subdirectory?: string | null
Only allowed if the source points to an archive file.