Exclude URL-keyed promisor entries from git fetch --all · Entire

Exclude URL-keyed promisor entries from git fetch --all

d9df8fc→main·

toothbrush·4d ago·2 files·+166 added/-1 removed

Every filtered checkpoint fetch from a URL leaves a remote. promisor config section behind (git needs it for lazy object fetches), and git treats each one as a fetchable remote: git fetch --all and git remote update dial every checkpoint URL ever fetched from, including dead mirror hosts and SSH github URLs. Stamp skipFetchAll/skipDefaultUpdate on the entry right after a filtered URL fetch so bulk fetches skip it while lazy fetches keep working. Gated on git having actually written the promisor entry, so no section is invented for unfiltered fetches.

Changes

2

3 unmodified lines

4
5
6
7
8
9
10
1 unmodified line

12
13
14
15
16
17
18
59 unmodified lines

78
79
80
79
81
82
83
84
85
8 unmodified lines

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

3 unmodified lines

"context"
    "encoding/base64"
    "fmt"
    "log/slog"
    "os"
    "os/exec"
    "path/filepath"
1 unmodified line

"strings"
    "sync"

"github.com/entireio/cli/cmd/entire/cli/logging"
    "github.com/entireio/cli/cmd/entire/cli/settings"
}

// markPromisorEntrySkipped excludes the URL-keyed config section that git
// creates for a filtered URL fetch (remote.<url>.promisor=true) from
// `git fetch --all` and `git remote update`. Git needs the promisor entry to
// lazy-fetch filtered-out objects later, but the entry also makes the URL show
// up as a fetchable remote, so without this every checkpoint URL ever fetched
// from lingers as a phantom remote that bulk fetches keep dialing.
// Best-effort: the fetch already succeeded, so failures only log.
func markPromisorEntrySkipped(ctx context.Context, dir, url string) {
    if !gitConfigBool(ctx, dir, "remote."+url".promisor") {
        // Git didn't record a promisor entry for this URL; don't invent a
        // config section that wouldn't otherwise exist.
        return
    }
    if gitConfigBool(ctx, dir, "remote."+url".skipFetchAll") {
        return
    }
    for _, key := range []string{"skipFetchAll", "skipDefaultUpdate"} {
        cmd := exec.CommandContext(ctx, "git", "config", "--local", "remote."+url+"."+key, "true")
        if dir != "" {
            cmd.Dir = dir
        }
        if out, cfgErr := cmd.CombinedOutput(); cfgErr != nil {
            logging.Warn(ctx, "failed to mark promisor config entry as skipped for bulk fetches",
                    slog.String("url", RedactURL(url)),
                    slog.String("key", key),
                    slog.String("output", strings.TrimSpace(string(out))),
                    slog.String("error", cfgErr.Error()),
                )
            return
        }
    }
}

// FetchBlobs fetches specific objects (typically blobs) by hash from a remote.
// Uses `git fetch-pack` rather than `git fetch` because the high-level
// porcelain enforces partial-clone integrity checks that reject blob-only

