GitoxideLabs/gitoxide · error
cannot delete the checked-out branch without selecting…
Error message
cannot delete the checked-out branch without selecting another checkout
What it means
A ref-update batch tried to delete the currently checked-out branch while the session had neither selected another checkout nor requested a checkout after finish (or the repo is bare). Deleting HEAD's branch would leave HEAD dangling, so the update is rejected before applying deletions.
Solutions
- Select another branch/commit for checkout (`selected`) before deleting the current branch
- Set `checkout_after_finish = true` so the tool moves HEAD off the branch first
- Skip the branch currently pointed at by HEAD in the deletion list
Example fix
// before
session.delete_ref(current_branch)?;
// after
if current_branch == repo.head_branch() {
session.select_checkout(other_branch);
}
session.delete_ref(current_branch)?; Defensive patterns
Strategy: validation
Validate before calling
fn deletion_is_safe(head_branch: &RefName, targets: &[RefName], selected: Option<&RefName>, checkout_after_finish: bool, bare: bool) -> bool {
!targets.contains(&head_branch) || selected.is_some() || checkout_after_finish || bare == false && false
} Try / catch
match result {
Err(e) if e.to_string().contains("checked-out branch") => {
eprintln!("select another checkout or enable checkout-after-finish before deleting HEAD's branch");
}
r => r?,
} Prevention
- Exclude the HEAD branch from automated pruning lists
- Always provide an alternate checkout when deleting the current branch
- Filter ref lists against repo.head_branch() before batch deletion
When it happens
Trigger: Calling the rebase/ref-edit session with a deletion of the branch that `HEAD` points to, with `self.selected == None` and `checkout_after_finish == false` in a worktree repo, or any such deletion in a bare repo where checkout re-selection is impossible.
Common situations: Automation pruning 'stale' branches that unknowingly includes the checked-out one; scripts deleting the feature branch right after rebasing while still on it; deleting branches parsed from an unfiltered ref list.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Refusing to checkout index into existing directory
- One or more errors occurred - checkout is incomplete
- One or more errors occurred - checkout is incomplete
- {notice}
- the current checkout has a pending rebase; time-travel to…
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/e9a2c40faf1f557a.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:2214
for transition in &transitions {
super::forget::preflight_tree_transition(
&transition.repo,
&transition.workdir,
transition.old,
transition.new,
)?;
}
let head = self.repo.head()?;
let unborn = head.is_unborn();
let current_ref = head.referent_name().map(ToOwned::to_owned);
let mut deferred_ref_deletions = Vec::new();
if let (Some(expected_refs), Some(current_ref)) = (&mut self.expected_refs, current_ref) {
if expected_refs
.iter()
.any(|expected| expected.name == current_ref && expected.old.is_some() && expected.new.is_none())
&& (self.repo.workdir().is_none() || (self.selected.is_none() && !self.checkout_after_finish))
{
anyhow::bail!("cannot delete the checked-out branch without selecting another checkout");
}
expected_refs.retain(|expected| {
let defer = expected.name == current_ref && expected.old.is_some() && expected.new.is_none();
if defer {
deferred_ref_deletions.push((expected.name.clone(), expected.old.expect("checked above")));
}
!defer
});
}
let updated_refs = update_refs(
&self.repo,
&self.rewritten,
unborn,
self.selected,
&self.committer,
self.expected_refs.take(),
(&self.pins, &self.delete_refs),
resource_edits,View on GitHub (pinned to e73179060b)