# git_operations: heal a prior --depth=1 shallow metadata branch

`301e0da`→[main](/content/gh/entireio/cli/commits/main/index.html)·
Soph·1mo ago·2 files·+74 added/-29 removed

Dropping --depth=1 stops creating new shallow boundaries, but a repo that an older CLI already shallowed keeps the metadata tip grafted in .git/shallow forever — so its merge-base disconnection checks stay broken until something unshallows it.

Set Unshallow:true on the metadata fetch. remote.Fetch only adds --unshallow when the repo is actually shallow, so this is a no-op on a normally-cloned repo and only does work where a prior shallow boundary needs removing. --unshallow fetches the missing ancestry, so the repo becomes genuinely complete (not a "claims complete but missing objects" state) and composes with --filter=blob:none.

Add TestFetchMetadataTreeOnly_HealsPriorShallow (and extract the shared seed+clone setup into a helper). Update the now-stale FetchMetadataBranch comment that said it never unshallows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

## Sessions

1b1d826bd5d5View transcript

## Changes

2

- cmd/entire/cli

- Mgit_operations.go+10/-2

- Mtreeless_fetch_full_depth_test.go+64/-27

```
406 unmodified lines

407
408
409
410
411
410
411
412
12 unmodified lines

425
426
427
428
429
430
431
432
433
20 unmodified lines

454
455
456
457
458
459
460
461
462
463
464
465
466

406 unmodified lines

// FetchMetadataBranch fetches the entire/checkpoints/v1 branch from origin
// with full blob content. Used as a fallback by resume/explain when the
// tree-only probe is insufficient (e.g. the metadata.json blob is missing).
// Does NOT --unshallow: --unshallow is a global property of the clone, so on
// shallow checkpoint repos it would also deepen unrelated branches.
func FetchMetadataBranch(ctx context.Context) error {
    return fetchMetadataFromOrigin(ctx, true /* noFilter */)
}
12 unmodified lines

// (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.
//
// It also unshallows a repo that an older CLI already shallowed, so the boundary
// left by a prior --depth=1 fetch is removed rather than lingering forever.
func FetchMetadataTreeOnly(ctx context.Context) error {
    return fetchMetadataFromOrigin(ctx, false /* noFilter */)
}
20 unmodified lines

RefSpecs: []string{refSpec},
        NoTags:   true,
        NoFilter: noFilter,
        // Heal a repo that an older CLI already shallowed with --depth=1: the
        // metadata tip is grafted in .git/shallow, which breaks merge-base
        // connectivity checks for the metadata branch. remote.Fetch only adds
        // --unshallow when the repo is actually shallow, so this is a no-op on a
        // normally-cloned repo and only does work where a prior shallow boundary
        // needs removing.
        Unshallow: true,
    })
    if fetchErr != nil {
        if ctx.Err() == context.DeadlineExceeded {
```

Mcmd/entire/cli/git_operations.go+10/-2

```
7 unmodified lines

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
11
12
13
14
15
16
17
18
19
20
21
22
23
32
33
24
25
26
12 unmodified lines

39
40
41
52
42
43
54
55
56
57
58
59
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
1 unmodified line

75
76
77
71
72
73
78
79
80
81
82
83
8 unmodified lines

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

7 unmodified lines

"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.

// seedTreelessFetchRepo builds a bare origin with a `main` commit and a 2-commit
    // orphan entire/checkpoints/v1 branch, then makes a single-branch file:// clone
    // of main (a real fetch-pack, not the local hardlink optimization, so the
    // metadata branch and its history are absent until fetched). Returns the clone
    // dir and the origin metadata tip. The caller is responsible for t.Chdir.
    clonedDir, originTip := seedTreelessFetchRepo(t)
    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 := gitOutput(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)
    // --depth=1 behavior this would be shallow.
    if repoIsShallow(t, clonedDir) {
        t.Errorf("repo is shallow after tree-only fetch; the tip-read must not create a shallow boundary")
    }

// The full metadata history is present (two commits), not truncated to one.
    t.Errorf("local primary ref %s = %q, want origin tip %q", localRef, got, originTip)
}

// TestFetchMetadataTreeOnly_HealsPriorShallow verifies that a repo already
// shallowed by an older CLI (a lingering --depth=1 boundary on the metadata
// branch) is unshallowed by the tip-read, so the poison doesn't persist
// indefinitely for users who ran the buggy version.
func TestFetchMetadataTreeOnly_HealsPriorShallow(t *testing.T) {
    // Uses t.Chdir() — cannot run in parallel.
    clonedDir, _ := seedTreelessFetchRepo(t)

// Reproduce the old behavior: a --depth=1 fetch grafts the metadata tip into
    // .git/shallow, marking the repo shallow.
    runGit(t, clonedDir, "fetch", "--depth=1", "origin",
        "+refs/heads/"+paths.MetadataBranchName+":refs/remotes/origin/"+paths.MetadataBranchName)
    if !repoIsShallow(t, clonedDir) {
        t.Fatal("precondition: expected a shallow repo after --depth=1 fetch")
    }

t.Chdir(clonedDir)
    if err := FetchMetadataTreeOnly(t.Context()); err != nil {
        t.Fatalf("FetchMetadataTreeOnly: %%v", err)
    }

if repoIsShallow(t, clonedDir) {
        t.Errorf("repo still shallow after tree-only fetch; a prior --depth=1 boundary must be healed")
    }
    originRef := "refs/remotes/origin/" + paths.MetadataBranchName
    if n := gitOutput(t, clonedDir, "rev-list", "--count", originRef); n != "2" {
        t.Errorf("metadata history = %s commit(s) after heal, want 2 (full depth)", n)
    }
}  
```

Mcmd/entire/cli/treeless_fetch_full_depth_test.go+64/-27
