Surface bootstrap pack subdivisions as inline notices · Entire

Surface bootstrap pack subdivisions as inline notices

When a bootstrap push hits the target body limit, the planner silently splits the remaining work into more packs — first 1 → 2, then on next rejection 2 → 4, and so on. Until now the only trace of this was a slog line behind --verbose, so users running with --progress just saw the pack denominator jump from 2 to 4 with no explanation.

Add a one-line notice at each subdivision point so the reason is visible inline with progress. Three trigger sites: the one-shot → batched fallback, the pre-push header-estimate split, and the post-rejection split. Messages flow through a new OnNotice hook on bstrap.Params, then through syncSession.notice, which routes them through progressReporter.notify (clears the live frame, prints the message, redraws on next tick) when --progress is on and falls back to plain stderr otherwise.

Sessions

Changes

65 unmodified lines

// Called from the goroutine driving Execute; implementations must not
// block.
OnPhase func(string)
// OnNotice, when non-nil, receives one-time human-readable messages
// about discrete events worth surfacing alongside progress (pack
// subdivision, switching to batched mode). Implementations should
// treat each call as one log line.
OnNotice func(string)
}

func (p Params) notice(msg string) {
if p.OnNotice != nil {
p.OnNotice(msg)
}
}

// Result holds the outcome of the bootstrap strategy.
78 unmodified lines
// terminate halts the ticker, draws one final frame so the printed line
// reflects the closing byte counts, and emits a newline so subsequent
// command output starts on a fresh row.
func humanBytes(n int64) string {
const unit = 1024
if n < unit {
return fmt.Sprintf("%d B", n)
}
div, exp := int64(unit), 0
for x := n / unit; x >= unit; x /= unit {
div *= unit
exp++
}
value := float64(n) / float64(div)
suffix := []string{"KB", "MB", "GB", "TB", "PB"}[exp]
if value >= 100 {
return fmt.Sprintf("%.0f %s", value, suffix)
}
if value >= 10 {
return fmt.Sprintf("%.1f %s", value, suffix)
}
return fmt.Sprintf("%.2f %s", value, suffix)
}

func isTargetBodyLimitError(err error) bool {
if err == nil {
return false
}

// notify writes a one-time message above the live progress line. The
// current frame is cleared first so the message lands on a clean row,
// and lastLen is reset so the next tick redraws the progress below.
// Safe to call from any goroutine, including while the ticker is
// running.
func (p *progressReporter) notify(msg string) {
p.mu.Lock()
defer p.mu.Unlock()
if p.lastLen > 0 {
fmt.Fprint(p.out, "\r"+strings.Repeat(" ", p.lastLen)+"\r")
}
fmt.Fprintln(p.out, msg)
p.lastLen = 0
}
// notice surfaces a one-line human-readable event during a sync. When
// the live progress ticker is active it prints above the current frame;
// otherwise it falls back to plain stderr so the message is still seen
// when --progress is off or the destination is not a TTY.
func (s *syncSession) notice(msg string) {
if s.progress != nil {
s.progress.notify(msg)
return
}
fmt.Fprintln(os.Stderr, msg)
}