Archived
Fetch in the background and follow changes in the repositories
- On startup, after the local state is shown, fetch the superproject and every initialized submodule (all remotes, prune, at most 8 at once). Fetches never prompt; failures show git's message. --no-fetch skips it, f fetches again. - The header shows "fetching n/m", then the fetch age and failures; the detail pane shows each submodule's last fetch and fetch errors. - The git provider polls each repository's metadata (refs, HEAD, index, .gitmodules) every 2 s and reports changes. The app reloads only what changed, keeping old values, expansion and cursor; loads are single-flight with coalesced reruns. Refresh is driven by the repositories, so a fetch in another shell updates the view like ours. - r reloads everything (worktree edits are not watched). - Docs: F50-F53, N1/N8 rewritten, D18-D20 (D12 superseded). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
19c6f17eb2
commit
27f0c0b5d2
@@ -4,9 +4,11 @@ package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"submarine/internal/submodule"
|
||||
@@ -15,7 +17,9 @@ import (
|
||||
// Provider serves a fixed fixture tree.
|
||||
type Provider struct {
|
||||
MinDelay, MaxDelay time.Duration
|
||||
statuses map[string]*submodule.Status
|
||||
|
||||
mu sync.RWMutex // Tree can run again (reload) while Status runs
|
||||
statuses map[string]*submodule.Status
|
||||
}
|
||||
|
||||
func New() *Provider {
|
||||
@@ -32,6 +36,8 @@ type node struct {
|
||||
}
|
||||
|
||||
func (p *Provider) Tree(ctx context.Context) (*submodule.Repo, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.statuses = map[string]*submodule.Status{}
|
||||
repo := &submodule.Repo{
|
||||
Path: "~/coding/my_project",
|
||||
@@ -57,6 +63,9 @@ func (p *Provider) build(nodes []node, parent *submodule.Submodule) []*submodule
|
||||
Parent: parent,
|
||||
}
|
||||
m.Children = p.build(n.children, m)
|
||||
if n.status.Presence == submodule.PresenceOK && n.status.FetchedAt.IsZero() {
|
||||
n.status.FetchedAt = now.Add(-2 * time.Minute)
|
||||
}
|
||||
p.statuses[full] = n.status
|
||||
out = append(out, m)
|
||||
}
|
||||
@@ -64,22 +73,54 @@ func (p *Provider) build(nodes []node, parent *submodule.Submodule) []*submodule
|
||||
}
|
||||
|
||||
func (p *Provider) Status(ctx context.Context, path string) (*submodule.Status, error) {
|
||||
delay := p.MinDelay
|
||||
if span := p.MaxDelay - p.MinDelay; span > 0 {
|
||||
delay += rand.N(span)
|
||||
}
|
||||
select {
|
||||
case <-time.After(delay):
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
if err := p.sleep(ctx, p.MinDelay, p.MaxDelay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.mu.RLock()
|
||||
s, ok := p.statuses[path]
|
||||
p.mu.RUnlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown submodule %q", path)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Fetchable is true for the superproject and initialized submodules.
|
||||
func (p *Provider) Fetchable(path string) bool {
|
||||
if path == "" {
|
||||
return true
|
||||
}
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
s, ok := p.statuses[path]
|
||||
return ok && s.Presence == submodule.PresenceOK
|
||||
}
|
||||
|
||||
// Fetch pretends to fetch; one submodule always fails. It changes no data.
|
||||
func (p *Provider) Fetch(ctx context.Context, path string) error {
|
||||
if err := p.sleep(ctx, 300*time.Millisecond, 2*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "src/my_submodule_3" {
|
||||
return errors.New("git@github.com: Permission denied (publickey).")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sleep waits a random duration in [min, max) or until ctx is done.
|
||||
func (p *Provider) sleep(ctx context.Context, min, max time.Duration) error {
|
||||
d := min
|
||||
if span := max - min; span > 0 {
|
||||
d += rand.N(span)
|
||||
}
|
||||
select {
|
||||
case <-time.After(d):
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// --- fixture -----------------------------------------------------------------
|
||||
|
||||
var now = time.Now()
|
||||
|
||||
Reference in New Issue
Block a user