fix(checkpoint): stamp on detached ctx and match remote URL exactly · Entire

fix(checkpoint): stamp on detached ctx and match remote URL exactly

09a7aaa→main·

pjbgf·3d ago·2 files·+89 added/-13 removed

Address two gaps in the checkpoint-remote stamping:

Assisted-by: Claude Opus 4.8 noreply@anthropic.com Signed-off-by: Paulo Gomes paulo@entire.io

Sessions

01KXGCX9ZK4SBFXVEB6HHDHK11View transcript

Changes

2

10 unmodified lines

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
90 unmodified lines

119
120
121
115
116
117
118
119
120
121
122
123
122
123
124
125
126
2 unmodified lines

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
28 unmodified lines

183
184
185
166
186
187
188
189
1 unmodified line

191
192
193
174
175
194
195
196
197
198
199
177
200
201
202
203
204
205
206
207
208
209
210
211

10 unmodified lines

"strconv"
    "strings"
    "sync"
    "time"

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

// stampConfigTimeout bounds the local git-config reads/writes that mark a newly
// created checkpoint remote as skipped. They run detached from the fetch's
// context (see stampNewlyCreatedRemote), so a bound guards against a stuck
// config lock hanging the caller.
const stampConfigTimeout = 10 * time.Second

// CheckpointTokenEnvVar is the environment variable for providing an access token
// used to authenticate git push/fetch operations for checkpoint branches.
// The token is injected as an HTTP Basic Authorization header per RFC 7617:
90 unmodified lines

disableTerminalPrompt(cmd)
    out, err := cmd.CombinedOutput()

// Stamp whenever this fetch newly created the section — even on a fetch
    // error. Git writes remote.<url>.promisor eagerly during connection setup,
    // so a failed filtered fetch still leaves the phantom remote behind; if we
    // only stamped on success it would linger unstamped forever (the section
    // then exists on the next attempt, so it never looks "new" again). Checking
    // existence after the fetch keeps us from inventing a section when the fetch
    // died before git wrote anything.
    if stampCandidate && !existedBefore && gitRemoteSectionExists(ctx, opts.Dir, stampURL) {
        markRemoteSkipped(ctx, opts.Dir, stampURL)
    if stampCandidate && !existedBefore {
        stampNewlyCreatedRemote(ctx, opts.Dir, stampURL)
    }

if err != nil {
2 unmodified lines

return out, nil
}

// stampNewlyCreatedRemote stamps a URL-keyed remote section that this fetch just
// created. Git writes remote.<url>.promisor eagerly during connection setup, so
// a filtered fetch that later fails still leaves the phantom remote behind;
// stamping here — rather than only on fetch success — keeps it from lingering
// unstamped forever (the section then exists on the next attempt, so it never
// looks "new" again). Re-checking existence keeps us from inventing a section
// when the fetch died before git wrote anything.
func stampNewlyCreatedRemote(ctx context.Context, dir, url string) {
    ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), stampConfigTimeout)
    defer cancel()
    if gitRemoteSectionExists(ctx, dir, url) {
        markRemoteSkipped(ctx, dir, url)
    }
}

// markRemoteSkipped stamps skipFetchAll/skipDefaultUpdate on a URL-keyed remote
// section so `git fetch --all` and `git remote update` skip it. Called only for
// remotes this fetch just created, so an adhoc checkpoint URL never lingers as a
// about to create a new URL-keyed remote, so we only stamp remotes we create and
// never rewrite ones the user already has.
func gitRemoteSectionExists(ctx context.Context, dir, url string) bool {
    cmd := exec.CommandContext(ctx, "git", "config", "--local", "--list")
    cmd := exec.CommandContext(ctx, "git", "config", "--local", "--list", "--name-only")
    if dir != "" {
        cmd.Dir = dir
    }
1 unmodified line

if err != nil {
        return false
    }
    // git --list preserves subsection (the URL) case, so match it verbatim.
    prefix := "remote." + url + "."
    // Each name is "remote.<url>.<key>". Git config keys carry no dots, so the
    // final dotted component is the key and everything between "remote." and it
    // is the subsection (the URL, whose case git preserves). Compare the
    // subsection exactly so a longer URL that shares a prefix (e.g. a
    // ".../repo.git" section vs a ".../repo" fetch) is not a false match.
    for line := range strings.SplitSeq(string(out), "\n") {
        if strings.HasPrefix(line, prefix) {
        rest, ok := strings.CutPrefix(line, "remote.")
        if !ok {
            continue
        }
        lastDot := strings.LastIndexByte(rest, '.')
        if lastDot < 0 {
            continue
        }
        if rest[:lastDot] == url {
            return true
        }
    }
}`