GitoxideLabs/gitoxide · error · anyhow::Error

an undo entry requires the exact previous reference value

Error message

an undo entry requires the exact previous reference value

What it means

Conversion failure in `edit::undo::state_from_expected`, called from `from_edit` when building undo queue state from a planned reference change. A `PreviousValue` expresses what a ref must have looked like before the edit; only `MustNotExist`, `MustExistAndMatch(target)` map cleanly onto an undo State. The wildcard variants (`Any`, `MustExist`, `Existing...`) carry no concrete value to restore, so an exact previous value cannot be recorded and the conversion bails. It indicates an undo entry was planned against a reference whose pre-edit value was not pinned; fix by ensuring the edit pins the exact expected prior ref value before it can be undone.

Solutions

  1. Record the undo entry against the reference's actual current value so the expected PreviousValue matches what the repository contains
  2. Regenerate the undo queue (drop the stale entries) so subsequent expectations are rebuilt from the real reference states
  3. If the reference's prior value is genuinely unknown, use PreviousValue::Any rather than asserting a specific prior state
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at gix-tix/src/edit/undo.rs:370 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/9490ee9cf29e6ff8. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/undo.rs:370

                existing.after = change.after;
            }
            None => {
                by_name.insert(change.name.clone(), change);
            }
        }
    }
    Ok(by_name
        .into_values()
        .filter(|change| change.before != change.after)
        .collect())
}

fn state_from_expected(expected: &PreviousValue) -> Result<State> {
    match expected {
        PreviousValue::MustNotExist => Ok(State::Missing),
        PreviousValue::MustExistAndMatch(target) => Ok(state_from_target(target)),
        PreviousValue::Any | PreviousValue::MustExist | PreviousValue::ExistingMustMatch(_) => {
            bail!("an undo entry requires the exact previous reference value")
        }
    }
}

fn state_from_target(target: &Target) -> State {
    match target {
        Target::Object(id) => State::Object(*id),
        Target::Symbolic(name) => State::Symbolic(name.clone()),
    }
}

fn state_from_target_ref(target: gix::refs::TargetRef<'_>) -> State {
    match target {
        gix::refs::TargetRef::Object(id) => State::Object(id.to_owned()),
        gix::refs::TargetRef::Symbolic(name) => State::Symbolic(name.to_owned()),
    }
}

View on GitHub (pinned to e73179060b)