Visitar URL original
Support Sapling working copies as flakes (`sl+file` input scheme) · Issue #670 · DeterminateSystems/nix-src · GitHub
Skip to content

Support Sapling working copies as flakes (sl+file input scheme) #670

Open

Description

@mikedodge04

Is your feature request related to a problem?

Our team uses Sapling against GitHub-hosted git repos. In a native Sapling checkout (sl clone, which creates .sl/ and no .git), Nix doesn't recognize the repo:

  • nix build . resolves to a path: flake, because local repo detection in flakeref.cc only looks for .git.
  • When the flake's source is copied to the store (any build that uses self), a path: flake copies the whole directory, including untracked files and build output. In a Sapling clone of our repo with a 1 GB untracked target/, the source came to 1.1 GB, against 14 MB for the tracked files. Our real target/ is several GB.
  • self.rev, self.dirtyRev and self.lastModified are missing, so anything that stamps a build with its commit falls back to "unknown".

Running sl inside a git checkout ("dotgit" mode) avoids this. But native Sapling checkouts are the default sl clone produces, and the failure is silent and slow.

Proposed solution

Add a local-only sl input scheme (sl+file://…, type = "sl"), modeled on the existing Mercurial fetcher. It would shell out to sl, the same way mercurial.cc shells out to hg:

  • Detection: flakeref.cc maps a directory containing .sl to sl+file, the same way .git maps to git+file.
  • Source: only tracked files go into the store (sl status --clean --modified --added --no-status --print0). Dirty trees follow allow-dirty / warn-dirty.
  • Attributes: rev, shortRev, dirtyRev, dirtyShortRev, lastModified, plus a lazily computed revCount. In a git-backed Sapling repo the commit ID is the git SHA, so a rev locked from a Sapling checkout matches github: / git+https locks of the same commit.
  • Write-back: flake.lock writes call sl add, --commit-lock-file calls sl commit, and nix flake init / new call sl add.
  • Tests: functional tests that mirror fetchMercurial.sh and flakes/mercurial.sh.

Out of scope for a first PR: remote sl+https / sl+ssh fetching. Sapling remotes are usually git servers, which git+https and github: already handle. Git-only attributes (submodules, lfs, exportIgnore, verifyCommit) are also out.

Alternative solutions

  • Document dotgit mode as the only supported Sapling setup. It works today, but users discover the problem only after a slow path: copy.
  • Point git+file at .sl/store/git. That's the bare git store, so it doesn't see working-copy changes.
  • Reuse the Mercurial fetcher with hg aliased to sl. Several commands it relies on behave differently in Sapling: branch doesn't exist, {rev} is a virtual number, and archive refuses without an explicit file list.

Additional context

A working implementation with functional tests is on mikedodge04/nix-src@sapling-fetcher. Committed state is read through Sapling's bare Git store (.sl/store/git) with the existing GitRepo, so a locked sl+file input has the same store path and fingerprint as git+file at the same commit. We're happy to open a PR. Two questions first:

  1. Would you accept this in Determinate Nix, or would you rather it go to NixOS/nix first?
  2. Any preference on the scheme name (sl vs sapling), or on keeping it behind an experimental feature?

Checklist

Activity

  1. grahamc commented on Oct 7, 2026

    @grahamc
    Member

    This is really neat, and I'm surprised by how brief the diff is! I'm inclined to go for it, but I'll talk to the team in the morning and get consensus.

    p.s. If you can share any more information about how you're using Nix at work, I'd be glad to hear about it :)! graham at determinate.systems

  2. mikedodge04 commented on Oct 16, 2026

    @mikedodge04
    Author

    Hi @grahamc , thanks for the kind words! Just checking in: did the team reach a decision?

    If it's a yes, I'm happy to open the PR against main whenever you're ready. The branch (mikedodge04/nix-src@sapling-fetcher) still merges cleanly onto today's 3.23.2. Since posting I've added one fix: a locked sl+file input now keeps revCount and lastModified from the lock file instead of recomputing them, as the git fetcher does.

    Also worth knowing: upstream added InputScheme::localRepoURL() in NixOS#16507 (it ships in 2.36), which lets each scheme claim its own checkout directories. Once that reaches Determinate, .sl detection moves into the scheme and the flakeref.cc change goes away. I can do that in this PR or as a follow-up, whichever you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions