e45cc89462f8
3.3.0: a whole-repo steal's progress line counts files, saves and events against their totals
- time
- 2026-10-03 14:42 UTC · 1 week ago
- machine
- Junes-MacBook-Pro.local
- agent
- claude-opus-5-5
- session
- steal progress totals
- parent
- 44adaadc6a8e
↓ koh-e45cc894.tar.gzatlas at this save
files changed5 files
VERSION+1 −1src/cli/cmd_steal.v+10 −7src/cli/progress.v+29 −3src/cli/progress_test.v+32 −0v.mod+1 −1src/cli/cmd_steal.v+10 −7
@@ -431,27 +431,30 @@431431 if exchange.signed_in_to(base) {432432 return steal_repo(repo, args)433433 }434- mut backend := remote.Backend(watch('stealing ${repo}', exchange.read_only('${base}/${repo}') or {435- return refuse(args.json(), err.msg())436- }))437- return steal_whole(repo, args, mut backend, '${base}/${repo}')434+ backend := exchange.read_only('${base}/${repo}') or { return refuse(args.json(), err.msg()) }435+ return steal_whole(repo, args, backend, '${base}/${repo}')438436 }439437 440438 // steal_repo fetches a whole repo into ./<name>, for a machine that does not441439 // have it yet.442440 fn steal_repo(name string, args Args) int {443- mut backend := watched_backend('stealing ${name}') or { return refuse(args.json(), err.msg()) }444- return steal_whole(name, args, mut backend, '')441+ backend := this_machine_backend() or { return refuse(args.json(), err.msg()) }442+ return steal_whole(name, args, backend, '')445443 }446444 447445 // steal_whole is that fetch, from whichever remote was chosen: the whole history in, then the head's448446 // files onto the disk. A non-empty `source` means the repo came from a read-only address and stays449447 // local until this machine signs in there.450-fn steal_whole(name string, args Args, mut backend remote.Backend, source string) int {448+fn steal_whole(name string, args Args, inner remote.Backend, source string) int {451449 target := os.join_path(os.getwd(), name)452450 if os.exists(target) {453451 return refuse(args.json(), '${name} already exists here — run steal inside it instead')454452 }453+ mut backend := remote.Backend(Watched{454+ label: 'stealing ${name}'455+ whole: true456+ inner: inner457+ })455458 fetched := exchange.fetch_repo(mut backend, name) or { return refuse(args.json(), err.msg()) }456459 store := land_repo(target, name, fetched, source) or {457460 os.rmdir_all(target) or {}src/cli/progress.v+29 −3
@@ -51,10 +51,16 @@5151 // line moves with the transfer. Exchange stays unaware of it.5252 struct Watched {5353 label string54+ // whole is a steal of a whole repo, which fetches every key it lists, so a55+ // listing is the total for that kind: files, saves or events.56+ whole bool5457 mut:5558 inner remote.Backend5659 received int5760 sent int61+ listed map[string]int62+ fetched map[string]int63+ kind string5864 }5965 6066 // watched_backend is this machine's backend, counted on the line under `label`.@@ -71,10 +77,13 @@7177 7278 fn (mut w Watched) show() {7379 mut text := w.label80+ if w.kind != '' {81+ text += ' · ${w.kind} ${w.fetched[w.kind]} of ${w.listed[w.kind]}'82+ }7483 if w.sent > 0 {7584 text += ' · ${w.sent} sent'7685 }77- if w.received > 0 {86+ if w.kind == '' && w.received > 0 {7887 text += ' · ${w.received} received'7988 }8089 working(text)@@ -82,13 +91,30 @@8291 8392 fn (mut w Watched) get(key string) !remote.Object {8493 object := w.inner.get(key)!85- w.received++94+ kind := key_kind(os.dir(key))95+ if kind in w.listed {96+ w.kind = kind97+ w.fetched[kind]++98+ } else {99+ w.received++100+ }86101 w.show()87102 return object88103 }89104 90105 fn (mut w Watched) list(prefix string) ![]string {91- return w.inner.list(prefix)!106+ keys := w.inner.list(prefix)!107+ if w.whole {108+ w.listed[key_kind(prefix)] += keys.len109+ }110+ return keys111+}112+113+// key_kind names what a key folder such as `repo/objects` holds, in the words114+// the person knows: an object is a file's contents.115+fn key_kind(folder string) string {116+ kind := os.base(folder)117+ return if kind == 'objects' { 'files' } else { kind }92118 }93119 94120 fn (mut w Watched) put_if_absent(key string, body []u8) !bool {src/cli/progress_test.v+32 −0
@@ -0,0 +1,32 @@1+module cli2+3+import os4+import src.remote5+6+fn test_a_whole_steal_counts_each_kind_against_its_listing() {7+ base := os.join_path(os.vtmp_dir(), 'koh-progress-${os.getpid()}')8+ defer {9+ os.rmdir_all(base) or {}10+ }11+ mut folder := remote.new_folder(base)!12+ for key in ['r/objects/a', 'r/objects/b', 'r/saves/s1'] {13+ folder.put_if_absent(key, 'x'.bytes())!14+ }15+ mut w := Watched{16+ label: 'stealing r'17+ whole: true18+ inner: remote.Backend(folder)19+ }20+ for prefix in ['r/objects', 'r/saves'] {21+ for key in w.list(prefix)! {22+ w.get(key)!23+ }24+ }25+ assert w.listed == {26+ 'files': 227+ 'saves': 128+ }29+ assert w.fetched == w.listed30+ assert w.kind == 'saves'31+ assert w.received == 032+}v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 name: 'koh3'3- version: '3.2.27'3+ version: '3.3.0'44 description: 'One SQLite file per repo, one line of history.'55 license: 'MIT'66 }