Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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]
  • --help Show 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:

packageoldnewnotes
epoch-changed1.2.31!1.2.3(M)
epoch-zero1.2.30!1.2.3
existence-added-1.2.3
existence-removed1.2.3-
major1.2.32.1.0(M)
minor1.2.31.3.4
validity-invalid-to-invalidfoobar(M)
validity-invalid-to-validfoo1.2.3(M)
validity-valid-to-invalid1.2.3foo(M)
zerover-change0.1.20.2.0(M)
zerover-same0.1.20.1.3

Test cases for demonstrating how the (D)/is_downgrade note works:

packageoldnewnotes
downgrade1.3.41.0.1(D)
upgrade1.0.11.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: int
  • added: int
  • removed: int
  • updated: 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:

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.