Redesign sidecar sync: baseline bootstrap + incremental patch#339
Redesign sidecar sync: baseline bootstrap + incremental patch#339michael-webster wants to merge 1 commit into
Conversation
Replaces the merge-base-recalculation-on-every-sync approach with a two-phase design that is more robust and GitHub-independent after setup. Bootstrap (sidecar setup / stale baseline): - Clones from GitHub if workspace absent, otherwise git fetch to update - Checks out exact local HEAD if branch is pushed, merge-base otherwise - Applies a patch covering all local changes from the checkout point - Stores BaselineRef + BaselineBranch in sidecar state Incremental sync (sidecar sync, same branch, valid baseline): - Generates git diff <BaselineRef> --binary locally - Resets sidecar to BaselineRef, applies patch — no GitHub access needed Staleness detection re-bootstraps automatically when: - No baseline stored (first sync after create) - Branch changed since last bootstrap - BaselineRef is no longer an ancestor of HEAD (rebase/force-push) Also fixes MergeBase() fallback: was returning git rev-parse origin/HEAD (the remote tip, potentially ahead of local HEAD); now uses git merge-base HEAD origin/HEAD for the actual common ancestor. Remote commands use sh -c "cd <path> && git ..." to support the sidecar's SSH server without requiring git -C (unavailable on old git). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
This seems like a good approach. A few Claude comments: Issue 1: Growing patch size — Confirmed In Sync (sync.go:~210), after successfully applying the patch, active.BaselineRef is never updated. The function loads the stored baseline, runs GeneratePatch(active.BaselineRef), resets, applies — then returns. Nothing writes a new BaselineRef back to state. Every subsequent call diffs from the original bootstrap SHA, so the patch grows linearly with accumulated commits. The fix would be to save active.BaselineRef = headSHA before returning. Issue 2: Double reset in Bootstrap — Confirmed The sequence in Bootstrap is: git reset --hard HEAD && git clean -fd (pre-checkout) Issue 3: Test coverage gaps — Confirmed sync_test.go has exactly one test function. It covers only the stale-baseline-triggers-Bootstrap path (and specifically the sub-case where MergeBase fails). There is no test for: The happy-path incremental sync: valid BaselineRef, same branch → patch applied, no re-bootstrap |
Summary
Replaces the current sync mechanism (merge-base recalculation + GitHub clone on every first sync) with a two-phase design:
sidecar setupor stale baseline): clones from GitHub if workspace absent, otherwisegit fetch; checks out exact local HEAD when pushed or merge-base otherwise; applies a patch of all local changes; storesBaselineRef+BaselineBranchsidecar sync, same branch, valid baseline): generatesgit diff <BaselineRef> --binarylocally, resets sidecar toBaselineRef, applies patch — no GitHub access needed after initial setupStaleness detection re-bootstraps automatically when the branch changes, no baseline is stored, or
BaselineRefis no longer an ancestor ofHEAD(rebase/force-push).Also fixes
MergeBase()fallback: was returninggit rev-parse origin/HEAD(the remote tip, potentially ahead of local HEAD); now correctly usesgit merge-base HEAD origin/HEAD.Remote commands use
sh -c "cd <path> && git ..."instead ofgit -Cwhich is unavailable on the sidecar's older git version.Changes
internal/sidecar/active.go— addBaselineRef,BaselineBranchfieldsinternal/gitutil/gitutil.go— addHeadRef,IsAncestor; fixMergeBasefallbackinternal/sidecar/sync.go— replacesyncWorkspace/SyncwithBootstrap/Sync; removeerrApplyFailedretry logicinternal/cmd/sidecar.go—sidecarSetupSynccallsBootstrap;synccommand passescwdinternal/sidecar/sync_test.go— updated for new behaviouracceptance/sidecar_test.go— sync flag test now runs from a git repoTest plan
go test -race ./...)dev-utils/e2e-sync.sh) passes all 4 scenarios on a live sidecar:🤖 Generated with Claude Code