Merge pull request #286 from klaussilveira/multidir

Multiple directory support
This commit is contained in:
Klaus Silveira
2013-04-03 06:14:32 -07:00
18 changed files with 249 additions and 93 deletions

View File

@@ -4,15 +4,32 @@ if (!isset($config)) {
die("No configuration object provided."); die("No configuration object provided.");
} }
$config->set('git', 'repositories', rtrim($config->get('git', 'repositories'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR); $repositories = $config->get('git', 'repositories');
if (!is_array($repositories)) {
# Convert the single item to an array - this is the internal handling
$repositories = array($repositories);
}
$tmp_arr = array();
foreach ($repositories as $repo) {
$tmp = rtrim($repo, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$tmp_arr []= $tmp;
}
$repositories = $tmp_arr;
// Startup and configure Silex application // Startup and configure Silex application
$app = new GitList\Application($config, __DIR__); $app = new GitList\Application($config, __DIR__);
// Mount the controllers // Mount the controllers
$app->mount('', new GitList\Controller\MainController()); $app->mount('', new GitList\Controller\MainController());
$app->mount('', new GitList\Controller\BlobController()); $app->mount('', new GitList\Controller\BlobController());
$app->mount('', new GitList\Controller\CommitController()); $app->mount('', new GitList\Controller\CommitController());
$app->mount('', new GitList\Controller\TreeController()); $app->mount('', new GitList\Controller\TreeController());
return $app; return $app;

View File

@@ -3,6 +3,19 @@ client = '/usr/bin/git' ; Your git executable path
repositories = '/var/www/projects/' ; Path to your repositories repositories = '/var/www/projects/' ; Path to your repositories
default_branch = 'master' ; Default branch when HEAD is detached default_branch = 'master' ; Default branch when HEAD is detached
;Windows Users
;client = '"C:\Program Files (x86)\Git\bin\git.exe"' ; Your git executable path
;repositories = 'C:\Path\to\Repos\' ; Path to your repositories
; If you want to specify multiple paths, use the array syntax instead:
;
;repositories[] = '/var/www/projects/'
;repositories[] = '/var/www/subdir/more_projects/'
;repositories[] = '/home/user/even_more_projects/'
; You can hide repositories from GitList, just copy this for each repository you want to hide ; You can hide repositories from GitList, just copy this for each repository you want to hide
; hidden[] = '/var/www/projects/BetaTest' ; hidden[] = '/var/www/projects/BetaTest'

View File

@@ -20,4 +20,6 @@ require 'vendor/autoload.php';
$config = GitList\Config::fromFile('config.ini'); $config = GitList\Config::fromFile('config.ini');
$app = require 'boot.php'; $app = require 'boot.php';
$app->run(); $app->run();

View File

@@ -30,19 +30,38 @@ class Application extends SilexApplication
$this['debug'] = $config->get('app', 'debug'); $this['debug'] = $config->get('app', 'debug');
$this['filetypes'] = $config->getSection('filetypes'); $this['filetypes'] = $config->getSection('filetypes');
$this['cache.archives'] = $root . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'archives'; $this['cache.archives'] = $root . DIRECTORY_SEPARATOR
. 'cache' . DIRECTORY_SEPARATOR . 'archives';
// Register services // Register services
$this->register(new TwigServiceProvider(), array( $this->register(new TwigServiceProvider(), array(
'twig.path' => $root . DIRECTORY_SEPARATOR . 'views', 'twig.path' => $root . DIRECTORY_SEPARATOR . 'views',
'twig.options' => array('cache' => $root . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'views'), 'twig.options' => array('cache' => $root . DIRECTORY_SEPARATOR
. 'cache' . DIRECTORY_SEPARATOR . 'views'),
)); ));
$repositories = $config->get('git', 'repositories');
$cached_repos = $config->get('app', 'cached_repos');
if (false === $cached_repos || empty($cached_repos)) {
$cached_repos = $root . DIRECTORY_SEPARATOR . 'cache'
. DIRECTORY_SEPARATOR . 'repos.json';
}
$this->register(new GitServiceProvider(), array( $this->register(new GitServiceProvider(), array(
'git.client' => $config->get('git', 'client'), 'git.client' => $config->get('git', 'client'),
'git.repos' => $config->get('git', 'repositories'), 'git.repos' => $repositories,
'git.hidden' => $config->get('git', 'hidden') ? $config->get('git', 'hidden') : array(), 'cache.repos' => $cached_repos,
'ini.file' => "config.ini",
'git.hidden' => $config->get('git', 'hidden') ?
$config->get('git', 'hidden') : array(),
'git.default_branch' => $config->get('git', 'default_branch') ? $config->get('git', 'default_branch') : 'master', 'git.default_branch' => $config->get('git', 'default_branch') ? $config->get('git', 'default_branch') : 'master',
)); ));
$cached_repos = $root . DIRECTORY_SEPARATOR .
'cache' . DIRECTORY_SEPARATOR . 'repos.json';
$this->register(new ViewUtilServiceProvider()); $this->register(new ViewUtilServiceProvider());
$this->register(new RepositoryUtilServiceProvider()); $this->register(new RepositoryUtilServiceProvider());
$this->register(new UrlGeneratorServiceProvider()); $this->register(new UrlGeneratorServiceProvider());
@@ -55,6 +74,7 @@ class Application extends SilexApplication
return $twig; return $twig;
})); }));
// Handle errors // Handle errors
$this->error(function (\Exception $e, $code) use ($app) { $this->error(function (\Exception $e, $code) use ($app) {
if ($app['debug']) { if ($app['debug']) {
@@ -67,3 +87,4 @@ class Application extends SilexApplication
}); });
} }
} }

