{"record":{"id":"df2cb82d30e8a5f4","repo":"gitbutlerapp/gitbutler","slug":"could-not-reset-submodule-at-sm-dir-to-commit","errorCode":null,"errorMessage":"Could not reset submodule at '{sm_dir}' to commit {id}: {err}","messagePattern":"Could not reset submodule at '(.+?)' to commit (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/tree_manipulation/discard_worktree_changes.rs","lineNumber":311,"sourceCode":"                }\n                update_index(gix::index::fs::Metadata::from_path_no_follow(&link_path)?)?;\n            }\n            EntryKind::Commit => {\n                if let RestoreMode::Update = mode {\n                    // TODO(gix): actual checkout/reset functionality - it will be fine to support that fully.\n                    // Since `git2` doesn't support filters, it will save us some trouble to just use Git for that.\n                    let submodule_repo_dir = &file_path;\n                    let out = std::process::Command::from(\n                        gix::command::prepare(format!(\n                            \"git reset --hard {id} && git clean -fxd\",\n                            id = state.id\n                        ))\n                        .with_shell(),\n                    )\n                    .current_dir(submodule_repo_dir)\n                    .output()?;\n                    if !out.status.success() {\n                        bail!(\n                            \"Could not reset submodule at '{sm_dir}' to commit {id}: {err}\",\n                            sm_dir = submodule_repo_dir.display(),\n                            id = state.id,\n                            err = out.stderr.as_bstr()\n                        );\n                    }\n                } else {\n                    let sm_repo = repo\n                        .submodules()?\n                        .into_iter()\n                        .flatten()\n                        .find_map(|sm| {\n                            let is_active = sm.is_active().ok()?;\n                            is_active.then(|| -> anyhow::Result<_> {\n                                Ok(\n                                    if sm.path().ok().is_some_and(|sm_path| sm_path == rela_path) {\n                                        sm.open()?\n                                    } else {","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/tree_manipulation/discard_worktree_changes.rs#L293-L329","documentation":"Wraps a failed attempt to reset a submodule working directory to the recorded commit via a shell git command (git reset) run inside the submodule's repository directory. The subprocess exited non-zero and the raw stderr is embedded in the message. It indicates the submodule could not be moved to the commit the outer discard operation requires.","triggerScenarios":"restore_state_to_worktree encounters a submodule change whose target state commit differs from the submodule's current HEAD and runs `git reset` in the submodule dir; the command fails (dirty submodule, missing objects, merge/rebase in progress, corrupt repo).","commonSituations":"Submodules with local uncommitted changes or an in-progress rebase/merge; submodules cloned shallowly so the target commit is missing; read-only or CI sandboxes where git cannot write into the submodule; git hooks or config interfering.","solutions":["Inspect the {err} stderr in the message; run the same `git reset <commit>` manually inside the submodule to see the full failure.","Commit or stash local changes inside the submodule, then retry the discard.","Fetch the missing commit in the submodule (git fetch --unshallow or fetch origin) so the target commit exists.","Abort any in-progress merge/rebase in the submodule (git rebase --abort / git merge --abort) before resetting."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// preflight: is the submodule clean and at resettable state?\nlet ok = Command::new(\"git\").args([\"status\", \"--porcelain\"])\n    .current_dir(submodule_repo_dir).output()?.status.success();","typeGuard":null,"tryCatchPattern":"// parse stderr from the error chain to branch on cause\nlet msg = format!(\"{err:#}\");\nif msg.contains(\"local changes\") { stash_and_retry()?; }\nelse if msg.contains(\"not found\") { fetch_submodule_then_retry()?; }\nelse { return Err(err); }","preventionTips":["Keep submodules clean (commit or stash) before running discard operations","Initialize and fully fetch submodules so target commits exist locally","Avoid running discards while a submodule rebase/merge is in progress","Include the recorded submodule commit in CI checkouts (git submodule update --init)"],"tags":["git","submodule","command-failed"],"backgroundTag":"git-command-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}