affaan-m/ECC · error

git restore failed for

Error message

git restore failed for {}: {stderr}

What it means

Thrown by reset_path when `git restore` used to discard unstaged changes for a status entry exits non-zero. The function wraps the git CLI and forwards its stderr in the message. It signals that discarding the file's working-tree changes failed at the git level.

Solutions

  1. Inspect the stderr in the message for git's specific failure reason.
  2. Skip reset_path for untracked entries (entry.untracked == true); delete the file or use remove_path semantics instead.
  3. Confirm the file still exists and is tracked: `git ls-files --error-unmatch <path>` before restoring.
  4. Check sparse-checkout configuration if stderr mentions excluded paths.

Example fix

// before: reset regardless of state
reset_path(&worktree, &entry)?;

// after: only reset tracked files
if !entry.untracked {
    reset_path(&worktree, &entry)?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn can_reset(worktree: &WorktreeInfo, entry: &GitStatusEntry) -> bool {
    !entry.untracked
        && worktree.path.join(&entry.path).exists()
        && worktree.path.join(".git").exists()
}

Type guard

fn is_tracked_modified(entry: &GitStatusEntry) -> bool { !entry.untracked && entry.unstaged }

Try / catch

match reset_path(&worktree, &entry) {
    Ok(()) => refresh_status(),
    Err(e) => show_dialog("Discard failed", &format!("{e:#}")),
}

Prevention

When it happens

Trigger: Calling reset_path(worktree, entry) when `git restore -- <path>` fails: file is untracked (restore only works on tracked files), path no longer exists, checkout conflicts with sparse-checkout settings, or the repo lacks a HEAD.

Common situations: Discarding changes on an untracked file (should be deleted instead of restored); file deleted from disk but still in the index; running inside a shallow or empty clone where HEAD is unborn; sparse-checkout entries excluded from the cone.

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 affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/a6091ab4ebc802f8. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/worktree/mod.rs:353

        } else {
            fs::remove_file(&target)
                .with_context(|| format!("Failed to remove {}", target.display()))?;
        }
        return Ok(());
    }

    let output = Command::new("git")
        .arg("-C")
        .arg(&worktree.path)
        .args(["restore", "--source=HEAD", "--staged", "--worktree", "--"])
        .arg(&entry.path)
        .output()
        .with_context(|| format!("Failed to reset {}", entry.path))?;
    if output.status.success() {
        Ok(())
    } else {
        let stderr = String::from_utf8_lossy(&output.stderr);
        anyhow::bail!("git restore failed for {}: {stderr}", entry.path);
    }
}

pub fn git_status_patch_view(
    worktree: &WorktreeInfo,
    entry: &GitStatusEntry,
) -> Result<Option<GitStatusPatchView>> {
    if entry.untracked {
        return Ok(None);
    }

    let staged_patch =
        git_diff_patch_text_for_paths(&worktree.path, &["--cached"], &[entry.path.clone()])?;
    let unstaged_patch = git_diff_patch_text_for_paths(&worktree.path, &[], &[entry.path.clone()])?;

    let mut sections = Vec::new();
    let mut hunks = Vec::new();

View on GitHub (pinned to 8321021c54)