View File

@@ -51,8 +51,24 @@ class Config
protected function validateOptions() protected function validateOptions()
{ {
if (!$this->get('git', 'repositories') || !is_dir($this->get('git', 'repositories'))) { $at_least_one_ok = false;
$at_least_one_wrong = false;
foreach ($this->get('git', 'repositories') as $dir) {
if (!$dir || !is_dir($dir)) {
$at_least_one_wrong = true;
} else {
$at_least_one_ok = true;
}
}
if (!$at_least_one_ok) {
die("Please, edit the config file and provide your repositories directory"); die("Please, edit the config file and provide your repositories directory");
} }
if ($at_least_one_wrong) {
die("One or more of the supplied repository paths appears to be wrong. Please, check the config file");
} }
} }
}

View File

@@ -13,7 +13,7 @@ class BlobController implements ControllerProviderInterface
$route = $app['controllers_factory']; $route = $app['controllers_factory'];
$route->get('{repo}/blob/{commitishPath}', function ($repo, $commitishPath) use ($app) { $route->get('{repo}/blob/{commitishPath}', function ($repo, $commitishPath) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
list($branch, $file) = $app['util.routing'] list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitishPath, $repo); ->parseCommitishPathParam($commitishPath, $repo);
@@ -47,7 +47,7 @@ class BlobController implements ControllerProviderInterface
->bind('blob'); ->bind('blob');
$route->get('{repo}/raw/{commitishPath}', function ($repo, $commitishPath) use ($app) { $route->get('{repo}/raw/{commitishPath}', function ($repo, $commitishPath) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
list($branch, $file) = $app['util.routing'] list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitishPath, $repo); ->parseCommitishPathParam($commitishPath, $repo);
@@ -73,3 +73,4 @@ class BlobController implements ControllerProviderInterface
return $route; return $route;
} }
} }

View File

