gitbutlerapp/gitbutler · error

Cannot restore branch

Error message

Cannot restore branch '{}' because it is checked out in worktrees: {dirs:?}

What it means

Thrown during `restore_snapshot` when the snapshot wants to move a branch to a different commit, but that branch is currently checked out in another git worktree. Moving a ref that a worktree has checked out would desynchronize that worktree's index/HEAD, so GitButler refuses via `SafeDelete::worktree_dirs_with_ref`.

Solutions

  1. Remove the extra worktree holding the branch: `git worktree remove <dir>` (or `git worktree prune` for stale entries)
  2. Check out a different branch in the other worktree so the target branch is free, then retry the restore
  3. Restore the snapshot from the worktree where the branch is actually checked out
  4. Run `but oplog restore` only when the repository has a single worktree referencing the branch

Example fix

// before: branch 'main' checked out in ../main-wt blocks restore
// after:
//   git worktree remove ../main-wt
//   but oplog restore  # now succeeds
Defensive patterns

Strategy: validation

Validate before calling

// fail fast before restore if the ref is checked out elsewhere
let safe = but_core::branch::SafeDelete::new(&repo)?;
if let Some(reference) = repo.try_find_reference(branch_ref)? {
    let dirs = safe.worktree_dirs_with_ref(&reference)?;
    if !dirs.is_empty() { return Err(format!("branch checked out in {dirs:?}")); }
}

Prevention

When it happens

Trigger: `restore_snapshot` restores a `SnapshotReference` whose target differs from the branch's current target while `but_core::branch::SafeDelete` reports the ref is checked out in one or more worktrees.

Common situations: Using linked worktrees (`git worktree add`) with GitButler branches; restoring oplog snapshots while the same branch is open in a second terminal/editor worktree; CI checkouts sharing the same repository.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/bf11bcac1d5ffef2. Report an issue: GitHub.

Appendix: source

Thrown at crates/gitbutler-oplog/src/oplog.rs:1057

    let repo = ctx.repo.get()?;
    let snapshot_commit = repo.find_commit(snapshot_commit_id)?;
    let snapshot_tree = snapshot_commit.tree()?;
    // Validate all metadata formats before creating the before-restore snapshot or mutating the
    // worktree, refs, config, TOML, or database.
    let (restored_project_meta, restored_virtual_branches, restored_branch_order) =
        snapshot_metadata(&snapshot_tree, &repo)?;
    let restored_checkout = snapshot_checkout(&snapshot_tree, &repo)?;
    let restored_reference = snapshot_reference(&snapshot_tree, &repo)?;
    let restored_target = restored_project_meta.target_commit_id_or_err()?;
    let restored_vb_toml = toml::to_string(&restored_virtual_branches)?;

    if let Some(reference) = restored_reference.as_ref()
        && let Some(current_reference) = repo.try_find_reference(reference.ref_name.as_ref())?
        && current_reference.target().try_id().map(ToOwned::to_owned) != reference.target
        && let Some(dirs) =
            but_core::branch::SafeDelete::new(&repo)?.worktree_dirs_with_ref(&current_reference)
    {
        bail!(
            "Cannot restore branch '{}' because it is checked out in worktrees: {dirs:?}",
            reference.ref_name.shorten()
        );
    }

    let before_restore_snapshot_tree_id = match restored_reference.as_ref() {
        Some(reference) => {
            prepare_snapshot_with_target_and_ref(
                ctx,
                exclusive_access.read_permission(),
                Some(reference.ref_name.as_ref()),
            )?
            .tree_id
        }
        None => prepare_snapshot(ctx, exclusive_access.read_permission())?,
    };
    let before_restore_snapshot_workdir_tree_id =
        get_v3_workdir_tree(repo.find_tree(before_restore_snapshot_tree_id)?)?

View on GitHub (pinned to 58e5313667)