commit 040307b324692a6146ddcb903f190879547dea7a
parent 813a8b355a68f691969c4ed9375368f0049e4f6d
Author: Martin Ashby <martin@ashbysoft.com>
Date: Mon, 26 Aug 2024 22:02:29 +0100
Implement show-ref
Diffstat:
M | src/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" {