| ... | ... | @@ -38,6 +38,32 @@ test parseOid { |
| 38 | 38 | try testing.expectError(error.InvalidOid, parseOid("HEAD")); |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | pub const Diagnostics = struct { |
| 42 | allocator: Allocator, |
| 43 | errors: std.ArrayListUnmanaged(Error) = .{}, |
| 44 | |
| 45 | pub const Error = union(enum) { |
| 46 | unable_to_create_sym_link: struct { |
| 47 | code: anyerror, |
| 48 | file_name: []const u8, |
| 49 | link_name: []const u8, |
| 50 | }, |
| 51 | }; |
| 52 | |
| 53 | pub fn deinit(d: *Diagnostics) void { |
| 54 | for (d.errors.items) |item| { |
| 55 | switch (item) { |
| 56 | .unable_to_create_sym_link => |info| { |
| 57 | d.allocator.free(info.file_name); |
| 58 | d.allocator.free(info.link_name); |
| 59 | }, |
| 60 | } |
| 61 | } |
| 62 | d.errors.deinit(d.allocator); |
| 63 | d.* = undefined; |
| 64 | } |
| 65 | }; |
| 66 | |
| 41 | 67 | pub const Repository = struct { |
| 42 | 68 | odb: Odb, |
| 43 | 69 | |
| ... | ... | @@ -55,6 +81,7 @@ pub const Repository = struct { |
| 55 | 81 | repository: *Repository, |
| 56 | 82 | worktree: std.fs.Dir, |
| 57 | 83 | commit_oid: Oid, |
| 84 | diagnostics: *Diagnostics, |
| 58 | 85 | ) !void { |
| 59 | 86 | try repository.odb.seekOid(commit_oid); |
| 60 | 87 | const tree_oid = tree_oid: { |
| ... | ... | @@ -62,7 +89,7 @@ pub const Repository = struct { |
| 62 | 89 | if (commit_object.type != .commit) return error.NotACommit; |
| 63 | 90 | break :tree_oid try getCommitTree(commit_object.data); |
| 64 | 91 | }; |
| 65 | | try repository.checkoutTree(worktree, tree_oid); |
| 92 | try repository.checkoutTree(worktree, tree_oid, "", diagnostics); |
| 66 | 93 | } |
| 67 | 94 | |
| 68 | 95 | /// Checks out the tree at `tree_oid` to `worktree`. |
| ... | ... | @@ -70,6 +97,8 @@ pub const Repository = struct { |
| 70 | 97 | repository: *Repository, |
| 71 | 98 | dir: std.fs.Dir, |
| 72 | 99 | tree_oid: Oid, |
| 100 | current_path: []const u8, |
| 101 | diagnostics: *Diagnostics, |
| 73 | 102 | ) !void { |
| 74 | 103 | try repository.odb.seekOid(tree_oid); |
| 75 | 104 | const tree_object = try repository.odb.readObject(); |
| ... | ... | @@ -87,7 +116,9 @@ pub const Repository = struct { |
| 87 | 116 | try dir.makeDir(entry.name); |
| 88 | 117 | var subdir = try dir.openDir(entry.name, .{}); |
| 89 | 118 | defer subdir.close(); |
| 90 | | try repository.checkoutTree(subdir, entry.oid); |
| 119 | const sub_path = try std.fs.path.join(repository.odb.allocator, &.{ current_path, entry.name }); |
| 120 | defer repository.odb.allocator.free(sub_path); |
| 121 | try repository.checkoutTree(subdir, entry.oid, sub_path, diagnostics); |
| 91 | 122 | }, |
| 92 | 123 | .file => { |
| 93 | 124 | var file = try dir.createFile(entry.name, .{}); |
| ... | ... | @@ -98,7 +129,23 @@ pub const Repository = struct { |
| 98 | 129 | try file.writeAll(file_object.data); |
| 99 | 130 | try file.sync(); |
| 100 | 131 | }, |
| 101 | | .symlink => return error.SymlinkNotSupported, |
| 132 | .symlink => { |
| 133 | try repository.odb.seekOid(entry.oid); |
| 134 | var symlink_object = try repository.odb.readObject(); |
| 135 | if (symlink_object.type != .blob) return error.InvalidFile; |
| 136 | const link_name = symlink_object.data; |
| 137 | dir.symLink(link_name, entry.name, .{}) catch |e| { |
| 138 | const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name }); |
| 139 | errdefer diagnostics.allocator.free(file_name); |
| 140 | const link_name_dup = try diagnostics.allocator.dupe(u8, link_name); |
| 141 | errdefer diagnostics.allocator.free(link_name_dup); |
| 142 | try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_sym_link = .{ |
| 143 | .code = e, |
| 144 | .file_name = file_name, |
| 145 | .link_name = link_name_dup, |
| 146 | } }); |
| 147 | }; |
| 148 | }, |
| 102 | 149 | .gitlink => { |
| 103 | 150 | // Consistent with git archive behavior, create the directory but |
| 104 | 151 | // do nothing else |