diff --git a/.gitignore b/.gitignore index e9b0612..4321e1b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ node_modules config.ini cache.properties composer.phar +phpunit.xml diff --git a/.htaccess b/.htaccess index efb9407..b3e5da9 100644 --- a/.htaccess +++ b/.htaccess @@ -2,8 +2,10 @@ Options -MultiViews RewriteEngine On + #RewriteBase /path/to/gitlist/ + RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ /index.php [L,NC] + RewriteRule ^(.*)$ index.php [L,NC] order allow,deny diff --git a/INSTALL.md b/INSTALL.md index 580c3da..97a8477 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,5 +1,5 @@ # GitList Installation -* Download GitList from [gitlist.org](http://gitlist.org/) and decompress to your `/var/www/gitlist` folder, or anywhere else you want to place GitList. +* Download GitList from [gitlist.org](http://gitlist.org/) and decompress to your `/var/www/gitlist` folder, or anywhere else you want to place GitList. * Rename the `config.ini-example` file to `config.ini`. * Open up the `config.ini` and configure your installation. You'll have to provide where your repositories are located and the base GitList URL (in our case, http://localhost/gitlist). * Create the cache folder and give read/write permissions to your web server user: @@ -13,7 +13,7 @@ chmod 777 cache That's it, installation complete! ## Webserver configuration -Apache is the "default" webserver for GitList. You will find the configuration inside the `.htaccess` file. However, nginx and lighttpd are also supported. +Apache is the "default" webserver for GitList. You will find the configuration inside the `.htaccess` file. However, nginx and lighttpd are also supported. ### nginx server.conf @@ -73,4 +73,16 @@ url.rewrite-once = ( "^/gitlist/favicon\.ico$" => "$0", "^/gitlist(/[^\?]*)(\?.*)?" => "/gitlist/index.php$1$2" ) -``` \ No newline at end of file +``` + +### hiawatha + +``` +UrlToolkit { + ToolkitID = gitlist + RequestURI isfile Return + # If you have example.com/gitlist/ ; Otherwise remove "/gitlist" below + Match ^/gitlist/.* Rewrite /gitlist/index.php + Match ^/gitlist/.*\.ini DenyAccess +} +``` diff --git a/boot.php b/boot.php index 5e36efe..6ab0915 100644 --- a/boot.php +++ b/boot.php @@ -1,11 +1,5 @@ set('git', 'repositories', rtrim($config->get('git', 'repositories'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR); - // Startup and configure Silex application $app = new GitList\Application($config, __DIR__); @@ -14,5 +8,6 @@ $app->mount('', new GitList\Controller\MainController()); $app->mount('', new GitList\Controller\BlobController()); $app->mount('', new GitList\Controller\CommitController()); $app->mount('', new GitList\Controller\TreeController()); +$app->mount('', new GitList\Controller\NetworkController()); return $app; diff --git a/build.xml b/build.xml index 114dec7..fcb1916 100644 --- a/build.xml +++ b/build.xml @@ -1,6 +1,7 @@ - + + @@ -9,12 +10,21 @@ + + + + + + + + + @@ -32,9 +42,7 @@ - - - + @@ -44,19 +52,17 @@ - + - + - - @@ -64,9 +70,7 @@ - - - + @@ -74,12 +78,25 @@ + + + + + + + + + - + + + + diff --git a/composer.json b/composer.json index 292e901..b795c85 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,18 @@ { "name": "klaussilveira/gitlist", "require": { - "silex/silex": "1.0.*", - "twig/twig": "1.9.*", - "symfony/twig-bridge": "2.1.*", - "symfony/filesystem": "2.1.*", - "klaussilveira/gitter": "dev-master" + "silex/silex": "1.0.*@dev", + "twig/twig": "1.12.*", + "symfony/twig-bridge": "2.2.*", + "symfony/filesystem": "2.2.*", + "klaussilveira/gitter": "0.2.*" }, "require-dev": { - "symfony/browser-kit": "2.1.*", - "symfony/css-selector": "2.1.*" + "symfony/browser-kit": "2.2.*", + "symfony/css-selector": "2.2.*", + "phpunit/phpunit": "3.7.*", + "phpmd/phpmd": "1.4.*", + "phploc/phploc": "1.7.*" }, "minimum-stability": "dev", "autoload": { diff --git a/composer.lock b/composer.lock index 786811c..ee786fe 100644 --- a/composer.lock +++ b/composer.lock @@ -1,35 +1,39 @@ { - "hash": "b1fc3d7e61707618f68e5cf940e97081", + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "3b87a5ccc4819475fffa9f49284e65a6", "packages": [ { "name": "klaussilveira/gitter", - "version": "dev-master", + "version": "0.2.0", "source": { "type": "git", - "url": "https://github.com/klaussilveira/gitter", - "reference": "1c9b6e4dde81d21acffe99d9f4559ed3bc59f947" + "url": "https://github.com/klaussilveira/gitter.git", + "reference": "0.2.0" }, "dist": { "type": "zip", - "url": "https://github.com/klaussilveira/gitter/zipball/1c9b6e4dde81d21acffe99d9f4559ed3bc59f947", - "reference": "1c9b6e4dde81d21acffe99d9f4559ed3bc59f947", + "url": "https://api.github.com/repos/klaussilveira/gitter/zipball/0.2.0", + "reference": "0.2.0", "shasum": "" }, "require": { "php": ">=5.3.0", - "symfony/process": ">=2.1" + "symfony/process": ">=2.2" }, "require-dev": { - "symfony/filesystem": ">=2.1" + "phpunit/phpunit": ">=3.7.1", + "symfony/filesystem": ">=2.2" }, - "time": "1351643953", "type": "library", - "installation-source": "source", "autoload": { "psr-0": { "Gitter": "lib/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-2-Clause" ], @@ -46,20 +50,21 @@ "keywords": [ "git", "vcs" - ] + ], + "time": "2013-06-01 13:32:37" }, { "name": "pimple/pimple", "version": "dev-master", "source": { "type": "git", - "url": "git://github.com/fabpot/Pimple.git", - "reference": "b9f27b8dc18c08f00627dec02359b46a24791dc3" + "url": "https://github.com/fabpot/Pimple.git", + "reference": "v1.0.2" }, "dist": { "type": "zip", - "url": "https://github.com/fabpot/Pimple/zipball/b9f27b8dc18c08f00627dec02359b46a24791dc3", - "reference": "b9f27b8dc18c08f00627dec02359b46a24791dc3", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/v1.0.2", + "reference": "v1.0.2", "shasum": "" }, "require": { @@ -71,12 +76,12 @@ "dev-master": "1.0.x-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Pimple": "lib/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -89,23 +94,61 @@ "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", "homepage": "http://pimple.sensiolabs.org", "keywords": [ - "dependency injection", - "container" + "container", + "dependency injection" ], - "time": "1347278988" + "time": "2013-03-08 08:21:40" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/php-fig/log/archive/1.0.0.zip", + "reference": "1.0.0", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" }, { "name": "silex/silex", "version": "dev-master", "source": { "type": "git", - "url": "git://github.com/fabpot/Silex.git", - "reference": "69d710011ee8f9fa286854fcf636a07ad76b570b" + "url": "https://github.com/fabpot/Silex.git", + "reference": "803f809cdaa409e5769b24db952655d7e9187677" }, "dist": { "type": "zip", - "url": "https://github.com/fabpot/Silex/zipball/69d710011ee8f9fa286854fcf636a07ad76b570b", - "reference": "69d710011ee8f9fa286854fcf636a07ad76b570b", + "url": "https://api.github.com/repos/fabpot/Silex/zipball/803f809cdaa409e5769b24db952655d7e9187677", + "reference": "803f809cdaa409e5769b24db952655d7e9187677", "shasum": "" }, "require": { @@ -117,42 +160,43 @@ "symfony/routing": ">=2.1,<2.3-dev" }, "require-dev": { - "monolog/monolog": ">=1.0.0,<1.2-dev", - "twig/twig": ">=1.8.0,<2.0-dev", - "swiftmailer/swiftmailer": "4.2.*", "doctrine/dbal": ">=2.2.0,<2.4.0-dev", - "symfony/security": ">=2.1,<2.3-dev", - "symfony/config": ">=2.1,<2.3-dev", - "symfony/locale": ">=2.1,<2.3-dev", - "symfony/form": ">=2.1,<2.3-dev", + "swiftmailer/swiftmailer": "4.2.*", "symfony/browser-kit": ">=2.1,<2.3-dev", + "symfony/config": ">=2.1,<2.3-dev", "symfony/css-selector": ">=2.1,<2.3-dev", + "symfony/dom-crawler": ">=2.1,<2.3-dev", "symfony/finder": ">=2.1,<2.3-dev", + "symfony/form": ">=2.1.4,<2.3-dev", + "symfony/locale": ">=2.1,<2.3-dev", "symfony/monolog-bridge": ">=2.1,<2.3-dev", + "symfony/options-resolver": ">=2.1,<2.3-dev", "symfony/process": ">=2.1,<2.3-dev", + "symfony/security": ">=2.1,<2.3-dev", + "symfony/serializer": ">=2.1,<2.3-dev", "symfony/translation": ">=2.1,<2.3-dev", "symfony/twig-bridge": ">=2.1,<2.3-dev", "symfony/validator": ">=2.1,<2.3-dev", - "symfony/serializer": ">=2.1,<2.3-dev" + "twig/twig": ">=1.8.0,<2.0-dev" }, "suggest": { "symfony/browser-kit": ">=2.1,<2.3-dev", "symfony/css-selector": ">=2.1,<2.3-dev", - "symfony/dom-crawler": ">=2.1,<2.3-dev" + "symfony/dom-crawler": ">=2.1,<2.3-dev", + "symfony/form": "To make use of the FormServiceProvider, >= 2.1.4 is required" }, - "time": "1351540898", "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Silex": "src/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -171,46 +215,46 @@ "homepage": "http://silex.sensiolabs.org", "keywords": [ "microframework" - ] + ], + "time": "2013-04-01 08:47:32" }, { "name": "symfony/event-dispatcher", - "version": "dev-master", + "version": "2.2.x-dev", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", - "url": "https://github.com/symfony/EventDispatcher", - "reference": "24a1039d52b6b9f533cb73dcb96c7748262db686" + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "v2.2.0-RC3" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/EventDispatcher/zipball/24a1039d52b6b9f533cb73dcb96c7748262db686", - "reference": "24a1039d52b6b9f533cb73dcb96c7748262db686", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.2.0-RC3", + "reference": "v2.2.0-RC3", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "symfony/dependency-injection": "2.2.*" + "symfony/dependency-injection": ">=2.0,<3.0" }, "suggest": { "symfony/dependency-injection": "2.2.*", "symfony/http-kernel": "2.2.*" }, - "time": "1349553479", "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -225,21 +269,22 @@ } ], "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com" + "homepage": "http://symfony.com", + "time": "2013-02-11 11:26:43" }, { "name": "symfony/filesystem", - "version": "2.1.x-dev", + "version": "2.2.x-dev", "target-dir": "Symfony/Component/Filesystem", "source": { "type": "git", - "url": "https://github.com/symfony/Filesystem", - "reference": "v2.1.3" + "url": "https://github.com/symfony/Filesystem.git", + "reference": "v2.2.0-RC3" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/Filesystem/zipball/v2.1.3", - "reference": "v2.1.3", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.2.0-RC3", + "reference": "v2.2.0-RC3", "shasum": "" }, "require": { @@ -248,15 +293,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { - "Symfony\\Component\\Filesystem": "" + "Symfony\\Component\\Filesystem\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -272,34 +317,32 @@ ], "description": "Symfony Filesystem Component", "homepage": "http://symfony.com", - "time": "1350717030" + "time": "2013-01-17 15:25:59" }, { "name": "symfony/http-foundation", - "version": "dev-master", + "version": "2.2.x-dev", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", - "url": "https://github.com/symfony/HttpFoundation", - "reference": "707e289629a10fde825bc4ba90aba743f79b173c" + "url": "https://github.com/symfony/HttpFoundation.git", + "reference": "9a87bc5856de55ebbc8b3e66c3c686e6aef35ba7" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/HttpFoundation/zipball/707e289629a10fde825bc4ba90aba743f79b173c", - "reference": "707e289629a10fde825bc4ba90aba743f79b173c", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/9a87bc5856de55ebbc8b3e66c3c686e6aef35ba7", + "reference": "9a87bc5856de55ebbc8b3e66c3c686e6aef35ba7", "shasum": "" }, "require": { "php": ">=5.3.3" }, - "time": "1351508251", "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Symfony\\Component\\HttpFoundation\\": "" @@ -308,6 +351,7 @@ "Symfony/Component/HttpFoundation/Resources/stubs" ] }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -322,37 +366,40 @@ } ], "description": "Symfony HttpFoundation Component", - "homepage": "http://symfony.com" + "homepage": "http://symfony.com", + "time": "2013-04-01 08:05:23" }, { "name": "symfony/http-kernel", - "version": "dev-master", + "version": "2.2.x-dev", "target-dir": "Symfony/Component/HttpKernel", "source": { "type": "git", - "url": "https://github.com/symfony/HttpKernel", - "reference": "4dcb0bf602788342fb80c28c6e28be818839d417" + "url": "https://github.com/symfony/HttpKernel.git", + "reference": "3bfc8fda577fb671b2d9395a59fbc87f449fa61f" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/HttpKernel/zipball/4dcb0bf602788342fb80c28c6e28be818839d417", - "reference": "4dcb0bf602788342fb80c28c6e28be818839d417", + "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/3bfc8fda577fb671b2d9395a59fbc87f449fa61f", + "reference": "3bfc8fda577fb671b2d9395a59fbc87f449fa61f", "shasum": "" }, "require": { "php": ">=5.3.3", - "symfony/event-dispatcher": "2.2.*", - "symfony/http-foundation": "2.2.*" + "psr/log": ">=1.0,<2.0", + "symfony/event-dispatcher": ">=2.1,<3.0", + "symfony/http-foundation": ">=2.2,<2.3-dev" }, "require-dev": { "symfony/browser-kit": "2.2.*", - "symfony/class-loader": "2.2.*", - "symfony/config": "2.2.*", + "symfony/class-loader": ">=2.1,<3.0", + "symfony/config": ">=2.0,<3.0", "symfony/console": "2.2.*", - "symfony/dependency-injection": "2.2.*", - "symfony/finder": "2.2.*", - "symfony/process": "2.2.*", - "symfony/routing": "2.2.*" + "symfony/dependency-injection": ">=2.0,<3.0", + "symfony/finder": ">=2.0,<3.0", + "symfony/process": ">=2.0,<3.0", + "symfony/routing": ">=2.2,<2.3-dev", + "symfony/stopwatch": ">=2.2,<2.3-dev" }, "suggest": { "symfony/browser-kit": "2.2.*", @@ -362,19 +409,18 @@ "symfony/dependency-injection": "2.2.*", "symfony/finder": "2.2.*" }, - "time": "1351530455", "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Symfony\\Component\\HttpKernel\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -389,7 +435,8 @@ } ], "description": "Symfony HttpKernel Component", - "homepage": "http://symfony.com" + "homepage": "http://symfony.com", + "time": "2013-03-23 10:43:44" }, { "name": "symfony/process", @@ -397,13 +444,13 @@ "target-dir": "Symfony/Component/Process", "source": { "type": "git", - "url": "https://github.com/symfony/Process", - "reference": "b35a2a4fae02286df3275d7094a3d3d575122db8" + "url": "https://github.com/symfony/Process.git", + "reference": "46b24c5905096914d467b769027e36433c7b5421" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/Process/zipball/b35a2a4fae02286df3275d7094a3d3d575122db8", - "reference": "b35a2a4fae02286df3275d7094a3d3d575122db8", + "url": "https://api.github.com/repos/symfony/Process/zipball/46b24c5905096914d467b769027e36433c7b5421", + "reference": "46b24c5905096914d467b769027e36433c7b5421", "shasum": "" }, "require": { @@ -412,15 +459,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Symfony\\Component\\Process\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -436,50 +483,49 @@ ], "description": "Symfony Process Component", "homepage": "http://symfony.com", - "time": "1351356874" + "time": "2013-03-23 08:06:49" }, { "name": "symfony/routing", - "version": "dev-master", + "version": "2.2.x-dev", "target-dir": "Symfony/Component/Routing", "source": { "type": "git", - "url": "https://github.com/symfony/Routing", - "reference": "29792d8ac4ed7308acdeed4933cb6d91b7f6510f" + "url": "https://github.com/symfony/Routing.git", + "reference": "6fea997908607f18a970a3848ec89c8e775255f3" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/Routing/zipball/29792d8ac4ed7308acdeed4933cb6d91b7f6510f", - "reference": "29792d8ac4ed7308acdeed4933cb6d91b7f6510f", + "url": "https://api.github.com/repos/symfony/Routing/zipball/6fea997908607f18a970a3848ec89c8e775255f3", + "reference": "6fea997908607f18a970a3848ec89c8e775255f3", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "doctrine/common": ">=2.2,<2.4-dev", - "symfony/config": "2.2.*", - "symfony/yaml": "2.2.*", - "symfony/http-kernel": "2.2.*" + "doctrine/common": ">=2.2,<3.0", + "psr/log": ">=1.0,<2.0", + "symfony/config": ">=2.2,<2.3-dev", + "symfony/yaml": ">=2.0,<3.0" }, "suggest": { - "doctrine/common": ">=2.2,<2.4-dev", + "doctrine/common": "~2.2", "symfony/config": "2.2.*", "symfony/yaml": "2.2.*" }, - "time": "1351466734", "type": "library", "extra": { "branch-alias": { "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { "Symfony\\Component\\Routing\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -494,55 +540,58 @@ } ], "description": "Symfony Routing Component", - "homepage": "http://symfony.com" + "homepage": "http://symfony.com", + "time": "2013-03-23 12:03:22" }, { "name": "symfony/twig-bridge", - "version": "2.1.x-dev", + "version": "2.2.x-dev", "target-dir": "Symfony/Bridge/Twig", "source": { "type": "git", - "url": "https://github.com/symfony/TwigBridge", - "reference": "v2.1.3" + "url": "https://github.com/symfony/TwigBridge.git", + "reference": "50bbc4a10d34a71325b58940c4887726b8a30841" }, "dist": { "type": "zip", - "url": "https://github.com/symfony/TwigBridge/zipball/v2.1.3", - "reference": "v2.1.3", + "url": "https://api.github.com/repos/symfony/TwigBridge/zipball/50bbc4a10d34a71325b58940c4887726b8a30841", + "reference": "50bbc4a10d34a71325b58940c4887726b8a30841", "shasum": "" }, "require": { "php": ">=5.3.3", - "twig/twig": ">=1.9.1,<2.0-dev" + "twig/twig": ">=1.11.0,<2.0" }, "require-dev": { - "symfony/form": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/templating": "2.1.*", - "symfony/translation": "2.1.*", - "symfony/yaml": "2.1.*", - "symfony/security": "2.1.*" + "symfony/form": "2.2.*", + "symfony/http-kernel": ">=2.2,<2.3-dev", + "symfony/routing": ">=2.2,<2.3-dev", + "symfony/security": ">=2.0,<2.3-dev", + "symfony/templating": ">=2.1,<3.0", + "symfony/translation": ">=2.0,<2.3-dev", + "symfony/yaml": ">=2.0,<3.0" }, "suggest": { - "symfony/form": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/templating": "2.1.*", - "symfony/translation": "2.1.*", - "symfony/yaml": "2.1.*", - "symfony/security": "2.1.*" + "symfony/form": "2.2.*", + "symfony/http-kernel": "2.2.*", + "symfony/routing": "2.2.*", + "symfony/security": "2.2.*", + "symfony/templating": "2.2.*", + "symfony/translation": "2.2.*", + "symfony/yaml": "2.2.*" }, "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, - "installation-source": "source", "autoload": { "psr-0": { - "Symfony\\Bridge\\Twig": "" + "Symfony\\Bridge\\Twig\\": "" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -558,38 +607,37 @@ ], "description": "Symfony Twig Bridge", "homepage": "http://symfony.com", - "time": "1349363877" + "time": "2013-03-15 10:14:31" }, { "name": "twig/twig", - "version": "v1.9.2", + "version": "dev-master", "source": { "type": "git", - "url": "git://github.com/fabpot/Twig.git", - "reference": "v1.9.2" + "url": "https://github.com/fabpot/Twig.git", + "reference": "399916916533589c95dc04e1afd4aeefa34928ad" }, "dist": { "type": "zip", - "url": "https://github.com/fabpot/Twig/zipball/v1.9.2", - "reference": "v1.9.2", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/399916916533589c95dc04e1afd4aeefa34928ad", + "reference": "399916916533589c95dc04e1afd4aeefa34928ad", "shasum": "" }, "require": { "php": ">=5.2.4" }, - "time": "2012-08-25 10:32:57", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.12-dev" } }, - "installation-source": "dist", "autoload": { "psr-0": { "Twig_": "lib/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3" ], @@ -607,15 +655,848 @@ "homepage": "http://twig.sensiolabs.org", "keywords": [ "templating" - ] + ], + "time": "2013-04-01 08:27:18" + } + ], + "packages-dev": [ + { + "name": "pdepend/pdepend", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "85b3d7f2d2c6105a76daea0f5a0aee7c7a140b25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/85b3d7f2d2c6105a76daea0f5a0aee7c7a140b25", + "reference": "85b3d7f2d2c6105a76daea0f5a0aee7c7a140b25", + "shasum": "" + }, + "require": { + "php": ">=5.2.3" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "description": "Official version of pdepend to be handled with Composer", + "time": "2013-01-25 15:09:33" + }, + { + "name": "phploc/phploc", + "version": "1.7.4", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phploc.git", + "reference": "1.7.4" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phploc/archive/1.7.4.zip", + "reference": "1.7.4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/finder": "2.1.x-dev", + "zetacomponents/console-tools": "dev-master" + }, + "bin": [ + "composer/bin/phploc" + ], + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "A tool for quickly measuring the size of a PHP project.", + "homepage": "https://github.com/sebastianbergmann/phploc", + "time": "2012-11-10 12:45:44" + }, + { + "name": "phpmd/phpmd", + "version": "1.4.1", + "source": { + "type": "git", + "url": "git://github.com/phpmd/phpmd.git", + "reference": "1.4.1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/phpmd/phpmd/archive/1.4.1.zip", + "reference": "1.4.1", + "shasum": "" + }, + "require": { + "pdepend/pdepend": "*", + "php": ">=5.3.0" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "../../pdepend/pdepend/src/main/php", + "src/main/php" + ], + "description": "Official version of PHPMD handled with Composer.", + "time": "2012-12-14 12:25:09" + }, + { + "name": "phpunit/php-code-coverage", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1.2.9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.9", + "reference": "1.2.9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-token-stream": ">=1.1.3@stable" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2013-02-26 18:55:56" + }, + { + "name": "phpunit/php-file-iterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2deb24c65ea78e126daa8d45b2089ddc29ec1d26", + "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-01-07 10:47:05" + }, + { + "name": "phpunit/php-text-template", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "1eeef106193d2f8c539728e566bb4793071a9e18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/1eeef106193d2f8c539728e566bb4793071a9e18", + "reference": "1eeef106193d2f8c539728e566bb4793071a9e18", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2013-01-07 10:56:17" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/ecf7920b27003a9412b07dad79dbb5ad1249e6c3", + "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-01-30 06:08:51" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c25dd88e1592e66dee2553c99ef244203d5a1b98", + "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2013-01-07 10:56:35" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "3.7.19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.19", + "reference": "3.7.19", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-timer": ">=1.0.2,<1.1.0", + "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", + "symfony/yaml": ">=2.0.0,<2.3.0" + }, + "require-dev": { + "pear-pear/pear": "1.9.4" + }, + "suggest": { + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "phpunit/php-invoker": ">=1.1.0,<1.2.0" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2013-03-25 11:45:06" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/d49b5683200b5db9b1c64cb06f52f50d147891c4", + "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-02-05 07:46:41" + }, + { + "name": "symfony/browser-kit", + "version": "2.2.x-dev", + "target-dir": "Symfony/Component/BrowserKit", + "source": { + "type": "git", + "url": "https://github.com/symfony/BrowserKit.git", + "reference": "d55e7ffd7f10bead48adb03b208bc67bd53926bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/d55e7ffd7f10bead48adb03b208bc67bd53926bb", + "reference": "d55e7ffd7f10bead48adb03b208bc67bd53926bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/dom-crawler": ">=2.0,<3.0" + }, + "require-dev": { + "symfony/css-selector": ">=2.0,<3.0", + "symfony/process": ">=2.0,<3.0" + }, + "suggest": { + "symfony/process": "2.2.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\BrowserKit\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "http://symfony.com", + "time": "2013-03-15 10:14:31" + }, + { + "name": "symfony/css-selector", + "version": "2.2.x-dev", + "target-dir": "Symfony/Component/CssSelector", + "source": { + "type": "git", + "url": "https://github.com/symfony/CssSelector.git", + "reference": "v2.2.0-RC3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.2.0-RC3", + "reference": "v2.2.0-RC3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\CssSelector\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "http://symfony.com", + "time": "2013-01-17 15:25:59" + }, + { + "name": "symfony/dom-crawler", + "version": "dev-master", + "target-dir": "Symfony/Component/DomCrawler", + "source": { + "type": "git", + "url": "https://github.com/symfony/DomCrawler.git", + "reference": "09f0cd5980511ee77c2e8142405ccd5c867506b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/09f0cd5980511ee77c2e8142405ccd5c867506b9", + "reference": "09f0cd5980511ee77c2e8142405ccd5c867506b9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/css-selector": ">=2.0,<3.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DomCrawler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "http://symfony.com", + "time": "2013-05-27 14:49:42" + }, + { + "name": "symfony/finder", + "version": "2.1.x-dev", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "v2.1.9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.1.9", + "reference": "v2.1.9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2013-03-06 19:26:55" + }, + { + "name": "symfony/yaml", + "version": "2.2.x-dev", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "3f6d4ab3fd8226ab4ba0be9fc8a238f4338b79ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/3f6d4ab3fd8226ab4ba0be9fc8a238f4338b79ab", + "reference": "3f6d4ab3fd8226ab4ba0be9fc8a238f4338b79ab", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2013-03-23 07:49:54" + }, + { + "name": "zetacomponents/base", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/zetacomponents/Base.git", + "reference": "642f63a8a72c32996f1aaf8a317fdf746bc32ce7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zetacomponents/Base/zipball/642f63a8a72c32996f1aaf8a317fdf746bc32ce7", + "reference": "642f63a8a72c32996f1aaf8a317fdf746bc32ce7", + "shasum": "" + }, + "require-dev": { + "zetacomponents/unit-test": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Sergey Alexeev" + }, + { + "name": "Sebastian Bergmann" + }, + { + "name": "Jan Borsodi" + }, + { + "name": "Raymond Bosman" + }, + { + "name": "Frederik Holljen" + }, + { + "name": "Kore Nordmann" + }, + { + "name": "Derick Rethans" + }, + { + "name": "Vadym Savchuk" + }, + { + "name": "Tobias Schlitt" + }, + { + "name": "Alexandru Stanoi" + } + ], + "description": "The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.", + "homepage": "https://github.com/zetacomponents", + "time": "2012-05-21 11:21:36" + }, + { + "name": "zetacomponents/console-tools", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/zetacomponents/ConsoleTools.git", + "reference": "90156abef01e4215fda8b9740f77c322f126fb02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zetacomponents/ConsoleTools/zipball/90156abef01e4215fda8b9740f77c322f126fb02", + "reference": "90156abef01e4215fda8b9740f77c322f126fb02", + "shasum": "" + }, + "require": { + "zetacomponents/base": "*" + }, + "require-dev": { + "zetacomponents/unit-test": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Sergey Alexeev" + }, + { + "name": "Sebastian Bergmann" + }, + { + "name": "Jan Borsodi" + }, + { + "name": "Raymond Bosman" + }, + { + "name": "Frederik Holljen" + }, + { + "name": "Kore Nordmann" + }, + { + "name": "Derick Rethans" + }, + { + "name": "Vadym Savchuk" + }, + { + "name": "Tobias Schlitt" + }, + { + "name": "Alexandru Stanoi" + } + ], + "description": "A set of classes to do different actions with the console (also called shell). It can render a progress bar, tables and a status bar and contains a class for parsing command line options.", + "homepage": "https://github.com/zetacomponents", + "time": "2012-05-21 09:55:34" } ], - "packages-dev": null, "aliases": [ ], "minimum-stability": "dev", "stability-flags": { - "klaussilveira/gitter": 20 - } + "silex/silex": 20 + }, + "platform": [ + + ], + "platform-dev": [ + + ] } diff --git a/config.ini-example b/config.ini-example index 70815f7..81e0688 100644 --- a/config.ini-example +++ b/config.ini-example @@ -1,12 +1,19 @@ [git] client = '/usr/bin/git' ; Your git executable path -repositories = '/var/www/projects/' ; Path to your repositories +default_branch = 'master' ; Default branch when HEAD is detached +repositories[] = '/home/git/repositories/' ; Path to your repositories + ; If you wish to add more repositories, just add a new line + +; WINDOWS USERS +;client = '"C:\Program Files (x86)\Git\bin\git.exe"' ; Your git executable path +;repositories[] = 'C:\Path\to\Repos\' ; Path to your repositories ; You can hide repositories from GitList, just copy this for each repository you want to hide -; hidden[] = '/var/www/projects/BetaTest' +; hidden[] = '/home/git/repositories/BetaTest' [app] debug = false +cache = true ; If you need to specify custom filetypes for certain extensions, do this here [filetypes] diff --git a/index.php b/index.php index 51eaaa6..9f83d60 100644 --- a/index.php +++ b/index.php @@ -1,7 +1,7 @@ run(); + diff --git a/pkg_builder/Makefile b/pkg_builder/Makefile new file mode 100644 index 0000000..32ffce8 --- /dev/null +++ b/pkg_builder/Makefile @@ -0,0 +1,147 @@ +# Main Info +NAME = gitlist +DESCRIPTION = "An elegant and modern git repository viewer" +LICENSE = New BSD +GROUP = gitlist +VENDOR = gitlist.org +URL = "http://www.gitlist.org" + +#BUILD Info +PREFIX = /usr/share +PROJROOT = "$(shell pwd)" +SRCROOT = "$(shell pwd)/gitlist" +UPSTREAM_VERSION = $(shell cat tools/release.info | head -n1 | cut -d"=" -f2) +BUILD_STAMP = $(shell date +"%Y%m%d%H%M%S") + +#Packager Info +PACKAGER = $(shell git config user.name) +PACKAGER_MAIL = $(shell git config user.email) + +#Debian Package Info +PACKAGE-VERSION= 1 +DEBIAN_BUILD_ROOT = ${PROJROOT}/debian/ +PROJECT_DEBIAN_LIKE_NAME=$(shell cat tools/release.info | grep name | cut -d"=" -f2) +DEBIAN_NAME=$(PROJECT_DEBIAN_LIKE_NAME)$(shell echo "_")$(UPSTREAM_VERSION)-${PACKAGE-VERSION}$(shell echo "_all.deb") +DEBIAN_VERSION = +# Generating control file +define control +Package: $(PROJECT_DEBIAN_LIKE_NAME) +Version: $(UPSTREAM_VERSION)-${PACKAGE-VERSION} +Architecture: all +Section: web +Priority: optional +Maintainer: "${PACKAGER} <${PACKAGER_MAIL}>" +Description: ${DESCRIPTION} +endef +export control + +all: + @echo "... $(UPSTREAM_VERSION)" + @echo "... $(PACKAGER)" + @echo "... $(PACKAGER_MAIL)" + @echo "... $(DEBIAN_NAME)" + +help: + @echo "To use this make file just:" + @echo "Download the gitlist tarball and stract it into a folder called gitlist" + @echo "make [build_deb|build_rpm|build(apache|nginx|lighthttp)]" + +clean_deb: + @echo "Cleaning . . ." + @rm -rf ${DEBIAN_BUILD_ROOT}/*.deb + @rm -rf ${PROJROOT}/debian + +prepare_deb: clean_deb + @echo "############################### - Building DEB" + @mkdir ${DEBIAN_BUILD_ROOT} -pv + @mkdir ${DEBIAN_BUILD_ROOT}/DEBIAN -pv + @mkdir ${DEBIAN_BUILD_ROOT}${PREFIX}/${PROJECT_DEBIAN_LIKE_NAME} -pv + +copy_deb_files: prepare_deb + @echo "$$control" > ${DEBIAN_BUILD_ROOT}/DEBIAN/control + +copy_deb: copy_deb_files + @echo Sync files + @rsync -avz ${SRCROOT} ${DEBIAN_BUILD_ROOT}${PREFIX}/ + +md5sum_deb: copy_deb + @cd debian; find . -type f ! -regex '.*\.hg.*' ! -regex '.*?debian-binary.*' ! -regex '.*?DEBIAN.*' | xargs -d "\n" md5sum > DEBIAN/md5sums + +deb_uniq: md5sum_deb + @mkdir ${PROJROOT}/pkg -p + @dpkg -b debian $(DEBIAN_NAME); + @mv $(DEBIAN_NAME) ${PROJROOT}/pkg/ + @rm debian -rf + @echo '### Wrote $(DEBIAN_NAME) in ${PROJROOT}/pkg/ . . . . . Success' + + +build_deb: deb_uniq + +#### RPM STUFF + +RPM_NAME=$(PROJECT_DEBIAN_LIKE_NAME)$(shell echo "_")$(UPSTREAM_VERSION)-${PACKAGE-VERSION}$(shell echo "_all.rpm") + +DIST_DIR = dist +TAR_DIR = tar + +RPM_DIR = rpm +RPM_DIRS = SPECS RPMS SOURCES BUILD + +clean_rpm: + @echo Cleaning temporary dirs... + @rm -rf $(TAR_DIR) + @rm -rf $(RPM_DIR) + @rm -rf $(DIST_DIR) + +rpm_init: clean_rpm + @echo Creating directories... + @echo $(DIST_DIR) + @mkdir -p $(DIST_DIR) + @for dir in $(RPM_DIRS); do \ + echo $(RPM_DIR)/$$dir; \ + mkdir -p $(RPM_DIR)/$$dir; \ + done + +rpm_preptar: rpm_init + @echo Copying files to generate tar... + @echo creating directory: $(TAR_DIR)/ + @mkdir $(TAR_DIR)/ -p + @rsync -avz --exclude ".git" --exclude ".gitignore" --exclude "builder" gitlist $(TAR_DIR)/ + +rpm_tar: rpm_preptar + @echo Generating tarball... + @cd $(PROJROOT)/$(TAR_DIR); \ + tar cf $(PROJROOT)/$(RPM_DIR)/SOURCES/$(NAME).tar . + +rpm: rpm_tar + @echo Calling rpmbuild... + @echo Vesion: $(VERSION) + @cp tools/$(NAME).spec $(RPM_DIR)/SPECS/ + + @cd $(PROJROOT)/$(RPM_DIR)/SPECS ; \ + rpmbuild -bb \ + --buildroot="$(PROJROOT)/$(RPM_DIR)/BUILD/$(NAME)" \ + --define "_topdir $(PROJROOT)/$(RPM_DIR)" \ + --define "name $(NAME)" \ + --define "summary "$(DESCRIPTION)"" \ + --define "version $(UPSTREAM_VERSION)" \ + --define "release $(PACKAGE-VERSION)" \ + --define "url _$(URL)_" \ + --define "license $(LICENSE)" \ + --define "group $(GROUP)" \ + --define "vendor $(VENDOR)" \ + --define "packager $(PACKAGER)" \ + --define "prefix $(PREFIX)" \ + --define "source_dir $(PROJROOT)/$(RPM_DIR)/SOURCES" \ + $(NAME).spec + @echo Copying generated RPM to dist dir... + @mkdir ${PROJROOT}/pkg -p + @cp $(PROJROOT)/$(RPM_DIR)/RPMS/noarch/*.rpm $(PROJROOT)/pkg + @rm -rf $(TAR_DIR) + @rm -rf $(RPM_DIR) + @rm -rf $(DIST_DIR) + + + +build_rpm: rpm + diff --git a/pkg_builder/README.md b/pkg_builder/README.md new file mode 100644 index 0000000..ffb4b0c --- /dev/null +++ b/pkg_builder/README.md @@ -0,0 +1,46 @@ +# GitList Builder: Tools to build gitlist package + +## Status + +### Ready to build + +* Core Deb Packages: +``` +$ make build_deb +``` + +* Core Rpm Packages: +``` +$ make build_rpm +``` + +### Not Ready (Comming soon) + +Packages to install configuration files: + +* make apache_deb +* make apache_rpm +* make nginx_deb +* make nginx_rpm + +## Dependencies + +* To use this package builder you may need to install some development packages like: dpkg-dev or evem rpm; +* A tarball of a stable release; + +## Instructions + +To use this builder just download the lastest stable release into this directory and build using the make functions avaible in make help + +## How to build + +The packages can be generated by running Makefile functions like: + +``` +$ make build_deb +$ make build_rpm +``` + +## Structure + +Core package is a simple source package while a configuration package requires all the dependencies. diff --git a/pkg_builder/tools/gitlist.spec b/pkg_builder/tools/gitlist.spec new file mode 100644 index 0000000..d7d675c --- /dev/null +++ b/pkg_builder/tools/gitlist.spec @@ -0,0 +1,37 @@ +Name: %{name} +Summary: %{summary} +Version: %{version} +Release: %{release} +URL: %{url} +License: %{license} +Group: %{group} +Vendor: %{vendor} +Packager: %{user} +Prefix: %{prefix} +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +Source0: %{name}.tar + +%description +%{summary} + +%prep +%setup -c + +%build + +%install +%{__rm} -rf %{buildroot} +%{__mkdir} -p %{buildroot}%{prefix} +%{__cp} -Ra * %{buildroot}%{prefix} + +%clean +rm -rf %{buildroot} + +%files +%defattr(0755,root,root) +%{prefix}/* + +%changelog +* Thu Jan 21 2013 Bruno Gurgel +- Initial diff --git a/pkg_builder/tools/release.info b/pkg_builder/tools/release.info new file mode 100644 index 0000000..4eac5e4 --- /dev/null +++ b/pkg_builder/tools/release.info @@ -0,0 +1,2 @@ +release=0.3 +name=gitlist diff --git a/src/GitList/Application.php b/src/GitList/Application.php index 7f7e2cd..30c061c 100644 --- a/src/GitList/Application.php +++ b/src/GitList/Application.php @@ -15,6 +15,8 @@ use GitList\Provider\RoutingUtilServiceProvider; */ class Application extends SilexApplication { + protected $path; + /** * Constructor initialize services. * @@ -24,30 +26,39 @@ class Application extends SilexApplication public function __construct(Config $config, $root = null) { parent::__construct(); - $app = $this; - $root = realpath($root); + $this->path = realpath($root); $this['debug'] = $config->get('app', 'debug'); $this['filetypes'] = $config->getSection('filetypes'); - $this['cache.archives'] = $root . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'archives'; + $this['cache.archives'] = $this->getCachePath() . 'archives'; // Register services $this->register(new TwigServiceProvider(), array( - 'twig.path' => $root . DIRECTORY_SEPARATOR . 'views', - 'twig.options' => array('cache' => $root . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'views'), + 'twig.path' => $this->getViewPath(), + 'twig.options' => $config->get('app', 'cache') ? + array('cache' => $this->getCachePath() . 'views') : array(), )); + + $repositories = $config->get('git', 'repositories'); + $this->register(new GitServiceProvider(), array( - 'git.client' => $config->get('git', 'client'), - 'git.repos' => $config->get('git', 'repositories'), - 'git.hidden' => $config->get('git', 'hidden') ? $config->get('git', 'hidden') : array(), + 'git.client' => $config->get('git', 'client'), + 'git.repos' => $repositories, + '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', )); + $this->register(new ViewUtilServiceProvider()); $this->register(new RepositoryUtilServiceProvider()); $this->register(new UrlGeneratorServiceProvider()); $this->register(new RoutingUtilServiceProvider()); - $this['twig'] = $this->share($this->extend('twig', function($twig, $app) { + $this['twig'] = $this->share($this->extend('twig', function ($twig, $app) { + $twig->addFilter('htmlentities', new \Twig_Filter_Function('htmlentities')); $twig->addFilter('md5', new \Twig_Filter_Function('md5')); return $twig; @@ -64,4 +75,25 @@ class Application extends SilexApplication )); }); } + + public function getPath() + { + return $this->path . DIRECTORY_SEPARATOR; + } + + public function setPath($path) + { + $this->path = $path; + return $this; + } + + public function getCachePath() + { + return $this->path . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; + } + + public function getViewPath() + { + return $this->path . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR; + } } diff --git a/src/GitList/Config.php b/src/GitList/Config.php index 33d5b39..fab9338 100644 --- a/src/GitList/Config.php +++ b/src/GitList/Config.php @@ -6,18 +6,22 @@ class Config { protected $data; - public static function fromFile($file) { + public static function fromFile($file) + { if (!file_exists($file)) { die(sprintf('Please, create the %1$s file.', $file)); } + $data = parse_ini_file($file, true); - return new static($data); + $config = new static($data); + $config->validateOptions(); + + return $config; } - public function __construct($data) + public function __construct($data = array()) { $this->data = $data; - $this->validateOptions(); } public function get($section, $option) @@ -49,8 +53,26 @@ class Config protected function validateOptions() { - if (!$this->get('git', 'repositories') || !is_dir($this->get('git', 'repositories'))) { + $repositories = $this->get('git', 'repositories'); + + $atLeastOneOk = false; + $atLeastOneWrong = false; + + foreach ($repositories as $directory) { + if (!$directory || !is_dir($directory)) { + $atLeastOneWrong = true; + } else { + $atLeastOneOk = true; + } + } + + if (!$atLeastOneOk) { die("Please, edit the config file and provide your repositories directory"); } + + if ($atLeastOneWrong) { + die("One or more of the supplied repository paths appears to be wrong. Please, check the config file"); + } } } + diff --git a/src/GitList/Controller/BlobController.php b/src/GitList/Controller/BlobController.php index 9a44a27..fc5ec90 100644 --- a/src/GitList/Controller/BlobController.php +++ b/src/GitList/Controller/BlobController.php @@ -12,8 +12,12 @@ class BlobController implements ControllerProviderInterface { $route = $app['controllers_factory']; - $route->get('{repo}/blob/{branch}/{file}', function($repo, $branch, $file) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->get('{repo}/blob/{commitishPath}', function ($repo, $commitishPath) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + list($branch, $file) = $app['util.routing'] + ->parseCommitishPathParam($commitishPath, $repo); + list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $blob = $repository->getBlob("$branch:\"$file\""); @@ -23,8 +27,7 @@ class BlobController implements ControllerProviderInterface if ($fileType !== 'image' && $app['util.repository']->isBinary($file)) { return $app->redirect($app['url_generator']->generate('blob_raw', array( 'repo' => $repo, - 'branch' => $branch, - 'file' => $file, + 'commitishPath' => $commitishPath, ))); } @@ -38,31 +41,34 @@ class BlobController implements ControllerProviderInterface 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), )); - })->assert('file', '.+') - ->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') + })->assert('repo', $app['util.routing']->getRepositoryRegex()) + ->assert('commitishPath', '.+') ->bind('blob'); - $route->get('{repo}/raw/{branch}/{file}', function($repo, $branch, $file) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->get('{repo}/raw/{commitishPath}', function ($repo, $commitishPath) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + list($branch, $file) = $app['util.routing'] + ->parseCommitishPathParam($commitishPath, $repo); + list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); + $blob = $repository->getBlob("$branch:\"$file\"")->output(); $headers = array(); if ($app['util.repository']->isBinary($file)) { $headers['Content-Disposition'] = 'attachment; filename="' . $file . '"'; - $headers['Content-Transfer-Encoding'] = 'application/octet-stream'; - $headers['Content-Transfer-Encoding'] = 'binary'; + $headers['Content-Type'] = 'application/octet-stream'; } else { - $headers['Content-Transfer-Encoding'] = 'text/plain'; + $headers['Content-Type'] = 'text/plain'; } return new Response($blob, 200, $headers); - })->assert('file', '.+') - ->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') + })->assert('repo', $app['util.routing']->getRepositoryRegex()) + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->bind('blob_raw'); return $route; } } + diff --git a/src/GitList/Controller/CommitController.php b/src/GitList/Controller/CommitController.php index 776f669..86dc409 100644 --- a/src/GitList/Controller/CommitController.php +++ b/src/GitList/Controller/CommitController.php @@ -12,14 +12,22 @@ class CommitController implements ControllerProviderInterface { $route = $app['controllers_factory']; - $route->get('{repo}/commits/{branch}/{file}', function($repo, $branch, $file) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->get('{repo}/commits/{commitishPath}', function ($repo, $commitishPath) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + if ($commitishPath === null) { + $commitishPath = $repository->getHead(); + } + + list($branch, $file) = $app['util.routing'] + ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); $type = $file ? "$branch -- \"$file\"" : $branch; $pager = $app['util.view']->getPager($app['request']->get('page'), $repository->getTotalCommits($type)); $commits = $repository->getPaginatedCommits($type, $pager['current']); + $categorized = array(); foreach ($commits as $commit) { $date = $commit->getDate(); @@ -30,6 +38,7 @@ class CommitController implements ControllerProviderInterface $template = $app['request']->isXmlHttpRequest() ? 'commits_list.twig' : 'commits.twig'; return $app['twig']->render($template, array( + 'page' => 'commits', 'pager' => $pager, 'repo' => $repo, 'branch' => $branch, @@ -39,15 +48,16 @@ class CommitController implements ControllerProviderInterface 'file' => $file, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') - ->assert('file', '.+') - ->value('branch', 'master') - ->value('file', '') + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) + ->value('commitishPath', null) ->bind('commits'); - $route->post('{repo}/commits/search', function(Request $request, $repo) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->post('{repo}/commits/{branch}/search', function (Request $request, $repo, $branch = '') use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + $query = $request->get('query'); + $commits = $repository->searchCommitLog($request->get('query')); + $categorized = array(); foreach ($commits as $commit) { $date = $commit->getDate(); @@ -57,21 +67,24 @@ class CommitController implements ControllerProviderInterface return $app['twig']->render('searchcommits.twig', array( 'repo' => $repo, - 'branch' => 'master', + 'branch' => $branch, 'file' => '', 'commits' => $categorized, 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), + 'query' => $query )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) + ->assert('branch', $app['util.routing']->getBranchRegex()) ->bind('searchcommits'); - $route->get('{repo}/commit/{commit}/', function($repo, $commit) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->get('{repo}/commit/{commit}', function ($repo, $commit) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $commit = $repository->getCommit($commit); + $branch = $repository->getHead(); return $app['twig']->render('commit.twig', array( - 'branch' => 'master', + 'branch' => $branch, 'repo' => $repo, 'commit' => $commit, )); @@ -79,8 +92,11 @@ class CommitController implements ControllerProviderInterface ->assert('commit', '[a-f0-9^]+') ->bind('commit'); - $route->get('{repo}/blame/{branch}/{file}', function($repo, $branch, $file) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $route->get('{repo}/blame/{commitishPath}', function ($repo, $commitishPath) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + list($branch, $file) = $app['util.routing'] + ->parseCommitishPathParam($commitishPath, $repo); list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); @@ -95,10 +111,10 @@ class CommitController implements ControllerProviderInterface 'blames' => $blames, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('file', '.+') - ->assert('branch', '[\w-._\/]+') + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->bind('blame'); return $route; } } + diff --git a/src/GitList/Controller/MainController.php b/src/GitList/Controller/MainController.php index 89b6f62..3c8919c 100644 --- a/src/GitList/Controller/MainController.php +++ b/src/GitList/Controller/MainController.php @@ -5,6 +5,7 @@ namespace GitList\Controller; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; class MainController implements ControllerProviderInterface { @@ -13,23 +14,28 @@ class MainController implements ControllerProviderInterface $route = $app['controllers_factory']; $route->get('/', function() use ($app) { - $repositories = array_map( - function ($repo) use ($app) { - $repo['relativePath'] = $app['util.routing']->getRelativePath($repo['path']); - return $repo; - }, - $app['git']->getRepositories($app['git.repos']) - ); + $repositories = $app['git']->getRepositories($app['git.repos']); return $app['twig']->render('index.twig', array( 'repositories' => $repositories, )); })->bind('homepage'); + + $route->get('/refresh', function(Request $request) use ($app ) { + # Go back to calling page + return $app->redirect($request->headers->get('Referer')); + })->bind('refresh'); + $route->get('{repo}/stats/{branch}', function($repo, $branch) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + if ($branch === null) { + $branch = $repository->getHead(); + } + $stats = $repository->getStatistics($branch); - $authors = $repository->getAuthorStatistics(); + $authors = $repository->getAuthorStatistics($branch); return $app['twig']->render('stats.twig', array( 'repo' => $repo, @@ -37,15 +43,20 @@ class MainController implements ControllerProviderInterface 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), 'stats' => $stats, - 'authors' => $authors, + 'authors' => $authors, )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') - ->value('branch', 'master') + ->assert('branch', $app['util.routing']->getBranchRegex()) + ->value('branch', null) ->bind('stats'); $route->get('{repo}/{branch}/rss/', function($repo, $branch) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + if ($branch === null) { + $branch = $repository->getHead(); + } + $commits = $repository->getPaginatedCommits($branch); $html = $app['twig']->render('rss.twig', array( @@ -56,7 +67,8 @@ class MainController implements ControllerProviderInterface return new Response($html, 200, array('Content-Type' => 'application/rss+xml')); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') + ->assert('branch', $app['util.routing']->getBranchRegex()) + ->value('branch', null) ->bind('rss'); return $route; diff --git a/src/GitList/Controller/NetworkController.php b/src/GitList/Controller/NetworkController.php new file mode 100644 index 0000000..6f3e167 --- /dev/null +++ b/src/GitList/Controller/NetworkController.php @@ -0,0 +1,113 @@ +get('{repo}/network/{commitishPath}/{page}.json', + function ($repo, $commitishPath, $page) use ($app) { + /** @var $repository Repository */ + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + if ($commitishPath === null) { + $commitishPath = $repository->getHead(); + } + + $pager = $app['util.view']->getPager($page, $repository->getTotalCommits($commitishPath)); + $commits = $repository->getPaginatedCommits($commitishPath, $pager['current']); + + $jsonFormattedCommits = array(); + + foreach ($commits as $commit) { + $detailsUrl = $app['url_generator']->generate( + 'commit', + array( + 'repo' => $repo, + 'commit' => $commit->getHash() + ) + ); + + $jsonFormattedCommits[$commit->getHash()] = array( + 'hash' => $commit->getHash(), + 'parentsHash' => $commit->getParentsHash(), + 'date' => $commit->getDate()->format('U'), + 'message' => htmlentities($commit->getMessage()), + 'details' => $detailsUrl, + 'author' => array( + 'name' => $commit->getAuthor()->getName(), + 'email' => $commit->getAuthor()->getEmail(), + // due to the lack of a inbuilt javascript md5 mechanism, build the full avatar url on the php side + 'image' => 'http://gravatar.com/avatar/' . md5( + strtolower($commit->getAuthor()->getEmail()) + ) . '?s=40' + ) + ); + } + + $nextPageUrl = null; + if ($pager['last'] !== $pager['current']) { + $nextPageUrl = $app['url_generator']->generate( + 'networkData', + array( + 'repo' => $repo, + 'commitishPath' => $commitishPath, + 'page' => $pager['next'] + ) + ); + } + + return $app->json( array( + 'repo' => $repo, + 'commitishPath' => $commitishPath, + 'nextPage' => $nextPageUrl, + 'start' => $commits[0]->getHash(), + 'commits' => $jsonFormattedCommits + ), 200 + ); + } + )->assert('repo', $app['util.routing']->getRepositoryRegex()) + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) + ->value('commitishPath', null) + ->assert('page', '\d+') + ->value('page', '0') + ->bind('networkData'); + + $route->get( + '{repo}/network/{commitishPath}', + function ($repo, $commitishPath) use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + if ($commitishPath === null) { + $commitishPath = $repository->getHead(); + } + + list($branch, $file) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo); + list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); + + return $app['twig']->render( + 'network.twig', + array( + 'repo' => $repo, + 'branch' => $branch, + 'commitishPath' => $commitishPath, + ) + ); + } + )->assert('repo', $app['util.routing']->getRepositoryRegex()) + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) + ->value('commitishPath', null) + ->bind('network'); + + return $route; + } +} diff --git a/src/GitList/Controller/TreeController.php b/src/GitList/Controller/TreeController.php index fe463b7..384b537 100644 --- a/src/GitList/Controller/TreeController.php +++ b/src/GitList/Controller/TreeController.php @@ -13,12 +13,14 @@ class TreeController implements ControllerProviderInterface { $route = $app['controllers_factory']; - $route->get('{repo}/tree/{branch}/{tree}/', $treeController = function($repo, $branch = '', $tree = '') use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); - if (!$branch) { - $branch = $repository->getHead(); + $route->get('{repo}/tree/{commitishPath}/', $treeController = function ($repo, $commitishPath = '') use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + if (!$commitishPath) { + $commitishPath = $repository->getHead(); } + list($branch, $tree) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo); + list($branch, $tree) = $app['util.repository']->extractRef($repository, $branch, $tree); $files = $repository->getTree($tree ? "$branch:\"$tree\"/" : $branch); $breadcrumbs = $app['util.view']->getBreadcrumbs($tree); @@ -39,22 +41,21 @@ class TreeController implements ControllerProviderInterface 'breadcrumbs' => $breadcrumbs, 'branches' => $repository->getBranches(), 'tags' => $repository->getTags(), - 'readme' => $app['util.repository']->getReadme($repo, $branch), + 'readme' => $app['util.repository']->getReadme($repository, $branch), )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') - ->assert('tree', '.+') + ->assert('commitishPath', $app['util.routing']->getCommitishPathRegex()) ->bind('tree'); - $route->post('{repo}/tree/{branch}/search', function(Request $request, $repo, $branch = '', $tree = '') use ($app) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); - + $route->post('{repo}/tree/{branch}/search', function (Request $request, $repo, $branch = '', $tree = '') use ($app) { + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if (!$branch) { $branch = $repository->getHead(); } - $breadcrumbs = $app['util.view']->getBreadcrumbs($tree); - $results = $repository->searchTree($request->get('query'), $branch); + $query = $request->get('query'); + $breadcrumbs = array(array('dir' => 'Search results for: ' . $query, 'path' => '')); + $results = $repository->searchTree($query, $branch); return $app['twig']->render('search.twig', array( 'results' => $results, @@ -66,22 +67,15 @@ class TreeController implements ControllerProviderInterface 'tags' => $repository->getTags(), )); })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') + ->assert('branch', $app['util.routing']->getBranchRegex()) ->bind('search'); - $route->get('{repo}/{branch}/', function($repo, $branch) use ($app, $treeController) { - return $treeController($repo, $branch); - })->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') - ->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) { - $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + $tree = $repository->getBranchTree($branch); if (false === $tree) { @@ -109,9 +103,22 @@ class TreeController implements ControllerProviderInterface )); })->assert('format', '(zip|tar)') ->assert('repo', $app['util.routing']->getRepositoryRegex()) - ->assert('branch', '[\w-._\/]+') + ->assert('branch', $app['util.routing']->getBranchRegex()) ->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; } } + diff --git a/src/GitList/Exception/BlankDataException.php b/src/GitList/Exception/BlankDataException.php index f11fb4e..1293fbb 100644 --- a/src/GitList/Exception/BlankDataException.php +++ b/src/GitList/Exception/BlankDataException.php @@ -2,4 +2,7 @@ namespace GitList\Exception; -class BlankDataException extends \RuntimeException {} +class BlankDataException extends \RuntimeException +{ + +} diff --git a/src/GitList/Exception/EmptyRepositoryException.php b/src/GitList/Exception/EmptyRepositoryException.php new file mode 100644 index 0000000..027a4d7 --- /dev/null +++ b/src/GitList/Exception/EmptyRepositoryException.php @@ -0,0 +1,8 @@ +setDefaultBranch($options['default_branch']); + $this->setHidden($options['hidden']); + } + + public function getRepositoryFromName($paths, $repo) + { + $repositories = $this->getRepositories($paths); + $path = $repositories[$repo]['path']; + + return $this->getRepository($path); + } + /** - * Creates a new repository on the specified path + * Searches for valid repositories on the specified path * - * @param string $path Path where the new repository will be created - * @return Repository Instance of Repository + * @param array $paths Array of paths where repositories will be searched + * @return array Found repositories, containing their name, path and description + */ + public function getRepositories($paths) + { + $allRepositories = array(); + + foreach ($paths as $path) { + $repositories = $this->recurseDirectory($path); + + if (empty($repositories)) { + throw new \RuntimeException('There are no GIT repositories in ' . $path); + } + + $allRepositories = array_merge($allRepositories, $repositories); + } + + $allRepositories = array_unique($allRepositories, SORT_REGULAR); + asort($allRepositories); + + return $allRepositories; + } + + private function recurseDirectory($path, $topLevel = true) + { + $dir = new \DirectoryIterator($path); + + $repositories = array(); + + foreach ($dir as $file) { + if ($file->isDot()) { + continue; + } + + if (strrpos($file->getFilename(), '.') === 0) { + continue; + } + + if (!$file->isReadable()) { + continue; + } + + if ($file->isDir()) { + $isBare = file_exists($file->getPathname() . '/HEAD'); + $isRepository = file_exists($file->getPathname() . '/.git/HEAD'); + + if ($isRepository || $isBare) { + if (in_array($file->getPathname(), $this->getHidden())) { + continue; + } + + if ($isBare) { + $description = $file->getPathname() . '/description'; + } else { + $description = $file->getPathname() . '/.git/description'; + } + + if (file_exists($description)) { + $description = file_get_contents($description); + } else { + $description = null; + } + + if (!$topLevel) { + $repoName = $file->getPathInfo()->getFilename() . '/' . $file->getFilename(); + } else { + $repoName = $file->getFilename(); + } + + $repositories[$repoName] = array( + 'name' => $repoName, + 'path' => $file->getPathname(), + 'description' => $description + ); + + continue; + } else { + $repositories = array_merge($repositories, $this->recurseDirectory($file->getPathname(), false)); + } + } + } + + return $repositories; + } + + /** + * Set default branch as a string. + * + * @param string $branch Name of branch to use when repo's HEAD is detached. + */ + protected function setDefaultBranch($branch) + { + $this->defaultBranch = $branch; + + return $this; + } + + /** + * Return name of default branch as a string. + */ + public function getDefaultBranch() + { + return $this->defaultBranch; + } + + /** + * Get hidden repository list + * + * @return array List of repositories to hide + */ + protected function getHidden() + { + return $this->hidden; + } + + /** + * Set the hidden repository list + * + * @param array $hidden List of repositories to hide + */ + protected function setHidden($hidden) + { + $this->hidden = $hidden; + + return $this; + } + + /** + * Overloads the parent::createRepository method for the correct Repository class instance + * + * {@inheritdoc} */ public function createRepository($path, $bare = null) { @@ -24,10 +171,9 @@ class Client extends BaseClient } /** - * Opens a repository at the specified path - * - * @param string $path Path where the repository is located - * @return Repository Instance of Repository + * Overloads the parent::getRepository method for the correct Repository class instance + * + * {@inheritdoc} */ public function getRepository($path) { @@ -35,10 +181,7 @@ class Client extends BaseClient throw new \RuntimeException('There is no GIT repository at ' . $path); } - if (in_array($path, $this->getHidden())) { - throw new \RuntimeException('You don\'t have access to this repository'); - } - return new Repository($path, $this); } } + diff --git a/src/GitList/Git/Repository.php b/src/GitList/Git/Repository.php index 4c0cf91..3806766 100644 --- a/src/GitList/Git/Repository.php +++ b/src/GitList/Git/Repository.php @@ -4,10 +4,202 @@ namespace GitList\Git; use Gitter\Repository as BaseRepository; use Gitter\Model\Commit\Commit; +use Gitter\Model\Commit\Diff; +use Gitter\PrettyFormat; use Symfony\Component\Filesystem\Filesystem; class Repository extends BaseRepository { + /** + * Return true if the repo contains this commit. + * + * @param $commitHash Hash of commit whose existence we want to check + * @return boolean Whether or not the commit exists in this repo + */ + public function hasCommit($commitHash) + { + $logs = $this->getClient()->run($this, "show $commitHash"); + $logs = explode("\n", $logs); + + return strpos($logs[0], 'commit') === 0; + } + + /** + * Get the current branch, returning a default value when HEAD is detached. + */ + public function getHead($default = null) + { + $client = $this->getClient(); + + return parent::getHead($client->getDefaultBranch()); + } + + /** + * Show the data from a specific commit + * + * @param string $commitHash Hash of the specific commit to read data + * @return array Commit data + */ + public function getCommit($commitHash) + { + $logs = $this->getClient()->run($this, + "show --pretty=format:\"%H" + . "%h%T%P" + . "%an%ae" + . "%at%cn%ce" + . "%ct" + . "" + . "" + . "\" $commitHash" + ); + + $xmlEnd = strpos($logs, '') + 7; + $commitInfo = substr($logs, 0, $xmlEnd); + $commitData = substr($logs, $xmlEnd); + $logs = explode("\n", $commitData); + + // Read commit metadata + $format = new PrettyFormat; + $data = $format->parse($commitInfo); + $commit = new Commit; + $commit->importData($data[0]); + + if ($commit->getParentsHash()) { + $command = 'diff ' . $commitHash . '~1..' . $commitHash; + $logs = explode("\n", $this->getClient()->run($this, $command)); + } + + $commit->setDiffs($this->readDiffLogs($logs)); + + return $commit; + } + + /** + * Blames the provided file and parses the output + * + * @param string $file File that will be blamed + * @return array Commits hashes containing the lines + */ + public function getBlame($file) + { + $blame = array(); + $logs = $this->getClient()->run($this, "blame --root -sl $file"); + $logs = explode("\n", $logs); + + $i = 0; + $previousCommit = ''; + foreach ($logs as $log) { + if ($log == '') { + continue; + } + + preg_match_all("/([a-zA-Z0-9]{40})\s+.*?([0-9]+)\)(.+)/", $log, $match); + + $currentCommit = $match[1][0]; + if ($currentCommit != $previousCommit) { + ++$i; + $blame[$i] = array( + 'line' => '', + 'commit' => $currentCommit, + 'commitShort' => substr($currentCommit, 0, 8) + ); + } + + $blame[$i]['line'] .= PHP_EOL . $match[3][0]; + $previousCommit = $currentCommit; + } + + return $blame; + } + + /** + * Read diff logs and generate a collection of diffs + * + * @param array $logs Array of log rows + * @return array Array of diffs + */ + public function readDiffLogs(array $logs) + { + $diffs = array(); + $lineNumOld = 0; + $lineNumNew = 0; + foreach ($logs as $log) { + # Skip empty lines + if ($log == "") { + continue; + } + + if ('diff' === substr($log, 0, 4)) { + if (isset($diff)) { + $diffs[] = $diff; + } + + $diff = new Diff; + if (preg_match('/^diff --[\S]+ a\/?(.+) b\/?/', $log, $name)) { + $diff->setFile($name[1]); + } + continue; + } + + if ('index' === substr($log, 0, 5)) { + $diff->setIndex($log); + continue; + } + + if ('---' === substr($log, 0, 3)) { + $diff->setOld($log); + continue; + } + + if ('+++' === substr($log, 0, 3)) { + $diff->setNew($log); + continue; + } + + // Handle binary files properly. + if ('Binary' === substr($log, 0, 6)) { + $m = array(); + if (preg_match('/Binary files (.+) and (.+) differ/', $log, $m)) { + $diff->setOld($m[1]); + $diff->setNew(" {$m[2]}"); + } + } + + if (!empty($log)) { + switch ($log[0]) { + case "@": + // Set the line numbers + preg_match('/@@ -([0-9]+)/', $log, $matches); + $lineNumOld = $matches[1] - 1; + $lineNumNew = $matches[1] - 1; + break; + case "-": + $lineNumOld++; + break; + case "+": + $lineNumNew++; + break; + default: + $lineNumOld++; + $lineNumNew++; + } + } else { + $lineNumOld++; + $lineNumNew++; + } + + if (isset($diff)) { + $diff->addLine($log, $lineNumOld, $lineNumNew); + } + } + + if (isset($diff)) { + $diffs[] = $diff; + } + + return $diffs; + } + /** * Show the repository commit log with pagination * @@ -18,13 +210,24 @@ class Repository extends BaseRepository { $page = 15 * $page; $pager = "--skip=$page --max-count=15"; - $command = "log $pager --pretty=format:'%H%h%T%P%an%ae%at%cn%ce%ct'"; + $command = + "log $pager --pretty=format:\"%H" + . "%h%T%P" + . "%an%ae" + . "%at%cn" + . "%ce" + . "%ct" + . "\""; if ($file) { $command .= " $file"; } - $logs = $this->getPrettyFormat($command); + try { + $logs = $this->getPrettyFormat($command); + } catch (\RuntimeException $e) { + return array(); + } foreach ($logs as $log) { $commit = new Commit; @@ -37,9 +240,21 @@ class Repository extends BaseRepository public function searchCommitLog($query) { - $command = "log --grep='$query' --pretty=format:'%H%h%T%P%an%ae%at%cn%ce%ct'"; + $query = escapeshellarg($query); + $command = + "log --grep={$query} --pretty=format:\"%H" + . "%h%T%P" + . "%an%ae" + . "%at%cn" + . "%ce" + . "%ct" + . "\""; - $logs = $this->getPrettyFormat($command); + try { + $logs = $this->getPrettyFormat($command); + } catch (\RuntimeException $e) { + return array(); + } foreach ($logs as $log) { $commit = new Commit; @@ -52,8 +267,10 @@ class Repository extends BaseRepository public function searchTree($query, $branch) { + $query = escapeshellarg($query); + try { - $results = $this->getClient()->run($this, "grep -I --line-number '$query' $branch"); + $results = $this->getClient()->run($this, "grep -I --line-number {$query} $branch"); } catch (\RuntimeException $e) { return false; } @@ -65,20 +282,22 @@ class Repository extends BaseRepository continue; } - preg_match_all('/([\w-._]+):(.+):([0-9]+):(.+)/', $result, $matches, PREG_SET_ORDER); + 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]; + $data['file'] = $matches[0][2]; + $data['line'] = $matches[0][3]; + $data['match'] = $matches[0][4]; + $searchResults[] = $data; } return $searchResults; } - public function getAuthorStatistics() + public function getAuthorStatistics($branch) { - $logs = $this->getClient()->run($this, 'log --pretty=format:"%an||%ae" ' . $this->getHead()); + $logs = $this->getClient()->run($this, 'log --pretty=format:"%an||%ae" ' . $branch); if (empty($logs)) { throw new \RuntimeException('No statistics available'); @@ -124,8 +343,12 @@ class Repository extends BaseRepository $data['size'] += $file[3]; } - if (($pos = strrpos($file[4], '.')) !== FALSE) { - $data['extensions'][] = substr($file[4], $pos); + if (($pos = strrpos($file[4], '.')) !== false) { + $extension = substr($file[4], $pos); + + if (($pos = strrpos($extension, '/')) === false) { + $data['extensions'][] = $extension; + } } } @@ -148,4 +371,25 @@ class Repository extends BaseRepository $fs->mkdir(dirname($output)); $this->getClient()->run($this, "archive --format=$format --output=$output $tree"); } + + /** + * Return true if $path exists in $branch; return false otherwise. + * + * @param string $commitish Commitish reference; branch, tag, SHA1, etc. + * @param string $path Path whose existence we want to verify. + * + * GRIPE Arguably belongs in Gitter, as it's generally useful functionality. + * Also, this really may not be the best way to do this. + */ + public function pathExists($commitish, $path) + { + $output = $this->getClient()->run($this, "ls-tree $commitish '$path'"); + + if (strlen($output) > 0) { + return true; + } + + return false; + } } + diff --git a/src/GitList/Provider/GitServiceProvider.php b/src/GitList/Provider/GitServiceProvider.php index 5427560..44f2cfa 100644 --- a/src/GitList/Provider/GitServiceProvider.php +++ b/src/GitList/Provider/GitServiceProvider.php @@ -8,6 +8,7 @@ use Silex\ServiceProviderInterface; class GitServiceProvider implements ServiceProviderInterface { + /** * Register the Git\Client on the Application ServiceProvider * @@ -19,6 +20,8 @@ class GitServiceProvider implements ServiceProviderInterface $app['git'] = function () use ($app) { $options['path'] = $app['git.client']; $options['hidden'] = $app['git.hidden']; + $options['ini.file'] = $app['ini.file']; + $options['default_branch'] = $app['git.default_branch']; return new Client($options); }; diff --git a/src/GitList/Util/Repository.php b/src/GitList/Util/Repository.php index e158b95..5f6c80c 100644 --- a/src/GitList/Util/Repository.php +++ b/src/GitList/Util/Repository.php @@ -52,6 +52,7 @@ class Repository 'r' => 'r', 'sh' => 'shell', 'ss' => 'scheme', + 'scala' => 'text/x-scala', 'scm' => 'scheme', 'sls' => 'scheme', 'sps' => 'scheme', @@ -160,10 +161,12 @@ class Repository return false; } - public function getReadme($repo, $branch = 'master') + public function getReadme($repository, $branch = null) { - $repository = $this->app['git']->getRepository($this->app['git.repos'] . $repo); $files = $repository->getTree($branch)->output(); + if ($branch === null) { + $branch = $repository->getHead(); + } foreach ($files as $file) { if (preg_match('/^readme*/i', $file['name'])) { @@ -185,7 +188,7 @@ class Repository * @param string $tree * @return array */ - public function extractRef($repository, $branch='', $tree='') + public function extractRef($repository, $branch = '', $tree = '') { $branch = trim($branch, '/'); $tree = trim($tree, '/'); @@ -196,26 +199,24 @@ class Repository $branch = $matches[1]; } else { // Otherwise, attempt to detect the ref using a list of the project's branches and tags - $valid_refs = array_merge((array) $repository->getBranches(), (array) $repository->getTags()); - foreach ($valid_refs as $k => $v) { - if (!preg_match("#{$v}/#", $input)) { - unset($valid_refs[$k]); + $validRefs = array_merge((array) $repository->getBranches(), (array) $repository->getTags()); + foreach ($validRefs as $key => $ref) { + if (!preg_match(sprintf("#^%s/#", preg_quote($ref, '#')), $input)) { + unset($validRefs[$key]); } } // No exact ref match, so just try our best - if (count($valid_refs) > 1) { + if (count($validRefs) > 1) { preg_match('/([^\/]+)(.*)/', $input, $matches); - $branch = preg_replace('/^\/|\/$/', '', $matches[0]); + $branch = preg_replace('/^\/|\/$/', '', $matches[1]); } else { // Extract branch name - $branch = array_shift($valid_refs); + $branch = array_shift($validRefs); } } - $tree = trim(str_replace($branch, "", $input), "/"); - return array($branch, $tree); } - } + diff --git a/src/GitList/Util/Routing.php b/src/GitList/Util/Routing.php index 884e251..c687c3c 100644 --- a/src/GitList/Util/Routing.php +++ b/src/GitList/Util/Routing.php @@ -1,8 +1,9 @@ app = $app; } + /* @brief Return $commitish, $path parsed from $commitishPath, based on + * what's in $repo. Raise a 404 if $branchpath does not represent a + * valid branch and path. + * + * A helper for parsing routes that use commit-ish names and paths + * separated by /, since route regexes are not enough to get that right. + */ + public function parseCommitishPathParam($commitishPath, $repo) + { + $app = $this->app; + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); + + $commitish = null; + $path = null; + + $slashPosition = strpos($commitishPath, '/'); + if (strlen($commitishPath) >= 40 && + ($slashPosition === false || + $slashPosition === 40)) { + // We may have a commit hash as our commitish. + $hash = substr($commitishPath, 0, 40); + if ($repository->hasCommit($hash)) { + $commitish = $hash; + } + } + + if ($commitish === null) { + $branches = $repository->getBranches(); + + $tags = $repository->getTags(); + if ($tags !== null && count($tags) > 0) { + $branches = array_merge($branches, $tags); + } + + $matchedBranch = null; + $matchedBranchLength = 0; + foreach ($branches as $branch) { + if (strpos($commitishPath, $branch) === 0 && + strlen($branch) > $matchedBranchLength) { + $matchedBranch = $branch; + $matchedBranchLength = strlen($matchedBranch); + } + } + + if ($matchedBranch === null) { + throw new EmptyRepositoryException('This repository is currently empty. There are no commits.'); + } + + $commitish = $matchedBranch; + } + + $commitishLength = strlen($commitish); + $path = substr($commitishPath, $commitishLength); + if (strpos($path, '/') === 0) { + $path = substr($path, 1); + } + + return array($commitish, $path); + } + + public function getBranchRegex() + { + static $branchRegex = null; + + if ($branchRegex === null) { + $branchRegex = '(?!/|.*([/.]\.|//|@\{|\\\\))[^\040\177 ~^:?*\[]+(?app; + $self = $this; $quotedPaths = array_map( - function ($repo) use ($app) { - return preg_quote($app['util.routing']->getRelativePath($repo['path']), '#'); + function ($repo) use ($app, $self) { + $repoName = $repo['name']; + //Windows + if ($self->isWindows()){ + $repoName = str_replace('\\', '\\\\',$repoName); + } + return $repoName; }, $this->app['git']->getRepositories($this->app['git.repos']) ); - usort($quotedPaths, function ($a, $b) { return strlen($b) - strlen($a); }); + + usort( + $quotedPaths, + function ($a, $b) { + return strlen($b) - strlen($a); + } + ); + $regex = implode('|', $quotedPaths); } 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 * - * @param string $repoPath Full path to the repository + * @param string $repoPath Full path to the repository * @return string Relative path to the repository from git.repositories */ public function getRelativePath($repoPath) { if (strpos($repoPath, $this->app['git.repos']) === 0) { $relativePath = substr($repoPath, strlen($this->app['git.repos'])); - return ltrim($relativePath, '/'); + + return ltrim(strtr($relativePath, '\\', '/'), '/'); } else { throw new \InvalidArgumentException( sprintf("Path '%s' does not match configured repository directory", $repoPath) @@ -50,3 +160,4 @@ class Routing } } } + diff --git a/tests/InterfaceTest.php b/tests/InterfaceTest.php index 38e437d..fecdfed 100644 --- a/tests/InterfaceTest.php +++ b/tests/InterfaceTest.php @@ -2,7 +2,7 @@ use Silex\WebTestCase; use Symfony\Component\Filesystem\Filesystem; -use Gitter\Client; +use GitList\Git\Client; class InterfaceTest extends WebTestCase { @@ -16,10 +16,10 @@ class InterfaceTest extends WebTestCase } elseif (getenv('TMPDIR')) { self::$tmpdir = getenv('TMPDIR'); } 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->mkdir(self::$tmpdir); @@ -30,6 +30,12 @@ class InterfaceTest extends WebTestCase $options['path'] = getenv('GIT_CLIENT') ?: '/usr/bin/git'; $options['hidden'] = array(self::$tmpdir . '/hiddenrepo'); + $options['default_branch'] = 'master'; + $options['ini.file'] = "config.ini"; + + $cacheDir = self::$tmpdir . DIRECTORY_SEPARATOR . 'cache'; + $fs->mkdir($cacheDir); + $git = new Client($options); self::$gitPath = $options['path']; @@ -49,13 +55,16 @@ class InterfaceTest extends WebTestCase // foobar repository fixture $git->createRepository(self::$tmpdir . 'foobar'); - $repository = $git->getRepository(self::$tmpdir . '/foobar'); + $repository = $git->getRepository(self::$tmpdir . 'foobar'); + 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!'); $fs->mkdir(self::$tmpdir . 'foobar/myfolder'); $fs->mkdir(self::$tmpdir . 'foobar/testfolder'); - file_put_contents(self::$tmpdir . 'foobar/myfolder/mytest.php', "setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.email', 'luke@rebel.org'); $repository->addAll(); @@ -65,7 +74,7 @@ class InterfaceTest extends WebTestCase $nested_dir = self::$tmpdir . 'nested/'; $fs->mkdir($nested_dir); $git->createRepository($nested_dir . 'NestedRepo'); - $repository = $git->getRepository($nested_dir . '/NestedRepo'); + $repository = $git->getRepository($nested_dir . 'NestedRepo'); 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'); $repository->setConfig('user.name', 'Luke Skywalker'); @@ -79,19 +88,43 @@ class InterfaceTest extends WebTestCase $repository->commit("Changing branch"); $repository->checkout("master"); + // master-less repository fixture + $git->createRepository(self::$tmpdir . 'develop'); + $repository = $git->getRepository(self::$tmpdir . 'develop'); + $repository->setConfig('user.name', 'Luke Skywalker'); + $repository->setConfig('user.email', 'luke@rebel.org'); + file_put_contents(self::$tmpdir . 'develop/README.md', "## develop\ndevelop is a *test* repository!"); + $repository->addAll(); + $repository->commit("First commit"); + $repository->createBranch("develop"); + $repository = $repository->checkout('develop'); + + file_put_contents(self::$tmpdir . 'develop/test.php', "setConfig('user.name', 'Luke Skywalker'); + $repository->setConfig('user.email', 'luke@rebel.org'); + $repository->addAll(); + $repository->commit("Initial commit"); + + // Detached HEAD repository fixture + $git->createRepository(self::$tmpdir . 'detached-head'); + $repository = $git->getRepository(self::$tmpdir . 'detached-head'); + $repository->setConfig('user.name', 'Luke Skywalker'); + $repository->setConfig('user.email', 'luke@rebel.org'); + file_put_contents(self::$tmpdir . 'detached-head/README.md', "## detached head\ndetached-head is a *test* repository!"); + $repository->addAll(); + $repository->commit("First commit"); + $repository->checkout('HEAD'); } public function createApplication() { - $config = new \GitList\Config(array( - 'git' => array( - 'client' => self::$gitPath, - 'repositories' => self::$tmpdir, - ), - 'app' => array( - 'debug' => true, - ), - )); + $config = new GitList\Config; + $config->set('app', 'debug', true); + $config->set('app', 'debug', false); + $config->set('git', 'client', self::$gitPath); + $config->set('git', 'default_branch', 'master'); + $config->set('git', 'repositories', array(self::$tmpdir)); + $app = require 'boot.php'; return $app; } @@ -103,15 +136,28 @@ class InterfaceTest extends WebTestCase $this->assertTrue($client->getResponse()->isOk()); $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/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('/nested/NestedRepo/master/rss/', $crawler->filter('.repository-header a')->eq(3)->attr('href')); + + $this->assertCount(1, $crawler->filter('div.repository-header a:contains("detached-head")')); + $this->assertEquals('/detached-head/', $crawler->filter('.repository-header a')->eq(2)->attr('href')); + $this->assertEquals('/detached-head/master/rss/', $crawler->filter('.repository-header a')->eq(3)->attr('href')); + + $this->assertCount(1, $crawler->filter('div.repository-header a:contains("develop")')); + $this->assertEquals('/develop/', $crawler->filter('.repository-header a')->eq(4)->attr('href')); + $this->assertEquals('/develop/master/rss/', $crawler->filter('.repository-header a')->eq(5)->attr('href')); + $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->assertEquals('/foobar/', $crawler->filter('.repository-header a')->eq(4)->attr('href')); - $this->assertEquals('/foobar/master/rss/', $crawler->filter('.repository-header a')->eq(5)->attr('href')); + $this->assertEquals('/foobar/', $crawler->filter('.repository-header a')->eq(6)->attr('href')); + $this->assertEquals('/foobar/master/rss/', $crawler->filter('.repository-header a')->eq(7)->attr('href')); + + $this->assertCount(1, $crawler->filter('div.repository-header a:contains("nested/NestedRepo")')); + $this->assertEquals('/nested/NestedRepo/', $crawler->filter('.repository-header a')->eq(8)->attr('href')); + $this->assertEquals('/nested/NestedRepo/master/rss/', $crawler->filter('.repository-header a')->eq(9)->attr('href')); + $this->assertCount(1, $crawler->filter('div.repository-body:contains("This is a NESTED test repo!")')); } public function testRepositoryPage() @@ -122,8 +168,8 @@ class InterfaceTest extends WebTestCase $this->assertTrue($client->getResponse()->isOk()); $this->assertCount(1, $crawler->filter('.tree tr:contains("README.md")')); $this->assertCount(1, $crawler->filter('.tree tr:contains("test.php")')); - $this->assertCount(1, $crawler->filter('.readme-header:contains("README.md")')); - $this->assertEquals("## GitTest\nGitTest is a *test* repository!", $crawler->filter('#readme-content')->eq(0)->text()); + $this->assertCount(1, $crawler->filter('.md-header:contains("README.md")')); + $this->assertEquals("## GitTest\nGitTest is a *test* repository!", $crawler->filter('#md-content')->eq(0)->text()); $this->assertEquals('/GitTest/blob/master/README.md', $crawler->filter('.tree tr td')->eq(0)->filter('a')->eq(0)->attr('href')); $this->assertEquals('/GitTest/blob/master/test.php', $crawler->filter('.tree tr td')->eq(3)->filter('a')->eq(0)->attr('href')); @@ -140,7 +186,7 @@ class InterfaceTest extends WebTestCase $this->assertEquals('/foobar/tree/master/myfolder/', $crawler->filter('.tree tr td')->eq(0)->filter('a')->eq(0)->attr('href')); $this->assertEquals('/foobar/tree/master/testfolder/', $crawler->filter('.tree tr td')->eq(3)->filter('a')->eq(0)->attr('href')); $this->assertEquals('/foobar/blob/master/bar.json', $crawler->filter('.tree tr td')->eq(6)->filter('a')->eq(0)->attr('href')); - $this->assertCount(0, $crawler->filter('.readme-header')); + $this->assertCount(0, $crawler->filter('.md-header')); $this->assertEquals('master', $crawler->filter('.dropdown-menu li')->eq(1)->text()); } @@ -149,11 +195,15 @@ class InterfaceTest extends WebTestCase $client = $this->createClient(); $crawler = $client->request('GET', '/GitTest/blob/master/test.php'); + $this->assertTrue($client->getResponse()->isOk()); $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/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')); + $this->assertEquals('/GitTest/raw/master/test.php', + $crawler->filter('.source-header .btn-group a')->eq(0)->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() @@ -172,12 +222,14 @@ class InterfaceTest extends WebTestCase $crawler = $client->request('GET', '/GitTest/blame/master/test.php'); $this->assertTrue($client->getResponse()->isOk()); $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'); $this->assertTrue($client->getResponse()->isOk()); $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() @@ -226,8 +278,10 @@ class InterfaceTest extends WebTestCase { $client = $this->createClient(); - $crawler = $client->request('GET', '/GitTest/master/rss/'); - $this->assertTrue($client->getResponse()->isOk()); + $client->request('GET', '/GitTest/master/rss/'); + $response = $client->getResponse(); + + $this->assertTrue($response->isOk()); $this->assertRegexp('/Latest commits in GitTest:master/', $client->getResponse()->getContent()); $this->assertRegexp('/Initial commit/', $client->getResponse()->getContent()); } @@ -236,11 +290,21 @@ class InterfaceTest extends WebTestCase { $client = $this->createClient(); - $crawler = $client->request('GET', '/nested/NestedRepo/'); - $this->assertTrue($client->getResponse()->isOk()); + $client->request('GET', '/nested/NestedRepo/'); + $response = $client->getResponse(); + + $this->assertTrue($response->isOk()); $this->assertRegexp('/NESTED TEST REPO README/', $client->getResponse()->getContent()); } + public function testDevelopRepo() + { + $client = $this->createClient(); + + $crawler = $client->request('GET', '/develop/'); + $this->assertTrue($client->getResponse()->isOk()); + } + public function testNestedRepoBranch() { $client = $this->createClient(); @@ -256,3 +320,4 @@ class InterfaceTest extends WebTestCase $fs->remove(self::$tmpdir); } } + diff --git a/views/blame.twig b/views/blame.twig index 039de5c..d58d918 100644 --- a/views/blame.twig +++ b/views/blame.twig @@ -14,7 +14,7 @@ {% for blame in blames %} - + {% endfor %} diff --git a/views/breadcrumb.twig b/views/breadcrumb.twig index c7e954a..735e8d7 100644 --- a/views/breadcrumb.twig +++ b/views/breadcrumb.twig @@ -1,8 +1,8 @@
{{ blame.commit }}{{ blame.commitShort }}
{{ blame.line }}
@@ -8,17 +9,20 @@ {% for item in commit %} - + {% endfor %}
- View {{ item.shortHash }} + View {{ item.shortHash }}

