@@ -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,21 +81,24 @@ 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 : {
61
88
var commit_object = try repository .odb .readObject ();
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`.
69
96
fn checkoutTree (
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
0 commit comments