jdx/mise · error

cannot merge dotfile Git history

Error message

cannot merge dotfile Git history: {}

What it means

mise runs `git merge-tree --no-messages -z` (via plumbing) to compute a merge of the local and remote dotfile histories. Exit code 1 signals conflicts and is tolerated, but any other non-success git exit triggers this error with git's stderr. It means the history-merge operation itself failed rather than producing a conflicted merge.

Solutions

  1. Check the embedded stderr message for the exact git failure.
  2. Confirm both refs resolve in the history repo (`git rev-parse <local> <remote>`).
  3. Ensure git is recent enough for the merge-tree flags used (`git --version`).
  4. If history objects are missing, re-clone/rebuild the dotfile history repo or reset the sync state.

Example fix

// before
Error: cannot merge dotfile Git history: fatal: not a valid object name origin/main

// after: fix the stale remote ref in the history repo
$ cd ~/.local/share/mise/dotfiles-history
$ git fetch origin
Defensive patterns

Strategy: try-catch

Validate before calling

let local_ok = git_ok(["rev-parse", "--verify", local]);
let remote_ok = git_ok(["rev-parse", "--verify", remote]);
if !(local_ok && remote_ok) { eprintln!("history refs missing; refetch remote"); }

Type guard

fn merge_inputs_valid(local: &str, remote: &str) -> bool {
  git_ok(["rev-parse", "--verify", local])
    && git_ok(["rev-parse", "--verify", remote])
}

Try / catch

match result {
  Err(e) if e.to_string().contains("cannot merge dotfile Git history") => {
    // refetch/rebuild history refs, check git version
  }
  other => other?,
}

Prevention

When it happens

Trigger: Calling the dotfile history sync/merge path when `git merge-tree` fails outright: invalid or unknown ref names, corrupt object database, or an object missing from the shadow repo.

Common situations: Remote history rewritten/force-pushed so objects referenced locally are gone; the shadow history repo partially pruned; git version too old to support the merge-tree flags used.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/36fb451065e0de2a. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/shadow.rs:1540

        }
        let text = String::from_utf8(output.stdout)?;
        Ok(text.lines().map(str::to_owned).collect())
    }

    /// Merge complete ordinary trees using Git's recursive merge-base handling.
    /// Conflict trees are never adopted or written into live configuration.
    pub(crate) fn merge_tree(&self, local: &str, remote: &str) -> Result<(String, Vec<String>)> {
        let output = self.git.output_unchecked(PlumbingCall::new([
            "merge-tree",
            "--write-tree",
            "--name-only",
            "--no-messages",
            "-z",
            local,
            remote,
        ]))?;
        if !output.status.success() && output.status.code() != Some(1) {
            bail!(
                "cannot merge dotfile Git history: {}",
                String::from_utf8_lossy(&output.stderr).trim()
            );
        }
        let text = String::from_utf8(output.stdout)?;
        let mut fields = text.split('\0');
        let tree = fields
            .next()
            .filter(|tree| !tree.is_empty())
            .ok_or_else(|| eyre::eyre!("Git did not return a merged tree"))?
            .to_owned();
        let conflicts = fields
            .filter(|path| !path.is_empty())
            .map(str::to_owned)
            .collect();
        Ok((tree, conflicts))
    }

View on GitHub (pinned to 533346cc37)