summaryrefslogtreecommitdiff
path: root/lib/gall/commit.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gall/commit.lua')
-rw-r--r--lib/gall/commit.lua165
1 files changed, 165 insertions, 0 deletions
diff --git a/lib/gall/commit.lua b/lib/gall/commit.lua
new file mode 100644
index 0000000..92afff8
--- /dev/null
+++ b/lib/gall/commit.lua
@@ -0,0 +1,165 @@
+-- gall.commit
+--
+-- Git Abstraction Layer for Lua -- Commit object interface
+--
+-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+--
+
+local ll = require "gall.ll"
+
+local objs = setmetatable({}, {__mode="k"})
+local repos = setmetatable({}, {__mode="k"})
+local parsed = setmetatable({}, {__mode="k"})
+
+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(repo, ps)
+ local ret = {}
+ for _, psha in ipairs(ps) do
+ ret[_] = repo:get(psha)
+ end
+ return ret
+end
+
+local function commitindex(commit, field)
+ if not parsed[commit] then
+ local raw = objs[commit].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", repos[commit]: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(repos[commit], headers.parent))
+ -- A message
+ rawset(commit, "message", body)
+ -- And an optional signature
+ rawset(commit, "signature", signature)
+ -- Promote the SHA
+ rawset(commit, "sha", objs[commit].sha)
+
+ -- Signal we are parsed
+ parsed[commit] = true
+ end
+
+ return rawget(commit, field)
+end
+
+local function committostring(commit)
+ return "<GitCommit(" .. tostring(objs[commit].sha) .. ") in " .. tostring(repos[commit]) .. ">"
+end
+
+local commitmeta = {
+ __index = commitindex,
+ __tostring = committostring
+}
+
+function _new(repo, obj)
+ local ret = setmetatable({}, commitmeta)
+ objs[ret] = obj
+ repos[ret] = repo
+ return ret
+end
+
+function _create(repo, data)
+ if not data.tree then
+ return nil, "No tree?"
+ end
+ if not data.author then
+ return nil, "No author?"
+ end
+ if not data.committer then
+ return nil, "No committer?"
+ end
+ if not data.author.realname then
+ return nil, "No author name?"
+ end
+ if not data.author.email then
+ return nil, "No author email?"
+ end
+ if not data.committer.realname then
+ return nil, "No committer name?"
+ end
+ if not data.committer.email then
+ return nil, "No committer email?"
+ end
+ if not data.message then
+ return nil, "No message?"
+ end
+ if not data.parents then
+ data.parents = {}
+ end
+
+ -- Construct the commandline and environment
+ local env = {
+ GIT_AUTHOR_NAME = data.author.realname,
+ GIT_AUTHOR_EMAIL = data.author.email,
+ GIT_COMMITTER_NAME = data.committer.realname,
+ GIT_COMMITTER_EMAIL = data.committer.email,
+ }
+ local cmd = {
+ "commit-tree", data.tree.sha
+ }
+ for i, v in ipairs(data.parents) do
+ cmd[#cmd+1] = "-p"
+ cmd[#cmd+1] = v.sha
+ end
+
+ if not data.message:match("\n$") then
+ data.message = data.message .. "\n"
+ end
+
+ local why, sha = repo:_run_with_input_and_env(env, data.message,
+ ll.chomp, unpack(cmd))
+
+ if why ~= 0 then
+ return nil, "commit-tree returned " .. tostring(why)
+ end
+
+ return repo:get(sha)
+end
+
+return {
+ create = _create,
+ new = _new
+}