authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-06-18 16:39:53 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-06-18 16:41:41 -07:00
logc171ea63e41ea81f9e9c973aa7cc05f45673849b
treed95093388756ba794e2b7ec3472e3c3fb38f97d8
parente1771f0b7a6ef37fc342a46f1dc8233bc5b2d1af
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

add CacheBehavior


1 files changed, 51 insertions(+), 40 deletions(-)

git.zig+51-40
...@@ -703,6 +703,8 @@ pub const Repository = struct {...@@ -703,6 +703,8 @@ pub const Repository = struct {
703 trees: std.StringArrayHashMapUnmanaged(Tree),703 trees: std.StringArrayHashMapUnmanaged(Tree),
704 tags: std.StringArrayHashMapUnmanaged(Tag),704 tags: std.StringArrayHashMapUnmanaged(Tag),
705705
706 pub const CacheBehavior = enum { no_cache, cache };
707
706 pub fn init(gitdir: nfs.Dir, gpa: std.mem.Allocator) Repository {708 pub fn init(gitdir: nfs.Dir, gpa: std.mem.Allocator) Repository {
707 return .{709 return .{
708 .gitdir = gitdir,710 .gitdir = gitdir,
...@@ -732,7 +734,7 @@ pub const Repository = struct {...@@ -732,7 +734,7 @@ pub const Repository = struct {
732 r.tags.deinit(r.gpa);734 r.tags.deinit(r.gpa);
733 }735 }
734736
735 pub fn getObject(r: *Repository, arena: std.mem.Allocator, oid: Id) anyerror!?GitObject {737 pub fn getObject(r: *Repository, arena: std.mem.Allocator, oid: Id, cache_behavior: CacheBehavior) anyerror!?GitObject {
736 const t = tracer.trace(@src(), " {s}", .{oid});738 const t = tracer.trace(@src(), " {s}", .{oid});
737 defer t.end();739 defer t.end();
738740
...@@ -764,7 +766,7 @@ pub const Repository = struct {...@@ -764,7 +766,7 @@ pub const Repository = struct {
764 const content = try list.toOwnedSlice(r.gpa);766 const content = try list.toOwnedSlice(r.gpa);
765 std.debug.assert(content.len == content_len);767 std.debug.assert(content.len == content_len);
766 const obj: GitObject = .{ .type = _type, .content = content };768 const obj: GitObject = .{ .type = _type, .content = content };
767 try r.unpacked_loose_objects.put(r.gpa, oid, obj);769 if (cache_behavior == .cache) try r.unpacked_loose_objects.put(r.gpa, oid, obj);
768 return obj;770 return obj;
769 }771 }
770772
...@@ -795,7 +797,7 @@ pub const Repository = struct {...@@ -795,7 +797,7 @@ pub const Repository = struct {
795 }797 }
796 const pack_index, const pack_offset = try r.getObjectPackIndex(oid) orelse return null;798 const pack_index, const pack_offset = try r.getObjectPackIndex(oid) orelse return null;
797 // parse .pack799 // parse .pack
798 return try r.getPackedObject(arena, oid, pack_index, pack_offset);800 return try r.getPackedObject(arena, oid, pack_index, pack_offset, cache_behavior);
799 }801 }
800802
801 fn getObjectPackIndex(r: *Repository, oid: Id) !?[2]usize {803 fn getObjectPackIndex(r: *Repository, oid: Id) !?[2]usize {
...@@ -857,7 +859,7 @@ pub const Repository = struct {...@@ -857,7 +859,7 @@ pub const Repository = struct {
857 return null;859 return null;
858 }860 }
859861
860 fn getPackedObject(r: *Repository, arena: std.mem.Allocator, maybe_oid: ?Id, pack_index: usize, pack_offset: usize) !GitObject {862 fn getPackedObject(r: *Repository, arena: std.mem.Allocator, maybe_oid: ?Id, pack_index: usize, pack_offset: usize, cache_behavior: CacheBehavior) !GitObject {
861 const t = tracer.trace(@src(), " {?s} {d} {d} {s}", .{ maybe_oid, pack_index, pack_offset, r.pack_content.keys()[pack_index] });863 const t = tracer.trace(@src(), " {?s} {d} {d} {s}", .{ maybe_oid, pack_index, pack_offset, r.pack_content.keys()[pack_index] });
862 defer t.end();864 defer t.end();
863865
...@@ -901,7 +903,7 @@ pub const Repository = struct {...@@ -901,7 +903,7 @@ pub const Repository = struct {
901 const _type = std.meta.stringToEnum(RefType, @tagName(ty)).?;903 const _type = std.meta.stringToEnum(RefType, @tagName(ty)).?;
902 const content = try list.toOwnedSlice(r.gpa);904 const content = try list.toOwnedSlice(r.gpa);
903 const obj: GitObject = .{ .type = _type, .content = content };905 const obj: GitObject = .{ .type = _type, .content = content };
904 try r.unpacked_objects.put(r.gpa, key, obj);906 if (cache_behavior == .cache) try r.unpacked_objects.put(r.gpa, key, obj);
905 return obj;907 return obj;
906 },908 },
907 .ofs_delta => {909 .ofs_delta => {
...@@ -913,13 +915,15 @@ pub const Repository = struct {...@@ -913,13 +915,15 @@ pub const Repository = struct {
913 offset += 1;915 offset += 1;
914 }916 }
915 const base_pack_offset = pack_offset - offset;917 const base_pack_offset = pack_offset - offset;
916 const base_obj = try r.getPackedObject(arena, null, pack_index, base_pack_offset);918 const base_obj = try r.getPackedObject(arena, null, pack_index, base_pack_offset, cache_behavior);
917 return r.getDeltadObject(maybe_oid, key, &packedobj_fbs, size, base_obj);919 defer if (cache_behavior == .no_cache) r.gpa.free(base_obj.content);
920 return r.getDeltadObject(maybe_oid, key, &packedobj_fbs, size, base_obj, cache_behavior);
918 },921 },
919 .ref_delta => {922 .ref_delta => {
920 const base_oid = extras.to_hex(packedobj_fbs.takeSlice(20)[0..20].*);923 const base_oid = extras.to_hex(packedobj_fbs.takeSlice(20)[0..20].*);
921 const base_obj = (try r.getObject(arena, &base_oid)).?;924 const base_obj = (try r.getObject(arena, &base_oid, cache_behavior)).?;
922 return r.getDeltadObject(maybe_oid, key, &packedobj_fbs, size, base_obj);925 defer if (cache_behavior == .no_cache) r.gpa.free(base_obj.content);
926 return r.getDeltadObject(maybe_oid, key, &packedobj_fbs, size, base_obj, cache_behavior);
923 },927 },
924 }928 }
925 comptime unreachable;929 comptime unreachable;
...@@ -931,7 +935,7 @@ pub const Repository = struct {...@@ -931,7 +935,7 @@ pub const Repository = struct {
931 }935 }
932 }936 }
933937
934 fn getDeltadObject(r: *Repository, maybe_oid: ?Id, key: u64, packedobj_fbs: *nio.FixedBufferStream([]const u8), size: usize, base_obj: GitObject) !GitObject {938 fn getDeltadObject(r: *Repository, maybe_oid: ?Id, key: u64, packedobj_fbs: *nio.FixedBufferStream([]const u8), size: usize, base_obj: GitObject, cache_behavior: CacheBehavior) !GitObject {
935 const compressed_content = packedobj_fbs.rest();939 const compressed_content = packedobj_fbs.rest();
936 var list: std.ArrayListUnmanaged(u8) = .empty;940 var list: std.ArrayListUnmanaged(u8) = .empty;
937 defer list.deinit(r.gpa);941 defer list.deinit(r.gpa);
...@@ -999,20 +1003,22 @@ pub const Repository = struct {...@@ -999,20 +1003,22 @@ pub const Repository = struct {
999 const content = try list2.toOwnedSlice(r.gpa);1003 const content = try list2.toOwnedSlice(r.gpa);
1000 const obj: GitObject = .{ .type = _type, .content = content };1004 const obj: GitObject = .{ .type = _type, .content = content };
1001 _ = maybe_oid;1005 _ = maybe_oid;
1002 try r.unpacked_objects.put(r.gpa, key, obj);1006 if (cache_behavior == .cache) try r.unpacked_objects.put(r.gpa, key, obj);
1003 return obj;1007 return obj;
1004 }1008 }
10051009
1006 pub fn getObjectA(r: *Repository, arena: std.mem.Allocator, oid: Id) !GitObject {1010 pub fn getObjectA(r: *Repository, arena: std.mem.Allocator, oid: Id, cache_behavior: CacheBehavior) !GitObject {
1007 return (try r.getObject(arena, oid)).?;1011 return (try r.getObject(arena, oid, cache_behavior)).?;
1008 }1012 }
10091013
1010 pub fn getObjectC(r: *Repository, arena: std.mem.Allocator, oid: Id) ![]const u8 {1014 pub fn getObjectC(r: *Repository, arena: std.mem.Allocator, oid: Id, cache_behavior: CacheBehavior) ![]const u8 {
1011 return (try r.getObjectA(arena, oid)).content;1015 return (try r.getObjectA(arena, oid, cache_behavior)).content;
1012 }1016 }
10131017
1014 pub fn getObjectS(r: *Repository, arena: std.mem.Allocator, oid: Id) !usize {1018 pub fn getObjectS(r: *Repository, arena: std.mem.Allocator, oid: Id, cache_behavior: CacheBehavior) !usize {
1015 return (try r.getObjectC(arena, oid)).len;1019 const content = try r.getObjectC(arena, oid, cache_behavior);
1020 defer if (cache_behavior == .no_cache) r.gpa.free(content);
1021 return content.len;
1016 }1022 }
10171023
1018 const GitObject = struct {1024 const GitObject = struct {
...@@ -1020,17 +1026,18 @@ pub const Repository = struct {...@@ -1020,17 +1026,18 @@ pub const Repository = struct {
1020 content: []const u8,1026 content: []const u8,
1021 };1027 };
10221028
1023 pub fn getBlob(r: *Repository, arena: std.mem.Allocator, id: BlobId) !?[]const u8 {1029 pub fn getBlob(r: *Repository, arena: std.mem.Allocator, id: BlobId, cache_behavior: CacheBehavior) !?[]const u8 {
1024 if (try r.getObject(arena, id.id)) |obj| {1030 if (try r.getObject(arena, id.id, cache_behavior)) |obj| {
1025 if (obj.type == .blob) {1031 if (obj.type == .blob) {
1026 return obj.content;1032 return obj.content;
1027 }1033 }
1034 if (cache_behavior == .no_cache) r.gpa.free(obj.content);
1028 }1035 }
1029 return null;1036 return null;
1030 }1037 }
10311038
1032 pub fn getBlobA(r: *Repository, arena: std.mem.Allocator, id: Id) ![]const u8 {1039 pub fn getBlobA(r: *Repository, arena: std.mem.Allocator, id: Id, cache_behavior: CacheBehavior) ![]const u8 {
1033 return (try r.getBlob(arena, .{ .id = id })).?;1040 return (try r.getBlob(arena, .{ .id = id }, cache_behavior)).?;
1034 }1041 }
10351042
1036 pub fn getCommit(r: *Repository, arena: std.mem.Allocator, id: CommitId) !?struct { CommitId, CommitIdx } {1043 pub fn getCommit(r: *Repository, arena: std.mem.Allocator, id: CommitId) !?struct { CommitId, CommitIdx } {
...@@ -1040,7 +1047,7 @@ pub const Repository = struct {...@@ -1040,7 +1047,7 @@ pub const Repository = struct {
1040 if (r.commits.getIndex(id.id)) |idx| {1047 if (r.commits.getIndex(id.id)) |idx| {
1041 return .{ id, @enumFromInt(idx) };1048 return .{ id, @enumFromInt(idx) };
1042 }1049 }
1043 if (try r.getObject(arena, id.id)) |obj| {1050 if (try r.getObject(arena, id.id, .cache)) |obj| {
1044 if (obj.type == .commit) {1051 if (obj.type == .commit) {
1045 const commit = try parseCommit(arena, obj.content);1052 const commit = try parseCommit(arena, obj.content);
1046 try r.commits.put(r.gpa, id.id, commit);1053 try r.commits.put(r.gpa, id.id, commit);
...@@ -1055,15 +1062,16 @@ pub const Repository = struct {...@@ -1055,15 +1062,16 @@ pub const Repository = struct {
1055 return (try r.getCommit(arena, .{ .id = id })).?.@"1";1062 return (try r.getCommit(arena, .{ .id = id })).?.@"1";
1056 }1063 }
10571064
1058 pub fn getTree(r: *Repository, arena: std.mem.Allocator, id: TreeId) !?struct { TreeId, Tree } {1065 pub fn getTree(r: *Repository, arena: std.mem.Allocator, id: TreeId, cache_behavior: CacheBehavior) !?struct { TreeId, Tree } {
1059 const t = tracer.trace(@src(), " {s}", .{id.id});1066 const t = tracer.trace(@src(), " {s}", .{id.id});
1060 defer t.end();1067 defer t.end();
10611068
1062 if (r.trees.getPtr(id.id)) |val| {1069 if (r.trees.getPtr(id.id)) |val| {
1063 return .{ id, val.* };1070 return .{ id, val.* };
1064 }1071 }
1065 if (try r.getObject(arena, id.id)) |obj| {1072 if (try r.getObject(arena, id.id, cache_behavior)) |obj| {
1066 if (obj.type == .tree) {1073 if (obj.type == .tree) {
1074 errdefer if (cache_behavior == .no_cache) r.gpa.free(obj.content);
1067 var children: std.ArrayList(Tree.Object) = .empty;1075 var children: std.ArrayList(Tree.Object) = .empty;
1068 errdefer children.deinit(r.gpa);1076 errdefer children.deinit(r.gpa);
1069 try children.ensureUnusedCapacity(r.gpa, 33);1077 try children.ensureUnusedCapacity(r.gpa, 33);
...@@ -1103,36 +1111,39 @@ pub const Repository = struct {...@@ -1103,36 +1111,39 @@ pub const Repository = struct {
1103 .none => unreachable,1111 .none => unreachable,
1104 };1112 };
1105 const tree: Tree = .{ .children = children_slice };1113 const tree: Tree = .{ .children = children_slice };
1106 try r.trees.put(r.gpa, id.id, tree);1114 if (cache_behavior == .cache) try r.trees.put(r.gpa, id.id, tree);
1107 return .{ id, tree };1115 return .{ id, tree };
1108 }1116 }
1117 if (cache_behavior == .no_cache) r.gpa.free(obj.content);
1109 }1118 }
1110 return null;1119 return null;
1111 }1120 }
11121121
1113 pub fn getTreeA(r: *Repository, arena: std.mem.Allocator, id: Id) !Tree {1122 pub fn getTreeA(r: *Repository, arena: std.mem.Allocator, id: Id, cache_behavior: CacheBehavior) !Tree {
1114 return (try r.getTree(arena, .{ .id = id })).?.@"1";1123 return (try r.getTree(arena, .{ .id = id }, cache_behavior)).?.@"1";
1115 }1124 }
11161125
1117 pub fn getTag(r: *Repository, arena: std.mem.Allocator, id: TagId) !?struct { TagId, Tag } {1126 pub fn getTag(r: *Repository, arena: std.mem.Allocator, id: TagId, cache_behavior: CacheBehavior) !?struct { TagId, Tag } {
1118 const t = tracer.trace(@src(), " {s}", .{id.id});1127 const t = tracer.trace(@src(), " {s}", .{id.id});
1119 defer t.end();1128 defer t.end();
11201129
1121 if (r.tags.getPtr(id.id)) |val| {1130 if (r.tags.getPtr(id.id)) |val| {
1122 return .{ id, val.* };1131 return .{ id, val.* };
1123 }1132 }
1124 if (try r.getObject(arena, id.id)) |obj| {1133 if (try r.getObject(arena, id.id, cache_behavior)) |obj| {
1125 if (obj.type == .tag) {1134 if (obj.type == .tag) {
1135 errdefer if (cache_behavior == .no_cache) r.gpa.free(obj.content);
1126 const tag = try parseTag(obj.content);1136 const tag = try parseTag(obj.content);
1127 try r.tags.put(r.gpa, id.id, tag);1137 if (cache_behavior == .cache) try r.tags.put(r.gpa, id.id, tag);
1128 return .{ id, tag };1138 return .{ id, tag };
1129 }1139 }
1140 if (cache_behavior == .no_cache) r.gpa.free(obj.content);
1130 }1141 }
1131 return null;1142 return null;
1132 }1143 }
11331144
1134 pub fn getTagA(r: *Repository, arena: std.mem.Allocator, id: Id) !Tag {1145 pub fn getTagA(r: *Repository, arena: std.mem.Allocator, id: Id, cache_behavior: CacheBehavior) !Tag {
1135 return (try r.getTag(arena, .{ .id = id })).?.@"1";1146 return (try r.getTag(arena, .{ .id = id }, cache_behavior)).?.@"1";
1136 }1147 }
11371148
1138 pub fn getHeads(r: *Repository, arena: std.mem.Allocator) ![]Ref {1149 pub fn getHeads(r: *Repository, arena: std.mem.Allocator) ![]Ref {
...@@ -1214,7 +1225,7 @@ pub const Repository = struct {...@@ -1214,7 +1225,7 @@ pub const Repository = struct {
1214 const base_idx = try r.getCommitA(arena, base_oid.id);1225 const base_idx = try r.getCommitA(arena, base_oid.id);
1215 const base = base_idx.reify(r);1226 const base = base_idx.reify(r);
1216 const base_tree_id = (try traverseTo(r, arena, base.tree, dir_path)).?;1227 const base_tree_id = (try traverseTo(r, arena, base.tree, dir_path)).?;
1217 const base_tree = try r.getTreeA(arena, base_tree_id.id);1228 const base_tree = try r.getTreeA(arena, base_tree_id.id, .cache);
1218 const total = base_tree.children.len;1229 const total = base_tree.children.len;
12191230
1220 var found: usize = 0;1231 var found: usize = 0;
...@@ -1252,7 +1263,7 @@ pub const Repository = struct {...@@ -1252,7 +1263,7 @@ pub const Repository = struct {
1252 };1263 };
1253 if (new_tree_id.eql(tree_id)) continue;1264 if (new_tree_id.eql(tree_id)) continue;
1254 tree_id = new_tree_id;1265 tree_id = new_tree_id;
1255 const tree = try r.getTreeA(arena, tree_id.id);1266 const tree = try r.getTreeA(arena, tree_id.id, .cache);
1256 var i: usize = 0;1267 var i: usize = 0;
1257 while (findFirstUnset(set, i)) |j| : (i += 1) {1268 while (findFirstUnset(set, i)) |j| : (i += 1) {
1258 i = j;1269 i = j;
...@@ -1295,7 +1306,7 @@ pub const Repository = struct {...@@ -1295,7 +1306,7 @@ pub const Repository = struct {
1295 try S.item(e, w, .none, mode, &@splat('0'), id, .A, p, name);1306 try S.item(e, w, .none, mode, &@splat('0'), id, .A, p, name);
1296 }1307 }
1297 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {1308 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {
1298 const tree = try e.getTreeA(e.gpa, t);1309 const tree = try e.getTreeA(e.gpa, t, .cache);
1299 for (tree.children[o..]) |obj| {1310 for (tree.children[o..]) |obj| {
1300 if (obj.mode.type == .directory) {1311 if (obj.mode.type == .directory) {
1301 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);1312 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
...@@ -1316,7 +1327,7 @@ pub const Repository = struct {...@@ -1316,7 +1327,7 @@ pub const Repository = struct {
1316 try S.item(e, w, mode, .none, id, &@splat('0'), .D, p, name);1327 try S.item(e, w, mode, .none, id, &@splat('0'), .D, p, name);
1317 }1328 }
1318 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {1329 fn dir(e: *Repository, w: anytype, t: Id, p: ?*const PathListNode, o: usize) !void {
1319 const tree = try e.getTreeA(e.gpa, t);1330 const tree = try e.getTreeA(e.gpa, t, .cache);
1320 for (tree.children[o..]) |obj| {1331 for (tree.children[o..]) |obj| {
1321 if (obj.mode.type == .directory) {1332 if (obj.mode.type == .directory) {
1322 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);1333 try dir(e, w, obj.id.tree.id, &.{ .prev = p, .data = obj.name }, 0);
...@@ -1335,11 +1346,11 @@ pub const Repository = struct {...@@ -1335,11 +1346,11 @@ pub const Repository = struct {
1335 const M = struct {1346 const M = struct {
1336 fn dir(e: *Repository, w: anytype, b_t: Id, a_t: Id, p: ?*const PathListNode) !void {1347 fn dir(e: *Repository, w: anytype, b_t: Id, a_t: Id, p: ?*const PathListNode) !void {
1337 var before_i: usize = 0;1348 var before_i: usize = 0;
1338 const before_tree = try e.getTreeA(e.gpa, b_t);1349 const before_tree = try e.getTreeA(e.gpa, b_t, .cache);
1339 const before_children = before_tree.children;1350 const before_children = before_tree.children;
13401351
1341 var after_i: usize = 0;1352 var after_i: usize = 0;
1342 const after_tree = try e.getTreeA(e.gpa, a_t);1353 const after_tree = try e.getTreeA(e.gpa, a_t, .cache);
1343 const after_children = after_tree.children;1354 const after_children = after_tree.children;
13441355
1345 while (true) {1356 while (true) {
...@@ -1610,7 +1621,7 @@ fn traverseTo(r: *Repository, arena: std.mem.Allocator, treestart_id: TreeId, di...@@ -1610,7 +1621,7 @@ fn traverseTo(r: *Repository, arena: std.mem.Allocator, treestart_id: TreeId, di
1610 if (dir_path.len == 0) return id;1621 if (dir_path.len == 0) return id;
1611 var iter = std.mem.splitScalar(u8, dir_path, '/');1622 var iter = std.mem.splitScalar(u8, dir_path, '/');
1612 while (iter.next()) |segment| {1623 while (iter.next()) |segment| {
1613 const tree = try r.getTreeA(arena, id.id);1624 const tree = try r.getTreeA(arena, id.id, .cache);
1614 const o = tree.get(segment) orelse return null;1625 const o = tree.get(segment) orelse return null;
1615 if (o.id != .tree) return null;1626 if (o.id != .tree) return null;
1616 id = o.id.tree;1627 id = o.id.tree;
...@@ -1869,7 +1880,7 @@ pub const Tree = struct {...@@ -1869,7 +1880,7 @@ pub const Tree = struct {
1869 self.name_buffer.appendSliceAssumeCapacity(base.name);1880 self.name_buffer.appendSliceAssumeCapacity(base.name);
1870 self.name_buffer.appendAssumeCapacity(0);1881 self.name_buffer.appendAssumeCapacity(0);
1871 if (base.id == .tree) {1882 if (base.id == .tree) {
1872 const new_tree = try self.repo.getTreeA(arena, base.id.tree.id);1883 const new_tree = try self.repo.getTreeA(arena, base.id.tree.id, .cache);
1873 {1884 {
1874 // errdefer new_dir.close();1885 // errdefer new_dir.close();
1875 try self.stack.append(gpa, .{1886 try self.stack.append(gpa, .{