// TestFetch_FilteredURLFetchMarksPromisorSkipped verifies that after a
// filtered fetch from a URL, the URL-keyed promisor config section git creates
// is excluded from `git fetch --all` / `git remote update` — otherwise every
// checkpoint URL ever fetched from lingers as a phantom remote that bulk
// fetches keep dialing.
func TestFetch_FilteredURLFetchMarksPromisorSkipped(t *testing.T) {
    ctx := context.Background()

tmpDir := t.TempDir()
    originBare := filepath.Join(tmpDir, "origin.git")
    checkpointBare := filepath.Join(tmpDir, "checkpoints.git")
    seedDir := filepath.Join(tmpDir, "seed")
    cloneDir := filepath.Join(tmpDir, "clone")

testutil.InitRepo(t, seedDir)
    testutil.WriteFile(t, seedDir, "f.txt", "init")
    testutil.GitAdd(t, seedDir, "f.txt")
    testutil.GitCommit(t, seedDir, "init")

// Separate origin and checkpoint repos, mirroring the real setup where
    // checkpoints are fetched by URL from a repo that is not origin.
    runIsolatedGit(ctx, t, "", "init", "--bare", originBare)
    runIsolatedGit(ctx, t, "", "init", "--bare", checkpointBare)
    runIsolatedGit(ctx, t, checkpointBare, "config", "uploadpack.allowFilter", "true")
    runIsolatedGit(ctx, t, seedDir, "push", originBare, "HEAD:refs/heads/main")
    runIsolatedGit(ctx, t, "", "clone", "--branch", "main", "file://"+originBare, cloneDir)

// A commit only in the checkpoint repo so the filtered fetch has
    // something to transfer.
    testutil.WriteFile(t, seedDir, "f.txt", "init\nnext\n")
    testutil.GitAdd(t, seedDir, "f.txt")
    testutil.GitCommit(t, seedDir, "next")
    runIsolatedGit(ctx, t, seedDir, "push", checkpointBare, "HEAD:refs/heads/main")

// Filtered fetches read .entire settings from the CWD repo.
    testutil.WriteFile(
        t,
        cloneDir,
        ".entire/settings.json",
        `{"enabled": true, "strategy_options": {"filtered_fetches": true}}`,
    )
    t.Chdir(cloneDir)

fetchURL := "file://" + checkpointBare
    out, err := Fetch(ctx, FetchOptions{
        Remote:   fetchURL,
        RefSpecs: []string{"+refs/heads/main:refs/entire-fetch-tmp/main"},
        NoTags:   true,
        Dir:      cloneDir,
    })
    require.NoError(t, err, "fetch output: %s", out)

// Sanity: git recorded the URL-keyed promisor entry for the filtered fetch.
    require.True(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".promisor"),
        "expected git to record a promisor entry for the filtered URL fetch")

assert.True(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".skipFetchAll"),
        "URL-keyed promisor entry should be excluded from git fetch --all")
    assert.True(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".skipDefaultUpdate"),
        "URL-keyed promisor entry should be excluded from git remote update")

// git fetch --all must no longer dial the phantom entry: with the
    // checkpoint repo gone, --all only succeeds if the URL-keyed entry is
    // skipped (origin is still reachable).
    require.NoError(t, os.RemoveAll(checkpointBare))
    runIsolatedGit(ctx, t, cloneDir, "fetch", "--all", "--no-auto-gc")
}

// TestFetch_UnfilteredFetchDoesNotCreateConfigSection verifies the stamp is
// gated on git having created a promisor entry: a plain (unfiltered) URL fetch
// must not invent a remote.<url> config section.
func TestFetch_UnfilteredFetchDoesNotCreateConfigSection(t *testing.T) {
    ctx := context.Background()

tmpDir := t.TempDir()
    bareDir := filepath.Join(tmpDir, "bare.git")
    seedDir := filepath.Join(tmpDir, "seed")
    cloneDir := filepath.Join(tmpDir, "clone")

testutil.InitRepo(t, seedDir)
    testutil.WriteFile(t, seedDir, "f.txt", "init")
    testutil.GitAdd(t, seedDir, "f.txt")
    testutil.GitCommit(t, seedDir, "init")

runIsolatedGit(ctx, t, "", "init", "--bare", bareDir)
    runIsolatedGit(ctx, t, seedDir, "remote", "add", "origin", bareDir)
    runIsolatedGit(ctx, t, seedDir, "push", "origin", "HEAD:refs/heads/main")
    runIsolatedGit(ctx, t, "", "clone", "--branch", "main", "file://"+bareDir, cloneDir)

testutil.WriteFile(
        t,
        cloneDir,
        ".entire/settings.json",
        `{"enabled": true, "strategy_options": {"filtered_fetches": true}}`,
    )
    t.Chdir(cloneDir)

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

assert.False(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".promisor"))
    assert.False(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".skipFetchAll"))
    assert.False(t, gitConfigBool(ctx, cloneDir, "remote."+fetchURL+".skipDefaultUpdate"))
}