╭ changed ─────────╮ │src/event/User.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │24 pub const conflicts_key = "conflicted-user-id->conflict"; │ │25 pub const id_to_field_to_oid_key = "user-id->field->oid"; │ │26 │ │27 +// resolves a commit's author email to its user at read time │ │28 +pub const email_to_user_id_key = "email->user-id"; │ │29 + │ │30 // a user's key in the name index │ │31 pub fn indexKey(self: Self, allocator: std.mem.Allocator) ![]const u8 {│ │32 _ = allocator; │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 92 const name_to_user_id_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "name->user-id")); │ │ 93 const name_to_user_id = try DB.HashMap(.read_write).init(name_to_user_id_cursor); │ │ 94 │ │ 95 + const email_to_user_id_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, email_to_user_id_key));│ │ 96 + const email_to_user_id = try DB.HashMap(.read_write).init(email_to_user_id_cursor); │ │ 97 + │ │ 98 const conflicts_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, conflicts_key)); │ │ 99 const conflicts = try DB.SortedMap(.read_write).init(conflicts_cursor); │ │100 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │106 │ │107 var event_to_write = event; │ │108 │ │103 - // if this event_id already maps to a user with a different name, │ │104 - // drop the stale name->id entry first │ │109 + // if this event_id already maps to a user with a different name or │ │110 + // email, drop the stale index entries first │ │111 var existing_event_maybe: ?@This() = null; │ │112 const existing_cursor_maybe = try event_id_to_user.getCursor(user_key);│ │113 if (existing_cursor_maybe) |existing_cursor| { │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │119 if (!std.mem.eql(u8, existing_event.name, event.name)) { │ │120 _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_event.name)); │ │121 } │ │122 + if (!std.mem.eql(u8, existing_event.email, event.email)) { │ │123 + _ = try email_to_user_id.remove(hash.hashInt(hash_kind, existing_event.email));│ │124 + } │ │125 │ │126 // any event settles the conflict, since resolving in the ui may │ │127 // keep our own values and so change nothing to detect │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │140 try evt.upsert(@This(), DB, hash_kind, user, event_to_write); │ │141 │ │142 try name_to_user_id.put(hash.hashInt(hash_kind, event.name), .{ .bytes = event_id }); │ │143 + try email_to_user_id.put(hash.hashInt(hash_kind, event.email), .{ .bytes = event_id });│ │144 │ │145 // first time we've seen this user: add it to the ordered set the users │ │146 // view paginates through. the key embeds the event id, so the set needs │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │152 try user_id_set.put(&order_key); │ │153 } │ │154 } else { │ │145 - // read the user's name so we can drop its name->id index entry │ │155 + // read the user so we can drop its name and email index entries │ │156 if (try event_id_to_user.getCursor(user_key)) |existing_cursor| { │ │157 const existing_user = try DB.HashMap(.read_only).init(existing_cursor); │ │158 const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_user);│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │159 _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_event.name)); │ │160 + _ = try email_to_user_id.remove(hash.hashInt(hash_kind, existing_event.email)); │ │161 │ │162 // drop it from the ordered set using its recorded creation timestamp │ │163 const user_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "user-id-set"));│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │292 try evt.consume(.xit, repo_opts, io, allocator, repo, evt.events_ref, &[_]evt.EventWithId{.{│ │293 .id = std.fmt.bytesToHex(user_id[0..evt.event_id_size].*, .lower), │ │294 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │295 + .author_email = user.email, │ │296 .event = .{ .user = updated }, │ │297 }}); │ │298 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