{"record":{"id":"2a83052ef33eb309","repo":"gitbutlerapp/gitbutler","slug":"target-ref-must-be-a-remote-tracking-branch","errorCode":null,"errorMessage":"target ref '{}' must be a remote tracking branch","messagePattern":"target ref '(.+?)' must be a remote tracking branch","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/init.rs","lineNumber":144,"sourceCode":"/// reachable from that target; otherwise it is replaced by the merge-base with `HEAD`. The target\n/// is always validated against `HEAD` before metadata is persisted.\n/// `push_remote`, if `Some`, is validated and stored; if `None`, an existing push remote\n/// is kept as is.\n///\n/// Unlike `set_base_branch()`, this neither creates stacks, nor updates the workspace\n/// commit, nor checks anything out. The caller is expected to hold exclusive worktree\n/// access, and to invalidate any cached workspace projection afterwards.\npub fn set_target_ref_and_init_project(\n    repo: &gix::Repository,\n    target_ref: &gix::refs::FullNameRef,\n    push_remote: Option<String>,\n) -> Result<()> {\n    let project_meta = ProjectMeta::resolve(repo)?;\n    let repaired =\n        but_core::ref_metadata::repair_target_metadata_for_migration(&project_meta, repo);\n\n    if target_ref.category() != Some(gix::refs::Category::RemoteBranch) {\n        bail!(\n            \"target ref '{}' must be a remote tracking branch\",\n            target_ref.as_bstr()\n        );\n    }\n\n    let target_head = repo\n        .try_find_reference(target_ref)?\n        .with_context(|| format!(\"remote branch '{}' not found\", target_ref.as_bstr()))?\n        .peel_to_commit()\n        .with_context(|| format!(\"failed to peel branch '{}' to commit\", target_ref.as_bstr()))?\n        .id;\n\n    // Reject targets whose remote isn't configured - reads like the base-branch data\n    // would fail on them later.\n    let (_upstream_ref, remote) = repo\n        .upstream_branch_and_remote_for_tracking_branch(target_ref)?\n        .with_context(|| {\n            format!(","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/init.rs#L126-L162","documentation":"Initializing a GitButler project's target requires a remote-tracking branch reference, because the target defines what the workspace pushes to and fetches from. If the provided target ref's category is not a remote branch (e.g. a local branch, tag, or other ref), initialization is refused.","triggerScenarios":"Calling set_target_ref_and_init_project with a ref whose category() is not gix::refs::Category::RemoteBranch — e.g. refs/heads/main instead of refs/remotes/origin/main.","commonSituations":"Passing a local branch name by mistake; a repo where the remote-tracking branch hasn't been fetched so only a local branch exists; scripts building full ref names incorrectly.","solutions":["Pass the remote-tracking form, e.g. origin/main (refs/remotes/origin/main)","Run git fetch (or the equivalent gix fetch) first so the remote-tracking branch exists","If you only have a local branch, push it to a remote or create a tracking branch, then use that"],"exampleFix":"// before\nbut.set_target_ref_and_init_project(repo, \"refs/heads/main\")?;\n// after\nbut.set_target_ref_and_init_project(repo, \"refs/remotes/origin/main\")?;","handlingStrategy":"validation","validationCode":"let r = repo.find_reference(&target_ref)?;\ndebug_assert_eq!(r.name().category(), Some(gix::refs::Category::RemoteBranch));\n// refuse non remote-tracking refs before calling","typeGuard":"fn is_remote_tracking_ref(name: &gix::refs::PartialNameRef) -> bool {\n    name.category() == Some(gix::refs::Category::RemoteBranch)\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"remote tracking branch\") => {\n        // convert e.g. heads/main → remotes/origin/main and retry\n    }\n    other => other?,\n}","preventionTips":["Always normalize user-supplied branch names to refs/remotes/<remote>/<branch>","Fetch remotes before project initialization so tracking branches exist","Validate ref category in input-parsing code, not deep in the call stack"],"tags":["git","refs","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}