Replace bootstrap progress logging with slog · Entire

Replace bootstrap progress logging with slog

d11c597→main·

Soph·3mo ago·3 files·+52 added/-31 removed

Sessions

75d8cdda2721View transcript

[?
Can you take a look at the go code (wasm) in /Users/soph/Work/entire/devenv/entire-io-worktree1 based a bit on that I wonder if something like this can be build:Codex·GPT-5.4·1 step](/content/gh/entireio/git-sync/session/019d6d29-8cf7-7fe3-adc9-8c3e4d9d5603#timeline-75d8cdda2721/index.html)

Changes

3

433 unmodified lines

434
435
436
437
437
438
439
440
2 unmodified lines

443
444
445
446
446
447
448
449

433 unmodified lines

- Relay strategies are separate packages with explicit inputs and outputs. Status: done
- Tag creation is correct whether or not a pack transfer is needed. Status: done
- Stats are concurrency-safe. Status: done
- Logging is structured and concurrency-safe. Status: open
- Logging is structured and concurrency-safe. Status: done
- Protocol parsing has explicit malformed-input tests. Status: done
- Rewrite passes `go test ./...` and `go test -race ./...`. Status: done
- Rewrite includes benchmarks for the critical planning and execution paths. Status: partial

Notes:
- Capability handling is much better centralized under `internal/gitproto`, but the rewrite still uses `go-git` transport/protocol types rather than fully owning the protocol layer end-to-end.
- Stats are now concurrency-safe and race-tested.
- Logging is still ad hoc `progressf` output rather than structured logging.
- Bootstrap logging now uses structured `slog` output instead of ad hoc formatted stderr lines.

## Suggested Execution Order

Mdocs/rewrite-issue-list.md+2/-2

7 unmodified lines

8
9
10
11
12
12
13
14
15
27 unmodified lines

43
44
45
46
47
48
46
47
48
49
50
51
52
23 unmodified lines

76
77
78
78
79
80
81
82
83
1 unmodified line

85
86
87
86
88
89
90
91
4 unmodified lines

96
97
98
97
99
100
101
102
1 unmodified line

104
105
106
105
107
108
109
110
111
39 unmodified lines

151
152
153
151
154
155
156
157
6 unmodified lines

164
165
166
164
165
166
167
168
167
168
169
170
171
172
173
174
175
176
177
3 unmodified lines

181
182
183
178
179
180
184
185
186
187
188
189
190
191
192
22 unmodified lines

215
216
217
209
210
218
219
220
221
222
223
224
12 unmodified lines

237
238
239
229
240
241
242
243
244
234
245
246
247
248
48 unmodified lines

297
298
299
289
300
301
302
303
44 unmodified lines

348
349
350
340
341
351
352
353
354
355
356
357
358
154 unmodified lines

513
514
515
502
503
516
517
518
519
506
520
521

7 unmodified lines

"errors"
    "fmt"
    "io"
    "log/slog"
    "net/http"
    "os"
    "regexp"
    "strconv"
    "strings"
27 unmodified lines

TargetAdv     *packp.AdvRefs
    DesiredRefs   map[plumbing.ReferenceName]planner.DesiredRef
    TargetRefs    map[plumbing.ReferenceName]plumbing.Hash
    MaxPackBytes int64
    BatchMaxPack int64
    Verbose      bool
    MaxPackBytes  int64
    BatchMaxPack  int64
    Verbose       bool
    Logger        *slog.Logger
}

// Result holds the outcome of the bootstrap strategy.
23 unmodified lines

// GitHub large-repo preflight
    if batchLimit, ok := githubBatchLimit(ctx, p); ok {
        p.BatchMaxPack = batchLimit
        progressf(p.Verbose, "bootstrap: github repo-size preflight selected batched mode with batch-max-pack-bytes=%d", p.BatchMaxPack)
        p.log("bootstrap github preflight selected batched mode",
            "batch_max_pack_bytes", p.BatchMaxPack)
    }

if p.BatchMaxPack > 0 {
    }

// One-shot bootstrap
    progressf(p.Verbose, "bootstrap: fetching %d ref(s) from source", len(plans))
    p.log("bootstrap fetching refs from source", "ref_count", len(plans))
    gpDesired := convert.DesiredRefs(p.DesiredRefs)
    packReader, err := p.SourceService.FetchPack(ctx, p.SourceConn, gpDesired, nil)
    if err != nil {
        4 unmodified lines
    }

packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)

progressf(p.Verbose, "bootstrap: pushing %d ref(s) to target", len(plans))
    p.log("bootstrap pushing refs to target", "ref_count", len(plans))
    cmds := gitproto.ToPushCommands(convert.PlansToPushPlans(plans))
    pushErr := gitproto.PushPack(ctx, p.TargetConn, p.TargetAdv, cmds, packReader, p.Verbose)
    if pushErr != nil {
        1 unmodified line
        if !ok {
            return result, fmt.Errorf("push target refs: %w", pushErr)
        }
        progressf(p.Verbose, "bootstrap: target rejected; retrying with batch-max-pack-bytes=%d", autoBatch)
        p.log("bootstrap retrying with batched mode after target rejection",
            "batch_max_pack_bytes", autoBatch)
        p.BatchMaxPack = autoBatch
        return executeBatched(ctx, p, plans, result)
    }
    39 unmodified lines

