╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │266 │ │267 const total = patches.count(); │ │268 if (total > 0) { │ │276 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .text = "Updating patches" }); │ │277 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = total } }); │ │269 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .text = "Updating patches" }); │ │270 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = total } });│ │271 } │ │272 │ │273 var refreshed = false; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │274 for (patches.keys(), patches.values()) |id, record| { │ │282 - defer reportProgress(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch }); │ │275 + defer serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch });│ │276 │ │277 // a branch patch opens no fork, so its checks need no lock of their own │ │278 var checks: MergeCheckInputs(repo_opts) = .{}; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │286 }; │ │287 refreshed = true; │ │288 } │ │296 - if (total > 0) reportProgress(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch }); │ │289 + if (total > 0) serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch });│ │290 return refreshed; │ │291 } │ │292 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │544 } │ │545 │ │546 // start progress reporting after the patch count is known │ │554 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .text = "Checking mergeability" }); │ │555 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = ids.items.len } }); │ │547 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .text = "Checking mergeability" }); │ │548 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = ids.items.len } });│ │549 │ │550 // refresh each collected patch independently │ │551 for (ids.items) |id| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │552 refreshMergeability(repo_opts, io, allocator, target_repo, id); │ │560 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch }); │ │553 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch });│ │554 } │ │555 │ │556 // finish progress reporting after all patches have been visited │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │564 - reportProgress(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch }); │ │557 + serve_common.reportProgress(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch });│ │558 } │ │559 │ │560 fn refreshMergeCheck( │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/push.zig│ ╰────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │18 label: []const u8 = "Processing commits", │ │19 count: usize = 0, │ │20 total: usize = 0, │ │21 + // the last reported step, so a line is sent only when it changes │ │22 + step: usize = 0, │ │23 + │ │24 + // how many items a phase with no total advances between reports │ │25 + const report_every = 1000; │ │26 + │ │27 + // the phases a push reports: the objects unpacked from the pack it │ │28 + // received, then everything counted after it │ │29 + fn reported(kind: rp.ProgressKind) bool { │ │30 + return kind == .writing_object_from_pack or kind == .writing_patch; │ │31 + } │ │32 │ │33 pub fn run(self: *PushProgress, _: std.Io, event: rp.ProgressEvent) !void {│ │34 switch (event) { │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │35 .start => |start| { │ │25 - if (start.kind != .writing_patch) return; │ │36 + if (!reported(start.kind)) return; │ │37 + // the unpack phase sends no label of its own │ │38 + if (start.kind == .writing_object_from_pack) self.label = "Unpacking objects";│ │39 self.total = start.estimated_total_items; │ │40 self.count = 0; │ │41 + self.step = 0; │ │42 }, │ │43 .complete_one => |kind| { │ │30 - if (kind != .writing_patch) return; │ │44 + if (!reported(kind)) return; │ │45 self.count += 1; │ │46 }, │ │47 .end => |kind| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │34 - if (kind != .writing_patch) return; │ │48 + if (!reported(kind)) return; │ │49 }, │ │50 // each phase names itself before reporting its count│ │51 .text => |text| { │ ╰─────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │54 }, │ │55 else => return, │ │56 } │ │43 - if (self.total == 0) return; │ │57 + // a phase that knows no total counts instead, and stays quiet until it │ │58 + // has something to count │ │59 + if (self.total == 0 and self.count == 0) return; │ │60 + │ │61 + // every report flushes, so one is sent per percent, or per block of │ │62 + // items when there is no total to turn into one │ │63 + const step: usize = if (self.total > 0) @intCast(@as(u128, self.count) * 100 / self.total) else self.count / report_every; │ │64 + if (event == .complete_one and step == self.step) return; │ │65 + self.step = step; │ │66 + │ │67 var buffer: [128]u8 = undefined; │ │45 - const percent = @as(u128, self.count) * 100 / self.total; │ │46 - const text = try std.fmt.bufPrint(&buffer, "{s}: {d}% ({d}/{d}){s}", .{ self.label, percent, self.count, self.total, if (event == .end) "\n" else "\r" });│ │68 + const suffix = if (event == .end) "\n" else "\r"; │ │69 + const text = if (self.total > 0) │ │70 + try std.fmt.bufPrint(&buffer, "{s}: {d}% ({d}/{d}){s}", .{ self.label, step, self.count, self.total, suffix }) │ │71 + else │ │72 + try std.fmt.bufPrint(&buffer, "{s}: {d}{s}", .{ self.label, self.count, suffix }); │ │73 try self.response.progress(self.writer, text); │ │74 } │ │75 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