jdx/mise · error
repos: {} has local changes; commit, stash, or clean them be
Error message
repos: {} has local changes; commit, stash, or clean them before bootstrap What it means
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.
Source
Thrown at src/system/repos.rs:161
})
}
}
impl std::fmt::Display for RepoRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", file::display_path(&self.path))
}
}
pub fn status(requests: &[RepoRequest]) -> Result<Vec<RepoStatus>> {
requests.iter().map(status_one).collect()
}
pub fn preflight_statuses(statuses: &[RepoStatus]) -> Result<()> {
for status in statuses {
match &status.state {
RepoState::Dirty => {
bail!(
"repos: {} has local changes; commit, stash, or clean them before bootstrap",
status.request
);
}
RepoState::Conflict(reason) => {
bail!("repos: {}: {reason}", status.request);
}
RepoState::Current | RepoState::Missing | RepoState::Differs => {}
}
}
Ok(())
}
/// Apply statuses previously validated with [`preflight_statuses`].
pub(crate) fn apply_statuses(statuses: &[RepoStatus], dry_run: bool) -> Result<()> {
for status in statuses {
match &status.state {
RepoState::Current => {View on GitHub (pinned to 9dcfcaa0dc)
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
Example fix
# before: mise bootstrap fails with "has local changes" $ git -C ~/src/dotfiles status --porcelain ?? local.txt M README.md # after $ git -C ~/src/dotfiles stash -u $ mise bootstrap
Defensive patterns
Strategy: validation
Validate before calling
fn repo_is_clean(path: &Path) -> bool {
std::process::Command::new("git")
.arg("-C").arg(path)
.args(["status", "--porcelain=v1"])
.output()
.map(|o| o.status.success() && o.stdout.is_empty())
.unwrap_or(false)
}
// before `mise bootstrap`:
assert!(repos.iter().all(|r| repo_is_clean(&r.path)), "worktree not clean"); Prevention
- 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
When it happens
Trigger: Running `mise bootstrap` (or `mise system install` flows that apply repos) while any `[bootstrap.repos]` checkout has uncommitted modifications, staged changes, or untracked files.
Common situations: 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.
Related errors
- repos: {}: {reason}
- git -C {} {} failed: {}
- git failed with status {status}
- no configured repo matched path: {filter}
- relative repo paths are only allowed in a project config; us
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/48386a4a220c2020.
Report an issue: GitHub.