jdx/mise · error

unresolved Git index entry {} in {}

Error message

unresolved Git index entry {} in {}

What it means

Thrown when a Git index entry read from the source repository is not in a fully merged, stage-0 state (its mode/metadata suffix is not " 0"), i.e. the entry has unresolved merge conflicts. The library only accepts cleanly resolved index entries when enumerating tracked files.

Source

Thrown at src/system/files.rs:1968

    }
    output
        .stdout
        .split(|byte| *byte == 0)
        .filter(|record| !record.is_empty())
        .map(|record| {
            let tab = record
                .iter()
                .position(|byte| *byte == b'\t')
                .ok_or_else(|| {
                    eyre::eyre!(
                        "unexpected git ls-files output for {}",
                        source.display_user()
                    )
                })?;
            let metadata = &record[..tab];
            let path = path_buf_from_git_bytes(&record[tab + 1..]);
            if !metadata.ends_with(b" 0") {
                bail!(
                    "unresolved Git index entry {} in {}",
                    path.display(),
                    source.display_user()
                );
            }
            Ok(GitTrackedPath {
                is_gitlink: metadata.starts_with(b"160000 "),
                is_symlink: metadata.starts_with(b"120000 "),
                path,
            })
        })
        .collect()
}

/// Capture only the files selected by a Git manifest, preserving the source
/// repository and any untracked files around them.
pub(crate) fn capture_git_manifest(req: &FileRequest) -> Result<()> {
    for entry in git_tracked_paths(&req.source)? {

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Resolve the conflicts in the source repository: edit the conflicted files, `git add` them, and commit or `git merge --continue`.
  2. Abort the in-progress operation if it is unwanted (`git merge --abort` / `git rebase --abort`).
  3. Run `git status` in the source to identify the conflicted path shown in the message.

Example fix

// before: mid-merge with conflicts
$ cd ~/dotfiles && git status
Unmerged paths: both modified: .gitconfig

// after
$ cd ~/dotfiles
$ $EDITOR .gitconfig   # resolve conflict markers
$ git add .gitconfig && git commit
$ mise files apply
Defensive patterns

Strategy: validation

Validate before calling

use std::process::Command;
fn ensure_no_conflicts(source: &std::path::Path) -> anyhow::Result<()> {
    let out = Command::new("git").args(["ls-files", "--unmerged"])
        .current_dir(source).output()?;
    if !out.stdout.is_empty() {
        anyhow::bail!("unresolved merge conflicts in {}", source.display());
    }
    Ok(())
}

Try / catch

match result {
    Err(e) if e.to_string().contains("unresolved Git index entry") => {
        eprintln!("Resolve conflicts in the dotfiles repo (git status) and commit before applying: {e}");
    }
    Err(e) => return Err(e),
    Ok(plan) => apply(plan),
}

Prevention

When it happens

Trigger: Listing Git-tracked files in a source repo that is mid-merge or mid-rebase with unresolved conflicts: `git ls-files --stage`/cached listing returns multiple stages (1/2/3) for a conflicted path.

Common situations: User merged branches or pulled upstream dotfile changes and left conflicts unresolved, then ran `mise files apply`; an interrupted rebase left conflict markers in the index.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/f6284fbe40175d10. Report an issue: GitHub.