╭ changed ───╮ │src/push.zig│ ╰────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │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 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │214 var receive_options = ctx.options; │ │215 receive_options.applied_ref_updates = ctx.updates; │ │216 receive_options.deferred_response = ctx.response; │ │191 - try xit.net_server_receive_pack.run(.xit, repo_opts, state, ctx.io, ctx.allocator, ctx.reader, ctx.writer, receive_options); │ │217 + try xit.net_server_receive_pack.run(.xit, repo_opts, state, ctx.io, ctx.allocator, ctx.reader, ctx.writer, receive_options, ctx.progress);│ │218 try writeReceivedPatches(repo_opts, state, ctx.io, ctx.allocator, ctx.response, ctx.writer); │ │219 │ │220 // a repo without an events branch has no events to consume. a │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │229 } │ │230 │ │231 // the indexes the pushed refs invalidate belong to the push too │ │206 - _ = try find.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator); │ │207 - _ = try srch_cmmt.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates.items.items); │ │232 + _ = try find.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.progress); │ │233 + _ = try srch_cmmt.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates.items.items, ctx.progress);│ │234 │ │235 try writeUndo(repo_opts, state, ctx.io, ctx.allocator, ctx.author, ctx.updates.items.items); │ │236 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