diff options
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | g2h/cli.py | 3 | ||||
| -rw-r--r-- | g2h/git.py | 3 | ||||
| -rw-r--r-- | g2h/page_factory.py | 4 | ||||
| -rw-r--r-- | g2h/tree_page.py | 51 | ||||
| -rw-r--r-- | templates/tree.md | 10 | ||||
| -rw-r--r-- | test_repo/abc/foo | 1 | ||||
| -rw-r--r-- | test_repo/git.tgz | bin | 16230 -> 22304 bytes | |||
| -rw-r--r-- | test_repo/sub/dir/2/other.file | 0 | ||||
| -rw-r--r-- | test_repo/sub/dir/test/some.file | 0 | ||||
| -rw-r--r-- | test_repo/sub/file | 0 | ||||
| -rw-r--r-- | tests/tree_test.py | 98 | ||||
| -rw-r--r-- | workspace.josh | 3 |
13 files changed, 174 insertions, 4 deletions
@@ -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. @@ -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() @@ -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 Binary files differindex 06a8be9..3ea5a1a 100644 --- a/test_repo/git.tgz +++ b/test_repo/git.tgz 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 |