Skip to content

gh stack sync can fail force-with-lease push after fetching stack branches #118

@ravi-pplx

Description

@ravi-pplx

Summary

gh stack sync can fetch and rebase a stack successfully, then fail during the final force push because the push uses a bare --force-with-lease with branch names instead of explicit destination refs and lease SHAs.

This is most visible in checkouts where local branch upstream/tracking configuration is missing, stale, or does not line up with the branches that gh stack sync just fetched.

Problem

Today the push shape is effectively:

git fetch origin b1
git rebase ...
git push origin --force-with-lease --atomic b1

The failure happens at the final git push. Git has to infer both the destination branch and the expected lease value from local branch configuration / remote-tracking refs. If that inference does not match the refs that gh stack sync just fetched, the push can be rejected even though the command already refreshed the relevant stack branch.

Expected Behavior

After gh stack sync fetches stack branches, the subsequent force push should use the fetched local tracking refs as the lease source.

For example:

git fetch origin b1
git rebase ...
git rev-parse --verify --quiet refs/remotes/origin/b1
git push origin --force-with-lease=refs/heads/b1:<fetched-sha> --atomic b1:refs/heads/b1

If someone updates b1 after the fetch, the push should still fail safely because refs/heads/b1 no longer equals <fetched-sha>.

If b1 did not have a local tracking ref, the lease should use an empty expected value, so creating the remote branch still fails if someone created it first.

Proposed Fix

Build explicit per-branch force-with-lease arguments:

--force-with-lease=refs/heads/<branch>:<tracking-ref-sha>

and push with explicit local-to-remote refspecs:

<branch>:refs/heads/<branch>

This avoids relying on Git's branch/upstream inference while preserving the stale-ref safety guarantee of --force-with-lease.

Related PR

I have a fix in my fork for reference ravi-pplx#1

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions