/* git2.c * * Simple binding to the parts of libgit2 in use by Gall. * * Copyright 2014 Daniel Silverstone */ #include #include #include "git2.h" typedef struct { git_repository *repo; git_odb *odb; } repo_et_al_t; static inline git_repository *to_repo(lua_State *L, int idx) { repo_et_al_t *real = lua_touserdata(L, idx); if (real == NULL) return NULL; return real->repo; } static inline git_odb *to_odb(lua_State *L, int idx) { repo_et_al_t *real = lua_touserdata(L, idx); if (real == NULL) return NULL; return real->odb; } /* Push the most recent error from libgit2 */ static int push_git2_error(lua_State *L) { const git_error *err = giterr_last(); lua_pushnil(L); lua_pushstring(L, err->message); return 2; } static int L_gc_repo(lua_State *L) { repo_et_al_t *real = lua_touserdata(L, 1); if (real->odb != NULL) git_odb_free(real->odb); if (real->repo != NULL) git_repository_free(real->repo); real->repo = NULL; real->odb = NULL; return 0; } /* Trivial Repository binding, GC will free the repo */ static int L_open_repo(lua_State *L) { const char *repopath = luaL_checkstring(L, 1); int ret; repo_et_al_t *real = lua_newuserdata(L, sizeof(*real)); real->repo = NULL; real->odb = NULL; ret = git_repository_open(&real->repo, repopath); if (ret != 0) { return push_git2_error(L); } ret = git_repository_odb(&real->odb, real->repo); if (ret != 0) { push_git2_error(L); git_repository_free(real->repo); return 2; } lua_pushvalue(L, lua_upvalueindex(1)); lua_setmetatable(L, -2); return 1; } static int L_lookup_symbolic_ref(lua_State *L) { git_repository *repo = to_repo(L, 1); const char *refname = luaL_checkstring(L, 2); git_reference *ref = NULL; if (git_reference_lookup(&ref, repo, refname) != 0) { return push_git2_error(L); } if (git_reference_type(ref) != GIT_REF_SYMBOLIC) { git_reference_free(ref); lua_pushnil(L); lua_pushfstring(L, "%s is not symbolic", refname); return 2; } lua_pushstring(L, git_reference_symbolic_target(ref)); git_reference_free(ref); return 1; } static int format_oid(lua_State *L, git_oid *oid) { char oidstr[40]; git_oid_fmt(oidstr, oid); lua_pushlstring(L, oidstr, 40); return 1; } static int L_lookup_sha_from_ref(lua_State *L) { git_repository *repo = to_repo(L, 1); const char *refname = luaL_checkstring(L, 2); git_oid ref; if (git_reference_name_to_id(&ref, repo, refname) != 0) { return push_git2_error(L); } return format_oid(L, &ref); } static int parse_oid(lua_State *L, int spos, git_oid *oid) { const char *oid_s = luaL_checkstring(L, spos); int ret = git_oid_fromstr(oid, oid_s); if (ret != 0) return push_git2_error(L); return 0; } static int L_merge_base(lua_State *L) { git_repository *repo = to_repo(L, 1); git_oid left, right, out; int ret; if (parse_oid(L, 2, &left) != 0) return 2; if (parse_oid(L, 3, &right) != 0) return 2; ret = git_merge_base(&out, repo, &left, &right); if (ret == 0) return format_oid(L, &out); if (ret == GIT_ENOTFOUND) { lua_pushnil(L); lua_pushliteral(L, "ENOTFOUND"); return 2; } return push_git2_error(L); } static int L_gc_odb_object(lua_State *L) { git_odb_object **obj = lua_touserdata(L, 1); if (*obj != NULL) git_odb_object_free(*obj); *obj = NULL; return 0; } static int L_get_object(lua_State *L) { git_odb *odb = to_odb(L, 1); git_odb_object **obj = lua_newuserdata(L, sizeof(*obj)); git_oid oid; if (parse_oid(L, 2, &oid) != 0) return 2; if (git_odb_read(obj, odb, &oid) != 0) return push_git2_error(L); lua_pushvalue(L, lua_upvalueindex(1)); lua_setmetatable(L, -2); return 1; } static int L_get_object_size(lua_State *L) { git_odb_object **obj = lua_touserdata(L, 1); lua_pushnumber(L, git_odb_object_size(*obj)); return 1; } static void push_object_type_str(lua_State *L, git_otype ty) { switch(ty) { case GIT_OBJ_ANY: lua_pushliteral(L, "any"); break; case GIT_OBJ_BAD: lua_pushliteral(L, "bad"); break; case GIT_OBJ__EXT1: lua_pushliteral(L, "reserved"); break; case GIT_OBJ_COMMIT: lua_pushliteral(L, "commit"); break; case GIT_OBJ_TREE: lua_pushliteral(L, "tree"); break; case GIT_OBJ_BLOB: lua_pushliteral(L, "blob"); break; case GIT_OBJ_TAG: lua_pushliteral(L, "tag"); break; case GIT_OBJ__EXT2: lua_pushliteral(L, "reserved"); break; case GIT_OBJ_OFS_DELTA: lua_pushliteral(L, "delta"); break; case GIT_OBJ_REF_DELTA: lua_pushliteral(L, "refdelta"); break; default: lua_pushliteral(L, "unknown"); break; } } static int L_get_object_type(lua_State *L) { git_odb_object **obj = lua_touserdata(L, 1); push_object_type_str(L, git_odb_object_type(*obj)); return 1; } static int L_get_object_raw(lua_State *L) { git_odb_object **obj = lua_touserdata(L, 1); lua_pushlstring(L, git_odb_object_data(*obj), git_odb_object_size(*obj)); return 1; } int luaopen_git2(lua_State *L) { lua_newtable(L); lua_pushliteral(L, "LIBGIT2_VERSION"); lua_pushliteral(L, LIBGIT2_VERSION); lua_settable(L, -3); lua_pushliteral(L, "open_repo"); lua_newtable(L); lua_pushliteral(L, "__gc"); lua_pushcclosure(L, L_gc_repo, 0); lua_settable(L, -3); lua_pushcclosure(L, L_open_repo, 1); lua_settable(L, -3); lua_pushliteral(L, "get_object"); lua_newtable(L); lua_pushliteral(L, "__gc"); lua_pushcclosure(L, L_gc_odb_object, 0); lua_settable(L, -3); lua_pushcclosure(L, L_get_object, 1); lua_settable(L, -3); #define BASIC_FUNC(FN) \ do { \ lua_pushliteral(L, #FN); \ lua_pushcclosure(L, L_##FN, 0); \ lua_settable(L, -3); \ } while (0) BASIC_FUNC(lookup_symbolic_ref); BASIC_FUNC(lookup_sha_from_ref); BASIC_FUNC(merge_base); BASIC_FUNC(get_object_size); BASIC_FUNC(get_object_type); BASIC_FUNC(get_object_raw); return 1; }