{"record":{"id":"05b96d23a270d04f","repo":"affaan-m/ECC","slug":"git-reset-failed-for-path-stderr","errorCode":null,"errorMessage":"git reset failed for {path}: {stderr}","messagePattern":"git reset failed for (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/worktree/mod.rs","lineNumber":320,"sourceCode":"    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\"git add failed for {path}: {stderr}\");\n    }\n}\n\npub fn unstage_path(worktree: &WorktreeInfo, path: &str) -> Result<()> {\n    let output = Command::new(\"git\")\n        .arg(\"-C\")\n        .arg(&worktree.path)\n        .args([\"reset\", \"HEAD\", \"--\"])\n        .arg(path)\n        .output()\n        .with_context(|| format!(\"Failed to unstage {}\", path))?;\n    if output.status.success() {\n        Ok(())\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\"git reset failed for {path}: {stderr}\");\n    }\n}\n\npub fn reset_path(worktree: &WorktreeInfo, entry: &GitStatusEntry) -> Result<()> {\n    if entry.untracked {\n        let target = worktree.path.join(&entry.path);\n        if !target.exists() {\n            return Ok(());\n        }\n        let metadata = fs::symlink_metadata(&target)\n            .with_context(|| format!(\"Failed to inspect untracked path {}\", target.display()))?;\n        if metadata.is_dir() {\n            fs::remove_dir_all(&target)\n                .with_context(|| format!(\"Failed to remove {}\", target.display()))?;\n        } else {\n            fs::remove_file(&target)\n                .with_context(|| format!(\"Failed to remove {}\", target.display()))?;\n        }","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L302-L338","documentation":"unstage_path runs `git -C <worktree.path> reset HEAD -- <path>`. On a non-zero exit git's stderr is re-thrown. The most common cause is an unborn branch (no HEAD commit yet) where `HEAD` is not a valid ref to reset against, or a path that was never in the index.","triggerScenarios":"Calling unstage_path on a brand-new repository with zero commits (HEAD does not resolve); unstaging a path that was never staged; unstaging after the index was already reset by another process; `.git/index.lock` contention.","commonSituations":"Fresh worktree on an empty base branch where the first commit has not been made; UI double-firing an unstage action; race between two UI sessions editing the same worktree's index.","solutions":["On unborn branches use `git rm --cached -- <path>` semantics instead of `reset HEAD` (requires a code path that detects the empty-HEAD case).","Refresh `git_status_entries` first and only call unstage_path for entries where `entry.staged == true`.","Remove a stale `.git/index.lock`.","Confirm HEAD resolves (`git -C <path> rev-parse --verify HEAD`) before calling."],"exampleFix":"// before\nunstage_path(&worktree, &entry.path)?;\n\n// after\nif !entry.staged {\n    return Ok(()); // nothing to unstage\n}\nunstage_path(&worktree, &entry.path)?;","handlingStrategy":"validation","validationCode":"fn can_unstage(worktree: &WorktreeInfo, path: &str) -> anyhow::Result<bool> {\n    let entries = git_status_entries(worktree)?;\n    let Some(entry) = entries.iter().find(|e| e.path == path) else {\n        return Ok(false);\n    };\n    if !entry.staged {\n        return Ok(false); // nothing staged to unstage\n    }\n    // On unborn branches `reset HEAD` fails; detect that.\n    let has_head = std::process::Command::new(\"git\")\n        .arg(\"-C\").arg(&worktree.path)\n        .args([\"rev-parse\", \"--verify\", \"HEAD^{commit}\"])\n        .output()?.status.success();\n    Ok(has_head)\n}\n\nif !can_unstage(&worktree, &path)? {\n    return Ok(()); // no-op\n}\nunstage_path(&worktree, &path)?;","typeGuard":null,"tryCatchPattern":"match unstage_path(&worktree, &path) {\n    Ok(()) => { /* refresh */ }\n    Err(e) => {\n        let m = format!(\"{e:#}\");\n        if m.contains(\"unknown revision\") || m.contains(\"HEAD\") {\n            // unborn branch: fall back to `git rm --cached`\n        } else {\n            return Err(e);\n        }\n    }\n}","preventionTips":["Only offer the unstage affordance for entries where `entry.staged == true`.","Detect unborn branches in the UI and switch to `git rm --cached` semantics.","Disable the unstage button immediately after it fires to prevent double-dispatch."],"tags":["git","subprocess","worktree","index","unstage"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}