affaan-m/ECC · error · anyhow::Error
git restore failed for {}: {stderr}
Error message
git restore failed for {}: {stderr} What it means
reset_path runs `git restore --source=HEAD --staged --worktree -- <path>` for tracked entries (untracked entries are deleted from the filesystem instead). The bail! re-throws git's stderr. Failure means git could not restore both the index and working tree to HEAD for that path.
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 01e15490f0)
Solutions
- Verify HEAD exists: `git -C <path> rev-parse --verify HEAD^{commit}` before calling reset_path.
- Handle the no-HEAD case by falling back to `git rm --cached` plus filesystem delete.
- Clear `.git/index.lock` if a previous git process was terminated.
- Run `git fsck` in the repo if object corruption is suspected.
Example fix
// before
reset_path(&worktree, &entry)?;
// after
let has_head = Command::new("git")
.arg("-C").arg(&worktree.path)
.args(["rev-parse", "--verify", "HEAD^{commit}"])
.output()?.status.success();
if !has_head {
anyhow::bail!("cannot reset: HEAD has no commits yet");
}
reset_path(&worktree, &entry)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_head_commit(worktree: &WorktreeInfo) -> anyhow::Result<bool> {
let out = std::process::Command::new("git")
.arg("-C").arg(&worktree.path)
.args(["rev-parse", "--verify", "HEAD^{commit}"])
.output()?;
Ok(out.status.success())
}
if !has_head_commit(&worktree)? {
anyhow::bail!("cannot reset tracked path: HEAD has no commits yet");
}
reset_path(&worktree, &entry)?; Try / catch
match reset_path(&worktree, &entry) {
Ok(()) => { /* refresh */ }
Err(e) => {
let m = format!("{e:#}");
if m.contains("unknown revision") || m.contains("HEAD") {
// fall back: remove from index + delete file
} else {
return Err(e);
}
}
} Prevention
- Disable 'reset to HEAD' on worktrees whose base branch has no commits.
- Run `git fsck` periodically on long-lived worktrees to catch object corruption early.
- Treat any reset_path failure after a concurrent rebase as a 'refresh and retry' signal.
When it happens
Trigger: Resetting a path on a repository with no HEAD commit yet (unborn branch); a path that does not exist in HEAD (e.g. it was added in a different branch and never committed to base); `.git/index.lock` held by another process; corrupted loose object referenced by HEAD.
Common situations: Reset-all action on a freshly created worktree whose base branch has no commits; HEAD moved out from under the operation by a concurrent rebase/checkout; object database corrupted by disk-full or hardware error.
Related errors
- git add failed for {path}: {stderr}
- git reset failed for {path}: {stderr}
- git worktree add failed: {stderr}
- git status failed: {stderr}
- cannot reset a staged hunk while the file also has unstaged
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/a6091ab4ebc802f8.
Report an issue: GitHub.