{{ item.message }}

- {{ item.author.name }} authored in {{ item.date | date('d/m/Y \\a\\t H:i:s') }} + {{ item.author.name }} authored on {{ item.date | date('d/m/Y \\a\\t H:i:s') }}
{% endfor %} +{% else %} +

No results found.

+{% endif %} {% if page != 'searchcommits' %} -{% endif %} \ No newline at end of file +{% endif %} diff --git a/views/file.twig b/views/file.twig index 8b530d2..7801e6e 100644 --- a/views/file.twig +++ b/views/file.twig @@ -12,19 +12,19 @@
{% if fileType == 'image' %} -
{{ file }}
+
{{ file }}
{% elseif fileType == 'markdown' %} -
{{ blob }}
+
{{ blob }}
{% else %} -
{{ blob }}
+
{{ blob|htmlentities|raw }}
{% endif %} diff --git a/views/index.twig b/views/index.twig index 07093cd..e78a35c 100644 --- a/views/index.twig +++ b/views/index.twig @@ -9,11 +9,15 @@ {% for repository in repositories %}
+ {% if repository.description %}

{{ repository.description }}

+ {% else %} +

There is no repository description file. Please, create one to remove this message.

+ {% endif %}
{% endfor %} diff --git a/views/layout.twig b/views/layout.twig index 9addf44..e0e9105 100644 --- a/views/layout.twig +++ b/views/layout.twig @@ -4,6 +4,7 @@ {% block title %}Welcome!{% endblock %} + @@ -12,9 +13,11 @@ {% block body %}{% endblock %} + + diff --git a/views/layout_page.twig b/views/layout_page.twig index 97f51f3..02919b9 100644 --- a/views/layout_page.twig +++ b/views/layout_page.twig @@ -6,8 +6,8 @@
- {% if page == 'commits' %} - {% else %} diff --git a/views/menu.twig b/views/menu.twig index 2a6574d..2b2d9fd 100644 --- a/views/menu.twig +++ b/views/menu.twig @@ -1,5 +1,6 @@ diff --git a/views/navigation.twig b/views/navigation.twig index eef7144..6391812 100644 --- a/views/navigation.twig +++ b/views/navigation.twig @@ -10,6 +10,7 @@