gitbutlerapp/gitbutler · error
Could not reset submodule at
Error message
Could not reset submodule at '{sm_dir}' to commit {id}: {err} What it means
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.
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.
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: is the submodule clean and at resettable state?
let ok = Command::new("git").args(["status", "--porcelain"])
.current_dir(submodule_repo_dir).output()?.status.success(); Try / catch
// parse stderr from the error chain to branch on cause
let msg = format!("{err:#}");
if msg.contains("local changes") { stash_and_retry()?; }
else if msg.contains("not found") { fetch_submodule_then_retry()?; }
else { return Err(err); } Prevention
- 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)
When it happens
Trigger: 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).
Common situations: 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.
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
- Aborting due to empty branch name
- Ad-hoc (single-branch) branch moves are not supported…
- An octopus merge commits must have at least two parents
- another pre-commit hook is already using the repository…
- askpass broker must be initialized
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/df2cb82d30e8a5f4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/tree_manipulation/discard_worktree_changes.rs:311
}
update_index(gix::index::fs::Metadata::from_path_no_follow(&link_path)?)?;
}
EntryKind::Commit => {
if let RestoreMode::Update = mode {
// TODO(gix): actual checkout/reset functionality - it will be fine to support that fully.
// Since `git2` doesn't support filters, it will save us some trouble to just use Git for that.
let submodule_repo_dir = &file_path;
let out = std::process::Command::from(
gix::command::prepare(format!(
"git reset --hard {id} && git clean -fxd",
id = state.id
))
.with_shell(),
)
.current_dir(submodule_repo_dir)
.output()?;
if !out.status.success() {
bail!(
"Could not reset submodule at '{sm_dir}' to commit {id}: {err}",
sm_dir = submodule_repo_dir.display(),
id = state.id,
err = out.stderr.as_bstr()
);
}
} else {
let sm_repo = repo
.submodules()?
.into_iter()
.flatten()
.find_map(|sm| {
let is_active = sm.is_active().ok()?;
is_active.then(|| -> anyhow::Result<_> {
Ok(
if sm.path().ok().is_some_and(|sm_path| sm_path == rela_path) {
sm.open()?
} else {View on GitHub (pinned to 58e5313667)