| ... | @@ -965,3 +965,89 @@ pub const BlameIterator = struct { | ... | @@ -965,3 +965,89 @@ pub const BlameIterator = struct { |
| 965 | line: string, | 965 | line: string, |
| 966 | }; | 966 | }; |
| 967 | }; | 967 | }; |
| | 968 | |
| | 969 | pub const Repository = struct { |
| | 970 | gitdir: nfs.Dir, |
| | 971 | gpa: std.mem.Allocator, |
| | 972 | raw_object_contents: std.StringArrayHashMapUnmanaged([]const u8) = .empty, |
| | 973 | commits: std.StringArrayHashMapUnmanaged(Commit), |
| | 974 | |
| | 975 | pub fn init(gitdir: nfs.Dir, gpa: std.mem.Allocator) Repository { |
| | 976 | return .{ |
| | 977 | .gitdir = gitdir, |
| | 978 | .gpa = gpa, |
| | 979 | .commits = .empty, |
| | 980 | }; |
| | 981 | } |
| | 982 | |
| | 983 | pub fn deinit(r: *Repository) void { |
| | 984 | for (r.raw_object_contents.values()) |v| r.gpa.free(v); |
| | 985 | r.raw_object_contents.deinit(r.gpa); |
| | 986 | r.commits.deinit(r.gpa); |
| | 987 | } |
| | 988 | |
| | 989 | fn getObject(r: *Repository, obj: Id) !?[]const u8 { |
| | 990 | if (r.raw_object_contents.get(obj)) |content| { |
| | 991 | return content; |
| | 992 | } |
| | 993 | if (obj.len == 40) blk: { //sha1 object |
| | 994 | var sub_path: [49:0]u8 = "objects/00/00000000000000000000000000000000000000".*; |
| | 995 | @memcpy(sub_path[8..][0..2], obj[0..2]); |
| | 996 | @memcpy(sub_path[11..], obj[2..]); |
| | 997 | const objfile = r.gitdir.openFile(&sub_path, .{}) catch |err| switch (err) { |
| | 998 | error.ENOENT => break :blk, |
| | 999 | else => |e| return e, |
| | 1000 | }; |
| | 1001 | defer objfile.close(); |
| | 1002 | var bufr = nio.BufferedReader(4096, nfs.File).init(objfile); |
| | 1003 | var list: std.ArrayList(u8) = .init(r.gpa); |
| | 1004 | errdefer list.deinit(); |
| | 1005 | try list.ensureUnusedCapacity(512); |
| | 1006 | try std.compress.flate.inflate.decompress(.zlib, bufr.anyReadable(), list.writer()); |
| | 1007 | const content = try list.toOwnedSlice(); |
| | 1008 | try r.raw_object_contents.put(r.gpa, obj, content); |
| | 1009 | return content; |
| | 1010 | } |
| | 1011 | return null; |
| | 1012 | } |
| | 1013 | |
| | 1014 | fn getGitObject(r: *Repository, obj: Id, arena: std.mem.Allocator) !?GitObject { |
| | 1015 | if (try r.getObject(obj)) |data| { |
| | 1016 | const header = data[0..std.mem.indexOfScalar(u8, data, 0).?]; |
| | 1017 | const _type = header[0..std.mem.indexOfScalar(u8, header, ' ').?]; |
| | 1018 | const content_len = try extras.parseDigits(u64, header[_type.len + 1 ..], 10); |
| | 1019 | std.debug.assert(data[header.len + 1 ..].len == content_len); |
| | 1020 | return .{ |
| | 1021 | .type = std.meta.stringToEnum(RefType, _type).?, |
| | 1022 | .content = try arena.dupe(u8, data[header.len + 1 ..]), |
| | 1023 | }; |
| | 1024 | } |
| | 1025 | return null; |
| | 1026 | } |
| | 1027 | |
| | 1028 | const RefType = enum { |
| | 1029 | blob, |
| | 1030 | tree, |
| | 1031 | commit, |
| | 1032 | tag, |
| | 1033 | }; |
| | 1034 | |
| | 1035 | const GitObject = struct { |
| | 1036 | type: RefType, |
| | 1037 | content: []const u8, |
| | 1038 | }; |
| | 1039 | |
| | 1040 | pub fn getCommit(r: *Repository, arena: std.mem.Allocator, id: CommitId) !?struct { CommitId, Commit } { |
| | 1041 | if (r.commits.getPtr(id.id)) |val| { |
| | 1042 | return .{ id, val.* }; |
| | 1043 | } |
| | 1044 | if (try r.getGitObject(id.id, arena)) |obj| { |
| | 1045 | if (obj.type == .commit) { |
| | 1046 | const commit = try parseCommit(arena, obj.content); |
| | 1047 | try r.commits.put(r.gpa, id.id, commit); |
| | 1048 | return .{ id, commit }; |
| | 1049 | } |
| | 1050 | } |
| | 1051 | return null; |
| | 1052 | } |
| | 1053 | }; |