summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--g2h/cli.py3
-rw-r--r--g2h/git.py3
-rw-r--r--g2h/page_factory.py4
-rw-r--r--g2h/tree_page.py51
-rw-r--r--templates/tree.md10
-rw-r--r--test_repo/abc/foo1
-rw-r--r--test_repo/git.tgzbin16230 -> 22304 bytes
-rw-r--r--test_repo/sub/dir/2/other.file0
-rw-r--r--test_repo/sub/dir/test/some.file0
-rw-r--r--test_repo/sub/file0
-rw-r--r--tests/tree_test.py98
-rw-r--r--workspace.josh3
13 files changed, 174 insertions, 4 deletions
diff --git a/README.md b/README.md
index f5b7732..582f912 100644
--- a/README.md
+++ b/README.md
@@ -50,9 +50,10 @@ For a full list of options, their defaults and how they're changing the behavior
- [ ] Show diffstat on commit page.
- [ ] Add button to browse tree.
- [ ] Add button to download tree tarball.
-- [ ] Tree view.
+- [x] Tree view.
Show the state of the tree at the current commit.
- Folders should be traversable by clicking and clicking on a file should open that file's page.
+ - [ ] Folders should be traversable by clicking.
+ - [ ] Clicking on a file should open that file's page.
- [ ] Individual file page.
File content in a codeblock with file extension based language detection.
For binary files a note that it can't be displayed.
diff --git a/g2h/cli.py b/g2h/cli.py
index b4c1c2b..52a2be7 100644
--- a/g2h/cli.py
+++ b/g2h/cli.py
@@ -31,6 +31,9 @@ def main():
factory.new_log(commits, branches, tags).render()
for commit in commits:
factory.new_commit(commit).render()
+ tree = factory.new_tree(commit, '', git.get_repo())
+ for tree_page in tree.build_pages(factory):
+ tree_page.render()
if __name__ == '__main__':
main()
diff --git a/g2h/git.py b/g2h/git.py
index 079c50b..1e835e7 100644
--- a/g2h/git.py
+++ b/g2h/git.py
@@ -13,5 +13,8 @@ class Git:
def get_commits(self):
return list(self.repo.iter_commits())
+ def get_repo(self):
+ return self.repo
+
def get_tags(self):
return self.repo.tags
diff --git a/g2h/page_factory.py b/g2h/page_factory.py
index 43a48ae..c4b89ed 100644
--- a/g2h/page_factory.py
+++ b/g2h/page_factory.py
@@ -1,6 +1,7 @@
from .commit_page import CommitPage
from .overview_page import OverviewPage
from .log_page import LogPage
+from .tree_page import TreePage
class PageFactory:
def __init__(self, out_dir, name, content_subdir):
@@ -16,3 +17,6 @@ class PageFactory:
def new_log(self, commits, branches, tags):
return LogPage(self.out_dir, self.name, commits, branches, tags, self.content_subdir)
+
+ def new_tree(self, commit, tree_segment, repo):
+ return TreePage(self.out_dir, self.name, commit, tree_segment, repo, self.content_subdir)
diff --git a/g2h/tree_page.py b/g2h/tree_page.py
new file mode 100644
index 0000000..22e563a
--- /dev/null
+++ b/g2h/tree_page.py
@@ -0,0 +1,51 @@
+import os
+
+from .page import Page
+
+class TreePage(Page):
+ def __init__(self, out_dir, name, commit, sub_tree, repo, content_subdir):
+ super(TreePage, self).__init__('tree.md', out_dir, os.path.join(commit.hexsha, 't', sub_tree, 'index.md'), name, content_subdir)
+ self.tree = commit.tree.join(sub_tree) if sub_tree else commit.tree
+ self.repo = repo
+ self.commit = commit
+ self.sub_tree = sub_tree
+
+ def _generate_entries(self, nodes):
+ entries = []
+ for node in nodes:
+ latest_commit = next(self.repo.iter_commits(self.commit, max_count=1, paths=node.path))
+ entry = {
+ 'name': node.name + str('/' if node.type == 'tree' else ''),
+ 'modified': latest_commit.committed_datetime
+ }
+ entries.append(entry)
+ return entries
+
+ def _get_entries(self):
+ files = self._generate_entries(self.tree.blobs)
+ dirs = self._generate_entries(self.tree.trees)
+ files.sort(key=lambda x: x['name'])
+ dirs.sort(key=lambda x: x['name'])
+ return dirs + files
+
+ def context(self):
+ return {
+ 'entries': self._get_entries(),
+ 'title': self.name + ': Tree'
+ }
+
+ def build_pages(self, factory):
+ visited = set()
+ remaining = [self.sub_tree]
+ tree_pages = []
+ while remaining:
+ current = remaining.pop()
+ if not current in visited:
+ visited.add(current)
+ tree = self.commit.tree.join(current) if current else self.commit.tree
+ tree_pages.append(factory.new_tree(self.commit, tree.path, self.repo))
+ for sub_tree in tree.trees:
+ remaining.append(sub_tree.path)
+
+ return tree_pages
+
diff --git a/templates/tree.md b/templates/tree.md
new file mode 100644
index 0000000..654c9d0
--- /dev/null
+++ b/templates/tree.md
@@ -0,0 +1,10 @@
+{% include '_head.md' %}
+
+{% if entries %}
+| Name | Last modified |
+|------|---------------|
+{%- for entry in entries %}
+| {{ entry.name }} | {% if entry.modified %} {{ entry.modified.strftime('%Y-%m-%d %H:%M:%S') }} {% endif %} |{% endfor %}
+{% else %}
+No files.
+{% endif %}
diff --git a/test_repo/abc/foo b/test_repo/abc/foo
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/test_repo/abc/foo
@@ -0,0 +1 @@
+bar
diff --git a/test_repo/git.tgz b/test_repo/git.tgz
index 06a8be9..3ea5a1a 100644
--- a/test_repo/git.tgz
+++ b/test_repo/git.tgz
Binary files differ
diff --git a/test_repo/sub/dir/2/other.file b/test_repo/sub/dir/2/other.file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_repo/sub/dir/2/other.file
diff --git a/test_repo/sub/dir/test/some.file b/test_repo/sub/dir/test/some.file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_repo/sub/dir/test/some.file
diff --git a/test_repo/sub/file b/test_repo/sub/file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_repo/sub/file
diff --git a/tests/tree_test.py b/tests/tree_test.py
new file mode 100644
index 0000000..ebad5e5
--- /dev/null
+++ b/tests/tree_test.py
@@ -0,0 +1,98 @@
+import os
+from pytest import fail
+from unittest.mock import MagicMock
+
+from g2h.tree_page import TreePage
+
+def test_init():
+ repo = MagicMock()
+ commit = MagicMock()
+ commit.hexsha = '123'
+ page = TreePage('test/tree', 'tree test', commit, 'test/sub/tree', repo, '')
+ assert(page.name == 'tree test')
+ assert(page.repo == repo)
+ assert(page.commit == commit)
+ commit.tree.join.assert_called_once_with('test/sub/tree')
+ commit_no_subtree = MagicMock()
+ commit_no_subtree.hexsha = '123'
+ commit_no_subtree.tree = 'no sub tree'
+ page_no_subtree = TreePage('test/tree', 'tree test', commit_no_subtree, '', repo, '')
+ assert(page_no_subtree.name == 'tree test')
+ assert(page_no_subtree.repo == repo)
+ assert(page_no_subtree.commit == commit_no_subtree)
+ assert(page_no_subtree.tree == 'no sub tree')
+
+def add_nodes(parent, paths):
+ nodes = []
+ for node in paths:
+ nodes.append(add_node(parent, node))
+ return nodes
+
+def add_node(parent, path):
+ node = MagicMock()
+ node.trees = []
+ node.blobs = []
+ node.name = path
+ node.path = os.path.join(parent.path, path)
+ if path.endswith('/'):
+ parent.trees.append(node)
+ else:
+ parent.blobs.append(node)
+ return node
+
+def test_context_empty():
+ commit = MagicMock()
+ commit.hexsha = '123'
+ page = TreePage('test/tree', 'tree test', commit, '', None, '')
+ context = page.context()
+ assert(context['entries'] == [])
+ assert(context['title'] == 'tree test: Tree')
+
+def test_context_filled():
+ tree = MagicMock()
+ tree.trees = []
+ tree.blobs = []
+ repo = MagicMock()
+ sub_dir = add_node(tree, 'sub/')
+ sub_file = add_node(sub_dir, 'file.txt')
+ readme = add_node(tree, 'README')
+ commit = MagicMock()
+ commit.hexsha = '123'
+ commit.author.email = 'unit@te.st'
+ commit.tree = tree
+ commit.committed_datetime = 'Date Time'
+ repo.iter_commits = MagicMock(return_value=iter([commit, commit]))
+ page = TreePage('test/tree', 'tree test', commit, '', repo, '')
+ context = page.context()
+ assert(repo.iter_commits.called)
+ assert(context['title'] == 'tree test: Tree')
+ assert(context['entries'] == [{'name': 'sub/', 'modified': 'Date Time'}, {'name': 'README', 'modified': 'Date Time'}])
+
+def test_traversal():
+ factory = MagicMock()
+ def new_tree_sideeffect(*args, **kwargs):
+ return args[1]
+ factory.new_tree.side_effect = new_tree_sideeffect
+ tree = MagicMock()
+ tree.trees = []
+ tree.blobs = []
+ tree.path = ''
+ root_tree = add_nodes(tree, ['README', 'sub/'])
+ sub_tree = add_nodes(root_tree[1], ['main.cc', 'lib.cc', 'include/'])
+ add_node(sub_tree[2], 'lib.h')
+ def tree_join_sideeffect(*args, **kwargs):
+ if args[0] == 'sub/':
+ return root_tree[1]
+ elif args[0] == 'sub/include/':
+ return sub_tree[2]
+ fail('Unexpected tree join: ' + args[0])
+ tree.join.side_effect = tree_join_sideeffect
+ commit = MagicMock()
+ commit.tree = tree
+ page = TreePage('test/tree', 'tree test', commit, '', None, '')
+ pages = page.build_pages(factory)
+ assert(len(pages) == 3)
+ assert(pages[0] == '')
+ assert(pages[1] == 'sub/')
+ assert(pages[2] == 'sub/include/')
+
diff --git a/workspace.josh b/workspace.josh
index cbc3d9f..df1f87e 100644
--- a/workspace.josh
+++ b/workspace.josh
@@ -1,3 +1,2 @@
-.mise-tasks = :/mise/git
LICENSE = :/licenses/3-BSD-2025.txt
-
+.mise-tasks = :/mise/git