wyag

Write yourself a git
Log | Files | Refs | README

commit 26a70bb0dd29c550f1f4e6f0c8e8a69ec565615e
parent 06bbaa657da60958a0ac2629b36a72ad6beaed53
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sat, 10 Aug 2024 23:19:32 +0100

Work on GitRepository structure

Diffstat:
Msrc/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(); +}