diff options
author | Daniel Silverstone <dsilvers@digital-scurf.org> | 2012-03-07 22:15:58 +0000 |
---|---|---|
committer | Daniel Silverstone <dsilvers@simtec.co.uk> | 2012-03-07 22:15:58 +0000 |
commit | 93dd4c00572d451c5d7b110aa91bef4ac196f129 (patch) | |
tree | c3601a8d5e266ff997657ce93a74d5c072d5c30c | |
parent | 1862e2b29845f226e53c4747ee7b1d2cc7cdb269 (diff) | |
download | gitano-93dd4c00572d451c5d7b110aa91bef4ac196f129.tar.gz |
LEGIT: Lots of initial objects. Can parse commits and trees and walk them
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | lib/legit/git/commit.lua | 96 | ||||
-rw-r--r-- | lib/legit/git/init.lua | 102 | ||||
-rw-r--r-- | lib/legit/git/ll.lua | 17 | ||||
-rw-r--r-- | lib/legit/git/object.lua | 60 | ||||
-rw-r--r-- | lib/legit/git/tree.lua | 47 |
6 files changed, 323 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03c55e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +run +t diff --git a/lib/legit/git/commit.lua b/lib/legit/git/commit.lua new file mode 100644 index 0000000..3db23cb --- /dev/null +++ b/lib/legit/git/commit.lua @@ -0,0 +1,96 @@ +-- legit.git.commit +-- +-- Core Git functionality for Legit +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- + +local ll = require "legit.git.ll" + +local _new + +local function parse_person(pers) + local real, email, when, tz = pers:match("^(.-) <([^>]+)> ([0-9]+) ([+-][0-9]+)$") + return { + realname = real, + email = email, + unixtime = when, + timezone = tz + } +end + +local function parse_parents(obj, ps) + local ret = {} + for _, psha in ipairs(ps) do + ret[_] = _new(obj.repo:get(psha)) + end + return ret +end + +local function commitindex(commit, field) + if not rawget(commit, "_parsed") then + local raw = commit.obj.raw + local headers, body, signature = {}, "" + local state = "headers" + + for l in raw:gmatch("([^\n]*)\n") do + if state == "headers" then + local first, second = l:match("^([^ ]+) (.+)$") + if first then + if headers[first] then + headers[first][#headers[first]+1] = second + else + headers[first] = { second } + end + else + state = "message" + end + elseif state == "message" then + if l == PGP_SIG_START then + signature = l .. "\n" + state = "signature" + else + body = body .. l .. "\n" + end + else + signature = signature .. l .. "\n" + end + end + + -- there's always one tree + rawset(commit, "tree", commit.obj.repo:get(headers.tree[1])) + -- Always one author + rawset(commit, "author", parse_person(headers.author[1])) + -- Always one committer + rawset(commit, "committer", parse_person(headers.committer[1])) + -- Zero or more parents + headers.parent = headers.parent or {} + rawset(commit, "parents", parse_parents(commit.obj, headers.parent)) + -- A message + rawset(commit, "message", body) + -- And an optional signature + rawset(commit, "signature", "signature") + + rawset(commit, "_parsed", true) + end + + return rawget(commit, field) +end + +local function committostring(commit) + return "<GitCommit(" .. tostring(commit.obj.sha) .. ") in " .. tostring(commit.obj.repo) .. ">" +end + +local commitmeta = { + __index = commitindex, + __tostring = committostring +} + +function _new(obj) + return setmetatable({obj=obj}, commitmeta) +end + +return { + new = _new +} diff --git a/lib/legit/git/init.lua b/lib/legit/git/init.lua new file mode 100644 index 0000000..8b0e29b --- /dev/null +++ b/lib/legit/git/init.lua @@ -0,0 +1,102 @@ +-- legit.git +-- +-- Core Git functionality for Legit +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- + +local ll = require "legit.git.ll" +local object = require "legit.git.object" +local commit = require "legit.git.commit" + +local chomp = ll.chomp + +local repomethod = {} + +local function _repotostring(repo) + return "<GitRepository(" .. repo.path .. ")>" +end + + +function repomethod:_run(want_output, ...) + local t = {...} + t.repo = self.path + if want_output then + t.stdout = want_output + end + return ll.rungit(t) +end + +function repomethod:gather(...) + return self:_run(chomp, ...) +end + +function repomethod:rawgather(...) + return self:_run(true, ...) +end + +function repomethod:get_ref(ref) + local ok, sha = self:_run(chomp, "show-ref", "-s", ref) + return (ok == 0) and sha or nil +end + +function repomethod:all_refs() + local ok, refs = self:_run(chomp, "show-ref") + if ok ~= 0 then return nil, refs end + local reft = {} + for sha, ref in refs:gmatch("([0-9a-f]+) (refs/[^\n]+)") do + reft[ref] = sha + end + return reft +end + +function repomethod:get(sha) + if not self.objmemo[sha] then + self.objmemo[sha] = object.new(self, sha) + end + return self.objmemo[sha] +end + +local repomt = { + __index = repomethod, + __tostring = _repotostring +} + +local function _repo(path) + -- return a new git repository object + -- with the git_dir set for the provided path + -- and, if we had to add /.git then the GIT_WORK_DIR set + -- appropriately too + + local retrepo = {objmemo={}} + + local repopath = path + local workpath = nil + + local ok = ll.symbolic_ref { "-q", "HEAD", stderr=true, repo=repopath } + if ok ~= 0 then + repopath = path .. "/.git" + workpath = path + ok = ll.symbolic_ref { "-q", "HEAD", stderr=true, repo=repopath } + end + + if ok ~= 0 then + return nil, "Unable to find Git repository at " .. path + end + + retrepo.path = repopath + retrepo.work = workpath + + return setmetatable(retrepo, repomt) +end + +return { + open = _repo, + + -- Sub modules + object = object, + commit = commit, + -- Finally LL + ll = ll +}
\ No newline at end of file diff --git a/lib/legit/git/ll.lua b/lib/legit/git/ll.lua index 6ad5c98..c38aead 100644 --- a/lib/legit/git/ll.lua +++ b/lib/legit/git/ll.lua @@ -35,21 +35,34 @@ local function _rungit(t) proc_args.stdout = sp.PIPE end + if t.stderr then + proc_args.stderr = sp.PIPE + end + local proc = sp.spawn_simple(proc_args) - local stdout + local stdout, stderr if t.stdout then stdout = proc.stdout:read("*a") + proc.stdout:close() if type(t.stdout) == "function" then stdout = t.stdout(stdout) end end + if t.stderr then + stderr = proc.stderr:read("*a") + proc.stderr:close() + if type(t.stderr) == "function" then + stderr = t.stderr(stderr) + end + end + local how, why = proc:wait() assert(how == "exit", "Not cleanly exited") - return why, stdout + return why, stdout, stderr end local function _setgit(e) diff --git a/lib/legit/git/object.lua b/lib/legit/git/object.lua new file mode 100644 index 0000000..a818779 --- /dev/null +++ b/lib/legit/git/object.lua @@ -0,0 +1,60 @@ +-- legit.git.object +-- +-- Core Git functionality for Legit +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- + +local ll = require "legit.git.ll" +local commit = require "legit.git.commit" +local tree = require "legit.git.tree" + +local function _objectindex(obj, field) + local ok, ret + if field == "type" then + ok, ret = obj.repo:gather("cat-file", "-t", obj.sha) + elseif field == "size" then + ok, ret = obj.repo:gather("cat-file", "-s", obj.sha) + elseif field == "raw" then + ok, ret = obj.repo:rawgather("cat-file", "-p", obj.sha) + elseif field == "content" then + if obj.type == "blob" then + ok, ret = 0, obj.raw + elseif obj.type == "commit" then + ok, ret = 0, commit.new(obj) + elseif obj.type == "tree" then + ok, ret = 0, tree.new(obj) + else + error("Unknown type <" .. obj.type .. "> for content parse") + end + else + error("Unknown field <" .. tostring(field) .. ">") + end + + assert(ok == 0, "Unable to retrieve " .. field) + + if field == "size" then ret = tonumber(ret) end + + rawset(obj, field, ret) + + return ret +end + +function _objecttostring(obj) + return "<GitObject(" .. tostring(obj.sha)..") in " .. tostring(obj.repo) .. ">" +end + +local objectmeta = { + __index = _objectindex, + __tostring = _objecttostring +} + +local function _new(repo, sha) + return setmetatable({repo=repo,sha=sha}, objectmeta) +end + + +return { + new = _new +} diff --git a/lib/legit/git/tree.lua b/lib/legit/git/tree.lua new file mode 100644 index 0000000..eeae4ce --- /dev/null +++ b/lib/legit/git/tree.lua @@ -0,0 +1,47 @@ +-- legit.git.tree +-- +-- Core Git functionality for Legit +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- + +local ll = require "legit.git.ll" + +local function treeindex(tree, field) + if not rawget(tree, "_parsed") then + local raw = tree.obj.raw + local n = 1 + for l in raw:gmatch("([^\n]+)\n") do + local perm, type, sha, name = l:match("^([0-9]+) ([^ ]+) ([0-9a-f]+)\t(.+)$") + local t = { + permissions = perm, + name = name, + type = type, + obj = tree.obj.repo:get(sha) + } + rawset(tree, n, t) + n = n + 1 + end + rawset(tree, "_parsed", true) + end + + return rawget(tree, field) +end + +local function treetostring(tree) + return "<GitTree(" .. tostring(tree.obj.sha) .. ") in " .. tostring(tree.obj.repo) .. ">" +end + +local treemeta = { + __index = treeindex, + __tostring = treetostring, +} + +local function _new(obj) + return setmetatable({obj=obj}, treemeta) +end + +return { + new = _new +} |