gitbutlerapp/gitbutler · error

Cannot operate on uncommitted changes in worktree {name} yet

Error message

Cannot operate on uncommitted changes in worktree {name} yet

What it means

Diff specs are built against one checkout's repository, so `push_changes_from_uncommitted` refuses `UncommittedHunkOrFile` values whose `source` differs from the builder's `source`. When the mismatched hunks come from a linked worktree you get this 'yet' message; the operation either needs a builder created for that worktree or does not support worktree sources. (A non-worktree mismatch produces the internal BUG variant instead.)

Source

Thrown at crates/but/src/utils/diff_specs.rs:104

            }
            CliId::Stack { .. } => {
                anyhow::bail!("Cannot compute diff specs for stacks")
            }
        }
    }

    pub fn push_changes_from_uncommitted(
        &mut self,
        uncommitted: &UncommittedHunkOrFile,
    ) -> anyhow::Result<()> {
        // Specs are built against one checkout's repository, so a hunk from
        // another would silently address the wrong files. Operations that support
        // worktree sources validate the selection and construct the builder via
        // [`Self::for_change_source`]; the rest read the main worktree and refuse
        // worktree hunks here.
        if uncommitted.source != self.source {
            if let Some(name) = uncommitted.source.worktree_name() {
                anyhow::bail!("Cannot operate on uncommitted changes in worktree {name} yet");
            }
            anyhow::bail!(
                "BUG: a change from {} was pushed into a builder reading {}",
                uncommitted.source.describe(),
                self.source.describe()
            );
        }
        let hunks = uncommitted.hunks.iter().cloned();
        self.push_hunks(hunks.map(|id_and_hunk| id_and_hunk.hunk))
    }

    pub fn push_changes_from_path_prefix(
        &mut self,
        hunks: &nonempty::NonEmpty<IdAndHunk>,
    ) -> anyhow::Result<()> {
        self.push_hunks(hunks.iter().map(|id_and_hunk| id_and_hunk.hunk.clone()))
    }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Construct the builder with `DiffSpecBuilder::for_change_source(...)` using the selection's source so it reads the correct worktree
  2. Filter the selection so it only contains hunks whose source matches the operation's expected source
  3. If the operation is intentionally main-worktree-only, reject worktree selections at the UI/CLI boundary before invoking it

Example fix

// before
let mut builder = DiffSpecBuilder::new(&main_repo); // reads the main worktree
builder.push_changes_from_uncommitted(&hunk_from_worktree)?;

// after
let mut builder = DiffSpecBuilder::for_change_source(&hunk_from_worktree.source, ...)?;
builder.push_changes_from_uncommitted(&hunk_from_worktree)?;
Defensive patterns

Strategy: validation

Validate before calling

// verify sources agree before pushing uncommitted hunks
if uncommitted.source != builder.source {
    if let Some(name) = uncommitted.source.worktree_name() {
        return Err(anyhow::anyhow!("operation does not support worktree `{name}`"));
    }
    return Err(anyhow::anyhow!("selection source does not match the builder"));
}

Type guard

fn matches_builder_source(uncommitted: &UncommittedHunkOrFile, builder: &DiffSpecBuilder<'_>) -> bool {
    uncommitted.source == builder.source
}

Try / catch

if let Err(err) = builder.push_changes_from_uncommitted(&uncommitted) {
    if err.to_string().contains("worktree") {
        let mut builder = DiffSpecBuilder::for_change_source(&uncommitted.source, /* ... */)?;
        builder.push_changes_from_uncommitted(&uncommitted)?;
    } else {
        return Err(err);
    }
}

Prevention

When it happens

Trigger: Passing uncommitted hunks captured in worktree `name` into a builder constructed for the main worktree (the default constructor) rather than via `DiffSpecBuilder::for_change_source` with the selection's own source.

Common situations: Multi-worktree projects where selections mix hunks from different checkouts; new operations reusing a main-worktree builder for arbitrary selections; UI flows that forward worktree hunks to main-worktree-only operations.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/9bf854c404bcc377. Report an issue: GitHub.