Archived
Fetch only on request, remove --no-fetch
Nothing is fetched on startup any more; the header and detail pane show the last fetch and f fetches everything. Records the change as D22. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,6 @@ and decisions. Read the relevant document before changing behavior:
|
||||
```sh
|
||||
go run ./cmd/submarine [dir] # run on a repository (default: cwd)
|
||||
go run ./cmd/submarine --mock # run on mock data
|
||||
go run ./cmd/submarine --no-fetch [dir] # without background fetch
|
||||
scripts/make-fixture.sh DIR # build a test superproject in DIR
|
||||
make dist # cross-compile into dist/
|
||||
GITEA_TOKEN=… make release # publish a tagged release (outward-facing)
|
||||
|
||||
@@ -24,8 +24,9 @@ the questions you actually have:
|
||||
(`·1`) and shows `●` if a staged pin is hidden.
|
||||
- **Words, not symbols.** Every state is a short word (`unpushed`, `conflict`,
|
||||
`missing`, `off pin`, `changed`), so nothing depends on color alone.
|
||||
- **Live.** Fetches in the background on startup and follows changes you make
|
||||
in other shells within about two seconds.
|
||||
- **Live.** Follows changes you make in other shells within about two
|
||||
seconds. It never fetches on its own: the header shows when you last did,
|
||||
`f` fetches everything.
|
||||
- **Built for dozens to hundreds of submodules.** Each status loads
|
||||
asynchronously, so the UI never freezes.
|
||||
|
||||
@@ -44,7 +45,6 @@ Update later with `sub update`.
|
||||
```sh
|
||||
sub # the repository in the current directory
|
||||
sub ~/code/project # any directory inside a repository
|
||||
sub --no-fetch # skip the background fetch
|
||||
sub --mock # built-in sample data, no repository needed
|
||||
```
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ func main() {
|
||||
}
|
||||
|
||||
useMock := flag.Bool("mock", false, "use built-in mock data instead of a real repository")
|
||||
noFetch := flag.Bool("no-fetch", false, "don't fetch remotes in the background on startup")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintln(flag.CommandLine.Output(), "usage: submarine [--mock] [--no-fetch] [dir]\n submarine update [--check] [--force]\n submarine version")
|
||||
fmt.Fprintln(flag.CommandLine.Output(), "usage: submarine [--mock] [dir]\n submarine update [--check] [--force]\n submarine version")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
@@ -72,9 +71,9 @@ func main() {
|
||||
zone.NewGlobal()
|
||||
defer zone.Close()
|
||||
|
||||
notice := checkForUpdate(ctx, *noFetch || *useMock)
|
||||
notice := checkForUpdate(ctx, *useMock)
|
||||
|
||||
p := tea.NewProgram(tui.New(ctx, provider, tui.Options{Fetch: !*noFetch}), tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
p := tea.NewProgram(tui.New(ctx, provider), tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
@@ -90,7 +89,7 @@ func main() {
|
||||
|
||||
// checkForUpdate looks for a newer release in the background (at most once a
|
||||
// day, cached) and delivers its version, or "" when there is none. Disabled
|
||||
// for dev builds, --no-fetch, --mock and SUBMARINE_NO_UPDATE_CHECK.
|
||||
// for dev builds, --mock and SUBMARINE_NO_UPDATE_CHECK.
|
||||
func checkForUpdate(ctx context.Context, off bool) <-chan string {
|
||||
out := make(chan string, 1)
|
||||
cache, err := os.UserCacheDir()
|
||||
|
||||
+1
-2
@@ -31,8 +31,7 @@ Installs `submarine` and the alias `sub` (linux/macOS, amd64/arm64) to
|
||||
|
||||
**Prototype.** The UI is complete for browsing. It reads real repositories
|
||||
through a git provider (`internal/submodule/git`): `submarine [dir]`. It
|
||||
fetches in the background on startup (`--no-fetch` to skip; `f` to fetch
|
||||
again) and follows changes made in other shells. `submarine --mock` shows
|
||||
fetches only when asked (`f`) and follows changes made in other shells. `submarine --mock` shows
|
||||
the built-in mock data (`internal/submodule/mock`). `!` shows only
|
||||
submodules with problems. The actions `u` and `b` only show a "not
|
||||
implemented" message ([drafts of the write actions](actions.md)).
|
||||
|
||||
@@ -119,9 +119,9 @@ The mock provider (`internal/submodule/mock`) covers every state and sleeps
|
||||
calling `Status()` → `statusLoadedMsg`.
|
||||
3. Each result is stored on the submodule (`Status` or `Err`); the view
|
||||
updates incrementally, including parents' hidden-issue counts.
|
||||
4. After the first tree: the watcher starts (`changeMsg`), and the
|
||||
background fetch starts one `tea.Cmd` per fetchable repository
|
||||
(`fetchDoneMsg`).
|
||||
4. After the first tree: the watcher starts (`changeMsg`). Nothing is
|
||||
fetched until the user presses `f`, which starts one `tea.Cmd` per
|
||||
fetchable repository (`fetchDoneMsg`).
|
||||
5. The spinner ticks only while something loads or fetches.
|
||||
|
||||
### Fetch and refresh are decoupled (D20)
|
||||
@@ -197,7 +197,7 @@ No CI runner: releases are built and published from a developer machine.
|
||||
so `sub` works too). `SUBMARINE_UPDATE_URL` overrides the registry.
|
||||
- After the TUI exits, a one-line notice is printed if a newer release
|
||||
exists. The check runs in the background at startup, at most once a day
|
||||
(cached in the user cache dir) and is off for dev builds, `--no-fetch`,
|
||||
(cached in the user cache dir) and is off for dev builds,
|
||||
`--mock` and `SUBMARINE_NO_UPDATE_CHECK=1`.
|
||||
|
||||
## Testing
|
||||
|
||||
+18
-1
@@ -150,7 +150,7 @@ removed + untracked.
|
||||
**Consequences.** One extra `git diff` per dirty submodule, none for clean
|
||||
ones. Not included: staged/unstaged markers, diff bodies, stash count.
|
||||
|
||||
## D18 — Background fetch on startup
|
||||
## D18 — Background fetch on startup (superseded by D22)
|
||||
|
||||
**Context.** Without fetching, every answer is only as fresh as the user's
|
||||
last `git fetch`, which they rarely run in every submodule.
|
||||
@@ -296,3 +296,20 @@ count as problems because their state is unknown (D7 counts `error` as danger
|
||||
too); the cost is that rows vanish as statuses arrive, so the selection is
|
||||
refitted after each status. Staged pins are not shown by it, since they are
|
||||
not issues.
|
||||
|
||||
## D22 — Fetch only on request
|
||||
|
||||
**Context.** D18 fetched every repository on startup. With hundreds of
|
||||
submodules that is hundreds of network round trips (and credential or
|
||||
host-key failures) every time the tool opens, whether or not the user wants
|
||||
fresh remote data.
|
||||
**Decision.** Supersedes D18. Nothing is fetched on startup. The header and
|
||||
the detail pane show the last fetch (`FETCH_HEAD`, also from fetches made in
|
||||
other shells), so the user can judge how stale the remote refs are and press
|
||||
`f` to fetch. The `--no-fetch` flag is removed with it (it still had the
|
||||
side effect of switching off the update check; that has
|
||||
`SUBMARINE_NO_UPDATE_CHECK`).
|
||||
**Consequences.** Right after startup, `unpushed`, `↓n` and the like are as
|
||||
fresh as the last fetch. `fetched never` appears for a repository that was
|
||||
never fetched. The progress display and per-submodule fetch errors stay as
|
||||
they are, they now belong to `f`.
|
||||
|
||||
@@ -100,9 +100,10 @@ commit hashes. It doesn't answer the questions users actually have:
|
||||
|
||||
### Fetch and refresh
|
||||
|
||||
- **F50** On startup, after the local state is shown, the superproject and
|
||||
every initialized submodule are fetched in the background (all remotes,
|
||||
with prune). `--no-fetch` turns this off.
|
||||
- **F50** Nothing is fetched on startup. The header and the detail pane show
|
||||
when the repositories were last fetched (`FETCH_HEAD`), whoever did it.
|
||||
`f` fetches the superproject and every initialized submodule in the
|
||||
background (all remotes, with prune).
|
||||
- **F51** While fetching, the header shows progress (`fetching 12/174`)
|
||||
instead of the fetch age; afterwards the age and the number of failed
|
||||
fetches. The detail pane shows each submodule's last fetch and, if it
|
||||
@@ -141,13 +142,12 @@ See [actions.md](actions.md) for the draft of the write actions.
|
||||
```sh
|
||||
submarine [dir] # the repository containing dir (default: current directory)
|
||||
submarine --mock # built-in mock data
|
||||
submarine --no-fetch [dir] # no background fetch on startup
|
||||
```
|
||||
|
||||
## Non-functional requirements
|
||||
|
||||
- **N1** Startup must not block on the network. The tool shows the local
|
||||
state at once and fetches in the background (F50).
|
||||
state at once and uses the network only when the user asks (F50).
|
||||
- **N2** Loading many submodules must not freeze the UI (async, per
|
||||
submodule).
|
||||
- **N3** Colors follow the user's terminal theme (ANSI 16 colors); the UI
|
||||
|
||||
+1
-11
@@ -29,17 +29,11 @@ const (
|
||||
detailWidthRatio = 0.4
|
||||
)
|
||||
|
||||
// Options configure the application.
|
||||
type Options struct {
|
||||
Fetch bool // fetch all repositories in the background on startup
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
ctx context.Context
|
||||
provider submodule.Provider
|
||||
fetcher submodule.Fetcher // nil if the provider can't fetch
|
||||
watcher submodule.Watcher // nil if the provider can't watch
|
||||
opts Options
|
||||
theme theme.Theme
|
||||
keys keyMap
|
||||
|
||||
@@ -66,7 +60,7 @@ type Model struct {
|
||||
showDetail bool
|
||||
}
|
||||
|
||||
func New(ctx context.Context, p submodule.Provider, opts Options) Model {
|
||||
func New(ctx context.Context, p submodule.Provider) Model {
|
||||
t := theme.Default()
|
||||
k := defaultKeys()
|
||||
sp := spinner.New(spinner.WithSpinner(spinner.MiniDot))
|
||||
@@ -79,7 +73,6 @@ func New(ctx context.Context, p submodule.Provider, opts Options) Model {
|
||||
m := Model{
|
||||
ctx: ctx,
|
||||
provider: p,
|
||||
opts: opts,
|
||||
theme: t,
|
||||
keys: k,
|
||||
loads: newReloader(),
|
||||
@@ -289,9 +282,6 @@ func (m Model) treeLoaded(msg treeLoadedMsg) (tea.Model, tea.Cmd) {
|
||||
m.changes = m.watcher.Watch(m.ctx)
|
||||
cmds = append(cmds, m.waitForChange())
|
||||
}
|
||||
if m.opts.Fetch {
|
||||
cmds = append(cmds, m.startFetch())
|
||||
}
|
||||
} else {
|
||||
m.tree.ReplaceRoots(m.repo.Submodules)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user