╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ search_commit.zig ────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
284 progress.report(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .walking_commit, .estimated_total_items = 0 } }); 285 try ancestry.finish(progress_ctx_maybe); 286 progress.report(repo_opts, io, progress_ctx_maybe, .{ .end = .walking_commit }); 287 288 // the walk stops at the shared history, so it trusts timestamps: a 289 // commit dated later than a descendant can be passed from the base 290 // side before the tip turns out to reach it too, and its postings are 291 // then dropped. settling that would mean walking the whole history. 292 // a wrong addition only re-puts postings, so a fast-forward removes 293 // nothing, which the base tip picking up the tip's flag proves. 294 const base_node = ancestry.nodes.get(ancestry.tips[0]) orelse unreachable; 295 const fast_forward = base_node.flags & Ancestry.two != 0; 296 297 // a commit one tip alone reaches has postings to move. tag objects are 298 // peeled through, not walked. 299 var total: usize = 0; 300 var counted = ancestry.nodes.valueIterator(); 301 while (counted.next()) |node| { 302 if (node.tag_target != null) continue; 303 switch (node.flags & Ancestry.both) { 304 Ancestry.one => total += @intFromBool(!fast_forward), 305 Ancestry.two => total += 1, 306 else => {}, 307 } 308 } 309 310 var message: std.ArrayList(u8) = .empty; 311 defer message.deinit(allocator); 312 313 progress.report(repo_opts, io, progress_ctx_maybe, .{ .text = "Indexing commits" }); 314 progress.report(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = total } }); 315 var iter = ancestry.nodes.iterator(); 316 while (iter.next()) |entry| { 317 if (entry.value_ptr.tag_target != null) continue; 318 const flags = entry.value_ptr.flags & Ancestry.both; 319 if (flags == Ancestry.both or flags == 0 or (flags == Ancestry.one and fast_forward)) continue; 320 if (progress.cancelled(repo_opts, progress_ctx_maybe)) return error.ClientGone; 321 defer progress.report(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch }); 322 323 var object = try obj.Object(.xit, repo_opts).init(state, io, allocator, entry.key_ptr); 324 defer object.deinit(); 325 const key = try readPosting(repo_opts, allocator, &message, &object); 326 const removed = flags == Ancestry.one; 327 try srch.replace(DB, version, allocator, &key, if (removed) message.items else "", if (removed) "" else message.items); 328 } 329 progress.report(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch }); 330 } 331 332 // index every commit `tip` reaches into an empty `version`, as the walk 333 // reaches it. 334 // TODO: the walk holds every visited oid in memory. if that proves too costly 335 // for large repositories, consider indexing first-parent history only, which 336 // needs no visited set and matches the commits list. 337 fn indexHistory( 338 comptime repo_opts: rp.RepoOpts(.xit), 339 state: rp.Repo(.xit, repo_opts).State(.read_only), 340 io: std.Io, 341 allocator: std.mem.Allocator, 342 version: rp.Repo(.xit, repo_opts).DB.SortedMap(.read_write), 343 tip: *const [hash.hexLen(repo_opts.hash)]u8, 344 progress_ctx_maybe: ?repo_opts.ProgressCtx, 345 ) !void { 346 const DB = rp.Repo(.xit, repo_opts).DB; 347 348 var message: std.ArrayList(u8) = .empty; 349 defer message.deinit(allocator); 350 351 // a whole history has no count until it is walked, so it reports the 352 // commits indexed so far 353 progress.report(repo_opts, io, progress_ctx_maybe, .{ .text = "Indexing commits" }); 354 progress.report(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = 0 } }); 355 var iter = try obj.ObjectIterator(.xit, repo_opts).init(state, io, allocator, .{ .kind = .commit }); 356 defer iter.deinit(); 357 try iter.include(tip); 358 while (try iter.next(allocator)) |object| { 359 defer object.deinit(); 360 if (progress.cancelled(repo_opts, progress_ctx_maybe)) return error.ClientGone; 361 const key = try readPosting(repo_opts, allocator, &message, object); 362 try srch.replace(DB, version, allocator, &key, "", message.items); 363 progress.report(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch }); 364 } 365 progress.report(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch }); 366 } 367 368 // replace `message` with the indexed prefix of `object`'s message and return 369 // the doc key it indexes under 370 fn readPosting( 371 comptime repo_opts: rp.RepoOpts(.xit), 372 allocator: std.mem.Allocator, 373 message: *std.ArrayList(u8), 374 object: *obj.Object(.xit, repo_opts), 375 ) ![docKeyLen(repo_opts.hash)]u8 { 376 message.clearRetainingCapacity(); 377 object.readMessage(allocator, message, .limited(srch.max_indexed_bytes)) catch |err| switch (err) { 378 // only the indexed prefix matters 379 error.StreamTooLong => {}, 380 else => |e| return e, 381 }; 382 return docKey(repo_opts.hash, object.content.commit.metadata.timestamp, &object.oid); 383 } 384