{"record":{"id":"7a807956c855ba4b","repo":"gitbutlerapp/gitbutler","slug":"branch-name-collides-with-existing-branch","errorCode":null,"errorMessage":"Branch name '{}' collides with existing branch '{}'","messagePattern":"Branch name '(.+?)' collides with existing branch '(.+?)'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/create_reference.rs","lineNumber":504,"sourceCode":"                 belongs to another branch in the workspace. Each commit can only \\\n                 belong to one branch at a time.\",\n                ref_name.shorten(),\n                ref_target_id,\n            )\n        }\n\n        // Actually apply the changes\n        repo.reference(\n            ref_name,\n            ref_target_id,\n            PreviousValue::ExistingMustMatch(gix::refs::Target::Object(ref_target_id)),\n            \"Dependent branch by GitButler\",\n        )\n        .map_err(|err| {\n            if is_not_a_directory_ref_edit_error(&err)\n                && let Ok(Some(colliding_ref)) = find_colliding_ref_ancestor(repo, ref_name)\n            {\n                return anyhow::anyhow!(\n                    \"Branch name '{}' collides with existing branch '{}'\",\n                    ref_name.shorten(),\n                    colliding_ref.shorten()\n                );\n            }\n            let code = match err {\n                gix::reference::edit::Error::FileTransactionCommit(\n                    gix::refs::file::transaction::commit::Error::CreateOrUpdateRefLog(\n                        gix::refs::file::log::create_or_update::Error::MissingCommitter,\n                    ),\n                ) => Some(but_error::Code::AuthorMissing),\n                _ => None,\n            };\n            let err = anyhow::Error::from(err);\n            if let Some(code) = code {\n                err.context(code)\n            } else {\n                err","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/create_reference.rs#L486-L522","documentation":"Git refused to create the ref because its name conflicts with an existing ref in the same namespace: you cannot have both refs/heads/foo and refs/heads/foo/bar in a files-based ref store (a ref is both a file path and a directory). gix reports this as a not-a-directory edit error; the code remaps it to a friendly message naming the colliding existing ref.","triggerScenarios":"Creating dependent branch 'feature/x' when branch 'feature' already exists (or vice versa) via create_reference; any generated branch name whose path prefix equals an existing ref, or that extends an existing ref with a slash component.","commonSituations":"Automatic branch-name generation (e.g. stacking schemes that insert slashes) colliding with user-created branches; importing branches from another remote with hierarchical naming.","solutions":["Rename the new branch so neither name is a path-prefix of the other (e.g. feature-x instead of feature/x)","Delete or rename the colliding existing branch named in the error if it is obsolete","When generating names programmatically, check the existing ref set for both prefix and extension collisions first"],"exampleFix":"# before\ngit branch feature        # exists\n# creating feature/x → \"Branch name 'feature/x' collides with existing branch 'feature'\"\n# after — use a non-hierarchical name\ngit branch feature-x      # no collision","handlingStrategy":"validation","validationCode":"// before creating refs, check both collision directions against existing refs\nfn collides(repo: &gix::Repository, name: &gix::refs::FullName) -> Option<gix::refs::FullName> {\n    let names: Vec<_> = repo.references().ok()?.map_while(Result::ok)\n        .map(|r| r.name().to_owned()).collect();\n    names.into_iter().find(|existing|\n        existing.as_bstr() == name.as_bstr()\n        || existing.as_bstr().starts_with(format!(\"{}/\", name.as_bstr()).as_bytes())\n        || name.as_bstr().starts_with(format!(\"{}/\", existing.as_bstr()).as_bytes()))\n}","typeGuard":"fn is_safe_branch_name(existing: &[String], candidate: &str) -> bool {\n    existing.iter().all(|e| e != candidate && !e.starts_with(&format!(\"{candidate}/\")) && !candidate.starts_with(&format!(\"{e}/\")))\n}","tryCatchPattern":"match create_reference(...) {\n    Err(e) if e.to_string().contains(\"collides with existing branch\") => { /* extract colliding name, suggest alternative like replacing '/' with '-' */ }\n    r => r,\n}","preventionTips":["Avoid hierarchical names (a/b) for generated branches, or normalize separators","Validate candidate names against the full ref list (both prefix directions) before creating","Clean up obsolete stacked branches whose names block a namespace"],"tags":["rust","git","refs","branch-naming","but-workspace"],"backgroundTag":"ref-name-collision","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}