1ca868c479bf

apply every strip held here, not only those in the fetch, to saves arriving later and to the clash preview (3.1.1)

time
2026-09-28 12:13 UTC · 2 weeks ago
machine
Junes-MacBook-Pro.local
agent
claude-opus-5-5
session
strips reach every arriving save
parent
c9861f20776d
5 files VERSION+1 −1src/exchange/exchange.v+13 −4src/exchange/exchange_test.v+32 −0src/exchange/steal.v+2 −2v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.1.01+3.1.1

view file

src/exchange/exchange.v+13 −4
@@ -278,20 +278,21 @@278278 	for hash, blob in fetched.repo_objects {279279 		store.put_object_blob(hash, blob)!280280 	}281-	mut strips := []events.Event{}281+	mut stripped := false282282 	for event in fetched.events_out {283283 		if !events.receive(store, event)! {284284 			continue285285 		}286286 		if event.kind == events.kind_strip {287287 			remove_path(store, event.path, event.save_id)!288-			strips << event288+			stripped = true289289 		}290290 	}291+	strips := known_strips(store, fetched)!291292 	for record in fetched.saves {292293 		import_arriving(store, record, strips)!293294 	}294-	if strips.len > 0 {295+	if stripped {295296 		store.drop_unreferenced_objects()!296297 	}297298 }@@ -302,10 +303,18 @@302303 	for hash, blob in fetched.repo_objects {303304 		store.put_object_blob(hash, blob)!304305 	}305-	strips := fetched.events_out.filter(it.kind == events.kind_strip)306+	strips := known_strips(store, fetched)!306307 	for record in fetched.saves {307308 		import_arriving(store, record, strips)!308309 	}310+}311+312+// known_strips is every strip held here and every one the fetch brings: an313+// arriving save loses what any of them reaches, not only what arrived with it.314+fn known_strips(store &storage.Store, fetched Fetched) ![]events.Event {315+	mut strips := events.strip_events(store)!316+	strips << fetched.events_out.filter(it.kind == events.kind_strip)317+	return strips309318 }310319 311320 // import_arriving lands one save from the remote, unless it is already here, less every path a

view file

src/exchange/exchange_test.v+32 −0
@@ -954,6 +954,38 @@954954 	assert !os.exists(os.join_path(c.root, 'secrets.env'))955955 }956956 957+// A strip this machine already holds bans its path from saves that arrive958+// later too, not only from those a fetch brings with the strip.959+fn test_a_held_strip_reaches_saves_arriving_after_it() {960+	base := scratch('strip_held')!961+	defer {962+		os.rmdir_all(base) or {}963+	}964+	mut backend := remote.new_folder(os.join_path(base, 'remote'))!965+	mut a := open_repo(machine('strip_held_a')!)!966+	defer {967+		a.close()968+	}969+	mut b := open_repo(machine('strip_held_b')!)!970+	defer {971+		b.close()972+	}973+	put(a.root, 'secrets.env', 'api_key=1\n')!974+	make_save(a, 'leak the key')!975+	offer_now(a, mut backend)!976+	join(b, mut backend)!977+	strip_local(b, 'secrets.env', '')!978+	put(a.root, 'keep.txt', 'fine\n')!979+	later := make_save(a, 'add keep')!980+	offer_now(a, mut backend)!981+	b.begin()!982+	write_fetched(b, fetch_back_to_known(b, mut backend, 'proj')!)!983+	b.commit()!984+	paths := history.files_of(b, later.id)!.map(it.path)985+	assert 'secrets.env' !in paths986+	assert 'keep.txt' in paths987+}988+957989 // The local half commits before the remote hears anything, so a remote failure958990 // leaves nothing to find locally; the pending note is what lets a re-run959991 // finish. Objects a remote-only save still needs must survive the strip.

view file

src/exchange/steal.v+2 −2
@@ -393,10 +393,10 @@393393 		[]snapshot.FileEntry{}394394 	}395395 	head := history.head(store) or { return []Clash{} }396-	// The transaction applies the incoming strips before it replays, so the396+	// The transaction applies every known strip before it replays, so the397397 	// clashes are found on the trees as they will be then, or the answers398398 	// would be about files that are no longer there.399-	strips := fetched.events_out.filter(it.kind == events.kind_strip)399+	strips := known_strips(store, fetched)!400400 	remote_head := fetched.saves.last()401401 	mut clashes := find_clashes(after_strips(base_files, fetched.base, strips), after_strips(history.files_of(store,402402 		head.id)!, head.id, strips), after_strips(to_files(remote_head), remote_head.id, strips))

view file

v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 	name: 'koh3'3-	version: '3.1.0'3+	version: '3.1.1'44 	description: 'One SQLite file per repo, one line of history.'55 	license: 'MIT'66 }

view file