From 50f6a1004bbd85953a0580a47deb39cb9ec93510 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Sat, 15 Sep 2012 14:13:40 -0300 Subject: [PATCH] Implementing search feature for trees and commit log --- src/GitList/Controller/CommitController.php | 22 +++++++++++ src/GitList/Controller/TreeController.php | 24 ++++++++++++ src/GitList/Git/Repository.php | 41 ++++++++++++++++++++ views/commits_list.twig | 2 + views/layout_page.twig | 11 ++++++ views/search.twig | 42 +++++++++++++++++++++ views/searchcommits.twig | 13 +++++++ web/css/style.css | 3 +- web/less/forms.less | 13 ++++--- 9 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 views/search.twig create mode 100644 views/searchcommits.twig diff --git a/src/GitList/Controller/CommitController.php b/src/GitList/Controller/CommitController.php index 658df9a..ee60e12 100644 --- a/src/GitList/Controller/CommitController.php +++ b/src/GitList/Controller/CommitController.php @@ -6,6 +6,7 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Silex\ControllerCollection; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; class CommitController implements ControllerProviderInterface { @@ -43,6 +44,27 @@ class CommitController implements ControllerProviderInterface ->value('file', '') ->bind('commits'); + $route->post('{repo}/commits/search', function(Request $request, $repo) use ($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $commits = $repository->searchCommitLog($request->get('query')); + + foreach ($commits as $commit) { + $date = $commit->getDate(); + $date = $date->format('m/d/Y'); + $categorized[$date][] = $commit; + } + + return $app['twig']->render('searchcommits.twig', array( + 'repo' => $repo, + 'branch' => 'master', + 'file' => '', + 'commits' => $categorized, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); + })->assert('repo', '[\w-._]+') + ->bind('searchcommits'); + $route->get('{repo}/commit/{commit}/', function($repo, $commit) use ($app) { $repository = $app['git']->getRepository($app['git.repos'] . $repo); $commit = $repository->getCommit($commit); diff --git a/src/GitList/Controller/TreeController.php b/src/GitList/Controller/TreeController.php index c8a9708..4abe17f 100644 --- a/src/GitList/Controller/TreeController.php +++ b/src/GitList/Controller/TreeController.php @@ -7,6 +7,7 @@ use Silex\ControllerProviderInterface; use Silex\ControllerCollection; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\Request; class TreeController implements ControllerProviderInterface { @@ -45,6 +46,29 @@ class TreeController implements ControllerProviderInterface ->assert('tree', '.+') ->bind('tree'); + $route->post('{repo}/tree/{branch}/search', function(Request $request, $repo, $branch = '', $tree = '') use ($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + + if (!$branch) { + $branch = $repository->getHead(); + } + + $breadcrumbs = $app['util.view']->getBreadcrumbs($tree); + $results = $repository->searchTree($request->get('query'), $branch); + + return $app['twig']->render('search.twig', array( + 'results' => $results, + 'repo' => $repo, + 'branch' => $branch, + 'path' => $tree, + 'breadcrumbs' => $breadcrumbs, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); + })->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+') + ->bind('search'); + $route->get('{repo}/{branch}/', function($repo, $branch) use ($app, $treeController) { return $treeController($repo, $branch); })->assert('repo', '[\w-._]+') diff --git a/src/GitList/Git/Repository.php b/src/GitList/Git/Repository.php index c146602..2c6202b 100644 --- a/src/GitList/Git/Repository.php +++ b/src/GitList/Git/Repository.php @@ -35,6 +35,47 @@ class Repository extends BaseRepository return $commits; } + public function searchCommitLog($query) + { + $command = "log --grep='$query' --pretty=format:'%H%h%T%P%an%ae%at%cn%ce%ct'"; + + $logs = $this->getPrettyFormat($command); + + foreach ($logs as $log) { + $commit = new Commit; + $commit->importData($log); + $commits[] = $commit; + } + + return $commits; + } + + public function searchTree($query, $branch) + { + try { + $results = $this->getClient()->run($this, "grep --line-number '$query' $branch"); + } catch (\RuntimeException $e) { + return false; + } + + $results = explode("\n", $results); + + foreach ($results as $result) { + if ($result == '') { + continue; + } + + preg_match_all('/([\w-._]+):(.+):([0-9]+):(.+)/', $result, $matches, PREG_SET_ORDER); + $data['branch'] = $matches[0][1]; + $data['file'] = $matches[0][2]; + $data['line'] = $matches[0][3]; + $data['match'] = $matches[0][4]; + $searchResults[] = $data; + } + + return $searchResults; + } + public function getAuthorStatistics() { $logs = $this->getClient()->run($this, 'log --pretty=format:"%an||%ae" ' . $this->getHead()); diff --git a/views/commits_list.twig b/views/commits_list.twig index e0ec786..959d9ff 100644 --- a/views/commits_list.twig +++ b/views/commits_list.twig @@ -20,6 +20,7 @@ {% endfor %} +{% if page != 'searchcommits' %} +{% endif %} \ No newline at end of file diff --git a/views/layout_page.twig b/views/layout_page.twig index 370fb0b..97f51f3 100644 --- a/views/layout_page.twig +++ b/views/layout_page.twig @@ -6,9 +6,20 @@
+ {% if page == 'commits' %} + + {% else %} + + {% endif %} + {% if branches is defined %} {% include 'branch_menu.twig' %} {% endif %} + {% include 'menu.twig' %}
diff --git a/views/search.twig b/views/search.twig new file mode 100644 index 0000000..d7804d3 --- /dev/null +++ b/views/search.twig @@ -0,0 +1,42 @@ +{% extends 'layout_page.twig' %} + +{% set page = 'files' %} + +{% block title %}GitList{% endblock %} + +{% block content %} + {% embed 'breadcrumb.twig' with {breadcrumbs: breadcrumbs} %} + {% block extra %} +
+
+ ZIP + TAR +
+ +
+ {% endblock %} + {% endembed %} + + {% if results %} + + + + + + + + + {% for result in results %} + + + + + {% endfor %} + +
namematch
{{ result.file }}{{ result.match }}
+ {% else %} +

No results found.

+ {% endif %} + +
+{% endblock %} diff --git a/views/searchcommits.twig b/views/searchcommits.twig new file mode 100644 index 0000000..8b2969b --- /dev/null +++ b/views/searchcommits.twig @@ -0,0 +1,13 @@ +{% extends 'layout_page.twig' %} + +{% set page = 'searchcommits' %} + +{% block title %}GitList{% endblock %} + +{% block content %} + {% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Commits search results', path:''}]} %} + + {% include 'commits_list.twig' %} + +
+{% endblock %} diff --git a/web/css/style.css b/web/css/style.css index a8b5fcf..f2fe4b0 100644 --- a/web/css/style.css +++ b/web/css/style.css @@ -193,7 +193,8 @@ input:focus:required:invalid,textarea:focus:required:invalid,select:focus:requir .input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} .input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} -.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;} +.form-search{margin:3px 0 0 0;padding:0;} +.search-query{padding:3px;margin-bottom:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;font-size:11px;} .form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;} .form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;} .form-search label,.form-inline label{display:inline-block;} diff --git a/web/less/forms.less b/web/less/forms.less index 7d967c6..3d0185d 100755 --- a/web/less/forms.less +++ b/web/less/forms.less @@ -466,13 +466,16 @@ select:focus:required:invalid { // SEARCH FORM // ----------- +.form-search { + margin: 3px 0 0 0; + padding: 0; +} + .search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ + padding: 3px; margin-bottom: 0; // remove the default margin on all inputs - .border-radius(14px); + .border-radius(4px); + font-size: 11px; }