GitoxideLabs/gitoxide · error

aborted without changes: refusing to materialize a conflict…

Error message

{operation} aborted without changes: refusing to materialize a conflict without a continuation output file

What it means

When conflict materialization is requested with destination '-' (stdout), tix refuses to proceed if stdout is a terminal, because conflict markers piped to an interactive terminal cannot serve as a continuation file. The operation aborts without changes. A real output file is required to continue the rebase later.

Solutions

  1. Redirect stdout to a file: `tix rebase apply ... --materialize-conflicts - > continuation.out`, or run non-interactively
  2. Pass a concrete file path instead of '-': `--materialize-conflicts ./rebase-continuation`
  3. Resolve the conflict upstream so no materialization is needed

Example fix

// before
tix rebase apply plan --materialize-conflicts -
// after
tix rebase apply plan --materialize-conflicts continuation.txt
Defensive patterns

Strategy: validation

Validate before calling

if dest == Path::new("-") && std::io::stdout().is_terminal() {
    return Err(anyhow!("use a file path for --materialize-conflicts in interactive shells"));
}

Try / catch

if let Err(e) = cmd.status() {
    if format!("{e}").contains("refusing to materialize") {
        eprintln!("redirect stdout or pass a file path");
    }
}

Prevention

When it happens

Trigger: Calling `tix rebase apply --materialize-conflicts -` from an interactive shell (stdout is a TTY) while a plan conflict occurs in handle_plan_conflict.

Common situations: Developer runs the command directly in a terminal instead of redirecting output to a file; scripts accidentally inherit a TTY; confusion between '-' (stdout) and a real path.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/command/rebase.rs:262

        }
    }
}

pub(super) fn handle_plan_conflict(
    repo: &gix::Repository,
    mut conflict: rebase::PlanConflict,
    materialize_conflicts: Option<&Path>,
    tips: &[ObjectId],
    operation: &str,
) -> Result<()> {
    let Some(destination) = materialize_conflicts else {
        anyhow::bail!(
            "{operation} aborted without changes: conflict while applying {}; pass --materialize-conflicts to opt in",
            conflict.original().to_hex_with_len(7)
        );
    };
    if destination == Path::new("-") && std::io::stdout().is_terminal() {
        anyhow::bail!(
            "{operation} aborted without changes: refusing to materialize a conflict without a continuation output file"
        );
    }
    conflict.persist_objects()?;
    let plan = conflict.continuation_plan();
    let mapped_tips = tips.iter().filter_map(|id| conflict.map(*id)).collect();
    let continuation = todo::prepare_continuation(conflict.repository(), &plan, mapped_tips, true)?.document;
    let revisions = mapped_revisions(tips, |id| conflict.map(id));
    if destination == Path::new("-") {
        let mut stdout = std::io::stdout().lock();
        stdout
            .write_all(&continuation)
            .and_then(|_| stdout.flush())
            .context("could not write the continuation rebase todo")?;
    } else {
        let mut output = std::fs::OpenOptions::new()
            .write(true)
            .create_new(true)

View on GitHub (pinned to e73179060b)