commit 871ebbae8d66341336b5e29535300d7c284d0fa1
parent 040307b324692a6146ddcb903f190879547dea7a
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Tue, 27 Aug 2024 22:01:09 +0100
Implement tag listing
Rename GitTree -> Tree to be more consistent
Diffstat:
| M | src/root.zig | | | 177 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ | 
1 file changed, 110 insertions(+), 67 deletions(-)
diff --git a/src/root.zig b/src/root.zig
@@ -53,6 +53,15 @@ pub fn doMain() !void {
     var show_ref: argparse.Subcommand = .{ .parent = &ap, .description = "List references in a local repository", .name = "show-ref" };
     try ap.addSubcommand(&show_ref);
 
+    var tag: argparse.Subcommand = .{ .parent = &ap, .description = "Manage tags", .name = "tag" };
+    try ap.addSubcommand(&tag);
+    var tag_name: argparse.Positional = .{ .name = "name", .description = "name for the new tag" };
+    try tag.addPositional(&tag_name);
+    var tag_object: argparse.Positional = .{ .name = "object", .description = "the object to add a tag for" };
+    try tag.addPositional(&tag_object);
+    var tag_kind: argparse.Flag = .{ .long = "add", .short = "a", .description = "whether to create a tag object", .hasarg = false };
+    try tag.addFlag(&tag_kind);
+
     if (!try ap.parseOrHelp()) {
         return;
     }
@@ -107,6 +116,12 @@ pub fn doMain() !void {
         }
     } else if (show_ref.wasExecuted) {
         try doShowRef(a, std.fs.cwd(), std.io.getStdOut().writer());
+    } else if (tag.wasExecuted) {
+        if (tag_name.value) |tagname| {
+            try doAddTag(a, std.fs.cwd(), std.io.getStdOut().writer(), tagname, tag_object.value);
+        } else {
+            try doListTags(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]});
@@ -117,6 +132,33 @@ pub fn doMain() !void {
     }
 }
 
