commit 26a70bb0dd29c550f1f4e6f0c8e8a69ec565615e
parent 06bbaa657da60958a0ac2629b36a72ad6beaed53
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sat, 10 Aug 2024 23:19:32 +0100
Work on GitRepository structure
Diffstat:
| M | src/root.zig |  |  | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- | 
1 file changed, 56 insertions(+), 20 deletions(-)
diff --git a/src/root.zig b/src/root.zig
@@ -1,5 +1,6 @@
 const std = @import("std");
 const argparse = @import("argparse.zig");
+const IniFile = @import("inifile.zig");
 
 pub fn doMain() !void {
     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@@ -21,41 +22,76 @@ pub fn doMain() !void {
     }
 }
 
-const GitConfig = struct {};
-
 pub const GitRepository = struct {
     worktree: []const u8,
     gitdir: []const u8,
-    conf: GitConfig,
+    conf: IniFile,
     _aa: std.heap.ArenaAllocator,
 
-    pub const InitError = error{
-        OutOfMemory,
-        NotAGitRepo,
-        FsError,
-    };
+    pub fn init(ca: std.mem.Allocator, path: []const u8, force: bool) !GitRepository {
+        var self: GitRepository = undefined;
+        self._aa = std.heap.ArenaAllocator.init(ca);
+        errdefer self.deinit();
 
-    pub fn init(ca: std.mem.Allocator, path: []const u8, force: bool) InitError!GitRepository {
-        const aa = std.heap.ArenaAllocator.init(ca);
-        errdefer aa.deinit();
-        const a = aa.allocator();
+        self.worktree = path;
 
+        const a = self._aa.allocator();
+        const cwd = std.fs.cwd();
         const gitdirpath = try std.fs.path.join(a, &.{ path, ".git" });
         if (!force) {
-            std.fs.cwd().openDir(gitdirpath, .{}) catch |e| switch (e) {
+            var gitdir = cwd.openDir(gitdirpath, .{}) catch |e| switch (e) {
                 error.FileNotFound, error.NotDir => return error.NotAGitRepo,
-                else => error.FsError,
+                else => return error.FsError,
             };
+            defer gitdir.close();
         }
-
-        return .{
-            ._aa = aa,
-            .worktree = path,
-            .gitdir = gitdirpath,
-        };
+        self.gitdir = gitdirpath;
+        const gitconfigpath = try std.fs.path.join(a, &.{ gitdirpath, "config" });
+        const configcontent = try cwd.readFileAlloc(a, gitconfigpath, 10_000_000);
+        defer a.free(configcontent);
+        self.conf = try IniFile.parse(a, configcontent);
+        return self;
     }
 
     pub fn deinit(self: *GitRepository) void {
         self._aa.deinit();
     }
+
+    /// Compute path under repo's gitdir.
+    fn repo_path(self: GitRepository, path: []const u8) ![]const u8 {
+        return try std.fs.path.join(self._aa.allocator(), &.{ self.gitdir, path });
+    }
+
+    /// Same as repo_path, but create dirname(*path) if absent.  For
+    /// example, repo_file(r, \"refs\", \"remotes\", \"origin\", \"HEAD\") will create
+    /// .git/refs/remotes/origin."""
+    fn repo_file(self: GitRepository, path: []const u8, mkdir: bool) !?[]const u8 {
+        const dirname = std.fs.path.dirname(path) orelse return error.EmptyPath;
+        if (try self.repo_dir(dirname, mkdir)) |_| {
+            return try repo_path(self, path);
+        } else {
+            return null;
+        }
+    }
+
+    /// Same as repo_path, but mkdir *path if absent if mkdir.
+    fn repo_dir(self: GitRepository, path: []const u8, mkdir: bool) !?[]const u8 {
+        const p = try self.repo_path(path);
+        if (mkdir) {
+            try std.fs.cwd().makePath(p);
+            return p;
+        } else {
+            const dir = std.fs.cwd().openDir(p, .{}) catch |e| switch (e) {
+                error.FileNotFound => return null,
+                else => return e,
+            };
+            dir.close();
+            return p;
+        }
+    }
 };
+
+test "init repo" {
+    var gr = try GitRepository.init(std.testing.allocator, ".", false);
+    defer gr.deinit();
+}