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:
A filtered fetch that times out leaves its context past the deadline, so the post-fetch git-config read/write no-op'd and the phantom remote git had already recorded was never stamped — reintroducing the same permanent miss this stamping prevents. Run the stamp helpers on a context detached from the fetch deadline (context.WithoutCancel plus a short bound so a stuck config lock can't hang the caller).
gitRemoteSectionExists matched on a "remote.
." string prefix, so a longer URL sharing a prefix (e.g. a ".../repo.git" section) made a ".../repo" fetch look pre-existing and skipped its stamp. Compare the full URL subsection exactly instead.
Assisted-by: Claude Opus 4.8 noreply@anthropic.com Signed-off-by: Paulo Gomes paulo@entire.io
Sessions
01KXGCX9ZK4SBFXVEB6HHDHK11View transcript
Changes
2
cmd/entire/cli/checkpoint/remote
Mgit.go+44/-13
Mgit_test.go+45
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
}
}
}`