git_operations: heal prior shallow metadata via ref-scoped --depth · Entire

git_operations: heal prior shallow metadata via ref-scoped --depth

d775042→main·

Soph·4w ago·3 files·+93 added/-7 removed

Follow-up to the --unshallow heal: --unshallow is repo-global, so on a repo whose source tree was independently shallow-cloned it would also deepen that unrelated history. Switch to a ref-scoped deep fetch instead.

Add FetchOptions.Depth (--depth=) and fetch the metadata branch with a large Depth (1e9). git semantics: a --depth that exceeds the branch's length fully fetches it (healing a prior --depth=1 boundary) and is a no-op on a full repo (no shallow introduced), while leaving an independently-shallow branch's .git/shallow entry intact. The value stays below math.MaxInt32 (2147483647), which git special-cases as a global unshallow.

Tests: TestFetch_Depth (remote) proves Depth heals the named branch while a second shallow branch stays shallow; the cli heal/no-shallow tests still pass.

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

Sessions

4277ced83b37View transcript

[?
so in ../entiredb "filtered_fetches" is enabled, now today one of my coworkers had this issue:Claude Code·2 steps](/content/gh/entireio/cli/session/e1c2cd64-bc7d-4315-9abd-f106c4eaa991#timeline-4277ced83b37/index.html)

Changes

3

44 unmodified lines

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
14 unmodified lines

74
75
76
77
78
79
80
81

44 unmodified lines

// checkpoint ancestry. Do not set on generic branch fetches — it would
    // silently convert a deliberately-shallow user clone into a full one.
    Unshallow bool
    // Depth adds --depth=<Depth>, fetching the refspec to this absolute depth.
    // Unlike Unshallow (which is repo-global) this is ref-scoped: it fully
    // fetches the named branch — healing a prior shallow boundary on it — while
    // leaving an independently-shallow source-tree clone untouched, and it does
    // not introduce shallowness on a full repo when the value exceeds the
    // branch's length. Use a value above the branch's realistic length but below
    // math.MaxInt32 (2147483647), which git special-cases as a global unshallow.
    // Ignored when zero or when Shallow is set.
    Depth     int
    Dir       string   // working directory (empty = CWD)
    ExtraArgs []string // additional flags before remote (e.g., "--no-write-fetch-head")
}
14 unmodified lines

switch {
    case opts.Shallow:
        args = append(args, "--depth=1")
    case opts.Depth > 0:
        args = append(args, fmt.Sprintf("--depth=%d", opts.Depth))
    case opts.Unshallow && isShallowRepository(ctx, opts.Dir):
        args = append(args, "--unshallow")
    }

Mcmd/entire/cli/checkpoint/remote/git.go+11

9 unmodified lines

10
11
12
13
14
15
16
308 unmodified lines

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

9 unmodified lines

"os"
    "os/exec"
    "path/filepath"
    "strconv"
    "strings"
    "sync"
    "testing"
308 unmodified lines

"Shallow=true should request --depth=1 and leave the repo shallow")
}

