- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| examples | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE-MIT | ||
| README.md | ||
cargo-overrides
Experimental npm-style transitive dependency overrides for Cargo.
Cargo intentionally will not unify SemVer-incompatible requirements. This is especially noticeable for pre-1.0 crates, where 0.7 and 0.8 are incompatible ranges. cargo-overrides gives the workspace owner an escape hatch when they have verified that the newer version is actually source-compatible.
What it does
Given a graph like:
app
├── parent-a -> foo ^0.7
├── parent-b -> foo ^0.8
└── parent-c -> foo ^0.8
and:
# Cargo.overrides.toml
[overrides]
foo = "0.8.5"
cargo-overrides:
- runs
cargo metadata; - finds crates.io parent crates that declare a crates.io dependency on
foo; - copies those parents into
target/cargo-overrides/shadow/; - rewrites those dependency requirements to exactly
=0.8.5(including already-compatible ranges, so the requested version is pinned); - generates a Cargo config with
[patch.crates-io]entries pointing at the shadow crates; - runs
cargo metadatawith that config to resolve/update the lockfile and verifies the requested crates.io version converged; - runs the requested Cargo command with
cargo --config <generated-config> ....
Cargo still performs dependency resolution and writes Cargo.lock. The tool does not manually forge lockfile entries.
Install
cargo install --path .
Then, from a Cargo workspace:
cargo overrides list
cargo overrides add foo@0.8.5
cargo overrides prepare
cargo overrides check
cargo overrides lock
cargo overrides exec build --release
cargo overrides exec test --all-features
You can also edit the file directly:
[overrides]
foo = "0.8.5"
bar = "0.12.3"
Important behavior
The override is intentionally stronger than Cargo's normal resolver: a dependency declaration such as:
foo = "0.7"
inside a crates.io parent can become:
foo = "=0.8.5"
in the generated shadow copy.
That may fail to compile or, worse, compile with unintended behavior. The root project is taking responsibility for violating the parent's declared compatibility range. Run tests.
Current MVP limitations
- Only exact target versions are accepted (
foo@0.8.5), not ranges. - Only crates.io parent packages are shadow-patched.
- If a workspace member itself declares the incompatible requirement, edit that workspace manifest directly.
- Git/alternate-registry parent patching is not implemented yet.
- The generated shadow directory is disposable and lives under
target/cargo-overrides. - Builds that rely on these overrides must be invoked through
cargo overrides ...(or use the generated Cargo config themselves). A lockfile alone cannot encode "ignore the parent's declared version requirement".
Why not edit Cargo.lock directly?
Cargo.lock describes a resolved graph. The incompatible requirement lives in the parent package's Cargo.toml. Forging a lockfile cannot make that requirement disappear; Cargo will validate/re-resolve the graph. Shadow-patching the parent manifest changes the input to the resolver, after which Cargo can generate a legitimate lockfile.
Files
Cargo.overrides.toml # commit this
target/cargo-overrides/ # generated, do not commit
Cargo.lock # generated by Cargo as usual
Safety / CI suggestion
A useful CI sequence is:
cargo overrides check -- --all-targets --all-features
cargo overrides exec test --all-features
Treat an override as temporary technical debt. Prefer upgrading or upstreaming a relaxed dependency requirement when possible.