Skip to content

fix(release): ferrflow release cannot run in a git worktree — the lock requires .git to be a directory #793

Description

@BryanFRD

ReleaseLock::acquire (src/monorepo/run/lock.rs:38) hard-requires a classic .git directory:

let git_dir = repo_root.join(".git");
if !git_dir.is_dir() {
    return Err(anyhow!(
        "release lock cannot acquire — {} is not a regular .git directory \
         (worktrees and submodules currently unsupported by the lock)", ...

In a linked worktree .git is a file containing gitdir: /path/to/main/.git/worktrees/<name>, so is_dir() is false and every mutating command (release, and anything else taking the lock) aborts before doing any work. Same for a submodule. The error message states the limitation honestly, but it means the tool simply cannot be used in a worktree-based flow — which is a normal way to run releases for a maintenance branch alongside main, and how several repos in this org are laid out.

The rest of the codebase already handles this correctly. cache.rs:39 uses repo.git_dir(), which gix resolves through the .git file to the real per-worktree git dir. The lock is the only place that reimplements the path by hand.

Fix

Take the Repository (or the resolved git dir) instead of repo_root and use repo.git_dir(), matching cache_dir. That lands the lock in .git/worktrees/<name>/ferrflow.lock for a worktree.

That raises a design question worth settling in the same change: which lock does a worktree release want? Two worktrees of the same repo push to the same remote and compete on the same refs, so a per-worktree lock does not actually prevent the race the lock is for. gix exposes the common dir (repo.common_dir()), which is shared across worktrees — putting the lock there gives one lock per repository, which is the correct semantics. Suggest common_dir(), with the per-worktree path only if there is a reason to allow parallel worktree releases.

Bare repos should keep erroring, but with a message that says "bare" rather than pointing at worktrees.

Tests

  • git worktree add a second checkout, acquire the lock from it, assert it succeeds and lands in the expected path.
  • Acquire from the main checkout while the worktree holds it, assert it is refused (confirms the common-dir choice).
  • Bare repo still errors, with the bare-specific message.

Activity

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

    P2Medium prioritybugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions