Skip to content

test: git-remote coverage (3/4) — divergence & recovery matrix#1638

Open
Soph wants to merge 5 commits into
soph/git-remote-tests-2-p0from
soph/git-remote-tests-3-divergence
Open

test: git-remote coverage (3/4) — divergence & recovery matrix#1638
Soph wants to merge 5 commits into
soph/git-remote-tests-2-p0from
soph/git-remote-tests-3-divergence

Conversation

@Soph

@Soph Soph commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

https://entire.io/gh/entireio/cli/trails/759

Part 3 of the stack on #1637. Systematizes the historically most re-broken area (#953, #1251, #1252, #1260: destructive local-v1-ref moves) into an explicit matrix, and covers the git-refs equivalents that only had unit coverage.

Tests

  • D1 (git-branch): table-driven matrix — local v1 {ahead, behind, diverged, disconnected} × {pre-push (real hook), explain fetch-on-miss, doctor}: local-only commits always survive, behind→FF, diverged→both sides preserved with linear tip (double-replay guard), repeated pre-push leaves the remote byte-identical. Cells already covered by safely_advance_local_ref_test.go / explain_test.go are not duplicated; the resume trigger shares the SafelyAdvanceLocalRef engine exercised via explain.
  • D4 (replay fidelity): no-op commit replay must not clobber the remote tip's tree (743c43f had no test); root-commit replay; >1000-commit replay pinned — the historical cap-removal fix (4cf01ed) was never merged (it's stranded on origin/no-limit), so TestCollectCommitsSince_CommitCapPinsCurrentBehavior pins the hard failure with a flip-when-fixed note.
  • D5 (TOCTOU): reconcile uses the fetched remote hash, not a stale pre-fetch observation (1e8628a had no test) — asserted as a behavioral invariant against a bare remote that advanced mid-flight.
  • D2/D3 (git-refs): remote-ahead per-checkpoint ref → real-hook push does fetch+replay+non-force retry with both sides surviving; rejected refs stay queued with the remote untouched (via a bare-side pre-receive rejection — a content-level cherry-pick conflict isn't deterministically reproducible at this layer); concurrent pushers from two clones.
  • D6 (multi-worktree): two worktrees enqueue into the shared common-dir push queue; a push from worktree A also lands worktree B's refs, queue compacted. New AddWorktree test helper.
  • D7 (two-remote pin): push to remote A drains the queue so remote B never receives the refs — pins current behavior, tracked as git-refs: push queue is drained by the first successful push — checkpoints never reach a second remote #1635.

Verification

  • mise run fmt && mise run lint clean per commit
  • mise run test:integration green (444 tests at tip)
  • go test ./cmd/entire/cli/strategy/ green
  • mise run test:e2e:canary green under both E2E_CHECKPOINT_STORE values

🤖 Generated with Claude Code

https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq


Note

Low Risk
Test-only changes and integration helpers; no runtime checkpoint sync or push logic is modified.

Overview
Adds integration and strategy tests that map local v1 / per-checkpoint ref divergence to real CLI paths (pre-push, checkpoint explain, entire doctor) and git-refs push-queue behavior, without changing production code.

D1 (git-branch): Table-driven cases for local v1 ahead, behind, diverged, disconnected through the pre-push hook (checkpoint survival, diverged replay keeps both sides with a linear tip, idempotent double pre-push). Separate tests cover fetch-on-miss via checkpoint explain (behind/diverged) and doctor --force on disconnected orphans.

D2/D3 (git-refs): End-to-end pre-push when a per-checkpoint ref is amended on the remote vs locally (fetch+replay, both file sets kept), server rejection (ref stays queued, remote unchanged, retry succeeds), and two clones racing on the same ref.

D6/D7 (queue): Shared common-dir queue across linked worktrees (one pre-push drains both, stale entries pruned). D7 intentionally asserts today’s two-remote gap: first push drains the queue so a second remote never gets the refs (#1635).

Test harness: commitMsgPath() for linked worktrees; new AddWorktree helper.

Strategy (replay_fidelity_test.go): Guards for no-op commits not clobbering remote trees on replay, root-commit chains, reconcile using the post-fetch remote tip; pins the >1000-commit collectCommitsSince cap until the unmerged cap-removal lands.

Reviewed by Cursor Bugbot for commit d93c1e3. Configure here.

Soph and others added 3 commits July 4, 2026 21:53
…D4, D5)

Systematize the local-v1 divergence & recovery matrix (the most re-broken
area, #953/#1251/#1252/#1260) plus the specifically-untested replay edges.

D1 (integration, git-branch): drive the local-v1 state {ahead, behind,
diverged, disconnected} through the real triggers end-to-end:
  - pre-push (real hook): local-only survives; ahead fast-forwards; diverged
    replays both sides on a linear tip; disconnected cherry-picks; a repeated
    push is idempotent (double-replay guard, #1260).
  - explain fetch-on-miss for {behind, diverged}, extending the existing
    {missing, ahead} coverage: a remote-only checkpoint is fetched/reconciled
    and local-only checkpoints survive.
  - doctor --force reconciles a disconnected v1, preserving both sides.

D4 (strategy unit): replay fidelity edges. A no-op commit must not clobber the
remote tip's accumulated tree (regression 743c43f); a root-commit chain
replays cleanly. Plus a KNOWN BUG pin: >1000-commit replay is still capped
(collectCommitsSince) because the cap-removal (4cf01ed) lives on the unmerged
branch origin/no-limit; flip the assertion when it lands.

D5 (strategy): TOCTOU guard for 1e8628a -- fetchAndRebaseRefCommon replays
onto the freshly fetched remote tip, not a stale earlier hash. The literal
ls-remote seam the fix patched no longer exists (fix is an ancestor), so this
pins the behavioural invariant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq
Entire-Checkpoint: b19f4c2ff3bd
…ery (D2, D3)

Drive the git-refs recovery end-to-end through the real pre-push path (drain
queue -> batch push -> per-ref fetch+replay), complementing the unit-level
coverage in strategy/refs_push_test.go.

D2:
  - remote ref amended by another clone (valid child commit) + a divergent
    local write of the same ref: the fast-forward-only batch push is rejected,
    recovery fetches the remote tip and replays the local change on top
    (non-force), so both sides' files survive and the remote commit stays an
    ancestor.
  - queue safety: when the remote refuses the ref update (bare pre-receive
    declining the checkpoint namespace), the ref stays queued and the remote
    is untouched (never force-overwritten); a later push retries and lands it.
    A pure content-level cherry-pick conflict is not reproducible at this layer
    (tree-delta apply is last-writer), so a server-side rejection stands in.

D3: two clones race on the SAME per-checkpoint ref from a shared base -- the
second pusher fetch+replay+retries (non-force), preserving both. The git-refs
analogue of TestConcurrentPush_SecondPusherRebasesAndRetries (v1-only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq
Entire-Checkpoint: faa45ac4455f
…ee, D7 two-remote pin)

D6: the git-refs push queue lives in the shared git common dir, so sessions in
two worktrees enqueue into ONE queue. A single pre-push from the main worktree
pushes both worktrees' checkpoint refs, drains + compacts the queue, and prunes
a stale entry (a queued ref with no local ref -> partitionLocalRefs). Adds an
AddWorktree test helper (its own .entire/, shared common dir, offset session
counter so session IDs don't collide in the shared session store).

D7: PIN the known two-remote queue-clearing gap (test plan decision D-2). The
queue records only the ref, not the remote, so a push to remote A drains the
queue and remote B never receives those refs. Asserts current behavior with a
clear "flip when per-remote tracking lands" note. No production change.

Also resolves the COMMIT_EDITMSG scratch path via `git rev-parse
--absolute-git-dir` so the shadow-commit test helpers work in a linked worktree
(where .git is a file); identical to the prior path for normal checkouts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq
Entire-Checkpoint: 057f6136f6f5

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a broader, end-to-end integration-test matrix around historically fragile checkpoint ref divergence/recovery behavior (v1 git-branch and git-refs per-checkpoint refs), plus strategy-level replay-fidelity regression guards. This strengthens confidence that ref reconciliation is correct and remains idempotent across pre-push, explain fetch-on-miss, and doctor flows—without changing production behavior.

Changes:

  • Add strategy tests that lock replay fidelity invariants (no-op commits, root chains, fetched-remote-hash TOCTOU) and pin the current >1000-commit traversal cap behavior.
  • Add integration tests covering (a) v1 divergence state × trigger matrix, (b) git-refs per-checkpoint ref divergence/rejection/concurrency recovery, and (c) push-queue semantics (multi-worktree shared queue + pinned two-remote gap).
  • Extend the integration test harness with linked-worktree support (AddWorktree) and correct commit message scratch resolution in linked worktrees.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
cmd/entire/cli/strategy/replay_fidelity_test.go New strategy-level regression tests for replay correctness and TOCTOU invariants; pins current commit traversal cap behavior.
cmd/entire/cli/integration_test/testenv.go Adds linked-worktree helpers and fixes COMMIT_EDITMSG resolution to work under git worktree.
cmd/entire/cli/integration_test/refs_queue_semantics_test.go New integration coverage for git-refs push-queue behavior across multi-worktree and multi-remote scenarios (including pinned known bug).
cmd/entire/cli/integration_test/refs_divergence_test.go New integration coverage for git-refs per-checkpoint ref divergence recovery, server-side rejection retry semantics, and concurrent pushers.
cmd/entire/cli/integration_test/divergence_matrix_test.go New integration matrix for v1 local/remote divergence states across pre-push, explain fetch-on-miss, and doctor reconciliation triggers.

Comment on lines +67 to +70
c := exec.CommandContext(env.T.Context(), "git", args...)
c.Dir = env.RepoDir
c.Env = append(testutil.GitIsolatedEnv(), "GIT_INDEX_FILE="+env.RepoDir+"/.git/entire-orphan-index")
if stdin != "" {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 56408fa — GIT_INDEX_FILE path now built with filepath.Join.

Soph and others added 2 commits July 5, 2026 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants