| ... | ... | @@ -965,3 +965,89 @@ pub const BlameIterator = struct { |
| 965 | 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 | }; |