╭ changed ──────────╮ │src/event/Patch.zig│ ╰───────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │347 .closed => .closed, │ │348 .merged => return error.InvalidPatchStatus, │ │349 }; │ │350 + // a closed patch may have missed source branch updates │ │351 + if (status == .open and updated.source_branch != null) updated.revision = null;│ │352 }, │ │353 .fields => |fields| { │ │354 if (!branchValid(fields.target_branch)) return error.InvalidTargetBranch; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │110 }; │ │111 } │ │112 │ │113 -// refresh tracked branches and, on the server, check open patches for merges. │ │113 +// refresh open patches' tracked branches and, on the server, their mergeability.│ │114 // this runs outside the caller's transaction. │ │115 pub fn refreshBranches( │ │116 host_kind: evt.HostKind, │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │170 var id: [evt.event_id_size]u8 = undefined; │ │171 if ((try id_pair.key_cursor.readBytes(&id)).len != id.len) return error.InvalidPatch; │ │172 const record = (try evt.Patch.readById(DB, repo_opts.hash, moment, &arena, &id)) orelse continue; │ │173 + if (record.removed or record.event.status.kind() != .open) continue; │ │174 if (conflicts) |map| if (try map.getCursor(&evt.orderKeyDesc(record.created_order, &id)) != null) continue;│ │175 if (record.event.revision) |revision| if (std.mem.eql(u8, revision.source_oid, &source)) continue; │ │176 try patches.put(allocator, id, record); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/test/event/patch.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │182 try pch.refreshBranches(.server, .xit, opts, io, allocator, &repo, null, &progress); │ │183 try std.testing.expect(std.mem.indexOf(u8, output.written(), "Updating patches: 100% (1/1)\n") != null); │ │184 │ │185 + // closed patches skip source updates and contribute no progress │ │186 + try evt.Patch.update(.server, .xit, opts, io, allocator, &repo, &id, .{ .status = .closed }, author); │ │187 + const next = try repo.commitAtRef(io, allocator, .{ .message = "revise while closed" }, null, .{ .kind = .head, .name = "feature" });│ │188 + const closed_tip = (try repo.readRef(io, evt.events_ref)) orelse return error.NotFound; │ │189 + const closed_progress_len = output.written().len; │ │190 + try pch.refreshBranches(.server, .xit, opts, io, allocator, &repo, null, &progress); │ │191 + try std.testing.expectEqualStrings(&closed_tip, &(try repo.readRef(io, evt.events_ref) orelse return error.NotFound)); │ │192 + try std.testing.expectEqual(closed_progress_len, output.written().len); │ │193 + │ │194 + // reopening captures the latest source and checks both merge styles │ │195 + try evt.Patch.update(.server, .xit, opts, io, allocator, &repo, &id, .{ .status = .open }, author); │ │196 var arena = std.heap.ArenaAllocator.init(allocator); │ │197 defer arena.deinit(); │ │198 const before = (try evt.Patch.readFromRepo(.xit, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │199 const revision = before.event.revision orelse return error.NotFound; │ │200 + try std.testing.expectEqualStrings(&next, revision.source_oid); │ │201 + { │ │202 + var moment = try repo.core.latestMoment(); │ │203 + const result = try pch.readMergeability(opts, .{ .core = &repo.core, .extra = .{ .moment = &moment } }, io, &arena, &id, before.event);│ │204 + try std.testing.expectEqualDeep(pch.Mergeability{ .source = .clean, .squash = .clean }, result); │ │205 + } │ │206 │ │207 // both merge styles use the stored revision, without fork data │ │208 try pch.merge(opts, io, allocator, path, &repo, .{ .id = id_hex, .revision = selection, .author = author, .timestamp = 100 }); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