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

  1. Push with an upstream: git push -u origin <branch>, which sets branch.<name>.remote/merge and creates the tracking ref
  2. Or set the config directly: git branch --set-upstream-to=origin/<branch>
  3. If two remotes match, prune the stale one (git remote prune) or remove the unused remote so exactly one match remains
  4. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/567a4ee8b1a1a89e. Report an issue: GitHub.