authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-14 16:34:47-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-12-14 16:34:47-05:00
log13a28345bb6da736c3e4f0a65ef709dfb69fb6f3
tree7ccad1c6922fabdb84d9378c6092a4a6efac050c
parent0fac47cf28dfc669397a2bb1f661b13915a0ab4c
parent5217da57fc95920be93dda02bc905baffe4ee89d
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22222 from ianprime0509/git-sha256

zig fetch: add support for SHA-256 Git repositories

13 files changed, 283 insertions(+), 169 deletions(-)

build.zig+15-26
......@@ -381,32 +381,6 @@ pub fn build(b: *std.Build) !void {
381381 const test_target_filters = b.option([]const []const u8, "test-target-filter", "Skip tests whose target triple do not match any filter") orelse &[0][]const u8{};
382382 const test_slow_targets = b.option(bool, "test-slow-targets", "Enable running module tests for targets that have a slow compiler backend") orelse false;
383383
384 const test_cases_options = b.addOptions();
385
386 test_cases_options.addOption(bool, "enable_tracy", false);
387 test_cases_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
388 test_cases_options.addOption(bool, "enable_logging", enable_logging);
389 test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
390 test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
391 test_cases_options.addOption(bool, "have_llvm", enable_llvm);
392 test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
393 test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
394 test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
395 test_cases_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
396 test_cases_options.addOption(bool, "force_gpa", force_gpa);
397 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
398 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
399 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
400 test_cases_options.addOption(bool, "enable_rosetta", b.enable_rosetta);
401 test_cases_options.addOption(bool, "enable_darling", b.enable_darling);
402 test_cases_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
403 test_cases_options.addOption(bool, "value_tracing", value_tracing);
404 test_cases_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir);
405 test_cases_options.addOption([:0]const u8, "version", version);
406 test_cases_options.addOption(std.SemanticVersion, "semver", semver);
407 test_cases_options.addOption([]const []const u8, "test_filters", test_filters);
408 test_cases_options.addOption(DevEnv, "dev", if (only_c) .bootstrap else .core);
409
410384 var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
411385 var chosen_mode_index: usize = 0;
412386 if (!skip_debug) {
......@@ -533,6 +507,21 @@ pub fn build(b: *std.Build) !void {
533507 .max_rss = 5029889638,
534508 }));
535509
510 test_modules_step.dependOn(tests.addModuleTests(b, .{
511 .test_filters = test_filters,
512 .test_target_filters = test_target_filters,
513 .test_slow_targets = test_slow_targets,
514 .root_src = "src/main.zig",
515 .name = "compiler-internals",
516 .desc = "Run the compiler internals tests",
517 .optimize_modes = optimization_modes,
518 .include_paths = &.{},
519 .skip_single_threaded = skip_single_threaded,
520 .skip_non_native = true,
521 .skip_libc = skip_libc,
522 .build_options = exe_options,
523 }));
524
536525 test_step.dependOn(test_modules_step);
537526
538527 test_step.dependOn(tests.addCompareOutputTests(b, test_filters, optimization_modes));
src/InternPool.zig+1
......@@ -9822,6 +9822,7 @@ test "basic usage" {
98229822 const gpa = std.testing.allocator;
98239823
98249824 var ip: InternPool = .empty;
9825 try ip.init(gpa, 1);
98259826 defer ip.deinit(gpa);
98269827
98279828 const i32_type = try ip.get(gpa, .main, .{ .int_type = .{
src/Package/Fetch.zig+19-24
......@@ -814,7 +814,7 @@ const Resource = union(enum) {
814814 const Git = struct {
815815 session: git.Session,
816816 fetch_stream: git.Session.FetchStream,
817 want_oid: [git.oid_length]u8,
817 want_oid: git.Oid,
818818 };
819819
820820 fn deinit(resource: *Resource) void {
......@@ -976,7 +976,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
976976 const want_oid = want_oid: {
977977 const want_ref =
978978 if (uri.fragment) |fragment| try fragment.toRawMaybeAlloc(arena) else "HEAD";
979 if (git.parseOid(want_ref)) |oid| break :want_oid oid else |_| {}
979 if (git.Oid.parseAny(want_ref)) |oid| break :want_oid oid else |_| {}
980980
981981 const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref});
982982 const want_ref_tag = try std.fmt.allocPrint(arena, "refs/tags/{s}", .{want_ref});
......@@ -1018,17 +1018,13 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
10181018 });
10191019 const notes_start = try eb.reserveNotes(notes_len);
10201020 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
1021 .msg = try eb.printString("try .url = \"{;+/}#{}\",", .{
1022 uri, std.fmt.fmtSliceHexLower(&want_oid),
1023 }),
1021 .msg = try eb.printString("try .url = \"{;+/}#{}\",", .{ uri, want_oid }),
10241022 }));
10251023 return error.FetchFailed;
10261024 }
10271025
1028 var want_oid_buf: [git.fmt_oid_length]u8 = undefined;
1029 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{
1030 std.fmt.fmtSliceHexLower(&want_oid),
1031 }) catch unreachable;
1026 var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined;
1027 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{want_oid}) catch unreachable;
10321028 var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| {
10331029 return f.fail(f.location_tok, try eb.printString(
10341030 "unable to create fetch stream: {s}",
......@@ -1163,7 +1159,7 @@ fn unpackResource(
11631159 });
11641160 return try unpackTarball(f, tmp_directory.handle, dcp.reader());
11651161 },
1166 .git_pack => return unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) {
1162 .git_pack => return unpackGitPack(f, tmp_directory.handle, &resource.git) catch |err| switch (err) {
11671163 error.FetchFailed => return error.FetchFailed,
11681164 error.OutOfMemory => return error.OutOfMemory,
11691165 else => |e| return f.fail(f.location_tok, try eb.printString(
......@@ -1298,11 +1294,10 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {
12981294 return res;
12991295}
13001296
1301fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!UnpackResult {
1297fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!UnpackResult {
13021298 const arena = f.arena.allocator();
13031299 const gpa = f.arena.child_allocator;
1304 const want_oid = resource.git.want_oid;
1305 const reader = resource.git.fetch_stream.reader();
1300 const object_format: git.Oid.Format = resource.want_oid;
13061301
13071302 var res: UnpackResult = .{};
13081303 // The .git directory is used to store the packfile and associated index, but
......@@ -1314,7 +1309,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac
13141309 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });
13151310 defer pack_file.close();
13161311 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
1317 try fifo.pump(reader, pack_file.writer());
1312 try fifo.pump(resource.fetch_stream.reader(), pack_file.writer());
13181313 try pack_file.sync();
13191314
13201315 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
......@@ -1323,7 +1318,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac
13231318 const index_prog_node = f.prog_node.start("Index pack", 0);
13241319 defer index_prog_node.end();
13251320 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());
1326 try git.indexPack(gpa, pack_file, index_buffered_writer.writer());
1321 try git.indexPack(gpa, object_format, pack_file, index_buffered_writer.writer());
13271322 try index_buffered_writer.flush();
13281323 try index_file.sync();
13291324 }
......@@ -1331,10 +1326,10 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac
13311326 {
13321327 const checkout_prog_node = f.prog_node.start("Checkout", 0);
13331328 defer checkout_prog_node.end();
1334 var repository = try git.Repository.init(gpa, pack_file, index_file);
1329 var repository = try git.Repository.init(gpa, object_format, pack_file, index_file);
13351330 defer repository.deinit();
13361331 var diagnostics: git.Diagnostics = .{ .allocator = arena };
1337 try repository.checkout(out_dir, want_oid, &diagnostics);
1332 try repository.checkout(out_dir, resource.want_oid, &diagnostics);
13381333
13391334 if (diagnostics.errors.items.len > 0) {
13401335 try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack packfile");
......@@ -1695,7 +1690,7 @@ const HashedFile = struct {
16951690fn stripRoot(fs_path: []const u8, root_dir: []const u8) []const u8 {
16961691 if (root_dir.len == 0 or fs_path.len <= root_dir.len) return fs_path;
16971692
1698 if (std.mem.eql(u8, fs_path[0..root_dir.len], root_dir) and fs_path[root_dir.len] == fs.path.sep) {
1693 if (std.mem.eql(u8, fs_path[0..root_dir.len], root_dir) and fs.path.isSep(fs_path[root_dir.len])) {
16991694 return fs_path[root_dir.len + 1 ..];
17001695 }
17011696
......@@ -1810,8 +1805,8 @@ const FileHeader = struct {
18101805 }
18111806
18121807 pub fn isExecutable(self: *FileHeader) bool {
1813 return std.mem.eql(u8, self.header[0..shebang.len], shebang) or
1814 std.mem.eql(u8, self.header[0..elf_magic.len], elf_magic);
1808 return std.mem.eql(u8, self.header[0..@min(self.bytes_read, shebang.len)], shebang) or
1809 std.mem.eql(u8, self.header[0..@min(self.bytes_read, elf_magic.len)], elf_magic);
18151810 }
18161811};
18171812
......@@ -2244,7 +2239,6 @@ const TestFetchBuilder = struct {
22442239 thread_pool: ThreadPool,
22452240 http_client: std.http.Client,
22462241 global_cache_directory: Cache.Directory,
2247 progress: std.Progress,
22482242 job_queue: Fetch.JobQueue,
22492243 fetch: Fetch,
22502244
......@@ -2260,8 +2254,6 @@ const TestFetchBuilder = struct {
22602254 self.http_client = .{ .allocator = allocator };
22612255 self.global_cache_directory = .{ .handle = cache_dir, .path = null };
22622256
2263 self.progress = .{ .dont_print_on_dumb = true };
2264
22652257 self.job_queue = .{
22662258 .http_client = &self.http_client,
22672259 .thread_pool = &self.thread_pool,
......@@ -2281,10 +2273,11 @@ const TestFetchBuilder = struct {
22812273 .lazy_status = .eager,
22822274 .parent_package_root = Cache.Path{ .root_dir = Cache.Directory{ .handle = cache_dir, .path = null } },
22832275 .parent_manifest_ast = null,
2284 .prog_node = self.progress.start("Fetch", 0),
2276 .prog_node = std.Progress.Node.none,
22852277 .job_queue = &self.job_queue,
22862278 .omit_missing_hash_error = true,
22872279 .allow_missing_paths_field = false,
2280 .use_latest_commit = true,
22882281
22892282 .package_root = undefined,
22902283 .error_bundle = undefined,
......@@ -2293,6 +2286,8 @@ const TestFetchBuilder = struct {
22932286 .actual_hash = undefined,
22942287 .has_build_zig = false,
22952288 .oom_flag = false,
2289 .latest_commit = null,
2290
22962291 .module = null,
22972292 };
22982293 return &self.fetch;
src/Package/Fetch/git.zig+237-116
......@@ -9,32 +9,133 @@ const mem = std.mem;
99const testing = std.testing;
1010const Allocator = mem.Allocator;
1111const Sha1 = std.crypto.hash.Sha1;
12const Sha256 = std.crypto.hash.sha2.Sha256;
1213const assert = std.debug.assert;
1314
14pub const oid_length = Sha1.digest_length;
15pub const fmt_oid_length = 2 * oid_length;
16/// The ID of a Git object (an SHA-1 hash).
17pub const Oid = [oid_length]u8;
15/// The ID of a Git object.
16pub const Oid = union(Format) {
17 sha1: [Sha1.digest_length]u8,
18 sha256: [Sha256.digest_length]u8,
1819
19pub fn parseOid(s: []const u8) !Oid {
20 if (s.len != fmt_oid_length) return error.InvalidOid;
21 var oid: Oid = undefined;
22 for (&oid, 0..) |*b, i| {
23 b.* = std.fmt.parseUnsigned(u8, s[2 * i ..][0..2], 16) catch return error.InvalidOid;
20 pub const max_formatted_length = len: {
21 var max: usize = 0;
22 for (std.enums.values(Format)) |f| {
23 max = @max(max, f.formattedLength());
24 }
25 break :len max;
26 };
27
28 pub const Format = enum {
29 sha1,
30 sha256,
31
32 pub fn byteLength(f: Format) usize {
33 return switch (f) {
34 .sha1 => Sha1.digest_length,
35 .sha256 => Sha256.digest_length,
36 };
37 }
38
39 pub fn formattedLength(f: Format) usize {
40 return 2 * f.byteLength();
41 }
42 };
43
44 const Hasher = union(Format) {
45 sha1: Sha1,
46 sha256: Sha256,
47
48 fn init(oid_format: Format) Hasher {
49 return switch (oid_format) {
50 .sha1 => .{ .sha1 = Sha1.init(.{}) },
51 .sha256 => .{ .sha256 = Sha256.init(.{}) },
52 };
53 }
54
55 // Must be public for use from HashedReader and HashedWriter.
56 pub fn update(hasher: *Hasher, b: []const u8) void {
57 switch (hasher.*) {
58 inline else => |*inner| inner.update(b),
59 }
60 }
61
62 fn finalResult(hasher: *Hasher) Oid {
63 return switch (hasher.*) {
64 inline else => |*inner, tag| @unionInit(Oid, @tagName(tag), inner.finalResult()),
65 };
66 }
67 };
68
69 pub fn fromBytes(oid_format: Format, bytes: []const u8) Oid {
70 assert(bytes.len == oid_format.byteLength());
71 return switch (oid_format) {
72 inline else => |tag| @unionInit(Oid, @tagName(tag), bytes[0..comptime tag.byteLength()].*),
73 };
2474 }
25 return oid;
26}
2775
28test parseOid {
29 try testing.expectEqualSlices(
30 u8,
31 &.{ 0xCE, 0x91, 0x9C, 0xCF, 0x45, 0x95, 0x18, 0x56, 0xA7, 0x62, 0xFF, 0xDB, 0x8E, 0xF8, 0x50, 0x30, 0x1C, 0xD8, 0xC5, 0x88 },
32 &try parseOid("ce919ccf45951856a762ffdb8ef850301cd8c588"),
33 );
34 try testing.expectError(error.InvalidOid, parseOid("ce919ccf"));
35 try testing.expectError(error.InvalidOid, parseOid("master"));
36 try testing.expectError(error.InvalidOid, parseOid("HEAD"));
37}
76 pub fn readBytes(oid_format: Format, reader: anytype) @TypeOf(reader).NoEofError!Oid {
77 return switch (oid_format) {
78 inline else => |tag| @unionInit(Oid, @tagName(tag), try reader.readBytesNoEof(tag.byteLength())),
79 };
80 }
81
82 pub fn parse(oid_format: Format, s: []const u8) error{InvalidOid}!Oid {
83 switch (oid_format) {
84 inline else => |tag| {
85 if (s.len != tag.formattedLength()) return error.InvalidOid;
86 var bytes: [tag.byteLength()]u8 = undefined;
87 for (&bytes, 0..) |*b, i| {
88 b.* = std.fmt.parseUnsigned(u8, s[2 * i ..][0..2], 16) catch return error.InvalidOid;
89 }
90 return @unionInit(Oid, @tagName(tag), bytes);
91 },
92 }
93 }
94
95 test parse {
96 try testing.expectEqualSlices(
97 u8,
98 &.{ 0xCE, 0x91, 0x9C, 0xCF, 0x45, 0x95, 0x18, 0x56, 0xA7, 0x62, 0xFF, 0xDB, 0x8E, 0xF8, 0x50, 0x30, 0x1C, 0xD8, 0xC5, 0x88 },
99 &(try parse(.sha1, "ce919ccf45951856a762ffdb8ef850301cd8c588")).sha1,
100 );
101 try testing.expectError(error.InvalidOid, parse(.sha256, "ce919ccf45951856a762ffdb8ef850301cd8c588"));
102 try testing.expectError(error.InvalidOid, parse(.sha1, "7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a"));
103 try testing.expectEqualSlices(
104 u8,
105 &.{ 0x7F, 0x44, 0x4A, 0x92, 0xBD, 0x45, 0x72, 0xEE, 0x4A, 0x28, 0xB2, 0xC6, 0x30, 0x59, 0x92, 0x4A, 0x9C, 0xA1, 0x82, 0x91, 0x38, 0x55, 0x3E, 0xF3, 0xE7, 0xC4, 0x1E, 0xE1, 0x59, 0xAF, 0xAE, 0x7A },
106 &(try parse(.sha256, "7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a")).sha256,
107 );
108 try testing.expectError(error.InvalidOid, parse(.sha1, "ce919ccf"));
109 try testing.expectError(error.InvalidOid, parse(.sha256, "ce919ccf"));
110 try testing.expectError(error.InvalidOid, parse(.sha1, "master"));
111 try testing.expectError(error.InvalidOid, parse(.sha256, "master"));
112 try testing.expectError(error.InvalidOid, parse(.sha1, "HEAD"));
113 try testing.expectError(error.InvalidOid, parse(.sha256, "HEAD"));
114 }
115
116 pub fn parseAny(s: []const u8) error{InvalidOid}!Oid {
117 return for (std.enums.values(Format)) |f| {
118 if (s.len == f.formattedLength()) break parse(f, s);
119 } else error.InvalidOid;
120 }
121
122 pub fn format(
123 oid: Oid,
124 comptime fmt: []const u8,
125 options: std.fmt.FormatOptions,
126 writer: anytype,
127 ) @TypeOf(writer).Error!void {
128 _ = fmt;
129 _ = options;
130 try writer.print("{}", .{std.fmt.fmtSliceHexLower(oid.slice())});
131 }
132
133 pub fn slice(oid: *const Oid) []const u8 {
134 return switch (oid.*) {
135 inline else => |*bytes| bytes,
136 };
137 }
138};
38139
39140pub const Diagnostics = struct {
40141 allocator: Allocator,
......@@ -72,8 +173,8 @@ pub const Diagnostics = struct {
72173pub const Repository = struct {
73174 odb: Odb,
74175
75 pub fn init(allocator: Allocator, pack_file: std.fs.File, index_file: std.fs.File) !Repository {
76 return .{ .odb = try Odb.init(allocator, pack_file, index_file) };
176 pub fn init(allocator: Allocator, format: Oid.Format, pack_file: std.fs.File, index_file: std.fs.File) !Repository {
177 return .{ .odb = try Odb.init(allocator, format, pack_file, index_file) };
77178 }
78179
79180 pub fn deinit(repository: *Repository) void {
......@@ -92,7 +193,7 @@ pub const Repository = struct {
92193 const tree_oid = tree_oid: {
93194 const commit_object = try repository.odb.readObject();
94195 if (commit_object.type != .commit) return error.NotACommit;
95 break :tree_oid try getCommitTree(commit_object.data);
196 break :tree_oid try getCommitTree(repository.odb.format, commit_object.data);
96197 };
97198 try repository.checkoutTree(worktree, tree_oid, "", diagnostics);
98199 }
......@@ -114,7 +215,11 @@ pub const Repository = struct {
114215 const tree_data = try repository.odb.allocator.dupe(u8, tree_object.data);
115216 defer repository.odb.allocator.free(tree_data);
116217
117 var tree_iter: TreeIterator = .{ .data = tree_data };
218 var tree_iter: TreeIterator = .{
219 .format = repository.odb.format,
220 .data = tree_data,
221 .pos = 0,
222 };
118223 while (try tree_iter.next()) |entry| {
119224 switch (entry.type) {
120225 .directory => {
......@@ -170,19 +275,20 @@ pub const Repository = struct {
170275
171276 /// Returns the ID of the tree associated with the given commit (provided as
172277 /// raw object data).
173 fn getCommitTree(commit_data: []const u8) !Oid {
278 fn getCommitTree(format: Oid.Format, commit_data: []const u8) !Oid {
174279 if (!mem.startsWith(u8, commit_data, "tree ") or
175 commit_data.len < "tree ".len + fmt_oid_length + "\n".len or
176 commit_data["tree ".len + fmt_oid_length] != '\n')
280 commit_data.len < "tree ".len + format.formattedLength() + "\n".len or
281 commit_data["tree ".len + format.formattedLength()] != '\n')
177282 {
178283 return error.InvalidCommit;
179284 }
180 return try parseOid(commit_data["tree ".len..][0..fmt_oid_length]);
285 return try .parse(format, commit_data["tree ".len..][0..format.formattedLength()]);
181286 }
182287
183288 const TreeIterator = struct {
289 format: Oid.Format,
184290 data: []const u8,
185 pos: usize = 0,
291 pos: usize,
186292
187293 const Entry = struct {
188294 type: Type,
......@@ -220,8 +326,9 @@ pub const Repository = struct {
220326 const name = iterator.data[iterator.pos..name_end :0];
221327 iterator.pos = name_end + 1;
222328
329 const oid_length = iterator.format.byteLength();
223330 if (iterator.pos + oid_length > iterator.data.len) return error.InvalidTree;
224 const oid = iterator.data[iterator.pos..][0..oid_length].*;
331 const oid: Oid = .fromBytes(iterator.format, iterator.data[iterator.pos..][0..oid_length]);
225332 iterator.pos += oid_length;
226333
227334 return .{ .type = @"type", .executable = executable, .name = name, .oid = oid };
......@@ -235,6 +342,7 @@ pub const Repository = struct {
235342/// The format of the packfile and its associated index are documented in
236343/// [pack-format](https://git-scm.com/docs/pack-format).
237344const Odb = struct {
345 format: Oid.Format,
238346 pack_file: std.fs.File,
239347 index_header: IndexHeader,
240348 index_file: std.fs.File,
......@@ -242,11 +350,12 @@ const Odb = struct {
242350 allocator: Allocator,
243351
244352 /// Initializes the database from open pack and index files.
245 fn init(allocator: Allocator, pack_file: std.fs.File, index_file: std.fs.File) !Odb {
353 fn init(allocator: Allocator, format: Oid.Format, pack_file: std.fs.File, index_file: std.fs.File) !Odb {
246354 try pack_file.seekTo(0);
247355 try index_file.seekTo(0);
248356 const index_header = try IndexHeader.read(index_file.reader());
249357 return .{
358 .format = format,
250359 .pack_file = pack_file,
251360 .index_header = index_header,
252361 .index_file = index_file,
......@@ -268,7 +377,7 @@ const Odb = struct {
268377 const base_object = while (true) {
269378 if (odb.cache.get(base_offset)) |base_object| break base_object;
270379
271 base_header = try EntryHeader.read(odb.pack_file.reader());
380 base_header = try EntryHeader.read(odb.format, odb.pack_file.reader());
272381 switch (base_header) {
273382 .ofs_delta => |ofs_delta| {
274383 try delta_offsets.append(odb.allocator, base_offset);
......@@ -292,6 +401,7 @@ const Odb = struct {
292401
293402 const base_data = try resolveDeltaChain(
294403 odb.allocator,
404 odb.format,
295405 odb.pack_file,
296406 base_object,
297407 delta_offsets.items,
......@@ -303,14 +413,15 @@ const Odb = struct {
303413
304414 /// Seeks to the beginning of the object with the given ID.
305415 fn seekOid(odb: *Odb, oid: Oid) !void {
306 const key = oid[0];
416 const oid_length = odb.format.byteLength();
417 const key = oid.slice()[0];
307418 var start_index = if (key > 0) odb.index_header.fan_out_table[key - 1] else 0;
308419 var end_index = odb.index_header.fan_out_table[key];
309420 const found_index = while (start_index < end_index) {
310421 const mid_index = start_index + (end_index - start_index) / 2;
311422 try odb.index_file.seekTo(IndexHeader.size + mid_index * oid_length);
312 const mid_oid = try odb.index_file.reader().readBytesNoEof(oid_length);
313 switch (mem.order(u8, &mid_oid, &oid)) {
423 const mid_oid = try Oid.readBytes(odb.format, odb.index_file.reader());
424 switch (mem.order(u8, mid_oid.slice(), oid.slice())) {
314425 .lt => start_index = mid_index + 1,
315426 .gt => end_index = mid_index,
316427 .eq => break mid_index,
......@@ -495,6 +606,7 @@ pub const Session = struct {
495606 location: Location,
496607 supports_agent: bool,
497608 supports_shallow: bool,
609 object_format: Oid.Format,
498610 allocator: Allocator,
499611
500612 const agent = "zig/" ++ @import("builtin").zig_version_string;
......@@ -513,6 +625,7 @@ pub const Session = struct {
513625 .location = try .init(allocator, uri),
514626 .supports_agent = false,
515627 .supports_shallow = false,
628 .object_format = .sha1,
516629 .allocator = allocator,
517630 };
518631 errdefer session.deinit();
......@@ -528,6 +641,10 @@ pub const Session = struct {
528641 session.supports_shallow = true;
529642 }
530643 }
644 } else if (mem.eql(u8, capability.key, "object-format")) {
645 if (std.meta.stringToEnum(Oid.Format, capability.value orelse continue)) |format| {
646 session.object_format = format;
647 }
531648 }
532649 }
533650 return session;
......@@ -708,6 +825,11 @@ pub const Session = struct {
708825 if (session.supports_agent) {
709826 try Packet.write(.{ .data = agent_capability }, body_writer);
710827 }
828 {
829 const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)});
830 defer session.allocator.free(object_format_packet);
831 try Packet.write(.{ .data = object_format_packet }, body_writer);
832 }
711833 try Packet.write(.delimiter, body_writer);
712834 for (options.ref_prefixes) |ref_prefix| {
713835 const ref_prefix_packet = try std.fmt.allocPrint(session.allocator, "ref-prefix {s}\n", .{ref_prefix});
......@@ -739,10 +861,14 @@ pub const Session = struct {
739861 try request.wait();
740862 if (request.response.status != .ok) return error.ProtocolError;
741863
742 return .{ .request = request };
864 return .{
865 .format = session.object_format,
866 .request = request,
867 };
743868 }
744869
745870 pub const RefIterator = struct {
871 format: Oid.Format,
746872 request: std.http.Client.Request,
747873 buf: [Packet.max_data_length]u8 = undefined,
748874
......@@ -764,7 +890,7 @@ pub const Session = struct {
764890 .data => |data| {
765891 const ref_data = Packet.normalizeText(data);
766892 const oid_sep_pos = mem.indexOfScalar(u8, ref_data, ' ') orelse return error.InvalidRefPacket;
767 const oid = parseOid(data[0..oid_sep_pos]) catch return error.InvalidRefPacket;
893 const oid = Oid.parse(iterator.format, data[0..oid_sep_pos]) catch return error.InvalidRefPacket;
768894
769895 const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len;
770896 const name = ref_data[oid_sep_pos + 1 .. name_sep_pos];
......@@ -778,7 +904,7 @@ pub const Session = struct {
778904 if (mem.startsWith(u8, attribute, "symref-target:")) {
779905 symref_target = attribute["symref-target:".len..];
780906 } else if (mem.startsWith(u8, attribute, "peeled:")) {
781 peeled = parseOid(attribute["peeled:".len..]) catch return error.InvalidRefPacket;
907 peeled = Oid.parse(iterator.format, attribute["peeled:".len..]) catch return error.InvalidRefPacket;
782908 }
783909 last_sep_pos = next_sep_pos;
784910 }
......@@ -814,6 +940,11 @@ pub const Session = struct {
814940 if (session.supports_agent) {
815941 try Packet.write(.{ .data = agent_capability }, body_writer);
816942 }
943 {
944 const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)});
945 defer session.allocator.free(object_format_packet);
946 try Packet.write(.{ .data = object_format_packet }, body_writer);
947 }
817948 try Packet.write(.delimiter, body_writer);
818949 // Our packfile parser supports the OFS_DELTA object type
819950 try Packet.write(.{ .data = "ofs-delta\n" }, body_writer);
......@@ -997,7 +1128,7 @@ const EntryHeader = union(Type) {
9971128 };
9981129 }
9991130
1000 fn read(reader: anytype) !EntryHeader {
1131 fn read(format: Oid.Format, reader: anytype) !EntryHeader {
10011132 const InitialByte = packed struct { len: u4, type: u3, has_next: bool };
10021133 const initial: InitialByte = @bitCast(reader.readByte() catch |e| switch (e) {
10031134 error.EndOfStream => return error.InvalidFormat,
......@@ -1016,7 +1147,7 @@ const EntryHeader = union(Type) {
10161147 .uncompressed_length = uncompressed_length,
10171148 } },
10181149 .ref_delta => .{ .ref_delta = .{
1019 .base_object = reader.readBytesNoEof(oid_length) catch |e| switch (e) {
1150 .base_object = Oid.readBytes(format, reader) catch |e| switch (e) {
10201151 error.EndOfStream => return error.InvalidFormat,
10211152 else => |other| return other,
10221153 },
......@@ -1081,7 +1212,7 @@ const IndexEntry = struct {
10811212
10821213/// Writes out a version 2 index for the given packfile, as documented in
10831214/// [pack-format](https://git-scm.com/docs/pack-format).
1084pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype) !void {
1215pub fn indexPack(allocator: Allocator, format: Oid.Format, pack: std.fs.File, index_writer: anytype) !void {
10851216 try pack.seekTo(0);
10861217
10871218 var index_entries: std.AutoHashMapUnmanaged(Oid, IndexEntry) = .empty;
......@@ -1089,7 +1220,7 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
10891220 var pending_deltas: std.ArrayListUnmanaged(IndexEntry) = .empty;
10901221 defer pending_deltas.deinit(allocator);
10911222
1092 const pack_checksum = try indexPackFirstPass(allocator, pack, &index_entries, &pending_deltas);
1223 const pack_checksum = try indexPackFirstPass(allocator, format, pack, &index_entries, &pending_deltas);
10931224
10941225 var cache: ObjectCache = .{};
10951226 defer cache.deinit(allocator);
......@@ -1099,7 +1230,7 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
10991230 while (i > 0) {
11001231 i -= 1;
11011232 const delta = pending_deltas.items[i];
1102 if (try indexPackHashDelta(allocator, pack, delta, index_entries, &cache)) |oid| {
1233 if (try indexPackHashDelta(allocator, format, pack, delta, index_entries, &cache)) |oid| {
11031234 try index_entries.put(allocator, oid, delta);
11041235 _ = pending_deltas.swapRemove(i);
11051236 }
......@@ -1117,7 +1248,7 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
11171248 }
11181249 mem.sortUnstable(Oid, oids.items, {}, struct {
11191250 fn lessThan(_: void, o1: Oid, o2: Oid) bool {
1120 return mem.lessThan(u8, &o1, &o2);
1251 return mem.lessThan(u8, o1.slice(), o2.slice());
11211252 }
11221253 }.lessThan);
11231254
......@@ -1125,15 +1256,16 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
11251256 var count: u32 = 0;
11261257 var fan_out_index: u8 = 0;
11271258 for (oids.items) |oid| {
1128 if (oid[0] > fan_out_index) {
1129 @memset(fan_out_table[fan_out_index..oid[0]], count);
1130 fan_out_index = oid[0];
1259 const key = oid.slice()[0];
1260 if (key > fan_out_index) {
1261 @memset(fan_out_table[fan_out_index..key], count);
1262 fan_out_index = key;
11311263 }
11321264 count += 1;
11331265 }
11341266 @memset(fan_out_table[fan_out_index..], count);
11351267
1136 var index_hashed_writer = hashedWriter(index_writer, Sha1.init(.{}));
1268 var index_hashed_writer = std.compress.hashedWriter(index_writer, Oid.Hasher.init(format));
11371269 const writer = index_hashed_writer.writer();
11381270 try writer.writeAll(IndexHeader.signature);
11391271 try writer.writeInt(u32, IndexHeader.supported_version, .big);
......@@ -1142,7 +1274,7 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
11421274 }
11431275
11441276 for (oids.items) |oid| {
1145 try writer.writeAll(&oid);
1277 try writer.writeAll(oid.slice());
11461278 }
11471279
11481280 for (oids.items) |oid| {
......@@ -1165,9 +1297,9 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
11651297 try writer.writeInt(u64, offset, .big);
11661298 }
11671299
1168 try writer.writeAll(&pack_checksum);
1300 try writer.writeAll(pack_checksum.slice());
11691301 const index_checksum = index_hashed_writer.hasher.finalResult();
1170 try index_writer.writeAll(&index_checksum);
1302 try index_writer.writeAll(index_checksum.slice());
11711303}
11721304
11731305/// Performs the first pass over the packfile data for index construction.
......@@ -1176,13 +1308,14 @@ pub fn indexPack(allocator: Allocator, pack: std.fs.File, index_writer: anytype)
11761308/// format).
11771309fn indexPackFirstPass(
11781310 allocator: Allocator,
1311 format: Oid.Format,
11791312 pack: std.fs.File,
11801313 index_entries: *std.AutoHashMapUnmanaged(Oid, IndexEntry),
11811314 pending_deltas: *std.ArrayListUnmanaged(IndexEntry),
1182) ![Sha1.digest_length]u8 {
1315) !Oid {
11831316 var pack_buffered_reader = std.io.bufferedReader(pack.reader());
11841317 var pack_counting_reader = std.io.countingReader(pack_buffered_reader.reader());
1185 var pack_hashed_reader = std.compress.hashedReader(pack_counting_reader.reader(), Sha1.init(.{}));
1318 var pack_hashed_reader = std.compress.hashedReader(pack_counting_reader.reader(), Oid.Hasher.init(format));
11861319 const pack_reader = pack_hashed_reader.reader();
11871320
11881321 const pack_header = try PackHeader.read(pack_reader);
......@@ -1191,12 +1324,12 @@ fn indexPackFirstPass(
11911324 while (current_entry < pack_header.total_objects) : (current_entry += 1) {
11921325 const entry_offset = pack_counting_reader.bytes_read;
11931326 var entry_crc32_reader = std.compress.hashedReader(pack_reader, std.hash.Crc32.init());
1194 const entry_header = try EntryHeader.read(entry_crc32_reader.reader());
1327 const entry_header = try EntryHeader.read(format, entry_crc32_reader.reader());
11951328 switch (entry_header) {
11961329 .commit, .tree, .blob, .tag => |object| {
11971330 var entry_decompress_stream = std.compress.zlib.decompressor(entry_crc32_reader.reader());
11981331 var entry_counting_reader = std.io.countingReader(entry_decompress_stream.reader());
1199 var entry_hashed_writer = hashedWriter(std.io.null_writer, Sha1.init(.{}));
1332 var entry_hashed_writer = std.compress.hashedWriter(std.io.null_writer, Oid.Hasher.init(format));
12001333 const entry_writer = entry_hashed_writer.writer();
12011334 // The object header is not included in the pack data but is
12021335 // part of the object's ID
......@@ -1229,8 +1362,8 @@ fn indexPackFirstPass(
12291362 }
12301363
12311364 const pack_checksum = pack_hashed_reader.hasher.finalResult();
1232 const recorded_checksum = try pack_buffered_reader.reader().readBytesNoEof(Sha1.digest_length);
1233 if (!mem.eql(u8, &pack_checksum, &recorded_checksum)) {
1365 const recorded_checksum = try Oid.readBytes(format, pack_buffered_reader.reader());
1366 if (!mem.eql(u8, pack_checksum.slice(), recorded_checksum.slice())) {
12341367 return error.CorruptedPack;
12351368 }
12361369 _ = pack_reader.readByte() catch |e| switch (e) {
......@@ -1245,6 +1378,7 @@ fn indexPackFirstPass(
12451378/// delta and we do not yet know the offset of the base object).
12461379fn indexPackHashDelta(
12471380 allocator: Allocator,
1381 format: Oid.Format,
12481382 pack: std.fs.File,
12491383 delta: IndexEntry,
12501384 index_entries: std.AutoHashMapUnmanaged(Oid, IndexEntry),
......@@ -1259,7 +1393,7 @@ fn indexPackHashDelta(
12591393 if (cache.get(base_offset)) |base_object| break base_object;
12601394
12611395 try pack.seekTo(base_offset);
1262 base_header = try EntryHeader.read(pack.reader());
1396 base_header = try EntryHeader.read(format, pack.reader());
12631397 switch (base_header) {
12641398 .ofs_delta => |ofs_delta| {
12651399 try delta_offsets.append(allocator, base_offset);
......@@ -1279,10 +1413,10 @@ fn indexPackHashDelta(
12791413 }
12801414 };
12811415
1282 const base_data = try resolveDeltaChain(allocator, pack, base_object, delta_offsets.items, cache);
1416 const base_data = try resolveDeltaChain(allocator, format, pack, base_object, delta_offsets.items, cache);
12831417
1284 var entry_hasher = Sha1.init(.{});
1285 var entry_hashed_writer = hashedWriter(std.io.null_writer, &entry_hasher);
1418 var entry_hasher: Oid.Hasher = .init(format);
1419 var entry_hashed_writer = std.compress.hashedWriter(std.io.null_writer, &entry_hasher);
12861420 try entry_hashed_writer.writer().print("{s} {}\x00", .{ @tagName(base_object.type), base_data.len });
12871421 entry_hasher.update(base_data);
12881422 return entry_hasher.finalResult();
......@@ -1294,6 +1428,7 @@ fn indexPackHashDelta(
12941428/// to obtain the final object.
12951429fn resolveDeltaChain(
12961430 allocator: Allocator,
1431 format: Oid.Format,
12971432 pack: std.fs.File,
12981433 base_object: Object,
12991434 delta_offsets: []const u64,
......@@ -1306,7 +1441,7 @@ fn resolveDeltaChain(
13061441
13071442 const delta_offset = delta_offsets[i];
13081443 try pack.seekTo(delta_offset);
1309 const delta_header = try EntryHeader.read(pack.reader());
1444 const delta_header = try EntryHeader.read(format, pack.reader());
13101445 const delta_data = try readObjectRaw(allocator, pack.reader(), delta_header.uncompressedLength());
13111446 defer allocator.free(delta_data);
13121447 var delta_stream = std.io.fixedBufferStream(delta_data);
......@@ -1394,46 +1529,22 @@ fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !vo
13941529 }
13951530}
13961531
1397fn HashedWriter(
1398 comptime WriterType: anytype,
1399 comptime HasherType: anytype,
1400) type {
1401 return struct {
1402 child_writer: WriterType,
1403 hasher: HasherType,
1404
1405 const Error = WriterType.Error;
1406 const Writer = std.io.Writer(*@This(), Error, write);
1407
1408 fn write(hashed_writer: *@This(), buf: []const u8) Error!usize {
1409 const amt = try hashed_writer.child_writer.write(buf);
1410 hashed_writer.hasher.update(buf);
1411 return amt;
1412 }
1413
1414 fn writer(hashed_writer: *@This()) Writer {
1415 return .{ .context = hashed_writer };
1416 }
1417 };
1418}
1419
1420fn hashedWriter(
1421 writer: anytype,
1422 hasher: anytype,
1423) HashedWriter(@TypeOf(writer), @TypeOf(hasher)) {
1424 return .{ .child_writer = writer, .hasher = hasher };
1425}
1426
1427test "packfile indexing and checkout" {
1428 // To verify the contents of this packfile without using the code in this
1429 // file:
1430 //
1431 // 1. Create a new empty Git repository (`git init`)
1432 // 2. `git unpack-objects <path/to/testdata.pack`
1433 // 3. `git fsck` -> note the "dangling commit" ID (which matches the commit
1434 // checked out below)
1435 // 4. `git checkout dd582c0720819ab7130b103635bd7271b9fd4feb`
1436 const testrepo_pack = @embedFile("git/testdata/testrepo.pack");
1532/// Runs the packfile indexing and checkout test.
1533///
1534/// The two testrepo repositories under testdata contain identical commit
1535/// histories and contents.
1536///
1537/// To verify the contents of the packfiles using Git alone, run the
1538/// following commands in an empty directory:
1539///
1540/// 1. `git init --object-format=(sha1|sha256)`
1541/// 2. `git unpack-objects <path/to/testrepo.pack`
1542/// 3. `git fsck` - will print one "dangling commit":
1543/// - SHA-1: `dd582c0720819ab7130b103635bd7271b9fd4feb`
1544/// - SHA-256: `7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a`
1545/// 4. `git checkout $commit`
1546fn runRepositoryTest(comptime format: Oid.Format, head_commit: []const u8) !void {
1547 const testrepo_pack = @embedFile("git/testdata/testrepo-" ++ @tagName(format) ++ ".pack");
14371548
14381549 var git_dir = testing.tmpDir(.{});
14391550 defer git_dir.cleanup();
......@@ -1443,27 +1554,27 @@ test "packfile indexing and checkout" {
14431554
14441555 var index_file = try git_dir.dir.createFile("testrepo.idx", .{ .read = true });
14451556 defer index_file.close();
1446 try indexPack(testing.allocator, pack_file, index_file.writer());
1557 try indexPack(testing.allocator, format, pack_file, index_file.writer());
14471558
14481559 // Arbitrary size limit on files read while checking the repository contents
1449 // (all files in the test repo are known to be much smaller than this)
1450 const max_file_size = 4096;
1560 // (all files in the test repo are known to be smaller than this)
1561 const max_file_size = 8192;
14511562
14521563 const index_file_data = try git_dir.dir.readFileAlloc(testing.allocator, "testrepo.idx", max_file_size);
14531564 defer testing.allocator.free(index_file_data);
14541565 // testrepo.idx is generated by Git. The index created by this file should
14551566 // match it exactly. Running `git verify-pack -v testrepo.pack` can verify
14561567 // this.
1457 const testrepo_idx = @embedFile("git/testdata/testrepo.idx");
1568 const testrepo_idx = @embedFile("git/testdata/testrepo-" ++ @tagName(format) ++ ".idx");
14581569 try testing.expectEqualSlices(u8, testrepo_idx, index_file_data);
14591570
1460 var repository = try Repository.init(testing.allocator, pack_file, index_file);
1571 var repository = try Repository.init(testing.allocator, format, pack_file, index_file);
14611572 defer repository.deinit();
14621573
14631574 var worktree = testing.tmpDir(.{ .iterate = true });
14641575 defer worktree.cleanup();
14651576
1466 const commit_id = try parseOid("dd582c0720819ab7130b103635bd7271b9fd4feb");
1577 const commit_id = try Oid.parse(format, head_commit);
14671578
14681579 var diagnostics: Diagnostics = .{ .allocator = testing.allocator };
14691580 defer diagnostics.deinit();
......@@ -1527,6 +1638,14 @@ test "packfile indexing and checkout" {
15271638 try testing.expectEqualStrings(expected_file_contents, actual_file_contents);
15281639}
15291640
1641test "SHA-1 packfile indexing and checkout" {
1642 try runRepositoryTest(.sha1, "dd582c0720819ab7130b103635bd7271b9fd4feb");
1643}
1644
1645test "SHA-256 packfile indexing and checkout" {
1646 try runRepositoryTest(.sha256, "7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a");
1647}
1648
15301649/// Checks out a commit of a packfile. Intended for experimenting with and
15311650/// benchmarking possible optimizations to the indexing and checkout behavior.
15321651pub fn main() !void {
......@@ -1534,14 +1653,16 @@ pub fn main() !void {
15341653
15351654 const args = try std.process.argsAlloc(allocator);
15361655 defer std.process.argsFree(allocator, args);
1537 if (args.len != 4) {
1538 return error.InvalidArguments; // Arguments: packfile commit worktree
1656 if (args.len != 5) {
1657 return error.InvalidArguments; // Arguments: format packfile commit worktree
15391658 }
15401659
1541 var pack_file = try std.fs.cwd().openFile(args[1], .{});
1660 const format = std.meta.stringToEnum(Oid.Format, args[1]) orelse return error.InvalidFormat;
1661
1662 var pack_file = try std.fs.cwd().openFile(args[2], .{});
15421663 defer pack_file.close();
1543 const commit = try parseOid(args[2]);
1544 var worktree = try std.fs.cwd().makeOpenPath(args[3], .{});
1664 const commit = try Oid.parse(format, args[3]);
1665 var worktree = try std.fs.cwd().makeOpenPath(args[4], .{});
15451666 defer worktree.close();
15461667
15471668 var git_dir = try worktree.makeOpenPath(".git", .{});
......@@ -1551,12 +1672,12 @@ pub fn main() !void {
15511672 var index_file = try git_dir.createFile("idx", .{ .read = true });
15521673 defer index_file.close();
15531674 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());
1554 try indexPack(allocator, pack_file, index_buffered_writer.writer());
1675 try indexPack(allocator, format, pack_file, index_buffered_writer.writer());
15551676 try index_buffered_writer.flush();
15561677 try index_file.sync();
15571678
15581679 std.debug.print("Starting checkout...\n", .{});
1559 var repository = try Repository.init(allocator, pack_file, index_file);
1680 var repository = try Repository.init(allocator, format, pack_file, index_file);
15601681 defer repository.deinit();
15611682 var diagnostics: Diagnostics = .{ .allocator = allocator };
15621683 defer diagnostics.deinit();
src/Package/Fetch/git/testdata/testrepo-sha1.idx created
Binary files /dev/null and b/src/Package/Fetch/git/testdata/testrepo-sha1.idx differ
src/Package/Fetch/git/testdata/testrepo-sha1.pack created
Binary files /dev/null and b/src/Package/Fetch/git/testdata/testrepo-sha1.pack differ
src/Package/Fetch/git/testdata/testrepo-sha256.idx created
Binary files /dev/null and b/src/Package/Fetch/git/testdata/testrepo-sha256.idx differ
src/Package/Fetch/git/testdata/testrepo-sha256.pack created
Binary files /dev/null and b/src/Package/Fetch/git/testdata/testrepo-sha256.pack differ
src/Package/Fetch/git/testdata/testrepo.idx deleted
Binary files a/src/Package/Fetch/git/testdata/testrepo.idx and /dev/null differ
src/Package/Fetch/git/testdata/testrepo.pack deleted
Binary files a/src/Package/Fetch/git/testdata/testrepo.pack and /dev/null differ
src/link/MachO/dyld_info/Rebase.zig+1-1
......@@ -281,7 +281,7 @@ test "rebase - no entries" {
281281 defer rebase.deinit(gpa);
282282
283283 try rebase.finalize(gpa);
284 try testing.expectEqual(@as(u64, 0), rebase.size());
284 try testing.expectEqual(0, rebase.buffer.items.len);
285285}
286286
287287test "rebase - single entry" {
src/main.zig+6-2
......@@ -34,6 +34,10 @@ const Zcu = @import("Zcu.zig");
3434const mingw = @import("mingw.zig");
3535const dev = @import("dev.zig");
3636
37test {
38 _ = Package;
39}
40
3741pub const std_options: std.Options = .{
3842 .wasiCwd = wasi_cwd,
3943 .logFn = log,
......@@ -7033,8 +7037,8 @@ fn cmdFetch(
70337037
70347038 var saved_path_or_url = path_or_url;
70357039
7036 if (fetch.latest_commit) |*latest_commit| resolved: {
7037 const latest_commit_hex = try std.fmt.allocPrint(arena, "{}", .{std.fmt.fmtSliceHexLower(latest_commit)});
7040 if (fetch.latest_commit) |latest_commit| resolved: {
7041 const latest_commit_hex = try std.fmt.allocPrint(arena, "{}", .{latest_commit});
70387042
70397043 var uri = try std.Uri.parse(path_or_url);
70407044
test/tests.zig+4
......@@ -1303,6 +1303,7 @@ const ModuleTestOptions = struct {
13031303 skip_libc: bool,
13041304 max_rss: usize = 0,
13051305 no_builtin: bool = false,
1306 build_options: ?*std.Build.Step.Options = null,
13061307};
13071308
13081309pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
......@@ -1392,6 +1393,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
13921393 .strip = test_target.strip,
13931394 });
13941395 if (options.no_builtin) these_tests.no_builtin = true;
1396 if (options.build_options) |build_options| {
1397 these_tests.root_module.addOptions("build_options", build_options);
1398 }
13951399 const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else "";
13961400 const backend_suffix = if (test_target.use_llvm == true)
13971401 "-llvm"