Skip to content

Flake Git inputs specifying only a rev always add/lock to the default ref #16233

Description

@gabschne

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

  1. Create or find a Git repository with a default branch (e.g., main) and a separate branch (e.g., feature-branch).
  2. Obtain a commit SHA (<feature-sha>) that exists only on the separate branch.
  3. 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 }: { };
}
  1. Run nix flake lock.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions