aboutsummaryrefslogtreecommitdiff
path: root/src/git.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/git.zig')
-rw-r--r--src/git.zig209
1 files changed, 209 insertions, 0 deletions
diff --git a/src/git.zig b/src/git.zig
new file mode 100644
index 0000000..4803103
--- /dev/null
+++ b/src/git.zig
@@ -0,0 +1,209 @@
+const std = @import("std");
+const testing = std.testing;
+const Allocator = std.mem.Allocator;
+const galloc = @import("galloc.zig");
+
+const git = @cImport({
+ @cInclude("git2.h");
+});
+
+const GitError = error{
+ RepositoryAlreadyInitialized,
+ Unkown,
+
+ //libgit specific error
+ GitError,
+ GitNotfound,
+ GitExists,
+ GitAmbiguous,
+ GitBufs,
+ GitUser,
+ GitUnbornBranch,
+ GitUnmerged,
+ GitNonFastForward,
+ GitInvalidSpec,
+ GitConflict,
+ GitLocked,
+ GitModified,
+ GitAuth,
+ GitCertificate,
+ GitApplied,
+ GitPeel,
+ GitEof,
+ GitInvalid,
+ GitUncommitted,
+ GitDirectory,
+ GitMergeConflict,
+ GitPassthrough,
+ GitIterOver,
+ GitRetry,
+ GitMismatch,
+ GitIndexDirty,
+ GitApplyFail,
+ GitOwner,
+ GitTimeout,
+ GitUnchanged,
+ GitNotSupported,
+ GitReadonly,
+};
+
+var alloc: galloc.GitAllocator = undefined;
+
+fn malloc(size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque {
+ return alloc.malloc(size);
+}
+
+fn relloc(ptr: ?*anyopaque, size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque {
+ const new_ptr = alloc.realloc(ptr, size);
+ return new_ptr;
+}
+
+fn free(ptr: ?*anyopaque) callconv(.C) void {
+ alloc.free(ptr);
+}
+
+fn err(code: c_int) GitError!void {
+ if (code >= 0) return;
+
+ return switch (code) {
+ git.GIT_ERROR => GitError.GitError,
+ git.GIT_ENOTFOUND => GitError.GitNotfound,
+ git.GIT_EEXISTS => GitError.GitExists,
+ git.GIT_EAMBIGUOUS => GitError.GitAmbiguous,
+ git.GIT_EBUFS => GitError.GitBufs,
+ git.GIT_EUSER => GitError.GitUser,
+ git.GIT_EUNBORNBRANCH => GitError.GitUnbornBranch,
+ git.GIT_EUNMERGED => GitError.GitUnmerged,
+ git.GIT_ENONFASTFORWARD => GitError.GitNonFastForward,
+ git.GIT_EINVALIDSPEC => GitError.GitInvalidSpec,
+ git.GIT_ECONFLICT => GitError.GitConflict,
+ git.GIT_ELOCKED => GitError.GitLocked,
+ git.GIT_EMODIFIED => GitError.GitModified,
+ git.GIT_EAUTH => GitError.GitAuth,
+ git.GIT_ECERTIFICATE => GitError.GitCertificate,
+ git.GIT_EAPPLIED => GitError.GitApplied,
+ git.GIT_EPEEL => GitError.GitPeel,
+ git.GIT_EEOF => GitError.GitEof,
+ git.GIT_EINVALID => GitError.GitInvalid,
+ git.GIT_EUNCOMMITTED => GitError.GitUncommitted,
+ git.GIT_EDIRECTORY => GitError.GitDirectory,
+ git.GIT_EMERGECONFLICT => GitError.GitMergeConflict,
+ git.GIT_PASSTHROUGH => GitError.GitPassthrough,
+ git.GIT_ITEROVER => GitError.GitIterOver,
+ git.GIT_RETRY => GitError.GitRetry,
+ git.GIT_EMISMATCH => GitError.GitMismatch,
+ git.GIT_EINDEXDIRTY => GitError.GitIndexDirty,
+ git.GIT_EAPPLYFAIL => GitError.GitApplyFail,
+ git.GIT_EOWNER => GitError.GitOwner,
+ git.GIT_TIMEOUT => GitError.GitTimeout,
+ git.GIT_EUNCHANGED => GitError.GitUnchanged,
+ git.GIT_ENOTSUPPORTED => GitError.GitNotSupported,
+ git.GIT_EREADONLY => GitError.GitReadonly,
+ else => GitError.Unkown,
+ };
+}
+
+const git_allocator = extern struct {
+ gmalloc: ?*const fn (size: usize, payload: ?*anyopaque) callconv(.C) ?*anyopaque,
+ grealloc: ?*const fn (ptr: ?*anyopaque, size: usize, payload: ?*anyopaque) callconv(.C) ?*anyopaque,
+ gfree: ?*const fn (ptr: ?*anyopaque) callconv(.C) void,
+};
+
+pub fn init(a: std.mem.Allocator) GitError!void {
+ alloc = galloc.GitAllocator.init(a);
+
+ const cAlloc = git_allocator{
+ .gmalloc = malloc,
+ .grealloc = relloc,
+ .gfree = free,
+ };
+
+ var code = git.git_libgit2_opts(git.GIT_OPT_SET_ALLOCATOR, &cAlloc);
+ try err(code);
+
+ code = git.git_libgit2_init();
+ try err(code);
+}
+
+pub fn deinit() !void {
+ defer alloc.deinit();
+ try err(git.git_libgit2_shutdown());
+}
+
+pub const Repository = struct {
+ repository: ?*git.git_repository = null,
+
+ fn validateInit(self: *Repository) GitError!void {
+ if (self.repository != null)
+ return GitError.RepositoryAlreadyInitialized;
+ }
+
+ pub fn open(self: *Repository, path: []const u8) GitError!void {
+ try self.validateInit();
+ try err(git.git_repository_open(@ptrCast(&self.repository), path.ptr));
+ }
+
+ pub fn init(self: *Repository, path: []const u8, bare: bool) GitError!void {
+ try self.validateInit();
+ try err(git.git_repository_init(@ptrCast(&self.repository), path.ptr, if (bare) 1 else 0));
+ }
+
+ pub fn deinit(self: *Repository) void {
+ if (self.repository) |repo| {
+ git.git_repository_free(repo);
+ }
+ }
+};
+
+test "init deinit" {
+ try init(testing.allocator);
+ try deinit();
+}
+
+test "open repository" {
+ try init(testing.allocator);
+ defer deinit() catch {};
+
+ var repository = Repository{};
+ defer repository.deinit();
+
+ try repository.open(".");
+}
+
+test "init repository" {
+ var tmp_dir = testing.tmpDir(.{});
+ defer tmp_dir.cleanup();
+
+ const full_path = try tmp_dir.dir.realpathAlloc(testing.allocator, ".");
+ defer testing.allocator.free(full_path);
+
+ try init(testing.allocator);
+ defer deinit() catch {};
+
+ var repository = Repository{};
+ defer repository.deinit();
+
+ try repository.init(full_path, false);
+}
+
+test "init repository bare" {
+ var tmp_dir = testing.tmpDir(.{});
+ defer tmp_dir.cleanup();
+
+ const full_path = try tmp_dir.dir.realpathAlloc(testing.allocator, ".");
+ defer testing.allocator.free(full_path);
+
+ try init(testing.allocator);
+ defer deinit() catch {};
+
+ var repository = Repository{};
+ defer repository.deinit();
+
+ try repository.init(full_path, false);
+
+ // try opening the repository to test if it is properly created.
+ var tmp = Repository{};
+ defer tmp.deinit();
+
+ try tmp.open(full_path);
+}