{"record":{"id":"48386a4a220c2020","repo":"jdx/mise","slug":"repos-has-local-changes-commit-stash-or-cle","errorCode":null,"errorMessage":"repos: {} has local changes; commit, stash, or clean them before bootstrap","messagePattern":"repos: (.+?) has local changes; commit, stash, or clean them before bootstrap","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/repos.rs","lineNumber":161,"sourceCode":"        })\n    }\n}\n\nimpl std::fmt::Display for RepoRequest {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", file::display_path(&self.path))\n    }\n}\n\npub fn status(requests: &[RepoRequest]) -> Result<Vec<RepoStatus>> {\n    requests.iter().map(status_one).collect()\n}\n\npub fn preflight_statuses(statuses: &[RepoStatus]) -> Result<()> {\n    for status in statuses {\n        match &status.state {\n            RepoState::Dirty => {\n                bail!(\n                    \"repos: {} has local changes; commit, stash, or clean them before bootstrap\",\n                    status.request\n                );\n            }\n            RepoState::Conflict(reason) => {\n                bail!(\"repos: {}: {reason}\", status.request);\n            }\n            RepoState::Current | RepoState::Missing | RepoState::Differs => {}\n        }\n    }\n    Ok(())\n}\n\n/// Apply statuses previously validated with [`preflight_statuses`].\npub(crate) fn apply_statuses(statuses: &[RepoStatus], dry_run: bool) -> Result<()> {\n    for status in statuses {\n        match &status.state {\n            RepoState::Current => {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/repos.rs#L143-L179","documentation":"Before applying any bootstrap changes, preflight_statuses inspects every configured repo; RepoState::Dirty is assigned when `git status --porcelain=v1` in that checkout returns any output — modified, staged, or untracked files all count. mise refuses to check out or pull in a dirty worktree to avoid destroying local changes, and this bail names the offending repo (path displayed home-tildefied) before anything is mutated.","triggerScenarios":"Running `mise bootstrap` (or `mise system install` flows that apply repos) while any `[bootstrap.repos]` checkout has uncommitted modifications, staged changes, or untracked files.","commonSituations":"Local edits in a dotfiles repo; generated files (build artifacts, `.env`, editor swap files) that are not gitignored — untracked files count as dirty; two bootstrap runs racing.","solutions":["Commit or stash the changes in the named repo — `git -C <repo> stash -u` also covers untracked files","Add untracked noise to the repo's .gitignore and commit that change","Remove the entry from `[bootstrap.repos]` if you manage that clone manually","Run `git -C <repo> status --porcelain` yourself to see exactly which lines make it dirty"],"exampleFix":"# before: mise bootstrap fails with \"has local changes\"\n$ git -C ~/src/dotfiles status --porcelain\n?? local.txt\n M README.md\n\n# after\n$ git -C ~/src/dotfiles stash -u\n$ mise bootstrap","handlingStrategy":"validation","validationCode":"fn repo_is_clean(path: &Path) -> bool {\n    std::process::Command::new(\"git\")\n        .arg(\"-C\").arg(path)\n        .args([\"status\", \"--porcelain=v1\"])\n        .output()\n        .map(|o| o.status.success() && o.stdout.is_empty())\n        .unwrap_or(false)\n}\n\n// before `mise bootstrap`:\nassert!(repos.iter().all(|r| repo_is_clean(&r.path)), \"worktree not clean\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `git status --porcelain` in every bootstrap repo before invoking bootstrap — untracked files count as dirty","Keep generated artifacts out of managed checkouts or gitignore them","Don't run two bootstrap operations over the same repos concurrently"],"tags":["mise","bootstrap","repos","git","dirty-worktree"],"backgroundTag":"dirty-worktree-rejected","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}