refactor(strategy): replace empty-remote network probe with a local, git-branch-only guard · Entire

refactor(strategy): replace empty-remote network probe with a local, git-branch-only guard

db1c52f→main·

karthik-rameshkumar·3d ago·7 files·+96 added/-453 removed

Implements pjbgf's CHANGES_REQUESTED review (#1744):

This deletes the whole probe/marker apparatus from the prior rounds — marker file, TTL, fingerprint, os.Root storage, probe timeout, disable-time sweep, and the PushTargetsInDir helper — since a local ref check needs no memoization.

Note: HEAD-specifically (refs/remotes//HEAD) isn't created by push (verified), so it would defer forever in the init+push flow; any tracking ref is the robust signal. The multi-pushurl-empty edge (Copilot's note) is intentionally no longer handled, per the auth/UX tradeoff above.

Tests: unit test proves the guard is local-only (unreachable URL, never dialed); integration test now asserts both behaviors — git-branch defers then publishes, git-refs publishes on the first push. Alternates canary records a tracking ref to reflect an established remote. Full canary 59/59.

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

Sessions

01KXGBJKM1XD05SF7J8PRD4AP6View transcript

Changes

7

2 unmodified lines

3

4

5

6

6

7

8
232 unmodified lines

241
242
243
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
244
245
246

2 unmodified lines

import (
    "context"
    "encoding/base64"
    "errors"
    "fmt"
    "log/slog"
    "os"
232 unmodified lines

return lsRemote(ctx, dir, remote, patterns...)
}

// PushTargetsInDir resolves the endpoint or endpoints Git will use for a push.
// A named remote may configure pushurl, which differs from its fetch URL and
// can contain more than one destination. URLs and local paths are already
// concrete push targets and are returned unchanged.
func PushTargetsInDir(ctx context.Context, dir, target string) ([]string, error) {
    if target == "" {
        return nil, errors.New("push target must not be empty")
    }
    if isConcretePushTarget(target) {
        return []string{target}, nil
    }

cmd := newCommand(ctx, "remote", "get-url", "--push", "--all", target)
    if dir != "" {
        cmd.Dir = dir
    }
    disableTerminalPrompt(cmd)
    out, err := cmd.Output()
    if err != nil {
        return nil, fmt.Errorf("git remote get-url --push: %w", err)
    }

var targets []string
    for _, line := range strings.Split(string(out), "\n") {
        if pushURL := strings.TrimSpace(line); pushURL != "" {
            targets = append(targets, pushURL)
        }
    }
    if len(targets) == 0 {
        return nil, fmt.Errorf("git remote get-url --push: no push target for %q", target)
    }
    return targets, nil
}

// isConcretePushTarget reports whether target is already a concrete push
// endpoint (a URL or local path) rather than a git remote NAME whose pushurl
// must be resolved. It deliberately does not reuse IsURL's '@' heuristic, which
// misclassifies a remote name that merely contains '@' (e.g. "build@ci") as a
// URL and would skip pushurl resolution for it. Following git's own transport
// detection, an scp-like SSH target has a colon before any slash
// ("[user@]host:path"); a bare remote name has neither a scheme nor such a
// colon.
func isConcretePushTarget(target string) bool {
    if strings.Contains(target, "://") || isLocalPath(target) {
        return true
    }
    // scp-like SSH URL: a colon appears before any slash.
    if i := strings.IndexAny(target, ":/"); i >= 0 && target[i] == ':' {
        return true
    }
    return false
}

func lsRemote(ctx context.Context, dir, remote string, patterns ...string) ([]byte, error) {
    args := append([]string{"ls-remote", remote}, patterns...)
    cmd := newCommand(ctx, args...)
``