+fn doListTags(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 {
+        // TODO could do with a more convenient wrapper probably, maybe even an arena
+        for (refs.keys()) |k| a.free(k);
+        refs.deinit();
+    }
+    for (refs.keys()) |k| {
+        // tags/
+        const tags_prefix = "tags/";
+        if (std.mem.startsWith(u8, k, tags_prefix)) {
+            try std.fmt.format(writer, "{s}\n", .{k[tags_prefix.len..]});
+        }
+    }
+}
+
+fn doAddTag(a: std.mem.Allocator, dir: std.fs.Dir, writer: anytype, tagname: []const u8, tagobj: ?[]const u8) !void {
+    _ = a;
+    _ = dir;
+    _ = writer;
+    _ = tagname;
+    _ = tagobj;
+    return error.Unimplemented;
+}
+
 fn doShowRef(a: std.mem.Allocator, dir: std.fs.Dir, writer: anytype) !void {
     var repo = try repo_find(a, dir);
     defer repo.deinit();
@@ -154,7 +196,7 @@ fn doCheckout(a: std.mem.Allocator, dir: std.fs.Dir, commit_ref: []const u8, che
 
 fn doCheckoutInternal(a: std.mem.Allocator, repo: *GitRepository, go: *GitObject, dir: std.fs.Dir) !void {
     if (go.kind != .tree) return error.NotATree;
-    var gt = try GitTree.parse(a, go.reader());
+    var gt = try Tree.parse(a, go.reader());
     defer gt.deinit();
     for (gt.leaves.items) |leaf| {
         var go2 = try repo.read_object_sha(a, leaf.sha);
@@ -199,7 +241,7 @@ fn lsTree(a: std.mem.Allocator, dir: Dir, ref: []const u8, writer: anytype, recu
 
 fn lsTreeInternal(a: std.mem.Allocator, repo: *GitRepository, go: *GitObject, writer: anytype, recurse: bool, path_prefix: []const u8) !void {
     if (go.kind != .tree) return error.NotATree;
-    var gt = try GitTree.parse(a, go.reader());
+    var gt = try Tree.parse(a, go.reader());
     defer gt.deinit();
     for (gt.leaves.items) |leaf| {
         const filetype_str = switch (leaf.filetype) {
@@ -512,7 +554,6 @@ pub const GitRepository = struct {
                 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 {
@@ -588,65 +629,6 @@ const ObjectKind = enum {
     blob,
 };
 
-const Commit = struct {
-    _kvlm: Kvlm,
-    tree: []const u8,
-    parents: std.ArrayListUnmanaged([]const u8),
-    author: []const u8,
-    committer: ?[]const u8,
-    gpgsig: ?[]const u8,
-    message: []const u8,
-
-    pub fn parse(a: std.mem.Allocator, z_reader: anytype) !Commit {
-        var kvlm = try Kvlm.parse(a, z_reader);
-        errdefer kvlm.deinit(a);
-        return .{
-            ._kvlm = kvlm,
-            .tree = if (kvlm.headers.get("tree")) |tree| tree.items[0] else return error.InvalidCommit,
-            .parents = kvlm.headers.get("parent") orelse .{},
-            .author = if (kvlm.headers.get("author")) |tree| tree.items[0] else return error.InvalidCommit,
-            .committer = if (kvlm.headers.get("committer")) |tree| tree.items[0] else null,
-            .gpgsig = if (kvlm.headers.get("gpgsig")) |tree| tree.items[0] else null,
-            .message = kvlm.message,
-        };
-    }
-
-    pub fn deinit(self: *Commit, a: std.mem.Allocator) void {
-        self._kvlm.deinit(a);
-    }
-};
-
-test "parse commit" {
-    const commit_str =
-        \\tree 29ff16c9c14e2652b22f8b78bb08a5a07930c147
-        \\parent 206941306e8a8af65b66eaaaea388a7ae24d49a0
-        \\author Thibault Polge <thibault@thb.lt> 1527025023 +0200
-        \\committer Thibault Polge <thibault@thb.lt> 1527025044 +0200
-        \\gpgsig -----BEGIN PGP SIGNATURE-----
-        \\ 
-        \\ iQIzBAABCAAdFiEExwXquOM8bWb4Q2zVGxM2FxoLkGQFAlsEjZQACgkQGxM2FxoL
-        \\ kGQdcBAAqPP+ln4nGDd2gETXjvOpOxLzIMEw4A9gU6CzWzm+oB8mEIKyaH0UFIPh
-        \\ rNUZ1j7/ZGFNeBDtT55LPdPIQw4KKlcf6kC8MPWP3qSu3xHqx12C5zyai2duFZUU
-        \\ wqOt9iCFCscFQYqKs3xsHI+ncQb+PGjVZA8+jPw7nrPIkeSXQV2aZb1E68wa2YIL
-        \\ 3eYgTUKz34cB6tAq9YwHnZpyPx8UJCZGkshpJmgtZ3mCbtQaO17LoihnqPn4UOMr
-        \\ V75R/7FjSuPLS8NaZF4wfi52btXMSxO/u7GuoJkzJscP3p4qtwe6Rl9dc1XC8P7k
-        \\ NIbGZ5Yg5cEPcfmhgXFOhQZkD0yxcJqBUcoFpnp2vu5XJl2E5I/quIyVxUXi6O6c
-        \\ /obspcvace4wy8uO0bdVhc4nJ+Rla4InVSJaUaBeiHTW8kReSFYyMmDCzLjGIu1q
-        \\ doU61OM3Zv1ptsLu3gUE6GU27iWYj2RWN3e3HE4Sbd89IFwLXNdSuM0ifDLZk7AQ
-        \\ WBhRhipCCgZhkj9g2NEk7jRVslti1NdN5zoQLaJNqSwO1MtxTmJ15Ksk3QP6kfLB
-        \\ Q52UWybBzpaP9HEd4XnR+HuQ4k2K0ns2KgNImsNvIyFwbpMUyUWLMPimaV1DWUXo
-        \\ 5SBjDB/V/W2JBFR+XKHFJeFwYhj7DD/ocsGr4ZMx/lgc8rjIBkI=
-        \\ =lgTX
-        \\ -----END PGP SIGNATURE-----
-        \\
-        \\Create first draft
-    ;
-    var fbs = std.io.fixedBufferStream(commit_str);
-    const rdr = fbs.reader();
-    var commit = try Commit.parse(std.testing.allocator, rdr);
-    defer commit.deinit(std.testing.allocator);
-}
-
 const Kvlm = struct {
     headers: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged([]const u8)),
     message: []const u8,
@@ -733,7 +715,68 @@ const Kvlm = struct {
     }
 };
 
-pub const GitTree = struct {
+const Commit = struct {
+    _kvlm: Kvlm,
+    tree: []const u8,
+    parents: std.ArrayListUnmanaged([]const u8),
+    author: []const u8,
+    committer: ?[]const u8,
+    gpgsig: ?[]const u8,
+    message: []const u8,
+
+    pub fn parse(a: std.mem.Allocator, z_reader: anytype) !Commit {
+        var kvlm = try Kvlm.parse(a, z_reader);
+        errdefer kvlm.deinit(a);
+        return .{
+            ._kvlm = kvlm,
+            .tree = if (kvlm.headers.get("tree")) |tree| tree.items[0] else return error.InvalidCommit,
+            .parents = kvlm.headers.get("parent") orelse .{},
+            .author = if (kvlm.headers.get("author")) |tree| tree.items[0] else return error.InvalidCommit,
+            .committer = if (kvlm.headers.get("committer")) |tree| tree.items[0] else null,
+            .gpgsig = if (kvlm.headers.get("gpgsig")) |tree| tree.items[0] else null,
+            .message = kvlm.message,
+        };
+    }
+
+    pub fn deinit(self: *Commit, a: std.mem.Allocator) void {
+        self._kvlm.deinit(a);
+    }
+};
+
+const Tag = Commit; // same structure and everything
+
+test "parse commit" {
+    const commit_str =
+        \\tree 29ff16c9c14e2652b22f8b78bb08a5a07930c147
+        \\parent 206941306e8a8af65b66eaaaea388a7ae24d49a0
+        \\author Thibault Polge <thibault@thb.lt> 1527025023 +0200
+        \\committer Thibault Polge <thibault@thb.lt> 1527025044 +0200
+        \\gpgsig -----BEGIN PGP SIGNATURE-----
+        \\ 
+        \\ iQIzBAABCAAdFiEExwXquOM8bWb4Q2zVGxM2FxoLkGQFAlsEjZQACgkQGxM2FxoL
+        \\ kGQdcBAAqPP+ln4nGDd2gETXjvOpOxLzIMEw4A9gU6CzWzm+oB8mEIKyaH0UFIPh
+        \\ rNUZ1j7/ZGFNeBDtT55LPdPIQw4KKlcf6kC8MPWP3qSu3xHqx12C5zyai2duFZUU
+        \\ wqOt9iCFCscFQYqKs3xsHI+ncQb+PGjVZA8+jPw7nrPIkeSXQV2aZb1E68wa2YIL
+        \\ 3eYgTUKz34cB6tAq9YwHnZpyPx8UJCZGkshpJmgtZ3mCbtQaO17LoihnqPn4UOMr
+        \\ V75R/7FjSuPLS8NaZF4wfi52btXMSxO/u7GuoJkzJscP3p4qtwe6Rl9dc1XC8P7k
+        \\ NIbGZ5Yg5cEPcfmhgXFOhQZkD0yxcJqBUcoFpnp2vu5XJl2E5I/quIyVxUXi6O6c
+        \\ /obspcvace4wy8uO0bdVhc4nJ+Rla4InVSJaUaBeiHTW8kReSFYyMmDCzLjGIu1q
+        \\ doU61OM3Zv1ptsLu3gUE6GU27iWYj2RWN3e3HE4Sbd89IFwLXNdSuM0ifDLZk7AQ
+        \\ WBhRhipCCgZhkj9g2NEk7jRVslti1NdN5zoQLaJNqSwO1MtxTmJ15Ksk3QP6kfLB
+        \\ Q52UWybBzpaP9HEd4XnR+HuQ4k2K0ns2KgNImsNvIyFwbpMUyUWLMPimaV1DWUXo
+        \\ 5SBjDB/V/W2JBFR+XKHFJeFwYhj7DD/ocsGr4ZMx/lgc8rjIBkI=
+        \\ =lgTX
+        \\ -----END PGP SIGNATURE-----
+        \\
+        \\Create first draft
+    ;
+    var fbs = std.io.fixedBufferStream(commit_str);
+    const rdr = fbs.reader();
+    var commit = try Commit.parse(std.testing.allocator, rdr);
+    defer commit.deinit(std.testing.allocator);
+}
+
+pub const Tree = struct {
     pub const Leaf = struct {
         pub const FileType = enum {
             file,
@@ -761,8 +804,8 @@ pub const GitTree = struct {
     aa: std.heap.ArenaAllocator,
     leaves: std.ArrayListUnmanaged(Leaf) = .{},
 
-    pub fn parse(ca: std.mem.Allocator, reader: anytype) !GitTree {
-        var result: GitTree = .{
+    pub fn parse(ca: std.mem.Allocator, reader: anytype) !Tree {
+        var result: Tree = .{
             .aa = std.heap.ArenaAllocator.init(ca),
         };
         errdefer result.aa.deinit();
@@ -787,7 +830,7 @@ pub const GitTree = struct {
         return result;
     }
 
-    pub fn deinit(self: *GitTree) void {
+    pub fn deinit(self: *Tree) void {
         self.aa.deinit();
     }
 };
@@ -795,7 +838,7 @@ pub const GitTree = struct {
 test "parse tree" {
     const a = std.testing.allocator;
     var fbs = std.io.fixedBufferStream(@embedFile("sample.tree"));
-    var tree = try GitTree.parse(a, fbs.reader());
+    var tree = try Tree.parse(a, fbs.reader());
     defer tree.deinit();
 }