cbbdbce05674

3.4.3: migrate verifies a save's files by path, so a Koh 2 manifest listing them out of order no longer fails

time
2026-10-03 18:24 UTC · 1 week ago
machine
Junes-MacBook-Pro.local
agent
claude-opus-5-5
session
fix nina migrate
parent
1b380cba71f7
4 files VERSION+1 −1src/migrate/migrate.v+8 −3src/migrate/migrate_test.v+21 −0v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.4.21+3.4.3

view file

src/migrate/migrate.v+8 −3
@@ -530,10 +530,15 @@530530 		if files.len != manifest.files.len {531531 			return error('save ${manifest.id} has ${files.len} files, its manifest has ${manifest.files.len}')532532 		}533-		for i, file in manifest.files {533+		// By path, not by position: Koh 2 did not always list a manifest's files in order.534+		mut stored := map[string]string{}535+		for file in files {536+			stored[file.path] = file.hash537+		}538+		for file in manifest.files {534539 			hash := bare_hash(file.hash)535-			if files[i].path != file.path || files[i].hash != hash {536-				return error('save ${manifest.id}: ${files[i].path} does not match its manifest')540+			if stored[file.path] or { '' } != hash {541+				return error('save ${manifest.id}: ${file.path} does not match its manifest')537542 			}538543 			store.get_object(hash) or {539544 				return error('save ${manifest.id} needs object ${hash}: ${err.msg()}')

view file

src/migrate/migrate_test.v+21 −0
@@ -388,3 +388,24 @@388388 	assert !os.exists(os.join_path(fixture.dir, draft_file))389389 	assert os.is_dir(os.join_path(fixture.dir, '.koh'))390390 }391+392+// Koh 2 did not always write a manifest's files in path order (nina has one such save).393+fn test_a_manifest_listing_its_files_out_of_order_migrates() {394+	koh2 := koh2_binary() or { return }395+	fixture := make_fixture(koh2)!396+	defer {397+		os.rmdir_all(fixture.dir) or {}398+	}399+	path := os.join_path(fixture.dir, '.koh', 'snapshots', '${fixture.first}.json')400+	mut raw := json2.decode[json2.Any](os.read_file(path)!)!.as_map()401+	mut files := raw['files']!.as_array()402+	files.reverse_in_place()403+	raw['files'] = json2.Any(files)404+	os.write_file(path, json2.encode(raw))!405+	run(prepare(fixture.dir, default_lane, [])!, quiet)!406+	store := storage.open(fixture.dir)!407+	defer {408+		store.close()409+	}410+	assert history.files_of(store, fixture.first)!.map(it.path) == ['big.txt', 'small.txt']411+}

view file

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

view file