{"record":{"id":"2afed5bdb4af8cd9","repo":"gitbutlerapp/gitbutler","slug":"cannot-mark-files-from-multiple-commits","errorCode":null,"errorMessage":"cannot mark files from multiple commits","messagePattern":"cannot mark files from multiple commits","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/command/legacy/status/tui/app/mark.rs","lineNumber":430,"sourceCode":"}\n\nimpl MarkStore<CommittedFileId> for Marks {\n    type Error = anyhow::Error;\n\n    fn contains_mark(&self, mark: &CommittedFileId) -> bool {\n        self.as_committed_files()\n            .is_some_and(|files| files.iter().any(|file| file == mark))\n    }\n\n    fn insert_mark(&mut self, mark: CommittedFileId) -> Result<(), Self::Error> {\n        if self.contains_mark(&mark) {\n            return Ok(());\n        }\n        match self {\n            Self::Empty => *self = Self::CommittedFiles(NonEmpty::new(mark)),\n            Self::CommittedFiles(files) => {\n                if files.head.commit_id != mark.commit_id {\n                    anyhow::bail!(\"cannot mark files from multiple commits\");\n                }\n                files.push(mark);\n            }\n            _ => anyhow::bail!(\"cannot mix mark sources\"),\n        }\n        Ok(())\n    }\n\n    fn remove_mark(&mut self, mark: &CommittedFileId) {\n        let Self::CommittedFiles(files) = self else {\n            return;\n        };\n\n        if remove_from_non_empty(files, |marked| marked == mark) {\n            *self = Self::Empty;\n        }\n    }\n}","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/command/legacy/status/tui/app/mark.rs#L412-L448","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Complete the operation with files from one commit, then repeat for the next commit.","If multi-commit file moves are genuinely needed, first move/squash the commits so the files share one commit.","Fix the TUI selection so files from other commits are not selectable while a committed-file mark session is active."],"exampleFix":"// before: marks hold files from commit A, user marks file from commit B\nmarks.insert_mark(CommittedFileId { commit_id: b, .. })?; // cannot mark files from multiple commits\n\n// after: scope the session to one commit\nassert_eq!(files.head.commit_id, mark.commit_id, \"single-commit mark session\");","handlingStrategy":"validation","validationCode":"if let Marks::CommittedFiles(files) = &marks {\n    if files.head.commit_id != mark.commit_id {\n        anyhow::bail!(\"finish the current commit's file marks before marking another commit\");\n    }\n}\nmarks.insert_mark(mark)?;","typeGuard":"fn same_commit_as_marks(marks: &Marks, commit_id: &CommitId) -> bool {\n    match marks {\n        Marks::Empty => true,\n        Marks::CommittedFiles(files) => &files.head.commit_id == commit_id,\n        _ => false,\n    }\n}","tryCatchPattern":"if let Err(err) = marks.insert_mark(file_id) {\n    if err.to_string().contains(\"multiple commits\") {\n        // flush the current operation, reset marks, restart from the new commit\n    } else { return Err(err); }\n}","preventionTips":["Treat each commit's file marks as one session; complete or clear before switching commits.","Make files under other commits non-selectable while a committed-file session is active.","Add tests for cross-commit marking attempts."],"tags":["tui","internal","invariant","selection","committed-files"],"backgroundTag":"selection-type-mismatch","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}