summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-03-07 22:15:58 +0000
committerDaniel Silverstone <dsilvers@simtec.co.uk>2012-03-07 22:15:58 +0000
commit93dd4c00572d451c5d7b110aa91bef4ac196f129 (patch)
treec3601a8d5e266ff997657ce93a74d5c072d5c30c
parent1862e2b29845f226e53c4747ee7b1d2cc7cdb269 (diff)
downloadgitano-93dd4c00572d451c5d7b110aa91bef4ac196f129.tar.gz
LEGIT: Lots of initial objects. Can parse commits and trees and walk them
-rw-r--r--.gitignore3
-rw-r--r--lib/legit/git/commit.lua96
-rw-r--r--lib/legit/git/init.lua102
-rw-r--r--lib/legit/git/ll.lua17
-rw-r--r--lib/legit/git/object.lua60
-rw-r--r--lib/legit/git/tree.lua47
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
+}