{"record":{"id":"368b9013a4061fcf","repo":"jdx/mise","slug":"forced-publication-is-not-supported","errorCode":null,"errorMessage":"forced publication is not supported","messagePattern":"forced publication is not supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/network.rs","lineNumber":250,"sourceCode":"    pub(crate) fn push(\n        &self,\n        refspecs: &[String],\n        lease: Option<(&str, Option<&str>)>,\n    ) -> Result<PushOutcome> {\n        validate_url(&self.url)?;\n        let mut args = vec![\"push\".to_string(), \"--quiet\".to_string()];\n        if let Some((branch, expected)) = lease {\n            let name = format!(\"refs/heads/{branch}\");\n            let refs = self.ls_remote()?;\n            let observed = refs.iter().find(|(_, candidate)| candidate == &name);\n            if observed.map(|(oid, _)| oid.as_str()) != expected {\n                return Ok(PushOutcome::Rejected(\n                    \"the origin branch changed; fetch and reconcile before pushing\".into(),\n                ));\n            }\n        }\n        if refspecs.iter().any(|refspec| refspec.starts_with('+')) {\n            bail!(\"forced publication is not supported\");\n        }\n        args.push(\"--\".into());\n        args.push(self.url.clone());\n        args.extend(refspecs.iter().cloned());\n        let output = self.repo.network(args.iter().map(String::as_str))?;\n        if output.status.success() {\n            return Ok(PushOutcome::Done);\n        }\n        let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();\n        if stderr.contains(\"stale info\")\n            || stderr.contains(\"rejected\")\n            || stderr.contains(\"fetch first\")\n        {\n            return Ok(PushOutcome::Rejected(stderr));\n        }\n        Err(NetworkError(format!(\"pushing to {}: {stderr}\", self.url)).into())\n    }\n","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/network.rs#L232-L268","documentation":"The push method publishes history refs to the origin setup repository. It deliberately never performs force-pushes: a forced publication would overwrite upstream history that other machines may have based their state on. If any refspec starts with '+', the library bails instead of passing it to git.","triggerScenarios":"Calling Network::push with a refspec string beginning with '+' (e.g. \"+refs/heads/main:refs/heads/main\") — typically constructed by caller code that hit a non-fast-forward and tried to force it through.","commonSituations":"Diverged local and origin history after another machine rewrote/pushed history; attempting to recover from a rejected push by force; copy-pasted refspecs from generic git workflows that assume --force is fine.","solutions":["Fetch from origin and reconcile (merge/rebase) local history with the upstream branch, then push a fast-forward","Remove the '+' prefix from the refspec — use plain \"src:dst\" refspecs","If upstream was intentionally rewritten, follow the documented reconcile flow (fetch, realign, re-publish) rather than forcing"],"exampleFix":"// before\nnet.push(&repo, &[\"+refs/history/*:refs/history/*\"])?; // bails\n// after\nnet.fetch_with_depth(&repo, \"origin\", None)?; // reconcile first\nnet.push(&repo, &[\"refs/history/*:refs/history/*\"])?;","handlingStrategy":"try-catch","validationCode":"if refspecs.iter().any(|r| r.starts_with('+')) {\n    return Err(anyhow!(\"remove '+' from refspecs; force-push is not supported\"));\n}","typeGuard":null,"tryCatchPattern":"match net.push(&repo, &refspecs) {\n    Ok(PushOutcome::Pushed) => { /* done */ }\n    Ok(PushOutcome::Rejected(msg)) | Err(e) => {\n        // fetch and reconcile, then retry without forcing\n        net.fetch_with_depth(&repo, \"origin\", None)?;\n        reconcile_and_push()?;\n    }\n}","preventionTips":["Never construct refspecs with a leading '+' in automation touching history refs","On non-fast-forward rejection, always fetch + reconcile instead of reaching for force","Teach recovery scripts to use the documented reconcile flow"],"tags":["git","push","safety","history-sync"],"backgroundTag":"unsupported-operation","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}