123456module exchange78import compress.zlib9import src.events10import src.history11import src.remote12import src.storage13141516pub fn fetch_missing(store &storage.Store, mut backend remote.Backend, repo string, ids []string) !int {17 mut fetched := Fetched{}18 mut wanted := ids.clone()19 mut seen := map[string]bool{}20 for wanted.len > 0 {21 id := wanted.pop()22 if id == '' || id in seen || history.by_id(store, id) != none {23 continue24 }25 seen[id] = true26 object := backend.get(save_key(repo, id))!27 if !object.found {28 return error('the remote does not have save ${id} either')29 }30 record := decode_save(object.body)!31 if record.id != id {32 return error('remote save ${id} holds the record of ${record.id}')33 }34 35 36 if record.created_at < 0 || record.size_bytes < 0 {37 return error('remote save ${id} has invalid metadata')38 }39 for file in record.files {40 if !store.has_object(file.hash)! && file.hash !in fetched.repo_objects {41 blob := backend.get(object_key(repo, file.hash))!42 if !blob.found {43 return error('the remote is missing object ${file.hash} that save ${id} needs')44 }45 fetched.repo_objects[file.hash] = blob.body46 }47 }48 fetched.saves << record49 wanted << record.parent50 wanted << record.restores51 }52 53 mut ordered := []SaveRecord{}54 mut placed := map[string]bool{}55 for ordered.len < fetched.saves.len {56 before := ordered.len57 for record in fetched.saves {58 if record.id !in placed && (record.parent !in seen || record.parent in placed) {59 ordered << record60 placed[record.id] = true61 }62 }63 if ordered.len == before {64 return error('the remote history has a parent cycle')65 }66 }67 fetched.saves = ordered68 store.begin()!69 write_fetched_records(store, fetched) or {70 store.rollback()71 return err72 }73 store.end()!74 return ordered.len75}76777879pub fn resend(store &storage.Store, mut backend remote.Backend, repo string, hashes []string, save_ids []string) !int {80 81 82 if unapplied_strips(store, mut backend, repo)!.len > 0 {83 return error(strip_not_applied)84 }85 mut uploaded := Uploaded{}86 for hash in hashes {87 key := object_key(repo, hash)88 existing := backend.get(key)!89 if existing.found {90 91 92 if data := zlib.decompress(existing.body) {93 if storage.hash_hex(data) == hash {94 continue95 }96 }97 backend.delete(key)!98 }99 store.get_object(hash)!100 if backend.put_if_absent(key, store.object_blob(hash)!)! {101 uploaded.objects[hash] = true102 }103 }104 for id in save_ids {105 save := history.by_id(store, id) or { return error('save ${id} is missing here too') }106 for file in history.files_of(store, id)! {107 store.get_object(file.hash)!108 if backend.put_if_absent(object_key(repo, file.hash), store.object_blob(file.hash)!)! {109 uploaded.objects[file.hash] = true110 }111 }112 if backend.put_if_absent(save_key(repo, id), encode_save(save_record(store, save)!).bytes())! {113 uploaded.saves << id114 }115 }116 117 late := unapplied_strips(store, mut backend, repo)!118 if late.len > 0 {119 take_back(store, mut backend, repo, uploaded, late)!120 return error(strip_not_applied)121 }122 return uploaded.objects.len + uploaded.saves.len123}124125126127pub fn reapply_strips(store &storage.Store) !int {128 store.begin()!129 removed := reapply_valid_strips(store) or {130 store.rollback()131 return err132 }133 store.end()!134 return removed135}136137138139fn reapply_valid_strips(store &storage.Store) !int {140 mut removed := 0141 for event in events.strip_events(store)!.filter(events.valid_strip(it)) {142 hashes := stripped_hashes(store, event.path, event.save_id)!143 removed += remove_path(store, event.path, event.save_id)!144 store.drop_unused(hashes)!145 }146 return removed147}