wyag

Write yourself a git
Log | Files | Refs | README

commit 929cfb179ecc9f7fe6c577181b158b6b9c63caf3
parent 104c4784208a32b03cdc2bafaa61eeeb8e788f1c
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sun, 11 Aug 2024 22:40:26 +0100

add repo_find function
Use std.fs.Dir instead of path in a few places where it makes more sense
to use dir directly.

Diffstat:
Msrc/root.zig | 54++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/src/root.zig b/src/root.zig @@ -41,13 +41,15 @@ pub const GitRepository = struct { conf: IniFile, _aa: std.heap.ArenaAllocator, - pub fn init(ca: std.mem.Allocator, path: []const u8) !GitRepository { + // Note: takes 'ownership' of dir; callers should not use it again (including closing it) after calling + // this function. + pub fn init(ca: std.mem.Allocator, dir: Dir) !GitRepository { var self: GitRepository = undefined; self._aa = std.heap.ArenaAllocator.init(ca); errdefer self._aa.deinit(); const a = self._aa.allocator(); - self.worktree = try std.fs.cwd().openDir(path, .{ .iterate = true }); + self.worktree = dir; errdefer self.worktree.close(); self.gitdir = try self.worktree.openDir(".git", .{ .iterate = true }); errdefer self.gitdir.close(); @@ -58,25 +60,26 @@ pub const GitRepository = struct { } pub fn deinit(self: *GitRepository) void { - self.gitdir.close(); - self.worktree.close(); + safeclose(&self.gitdir); + safeclose(&self.worktree); self._aa.deinit(); } }; test "init repo" { - var gr = try GitRepository.init(std.testing.allocator, ".", false); + var gr = try GitRepository.init(std.testing.allocator, std.fs.cwd()); defer gr.deinit(); } fn repo_create(ca: std.mem.Allocator, path: []const u8) !GitRepository { + var worktree: Dir = undefined; { var aa = std.heap.ArenaAllocator.init(ca); defer aa.deinit(); const a = aa.allocator(); const cwd = std.fs.cwd(); - var worktree = try cwd.makeOpenPath(path, .{}); - defer worktree.close(); + worktree = try cwd.makeOpenPath(path, .{}); + errdefer worktree.close(); var gitdir = try worktree.makeOpenPath(".git", .{}); defer gitdir.close(); try gitdir.makePath("branches"); @@ -95,5 +98,40 @@ fn repo_create(ca: std.mem.Allocator, path: []const u8) !GitRepository { \\ bare = false ); } - return try GitRepository.init(ca, path); + return try GitRepository.init(ca, worktree); } + +// takes ownership of "dir", the variable should not be used +// by any other code after calling this function. +fn repo_find(a: std.mem.Allocator, dir: Dir) !GitRepository { + const stat = dir.statFile(".git") catch |e| switch (e) { + error.FileNotFound => { + // try the parent + var parentdir = dir.openDir("..", .{.iterate = true}) catch |e2| switch (e2) { + error.FileNotFound => return error.NoGitDirFound, + else => return e2, + }; + errdefer parentdir.close(); + return repo_find(a, parentdir); + }, + else => return e, + }; + if (stat.kind == .directory) { + return try GitRepository.init(a, dir); + } else { + return error.NoGitDirFound; + } +} + +fn safeclose(dir: *Dir) void { + if (std.fs.cwd().fd != dir.fd) { + dir.close(); + } +} + +test "repo_find" { + const srcdir = try std.fs.cwd().openDir("src/foo/bar/baz", .{.iterate = true}); + var gr = try repo_find(std.testing.allocator, srcdir); + defer gr.deinit(); +} +