login

<     >

2022-02-27 12:28:57 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

add tree browsing

diff --git a/app.lua b/app.lua
index 32f21ef..fb1ef47 100644
--- a/app.lua
+++ b/app.lua
@@ -101,6 +101,28 @@ local routes = {
         }
         return lud.template.render_file("view/commit.html", env)
     end},
+    {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)/tree/(.*)",
+    function (req, gname, rname, cid, path)
+        local repo = groups[gname][rname]
+        local commit = repo:commit(cid)
+        local node = commit:tree_entry(path)
+        if node == nil then
+            return "File not found", 404, "Not found"
+        end
+        local parts = {}
+        for part in path:gmatch("[^/]+") do
+            table.insert(parts, part)
+        end
+        local env = {
+            gname=gname, rname=rname, cid=cid, path=path,
+            base=req.path, parts=parts, node=node,
+        }
+        if node.type_ == "dir" then
+            return lud.template.render_file("view/dir.html", env)
+        elseif node.type_ == "file" then
+            return lud.template.render_file("view/file.html", env)
+        end
+    end},
 }
 
 local app = lud.app.new_app(routes)

diff --git a/git.lua b/git.lua
index c444cc3..5b7e399 100644
--- a/git.lua
+++ b/git.lua
@@ -133,6 +133,13 @@ int git_commit_tree(git_tree **tree_out, const git_commit *commit);
 size_t git_tree_entrycount(const git_tree *tree);
 const git_tree_entry * git_tree_entry_byindex(const git_tree *tree, size_t idx);
 git_object_t git_tree_entry_type(const git_tree_entry *entry);
+const char * git_tree_entry_name(const git_tree_entry *entry);
+int git_tree_entry_bypath(git_tree_entry **out, const git_tree *root, const char *path);
+int git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
+void git_tree_entry_free(git_tree_entry *entry);
+
+git_object_size_t git_blob_rawsize(const git_blob *blob);
+const void * git_blob_rawcontent(const git_blob *blob);
 
 int git_diff_tree_to_tree(git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts);
 int git_diff_print(git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload);
@@ -151,8 +158,8 @@ end
 local Commit = {}
 Commit.__index = Commit
 
-local function get_commit(commit)
-    return setmetatable({commit=commit}, Commit)
+local function get_commit(commit, repo)
+    return setmetatable({commit=commit, repo=repo}, Commit)
 end
 
 function Commit:id(len)
@@ -185,7 +192,7 @@ function Commit:parent(n)
     local pcommit = ffi.new("git_commit *[1]")
     local ret = C.git_commit_parent(pcommit, self.commit, n-1)
     if ret == 0 then
-        return get_commit(pcommit[0])
+        return get_commit(pcommit[0], self.repo)
     else
         return nil
     end
@@ -201,6 +208,60 @@ function Commit:tree()
     end
 end
 
+function Commit:children(tree)
+    local n = tonumber(C.git_tree_entrycount(tree))
+    local children = {}
+    for i = 1, n do
+        local entry = C.git_tree_entry_byindex(tree, i-1)
+        local entry_type = C.git_tree_entry_type(entry)
+        local entry_name = ffi.string(C.git_tree_entry_name(entry))
+        local child
+        if entry_type == 2 then
+            child = entry_name.."/"
+        elseif entry_type == 3 then
+            child = entry_name
+        end
+        table.insert(children, child)
+    end
+    return children
+end
+
+function Commit:node(entry)
+    local entry_type = C.git_tree_entry_type(entry)
+    local entry_name = C.git_tree_entry_name(entry)
+    local pobj = ffi.new("git_object *[1]")
+    C.git_tree_entry_to_object(pobj, self.repo, entry)
+    if entry_type == 2 then
+        local tree = ffi.cast("git_tree *", pobj[0])
+        local children = self:children(tree)
+        return {type_="dir", name=name, children=children}
+    elseif entry_type == 3 then
+        local blob = ffi.cast("git_blob *", pobj[0])
+        local data = ffi.string(C.git_blob_rawcontent(blob), C.git_blob_rawsize(blob))
+        return {type_="file", name=name, data=data}
+    else
+        return nil
+    end
+end
+
+function Commit:tree_entry(path)
+    local tree = self:tree()
+    if tree == nil then
+        return nil
+    end
+    if path == "" then
+        local children = self:children(tree)
+        return {type_="dir", name="", children=children}
+    end
+    local pentry = ffi.new("git_tree_entry *[1]")
+    local ret = C.git_tree_entry_bypath(pentry, tree, path)
+    if ret == 0 then
+        return self:node(pentry[0])
+    else
+        return nil
+    end
+end
+
 local Repo = {}
 Repo.__index = Repo
 
@@ -240,7 +301,7 @@ function Repo:commit(rev)
     C.git_commit_lookup(pcommit, self.repo, oid)
     local commit = pcommit[0]
     C.git_object_free(obj)
-    return get_commit(commit)
+    return get_commit(commit, self.repo)
 end
 
 function Repo:find_prev(cid, dist)

diff --git a/view/dir.html b/view/dir.html
new file mode 100644
index 0000000..8a1ee2f
--- /dev/null
+++ b/view/dir.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>cogit - {{$gname}} - {{$rname}} - {{$cid}} - {{path}}</title>
+</head>
+<body>
+  <p>
+    <a href="/">home</a>
+    &gt;
+    <a href="/group/{{$gname}}">{{$gname}}</a>
+    &gt;
+    <a href="/group/{{$gname}}/repo/{{$rname}}">{{$rname}}</a>
+    &gt;
+    <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$cid}}">{{$cid}}</a>
+    &gt;
+    % set partial = "/group/"..$gname.."/repo/"..$rname.."/commit/"..$cid.."/tree"
+    <a href="{{$partial}}/">tree</a>
+    % for part in $parts do
+    % set partial = $partial .. "/" .. $part
+    &gt;
+    <a href="{{$partial}}">{{$part}}</a>
+    % end
+  </p>
+  <ul>
+    % for child in $node.children do
+    <li><a href="{{$base..$child}}">{{$child}}</a></li>
+    % end
+  </ul>
+</body>
+</html>

diff --git a/view/file.html b/view/file.html
new file mode 100644
index 0000000..eaf79e0
--- /dev/null
+++ b/view/file.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>cogit - {{$gname}} - {{$rname}} - {{$cid}} - {{path}}</title>
+</head>
+<body>
+  <p>
+    <a href="/">home</a>
+    &gt;
+    <a href="/group/{{$gname}}">{{$gname}}</a>
+    &gt;
+    <a href="/group/{{$gname}}/repo/{{$rname}}">{{$rname}}</a>
+    &gt;
+    <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$cid}}">{{$cid}}</a>
+    &gt;
+    % set partial = "/group/"..$gname.."/repo/"..$rname.."/commit/"..$cid.."/tree"
+    <a href="{{$partial}}">tree</a>
+    % for part in $parts do
+    % set partial = $partial .. "/" .. $part
+    &gt;
+    <a href="{{$partial}}">{{$part}}</a>
+    % end
+  </p>
+  <pre>{{!$node.data}}</pre>
+</body>
+</html>