gitbutlerapp/gitbutler · error

cannot mark files from multiple commits

Error message

cannot mark files from multiple commits

What it means

At crates/but/src/command/legacy/status/tui/app/mark.rs:430, the CommittedFiles variant enforces a second invariant: all marked CommittedFileIds must come from the same commit (files.head.commit_id != mark.commit_id bails 'cannot mark files from multiple commits'). Marking files across commits in one operation is not supported because the resulting operation would be ambiguous.

Source

Thrown at crates/but/src/command/legacy/status/tui/app/mark.rs:430

}

impl MarkStore<CommittedFileId> for Marks {
    type Error = anyhow::Error;

    fn contains_mark(&self, mark: &CommittedFileId) -> bool {
        self.as_committed_files()
            .is_some_and(|files| files.iter().any(|file| file == mark))
    }

    fn insert_mark(&mut self, mark: CommittedFileId) -> Result<(), Self::Error> {
        if self.contains_mark(&mark) {
            return Ok(());
        }
        match self {
            Self::Empty => *self = Self::CommittedFiles(NonEmpty::new(mark)),
            Self::CommittedFiles(files) => {
                if files.head.commit_id != mark.commit_id {
                    anyhow::bail!("cannot mark files from multiple commits");
                }
                files.push(mark);
            }
            _ => anyhow::bail!("cannot mix mark sources"),
        }
        Ok(())
    }

    fn remove_mark(&mut self, mark: &CommittedFileId) {
        let Self::CommittedFiles(files) = self else {
            return;
        };

        if remove_from_non_empty(files, |marked| marked == mark) {
            *self = Self::Empty;
        }
    }
}

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Complete the operation with files from one commit, then repeat for the next commit.
  2. If multi-commit file moves are genuinely needed, first move/squash the commits so the files share one commit.
  3. Fix the TUI selection so files from other commits are not selectable while a committed-file mark session is active.

Example fix

// before: marks hold files from commit A, user marks file from commit B
marks.insert_mark(CommittedFileId { commit_id: b, .. })?; // cannot mark files from multiple commits

// after: scope the session to one commit
assert_eq!(files.head.commit_id, mark.commit_id, "single-commit mark session");
Defensive patterns

Strategy: validation

Validate before calling

if let Marks::CommittedFiles(files) = &marks {
    if files.head.commit_id != mark.commit_id {
        anyhow::bail!("finish the current commit's file marks before marking another commit");
    }
}
marks.insert_mark(mark)?;

Type guard

fn same_commit_as_marks(marks: &Marks, commit_id: &CommitId) -> bool {
    match marks {
        Marks::Empty => true,
        Marks::CommittedFiles(files) => &files.head.commit_id == commit_id,
        _ => false,
    }
}

Try / catch

if let Err(err) = marks.insert_mark(file_id) {
    if err.to_string().contains("multiple commits") {
        // flush the current operation, reset marks, restart from the new commit
    } else { return Err(err); }
}

Prevention

When it happens

Trigger: Marking a file from commit B while files from commit A are already marked (e.g. marking a file shown under a different commit's section in the TUI).

Common situations: Users trying to gather files from several commits for one action; UI lists where commit boundaries are not obvious; new operations that would legitimately need multi-commit file selections.

Related errors


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