Index

resty-gitweb / 9944977

A git web interface for Lua/OpenResty (you're on it right now!)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
628 Dec 2020 15:439944977Update git_commands.lua with libgit2 bindingsJosh Stockin115157G

Blob @ resty-gitweb / git / git_commands.lua

text/plain9275 bytesdownload raw
1-- resty-gitweb@git/git_commands.lua
2-- git commands and parser functions
3
4-- Copyright (c) 2020 Joshua 'joshuas3' Stockin
5-- <https://git.joshstock.in/resty-gitweb>
6-- This software is licensed under the MIT License.
7
8local ffi = require("ffi")
9local utils = require("utils/utils")
10
11local _M = {}
12
13local git = function(repo_dir, command)
14 local formatted_command = string.format(
15 "git --git-dir=%s %s",
16 repo_dir, command
17 )
18 return utils.process(formatted_command)
19end
20
21local function git2_error(err, message)
22 if err < 0 then
23 local git2_error_message = ffi.string(git2.git_error_last().message)
24 error(string.format([[libgit2 Error - %s ("%s")]], message, git2_error_message))
25 end
26end
27
28_M.show_file = function(repo_dir, ref_name, file_path)
29 local err = 0
30
31 -- Open git repo
32 local repo_obj = ffi.new("git_repository*[1]")
33 err = git2.git_repository_open(ffi.cast("git_repository**", repo_obj), repo_dir)
34 git2_error(err, "Failed opening git repository")
35 repo_obj = repo_obj[0]
36
37 -- Get tree root
38 local tree = ffi.new("git_object*[1]")
39 err = git2.git_revparse_single(ffi.cast("git_object**", tree), repo_obj, ref_name)
40 git2_error(err, "Failed to look up tree from rev name")
41 tree = tree[0]
42
43 -- Get tree entry object (blob)
44 local blob = ffi.new("git_object*[1]")
45 err = git2.git_object_lookup_bypath(ffi.cast("git_object**", blob), tree, file_path, git2.GIT_OBJECT_BLOB)
46 git2_error(err, "Failed to look up blob")
47 blob = ffi.cast("git_blob*", blob[0])
48
49 -- Get blob content
50 local buf = ffi.new("git_buf")
51 err = git2.git_blob_filter(buf, blob, file_path, nil)
52 git2_error(err, "Failed to filter blob")
53 local raw = ffi.string(buf.ptr)
54
55 -- Free everything
56 git2.git_buf_free(buf)
57 git2.git_blob_free(blob)
58 git2.git_object_free(tree)
59 git2.git_repository_free(repo_obj)
60
61 return raw
62end
63
64_M.get_head = function(repo_dir, ref_name)
65 local err = 0
66 ref_name = ref_name or "HEAD"
67
68 -- Open git repo
69 local repo_obj = ffi.new("git_repository*[1]")
70 err = git2.git_repository_open(ffi.cast("git_repository**", repo_obj), repo_dir)
71 git2_error(err, "Failed opening git repository")
72 repo_obj = repo_obj[0]
73
74 -- Get object/reference
75 local object = ffi.new("git_object*[1]")
76 local reference = ffi.new("git_reference*[1]")
77 err = git2.git_revparse_ext(ffi.cast("git_object**", object), ffi.cast("git_reference**", reference), repo_obj, ref_name)
78 git2_error(err, "Failed to find reference")
79 object = object[0]
80 reference = reference[0]
81
82 -- Get full name if intermediate reference exists
83 local name = ref_name
84 if reference ~= nil then
85 local ref_type = git2.git_reference_type(reference)
86 if ref_type == git2.GIT_REFERENCE_SYMBOLIC then
87 name = ffi.string(git2.git_reference_symbolic_target(reference))
88 else
89 name = ffi.string(git2.git_reference_name(reference))
90 end
91 end
92
93 -- Get OID
94 local oid = git2.git_object_id(object)
95
96 -- Format oid as SHA1 hash
97 local hash = ffi.new("char[41]") -- SHA1 length (40 chars) + \0
98 err = git2.git_oid_fmt(hash, oid)
99 git2_error(err, "Failed formatting OID")
100 hash = ffi.string(hash)
101
102 -- Free all
103 git2.git_object_free(object)
104 if reference ~= nil then
105 git2.git_reference_free(reference)
106 end
107 git2.git_repository_free(repo_obj)
108
109 -- Format
110 local ref = {}
111 ref.full = name
112 if name:match("^refs/heads/") then
113 ref.name = string.sub(name, 12, string.len(name))
114 elseif name:match("^refs/tags/") then
115 ref.name = string.sub(name, 11, string.len(name))
116 else
117 ref.name = ref_name -- just pass input as default output
118 end
119 ref.hash = hash
120 ref.shorthash = string.sub(hash, 1, 7)
121
122 return ref
123end
124
125_M.count = function(repo_dir, hash)
126 hash = hash or "@"
127 local output = git(repo_dir, "rev-list --count "..hash.." --")
128 return tonumber(string.trim(output))
129end
130
131_M.log = function(repo_dir, hash, file, number, skip, gpg)
132 hash = hash or "@"
133 file = file or ""
134 number = tostring(number or 25)
135 skip = tostring(skip or 0)
136 gpg = gpg or false
137 local output
138 if gpg then
139 output = git(repo_dir, "log --pretty=tformat:'%x00%x01%H%x00%cI%x00%cn%x00%ce%x00%s%x00%b%x00%G?%x00%GK%x00%GG%x00' --numstat -n "..number.." --skip "..skip.." "..hash.." -- "..file)
140 else
141 output = git(repo_dir, "log --pretty=tformat:'%x00%x01%H%x00%cI%x00%cn%x00%ce%x00%s%x00%b%x00' --numstat -n "..number.." --skip "..skip.." "..hash.." -- "..file)
142 end
143 local commits = {}
144 local a = string.split(output,"\0\1")
145 local f = false
146 for i,v in pairs(a) do
147 if f == true then
148 local commit = {}
149 local c = string.split(v, "\0")
150 commit.hash = c[1]
151 commit.shorthash = string.sub(c[1], 1,7)
152 commit.timestamp = c[2]
153 commit.author = c[3]
154 commit.email = c[4]
155 commit.subject = c[5]
156 commit.body = string.trim(c[6])
157 local diffs
158 if gpg then
159 commit.gpggood = c[7]
160 commit.gpgkey = c[8]
161 commit.gpgfull = string.trim(c[9])
162 diffs = string.trim(c[10])
163 else
164 diffs = string.trim(c[7])
165 end
166 commit.diff = {}
167 local b = string.split(diffs, "\n")
168 commit.diff.plus = 0
169 commit.diff.minus = 0
170 commit.diff.num = 0
171 commit.diff.files = {}
172 for i,v in pairs(b) do
173 local d = string.split(v,"\t")
174 local x = {}
175 x.plus = tonumber(d[1]) or 0
176 commit.diff.plus = commit.diff.plus + x.plus
177 x.minus = tonumber(d[2]) or 0
178 commit.diff.minus = commit.diff.minus + x.minus
179 commit.diff.files[d[3]] = x
180 commit.diff.num = commit.diff.num + 1
181 end
182 table.insert(commits, commit)
183 else
184 f = true
185 end
186 end
187 return commits
188end
189
190_M.commit = function(repo_dir, hash)
191 local commit = _M.log(repo_dir, hash, "", 1, 0, true)[1]
192 commit.count = _M.count(repo_dir, hash)
193 return commit
194end
195
196_M.list_refs = function(repo_dir)
197 -- Open git repo
198 local repo_obj = ffi.new("git_repository*[1]")
199 local err = git2.git_repository_open(ffi.cast("git_repository**", repo_obj), repo_dir)
200 repo_obj = repo_obj[0]
201
202 -- List refs
203 local refs = ffi.new("git_strarray")
204 local err = git2.git_reference_list(ffi.cast("git_strarray*", refs), repo_obj)
205
206 local ret = {}
207 ret.heads = {}
208 ret.tags = {}
209
210 for i = 0, tonumber(refs.count)-1 do
211
212 local name = ffi.string(refs.strings[i])
213
214 local dest
215 local prefix_len
216 if name:match("^refs/heads/") then
217 dest = ret.heads
218 prefix_len = 12
219 elseif name:match("^refs/tags/") then
220 dest = ret.tags
221 prefix_len = 11
222 end
223
224 if dest then
225 local oid = ffi.new("git_oid")
226 local err = git2.git_reference_name_to_id(ffi.cast("git_oid*", oid), repo_obj, refs.strings[i])
227
228 -- Format oid as SHA1 hash
229 local hash = ffi.new("char[41]") -- SHA1 length (40 chars) + \0
230 local err = git2.git_oid_fmt(hash, ffi.cast("git_oid*", oid))
231 hash = ffi.string(hash)
232
233 local ref = {}
234 ref.name = string.sub(name, prefix_len, string.len(name))
235 ref.full = name
236 ref.hash = hash
237 ref.shorthash = string.sub(hash, 1, 7)
238 table.insert(dest, ref)
239 end
240
241 end
242
243 if refs then
244 git2.git_strarray_free(ffi.cast("git_strarray*", refs))
245 end
246 if repo_obj then
247 git2.git_repository_free(ffi.cast("git_repository*", repo_obj))
248 end
249
250 return ret
251end
252
253local list_dirs = function(repo_dir, hash, path)
254 hash = hash or "@"
255 path = path or ""
256 local output = git(repo_dir, "ls-tree -d --name-only "..hash.." -- "..path)
257 local dirs = string.split(output, "\n")
258 table.remove(dirs, #dirs) -- remove trailing \n
259 return dirs
260end
261
262local list_all = function(repo_dir, hash, path)
263 hash = hash or "@"
264 path = path or ""
265 local output = git(repo_dir, "ls-tree --name-only "..hash.." -- "..path)
266 local all = string.split(output, "\n")
267 table.remove(all, #all) -- remove trailing \n
268 return all
269end
270
271_M.list_tree = function(repo_dir, hash, path)
272 hash = hash or "@"
273 path = path or ""
274 local files = list_all(repo_dir, hash, path)
275 local dirs = list_dirs(repo_dir, hash, path)
276 local ret = {}
277 ret.dirs = {}
278 ret.files = {}
279 for i,v in pairs(files) do -- iterate over all objects, separate directories from files
280 local not_dir = true
281 for _,d in pairs(dirs) do -- check if object is directory
282 if v == d then
283 not_dir = false
284 break
285 end
286 end
287 if not_dir then
288 table.insert(ret.files, v)
289 else
290 table.insert(ret.dirs, v)
291 end
292 end
293 return ret
294end
295
296return _M
297