Describe the bug
When a flake specifies a Git input with a specific commit (rev) but omits the branch (ref), Nix defaults to querying the remote for its HEAD (e.g., refs/heads/main). It then injects this default branch into the locked input data, even if the provided rev actually doesn't belong to this branch.
Because Nix currently lacks validation to check if a rev actually exists on the fetched ref (as described in #12974), the lock process succeeds silently. While Nix may still successfully evaluate the flake later by utilizing the locked rev hash directly, the lockfile itself contains factually incorrect metadata.
Of course, branch names and tags are inherently mutable anyway (branches can move around, tags can be reassigned, and only commit SHAs are truly immutable), which admittedly opens up a discussion whether this behavior should even be considered a bug at all and how much the refs value should be taken as the truth overall.
Still, I wanted to open this issue because automatically locking the default branch can, as explained above, introduce flat-out wrong metadata right out of the gate. Regardless of how much weight you place on refs, the locked branch in this scenario might never have been tied to the requested commit in the first place. And this is also something that would need to be taken into account when adding validation to address #12974.
Steps To Reproduce
- Create or find a Git repository with a default branch (e.g.,
main) and a separate branch (e.g., feature-branch).
- Obtain a commit SHA (
<feature-sha>) that exists only on the separate branch.
- Create a
flake.nix that uses this SHA without specifying the branch:
{
inputs.my-input.url = "git+https://github.com/owner/repo.git?rev=<feature-sha>";
outputs = { self, my-input }: { };
}
- Run
nix flake lock.
- Inspect the generated
flake.lock. Nix incorrectly injects "ref": "refs/heads/main" into the locked node:
"my-input": {
"locked": {
"ref": "refs/heads/main",
"rev": "<feature-sha>",
"type": "git",
"url": "https://github.com/owner/repo.git"
},
"original": {
"rev": "<feature-sha>",
"type": "git",
"url": "https://github.com/owner/repo.git"
}
}
The same behavior can be observed instantly without a file by running nix flake metadata "git+https://github.com/owner/repo.git?rev=<feature-sha>", which will output a locked URL containing ref=refs/heads/main.
Expected behavior
If a user specifies a rev without a ref, Nix should not blindly guess and lock the default ref. Since a specific commit might belong to multiple branches, or no branches at all, guessing is inherently flawed.
My suggestion - like in #6978 - would be to omit the ref field from the locked metadata entirely if it wasn’t explicitly provided in the URL. This would especially make sense when the specified commit doesn't belong to a branch at all, which is rather uncommon, but possible if the targeted Git server has uploadpack.allowAnySHA1InWant enabled. Additionally, this also makes the lockfile more informative, since it clearly signals that the input was pinned directly to a specific rev rather than pulled from a branch.
Metadata
nix-env (Nix) 2.34.7
Additional context
More or less related issues:
Checklist
Add 👍 to issues you find important
Describe the bug
When a flake specifies a Git input with a specific commit (
rev) but omits the branch (ref), Nix defaults to querying the remote for its HEAD (e.g.,refs/heads/main). It then injects this default branch into the locked input data, even if the providedrevactually doesn't belong to this branch.Because Nix currently lacks validation to check if a
revactually exists on the fetchedref(as described in #12974), the lock process succeeds silently. While Nix may still successfully evaluate the flake later by utilizing the lockedrevhash directly, the lockfile itself contains factually incorrect metadata.Of course, branch names and tags are inherently mutable anyway (branches can move around, tags can be reassigned, and only commit SHAs are truly immutable), which admittedly opens up a discussion whether this behavior should even be considered a bug at all and how much the
refs value should be taken as the truth overall.Still, I wanted to open this issue because automatically locking the default branch can, as explained above, introduce flat-out wrong metadata right out of the gate. Regardless of how much weight you place on
refs, the locked branch in this scenario might never have been tied to the requested commit in the first place. And this is also something that would need to be taken into account when adding validation to address #12974.Steps To Reproduce
main) and a separate branch (e.g.,feature-branch).<feature-sha>) that exists only on the separate branch.flake.nixthat uses this SHA without specifying the branch:nix flake lock.flake.lock. Nix incorrectly injects"ref": "refs/heads/main"into the locked node:The same behavior can be observed instantly without a file by running
nix flake metadata "git+https://github.com/owner/repo.git?rev=<feature-sha>", which will output a locked URL containingref=refs/heads/main.Expected behavior
If a user specifies a
revwithout aref, Nix should not blindly guess and lock the defaultref. Since a specific commit might belong to multiple branches, or no branches at all, guessing is inherently flawed.My suggestion - like in #6978 - would be to omit the
reffield from the locked metadata entirely if it wasn’t explicitly provided in the URL. This would especially make sense when the specified commit doesn't belong to a branch at all, which is rather uncommon, but possible if the targeted Git server hasuploadpack.allowAnySHA1InWantenabled. Additionally, this also makes the lockfile more informative, since it clearly signals that the input was pinned directly to a specificrevrather than pulled from a branch.Metadata
nix-env (Nix) 2.34.7
Additional context
More or less related issues:
fetchGitshould fail ifrevandrefdon't match #12974Checklist
Add 👍 to issues you find important