{"record":{"id":"0c0468adb51da116","repo":"jdx/mise","slug":"git-merge-file-failed","errorCode":null,"errorMessage":"git merge-file failed: {}","messagePattern":"git merge-file failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/shadow.rs","lineNumber":1370,"sourceCode":"        std::fs::write(&t, theirs)?;\n        let output = self.git.output_unchecked(PlumbingCall::new([\n            \"merge-file\",\n            \"-p\",\n            \"-L\",\n            \"local\",\n            \"-L\",\n            \"base\",\n            \"-L\",\n            \"remote\",\n            &o.to_string_lossy(),\n            &b.to_string_lossy(),\n            &t.to_string_lossy(),\n        ]))?;\n        // exit status is the number of conflicts; negative on error\n        match output.status.code() {\n            Some(0) => Ok(Some(output.stdout)),\n            Some(code) if code > 0 => Ok(None),\n            _ => bail!(\n                \"git merge-file failed: {}\",\n                String::from_utf8_lossy(&output.stderr).trim()\n            ),\n        }\n    }\n\n    /// Runs a network command with the user's git configuration.\n    pub(crate) fn network<'a>(\n        &self,\n        args: impl IntoIterator<Item = &'a str>,\n    ) -> Result<std::process::Output> {\n        self.git.network_output(PlumbingCall::new(args))\n    }\n\n    const ANNOTATION_TRAILER: &'static str = \"Mise-Annotation: \";\n\n    /// An annotation is an empty ordinary child commit, so labels and\n    /// descriptions travel with the same ancestry without rewriting it.","sourceCodeStart":1352,"sourceCodeEnd":1388,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/shadow.rs#L1352-L1388","documentation":"`git merge-file` exit status encodes the number of conflicts as a positive code, and success is 0. A negative code (or None) means the process itself failed — it was killed by a signal or never ran — rather than reporting merge conflicts. The tool treats conflicts (positive codes) as a normal 'no clean merge' result, but a negative code is an infrastructure failure, so it bails with git's stderr.","triggerScenarios":"Three-way merging a dotfile when `git merge-file` cannot execute: the temp file paths are missing/unwritable, git is broken, or the child process is killed by a signal (negative exit code from a signal death).","commonSituations":"Antivirus or system cleanup deletes temp files mid-operation; a sandboxed/limited environment prevents git from writing temp files; the system git installation is damaged.","solutions":["Run `git merge-file <ours> <base> <theirs>` manually on the reported files and observe the failure.","Verify `git --version` works and that the temp directory is writable.","Check for signal kills (OOM killer, AV interference) in system logs around the failure time.","Re-run the dotfile sync; if persistent, reinstall or repair the system git."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"for f in [ours, base, theirs] {\n    assert!(Path::new(f).exists(), \"merge-file input {f} missing\");\n}","typeGuard":null,"tryCatchPattern":"match history.three_way_merge(ours, base, theirs) {\n    Ok(Some(out)) => write_clean(out),\n    Ok(None) => prompt_manual_conflict_resolution(),\n    Err(e) if e.to_string().contains(\"git merge-file failed\") => {\n        eprintln!(\"merge-file process failed (signal/IO): {e}\");\n        fallback_to_manual_merge()?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep temp files on a local writable filesystem","Configure AV/endpoint tools to skip git temp files","Repair a broken system git installation promptly"],"tags":["git","merge-file","subprocess","dotfiles"],"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"}