// TestFetch_Depth verifies that Depth is ref-scoped: it fully fetches (heals)
// the named branch while leaving an independently-shallow branch shallow —
// unlike Unshallow, which is repo-global.
func TestFetch_Depth(t *testing.T) {
    t.Parallel()
    ctx := context.Background()

tmpDir := t.TempDir()
    bareDir := filepath.Join(tmpDir, "bare.git")
    seedDir := filepath.Join(tmpDir, "seed")
    runIsolatedGit(ctx, t, "", "init", "--bare", bareDir)

testutil.InitRepo(t, seedDir)
    runIsolatedGit(ctx, t, seedDir, "remote", "add", "origin", bareDir)
    for _, c := range []string{"m1", "m2", "m3"} { // main: 3 commits
        testutil.WriteFile(t, seedDir, "f.txt", c)
        testutil.GitAdd(t, seedDir, "f.txt")
        testutil.GitCommit(t, seedDir, c)
    }
    runIsolatedGit(ctx, t, seedDir, "push", "origin", "HEAD:refs/heads/main")
    runIsolatedGit(ctx, t, seedDir, "checkout", "--orphan", "meta")
    runIsolatedGit(ctx, t, seedDir, "rm", "-rf", ".")
    for _, c := range []string{"c1", "c2"} { // meta: 2 commits
        testutil.WriteFile(t, seedDir, "g.txt", c)
        testutil.GitAdd(t, seedDir, "g.txt")
        testutil.GitCommit(t, seedDir, c)
    }
    runIsolatedGit(ctx, t, seedDir, "push", "origin", "HEAD:refs/heads/meta")

// Shallow clone of main + shallow fetch of meta → both branches shallow.
    cloneDir := filepath.Join(tmpDir, "clone")
    runIsolatedGit(ctx, t, "", "clone", "--depth=1", "--single-branch", "--branch", "main", "file://"+bareDir, cloneDir)
    runIsolatedGit(ctx, t, cloneDir, "fetch", "--depth=1", "origin", "+refs/heads/meta:refs/remotes/origin/meta")
    require.True(t, isShallowRepository(ctx, cloneDir))
    require.Equal(t, 1, revListCount(ctx, t, cloneDir, "refs/remotes/origin/meta"))
    require.Equal(t, 1, revListCount(ctx, t, cloneDir, "refs/remotes/origin/main"))

out, err := Fetch(ctx, FetchOptions{
        Remote:   "file://" + bareDir,
        RefSpecs: []string{"+refs/heads/meta:refs/remotes/origin/meta"},
        NoTags:   true,
        Depth:    1_000_000_000,
        Dir:      cloneDir,
    })
    require.NoError(t, err, "fetch output: %s", out)

assert.Equal(t, 2, revListCount(ctx, t, cloneDir, "refs/remotes/origin/meta"),
        "Depth should fully fetch (heal) the named branch")
    assert.Equal(t, 1, revListCount(ctx, t, cloneDir, "refs/remotes/origin/main"),
        "Depth is ref-scoped: an independently-shallow branch must stay shallow")
    assert.True(t, isShallowRepository(ctx, cloneDir),
        "repo stays shallow because main is still bounded")
}

func revListCount(ctx context.Context, t *testing.T, dir, ref string) int {
    t.Helper()
    cmd := exec.CommandContext(ctx, "git", "rev-list", "--count", ref)
    cmd.Dir = dir
    cmd.Env = testutil.GitIsolatedEnv()
    out, err := cmd.Output()
    require.NoError(t, err)
    n, err := strconv.Atoi(strings.TrimSpace(string(out)))
    require.NoError(t, err)
    return n
}

// setupShallowClone creates a bare origin, a seed repo with one commit pushed
// to it, a shallow (--depth=1) clone, and then advances origin by one more
// commit so that a subsequent fetch into the clone has work to do. Returns the

Mcmd/entire/cli/checkpoint/remote/git_test.go+67

403 unmodified lines

404
405
406
407
408
409
410
411
412
413
414
415
416
16 unmodified lines

433
434
435
429
430
436
437
438
439
440
441
22 unmodified lines

464
465
466
459
460
461
462
463
467
468
469
470
471
472
473
474

403 unmodified lines

return CheckoutBranch(ctx, branchName)
}

// metadataFetchDepth is the absolute --depth used when fetching the metadata
// branch. It is far above any realistic checkpoint-branch length, so it fully
// fetches the branch (and heals a prior --depth=1 shallow boundary on it),
// while staying below math.MaxInt32 (2147483647) — which git special-cases as
// a global unshallow that would also deepen an unrelated shallow source tree.
const metadataFetchDepth = 1_000_000_000

// 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).
16 unmodified lines

// 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.
// It also heals a repo that an older CLI already shallowed: the ref-scoped deep
// fetch removes the boundary left by a prior --depth=1 fetch rather than letting
// it linger forever, without deepening an independently-shallow source tree.
func FetchMetadataTreeOnly(ctx context.Context) error {
    return fetchMetadataFromOrigin(ctx, false /* noFilter */)
}
22 unmodified lines

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,
        // connectivity checks for the metadata branch. A ref-scoped deep fetch
        // removes that boundary without deepening an independently-shallow
        // source-tree clone (unlike --unshallow), and is a no-op on a normally
        // cloned repo.
        Depth: metadataFetchDepth,
    })
    if fetchErr != nil {
        if ctx.Err() == context.DeadlineExceeded {