var batches []planner.BootstrapBatch
    if len(planRefs) > 0 {
        progressf(p.Verbose, "bootstrap-batch: planning checkpoints for %d branch ref(s)", len(planRefs))
        p.log("bootstrap batch planning checkpoints", "branch_ref_count", len(planRefs))
        var err error
        batches, err = planBatches(ctx, p, planRefs)
        if err != nil {
            6 unmodified lines
        }

batchLimit = p.MaxPackBytes
    }

for _, batch := range batches {
        result.PlannedBatchCount += len(batch.Checkpoints)
        result.TempRefs = append(result.TempRefs, batch.TempRef.String())
        progressf(p.Verbose, "bootstrap-batch: branch=%s temp-ref=%s planned-batches=%d resume=%s",
            batch.Plan.TargetRef, batch.TempRef, len(batch.Checkpoints), planner.ShortHash(batch.ResumeHash))
        for _, batch := range batches {
            result.PlannedBatchCount += len(batch.Checkpoints)
            result.TempRefs = append(result.TempRefs, batch.TempRef.String())
            p.log("bootstrap batch branch plan",
                "branch", batch.Plan.TargetRef.String(),
                "temp_ref", batch.TempRef.String(),
                "planned_batches", len(batch.Checkpoints),
                "resume_hash", planner.ShortHash(batch.ResumeHash))

current := batch.ResumeHash
        startIdx, err := planner.BootstrapResumeIndex(batch.Checkpoints, batch.ResumeHash)
        3 unmodified lines

for idx := startIdx; idx < len(batch.Checkpoints); idx++ {
            checkpoint := batch.Checkpoints[idx]
            progressf(p.Verbose, "bootstrap-batch: branch=%s batch=%d/%d from=%s to=%s",
                batch.Plan.TargetRef, idx+1, len(batch.Checkpoints),
                planner.ShortHash(current), planner.ShortHash(checkpoint))
            p.log("bootstrap batch push checkpoint",
                "branch", batch.Plan.TargetRef.String(),
                "batch", idx+1,
                "batch_total", len(batch.Checkpoints),
                "from", planner.ShortHash(current),
                "to", planner.ShortHash(checkpoint))

stagePlans := []planner.BranchPlan{{
                Branch: batch.Plan.Branch, SourceRef: batch.Plan.SourceRef,
            22 unmodified lines

if err := gitproto.PushPack(ctx, p.TargetConn, p.TargetAdv, cmds, packReader, p.Verbose); err != nil {
                return result, fmt.Errorf("push bootstrap batch for %s: %w", batch.Plan.TargetRef, err)
            }
            progressf(p.Verbose, "bootstrap-batch: branch=%s batch=%d/%d complete",
                batch.Plan.TargetRef, idx+1, len(batch.Checkpoints))
            p.log("bootstrap batch checkpoint complete",
                "branch", batch.Plan.TargetRef.String(),
                "batch", idx+1,
                "batch_total", len(batch.Checkpoints))
            current = checkpoint
            result.BatchCount++
        }
    }

// Tag phase (issue #1)
    if len(tagPlans) > 0 {
        progressf(p.Verbose, "bootstrap-batch: pushing %d tag(s) after branch batches", len(tagPlans))
        p.log("bootstrap batch pushing tags after branch batches", "tag_count", len(tagPlans))
        tagTargetRefs := planner.CopyRefHashMap(p.TargetRefs)
        for _, batch := range batches {
            tagTargetRefs[batch.Plan.TargetRef] = batch.Plan.SourceHash
            48 unmodified lines
    }

// PlanCheckpoints plans the checkpoint hashes for a single branch during batched bootstrap.
func PlanCheckpoints(ctx context.Context, p Params, ref planner.DesiredRef) ([]plumbing.Hash, error) {
    progressf(p.Verbose, "bootstrap-batch: fetching commit graph for %s", ref.TargetRef)
    p.log("bootstrap batch fetching commit graph", "branch", ref.TargetRef.String())
    graphStore := memory.NewStorage()
    gpRef := gitproto.DesiredRef{SourceRef: ref.SourceRef, TargetRef: ref.TargetRef, SourceHash: ref.SourceHash}
    if err := p.SourceService.FetchCommitGraph(ctx, graphStore, p.SourceConn, gpRef); err != nil {
        44 unmodified lines
    }

prevIdx = bestIdx
    prevHash = chain[bestIdx]
    checkpoints = append(checkpoints, prevHash)
    progressf(p.Verbose, "bootstrap-batch: branch=%s planned-checkpoint=%s selected=%d chain-len=%d",
        ref.TargetRef, planner.ShortHash(prevHash), len(checkpoints), len(chain))
    p.log("bootstrap batch planned checkpoint",
        "branch", ref.TargetRef.String(),
        "checkpoint", planner.ShortHash(prevHash),
        "selected", len(checkpoints),
        "chain_len", len(chain))
    }
    return checkpoints, nil
}

154 unmodified lines
}

func progressf(verbose bool, format string, args ...any) {
    if !verbose {
func (p Params) log(msg string, args ...any) {
    if p.Logger == nil {
        return
    }
    fmt.Fprintf(os.Stderr, "[git-sync] %s\n", fmt.Sprintf(format, args...))
    p.Logger.Info(msg, args...)
}