Improving semver for git dependencies

I'm wondering whether versioning of git dependencies in Cargo could be improved. Currently:

dep = { git = "url", version = "0.17" }

will only look at the latest commit, and fail if the version doesn't match. OTOH if you select a commit with rev, then it'll be locked forever, and cargo update won't update it. This makes versioning and updates of git deps a second-class citizen compared to registries.

Would it make sense to make Cargo smarter about this? For example, scan commits in a given branch to find a matching version.

2 Likes

The repository owner can tag revisions by major version number, and then move the tag when publishing a backwards-compatible release:

dep = { git = "url", version = "0.17", tag = "dep-0.17" }

This is the solution. Altho we do wonder, is there any way to require signed commits?

That still looks like a workaround to me. A standard usage of git is to tag releases, and never ever change the tag.

3 Likes

I find it surprising that version is allowed with a git dep. I've always considered that git dependencies are versioned via tag/branch/rev instead of version; tag and rev are ~equivalent to an =x.y.z version, branch would commonly be a ^x.y.z version where each major version has a branch, and rev is also used by the lockfile to record the exact version chosen. My expectation for a scheme like @2e71828 mentions would be that you use branch = "0.17.x" and the repository owner updates that to the latest 0.17.* tag as they are published.

This is how it works with a lockfile, the branch is scanned for the rev recorded in the lockfile.

3 Likes

The reason is that git clients never re-fetch tags, so changing them is strongly discouraged.

See git-tag: On re-tagging.

1 Like

I'm asking about the improvement, because at my workplace we're evaluating whether we should abandon using git dependencies and switch to using an internal registry instead. Cargo's inferior support for versioning and updating git deps has been the main reason for switching.

We currently don't maintain version branches, only tag releases. I see a branch like 0.17.x could work for updates, but it's still inferior to ease of cargo publish, and incompatible cargo outdated. If Cargo scanned branches or tags for available versions, git deps could have feature-parity with registries in this area.

1 Like

I was just about to start a new thread on this same topic, but happened to dig up this old one. I have the same desire as @kornel to improve git dependency handling in Cargo.

I'm working with repos where we have a 1:1 relationship between Cargo versions and git tags, where the tags are nearly the same as the Cargo semver string (ex. git tag 'v1.0.0' maps to version '1.0.0'). Is reasonable to propose an optional feature where Cargo would checkout the corresponding git tag, based on the Cargo version specified for the dependency?

For example, 'dep = "1.0.0' would checkout the tag 'v1.0.0'. And 'dep = 1.0' would implicitly fetch all tags, and select the tag with a patch release of the highest value (ex. 'v1.0.106'). If useful, the regex used to match the tags could be configurable, to support different tag naming schemes.

Is there some reason this would be a bad idea? If people like the idea, I would be interested in working on patches for this feature. Best case, I'm guessing there's other related requirements, which should be considered.

I know that the git tag can be called out explicitly in the dependency definition. It's just that, for structured tags, it seems like an extra step.

2 Likes

You can choose to specify only the tag and omit the dependency version.

Note that there are cases where multiple crates live in a repository and they're not necessarily all versioned together (though there is usually correlation). One repo I work on started off as one crate and has since become 3, so the "original" crate (by name) gets the "naked" vX.Y.Z tags while the others get crate-name/vX.Y.Z. Therefore I think this is a dangerous assumption to make in general at the cargo level.

I haven't exactly thought it through, but there is an alternative scanning mechanism which could look through all tags, and read versions out of Cargo.toml, this is perhaps more expensive than mapping versions to tags, since it involves some amount of reading the index.

but has some benefit of perhaps not needing regexp and repository specific configuration? (It is obvious but i'll say it) If mapping version to tag is the thing, its good to also check that the Cargo.toml version is in sync with the version from which the tag is derived.

1 Like