sync: Add failing test scripts for branch sync feature#1262
Open
ed-irl wants to merge 12 commits into
Open
Conversation
Collaborator
Author
|
This change is part of the following stack: Change managed by git-spice. |
78bdce3 to
cbb4a72
Compare
f07f16f to
fb84995
Compare
cbb4a72 to
e21c83a
Compare
fb84995 to
4f2dc98
Compare
e21c83a to
dd0a8c2
Compare
a8a6674 to
f214732
Compare
This was referenced Jun 24, 2026
Drafts the end-to-end test surface for the upcoming `gs branch sync` and its multi-branch / repo-sync variants. Five script tests covering: - fast-forward when local matches LastPushed - rebase when both sides have new commits - conflict surfacing + gs rebase continue resumption - .gitattributes regenerate merge driver auto-resolution - repo sync orchestrating multiple tracked branches bottom-up All five fail today because the sync commands and per-branch LastPushedHash state do not yet exist. Subsequent commits add the implementation needed to turn them green.
Add LastPushedHash to branchUpstreamState so each tracked branch
remembers the commit hash that was last successfully pushed to its
upstream. This is the missing piece needed by the forthcoming
'gs branch sync' command to distinguish:
- "remote moved, local stayed" (fast-forward safe)
- "local moved past last push, remote stayed" (will push next)
- "both moved past last push" (diverged; needs rebase)
Plumbing:
- branchUpstreamState gains LastPushedHash with json:"omitempty" so
existing state files don't grow until a hash is recorded.
- LookupResponse exposes it as UpstreamLastPushedHash (git.Hash).
- UpsertRequest gains *git.Hash UpstreamLastPushedHash, preserving
the existing-value semantics from other Upsert fields.
- Setting UpstreamBranch to the same name preserves the prior
LastPushedHash; clearing or renaming the upstream resets it.
Tests cover JSON round-trip (with and without the hash), the omit-when-
empty marshaling contract, and the Upsert preserve / advance / clear
flows.
Capture the pushed commit hash at both push sites in the submit handler (initial publish and subsequent updates) and write it into per-branch state alongside the upstream name. This populates the marker the upcoming 'gs branch sync' command uses to distinguish a fast-forwardable remote update from a true divergence. If hash recording fails (peel or store error), the push has already succeeded -- log a warning and continue rather than failing the submit. The branch will pick up a fresh hash on the next push.
Add a branchsync handler and the 'gs branch sync' CLI command that
pulls remote-side commits added to a tracked branch since the last
push. Fast-forward only in this commit:
- if the remote is strictly ahead of LastPushedHash and local hasn't
moved past LastPushedHash, advance the branch to the remote and
record the new LastPushedHash;
- if local is ahead of remote, report "ahead of remote" and exit;
- if both diverge, report and exit; rebase support lands in a
follow-up.
For the currently checked-out branch the handler runs
'git pull --rebase --autostash' so HEAD and the working tree advance
together; a no-commits-to-replay rebase degenerates into a clean
fast-forward. For other branches it does a plain SetRef update.
Wired into main.go so the command resolves through the existing Kong
provider tree. testdata/script/branch_sync_fast_forward.txt now passes.
After the trunk pull and merged-branch handling, iterate every tracked branch and run the branchsync handler against it. Fast-forwarded branches trigger an upstack restack so children that were based on the old hash get rebased onto the new tip; otherwise the next 'gs branch restack' would still see the stale base. Add --no-pull-branches to opt out (matches the safe default mental model: someone with no autofix bots, no force-pushes from collaborators, and no co-developers on a branch can skip the extra fetches). testdata/script/repo_sync_pull_branches.txt now passes.
Extend 'gs branch sync' with --rebase. When local has commits past
LastPushedHash AND the remote has new commits (the typical case after
a local restack races a CI bot push), replay the remote-only commits
on top of local instead of skipping the branch.
Algorithm:
1. Confirm LastPushedHash is in both L and R's history. If either
side rewrote history below it, skip with a clear reason.
2. Check out the branch, reset --hard to R so HEAD = R.
3. 'git rebase --onto L LastPushedHash' replays p..R onto L.
Conflicts surface as a normal interrupted rebase; 'gs rebase --continue'
resumes the operation because git-spice's continuation already inspects
.git/rebase-merge/head-name and our rebase sets it to the branch name.
.gitattributes merge drivers DO apply during step 3 because git uses
three-way merges for every commit replay. A file mapped to
merge=regenerate has its conflict auto-resolved by re-running the
configured generator. git rerere is NOT force-enabled by this command;
honor user-set rerere.enabled if present.
Surfaced via SyncRequest{Branch, Mode} so multi-branch callers can
opt in per-branch; the Sync entry point now takes the request struct.
All five script tests pass: fast_forward, rebase, conflict (with
gs rebase --continue resumption), regenerate_driver (with merge driver
auto-resolution), and the repo_sync_pull_branches orchestrator.
Add three new CLI commands mirroring the existing stack / upstack / downstack restack shape: - gs stack sync -- sync every branch in the current stack - gs upstack sync -- sync the current branch and those above it - gs downstack sync -- sync the current branch and those below it Each accepts --branch to retarget and --rebase to integrate diverged branches via the rebase path. All three share a syncBranches() helper that iterates in dependency order and triggers an upstack restack for any branch whose tip moved -- the same orchestration 'gs repo sync' performs across all tracked branches.
Replace the binary --no-pull-branches flag with an enum-backed
spice.repoSync.pullBranches config key. Values:
off -- skip the per-branch pull entirely
fastForward -- (default) advance safe branches, skip diverged ones
rebase -- replay diverged branches' local commits onto the
remote (matches 'gs branch sync --rebase' semantics)
Default is fastForward, which solves the autofix-ci stale-info problem
without ever surfacing a conflict mid repo sync. Users with stacks
that have mature merge drivers can opt up to rebase via either the
flag or `gs config set spice.repoSync.pullBranches rebase`.
- Document the new pull-branches behavior in the syncing-with-upstream
guide, including the per-scope (branch/upstack/downstack/stack)
variants and how to opt up to rebase mode.
- Four changelog entries:
Added: submit records LastPushedHash on push.
Added: gs branch sync + scope variants.
Changed: gs repo sync now pulls every tracked branch by default.
Fixed: shamhub bare-repo --git-dir on git 2.50+.
- Regenerate doc/includes/cli-reference.md, cli-shorthands.md, the
per-command testdata/help/*.txt fixtures, and gs.txt.
1725dbc to
7363b3a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
sync: Add failing test scripts for branch sync feature
Drafts the end-to-end test surface for the upcoming
gs branch syncandits multi-branch / repo-sync variants. Five script tests covering:
All five fail today because the sync commands and per-branch
LastPushedHash state do not yet exist. Subsequent commits add the
implementation needed to turn them green.
state: Track LastPushedHash per upstream
Add LastPushedHash to branchUpstreamState so each tracked branch
remembers the commit hash that was last successfully pushed to its
upstream. This is the missing piece needed by the forthcoming
'gs branch sync' command to distinguish:
Plumbing:
existing state files don't grow until a hash is recorded.
the existing-value semantics from other Upsert fields.
LastPushedHash; clearing or renaming the upstream resets it.
Tests cover JSON round-trip (with and without the hash), the omit-when-
empty marshaling contract, and the Upsert preserve / advance / clear
flows.
submit: Record LastPushedHash on every push
Capture the pushed commit hash at both push sites in the submit handler
(initial publish and subsequent updates) and write it into per-branch
state alongside the upstream name. This populates the marker the
upcoming 'gs branch sync' command uses to distinguish a fast-forwardable
remote update from a true divergence.
If hash recording fails (peel or store error), the push has already
succeeded -- log a warning and continue rather than failing the submit.
The branch will pick up a fresh hash on the next push.
sync: Add 'gs branch sync' fast-forward primitive
Add a branchsync handler and the 'gs branch sync' CLI command that
pulls remote-side commits added to a tracked branch since the last
push. Fast-forward only in this commit:
moved past LastPushedHash, advance the branch to the remote and
record the new LastPushedHash;
follow-up.
For the currently checked-out branch the handler runs
'git pull --rebase --autostash' so HEAD and the working tree advance
together; a no-commits-to-replay rebase degenerates into a clean
fast-forward. For other branches it does a plain SetRef update.
Wired into main.go so the command resolves through the existing Kong
provider tree. testdata/script/branch_sync_fast_forward.txt now passes.
repo sync: Pull remote-side commits into tracked branches
After the trunk pull and merged-branch handling, iterate every tracked
branch and run the branchsync handler against it. Fast-forwarded
branches trigger an upstack restack so children that were based on the
old hash get rebased onto the new tip; otherwise the next 'gs branch
restack' would still see the stale base.
Add --no-pull-branches to opt out (matches the safe default mental
model: someone with no autofix bots, no force-pushes from collaborators,
and no co-developers on a branch can skip the extra fetches).
testdata/script/repo_sync_pull_branches.txt now passes.
sync: Add --rebase mode for diverged branches
Extend 'gs branch sync' with --rebase. When local has commits past
LastPushedHash AND the remote has new commits (the typical case after
a local restack races a CI bot push), replay the remote-only commits
on top of local instead of skipping the branch.
Algorithm:
side rewrote history below it, skip with a clear reason.
Conflicts surface as a normal interrupted rebase; 'gs rebase --continue'
resumes the operation because git-spice's continuation already inspects
.git/rebase-merge/head-name and our rebase sets it to the branch name.
.gitattributes merge drivers DO apply during step 3 because git uses
three-way merges for every commit replay. A file mapped to
merge=regenerate has its conflict auto-resolved by re-running the
configured generator. git rerere is NOT force-enabled by this command;
honor user-set rerere.enabled if present.
Surfaced via SyncRequest{Branch, Mode} so multi-branch callers can
opt in per-branch; the Sync entry point now takes the request struct.
All five script tests pass: fast_forward, rebase, conflict (with
gs rebase --continue resumption), regenerate_driver (with merge driver
auto-resolution), and the repo_sync_pull_branches orchestrator.
sync: Add stack / upstack / downstack sync variants
Add three new CLI commands mirroring the existing stack / upstack /
downstack restack shape:
Each accepts --branch to retarget and --rebase to integrate diverged
branches via the rebase path. All three share a syncBranches() helper
that iterates in dependency order and triggers an upstack restack for
any branch whose tip moved -- the same orchestration 'gs repo sync'
performs across all tracked branches.
repo sync: Make pull-branches policy configurable
Replace the binary --no-pull-branches flag with an enum-backed
spice.repoSync.pullBranches config key. Values:
off -- skip the per-branch pull entirely
fastForward -- (default) advance safe branches, skip diverged ones
rebase -- replay diverged branches' local commits onto the
remote (matches 'gs branch sync --rebase' semantics)
Default is fastForward, which solves the autofix-ci stale-info problem
without ever surfacing a conflict mid repo sync. Users with stacks
that have mature merge drivers can opt up to rebase via either the
flag or
gs config set spice.repoSync.pullBranches rebase.sync: Add docs, changelog entries, and regenerated CLI reference
guide, including the per-scope (branch/upstack/downstack/stack)
variants and how to opt up to rebase mode.
Added: submit records LastPushedHash on push.
Added: gs branch sync + scope variants.
Changed: gs repo sync now pulls every tracked branch by default.
Fixed: shamhub bare-repo --git-dir on git 2.50+.
per-command testdata/help/*.txt fixtures, and gs.txt.