{"record":{"id":"34699c709fa652e1","repo":"gitbutlerapp/gitbutler","slug":"local-target-branch-name-head-is-out-of-sy","errorCode":null,"errorMessage":"Local `{target_branch_name}` ({head}) is out of sync with `{push_remote_name}/{target_branch_name}` ({tracking}). Run `but pull` to resync first.","messagePattern":"Local `(.+?)` \\((.+?)\\) is out of sync with `(.+?)/(.+?)` \\((.+?)\\)\\. Run `but pull` to resync first\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/land/deliver.rs","lineNumber":33,"sourceCode":"pub(super) fn update_local_target_refs(\n    repo: &gix::Repository,\n    new_target_oid: gix::ObjectId,\n    expected_target_oid: gix::ObjectId,\n    push_remote_name: &str,\n    target_branch_name: &str,\n) -> anyhow::Result<()> {\n    let head_ref = format!(\"refs/heads/{target_branch_name}\");\n    let tracking_ref = format!(\"refs/remotes/{push_remote_name}/{target_branch_name}\");\n\n    // The two refs must already point at the same commit. If local `<target>` has diverged from\n    // `<remote>/<target>`, the shared compare-and-swap below can never succeed (the head edit keeps\n    // failing), so surface that as a clear, non-retryable error rather than looping on it.\n    if let (Some(head), Some(tracking)) = (\n        super::peel_ref(repo, &head_ref)?,\n        super::peel_ref(repo, &tracking_ref)?,\n    ) && head != tracking\n    {\n        bail!(\n            \"Local `{target_branch_name}` ({head}) is out of sync with \\\n             `{push_remote_name}/{target_branch_name}` ({tracking}). Run `but pull` to resync first.\"\n        );\n    }\n\n    // Advance both refs to the landed commit in one transaction, failing if either moved meanwhile.\n    let expected = PreviousValue::ExistingMustMatch(expected_target_oid.into());\n    let ref_log_message = \"GitButler land\";\n    let edits = [\n        RefEdit::update(\n            head_ref.try_into()?,\n            new_target_oid,\n            expected.clone(),\n            ref_log_message,\n        ),\n        RefEdit::update(\n            tracking_ref.try_into()?,\n            new_target_oid,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/land/deliver.rs#L15-L51","documentation":"Before landing, the library advances both the local target branch and its remote tracking ref in one transaction. If the local branch and `{push_remote}/{branch}` have diverged (different peeled IDs), landing would clobber or lose commits, so it refuses with a non-retryable error telling the user to resync with `but pull`.","triggerScenarios":"Running `but merge`/land while the local target branch HEAD differs from the peeled remote tracking ref — i.e. local commits exist that were never pushed, or the tracking ref is stale/ahead.","commonSituations":"Developer committed directly on the target branch locally without pushing; another machine pushed and this clone never fetched+pulled; a force-push happened on the remote.","solutions":["Run `but pull` (or `git pull --rebase` on the target branch) to resync local and remote, then retry the merge","Push any local-only commits on the target branch with `git push` before landing","If the remote history was rewritten, realign with `git fetch` and reset/merge the local branch appropriately"],"exampleFix":"# before\nbut merge feature-branch\n# error: local main out of sync with origin/main\n\n# after\nbut pull\nbut merge feature-branch","handlingStrategy":"try-catch","validationCode":"let head = repo.revparse_single(format!(\"refs/heads/{target}\").as_str())?.peel_to_id()?;\nlet tracking = repo.revparse_single(format!(\"refs/remotes/{remote}/{target}\").as_str())?.peel_to_id()?;\nif head != tracking { println!(\"run `but pull` first\"); return Ok(()); }","typeGuard":null,"tryCatchPattern":"match land_result {\n    Err(e) if e.to_string().contains(\"out of sync with\") => {\n        run(\"but pull\");\n        retry_land();\n    }\n    other => other?,\n}","preventionTips":["Always `but pull` / fetch before landing, especially in multi-machine setups","Avoid committing directly on the target branch; keep target branch push-only via settings","Check `git status -sb` ahead/behind markers before running `but merge`"],"tags":["git","sync","landing","diverged-refs"],"backgroundTag":"invalid-state-transition","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"}