c3643bd4833e
bind a clone to the repo it was stolen from so a rename cannot fork it (3.0.11)
- time
- 2026-09-23 22:01 UTC · 2 weeks ago
- machine
- koh2
- agent
- claude-opus-5-5
- session
- koh 3 review fixes
- parent
- 7d49e0453acd
↓ koh-c3643bd4.tar.gzatlas at this save
files changed5 files
VERSION+1 −1src/cli/cmd_steal.v+4 −0src/sync/sync.v+6 −0tests/cli_test.v+28 −0v.mod+1 −1src/cli/cmd_steal.v+4 −0
@@ -157,6 +157,10 @@157157 store.rollback()158158 return refuse(args.json(), err.msg())159159 }160+ sync.remember_repo(store, name) or {161+ store.rollback()162+ return refuse(args.json(), err.msg())163+ }160164 store.commit() or { return refuse(args.json(), err.msg()) }161165 files := history.files_of(store, fetched.head) or { return refuse(args.json(), err.msg()) }162166 snapshot.restore_files(store, target, snapshot.Ignore{}, files, []) or {src/sync/sync.v+6 −0
@@ -31,6 +31,12 @@3131 head string3232 }3333 34+// remember_repo binds a repo to its name on the remote. A clone does it at35+// once: its folder may be renamed before it ever offers.36+pub fn remember_repo(store &storage.Store, name string) ! {37+ store.set_state(repo_state_key, name)!38+}39+3440 // repo_name is the folder's name at first offer, remembered in the repo so3541 // renaming the folder later changes nothing.3642 pub fn repo_name(store &storage.Store, mut backend remote.Backend) !string {tests/cli_test.v+28 −0
@@ -431,6 +431,34 @@431431 assert run_home(dir, home, 'remote', '--json').out.contains('"configured":false')432432 }433433 434+// A clone belongs to the repo it was stolen from, whatever its folder is435+// called later: renaming it must not start a second repo on the remote.436+fn test_a_renamed_clone_keeps_offering_to_its_repo() {437+ syncroot := temp_repo('rename-clone')438+ home := temp_repo('rename-clone-home')439+ defer {440+ os.rmdir_all(syncroot) or {}441+ os.rmdir_all(home) or {}442+ }443+ remote := os.join_path(syncroot, 'remote')444+ a := os.join_path(syncroot, 'A', 'proj')445+ b_parent := os.join_path(syncroot, 'B')446+ os.mkdir_all(a) or { panic(err) }447+ os.mkdir_all(b_parent) or { panic(err) }448+ init_repo(a)449+ write(a, 'a.txt', 'from A\n')450+ assert run_home(a, home, 'remote', 'folder', remote).code == 0451+ assert run_home(a, home, 'save', 'A first').code == 0452+ assert run_home(b_parent, home, 'steal', 'proj').code == 0453+ renamed := os.join_path(b_parent, 'renamed')454+ os.mv(os.join_path(b_parent, 'proj'), renamed) or { panic(err) }455+ write(renamed, 'b.txt', 'from B\n')456+ assert run_home(renamed, home, 'save', 'B first').code == 0457+ assert !os.exists(os.join_path(remote, 'renamed'))458+ assert run_home(a, home, 'steal').code == 0459+ assert os.read_file(os.join_path(a, 'b.txt'))! == 'from B\n'460+}461+434462 fn test_offer_steal_folder_remote() {435463 syncroot := temp_repo('sync')436464 home := temp_repo('sync-home')v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 name: 'koh3'3- version: '3.0.10'3+ version: '3.0.11'44 description: 'One SQLite file per repo, one line of history.'55 license: 'MIT'66 }