wyag

Write yourself a git
Log | Files | Refs | README

commit 040307b324692a6146ddcb903f190879547dea7a
parent 813a8b355a68f691969c4ed9375368f0049e4f6d
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Mon, 26 Aug 2024 22:02:29 +0100

Implement show-ref

Diffstat:
Msrc/root.zig | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+), 0 deletions(-)

diff --git a/src/root.zig b/src/root.zig @@ -50,6 +50,9 @@ pub fn doMain() !void { var checkout_directory: argparse.Positional = .{ .name = "directory", .description = "The directory to checkout into" }; try checkout.addPositional(&checkout_directory); + var show_ref: argparse.Subcommand = .{ .parent = &ap, .description = "List references in a local repository", .name = "show-ref" }; + try ap.addSubcommand(&show_ref); + if (!try ap.parseOrHelp()) { return; } @@ -102,6 +105,8 @@ pub fn doMain() !void { } else { std.log.err("No commit supplied to checkout", .{}); } + } else if (show_ref.wasExecuted) { + try doShowRef(a, std.fs.cwd(), std.io.getStdOut().writer()); } else { if (ap.excess.items.len > 0) { std.log.err("Unsupported sub-command {s}, have you tried implementing it yourself?", .{ap.excess.items[0]}); @@ -112,6 +117,23 @@ pub fn doMain() !void { } } +fn doShowRef(a: std.mem.Allocator, dir: std.fs.Dir, writer: anytype) !void { + var repo = try repo_find(a, dir); + defer repo.deinit(); + var refs = try repo.ref_list(a); + defer { + var it = refs.iterator(); + while (it.next()) |e| { + a.free(e.key_ptr.*); + } + refs.deinit(); + } + var it = refs.iterator(); + while (it.next()) |e| { + try std.fmt.format(writer, "{} refs/{s}\n", .{ std.fmt.fmtSliceHexLower(&(e.value_ptr.*)), e.key_ptr.* }); + } +} + fn doCheckout(a: std.mem.Allocator, dir: std.fs.Dir, commit_ref: []const u8, checkout_directory: []const u8) !void { var repo = try repo_find(a, dir); defer repo.deinit(); @@ -453,6 +475,57 @@ pub const GitRepository = struct { } return try ca.dupe(u8, ref); } + + pub fn resolve(self: GitRepository, a: std.mem.Allocator, ref: []const u8) ![20]u8 { + const ref_file_path = try std.fs.path.join(a, &.{ "refs", ref }); + defer a.free(ref_file_path); + const ref_file_content = try self.gitdir.readFileAlloc(a, ref_file_path, 1024); + defer a.free(ref_file_content); + if (std.mem.startsWith(u8, ref_file_content, "ref: ")) { + return try resolve(self, a, ref_file_content[5..]); + } else { + var res: [20]u8 = undefined; + _ = try std.fmt.hexToBytes(&res, std.mem.trim(u8, ref_file_content, &std.ascii.whitespace)); + return res; + } + } + + // Caller owns the map and the keys to the map. + pub fn ref_list(self: GitRepository, a: std.mem.Allocator) !std.StringArrayHashMap([20]u8) { + var res = std.StringArrayHashMap([20]u8).init(a); + errdefer { + var it = res.iterator(); + while (it.next()) |e| { + a.free(e.key_ptr.*); + } + res.deinit(); + } + + const ref_dir = try self.gitdir.openDir("refs", .{ .iterate = true }); + var walker = try ref_dir.walk(a); + defer walker.deinit(); + while (try walker.next()) |we| { + if (we.kind == .file) { + const ref = try self.resolve(a, we.path); + const key = try a.dupe(u8, we.path); + errdefer a.free(key); + try res.put(key, ref); + } + } + //`fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool` + const Srt = struct { + map: std.StringArrayHashMap([20]u8), + pub fn lessThan(slf: *@This(), ai: usize, bi: usize) bool { + const keys = slf.map.keys(); + const ka = keys[ai]; + const kb = keys[bi]; + return std.mem.lessThan(u8, ka, kb); + } + }; + var srt = Srt{ .map = res }; + res.sort(&srt); + return res; + } }; test "init repo" {