gitbutlerapp/gitbutler · warning · anyhow::Error
Branch '{ref_name}' has no tracking branch
Error message
Branch '{ref_name}' has no tracking branch What it means
resolve_tracking_branch_ref_name() first tries the configured upstream (branch.<name>.remote/merge via gix) and requires the tracking ref to exist; otherwise it scans refs/remotes/<remote>/<branch> across all remotes and only succeeds on exactly one match. This bail fires when there is no configured upstream and zero matches — or two or more remotes both carry the branch, making the fallback ambiguous.
Source
Thrown at crates/but-core/src/branch/mod.rs:63
.map(|reference| {
reference.map(|_| {
full_name
.try_into()
.expect("constructed remote-tracking refname must be valid")
})
})
})
.collect::<Result<Vec<gix::refs::FullName>, _>>()?;
if remote_matches.len() == 1 {
return Ok(Cow::Owned(
remote_matches
.pop()
.expect("exactly one remote match exists"),
));
}
bail!("Branch '{ref_name}' has no tracking branch")
}
/// A way to safely delete branches, which is only the case it's checked out nowhere.
pub mod safe_delete;
/// State for reuse when [safely deleting references](SafeDelete::delete_reference).
#[derive(Debug)]
pub struct SafeDelete {
/// A mapping of one or more worktree paths that are affected by changes to the keyed reference name.
worktrees_by_ref: WorktreePathByRef,
}
View on GitHub (pinned to caf1f223d3)
Solutions
- Push with an upstream: git push -u origin <branch>, which sets branch.<name>.remote/merge and creates the tracking ref
- Or set the config directly: git branch --set-upstream-to=origin/<branch>
- If two remotes match, prune the stale one (git remote prune) or remove the unused remote so exactly one match remains
- As a last resort for read-only resolution, compute the tracking ref name yourself from the known remote
Example fix
# before: local branch never pushed but stack sync # -> Branch 'refs/heads/feature' has no tracking branch # after: establish the upstream git push -u origin feature but stack sync
Defensive patterns
Strategy: validation
Validate before calling
// replicate the resolution order cheaply before acting
let configured = repo.branch_remote_tracking_ref_name(ref_name, gix::remote::Direction::Fetch)
.transpose()?.filter(|n| repo.try_find_reference(n.as_ref())?.is_some());
if configured.is_none() {
let matches: Vec<_> = repo.remote_names().iter().filter_map(|r| {
let full = format!("refs/remotes/{r}/{}", ref_name.shorten());
repo.try_find_reference(&full).ok().flatten().map(|_| full)
}).collect();
anyhow::ensure!(matches.len() == 1, "push first: git push -u origin {}", ref_name.shorten());
} Try / catch
match but_core::branch::resolve_tracking_branch_ref_name(&ref_name, &repo) {
Err(e) if e.to_string().contains("no tracking branch") =>
establish_upstream_and_retry(&ref_name).await?, // git push -u then retry
r => r?,
} Prevention
- Always create remote branches with 'git push -u' so upstream config exists from commit one
- Prune stale remotes after renames: git remote prune origin
- When a branch exists on multiple remotes, set branch.<name>.remote explicitly to disambiguate
When it happens
Trigger: Calling resolve_tracking_branch_ref_name for a local branch that was never pushed (no refs/remotes entry and no branch.<name>.remote config), or a branch fetched identically from multiple remotes (origin + fork) so remote_matches.len() != 1 with no explicit upstream configured.
Common situations: Freshly created local branches before 'git push -u'; repos cloned with multiple remotes where the same branch name exists on both; stale refs/remotes entries left after a remote was renamed; upstream config lost after branch recreate.
Related errors
- Could not turn {name:?} into a valid reference name
- Not currently on a gitbutler/* branch.
- GitButler mode exit required: please run `but teardown` to p
- Failed to find remote branch's corresponding remote
- HEAD is detached after switching branches
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/567a4ee8b1a1a89e.
Report an issue: GitHub.