jdx/mise · error

global configuration at {shown} cannot be fast-forwarded to

Error message

global configuration at {shown} cannot be fast-forwarded to the transferred revision

What it means

During a dry-run `--update`, install_at checks whether the destination's current HEAD is an ancestor of the transferred revision (`git merge-base --is-ancestor`). If HEAD has diverged — containing commits the transferred revision does not have — a fast-forward is impossible and the tool aborts rather than performing a merge or rebase. Only strictly-behind or identical states are updatable.

Source

Thrown at src/system/remote_repository.rs:276

        }
        if update {
            if git(destination, &["symbolic-ref", "--short", "HEAD"])? != branch {
                bail!("global configuration branch differs from the transferred branch");
            }
            if dry_run {
                // the bundle holds the history from the checkout's commit on,
                // so the fast-forward is checked there without fetching into
                // the destination
                let head = git(destination, &["rev-parse", "HEAD"])?;
                if head == revision {
                    miseprintln!(
                        "Would keep {shown} at {revision}, already the transferred revision"
                    );
                } else if git(&checkout, &["merge-base", "--is-ancestor", &head, revision]).is_ok()
                {
                    miseprintln!("Would fast-forward {shown} from {head} to {revision}");
                } else {
                    bail!(
                        "global configuration at {shown} cannot be fast-forwarded to the transferred revision"
                    );
                }
                return Ok(destination.to_path_buf());
            }
            git(
                destination,
                &[
                    "fetch",
                    "--no-tags",
                    bundle
                        .to_str()
                        .ok_or_else(|| eyre::eyre!("invalid bundle path"))?,
                    "HEAD",
                ],
            )?;
            git(
                destination,

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Push/commit the destination's local commits to the source repository and regenerate the transfer bundle so it contains them.
  2. Discard divergent local commits if unwanted: `git -C <destination> reset --hard origin/<branch>` (verify first with `git log`).
  3. Rebase local commits onto the transferred revision manually, then retry so histories are linear again.
  4. If histories were rewritten intentionally, reset the destination to the transferred revision (after backing up local work).

Example fix

// before: destination has local commit not in transferred revision
git -C ~/.config/mise log --oneline origin/main..HEAD   # abc123 local change
# push it into the source repo, regenerate the bundle, then retry
// after: HEAD is an ancestor of the transferred revision; dry-run reports a fast-forward
Defensive patterns

Strategy: validation

Validate before calling

const head = execFileSync('git', ['-C', dest, 'rev-parse', 'HEAD']).toString().trim();
// ancestor check must run against the transferred revision; approximate by checking local-only commits
const ahead = execFileSync('git', ['-C', dest, 'log', '--oneline', `origin/${branch}..HEAD`]).toString();
if (ahead.trim().length > 0) throw new Error('destination has commits not in the transferred revision');

Prevention

When it happens

Trigger: Running `--update` (dry-run) where the destination checkout has local commits not present in the transferred revision, e.g. the bundle was created from a different, older, or diverged clone of the same origin.

Common situations: User committed directly in the destination config repo and never pushed; the transfer source was cloned from a stale state; history was rewritten (rebase/amend) on either side making the histories diverge.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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