From 780b7fde2894a42749ac292fa3d180147afeff1a Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 15 Feb 2013 17:16:20 +0100 Subject: [PATCH] moved page helper classes to own files --- htdocs/lib/PHPGitWeb.class.php | 2 + htdocs/lib/PageClasses.difftree.class.php | 181 ++++++++++++++++++++++ htdocs/lib/PageClasses.shortlog.class.php | 143 +++++++++++++++++ htdocs/pages/commit.class.php | 162 ------------------- htdocs/pages/commitdiff.class.php | 2 - htdocs/pages/shortlog.class.php | 127 --------------- 6 files changed, 326 insertions(+), 291 deletions(-) create mode 100644 htdocs/lib/PageClasses.difftree.class.php create mode 100644 htdocs/lib/PageClasses.shortlog.class.php diff --git a/htdocs/lib/PHPGitWeb.class.php b/htdocs/lib/PHPGitWeb.class.php index dee9292..95cbe23 100644 --- a/htdocs/lib/PHPGitWeb.class.php +++ b/htdocs/lib/PHPGitWeb.class.php @@ -23,6 +23,8 @@ require_once("lib/CommitLoader.class.php"); require_once("lib/GitCommand.class.php"); require_once("lib/Tools.class.php"); require_once("lib/Validation.class.php"); +require_once("lib/PageClasses.difftree.class.php"); +require_once("lib/PageClasses.shortlog.class.php"); require_once("lib/graph.class.php"); class PHPGitWeb { diff --git a/htdocs/lib/PageClasses.difftree.class.php b/htdocs/lib/PageClasses.difftree.class.php new file mode 100644 index 0000000..4b81e34 --- /dev/null +++ b/htdocs/lib/PageClasses.difftree.class.php @@ -0,0 +1,181 @@ +. + */ + +class difftree { + private $difftree_data = null; + + public function push_difftree_data($data) { + $this->difftree_data = $data; + } + + public function generate_difftree($project, $commit, $patch_link) { + $difftree = new ContentProvider('commit', 'difftree'); + if($this->difftree_data) + $tree = $this->difftree_data; + else + $tree = GitCommand::get_commit_changes($project['path'], $commit['id'], $commit['parent']); + $entry_count = 0; + if(count($commit['parent']) > 1) + $difftree->set('class', ' combined'); + else + $difftree->set('class', ''); + $difftree->set('tree_count', ''); + foreach($tree as $entry) { + $entry_count++; + $difftree->append('tree', $this->tree_entry($entry_count, (($entry_count % 2) ? 'dark' : 'light'), $commit, $entry, $patch_link)); + } + if($entry_count == 0) + $difftree->set('tree', ''); + else if($entry_count > 10) + $difftree->set('tree_count', new ContentProvider('commit', 'tree_count', array('count' => $entry_count))); + return $difftree; + } + + private function tree_entry($diffid, $class, $commit, $entry, $patch_link) { + $tree = new ContentProvider('commit', 'tree'); + $tree->set('class', $class); + $tree->set('hash', $commit['id']); + $tree->set('file', $entry['file']); + + if(count($commit['parent']) > 1) { + if(preg_match('/^[0]{40}$/', $entry['to_id'])) //file doesn't exist in the result (child) commit + $tree->set('file', $entry['file']); + else + $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file']))); + + $tree->set('specials', ''); + + if($patch_link) + $tree->append('merge', new ContentProvider('commit', 'tree_merge_patch_link', array("patch_marker" => "patch".$diffid))); + + $has_history = false; + $not_deleted = false; + + for($i = 0; $i < count($commit['parent']); $i++) { + $status = $entry['status'][$i]; + $merge = null; + + $has_history = ($has_history || ($status != 'A')); + $not_deleted = ($not_deleted || ($status != 'D')); + + switch($status) { + case 'A': //Added + $merge = new ContentProvider('commit', 'tree_merge_new'); + break; + case 'D': //Deleted + $merge = new ContentProvider('commit', 'tree_merge'); + $merge->set('class', ''); + $blob_link = new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['parent'][$i], 'file' => $entry['file'])); + $merge->set('links', array($blob_link, ' | ')); + break; + default: + $merge = new ContentProvider('commit', 'tree_merge'); + if($entry['from_id'][$i] == $entry['to_id']) { + $merge->set('class', ' nochange'); + $merge->set('links', ' | '); + } else { + $merge->set('class', ''); + $merge->set('links', new ContentProvider('commit', 'tree_merge_diff', array('hash' => $commit['id'], 'parent' => $commit['parent'][$i], 'file' => $entry['file'], 'id' => ($i + 1)))); + } + } + $tree->append('merge', $merge); + } + + $tree->set('links', ''); + if($not_deleted) { + $tree->append('links', new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['id'], 'file' => $entry['file']))); + if($has_history) + $tree->append('links', ' | '); + } + if($has_history) + $tree->append('links', new ContentProvider('commit', 'tree_merge_history', array('hash' => $commit['id'], 'file' => $entry['file']))); + + } else { + $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file']))); + $tree->set('merge', ''); + + $from_type = Tools::get_filetype($entry['from_mode']); + $to_type = Tools::get_filetype($entry['to_mode']); + + $from_mode = (Tools::is_regular_file($entry['from_mode']) ? Tools::get_file_permissions($entry['from_mode']) : null); + $to_mode = (Tools::is_regular_file($entry['to_mode']) ? Tools::get_file_permissions($entry['to_mode']) : null); + + $link_placeholders = array( + "hash" => $commit['id'], + "file" => $entry['file'], + "parent" => (count($commit['parent']) ? $commit['parent'][0] : ""), + ); + if($patch_link) + $tree->append('links', new ContentProvider('commit', 'tree_patch_link', array("patch_marker" => "patch".$diffid))); + + switch($entry['status']) { + case 'A': //Added + $tree->set('specials', new ContentProvider('commit', (Tools::is_regular_file($entry['to_mode']) ? 'tree_new_file' : 'tree_new'), array('type' => $to_type, 'mode' => $to_mode))); + $tree->append('links', new ContentProvider('commit', 'tree_new_links', $link_placeholders)); + break; + case 'D': //Deleted + $tree->set('specials', new ContentProvider('commit', 'tree_deleted', array('type' => $from_type))); + $tree->append('links', new ContentProvider('commit', 'tree_deleted_links', $link_placeholders)); + break; + case 'M': //Modified + case 'T': //Type changed + if($entry['from_mode'] != $entry['to_mode']) { + $modified = new ContentProvider('commit', 'tree_changed'); + $tree->set('specials', $modified); + if($from_type != $to_type) + $modified->append('changes', new ContentProvider('commit', 'tree_changed_type', array('from' => $from_type, 'to' => $to_type))); + if($from_mode != $to_mode && $to_mode) { + if($from_mode) + $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode', array('from' => $from_mode, 'to' => $to_mode))); + else + $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode_to', array('to' => $to_mode))); + } + } else + $tree->set('specials', ''); + if(!$patch_link && $entry['from_id'] != $entry['to_id']) + $tree->append('links', new ContentProvider('commit', 'tree_changed_links_diff', $link_placeholders)); + $tree->append('links', new ContentProvider('commit', 'tree_changed_links', $link_placeholders)); + break; + case 'R': //Renamed + case 'C': //Copied + $actions = array('R' => 'tree_moved', 'C' => 'tree_copied'); + $move = new ContentProvider('commit', $actions[$entry['status']]); + $tree->set('specials', $move); + $tree->set('file', $entry['to_file']); + $move->set('file', $entry['from_file']); + $move->set('hash', $commit['parent'][0]); + $move->set('similarity', $entry['similarity']); + if($from_mode != $to_mode) + $move->set('mode', new ContentProvider('commit', 'tree_moved_mode', array('mode' => $to_mode))); + else + $move->set('mode', ''); + if($entry['from_id'] != $entry['to_id']) + $tree->append('links', new ContentProvider('commit', 'tree_moved_links_diff', $link_placeholders)); + $tree->append('links', new ContentProvider('commit', 'tree_moved_links', $link_placeholders)); + break; + default: + + } + + } + return $tree; + } + +} + +?> \ No newline at end of file diff --git a/htdocs/lib/PageClasses.shortlog.class.php b/htdocs/lib/PageClasses.shortlog.class.php new file mode 100644 index 0000000..75573f2 --- /dev/null +++ b/htdocs/lib/PageClasses.shortlog.class.php @@ -0,0 +1,143 @@ +. + */ + +class shortlog { + private $project; + private $graph_data; + private $first_commit; + private $have_more = false; + + public function generate_shortlog($project, $head, $max, $skip, $file = null, $pages = true, $next_page = 0) { + $this->project = $project; + if($head && strtolower($head) == 'all') + $head = null; + $content = new ContentProvider('shortlog', 'shortlog'); + + if(GitConfig::GITGRAPH_ENABLE) { + if($max+$skip >= 2000) { //only load the last 2k commits + $real_skip = ($max+$skip) - 2000; + $skip -= $real_skip; + } else + $real_skip = 0; + } else { + $real_skip = $skip; + $skip = 0; + } + + $commits = GitCommand::get_commits($project['path'], $head, $max+$skip+1, $real_skip, $file); + + if(GitConfig::GITGRAPH_ENABLE) { + $this->graph_data = new graph_data_generator(); + if($head == null) { + //add all refs to the graph + $rhash = GitCommand::get_hash($project['path'], "HEAD"); + if($rhash) + $this->graph_data->add_branch($rhash, null); + + foreach($this->project['refs'] as $ref => $rhash) { + if(preg_match('#^refs/heads/#i', $ref) && preg_match('/^[a-f0-9]*$/i', $rhash)) { + $this->graph_data->add_branch($rhash, $ref); + } + } + foreach($this->project['refs'] as $ref => $rhash) { + if(preg_match('#^refs/remotes/#i', $ref) && preg_match('/^[a-f0-9]*$/i', $rhash)) { + $this->graph_data->add_branch($rhash, $ref); + } + } + } + $this->graph_data->parse($commits); + $content->set('graph_data', $this->graph_data->get_header_graph()); + } + + $commit_counter = 0; + $this->first_commit = $commits[0]; + foreach($commits as $commit) { + $commit_counter++; + if($commit_counter <= $skip) + continue; + if($commit_counter > $max+$skip) { + $this->have_more = true; + if($pages) { + $content->append('entries', new ContentProvider('shortlog', 'shortlog_page', array("page" => $next_page))); + } else + $content->append('entries', new ContentProvider('shortlog', 'shortlog_more')); + } else + $content->append('entries', $this->shortlog_entry(($commit_counter % 2 ? 'dark' : 'light'), $commit)); + } + + return $content; + } + + public function get_first_commit() { + return $this->first_commit; + } + + public function get_have_more() { + return $this->have_more; + } + + private function shortlog_entry($class, $commit) { + $entry = new ContentProvider('shortlog', 'shortlog_entry'); + $entry->set('class', $class); + $entry->set('hash', $commit['id']); + $entry->set('author', htmlentities($commit['author'])); + $entry->set('message', htmlentities(Tools::chop_text($commit['text'], 50, 5))); + $age = time() - $commit['committer_time']; + $date_str = date("Y-m-d", ($commit['committer_time'] ? $commit['committer_time'] : $commit['author_time'])); + $age_calc = Tools::age_calculate($commit['committer_time']); + $age_str = $age_calc['age_str']; + if($age > 60*60*24*7*2) { + $entry->set('date', $age_str); + $entry->set('age', $date_str); + } else { + $entry->set('date', $date_str); + $entry->set('age', $age_str); + } + if(GitConfig::GITGRAPH_ENABLE) + $entry->set('graph_data', $this->graph_data->get_graph($commit['id'])); + + $entry->set('refs', $this->shortlog_commit_refs($this->project, $commit['id'])); + + return $entry; + } + + public function shortlog_commit_refs($project, $hash) { + if(!is_array($project['refs'])) + return ""; + $refs = new ContentProvider('shortlog', 'shortlog_refs'); + $found = false; + foreach($project['refs'] as $ref => $rhash) { + if(strtolower($rhash) == strtolower($hash)) { + $refexp = explode('/', $ref, 3); + $reftype = $refexp[1]; + if($reftype == 'heads') + $reftype = 'head'; + else if($reftype == 'remotes') + $reftype = 'remote'; + else if($reftype == 'tags') + $reftype = 'tag'; + $refs->append('refs', new ContentProvider('shortlog', 'shortlog_ref_'.$reftype, array("name" => $refexp[2], "ref_link" => $ref))); + $found = true; + } + } + return ($found ? $refs : ""); + } + +} + +?> \ No newline at end of file diff --git a/htdocs/pages/commit.class.php b/htdocs/pages/commit.class.php index 57f20e5..55ece54 100644 --- a/htdocs/pages/commit.class.php +++ b/htdocs/pages/commit.class.php @@ -16,168 +16,6 @@ * along with this program. If not, see . */ -class difftree { - private $difftree_data = null; - - public function push_difftree_data($data) { - $this->difftree_data = $data; - } - - public function generate_difftree($project, $commit, $patch_link) { - $difftree = new ContentProvider('commit', 'difftree'); - if($this->difftree_data) - $tree = $this->difftree_data; - else - $tree = GitCommand::get_commit_changes($project['path'], $commit['id'], $commit['parent']); - $entry_count = 0; - if(count($commit['parent']) > 1) - $difftree->set('class', ' combined'); - else - $difftree->set('class', ''); - $difftree->set('tree_count', ''); - foreach($tree as $entry) { - $entry_count++; - $difftree->append('tree', $this->tree_entry($entry_count, (($entry_count % 2) ? 'dark' : 'light'), $commit, $entry, $patch_link)); - } - if($entry_count == 0) - $difftree->set('tree', ''); - else if($entry_count > 10) - $difftree->set('tree_count', new ContentProvider('commit', 'tree_count', array('count' => $entry_count))); - return $difftree; - } - - private function tree_entry($diffid, $class, $commit, $entry, $patch_link) { - $tree = new ContentProvider('commit', 'tree'); - $tree->set('class', $class); - $tree->set('hash', $commit['id']); - $tree->set('file', $entry['file']); - - if(count($commit['parent']) > 1) { - if(preg_match('/^[0]{40}$/', $entry['to_id'])) //file doesn't exist in the result (child) commit - $tree->set('file', $entry['file']); - else - $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file']))); - - $tree->set('specials', ''); - - if($patch_link) - $tree->append('merge', new ContentProvider('commit', 'tree_merge_patch_link', array("patch_marker" => "patch".$diffid))); - - $has_history = false; - $not_deleted = false; - - for($i = 0; $i < count($commit['parent']); $i++) { - $status = $entry['status'][$i]; - $merge = null; - - $has_history = ($has_history || ($status != 'A')); - $not_deleted = ($not_deleted || ($status != 'D')); - - switch($status) { - case 'A': //Added - $merge = new ContentProvider('commit', 'tree_merge_new'); - break; - case 'D': //Deleted - $merge = new ContentProvider('commit', 'tree_merge'); - $merge->set('class', ''); - $blob_link = new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['parent'][$i], 'file' => $entry['file'])); - $merge->set('links', array($blob_link, ' | ')); - break; - default: - $merge = new ContentProvider('commit', 'tree_merge'); - if($entry['from_id'][$i] == $entry['to_id']) { - $merge->set('class', ' nochange'); - $merge->set('links', ' | '); - } else { - $merge->set('class', ''); - $merge->set('links', new ContentProvider('commit', 'tree_merge_diff', array('hash' => $commit['id'], 'parent' => $commit['parent'][$i], 'file' => $entry['file'], 'id' => ($i + 1)))); - } - } - $tree->append('merge', $merge); - } - - $tree->set('links', ''); - if($not_deleted) { - $tree->append('links', new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['id'], 'file' => $entry['file']))); - if($has_history) - $tree->append('links', ' | '); - } - if($has_history) - $tree->append('links', new ContentProvider('commit', 'tree_merge_history', array('hash' => $commit['id'], 'file' => $entry['file']))); - - } else { - $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file']))); - $tree->set('merge', ''); - - $from_type = Tools::get_filetype($entry['from_mode']); - $to_type = Tools::get_filetype($entry['to_mode']); - - $from_mode = (Tools::is_regular_file($entry['from_mode']) ? Tools::get_file_permissions($entry['from_mode']) : null); - $to_mode = (Tools::is_regular_file($entry['to_mode']) ? Tools::get_file_permissions($entry['to_mode']) : null); - - $link_placeholders = array( - "hash" => $commit['id'], - "file" => $entry['file'], - "parent" => (count($commit['parent']) ? $commit['parent'][0] : ""), - ); - if($patch_link) - $tree->append('links', new ContentProvider('commit', 'tree_patch_link', array("patch_marker" => "patch".$diffid))); - - switch($entry['status']) { - case 'A': //Added - $tree->set('specials', new ContentProvider('commit', (Tools::is_regular_file($entry['to_mode']) ? 'tree_new_file' : 'tree_new'), array('type' => $to_type, 'mode' => $to_mode))); - $tree->append('links', new ContentProvider('commit', 'tree_new_links', $link_placeholders)); - break; - case 'D': //Deleted - $tree->set('specials', new ContentProvider('commit', 'tree_deleted', array('type' => $from_type))); - $tree->append('links', new ContentProvider('commit', 'tree_deleted_links', $link_placeholders)); - break; - case 'M': //Modified - case 'T': //Type changed - if($entry['from_mode'] != $entry['to_mode']) { - $modified = new ContentProvider('commit', 'tree_changed'); - $tree->set('specials', $modified); - if($from_type != $to_type) - $modified->append('changes', new ContentProvider('commit', 'tree_changed_type', array('from' => $from_type, 'to' => $to_type))); - if($from_mode != $to_mode && $to_mode) { - if($from_mode) - $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode', array('from' => $from_mode, 'to' => $to_mode))); - else - $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode_to', array('to' => $to_mode))); - } - } else - $tree->set('specials', ''); - if(!$patch_link && $entry['from_id'] != $entry['to_id']) - $tree->append('links', new ContentProvider('commit', 'tree_changed_links_diff', $link_placeholders)); - $tree->append('links', new ContentProvider('commit', 'tree_changed_links', $link_placeholders)); - break; - case 'R': //Renamed - case 'C': //Copied - $actions = array('R' => 'tree_moved', 'C' => 'tree_copied'); - $move = new ContentProvider('commit', $actions[$entry['status']]); - $tree->set('specials', $move); - $tree->set('file', $entry['to_file']); - $move->set('file', $entry['from_file']); - $move->set('hash', $commit['parent'][0]); - $move->set('similarity', $entry['similarity']); - if($from_mode != $to_mode) - $move->set('mode', new ContentProvider('commit', 'tree_moved_mode', array('mode' => $to_mode))); - else - $move->set('mode', ''); - if($entry['from_id'] != $entry['to_id']) - $tree->append('links', new ContentProvider('commit', 'tree_moved_links_diff', $link_placeholders)); - $tree->append('links', new ContentProvider('commit', 'tree_moved_links', $link_placeholders)); - break; - default: - - } - - } - return $tree; - } - -} - class page_commit { private $page, $phpgitweb; diff --git a/htdocs/pages/commitdiff.class.php b/htdocs/pages/commitdiff.class.php index 5ef3fa0..096385c 100644 --- a/htdocs/pages/commitdiff.class.php +++ b/htdocs/pages/commitdiff.class.php @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -require_once("pages/commit.class.php"); - class page_commitdiff { private $page, $phpgitweb; diff --git a/htdocs/pages/shortlog.class.php b/htdocs/pages/shortlog.class.php index 091e1d1..a3419bd 100644 --- a/htdocs/pages/shortlog.class.php +++ b/htdocs/pages/shortlog.class.php @@ -16,133 +16,6 @@ * along with this program. If not, see . */ -require_once('lib/graph.class.php'); - -class shortlog { - private $project; - private $graph_data; - private $first_commit; - private $have_more = false; - - public function generate_shortlog($project, $head, $max, $skip, $file = null, $pages = true, $next_page = 0) { - $this->project = $project; - if($head && strtolower($head) == 'all') - $head = null; - $content = new ContentProvider('shortlog', 'shortlog'); - - if(GitConfig::GITGRAPH_ENABLE) { - if($max+$skip >= 2000) { //only load the last 2k commits - $real_skip = ($max+$skip) - 2000; - $skip -= $real_skip; - } else - $real_skip = 0; - } else { - $real_skip = $skip; - $skip = 0; - } - - $commits = GitCommand::get_commits($project['path'], $head, $max+$skip+1, $real_skip, $file); - - if(GitConfig::GITGRAPH_ENABLE) { - $this->graph_data = new graph_data_generator(); - if($head == null) { - //add all refs to the graph - $rhash = GitCommand::get_hash($project['path'], "HEAD"); - if($rhash) - $this->graph_data->add_branch($rhash, null); - - foreach($this->project['refs'] as $ref => $rhash) { - if(preg_match('#^refs/heads/#i', $ref) && preg_match('/^[a-f0-9]*$/i', $rhash)) { - $this->graph_data->add_branch($rhash, $ref); - } - } - foreach($this->project['refs'] as $ref => $rhash) { - if(preg_match('#^refs/remotes/#i', $ref) && preg_match('/^[a-f0-9]*$/i', $rhash)) { - $this->graph_data->add_branch($rhash, $ref); - } - } - } - $this->graph_data->parse($commits); - $content->set('graph_data', $this->graph_data->get_header_graph()); - } - - $commit_counter = 0; - $this->first_commit = $commits[0]; - foreach($commits as $commit) { - $commit_counter++; - if($commit_counter <= $skip) - continue; - if($commit_counter > $max+$skip) { - $this->have_more = true; - if($pages) { - $content->append('entries', new ContentProvider('shortlog', 'shortlog_page', array("page" => $next_page))); - } else - $content->append('entries', new ContentProvider('shortlog', 'shortlog_more')); - } else - $content->append('entries', $this->shortlog_entry(($commit_counter % 2 ? 'dark' : 'light'), $commit)); - } - - return $content; - } - - public function get_first_commit() { - return $this->first_commit; - } - - public function get_have_more() { - return $this->have_more; - } - - private function shortlog_entry($class, $commit) { - $entry = new ContentProvider('shortlog', 'shortlog_entry'); - $entry->set('class', $class); - $entry->set('hash', $commit['id']); - $entry->set('author', htmlentities($commit['author'])); - $entry->set('message', htmlentities(Tools::chop_text($commit['text'], 50, 5))); - $age = time() - $commit['committer_time']; - $date_str = date("Y-m-d", ($commit['committer_time'] ? $commit['committer_time'] : $commit['author_time'])); - $age_calc = Tools::age_calculate($commit['committer_time']); - $age_str = $age_calc['age_str']; - if($age > 60*60*24*7*2) { - $entry->set('date', $age_str); - $entry->set('age', $date_str); - } else { - $entry->set('date', $date_str); - $entry->set('age', $age_str); - } - if(GitConfig::GITGRAPH_ENABLE) - $entry->set('graph_data', $this->graph_data->get_graph($commit['id'])); - - $entry->set('refs', $this->shortlog_commit_refs($this->project, $commit['id'])); - - return $entry; - } - - public function shortlog_commit_refs($project, $hash) { - if(!is_array($project['refs'])) - return ""; - $refs = new ContentProvider('shortlog', 'shortlog_refs'); - $found = false; - foreach($project['refs'] as $ref => $rhash) { - if(strtolower($rhash) == strtolower($hash)) { - $refexp = explode('/', $ref, 3); - $reftype = $refexp[1]; - if($reftype == 'heads') - $reftype = 'head'; - else if($reftype == 'remotes') - $reftype = 'remote'; - else if($reftype == 'tags') - $reftype = 'tag'; - $refs->append('refs', new ContentProvider('shortlog', 'shortlog_ref_'.$reftype, array("name" => $refexp[2], "ref_link" => $ref))); - $found = true; - } - } - return ($found ? $refs : ""); - } - -} - - class page_shortlog { private $page, $phpgitweb; -- 2.20.1