T tomlkit·org
Inspect Formatter Validator Lint Stats Keys Query Set value pyproject Cargo Convert TOMLJSON JSONTOML TOMLYAML YAMLTOML INITOML TOMLINI .envTOML TOML.env TOMLTS TOMLCSV CSVTOML TOMLXML TOML.properties .propertiesTOML Transform Sort keys Flatten Unflatten Redact Minify Generate Go struct Rust struct Python types JSON Schema Compare Diff Merge Lockfile diff

Lockfile Diff

updated 31 August 2026

A lockfile diff in git is four thousand lines of noise. The question is always the same one — which packages moved, and by how much — and the answer is a page long. This keys on the package name instead of the line number and gives you that page.

Cargo.lock · poetry.lock · uv.lock

What this tool does

It reads two lockfiles, matches their packages by name rather than by position, and reports what moved: upgraded, downgraded, added, removed, and — for packages whose version did not change — whether their source or checksum did.

The intent it closes: "cargo update touched 4,000 lines and I need to tell a reviewer what actually happened." A lockfile is an ordered array of tables, so inserting one package shifts every entry after it and a line diff turns into a wall. A generic TOML diff is no better — it reports package[417].version changed, which is true and tells you nothing.

It owns one job: comparing two resolved dependency sets. It does not resolve them, fetch anything, or check for advisories.

When you'd reach for it

  • Reviewing a dependency-bump PR. Paste both versions of the lockfile, switch the output to Markdown table, and put the result in the PR description. Reviewers read six rows instead of scrolling past four thousand.
  • After a routine update. cargo update, poetry lock or uv lock and you want to know whether anything crossed a major version before you trust the test run.
  • A build that works on one machine and not another. Compare the two lockfiles: the difference is nearly always a single package, and this finds it in a second.
  • Auditing a supply-chain surprise. Same version, different checksum or source is exactly what a lockfile exists to catch, and it is invisible in a version-only comparison.

How the comparison works

1. Find the packages

All three formats keep their resolved set in a [[package]] array of tables with name and version on each entry. That array is read from both sides and turned into a map from name to the set of versions present — a set, because Cargo genuinely does lock several versions of the same crate at once when two dependencies disagree.

2. Compare by name

Every name in either file is looked at once. Present only in B is an addition; only in A is a removal; in both with the same version set is unchanged, unless the source or checksum moved.

3. Say which direction, and how far

When a package has exactly one version on each side, the two are compared component by component — numerically, so 1.10.0 is correctly newer than 1.9.0, which string comparison gets backwards. A pre-release suffix sorts before the release it precedes, so 1.0.0-rc1 → 1.0.0 is an upgrade. The size of the bump is labelled major, minor or patch from the first component that differs.

When the versions are not comparable that way — a git or path dependency, or a package locked at two versions — it is reported as a change with both sides shown, rather than being guessed at in a direction.

Options reference

Output

  • Report — grouped and aligned for reading, one section per kind of change. The default.
  • Markdown table — paste straight into a pull request, an issue, or a release note. The header line becomes the bold summary above the table.
  • JSON — for a script. Counts plus the four lists, with every version on each side.
  • Names only — one package name per line, for feeding to something else.

Show

Major bumps only filters the upgrade and downgrade lists down to the ones that crossed a major version — the changes that can actually break you. The summary line keeps counting all of them, so you can still see how much was hidden.

Unchanged

Off by default because it is nearly the whole file. On, an unchanged section is appended — useful when you want the full inventory in one place.

Example

Two Cargo.lock files, three packages each:

Cargo.lock v3 → Cargo.lock v3 · 3 → 3 package(s) · 4 change(s): 1 added, 1 removed, 2 upgraded, 0 downgraded

upgraded (2)
  ↑ serde      1.0.180 → 1.0.190   (patch)
  ↑ toml       0.8.2 → 0.9.0       (minor)

added (1)
  + tokio      1.35.1

removed (1)
  - once_cell  1.18.0

The same comparison as a Markdown table drops straight into the pull request that caused it.

Limits

  • It compares what the lockfile says, not what it means. No registry is contacted, so it cannot tell you whether a bump fixes an advisory or whether a new package is a transitive dependency of something you added.
  • Version comparison is dotted-numeric with a pre-release rule, not full semver. Build metadata is ignored, and anything that does not start with numbers is reported without a direction rather than sorted wrongly.
  • Both sides have to parse. A lockfile is machine-written so this is rarely a problem, but a truncated paste will be refused — the validator says where.
  • package-lock.json and Pipfile.lock are JSON, not TOML. They are not read here.

FAQ

Which lockfiles work?

Cargo.lock (v1, v2 and v3), poetry.lock, and uv.lock. Anything else with a [[package]] array carrying name and version will also work; the format label in the summary is a best guess from the surrounding metadata and does not affect the comparison.

Why is a package listed as changed rather than upgraded?

Three reasons. It is locked at more than one version on one of the sides — normal in Cargo when two dependencies want incompatible ranges. Its version string is not dotted-numeric, as with a git revision. Or the version is identical and it was the source or the checksum that moved, which is called out by name.

How is this different from the TOML Diff tool?

TOML Diff compares two documents by key path, which is the right answer for a config file and the wrong one for an ordered array: insert a package near the top and it reports every later index as changed. This one throws the ordering away and matches on the name.

Can I diff a lockfile against itself after a format change?

Yes, and it will correctly report no changes — ordering and formatting are not part of the comparison. That makes it a good check that a tool which rewrote your lockfile did not also resolve something differently.