diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | lib/gall/ll/git2.c | 317 | ||||
-rw-r--r-- | lib/gall/object.lua | 9 | ||||
-rw-r--r-- | lib/gall/repository.lua | 61 | ||||
-rw-r--r-- | lib/gall/tree.lua | 14 | ||||
m--------- | libgit2 | 0 | ||||
m--------- | luagit2 | 0 | ||||
-rw-r--r-- | test/test-gall.repository.lua | 2 |
9 files changed, 369 insertions, 63 deletions
diff --git a/.gitmodules b/.gitmodules index 8734ea9..6f8f2a5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "libgit2"] path = libgit2 url = git://git.gitano.org.uk/libgit2.git -[submodule "luagit2"] - path = luagit2 - url = git://git.gitano.org.uk/luagit2.git [submodule "extras/luacov"] path = extras/luacov url = git://git.gitano.org.uk/luacov.git @@ -10,6 +10,15 @@ INST_BASE := $(PREFIX) INST_ROOT := $(DESTDIR)$(INST_BASE)/share/lua/$(LUA_VER) C_INST_ROOT := $(DESTDIR)$(INST_BASE)/lib/lua/$(LUA_VER) +INCS := -I/usr/include/lua$(LUA_VER) -Ilibgit2/build/gall-install/include +OPT := -O0 -g +PIC := -fPIC +WARN := -Wall -Werror +DEFS := +CFLAGS := $(INCS) $(OPT) $(WARN) $(DEFS) $(PIC) $(CFLAGS) +LIBGIT2_LDEPS ?= -lssl -lssh2 -lrt +LFLAGS := -O1 -g -Llibgit2/build/gall-install/lib -lgit2 $(LIBGIT2_LDEPS) $(LFLAGS) + MOD_FILES := $(patsubst %,%.lua,$(subst .,/,$(MODULES))) install: cmodule @@ -32,26 +41,21 @@ try-cmodule: do-cmodule: lib/gall/ll/git2.so -lib/gall/ll/git2.so: luagit2/build/git2.so - mkdir -p lib/gall/ll - cp $< $@ - -luagit2/build/git2.so: libgit2/build/gall-install-stamp - mkdir -p luagit2/build - cd luagit2/build && PKG_CONFIG_PATH="$(shell pwd)/libgit2/build/gall-install/lib/pkgconfig:$(PKG_CONFIG_PATH)" cmake -DCMAKE_C_FLAGS:STRING="$${CMAKE_C_FLAGS} -Dluaopen_git2=luaopen_gall_ll_git2" -DCMAKE_MODULE_LINKER_FLAGS="-lssl" .. - cd luagit2/build && $(MAKE) +lib/gall/ll/git2.so: libgit2/build/gall-install-stamp lib/gall/ll/git2.c + $(CC) $(CFLAGS) lib/gall/ll/git2.c -o $@ -shared $(LFLAGS) libgit2/build/gall-install-stamp: mkdir -p libgit2/build - cd libgit2/build && cmake -DBUILD_SHARED_LIBS:BOOLEAN=OFF -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/libgit2/build/gall-install .. + cd libgit2/build && cmake -DBUILD_SHARED_LIBS:BOOLEAN=OFF -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/libgit2/build/gall-install -DCMAKE_C_FLAGS=-fPIC .. cd libgit2/build && $(MAKE) - cd libgit2/build && $(MAKE) test + echo "DISABLED BECAUSE LIBGIT2 AUTHORS ARE MAD: cd libgit2/build && $(MAKE) test" + cd libgit2/build && ./libgit2_clar -xonline cd libgit2/build && $(MAKE) install DESTDIR="" touch $@ clean: $(RM) luacov.report.out luacov.report.git2.out luacov.stats.out - $(RM) -r libgit2/build luagit2/build lib/gall/ll + $(RM) -r libgit2/build luagit2/build lib/gall/ll/git2.so distclean: clean find . -name "*~" -delete diff --git a/lib/gall/ll/git2.c b/lib/gall/ll/git2.c new file mode 100644 index 0000000..27e2450 --- /dev/null +++ b/lib/gall/ll/git2.c @@ -0,0 +1,317 @@ +/* git2.c + * + * Simple binding to the parts of libgit2 in use by Gall. + * + * Copyright 2014 Daniel Silverstone <dsilvers@digital-scurf.org> + */ + +#include <lua.h> +#include <lauxlib.h> +#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, const 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_set_symbolic_ref(lua_State *L) +{ + git_repository *repo = to_repo(L, 1); + git_reference *ref; + if (git_reference_symbolic_create(&ref, repo, + luaL_checkstring(L, 2), + luaL_checkstring(L, 3), + 1, NULL, NULL) != 0) { + return push_git2_error(L); + } + git_reference_free(ref); + lua_pushboolean(L, 1); + return 1; +} + +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; +} + +static int L_get_tree_table(lua_State *L) +{ + git_repository *repo = to_repo(L, 1); + git_oid oid; + git_tree *tree; + size_t ent; + if (parse_oid(L, 2, &oid) != 0) + return 2; + if (git_tree_lookup(&tree, repo, &oid) != 0) + return push_git2_error(L); + lua_newtable(L); + for (ent = 0; ent < git_tree_entrycount(tree); ++ent) { + const git_tree_entry *tree_ent = + git_tree_entry_byindex(tree, ent); + lua_pushnumber(L, ent+1); + lua_newtable(L); + lua_pushliteral(L, "name"); + lua_pushstring(L, git_tree_entry_name(tree_ent)); + lua_settable(L, -3); + lua_pushliteral(L, "sha"); + format_oid(L, git_tree_entry_id(tree_ent)); + lua_settable(L, -3); + lua_pushliteral(L, "perms"); + lua_pushnumber(L, git_tree_entry_filemode_raw(tree_ent)); + lua_settable(L, -3); + lua_settable(L, -3); + } + + git_tree_free(tree); + return 1; +} + +int luaopen_gall_ll_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(set_symbolic_ref); + BASIC_FUNC(merge_base); + BASIC_FUNC(get_object_size); + BASIC_FUNC(get_object_type); + BASIC_FUNC(get_object_raw); + BASIC_FUNC(get_tree_table); + return 1; +} diff --git a/lib/gall/object.lua b/lib/gall/object.lua index bedc6f1..f59fb39 100644 --- a/lib/gall/object.lua +++ b/lib/gall/object.lua @@ -23,19 +23,19 @@ local function _objectindex(obj, field) local blob = blobs[obj] if field == "type" then if blob then - ok, ret = 0, blob:type() + ok, ret = 0, ll.git2.get_object_type(blob) else ok, ret = repos[obj]:gather("cat-file", "-t", obj.sha) end elseif field == "size" then if blob then - ok, ret = 0, blob:size() + ok, ret = 0, ll.git2.get_object_size(blob) else ok, ret = repos[obj]:gather("cat-file", "-s", obj.sha) end elseif field == "raw" then if blob and obj.type ~= "tree" then - ok, ret = 0, blob:data() + ok, ret = 0, ll.git2.get_object_raw(blob) else ok, ret = repos[obj]:rawgather("cat-file", (obj.type == "tag" and "tag" or "-p"), obj.sha) end @@ -76,8 +76,7 @@ local function _new(repo, sha) local ret = setmetatable({sha=sha}, objectmeta) repos[ret] = repo if ll.git2 then - local oid = ll.git2.OID.hex(sha) - blobs[ret] = repo.git2.odb:read(oid) + blobs[ret] = ll.git2.get_object(repo.git2.repo, sha) end return ret end diff --git a/lib/gall/repository.lua b/lib/gall/repository.lua index 70381cb..38ed398 100644 --- a/lib/gall/repository.lua +++ b/lib/gall/repository.lua @@ -84,14 +84,7 @@ end if ll.git2 then function repomethod:get_ref(ref) - local rref = ll.git2.Reference.lookup(self.git2.repo, ref) - if not rref then - return nil - end - if rref:type() ~= ll.git2.REF_OID then - return nil - end - return tostring(rref:oid()) + return ll.git2.lookup_sha_from_ref(self.git2.repo, ref) end end @@ -153,15 +146,9 @@ function repomethod:normalise(sha) else local fullsha if ll.git2 then - local refobj = ll.git2.Reference.lookup(self.git2.repo, sha) + local refobj = ll.git2.lookup_sha_from_ref(self.git2.repo, sha) if refobj then - if refobj:type() == ll.git2.REF_SYMBOLIC then - refobj = ll.git2.Reference.lookup(self.git2.repo, - refobj:target()) - end - if refobj:type() == ll.git2.REF_OID then - fullsha = tostring(refobj:oid()) - end + fullsha = refobj end end if not fullsha then @@ -228,16 +215,16 @@ if ll.git2 then commitish_2 = commitish_2.content.object end end) - local oid_1 = ll.git2.OID.hex(commitish_1.sha) - local oid_2 = ll.git2.OID.hex(commitish_2.sha) - local oid_base, err = ll.git2.merge.base(self.git2.repo, oid_1, oid_2) + local oid_base, err = ll.git2.merge_base(self.git2.repo, + commitish_1.sha, + commitish_2.sha) if not oid_base then if tostring(err) == "ENOTFOUND" then return true end return nil, err end - return tostring(oid_base), err + return oid_base, err end end @@ -282,17 +269,18 @@ end if ll.git2 then function repomethod:symbolic_ref(name, toref) - local symref = ll.git2.Reference.lookup(self.git2.repo, name) - if not symref then - return nil, "No such ref: " .. tostring(toref) - end - if symref:type() ~= ll.git2.REF_SYMBOLIC then - return false - end if toref then - symref:set_target(toref) + -- Set the ref... + local ok, err = ll.git2.set_symbolic_ref(self.git2.repo, name, toref) + if not ok then + return nil, err + end end - return true, symref:target() + local symref, err = ll.git2.lookup_symbolic_ref(self.git2.repo, name) + if not symref then + return nil, "No such ref: " .. tostring(toref) .. " (" .. err .. ")" + end + return true, symref end end @@ -305,6 +293,8 @@ function repomethod:config(confname, value) end end +--[[ +-- TODO: Add config support to git2.c and convert this if ll.git2 then local old_config = repomethod.config function repomethod:config(confname, value) @@ -325,6 +315,7 @@ if ll.git2 then end end end +]] local repomt = { __index = repomethod, @@ -350,14 +341,12 @@ local function _new(path) local symref if ll.git2 then - local git2, msg = ll.git2.Repository(repopath) + local git2, msg = ll.git2.open_repo(repopath) if not git2 then return nil, "Unable to find Git repository at " .. path end - local odb = git2:odb() - retrepo.git2 = { repo = git2, odb = odb } - symref = ll.git2.Reference.lookup(git2, "HEAD") - symref = symref:target() + retrepo.git2 = { repo = git2 } + symref = ll.git2.lookup_symbolic_ref(git2, "HEAD") else ok, symref = ll.symbolic_ref { "-q", "HEAD", stderr=true, repo=repopath } if ok ~= 0 then @@ -370,6 +359,8 @@ local function _new(path) retrepo.work = workpath retrepo.HEAD = symref +--[[ +-- This seems redundant if ll.git2 then local git2, msg = ll.git2.Repository(retrepo.path) if not git2 then @@ -378,7 +369,7 @@ local function _new(path) local odb = git2:odb() retrepo.git2 = { repo = git2, odb = odb } end - +--]] return setmetatable(retrepo, repomt) end diff --git a/lib/gall/tree.lua b/lib/gall/tree.lua index 14d75ba..a44b333 100644 --- a/lib/gall/tree.lua +++ b/lib/gall/tree.lua @@ -107,14 +107,12 @@ if ll.git2 then end if not parsed[tree] then - local repo = repos[tree] - local oid = ll.git2.OID.hex(objs[tree].sha) - local treeobj = ll.git2.Tree.lookup(repo.git2.repo, oid) - for i = 0, treeobj:entrycount() - 1 do - local entry = treeobj:entry_byindex(i) - local perm = string.format('0x%08X', entry:filemode()) - local sha = tostring(entry:id()) - local name = entry:name() + local treetab = ll.git2.get_tree_table(repos[tree].git2.repo, + objs[tree].sha) + for _, tab in ipairs(treetab) do + local perm = string.format('0x%08X', tab.perms) + local sha = tab.sha + local name = tab.name local obj = repos[tree]:get(sha) local type = obj.type local t = { diff --git a/libgit2 b/libgit2 -Subproject 9f20b6d32ce9e971a251dca6b4e41a574bb3511 +Subproject b4d00c1d2466de3558a7cc6983dce4eb2ee9843 diff --git a/luagit2 b/luagit2 deleted file mode 160000 -Subproject 8ad8200fd7e40095f152b4a3a44da12c8ee817d diff --git a/test/test-gall.repository.lua b/test/test-gall.repository.lua index 0f59993..12f2f46 100644 --- a/test/test-gall.repository.lua +++ b/test/test-gall.repository.lua @@ -268,7 +268,7 @@ end function suite.symbolic_ref_not_symbolic() local repo = test_repo() local ok, ref = repo:symbolic_ref "refs/heads/master" - assert(ok == false) + assert(not ok) end function suite.symbolic_ref_bad_name() |