{"record":{"id":"567a4ee8b1a1a89e","repo":"gitbutlerapp/gitbutler","slug":"branch-ref-name-has-no-tracking-branch","errorCode":null,"errorMessage":"Branch '{ref_name}' has no tracking branch","messagePattern":"Branch '(.+?)' has no tracking branch","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/but-core/src/branch/mod.rs","lineNumber":63,"sourceCode":"                .map(|reference| {\n                    reference.map(|_| {\n                        full_name\n                            .try_into()\n                            .expect(\"constructed remote-tracking refname must be valid\")\n                    })\n                })\n        })\n        .collect::<Result<Vec<gix::refs::FullName>, _>>()?;\n\n    if remote_matches.len() == 1 {\n        return Ok(Cow::Owned(\n            remote_matches\n                .pop()\n                .expect(\"exactly one remote match exists\"),\n        ));\n    }\n\n    bail!(\"Branch '{ref_name}' has no tracking branch\")\n}\n\n/// A way to safely delete branches, which is only the case it's checked out nowhere.\npub mod safe_delete;\n\n/// State for reuse when [safely deleting references](SafeDelete::delete_reference).\n#[derive(Debug)]\npub struct SafeDelete {\n    /// A mapping of one or more worktree paths that are affected by changes to the keyed reference name.\n    worktrees_by_ref: WorktreePathByRef,\n}\n","sourceCodeStart":45,"sourceCodeEnd":75,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-core/src/branch/mod.rs#L45-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: local branch never pushed\nbut stack sync   # -> Branch 'refs/heads/feature' has no tracking branch\n\n# after: establish the upstream\n git push -u origin feature\nbut stack sync","handlingStrategy":"validation","validationCode":"// replicate the resolution order cheaply before acting\nlet configured = repo.branch_remote_tracking_ref_name(ref_name, gix::remote::Direction::Fetch)\n    .transpose()?.filter(|n| repo.try_find_reference(n.as_ref())?.is_some());\nif configured.is_none() {\n    let matches: Vec<_> = repo.remote_names().iter().filter_map(|r| {\n        let full = format!(\"refs/remotes/{r}/{}\", ref_name.shorten());\n        repo.try_find_reference(&full).ok().flatten().map(|_| full)\n    }).collect();\n    anyhow::ensure!(matches.len() == 1, \"push first: git push -u origin {}\", ref_name.shorten());\n}","typeGuard":null,"tryCatchPattern":"match but_core::branch::resolve_tracking_branch_ref_name(&ref_name, &repo) {\n    Err(e) if e.to_string().contains(\"no tracking branch\") =>\n        establish_upstream_and_retry(&ref_name).await?, // git push -u then retry\n    r => r?,\n}","preventionTips":["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"],"tags":["git","branch","upstream","remote-tracking"],"backgroundTag":"no-upstream-configured","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}