@@ -13,7 +13,7 @@ class CommitController implements ControllerProviderInterface
$route = $app['controllers_factory']; $route = $app['controllers_factory'];
$route->get('{repo}/commits/{commitishPath}', function ($repo, $commitishPath) use ($app) { $route->get('{repo}/commits/{commitishPath}', function ($repo, $commitishPath) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
if ($commitishPath === null) { if ($commitishPath === null) {
$commitishPath = $repository->getHead(); $commitishPath = $repository->getHead();
@@ -53,9 +53,10 @@ class CommitController implements ControllerProviderInterface
->bind('commits'); ->bind('commits');
$route->post('{repo}/commits/{branch}/search', function (Request $request, $repo, $branch = '') use ($app) { $route->post('{repo}/commits/{branch}/search', function (Request $request, $repo, $branch = '') use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
$query = $request->get('query'); $query = $request->get('query');
$commits = $repository->searchCommitLog($query);
$commits = $repository->searchCommitLog($request->get('query'));
$categorized = array(); $categorized = array();
foreach ($commits as $commit) { foreach ($commits as $commit) {
@@ -78,7 +79,7 @@ class CommitController implements ControllerProviderInterface
->bind('searchcommits'); ->bind('searchcommits');
$route->get('{repo}/commit/{commit}', function ($repo, $commit) use ($app) { $route->get('{repo}/commit/{commit}', function ($repo, $commit) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
$commit = $repository->getCommit($commit); $commit = $repository->getCommit($commit);
$branch = $repository->getHead(); $branch = $repository->getHead();
@@ -92,7 +93,7 @@ class CommitController implements ControllerProviderInterface
->bind('commit'); ->bind('commit');
$route->get('{repo}/blame/{commitishPath}', function ($repo, $commitishPath) use ($app) { $route->get('{repo}/blame/{commitishPath}', function ($repo, $commitishPath) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
list($branch, $file) = $app['util.routing'] list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitishPath, $repo); ->parseCommitishPathParam($commitishPath, $repo);
@@ -116,3 +117,4 @@ class CommitController implements ControllerProviderInterface
return $route; return $route;
} }
} }

View File

@@ -5,6 +5,7 @@ namespace GitList\Controller;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
class MainController implements ControllerProviderInterface class MainController implements ControllerProviderInterface
{ {
@@ -13,27 +14,29 @@ class MainController implements ControllerProviderInterface
$route = $app['controllers_factory']; $route = $app['controllers_factory'];
$route->get('/', function() use ($app) { $route->get('/', function() use ($app) {
$repositories = array_map( $repositories = $app['git']->getRepositories($app['git.repos']);
function ($repo) use ($app) {
$repo['relativePath'] = $app['util.routing']->getRelativePath($repo['path']);
$repository = $app['git']->getRepository($repo['path']);
$repo['branch'] = $repository->getHead();
return $repo;
},
$app['git']->getRepositories($app['git.repos'])
);
return $app['twig']->render('index.twig', array( return $app['twig']->render('index.twig', array(
'repositories' => $repositories, 'repositories' => $repositories,
)); ));
})->bind('homepage'); })->bind('homepage');
$route->get('/refresh', function(Request $request) use ($app ) {
$app['git']->deleteCached();
# Go back to calling page
return $app->redirect($request->headers->get('Referer'));
})->bind('refresh');
$route->get('{repo}/stats/{branch}', function($repo, $branch) use ($app) { $route->get('{repo}/stats/{branch}', function($repo, $branch) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
if ($branch === null) { if ($branch === null) {
$branch = $repository->getHead(); $branch = $repository->getHead();
} }
$stats = $repository->getStatistics($branch); $stats = $repository->getStatistics($branch);
$authors = $repository->getAuthorStatistics(); $authors = $repository->getAuthorStatistics();
@@ -51,7 +54,7 @@ class MainController implements ControllerProviderInterface
->bind('stats'); ->bind('stats');
$route->get('{repo}/{branch}/rss/', function($repo, $branch) use ($app) { $route->get('{repo}/{branch}/rss/', function($repo, $branch) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
if ($branch === null) { if ($branch === null) {
$branch = $repository->getHead(); $branch = $repository->getHead();
@@ -74,3 +77,4 @@ class MainController implements ControllerProviderInterface
return $route; return $route;
} }
} }

View File

@@ -14,7 +14,7 @@ class TreeController implements ControllerProviderInterface
$route = $app['controllers_factory']; $route = $app['controllers_factory'];
$route->get('{repo}/tree/{commitishPath}/', $treeController = function ($repo, $commitishPath = '') use ($app) { $route->get('{repo}/tree/{commitishPath}/', $treeController = function ($repo, $commitishPath = '') use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
if (!$commitishPath) { if (!$commitishPath) {
$commitishPath = $repository->getHead(); $commitishPath = $repository->getHead();
} }
@@ -41,15 +41,14 @@ class TreeController implements ControllerProviderInterface
'breadcrumbs' => $breadcrumbs, 'breadcrumbs' => $breadcrumbs,
'branches' => $repository->getBranches(), 'branches' => $repository->getBranches(),
'tags' => $repository->getTags(), 'tags' => $repository->getTags(),
'readme' => $app['util.repository']->getReadme($repo, $branch), 'readme' => $app['util.repository']->getReadme($repository, $branch),
)); ));
})->assert('repo', $app['util.routing']->getRepositoryRegex()) })->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex())
->bind('tree'); ->bind('tree');
$route->post('{repo}/tree/{branch}/search', function (Request $request, $repo, $branch = '', $tree = '') use ($app) { $route->post('{repo}/tree/{branch}/search', function (Request $request, $repo, $branch = '', $tree = '') use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
if (!$branch) { if (!$branch) {
$branch = $repository->getHead(); $branch = $repository->getHead();
} }
@@ -71,19 +70,12 @@ class TreeController implements ControllerProviderInterface
->assert('branch', $app['util.routing']->getBranchRegex()) ->assert('branch', $app['util.routing']->getBranchRegex())
->bind('search'); ->bind('search');
$route->get('{repo}/{branch}/', function ($repo, $branch) use ($app, $treeController) {
return $treeController($repo, $branch);
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch', $app['util.routing']->getBranchRegex())
->bind('branch');
$route->get('{repo}/', function ($repo) use ($app, $treeController) {
return $treeController($repo);
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->bind('repository');
# Intentionally before next statement, because order appears
# to be important, and the other statement got precedence previously.
$route->get('{repo}/{format}ball/{branch}', function($repo, $format, $branch) use ($app) { $route->get('{repo}/{format}ball/{branch}', function($repo, $format, $branch) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
$tree = $repository->getBranchTree($branch); $tree = $repository->getBranchTree($branch);
if (false === $tree) { if (false === $tree) {
@@ -114,6 +106,19 @@ class TreeController implements ControllerProviderInterface
->assert('branch', $app['util.routing']->getBranchRegex()) ->assert('branch', $app['util.routing']->getBranchRegex())
->bind('archive'); ->bind('archive');
$route->get('{repo}/{branch}/', function($repo, $branch) use ($app, $treeController) {
return $treeController($repo, $branch);
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch', $app['util.routing']->getBranchRegex())
->bind('branch');
$route->get('{repo}/', function($repo) use ($app, $treeController) {
return $treeController($repo);
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->bind('repository');
return $route; return $route;
} }
} }

View File

@@ -57,13 +57,17 @@ class Client extends BaseClient
} }
/** /**
* Opens a repository at the specified path * Opens a specified repository
* *
* @param string $path Path where the repository is located * @param array $repos Array of items describing configured repositories
* @param string $repo Name of repository we are currently handling
* @return Repository Instance of Repository * @return Repository Instance of Repository
*/ */
public function getRepository($path) public function getRepository($repos, $repo)
{ {
$repotmp = $this->getRepositoryCached($repos, $repo);
$path = $repotmp->getPath();
if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) {
throw new \RuntimeException('There is no GIT repository at ' . $path); throw new \RuntimeException('There is no GIT repository at ' . $path);
} }
@@ -75,3 +79,4 @@ class Client extends BaseClient
return new Repository($path, $this); return new Repository($path, $this);
} }
} }

View File

@@ -42,7 +42,16 @@ class Repository extends BaseRepository
*/ */
public function getCommit($commitHash) public function getCommit($commitHash)
{ {
$logs = $this->getClient()->run($this, "show --pretty=format:\"<item><hash>%H</hash><short_hash>%h</short_hash><tree>%T</tree><parents>%P</parents><author>%an</author><author_email>%ae</author_email><date>%at</date><commiter>%cn</commiter><commiter_email>%ce</commiter_email><commiter_date>%ct</commiter_date><message><![CDATA[%s]]></message><body><![CDATA[%b]]></body></item>\" $commitHash"); $logs = $this->getClient()->run($this,
"show --pretty=format:\"<item><hash>%H</hash>"
. "<short_hash>%h</short_hash><tree>%T</tree><parents>%P</parents>"
. "<author>%an</author><author_email>%ae</author_email>"
. "<date>%at</date><commiter>%cn</commiter><commiter_email>%ce</commiter_email>"
. "<commiter_date>%ct</commiter_date>"
. "<message><![CDATA[%s]]></message>"
. "<body><![CDATA[%b]]></body>"
. "</item>\" $commitHash"
);
$xmlEnd = strpos($logs, '</item>') + 7; $xmlEnd = strpos($logs, '</item>') + 7;
$commitInfo = substr($logs, 0, $xmlEnd); $commitInfo = substr($logs, 0, $xmlEnd);
$commitData = substr($logs, $xmlEnd); $commitData = substr($logs, $xmlEnd);
@@ -198,7 +207,14 @@ class Repository extends BaseRepository
{ {
$page = 15 * $page; $page = 15 * $page;
$pager = "--skip=$page --max-count=15"; $pager = "--skip=$page --max-count=15";
$command = "log $pager --pretty=format:\"<item><hash>%H</hash><short_hash>%h</short_hash><tree>%T</tree><parent>%P</parent><author>%an</author><author_email>%ae</author_email><date>%at</date><commiter>%cn</commiter><commiter_email>%ce</commiter_email><commiter_date>%ct</commiter_date><message><![CDATA[%s]]></message></item>\""; $command =
"log $pager --pretty=format:\"<item><hash>%H</hash>"
. "<short_hash>%h</short_hash><tree>%T</tree><parent>%P</parent>"
. "<author>%an</author><author_email>%ae</author_email>"
. "<date>%at</date><commiter>%cn</commiter>"
. "<commiter_email>%ce</commiter_email>"
. "<commiter_date>%ct</commiter_date>"
. "<message><![CDATA[%s]]></message></item>\"";
if ($file) { if ($file) {
$command .= " $file"; $command .= " $file";
@@ -222,7 +238,14 @@ class Repository extends BaseRepository
public function searchCommitLog($query) public function searchCommitLog($query)
{ {
$query = escapeshellarg($query); $query = escapeshellarg($query);
$command = "log --grep={$query} --pretty=format:\"<item><hash>%H</hash><short_hash>%h</short_hash><tree>%T</tree><parent>%P</parent><author>%an</author><author_email>%ae</author_email><date>%at</date><commiter>%cn</commiter><commiter_email>%ce</commiter_email><commiter_date>%ct</commiter_date><message><![CDATA[%s]]></message></item>\""; $command =
"log --grep={$query} --pretty=format:\"<item><hash>%H</hash>"
. "<short_hash>%h</short_hash><tree>%T</tree><parent>%P</parent>"
. "<author>%an</author><author_email>%ae</author_email>"
. "<date>%at</date><commiter>%cn</commiter>"
. "<commiter_email>%ce</commiter_email>"
. "<commiter_date>%ct</commiter_date>"
. "<message><![CDATA[%s]]></message></item>\"";
try { try {
$logs = $this->getPrettyFormat($command); $logs = $this->getPrettyFormat($command);
@@ -366,3 +389,4 @@ class Repository extends BaseRepository
return false; return false;
} }
} }

View File

@@ -8,6 +8,7 @@ use Silex\ServiceProviderInterface;
class GitServiceProvider implements ServiceProviderInterface class GitServiceProvider implements ServiceProviderInterface
{ {
/** /**
* Register the Git\Client on the Application ServiceProvider * Register the Git\Client on the Application ServiceProvider
* *
@@ -19,6 +20,8 @@ class GitServiceProvider implements ServiceProviderInterface
$app['git'] = function () use ($app) { $app['git'] = function () use ($app) {
$options['path'] = $app['git.client']; $options['path'] = $app['git.client'];
$options['hidden'] = $app['git.hidden']; $options['hidden'] = $app['git.hidden'];
$options['ini.file'] = $app['ini.file'];
$options['cache.repos'] = $app['cache.repos'];
$options['default_branch'] = $app['git.default_branch']; $options['default_branch'] = $app['git.default_branch'];
return new Client($options); return new Client($options);
@@ -29,3 +32,4 @@ class GitServiceProvider implements ServiceProviderInterface
{ {
} }
} }

View File

@@ -161,13 +161,12 @@ class Repository
return false; return false;
} }
public function getReadme($repo, $branch = null) public function getReadme($repository, $branch = null)
{ {
$repository = $this->app['git']->getRepository($this->app['git.repos'] . $repo); $files = $repository->getTree($branch)->output();
if ($branch === null) { if ($branch === null) {
$branch = $repository->getHead(); $branch = $repository->getHead();
} }
$files = $repository->getTree($branch)->output();
foreach ($files as $file) { foreach ($files as $file) {
if (preg_match('/^readme*/i', $file['name'])) { if (preg_match('/^readme*/i', $file['name'])) {
@@ -220,3 +219,4 @@ class Repository
return array($branch, $tree); return array($branch, $tree);
} }
} }

View File

@@ -23,7 +23,7 @@ class Routing
public function parseCommitishPathParam($commitishPath, $repo) public function parseCommitishPathParam($commitishPath, $repo)
{ {
$app = $this->app; $app = $this->app;
$repository = $app['git']->getRepository($app['git.repos'] . $repo); $repository = $app['git']->getRepository($app['git.repos'], $repo);
$commitish = null; $commitish = null;
$path = null; $path = null;
@@ -108,9 +108,15 @@ class Routing
if ($regex === null) { if ($regex === null) {
$app = $this->app; $app = $this->app;
$self = $this;
$quotedPaths = array_map( $quotedPaths = array_map(
function ($repo) use ($app) { function ($repo) use ($app, $self) {
return preg_quote($app['util.routing']->getRelativePath($repo['path']), '#'); $repoName = $repo['name'] ;
//Windows
if ($self->isWindows()){
$repoName = str_replace('\\', '\\\\',$repoName);
}
return $repoName;
}, },
$this->app['git']->getRepositories($this->app['git.repos']) $this->app['git']->getRepositories($this->app['git.repos'])
); );
@@ -128,6 +134,17 @@ class Routing
return $regex; return $regex;
} }
public function isWindows()
{
switch(PHP_OS){
case 'WIN32':
case 'WINNT':
case 'Windows': return true;
default : return false;
}
}
/** /**
* Strips the base path from a full repository path * Strips the base path from a full repository path
* *
@@ -147,3 +164,4 @@ class Routing
} }
} }
} }

View File

@@ -8,6 +8,7 @@ class InterfaceTest extends WebTestCase
{ {
protected static $tmpdir; protected static $tmpdir;
protected static $gitPath; protected static $gitPath;
protected static $cached_repos;
public static function setUpBeforeClass() public static function setUpBeforeClass()
{ {
@@ -16,10 +17,10 @@ class InterfaceTest extends WebTestCase
} elseif (getenv('TMPDIR')) { } elseif (getenv('TMPDIR')) {
self::$tmpdir = getenv('TMPDIR'); self::$tmpdir = getenv('TMPDIR');
} else { } else {
self::$tmpdir = '/tmp'; self::$tmpdir = DIRECTORY_SEPARATOR . 'tmp';
} }
self::$tmpdir .= '/gitlist_' . md5(time() . mt_rand()) . '/'; self::$tmpdir .= DIRECTORY_SEPARATOR . 'gitlist_' . md5(time() . mt_rand()) . DIRECTORY_SEPARATOR;
$fs = new Filesystem(); $fs = new Filesystem();
$fs->mkdir(self::$tmpdir); $fs->mkdir(self::$tmpdir);
@@ -30,13 +31,21 @@ class InterfaceTest extends WebTestCase
$options['path'] = getenv('GIT_CLIENT') ?: '/usr/bin/git'; $options['path'] = getenv('GIT_CLIENT') ?: '/usr/bin/git';
$options['hidden'] = array(self::$tmpdir . '/hiddenrepo'); $options['hidden'] = array(self::$tmpdir . '/hiddenrepo');
$options['ini.file'] = "config.ini";
$cached_dir = self::$tmpdir . DIRECTORY_SEPARATOR . 'cache';
$fs->mkdir($cached_dir);
self::$cached_repos = $cached_dir . DIRECTORY_SEPARATOR . 'repos.json';
$options['cache.repos'] = self::$cached_repos;
$git = new Client($options); $git = new Client($options);
self::$gitPath = $options['path']; self::$gitPath = $options['path'];
// GitTest repository fixture // GitTest repository fixture
$git->createRepository(self::$tmpdir . 'GitTest'); $git->createRepository(self::$tmpdir . 'GitTest');
$repository = $git->getRepository(self::$tmpdir . 'GitTest'); $repository = $git->getRepositoryCached(self::$tmpdir, 'GitTest');
file_put_contents(self::$tmpdir . 'GitTest/README.md', "## GitTest\nGitTest is a *test* repository!"); file_put_contents(self::$tmpdir . 'GitTest/README.md', "## GitTest\nGitTest is a *test* repository!");
file_put_contents(self::$tmpdir . 'GitTest/test.php', "<?php\necho 'Hello World'; // This is a test"); file_put_contents(self::$tmpdir . 'GitTest/test.php', "<?php\necho 'Hello World'; // This is a test");
$repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.name', 'Luke Skywalker');
@@ -49,13 +58,16 @@ class InterfaceTest extends WebTestCase
// foobar repository fixture // foobar repository fixture
$git->createRepository(self::$tmpdir . 'foobar'); $git->createRepository(self::$tmpdir . 'foobar');
$repository = $git->getRepository(self::$tmpdir . '/foobar'); $repository = $git->getRepositoryCached(self::$tmpdir, 'foobar');
file_put_contents(self::$tmpdir . 'foobar/bar.json', "{\n\"name\": \"foobar\"\n}"); file_put_contents(self::$tmpdir . 'foobar/bar.json', "{\n\"name\": \"foobar\"\n}");
file_put_contents(self::$tmpdir . 'foobar/.git/description', 'This is a test repo!'); file_put_contents(self::$tmpdir . 'foobar/.git/description', 'This is a test repo!');
$fs->mkdir(self::$tmpdir . 'foobar/myfolder'); $fs->mkdir(self::$tmpdir . 'foobar/myfolder');
$fs->mkdir(self::$tmpdir . 'foobar/testfolder'); $fs->mkdir(self::$tmpdir . 'foobar/testfolder');
file_put_contents(self::$tmpdir . 'foobar/myfolder/mytest.php', "<?php\necho 'Hello World'; // This is my test"); file_put_contents(self::$tmpdir . 'foobar/myfolder/mytest.php',
file_put_contents(self::$tmpdir . 'foobar/testfolder/test.php', "<?php\necho 'Hello World'; // This is a test"); "<?php\necho 'Hello World'; // This is my test");
file_put_contents(self::$tmpdir . 'foobar/testfolder/test.php',
"<?php\necho 'Hello World'; // This is a test");
$repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.name', 'Luke Skywalker');
$repository->setConfig('user.email', 'luke@rebel.org'); $repository->setConfig('user.email', 'luke@rebel.org');
$repository->addAll(); $repository->addAll();
@@ -65,7 +77,7 @@ class InterfaceTest extends WebTestCase
$nested_dir = self::$tmpdir . 'nested/'; $nested_dir = self::$tmpdir . 'nested/';
$fs->mkdir($nested_dir); $fs->mkdir($nested_dir);
$git->createRepository($nested_dir . 'NestedRepo'); $git->createRepository($nested_dir . 'NestedRepo');
$repository = $git->getRepository($nested_dir . '/NestedRepo'); $repository = $git->getRepositoryCached($nested_dir, 'NestedRepo');
file_put_contents($nested_dir . 'NestedRepo/.git/description', 'This is a NESTED test repo!'); file_put_contents($nested_dir . 'NestedRepo/.git/description', 'This is a NESTED test repo!');
file_put_contents($nested_dir . 'NestedRepo/README.txt', 'NESTED TEST REPO README'); file_put_contents($nested_dir . 'NestedRepo/README.txt', 'NESTED TEST REPO README');
$repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.name', 'Luke Skywalker');
@@ -81,7 +93,7 @@ class InterfaceTest extends WebTestCase
// master-less repository fixture // master-less repository fixture
$git->createRepository(self::$tmpdir . 'develop'); $git->createRepository(self::$tmpdir . 'develop');
$repository = $git->getRepository(self::$tmpdir . '/develop'); $repository = $git->getRepositoryCached(self::$tmpdir, 'develop');
$repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.name', 'Luke Skywalker');
$repository->setConfig('user.email', 'luke@rebel.org'); $repository->setConfig('user.email', 'luke@rebel.org');
file_put_contents(self::$tmpdir . 'develop/README.md', "## develop\ndevelop is a *test* repository!"); file_put_contents(self::$tmpdir . 'develop/README.md', "## develop\ndevelop is a *test* repository!");
@@ -98,26 +110,26 @@ class InterfaceTest extends WebTestCase
// Detached HEAD repository fixture // Detached HEAD repository fixture
$git->createRepository(self::$tmpdir . 'detached-head'); $git->createRepository(self::$tmpdir . 'detached-head');
$repository = $git->getRepository(self::$tmpdir . '/detached-head'); $repository = $git->getRepositoryCached(self::$tmpdir, 'detached-head');
# $repository = $git->getRepository(self::$tmpdir . '/detached-head');
$repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.name', 'Luke Skywalker');
$repository->setConfig('user.email', 'luke@rebel.org'); $repository->setConfig('user.email', 'luke@rebel.org');
file_put_contents(self::$tmpdir . 'detached-head/README.md', "## detached head\ndetached-head is a *test* repository!"); file_put_contents(self::$tmpdir . 'detached-head/README.md', "## detached head\ndetached-head is a *test* repository!");
$repository->addAll(); $repository->addAll();
$repository->commit("First commit"); $repository->commit("First commit");
$repository->checkout('HEAD'); $repository->checkout('HEAD');
$git->deleteCached();
} }
public function createApplication() public function createApplication()
{ {
$config = new \GitList\Config(array( $config = GitList\Config::fromFile('config.ini');
'git' => array( $config->set('app', 'cached_repos', self::$cached_repos);
'client' => self::$gitPath, $config->set('git', 'repositories', self::$tmpdir);
'repositories' => self::$tmpdir, $config->set('app', 'debug', true);
), $config->set('git', 'client', self::$gitPath);
'app' => array(
'debug' => true,
),
));
$app = require 'boot.php'; $app = require 'boot.php';
return $app; return $app;
} }
@@ -129,11 +141,11 @@ class InterfaceTest extends WebTestCase
$this->assertTrue($client->getResponse()->isOk()); $this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('title:contains("GitList")')); $this->assertCount(1, $crawler->filter('title:contains("GitList")'));
$this->assertCount(1, $crawler->filter('div.repository-header:contains("GitTest")')); $this->assertCount(1, $crawler->filter('div.repository-header a:contains("GitTest")'));
$this->assertEquals('/GitTest/', $crawler->filter('.repository-header a')->eq(0)->attr('href')); $this->assertEquals('/GitTest/', $crawler->filter('.repository-header a')->eq(0)->attr('href'));
$this->assertEquals('/GitTest/master/rss/', $crawler->filter('.repository-header a')->eq(1)->attr('href')); $this->assertEquals('/GitTest/master/rss/', $crawler->filter('.repository-header a')->eq(1)->attr('href'));
$this->assertEquals('/nested/NestedRepo/', $crawler->filter('.repository-header a')->eq(2)->attr('href')); $this->assertEquals('/NestedRepo/', $crawler->filter('.repository-header a')->eq(2)->attr('href'));
$this->assertEquals('/nested/NestedRepo/master/rss/', $crawler->filter('.repository-header a')->eq(3)->attr('href')); $this->assertEquals('/NestedRepo/master/rss/', $crawler->filter('.repository-header a')->eq(3)->attr('href'));
$this->assertCount(1, $crawler->filter('div.repository-header:contains("foobar")')); $this->assertCount(1, $crawler->filter('div.repository-header:contains("foobar")'));
$this->assertCount(1, $crawler->filter('div.repository-body:contains("This is a test repo!")')); $this->assertCount(1, $crawler->filter('div.repository-body:contains("This is a test repo!")'));
$this->assertEquals('/foobar/', $crawler->filter('.repository-header a')->eq(8)->attr('href')); $this->assertEquals('/foobar/', $crawler->filter('.repository-header a')->eq(8)->attr('href'));
@@ -175,11 +187,15 @@ class InterfaceTest extends WebTestCase
$client = $this->createClient(); $client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/blob/master/test.php'); $crawler = $client->request('GET', '/GitTest/blob/master/test.php');
$this->assertTrue($client->getResponse()->isOk()); $this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('.breadcrumb .active:contains("test.php")')); $this->assertCount(1, $crawler->filter('.breadcrumb .active:contains("test.php")'));
$this->assertEquals('/GitTest/raw/master/test.php', $crawler->filter('.source-header .btn-group a')->eq(0)->attr('href')); $this->assertEquals('/GitTest/raw/master/test.php',
$this->assertEquals('/GitTest/blame/master/test.php', $crawler->filter('.source-header .btn-group a')->eq(1)->attr('href')); $crawler->filter('.source-header .btn-group a')->eq(0)->attr('href'));
$this->assertEquals('/GitTest/commits/master/test.php', $crawler->filter('.source-header .btn-group a')->eq(2)->attr('href')); $this->assertEquals('/GitTest/blame/master/test.php',
$crawler->filter('.source-header .btn-group a')->eq(1)->attr('href'));
$this->assertEquals('/GitTest/commits/master/test.php',
$crawler->filter('.source-header .btn-group a')->eq(2)->attr('href'));
} }
public function testRawPage() public function testRawPage()
@@ -198,12 +214,14 @@ class InterfaceTest extends WebTestCase
$crawler = $client->request('GET', '/GitTest/blame/master/test.php'); $crawler = $client->request('GET', '/GitTest/blame/master/test.php');
$this->assertTrue($client->getResponse()->isOk()); $this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('.source-header .meta:contains("test.php")')); $this->assertCount(1, $crawler->filter('.source-header .meta:contains("test.php")'));
$this->assertRegexp('/\/GitTest\/commit\/[a-zA-Z0-9%]+/', $crawler->filter('.blame-view .commit')->eq(0)->filter('a')->attr('href')); $this->assertRegexp('/\/GitTest\/commit\/[a-zA-Z0-9%]+/',
$crawler->filter('.blame-view .commit')->eq(0)->filter('a')->attr('href'));
$crawler = $client->request('GET', '/foobar/blame/master/bar.json'); $crawler = $client->request('GET', '/foobar/blame/master/bar.json');
$this->assertTrue($client->getResponse()->isOk()); $this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('.source-header .meta:contains("bar.json")')); $this->assertCount(1, $crawler->filter('.source-header .meta:contains("bar.json")'));
$this->assertRegexp('/\/foobar\/commit\/[a-zA-Z0-9%]+/', $crawler->filter('.blame-view .commit')->eq(0)->filter('a')->attr('href')); $this->assertRegexp('/\/foobar\/commit\/[a-zA-Z0-9%]+/',
$crawler->filter('.blame-view .commit')->eq(0)->filter('a')->attr('href'));
} }
public function testHistoryPage() public function testHistoryPage()
@@ -252,8 +270,10 @@ class InterfaceTest extends WebTestCase
{ {
$client = $this->createClient(); $client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/master/rss/'); $client->request('GET', '/GitTest/master/rss/');
$this->assertTrue($client->getResponse()->isOk()); $response = $client->getResponse();
$this->assertTrue($response->isOk());
$this->assertRegexp('/Latest commits in GitTest:master/', $client->getResponse()->getContent()); $this->assertRegexp('/Latest commits in GitTest:master/', $client->getResponse()->getContent());
$this->assertRegexp('/Initial commit/', $client->getResponse()->getContent()); $this->assertRegexp('/Initial commit/', $client->getResponse()->getContent());
} }
@@ -262,8 +282,10 @@ class InterfaceTest extends WebTestCase
{ {
$client = $this->createClient(); $client = $this->createClient();
$crawler = $client->request('GET', '/nested/NestedRepo/'); $client->request('GET', '/NestedRepo/');
$this->assertTrue($client->getResponse()->isOk()); $response = $client->getResponse();
$this->assertTrue($response->isOk());
$this->assertRegexp('/NESTED TEST REPO README/', $client->getResponse()->getContent()); $this->assertRegexp('/NESTED TEST REPO README/', $client->getResponse()->getContent());
} }
@@ -279,7 +301,7 @@ class InterfaceTest extends WebTestCase
{ {
$client = $this->createClient(); $client = $this->createClient();
$crawler = $client->request('GET', '/nested/NestedRepo/testing/'); $crawler = $client->request('GET', '/NestedRepo/testing/');
$this->assertTrue($client->getResponse()->isOk()); $this->assertTrue($client->getResponse()->isOk());
$this->assertRegexp('/NESTED TEST BRANCH README/', $client->getResponse()->getContent()); $this->assertRegexp('/NESTED TEST BRANCH README/', $client->getResponse()->getContent());
} }
@@ -290,3 +312,4 @@ class InterfaceTest extends WebTestCase
$fs->remove(self::$tmpdir); $fs->remove(self::$tmpdir);
} }
} }

View File

@@ -9,8 +9,8 @@
{% for repository in repositories %} {% for repository in repositories %}
<div class="repository"> <div class="repository">
<div class="repository-header"> <div class="repository-header">
<i class="icon-folder-open icon-spaced"></i> <a href="{{ path('repository', {repo: repository.relativePath}) }}">{{ repository.name }}</a> <i class="icon-folder-open icon-spaced"></i> <a href="{{ path('repository', {repo: repository.name}) }}">{{ repository.name }}</a>
<a href="{{ path('rss', {repo: repository.relativePath, branch: repository.branch }) }}"><i class="rss pull-right"></i></a> <a href="{{ path('rss', {repo: repository.name, branch: 'master'}) }}"><i class="rss pull-right"></i></a>
</div> </div>
<div class="repository-body"> <div class="repository-body">
<p>{{ repository.description }}</p> <p>{{ repository.description }}</p>

View File

@@ -10,6 +10,7 @@
<div class="nav-collapse"> <div class="nav-collapse">
<ul class="nav pull-right"> <ul class="nav pull-right">
<li><a href="http://gitlist.org/">About</a></li> <li><a href="http://gitlist.org/">About</a></li>
<li><a href="{{ path('homepage') }}refresh">Refresh</a></li>
<li><a href="https://github.com/klaussilveira/gitlist/issues/new">Report bug</a></li> <li><a href="https://github.com/klaussilveira/gitlist/issues/new">Report bug</a></li>
<li><a href="https://github.com/klaussilveira/gitlist/wiki">Help</a></li> <li><a href="https://github.com/klaussilveira/gitlist/wiki">Help</a></li>
</ul> </ul>

View File

@@ -28,7 +28,7 @@
<tbody> <tbody>
{% for result in results %} {% for result in results %}
<tr> <tr>
<td><i class="icon-file icon-spaced"></i> <a href="{{ path('blob', {repo: repo, branch: branch, file: result.file}) }}#L{{ result.line }}">{{ result.file }}</a></td> <td><i class="icon-file icon-spaced"></i> <a href="{{ path('blob', {repo: repo, branch: branch, file: result.file, commitishPath: branch ~ '/' ~ result.file}) }}#L{{ result.line }}">{{ result.file }}</a></td>
<td>{{ result.match }}</td> <td>{{ result.match }}</td>
</tr> </tr>
{% endfor %} {% endfor %}