{"record":{"id":"9c8224aae6188976","repo":"jdx/mise","slug":"git-diff-failed","errorCode":null,"errorMessage":"git diff failed ({}): {}","messagePattern":"git diff failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/shadow.rs","lineNumber":919,"sourceCode":"                    output: vec![],\n                    changed: true,\n                }),\n                _ => bail!(\"git diff failed ({status})\"),\n            };\n        }\n        let output = self.git.output_unchecked(call)?;\n        match output.status.code() {\n            Some(0) => Ok(DiffResult {\n                output: output.stdout,\n                changed: false,\n            }),\n            Some(1) => Ok(DiffResult {\n                output: output.stdout,\n                changed: true,\n            }),\n            _ => {\n                let stderr = String::from_utf8_lossy(&output.stderr);\n                bail!(\"git diff failed ({}): {}\", output.status, stderr.trim())\n            }\n        }\n    }\n\n    /// Decrypt only for this requested comparison. Git's no-index diff sees\n    /// private temporary files, never plaintext objects in the repository.\n    fn decrypted_diff(&self, a: &str, b: &str, opts: &DiffOpts) -> Result<DiffResult> {\n        use std::io::Write;\n        let mut paths = BTreeSet::new();\n        for (tree, prefix) in [\n            (a, opts.paths.as_ref().map(|p| p.0.as_str())),\n            (b, opts.paths.as_ref().map(|p| p.1.as_str())),\n        ] {\n            for entry in self.ls_tree(tree)? {\n                if entry.path.starts_with(\".mise-history/\") {\n                    continue;\n                }\n                if let Some(prefix) = prefix {","sourceCodeStart":901,"sourceCodeEnd":937,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/shadow.rs#L901-L937","documentation":"In the general (non-fast-path) diff, git's exit code is checked after capturing stdout/stderr. Any code other than 0 (unchanged) or 1 (changed) is treated as a git failure, and the error includes the status and trimmed stderr to expose git's actual complaint.","triggerScenarios":"diff runs git diff via output_unchecked and receives an exit code other than 0 or 1; the bail surfaces git's stderr (e.g. 'fatal: bad object <oid>').","commonSituations":"Missing/pruned git objects for old snapshots; bad object ids in stored history metadata; git version incompatibilities; permission problems reading the repository.","solutions":["Read the stderr embedded in the message to identify git's specific failure","Verify snapshot oids exist (`git cat-file -e <oid>`) and restore missing objects (`git fetch`/re-capture)","Run `git fsck` on the history repository to find corruption"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Shell: surface git's real error before calling the library\ngit cat-file -e \"$FROM_OID\" && git cat-file -e \"$TO_OID\" || echo \"missing snapshot object\"","typeGuard":null,"tryCatchPattern":"match differ.diff(from, to, paths) {\n    Err(e) if e.to_string().contains(\"git diff failed\") => {\n        eprintln!(\"{e}\"); // message already embeds git's trimmed stderr\n    }\n    other => other?,\n}","preventionTips":["Read the stderr embedded in the error to diagnose git-level causes","Keep history repo objects intact (no aggressive gc/prune of snapshot blobs)","Ensure the git binary on PATH is functional and compatible"],"tags":["git","diff","process"],"backgroundTag":"git-command-failed","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}