| ... | @@ -721,11 +721,13 @@ pub const Repository = struct { | ... | @@ -721,11 +721,13 @@ pub const Repository = struct { |
| 721 | gpa: std.mem.Allocator, | 721 | gpa: std.mem.Allocator, |
| 722 | unpacked_loose_objects: std.StringArrayHashMapUnmanaged(GitObject), | 722 | unpacked_loose_objects: std.StringArrayHashMapUnmanaged(GitObject), |
| 723 | unpacked_objects: std.AutoArrayHashMapUnmanaged(u64, GitObject), | 723 | unpacked_objects: std.AutoArrayHashMapUnmanaged(u64, GitObject), |
| | 724 | unpacked_objects_window: usize, |
| 724 | idx_content: std.StringArrayHashMapUnmanaged([]const u8), | 725 | idx_content: std.StringArrayHashMapUnmanaged([]const u8), |
| 725 | pack_content: std.StringArrayHashMapUnmanaged([]const u8), | 726 | pack_content: std.StringArrayHashMapUnmanaged([]const u8), |
| 726 | commits: std.StringArrayHashMapUnmanaged(*Commit), | 727 | commits: std.StringArrayHashMapUnmanaged(*Commit), |
| 727 | trees: std.StringArrayHashMapUnmanaged(*Tree), | 728 | trees: std.StringArrayHashMapUnmanaged(*Tree), |
| 728 | tags: std.StringArrayHashMapUnmanaged(*Tag), | 729 | tags: std.StringArrayHashMapUnmanaged(*Tag), |
| | 730 | mailmap_content: []const u8, |
| 729 | mailmap: std.hash_map.StringHashMapUnmanaged([]const u8), | 731 | mailmap: std.hash_map.StringHashMapUnmanaged([]const u8), |
| 730 | mailmap_names: std.hash_map.StringHashMapUnmanaged([]const u8), | 732 | mailmap_names: std.hash_map.StringHashMapUnmanaged([]const u8), |
| 731 | | 733 | |
| ... | @@ -737,11 +739,13 @@ pub const Repository = struct { | ... | @@ -737,11 +739,13 @@ pub const Repository = struct { |
| 737 | .gpa = gpa, | 739 | .gpa = gpa, |
| 738 | .unpacked_loose_objects = .empty, | 740 | .unpacked_loose_objects = .empty, |
| 739 | .unpacked_objects = .empty, | 741 | .unpacked_objects = .empty, |
| | 742 | .unpacked_objects_window = 0, |
| 740 | .idx_content = .empty, | 743 | .idx_content = .empty, |
| 741 | .pack_content = .empty, | 744 | .pack_content = .empty, |
| 742 | .commits = .empty, | 745 | .commits = .empty, |
| 743 | .trees = .empty, | 746 | .trees = .empty, |
| 744 | .tags = .empty, | 747 | .tags = .empty, |
| | 748 | .mailmap_content = "", |
| 745 | .mailmap = .empty, | 749 | .mailmap = .empty, |
| 746 | .mailmap_names = .empty, | 750 | .mailmap_names = .empty, |
| 747 | }; | 751 | }; |
| ... | @@ -795,6 +799,7 @@ pub const Repository = struct { | ... | @@ -795,6 +799,7 @@ pub const Repository = struct { |
| 795 | } | 799 | } |
| 796 | } | 800 | } |
| 797 | } | 801 | } |
| | 802 | r.mailmap_content = mailmap_content; |
| 798 | r.mailmap = mailmap; | 803 | r.mailmap = mailmap; |
| 799 | r.mailmap_names = mailmap_names; | 804 | r.mailmap_names = mailmap_names; |
| 800 | } | 805 | } |
| ... | @@ -966,7 +971,19 @@ pub const Repository = struct { | ... | @@ -966,7 +971,19 @@ pub const Repository = struct { |
| 966 | const _type = std.meta.stringToEnum(RefType, @tagName(ty)).?; | 971 | const _type = std.meta.stringToEnum(RefType, @tagName(ty)).?; |
| 967 | const content = try list.toOwnedSlice(); | 972 | const content = try list.toOwnedSlice(); |
| 968 | const obj: GitObject = .{ .type = _type, .content = content }; | 973 | const obj: GitObject = .{ .type = _type, .content = content }; |
| 969 | if (cache_behavior == .cache) try r.unpacked_objects.put(r.gpa, key, obj); | 974 | const window_max = 1024 * 128; |
| | 975 | if (cache_behavior == .cache) { |
| | 976 | if (r.unpacked_objects.count() < window_max) { |
| | 977 | try r.unpacked_objects.put(r.gpa, key, obj); |
| | 978 | } else { |
| | 979 | const item = &r.unpacked_objects.values()[r.unpacked_objects_window]; |
| | 980 | r.gpa.free(item.content); |
| | 981 | r.unpacked_objects.keys()[r.unpacked_objects_window] = key; |
| | 982 | item.* = obj; |
| | 983 | } |
| | 984 | r.unpacked_objects_window += 1; |
| | 985 | r.unpacked_objects_window %= window_max; |
| | 986 | } |
| 970 | return obj; | 987 | return obj; |
| 971 | }, | 988 | }, |
| 972 | .ofs_delta => { | 989 | .ofs_delta => { |
| ... | @@ -1064,7 +1081,19 @@ pub const Repository = struct { | ... | @@ -1064,7 +1081,19 @@ pub const Repository = struct { |
| 1064 | const _type = base_obj.type; | 1081 | const _type = base_obj.type; |
| 1065 | const content = try list2.toOwnedSlice(r.gpa); | 1082 | const content = try list2.toOwnedSlice(r.gpa); |
| 1066 | const obj: GitObject = .{ .type = _type, .content = content }; | 1083 | const obj: GitObject = .{ .type = _type, .content = content }; |
| 1067 | if (cache_behavior == .cache) try r.unpacked_objects.put(r.gpa, key, obj); | 1084 | const window_max = 1024 * 128; |
| | 1085 | if (cache_behavior == .cache) { |
| | 1086 | if (r.unpacked_objects.count() < window_max) { |
| | 1087 | try r.unpacked_objects.put(r.gpa, key, obj); |
| | 1088 | } else { |
| | 1089 | const item = &r.unpacked_objects.values()[r.unpacked_objects_window]; |
| | 1090 | r.gpa.free(item.content); |
| | 1091 | r.unpacked_objects.keys()[r.unpacked_objects_window] = key; |
| | 1092 | item.* = obj; |
| | 1093 | } |
| | 1094 | r.unpacked_objects_window += 1; |
| | 1095 | r.unpacked_objects_window %= window_max; |
| | 1096 | } |
| 1068 | return obj; | 1097 | return obj; |
| 1069 | } | 1098 | } |
| 1070 | | 1099 | |
| ... | @@ -1090,9 +1119,9 @@ pub const Repository = struct { | ... | @@ -1090,9 +1119,9 @@ pub const Repository = struct { |
| 1090 | pub fn getBlob(r: *Repository, id: BlobId, cache_behavior: CacheBehavior) !?[]const u8 { | 1119 | pub fn getBlob(r: *Repository, id: BlobId, cache_behavior: CacheBehavior) !?[]const u8 { |
| 1091 | if (try r.getObject(id.id, cache_behavior)) |obj| { | 1120 | if (try r.getObject(id.id, cache_behavior)) |obj| { |
| 1092 | if (obj.type == .blob) { | 1121 | if (obj.type == .blob) { |
| | 1122 | if (cache_behavior == .cache) return try r.gpa.dupe(u8, obj.content); |
| 1093 | return obj.content; | 1123 | return obj.content; |
| 1094 | } | 1124 | } |
| 1095 | if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1096 | } | 1125 | } |
| 1097 | return null; | 1126 | return null; |
| 1098 | } | 1127 | } |
| ... | @@ -1108,18 +1137,16 @@ pub const Repository = struct { | ... | @@ -1108,18 +1137,16 @@ pub const Repository = struct { |
| 1108 | if (cache_behavior == .cache) if (r.commits.get(id.id)) |val| { | 1137 | if (cache_behavior == .cache) if (r.commits.get(id.id)) |val| { |
| 1109 | return .{ id, val }; | 1138 | return .{ id, val }; |
| 1110 | }; | 1139 | }; |
| 1111 | if (try r.getObject(id.id, cache_behavior)) |obj| { | 1140 | if (try r.getObject(id.id, .cache)) |obj| { |
| 1112 | if (obj.type == .commit) { | 1141 | if (obj.type == .commit) { |
| 1113 | errdefer if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1114 | const raw = try r.gpa.dupe(u8, obj.content); | 1142 | const raw = try r.gpa.dupe(u8, obj.content); |
| 1115 | errdefer r.gpa.free(raw); | 1143 | errdefer r.gpa.free(raw); |
| 1116 | const commit = try r.gpa.create(Commit); | 1144 | const commit = try r.gpa.create(Commit); |
| 1117 | errdefer r.gpa.destroy(commit); | 1145 | errdefer r.gpa.destroy(commit); |
| 1118 | commit.* = try parseCommit(r.gpa, obj.content, &r.mailmap, &r.mailmap_names); | 1146 | commit.* = try parseCommit(r.gpa, raw, &r.mailmap, &r.mailmap_names); |
| 1119 | try r.commits.put(r.gpa, id.id, commit); | 1147 | if (cache_behavior == .cache) try r.commits.put(r.gpa, id.id, commit); |
| 1120 | return .{ id, commit }; | 1148 | return .{ id, commit }; |
| 1121 | } | 1149 | } |
| 1122 | if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1123 | } | 1150 | } |
| 1124 | return null; | 1151 | return null; |
| 1125 | } | 1152 | } |
| ... | @@ -1135,25 +1162,24 @@ pub const Repository = struct { | ... | @@ -1135,25 +1162,24 @@ pub const Repository = struct { |
| 1135 | if (cache_behavior == .cache) if (r.trees.get(id.id)) |val| { | 1162 | if (cache_behavior == .cache) if (r.trees.get(id.id)) |val| { |
| 1136 | return .{ id, val }; | 1163 | return .{ id, val }; |
| 1137 | }; | 1164 | }; |
| 1138 | if (try r.getObject(id.id, cache_behavior)) |obj| { | 1165 | if (try r.getObject(id.id, .cache)) |obj| { |
| 1139 | if (obj.type == .tree) { | 1166 | if (obj.type == .tree) { |
| 1140 | errdefer if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1141 | const raw = try r.gpa.dupe(u8, obj.content); | 1167 | const raw = try r.gpa.dupe(u8, obj.content); |
| 1142 | errdefer r.gpa.free(raw); | 1168 | errdefer r.gpa.free(raw); |
| 1143 | var children: std.ArrayList(Tree.Object) = .empty; | 1169 | var children: std.ArrayList(Tree.Object) = .empty; |
| 1144 | errdefer children.deinit(r.gpa); | 1170 | errdefer children.deinit(r.gpa); |
| 1145 | try children.ensureUnusedCapacity(r.gpa, 33); | 1171 | try children.ensureUnusedCapacity(r.gpa, 33); |
| 1146 | var i: usize = 0; | 1172 | var i: usize = 0; |
| 1147 | while (i < obj.content.len) { | 1173 | while (i < raw.len) { |
| 1148 | const mode_end = std.mem.indexOfScalar(u8, obj.content[i..], ' ').?; | 1174 | const mode_end = std.mem.indexOfScalar(u8, raw[i..], ' ').?; |
| 1149 | const mode = obj.content[i..][0..mode_end]; | 1175 | const mode = raw[i..][0..mode_end]; |
| 1150 | i += mode_end + 1; | 1176 | i += mode_end + 1; |
| 1151 | | 1177 | |
| 1152 | const name_end = std.mem.indexOfScalar(u8, obj.content[i..], 0).?; | 1178 | const name_end = std.mem.indexOfScalar(u8, raw[i..], 0).?; |
| 1153 | const name = obj.content[i..][0..name_end :0]; | 1179 | const name = raw[i..][0..name_end :0]; |
| 1154 | i += name_end + 1; | 1180 | i += name_end + 1; |
| 1155 | | 1181 | |
| 1156 | const oid_raw = obj.content[i..][0..20].*; | 1182 | const oid_raw = raw[i..][0..20].*; |
| 1157 | const oid_hex = extras.to_hex(oid_raw); | 1183 | const oid_hex = extras.to_hex(oid_raw); |
| 1158 | i += 20; | 1184 | i += 20; |
| 1159 | | 1185 | |
| ... | @@ -1180,11 +1206,10 @@ pub const Repository = struct { | ... | @@ -1180,11 +1206,10 @@ pub const Repository = struct { |
| 1180 | }; | 1206 | }; |
| 1181 | const tree = try r.gpa.create(Tree); | 1207 | const tree = try r.gpa.create(Tree); |
| 1182 | errdefer r.gpa.destroy(tree); | 1208 | errdefer r.gpa.destroy(tree); |
| 1183 | tree.* = .{ .raw = obj.content, .children = children_slice }; | 1209 | tree.* = .{ .raw = raw, .children = children_slice }; |
| 1184 | if (cache_behavior == .cache) try r.trees.put(r.gpa, id.id, tree); | 1210 | if (cache_behavior == .cache) try r.trees.put(r.gpa, id.id, tree); |
| 1185 | return .{ id, tree }; | 1211 | return .{ id, tree }; |
| 1186 | } | 1212 | } |
| 1187 | if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1188 | } | 1213 | } |
| 1189 | return null; | 1214 | return null; |
| 1190 | } | 1215 | } |
| ... | @@ -1200,18 +1225,16 @@ pub const Repository = struct { | ... | @@ -1200,18 +1225,16 @@ pub const Repository = struct { |
| 1200 | if (cache_behavior == .cache) if (r.tags.get(id.id)) |val| { | 1225 | if (cache_behavior == .cache) if (r.tags.get(id.id)) |val| { |
| 1201 | return .{ id, val }; | 1226 | return .{ id, val }; |
| 1202 | }; | 1227 | }; |
| 1203 | if (try r.getObject(id.id, cache_behavior)) |obj| { | 1228 | if (try r.getObject(id.id, .cache)) |obj| { |
| 1204 | if (obj.type == .tag) { | 1229 | if (obj.type == .tag) { |
| 1205 | errdefer if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1206 | const raw = try r.gpa.dupe(u8, obj.content); | 1230 | const raw = try r.gpa.dupe(u8, obj.content); |
| 1207 | errdefer r.gpa.free(raw); | 1231 | errdefer r.gpa.free(raw); |
| 1208 | const tag = try r.gpa.create(Tag); | 1232 | const tag = try r.gpa.create(Tag); |
| 1209 | errdefer r.gpa.destroy(tag); | 1233 | errdefer r.gpa.destroy(tag); |
| 1210 | tag.* = try parseTag(obj.content); | 1234 | tag.* = try parseTag(raw); |
| 1211 | if (cache_behavior == .cache) try r.tags.put(r.gpa, id.id, tag); | 1235 | if (cache_behavior == .cache) try r.tags.put(r.gpa, id.id, tag); |
| 1212 | return .{ id, tag }; | 1236 | return .{ id, tag }; |
| 1213 | } | 1237 | } |
| 1214 | if (cache_behavior == .no_cache) r.gpa.free(obj.content); | | |
| 1215 | } | 1238 | } |
| 1216 | return null; | 1239 | return null; |
| 1217 | } | 1240 | } |
| ... | @@ -1301,9 +1324,10 @@ pub const Repository = struct { | ... | @@ -1301,9 +1324,10 @@ pub const Repository = struct { |
| 1301 | | 1324 | |
| 1302 | // const start = time.milliTimestamp(); | 1325 | // const start = time.milliTimestamp(); |
| 1303 | | 1326 | |
| 1304 | const base = try r.getCommitA(base_oid.id, .cache); | 1327 | const base = try r.getCommitA(base_oid.id, .no_cache); |
| 1305 | const base_tree_id = (try traverseTo(r, base.tree, dir_path)).?; | 1328 | const base_tree_id, const base_tree_id_parent = try traverseTo(r, base.tree, dir_path); |
| 1306 | const base_tree = try r.getTreeA(base_tree_id.id, .cache); | 1329 | defer if (base_tree_id_parent) |p| p.destroy(r); |
| | 1330 | const base_tree = try r.getTreeA(base_tree_id.?.id, .cache); |
| 1307 | const total = base_tree.children.len; | 1331 | const total = base_tree.children.len; |
| 1308 | | 1332 | |
| 1309 | var found: usize = 0; | 1333 | var found: usize = 0; |
| ... | @@ -1317,29 +1341,38 @@ pub const Repository = struct { | ... | @@ -1317,29 +1341,38 @@ pub const Repository = struct { |
| 1317 | var searched: usize = 1; | 1341 | var searched: usize = 1; |
| 1318 | var commit_id_prev = base_oid; | 1342 | var commit_id_prev = base_oid; |
| 1319 | var commit_id = base_oid; | 1343 | var commit_id = base_oid; |
| | 1344 | var commit_prev_prev: ?*Commit = null; |
| | 1345 | var commit_prev: ?*Commit = null; |
| 1320 | var commit = base; | 1346 | var commit = base; |
| 1321 | var tree_id = base_tree_id; | 1347 | var tree_id = base_tree_id.?; |
| 1322 | while (true) { | 1348 | while (true) : ({ |
| 1323 | if (commit.parents.len == 0) break; | 1349 | if (commit_prev_prev) |p| p.destroy(r); |
| 1324 | commit_id, commit = (try r.getCommit(commit.parents[0], .cache)).?; | 1350 | commit_prev_prev = commit_prev; |
| | 1351 | commit_prev = commit; |
| | 1352 | commit_id_prev = commit_id; |
| | 1353 | commit_id, commit = (try r.getCommit(commit.parents[0], .no_cache)).?; |
| | 1354 | }) { |
| 1325 | searched += 1; | 1355 | searched += 1; |
| 1326 | defer commit_id_prev = commit_id; | 1356 | if (commit.parents.len == 0) break; |
| 1327 | const new_tree_id = try traverseTo(r, commit.tree, dir_path) orelse { | 1357 | const new_tree_id, const new_tree_id_parent = try traverseTo(r, commit.tree, dir_path); |
| | 1358 | defer if (new_tree_id_parent) |p| p.destroy(r); |
| | 1359 | if (new_tree_id == null) { |
| 1328 | var i: usize = 0; | 1360 | var i: usize = 0; |
| 1329 | while (findFirstUnset(set, i)) |j| : (i += 1) { | 1361 | while (findFirstUnset(set, i)) |j| : (i += 1) { |
| 1330 | i = j; | 1362 | i = j; |
| 1331 | const k = result.keys()[i]; | 1363 | const k = result.keys()[i]; |
| 1332 | found += 1; | 1364 | found += 1; |
| 1333 | result.putAssumeCapacity(k, commit_id_prev); | 1365 | result.putAssumeCapacity(k, .{ .id = (try r.gpa.dupe(u8, commit_id_prev.id))[0..40] }); |
| 1334 | set.set(i); | 1366 | set.set(i); |
| 1335 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, found {s}", .{ found, total, searched, k }); | 1367 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, found {s}", .{ found, total, searched, k }); |
| 1336 | continue; | 1368 | continue; |
| 1337 | } | 1369 | } |
| 1338 | break; | 1370 | break; |
| 1339 | }; | 1371 | } |
| 1340 | if (new_tree_id.eql(tree_id)) continue; | 1372 | if (new_tree_id.?.eql(tree_id)) continue; |
| 1341 | tree_id = new_tree_id; | 1373 | tree_id = new_tree_id.?; |
| 1342 | const tree = try r.getTreeA(tree_id.id, .cache); | 1374 | const tree = try r.getTreeA(tree_id.id, .no_cache); |
| | 1375 | defer tree.destroy(r); |
| 1343 | var i: usize = 0; | 1376 | var i: usize = 0; |
| 1344 | while (findFirstUnset(set, i)) |j| : (i += 1) { | 1377 | while (findFirstUnset(set, i)) |j| : (i += 1) { |
| 1345 | i = j; | 1378 | i = j; |
| ... | @@ -1347,14 +1380,14 @@ pub const Repository = struct { | ... | @@ -1347,14 +1380,14 @@ pub const Repository = struct { |
| 1347 | const new = tree.get(k); | 1380 | const new = tree.get(k); |
| 1348 | if (new == null) { | 1381 | if (new == null) { |
| 1349 | found += 1; | 1382 | found += 1; |
| 1350 | result.putAssumeCapacity(k, commit_id_prev); | 1383 | result.putAssumeCapacity(k, .{ .id = (try r.gpa.dupe(u8, commit_id_prev.id))[0..40] }); |
| 1351 | set.set(i); | 1384 | set.set(i); |
| 1352 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, at {d} found {s}", .{ found, total, searched, i, k }); | 1385 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, at {d} found {s}", .{ found, total, searched, i, k }); |
| 1353 | continue; | 1386 | continue; |
| 1354 | } | 1387 | } |
| 1355 | if (!std.mem.eql(u8, new.?.id.erase(), base_tree.children[i].id.erase())) { | 1388 | if (!std.mem.eql(u8, new.?.id.erase(), base_tree.children[i].id.erase())) { |
| 1356 | found += 1; | 1389 | found += 1; |
| 1357 | result.putAssumeCapacity(k, commit_id_prev); | 1390 | result.putAssumeCapacity(k, .{ .id = (try r.gpa.dupe(u8, commit_id_prev.id))[0..40] }); |
| 1358 | set.set(i); | 1391 | set.set(i); |
| 1359 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, at {d} found {s}", .{ found, total, searched, i, k }); | 1392 | // std.log.debug("found [{d}/{d}] objects after searching {d} commits, at {d} found {s}", .{ found, total, searched, i, k }); |
| 1360 | continue; | 1393 | continue; |
| ... | @@ -1709,17 +1742,21 @@ const ZlibCode = enum(c_int) { | ... | @@ -1709,17 +1742,21 @@ const ZlibCode = enum(c_int) { |
| 1709 | Z_VERSION_ERROR = -6, | 1742 | Z_VERSION_ERROR = -6, |
| 1710 | }; | 1743 | }; |
| 1711 | | 1744 | |
| 1712 | fn traverseTo(r: *Repository, treestart_id: TreeId, dir_path: []const u8) !?TreeId { | 1745 | fn traverseTo(r: *Repository, treestart_id: TreeId, dir_path: []const u8) !struct { ?TreeId, ?*Tree } { |
| 1713 | var id = treestart_id; | 1746 | var id = treestart_id; |
| 1714 | if (dir_path.len == 0) return id; | 1747 | if (dir_path.len == 0) return .{ id, null }; |
| 1715 | var iter = std.mem.splitScalar(u8, dir_path, '/'); | 1748 | var iter = std.mem.splitScalar(u8, dir_path, '/'); |
| | 1749 | var prev: ?*Tree = null; |
| 1716 | while (iter.next()) |segment| { | 1750 | while (iter.next()) |segment| { |
| 1717 | const tree = try r.getTreeA(id.id, .cache); | 1751 | const p = prev; |
| 1718 | const o = tree.get(segment) orelse return null; | 1752 | const tree = try r.getTreeA(id.id, .no_cache); |
| 1719 | if (o.id != .tree) return null; | 1753 | defer prev = tree; |
| | 1754 | if (p) |_| p.?.destroy(r); |
| | 1755 | const o = tree.get(segment) orelse return .{ null, tree }; |
| | 1756 | if (o.id != .tree) return .{ null, tree }; |
| 1720 | id = o.id.tree; | 1757 | id = o.id.tree; |
| 1721 | } | 1758 | } |
| 1722 | return id; | 1759 | return .{ id, prev }; |
| 1723 | } | 1760 | } |
| 1724 | | 1761 | |
| 1725 | pub const Tree = struct { | 1762 | pub const Tree = struct { |