gitbutlerapp/gitbutler · error
No remote refspec maps
Error message
No remote refspec maps {} to a local branch What it means
setup_local_tracking_configuration maps a remote-tracking branch (e.g. refs/remotes/origin/main) to a local branch via gix's upstream_branch_and_remote_for_tracking_branch. When no remote refspec in the configuration maps that tracking ref back to a local branch, the mapping is impossible and setup fails.
Solutions
- Restore a wildcard fetch refspec in the remote config: `git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`
- Run `git remote set-url`/`git fetch origin` to re-establish standard tracking configuration
- Choose a remote-tracking branch that is covered by the existing refspec
Example fix
// before (config) [remote "origin"] fetch = +refs/heads/main:refs/remotes/origin/main // after (config) [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/*
Defensive patterns
Strategy: validation
Validate before calling
fn has_matching_refspec(repo: &gix::Repository, tracking: &gix::refs::FullNameRef) -> anyhow::Result<bool> {
Ok(repo.upstream_branch_and_remote_for_tracking_branch(tracking)?.is_some())
} Try / catch
match result {
Err(e) if e.to_string().contains("No remote refspec maps") => {
eprintln!("fix remote fetch refspec in .git/config, then retry");
std::process::exit(1);
}
other => other?,
} Prevention
- Keep the wildcard fetch refspec +refs/heads/*:refs/remotes/<remote>/* on every remote
- Re-check config after custom clones or refspec narrowing
- Run git fetch regularly so tracking refs and refspecs stay consistent
When it happens
Trigger: Calling apply or local_tracking_branch with a remote_tracking_ref whose remote has no fetch refspec matching it (e.g. fetch refspec deleted or narrowed), so upstream_branch_and_remote_for_tracking_branch returns None.
Common situations: Cloning with a custom/narrowed fetch refspec like `+refs/heads/main:refs/remotes/origin/main` then trying to apply a different remote branch; hand-edited .git/config missing `+refs/heads/*:refs/remotes/origin/*`.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Couldn't find remote refspecs that would match
- Could not read repository-local Git configuration
- Couldn't find remote refspecs that would match
- editing config through a repository is not supported
- Neither found push remote found in workspace nor…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/6c09e0c7daa0daec.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/branch/mod.rs:563
Ok(branch_ref)
}
/// Setup `local_tracking_ref` to track `remote_tracking_ref`, and prepare a locked configuration
/// transaction with the branch configuration added.
/// We also return the commit at which `local_tracking_ref` should be placed, which is assumed to not exist.
pub(crate) fn setup_local_tracking_configuration(
repo: &gix::Repository,
local_tracking_ref: &gix::refs::FullNameRef,
remote_tracking_ref: &gix::refs::FullNameRef,
) -> anyhow::Result<(gix::config::FileTransaction, gix::ObjectId)> {
let remote_tracking_commit_id = repo
.find_reference(remote_tracking_ref)?
.peel_to_commit()?
.id();
let Some((upstream_branch, remote)) =
repo.upstream_branch_and_remote_for_tracking_branch(remote_tracking_ref)?
else {
anyhow::bail!(
"No remote refspec maps {} to a local branch",
remote_tracking_ref.as_bstr()
);
};
let remote_name = remote
.name()
.expect("a remote loaded by name is never anonymous");
let mut config = repo.config_file_mut(repo.common_dir().join("config"))?;
let mut section =
config.section_mut_or_create_new("branch", Some(local_tracking_ref.shorten()))?;
// Each key only where the user has not set it: neither one nor other keys are a reason to skip.
let remote_key = gix::config::tree::Branch::REMOTE.name;
if section.value(remote_key).is_none() {
section.push(remote_key, Some(remote_name.as_bstr()))?;
}
let merge_key = gix::config::tree::Branch::MERGE.name;
if section.value(merge_key).is_none() {View on GitHub (pinned to 58e5313667)