git_operations: stop shallow-fetching the metadata tip · Entire
git_operations: stop shallow-fetching the metadata tip
ebe73db→main·
Soph·1mo ago·2 files·+117 added/-7 removed
FetchMetadataTreeOnly resolves the latest checkpoint on resume / explain / attach. It fetched with --depth=1, which adds the fetched tip to .git/shallow. Once the entire/checkpoints/v1 tip is a shallow boundary, a later git merge-base against refs/remotes/origin/entire/checkpoints/v1 can no longer reach the real common ancestor (it lives below the boundary), so the disconnection check falsely reports "no common ancestor" — aborting git push and looping entire doctor. This is the upstream source of the shallow-metadata false-disconnect: it is self-inflicted by the CLI's own tip-read on essentially every resume when checkpoints live on origin (no checkpoint_remote short-circuit).
The shallow boundary is intrinsic to --depth=1 — the fetched commit is truncated regardless of which ref it lands on, and git opportunistically points refs/remotes/origin/
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
b85c03197b07View transcript
[?
so in ../entiredb "filtered_fetches" is enabled, now today one of my coworkers had this issue:Claude Code·1 step](/content/gh/entireio/cli/session/e1c2cd64-bc7d-4315-9abd-f106c4eaa991#timeline-b85c03197b07/index.html)
Changes
2
cmd/entire/cli
Mgit_operations.go+15/-7
Atreeless_fetch_full_depth_test.go+102
412 unmodified lines
413
414
415
416
417
418
419
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
421
431
432
433
434
435
426
436
437
438
19 unmodified lines
458
459
460
452
461
462
463
412 unmodified lines
return fetchMetadataFromOrigin(ctx, fetchMetadataOpts{NoFilter: true})
// FetchMetadataTreeOnly fetches just the tip of the entire/checkpoints/v1
// branch (--depth=1). Used by resume/explain to resolve the latest checkpoint
// cheaply without pulling the entire history. May leave .git/shallow set;
// FetchMetadataBranch will undo that when full ancestry is later needed.
// FetchMetadataTreeOnly fetches the entire/checkpoints/v1 commit+tree graph
// from origin to resolve the latest checkpoint, relying on --filter=blob:none
// (when filtered fetches are enabled) to skip blob content rather than on a
// shallow --depth=1 fetch.
//
// It deliberately does NOT use --depth=1. A depth-1 fetch adds the fetched tip
// to .git/shallow, and any ref pointing at a shallow commit (the durable
// refs/remotes/origin/<branch> that git updates opportunistically, or the local
// primary) can no longer be walked past that boundary. A later `git merge-base`
// against it then falsely reports "no common ancestor", which makes push and
// `entire doctor` treat an ordinary diverged-but-behind branch as disconnected
// (see strategy.IsMetadataDisconnected). Fetching at full depth keeps the
// remote-tracking ref connected; git fetches incrementally, so after the first
// fetch only new commits/trees travel.
func FetchMetadataTreeOnly(ctx context.Context) error {
return fetchMetadataFromOrigin(ctx, fetchMetadataOpts{Shallow: true})
return fetchMetadataFromOrigin(ctx, fetchMetadataOpts{})
}
type fetchMetadataOpts struct {
NoFilter bool
Shallow bool
Unshallow bool
}
19 unmodified lines
RefSpecs: []string{refSpec},
NoTags: true,
NoFilter: fopts.NoFilter,
Shallow: fopts.Shallow,
Unshallow: fopts.Unshallow,
})
if fetchErr != nil {
Mcmd/entire/cli/git_operations.go+15/-7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package cli
import (
"context"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/entireio/cli/cmd/entire/cli/paths"
"github.com/entireio/cli/cmd/entire/cli/testutil"
)
// TestFetchMetadataTreeOnly_DoesNotShallowRepo is a regression test for the
// shallow-metadata false-disconnect.
//
// FetchMetadataTreeOnly resolves the latest checkpoint on resume/explain/attach.
// It used to fetch with --depth=1, which adds the fetched tip to .git/shallow.
// Once the metadata tip is a shallow boundary, a later `git merge-base` against
// refs/remotes/origin/entire/checkpoints/v1 can't reach the real common
// ancestor (it's below the boundary) and the disconnection check falsely
// reports "no common ancestor" — aborting push and looping doctor.
//
// The fix drops --depth=1 and relies on blob filtering for cheapness, so the
// fetch never creates a shallow boundary.
func TestFetchMetadataTreeOnly_DoesNotShallowRepo(t *testing.T) {
// Uses t.Chdir() — cannot run in parallel.
tmpDir := t.TempDir()
bareDir := filepath.Join(tmpDir, "bare.git")
localDir := filepath.Join(tmpDir, "local")
runGit(t, tmpDir, "init", "--bare", bareDir)
// Seed: a main commit plus an orphan metadata branch with two checkpoint
// commits, so a --depth=1 fetch would visibly truncate to one.
testutil.InitRepo(t, localDir)
testutil.WriteFile(t, localDir, "README.md", "hello")
testutil.GitAdd(t, localDir, "README.md")
testutil.GitCommit(t, localDir, "init")
runGit(t, localDir, "branch", "-M", "main")
runGit(t, localDir, "remote", "add", "origin", bareDir)
runGit(t, localDir, "checkout", "--orphan", paths.MetadataBranchName)
runGit(t, localDir, "rm", "-rf", ".")
testutil.WriteFile(t, localDir, "a/metadata.json", `{\"checkpoint_id\":\"deadbeef0001\"}`)
testutil.GitAdd(t, localDir, "a/metadata.json")
testutil.GitCommit(t, localDir, "Checkpoint: deadbeef0001")
testutil.WriteFile(t, localDir, "b/metadata.json", `{\"checkpoint_id\":\"deadbeef0002\"}`)
testutil.GitAdd(t, localDir, "b/metadata.json")
testutil.GitCommit(t, localDir, "Checkpoint: deadbeef0002")
runGit(t, localDir, "checkout", "main")
runGit(t, localDir, "push", "origin", "HEAD:refs/heads/main", paths.MetadataBranchName)
runGit(t, bareDir, "symbolic-ref", "HEAD", "refs/heads/main")
originTip := gitRevParse(t, bareDir, "refs/heads/"+paths.MetadataBranchName)
clonedDir := filepath.Join(tmpDir, "cloned")
runGit(t, tmpDir, "clone", bareDir, clonedDir)
runGit(t, clonedDir, "config", "user.email", "test@example.com")
runGit(t, clonedDir, "config", "user.name", "Test")
t.Chdir(clonedDir)
if err := FetchMetadataTreeOnly(t.Context()); err != nil {
t.Fatalf("FetchMetadataTreeOnly: %v", err)
}
// The fix: the tip-read must not leave the repo shallow. Under the old
// --depth=1 behavior this would be "true".
if shallow := gitOut(t, clonedDir, "rev-parse", "--is-shallow-repository"); shallow != "false" {
t.Errorf("repo is shallow after tree-only fetch (--is-shallow-repository=%q); the tip-read must not create a shallow boundary", shallow)
}
// The full metadata history is present (two commits), not truncated to one.
originRef := "refs/remotes/origin/" + paths.MetadataBranchName
if n := gitOut(t, clonedDir, "rev-list", "--count", originRef); n != "2" {
t.Errorf("origin metadata history has %s commit(s), want 2 (full depth)", n)
}
// The local primary ref is advanced to the tip so reads work.
localRef := "refs/heads/" + paths.MetadataBranchName
if got := gitRevParse(t, clonedDir, localRef); got != originTip {
t.Errorf("local primary ref %s = %q, want origin tip %q", localRef, got, originTip)
}
}
func gitRevParse(t *testing.T, dir, rev string) string {
t.Helper()
return gitOut(t, dir, "rev-parse", rev)
}
func gitOut(t *testing.T, dir string, args ...string) string {
t.Helper()
cmd := exec.CommandContext(context.Background(), "git", args...)
cmd.Dir = dir
cmd.Env = testutil.GitIsolatedEnv()
out, err := cmd.Output()
if err != nil {
t.Fatalf("git %s in %s: %v", strings.Join(args, " "), dir, err)
}
return strings.TrimSpace(string(out))
}