.gitignore 0000664 0000000 0000000 00000000433 15160746067 0013062 0 ustar 00root root 0000000 0000000 cache/
vendor/
build/
*.diff
*.err
*.orig
*.log
*.rej
*.swo
*.swp
*.zip
*.vi
*~
*.sass-cache
.DS_Store
._*
Thumbs.db
.cache
.project
.settings
.tmproj
*.esproj
nbproject
*.sublime-project
*.sublime-workspace
.hg
.svn
.CVS
.idea
node_modules
config.ini
cache.properties
composer.phar
.htaccess 0000664 0000000 0000000 00000000412 15160746067 0012665 0 ustar 00root root 0000000 0000000
Options -MultiViews
RewriteEngine On
#RewriteBase /path/to/gitlist/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,NC]
order allow,deny
deny from all
.travis.yml 0000664 0000000 0000000 00000000234 15160746067 0013202 0 ustar 00root root 0000000 0000000 language: php
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar install --dev
php:
- 5.3
- 5.4
script: phpunit
INSTALL.md 0000664 0000000 0000000 00000004301 15160746067 0012520 0 ustar 00root root 0000000 0000000 # 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.
* 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:
```
cd /var/www/gitlist
mkdir cache
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.
### nginx server.conf
```
server {
server_name MYSERVER;
access_log /var/log/nginx/MYSERVER.access_log main;
error_log /var/log/nginx/MYSERVER.error_log debug_http;
root /var/www/DIR;
index index.php;
# auth_basic "Restricted";
# auth_basic_user_file rhtpasswd;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~* ^/index.php.*$ {
fastcgi_pass 127.0.0.1:9000;
include fastcgi.conf;
}
location / {
try_files $uri @gitlist;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
add_header Vary "Accept-Encoding";
expires max;
try_files $uri @gitlist;
tcp_nodelay off;
tcp_nopush on;
}
# location ~* \.(git|svn|patch|htaccess|log|route|plist|inc|json|pl|po|sh|ini|sample|kdev4)$ {
# deny all;
# }
location @gitlist {
rewrite ^/.*$ /index.php;
}
}
```
### lighttpd
```
# GitList is located in /var/www/gitlist
server.document-root = "/var/www"
url.rewrite-once = (
"^/gitlist/web/.+" => "$0",
"^/gitlist/favicon\.ico$" => "$0",
"^/gitlist(/[^\?]*)(\?.*)?" => "/gitlist/index.php$1$2"
)
```
### 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
}
```
LICENSE.txt 0000664 0000000 0000000 00000002705 15160746067 0012721 0 ustar 00root root 0000000 0000000 Copyright (c) 2012, Klaus Silveira and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of GitList nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md 0000664 0000000 0000000 00000012034 15160746067 0012351 0 ustar 00root root 0000000 0000000 # GitList: an elegant and modern git repository viewer
[](http://travis-ci.org/klaussilveira/gitlist)
GitList is an elegant and modern web interface for interacting with multiple git repositories. It allows you to browse repositories using your favorite browser, viewing files under different revisions, commit history, diffs. It also generates RSS feeds for each repository, allowing you to stay up-to-date with the latest changes anytime, anywhere. GitList was written in PHP, on top of the [Silex](http://silex.sensiolabs.org/) microframework and powered by the Twig template engine. This means that GitList is easy to install and easy to customize. Also, the GitList gorgeous interface was made possible due to [Bootstrap](http://twitter.github.com/bootstrap/).
## Features
* Multiple repository support
* Multiple branch support
* Multiple tag support
* Commit history, blame, diff
* RSS feeds
* Syntax highlighting
* Repository statistics
## Screenshots
[](http://cloud.github.com/downloads/klaussilveira/gitlist/1.jpg)
[](http://cloud.github.com/downloads/klaussilveira/gitlist/2.jpg)
[](http://cloud.github.com/downloads/klaussilveira/gitlist/3.jpg)
[](http://cloud.github.com/downloads/klaussilveira/gitlist/4.jpg)
[](http://cloud.github.com/downloads/klaussilveira/gitlist/5.jpg)
You can also see a live demo [here](http://git.gofedora.com).
## Authors and contributors
* [Klaus Silveira](http://www.klaussilveira.com) (Creator, developer)
## License
[New BSD license](http://www.opensource.org/licenses/bsd-license.php)
## Todo
* improve the current test code coverage
* test the interface
* error handling can be greatly improved during parsing
* submodule support
* multilanguage support
## Requirements
In order to run GitList on your server, you'll need:
* git
* Apache with mod_rewrite enabled or nginx
* PHP 5.3.3
## Installing
* 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:
```
cd /var/www/gitlist
mkdir cache
chmod 777 cache
```
That's it, installation complete! If you're having problems, check this [tutorial](http://gofedora.com/insanely-awesome-web-interface-git-repos/) by Kulbir Saini or the [Troubleshooting](https://github.com/klaussilveira/gitlist/wiki/Troubleshooting) page.
## Building
GitList uses [Composer](http://getcomposer.org/) to manage dependencies and [Ant](http://ant.apache.org/) to build the project. In order to run all the targets in the build script, you will need [PHPUnit](http://www.phpunit.de/), [phpcpd](https://github.com/sebastianbergmann/phpcpd), [phploc](https://github.com/sebastianbergmann/phploc), [PHPMD](http://phpmd.org/) and [PHP_Depend](http://pdepend.org).
Once you have all the dependencies set, you can clone the repository and run Ant:
```
git clone https://github.com/klaussilveira/gitlist.git
ant
```
If you just want to get the project dependencies, instead of building everything:
```
git clone https://github.com/klaussilveira/gitlist.git
curl -s http://getcomposer.org/installer | php
php composer.phar install
```
If you have Composer in your path, things get easier. But you know the drill.
## Contributing
If you are a developer, we need your help. GitList is a young project and we have lot's of stuff to do. Some developers are contributing with new features, others with bug fixes. But you can also dedicate yourself to refactoring the current codebase and improving what we already have. This is very important, we want GitList to be a state-of-the-art application, and we need your help for that.
* Stay tuned to possible bugs, suboptimal code, duplicated code, overcomplicated expressions and unused code with [PHPMD](http://ci.gitlist.org:8080/job/GitList%20\(master\)/9/pmdResult/?) in our CI server
* Try to fix any [violations](http://ci.gitlist.org:8080/job/GitList%20\(master\)/violations/) reported
* Improve the [test coverage](http://ci.gitlist.org:8080/job/GitList%20\(master\)/9/cloverphp-report/) by creating unit and functional tests
## Further information
If you want to know more about customizing GitList, check the [Customization](https://github.com/klaussilveira/gitlist/wiki/Customizing) page on the wiki. Also, if you're having problems with GitList, check the [Troubleshooting](https://github.com/klaussilveira/gitlist/wiki/Troubleshooting) page. Don't forget to report issues and suggest new features! :)
boot.php 0000664 0000000 0000000 00000001073 15160746067 0012547 0 ustar 00root root 0000000 0000000 set('git', 'repositories', rtrim($config->get('git', 'repositories'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR);
// Startup and configure Silex application
$app = new GitList\Application($config, __DIR__);
// Mount the controllers
$app->mount('', new GitList\Controller\MainController());
$app->mount('', new GitList\Controller\BlobController());
$app->mount('', new GitList\Controller\CommitController());
$app->mount('', new GitList\Controller\TreeController());
return $app;
build.xml 0000664 0000000 0000000 00000006477 15160746067 0012731 0 ustar 00root root 0000000 0000000
cache/ 0000775 0000000 0000000 00000000000 15160746067 0012135 5 ustar 00root root 0000000 0000000 cache/.gitkeep 0000664 0000000 0000000 00000000000 15160746067 0013554 0 ustar 00root root 0000000 0000000 composer.json 0000664 0000000 0000000 00000001152 15160746067 0013613 0 ustar 00root root 0000000 0000000 {
"require": {
"silex/silex": "1.0.*",
"twig/twig": "1.9.*",
"symfony/twig-bridge": "2.1.*",
"symfony/filesystem": "2.1.*",
"klaussilveira/gitter": "dev-fix-get-branches"
},
"require-dev": {
"symfony/browser-kit": "2.1.*",
"symfony/css-selector": "2.1.*",
"phpunit/phpunit": "3.7.*"
},
"minimum-stability": "dev",
"autoload": {
"psr-0": {
"GitList": "src/"
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/NateEag/gitter.git"
}
]
}
composer.lock 0000664 0000000 0000000 00000117761 15160746067 0013610 0 ustar 00root root 0000000 0000000 {
"hash": "ed5320fb27429d26c0b32ae24d25bcfc",
"packages": [
{
"name": "klaussilveira/gitter",
"version": "dev-fix-get-branches",
"source": {
"type": "git",
"url": "https://github.com/NateEag/gitter.git",
"reference": "85f7841fdce478efae78cca090abe3488aa2d2e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/NateEag/gitter/zipball/85f7841fdce478efae78cca090abe3488aa2d2e5",
"reference": "85f7841fdce478efae78cca090abe3488aa2d2e5",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/process": ">=2.1"
},
"require-dev": {
"phpunit/phpunit": ">=3.7.1",
"symfony/filesystem": ">=2.1"
},
"time": "2013-02-15 01:30:05",
"type": "library",
"autoload": {
"psr-0": {
"Gitter": "lib/"
}
},
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Klaus Silveira",
"email": "klaussilveira@php.net",
"homepage": "http://www.klaussilveira.com/",
"role": "Developer"
}
],
"description": "Gitter allows you to interact in an object oriented manner with Git repositories.",
"homepage": "https://github.com/klaussilveira/gitter",
"keywords": [
"git",
"vcs"
],
"support": {
"source": "https://github.com/nateeag/gitter/tree/fix-get-branches"
}
},
{
"name": "pimple/pimple",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Pimple.git",
"reference": "5a8936d9d31eb613f1a33753e5f22bffebe0bbc3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/5a8936d9d31eb613f1a33753e5f22bffebe0bbc3",
"reference": "5a8936d9d31eb613f1a33753e5f22bffebe0bbc3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2013-02-09 15:33:26",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
]
},
{
"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": ""
},
"time": "2012-12-21 11:40:51",
"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"
]
},
{
"name": "silex/silex",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Silex.git",
"reference": "c421eab3c4ca9d106cc5b68266d58adece8636cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Silex/zipball/c421eab3c4ca9d106cc5b68266d58adece8636cd",
"reference": "c421eab3c4ca9d106cc5b68266d58adece8636cd",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"pimple/pimple": "1.*",
"symfony/event-dispatcher": ">=2.1,<2.3-dev",
"symfony/http-foundation": ">=2.1,<2.3-dev",
"symfony/http-kernel": ">=2.1,<2.3-dev",
"symfony/routing": ">=2.1,<2.3-dev"
},
"require-dev": {
"doctrine/dbal": ">=2.2.0,<2.4.0-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,<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",
"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"
},
"time": "2013-02-13 11:44:19",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Silex": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
}
],
"description": "The PHP micro-framework based on the Symfony2 Components",
"homepage": "http://silex.sensiolabs.org",
"keywords": [
"microframework"
]
},
{
"name": "symfony/event-dispatcher",
"version": "2.2.x-dev",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher",
"reference": "v2.2.0-RC2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.2.0-RC2",
"reference": "v2.2.0-RC2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": ">=2.0,<3.0"
},
"suggest": {
"symfony/dependency-injection": "2.2.*",
"symfony/http-kernel": "2.2.*"
},
"time": "2013-02-11 11:26:43",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"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 EventDispatcher Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/filesystem",
"version": "2.1.x-dev",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem",
"reference": "v2.1.7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.1.7",
"reference": "v2.1.7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2013-01-09 08:51:07",
"type": "library",
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem": ""
}
},
"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 Filesystem Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/http-foundation",
"version": "2.2.x-dev",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpFoundation",
"reference": "v2.2.0-RC2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.2.0-RC2",
"reference": "v2.2.0-RC2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2013-02-11 12:46:49",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpFoundation\\": ""
},
"classmap": [
"Symfony/Component/HttpFoundation/Resources/stubs"
]
},
"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 HttpFoundation Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/http-kernel",
"version": "2.2.x-dev",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpKernel",
"reference": "cec254f40ea9baaa2b90de7ca6de236de76823eb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/cec254f40ea9baaa2b90de7ca6de236de76823eb",
"reference": "cec254f40ea9baaa2b90de7ca6de236de76823eb",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"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.1,<3.0",
"symfony/config": ">=2.0,<3.0",
"symfony/console": "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.*",
"symfony/class-loader": "2.2.*",
"symfony/config": "2.2.*",
"symfony/console": "2.2.*",
"symfony/dependency-injection": "2.2.*",
"symfony/finder": "2.2.*"
},
"time": "2013-02-13 01:35:51",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpKernel\\": ""
}
},
"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 HttpKernel Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/process",
"version": "dev-master",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process",
"reference": "c99475d555934461f079521d024d88a0d4e861eb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/c99475d555934461f079521d024d88a0d4e861eb",
"reference": "c99475d555934461f079521d024d88a0d4e861eb",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2013-01-31 21:39:01",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
}
},
"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 Process Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/routing",
"version": "2.2.x-dev",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing",
"reference": "v2.2.0-RC2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/v2.2.0-RC2",
"reference": "v2.2.0-RC2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"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",
"symfony/config": "2.2.*",
"symfony/yaml": "2.2.*"
},
"time": "2013-02-11 11:24:47",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Routing\\": ""
}
},
"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 Routing Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/twig-bridge",
"version": "2.1.x-dev",
"target-dir": "Symfony/Bridge/Twig",
"source": {
"type": "git",
"url": "https://github.com/symfony/TwigBridge",
"reference": "v2.1.7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.1.7",
"reference": "v2.1.7",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"twig/twig": ">=1.9.1,<2.0-dev"
},
"require-dev": {
"symfony/form": "2.1.*",
"symfony/routing": "2.1.*",
"symfony/security": "2.1.*",
"symfony/templating": "2.1.*",
"symfony/translation": "2.1.*",
"symfony/yaml": "2.1.*"
},
"suggest": {
"symfony/form": "2.1.*",
"symfony/routing": "2.1.*",
"symfony/security": "2.1.*",
"symfony/templating": "2.1.*",
"symfony/translation": "2.1.*",
"symfony/yaml": "2.1.*"
},
"time": "2013-01-17 15:20:05",
"type": "symfony-bridge",
"autoload": {
"psr-0": {
"Symfony\\Bridge\\Twig": ""
}
},
"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 Twig Bridge",
"homepage": "http://symfony.com"
},
{
"name": "twig/twig",
"version": "v1.9.2",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Twig.git",
"reference": "v1.9.2"
},
"dist": {
"type": "zip",
"url": "https://github.com/fabpot/Twig/zipball/v1.9.2",
"reference": "v1.9.2",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"time": "2012-08-25 10:32:57",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"license": [
"BSD-3"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
}
],
"packages-dev": [
{
"name": "phpunit/php-code-coverage",
"version": "1.2.x-dev",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "25232ac64f081abd7478ae2d63fdb757f45e92b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/25232ac64f081abd7478ae2d63fdb757f45e92b9",
"reference": "25232ac64f081abd7478ae2d63fdb757f45e92b9",
"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"
},
"time": "2013-02-14 10:33:04",
"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"
]
},
{
"name": "phpunit/php-file-iterator",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://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"
},
"time": "2013-01-07 10:47:05",
"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"
]
},
{
"name": "phpunit/php-text-template",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://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"
},
"time": "2013-01-07 10:56:17",
"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"
]
},
{
"name": "phpunit/php-timer",
"version": "1.0.x-dev",
"source": {
"type": "git",
"url": "git://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"
},
"time": "2013-01-30 06:08:51",
"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"
]
},
{
"name": "phpunit/php-token-stream",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://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"
},
"time": "2013-01-07 10:56:35",
"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"
]
},
{
"name": "phpunit/phpunit",
"version": "3.7.x-dev",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/phpunit.git",
"reference": "3.7.14"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.14",
"reference": "3.7.14",
"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.1.0,<2.2.0"
},
"suggest": {
"ext-json": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*",
"phpunit/php-invoker": ">=1.1.0,<1.2.0"
},
"time": "2013-02-14 08:07:17",
"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"
]
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "1.2.x-dev",
"source": {
"type": "git",
"url": "git://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": "*"
},
"time": "2013-02-05 07:46:41",
"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"
]
},
{
"name": "symfony/browser-kit",
"version": "2.1.x-dev",
"target-dir": "Symfony/Component/BrowserKit",
"source": {
"type": "git",
"url": "https://github.com/symfony/BrowserKit",
"reference": "v2.1.7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.1.7",
"reference": "v2.1.7",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/dom-crawler": "2.1.*"
},
"require-dev": {
"symfony/css-selector": "2.1.*",
"symfony/process": "2.1.*"
},
"suggest": {
"symfony/process": "2.1.*"
},
"time": "2013-01-09 08:51:07",
"type": "library",
"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"
},
{
"name": "symfony/css-selector",
"version": "2.1.x-dev",
"target-dir": "Symfony/Component/CssSelector",
"source": {
"type": "git",
"url": "https://github.com/symfony/CssSelector",
"reference": "v2.1.7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.1.7",
"reference": "v2.1.7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2013-01-09 08:51:07",
"type": "library",
"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"
},
{
"name": "symfony/dom-crawler",
"version": "2.1.x-dev",
"target-dir": "Symfony/Component/DomCrawler",
"source": {
"type": "git",
"url": "https://github.com/symfony/DomCrawler",
"reference": "v2.1.7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/DomCrawler/zipball/v2.1.7",
"reference": "v2.1.7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/css-selector": "2.1.*"
},
"suggest": {
"symfony/css-selector": "2.1.*"
},
"time": "2013-01-09 08:51:07",
"type": "library",
"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"
},
{
"name": "symfony/yaml",
"version": "2.1.x-dev",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml",
"reference": "882b70fb7072e3f1fa95d249fd527a4e3998dc1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/882b70fb7072e3f1fa95d249fd527a4e3998dc1a",
"reference": "882b70fb7072e3f1fa95d249fd527a4e3998dc1a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2013-01-27 16:12:43",
"type": "library",
"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"
}
],
"aliases": [
],
"minimum-stability": "dev",
"stability-flags": {
"klaussilveira/gitter": 20
}
}
config.ini-example 0000664 0000000 0000000 00000001125 15160746067 0014470 0 ustar 00root root 0000000 0000000 [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
; You can hide repositories from GitList, just copy this for each repository you want to hide
; hidden[] = '/var/www/projects/BetaTest'
[app]
debug = false
; If you need to specify custom filetypes for certain extensions, do this here
[filetypes]
; extension = type
; dist = xml
; If you need to set file types as binary or not, do this here
[binary_filetypes]
; extension = true
; svh = false
; map = true
index.php 0000664 0000000 0000000 00000000741 15160746067 0012714 0 ustar 00root root 0000000 0000000 run();
phpunit.xml.dist 0000664 0000000 0000000 00000001350 15160746067 0014244 0 ustar 00root root 0000000 0000000
./tests/
src/ 0000775 0000000 0000000 00000000000 15160746067 0011661 5 ustar 00root root 0000000 0000000 src/GitList/ 0000775 0000000 0000000 00000000000 15160746067 0013240 5 ustar 00root root 0000000 0000000 src/GitList/Application.php 0000664 0000000 0000000 00000004760 15160746067 0016223 0 ustar 00root root 0000000 0000000 get('app', 'debug');
$this['filetypes'] = $config->getSection('filetypes');
$this['cache.archives'] = $root . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . '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'),
));
$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.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) {
$twig->addFilter('htmlentities', new \Twig_Filter_Function('htmlentities'));
$twig->addFilter('md5', new \Twig_Filter_Function('md5'));
return $twig;
}));
// Handle errors
$this->error(function (\Exception $e, $code) use ($app) {
if ($app['debug']) {
return;
}
return $app['twig']->render('error.twig', array(
'message' => $e->getMessage(),
));
});
}
}
src/GitList/Config.php 0000664 0000000 0000000 00000002363 15160746067 0015162 0 ustar 00root root 0000000 0000000 data = $data;
$this->validateOptions();
}
public function get($section, $option)
{
if (!array_key_exists($section, $this->data)) {
return false;
}
if (!array_key_exists($option, $this->data[$section])) {
return false;
}
return $this->data[$section][$option];
}
public function getSection($section)
{
if (!array_key_exists($section, $this->data)) {
return false;
}
return $this->data[$section];
}
public function set($section, $option, $value)
{
$this->data[$section][$option] = $value;
}
protected function validateOptions()
{
if (!$this->get('git', 'repositories') || !is_dir($this->get('git', 'repositories'))) {
die("Please, edit the config file and provide your repositories directory");
}
}
}
src/GitList/Controller/ 0000775 0000000 0000000 00000000000 15160746067 0015363 5 ustar 00root root 0000000 0000000 src/GitList/Controller/BlobController.php 0000664 0000000 0000000 00000005762 15160746067 0021030 0 ustar 00root root 0000000 0000000 get('{repo}/blob/{commitish_path}', function($repo, $commitish_path) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitish_path, $repo);
list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file);
$blob = $repository->getBlob("$branch:\"$file\"");
$breadcrumbs = $app['util.view']->getBreadcrumbs($file);
$fileType = $app['util.repository']->getFileType($file);
if ($fileType !== 'image' && $app['util.repository']->isBinary($file)) {
return $app->redirect($app['url_generator']->generate('blob_raw', array(
'repo' => $repo,
'branch' => $branch,
'file' => $file,
)));
}
return $app['twig']->render('file.twig', array(
'file' => $file,
'fileType' => $fileType,
'blob' => $blob->output(),
'repo' => $repo,
'branch' => $branch,
'breadcrumbs' => $breadcrumbs,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('commitish_path', '.+')
->bind('blob');
$route->get('{repo}/raw/{commitish_path}', function($repo, $commitish_path) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitish_path, $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';
} else {
$headers['Content-Transfer-Encoding'] = 'text/plain';
}
return new Response($blob, 200, $headers);
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('commitish_path', $app['util.routing']->getCommitishPathRegex())
->bind('blob_raw');
return $route;
}
}
src/GitList/Controller/CommitController.php 0000664 0000000 0000000 00000011534 15160746067 0021374 0 ustar 00root root 0000000 0000000 get('{repo}/commits/{commitish_path}', function($repo, $commitish_path) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
if ($commitish_path === null) {
$commitish_path = $repository->getHead();
}
list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($commitish_path, $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();
$date = $date->format('m/d/Y');
$categorized[$date][] = $commit;
}
$template = $app['request']->isXmlHttpRequest() ? 'commits_list.twig' : 'commits.twig';
return $app['twig']->render($template, array(
'page' => 'commits',
'pager' => $pager,
'repo' => $repo,
'branch' => $branch,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
'commits' => $categorized,
'file' => $file,
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('commitish_path', $app['util.routing']->getCommitishPathRegex())
->value('commitish_path', null)
->bind('commits');
$route->post('{repo}/commits/{branch}/search', function(Request $request, $repo, $branch = '') use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
$query = $request->get('query');
$commits = $repository->searchCommitLog($query);
$categorized = array();
foreach ($commits as $commit) {
$date = $commit->getDate();
$date = $date->format('m/d/Y');
$categorized[$date][] = $commit;
}
return $app['twig']->render('searchcommits.twig', array(
'repo' => $repo,
'branch' => $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);
$commit = $repository->getCommit($commit);
$branch = $repository->getHead();
return $app['twig']->render('commit.twig', array(
'branch' => $branch,
'repo' => $repo,
'commit' => $commit,
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->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);
list($branch, $file) = $app['util.routing']
->parseCommitishPathParam($branch_file, $repo);
list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file);
$blames = $repository->getBlame("$branch -- \"$file\"");
return $app['twig']->render('blame.twig', array(
'file' => $file,
'repo' => $repo,
'branch' => $branch,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
'blames' => $blames,
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch_file', $app['util.routing']->getCommitishPathRegex())
->bind('blame');
return $route;
}
}
src/GitList/Controller/MainController.php 0000664 0000000 0000000 00000005364 15160746067 0021034 0 ustar 00root root 0000000 0000000 get('/', function() use ($app) {
$repositories = array_map(
function ($repo) use ($app) {
$repo['relativePath'] = $app['util.routing']->getRelativePath($repo['path']);
$repository = $app['git']->getRepository($repo['path']);
$repo['branch'] = $repository->getHead();
return $repo;
},
$app['git']->getRepositories($app['git.repos'])
);
return $app['twig']->render('index.twig', array(
'repositories' => $repositories,
));
})->bind('homepage');
$route->get('{repo}/stats/{branch}', function($repo, $branch) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
if ($branch === null) {
$branch = $repository->getHead();
}
$stats = $repository->getStatistics($branch);
$authors = $repository->getAuthorStatistics();
return $app['twig']->render('stats.twig', array(
'repo' => $repo,
'branch' => $branch,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
'stats' => $stats,
'authors' => $authors,
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->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);
if ($branch === null) {
$branch = $repository->getHead();
}
$commits = $repository->getPaginatedCommits($branch);
$html = $app['twig']->render('rss.twig', array(
'repo' => $repo,
'branch' => $branch,
'commits' => $commits,
));
return new Response($html, 200, array('Content-Type' => 'application/rss+xml'));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch', $app['util.routing']->getBranchRegex())
->value('branch', null)
->bind('rss');
return $route;
}
}
src/GitList/Controller/TreeController.php 0000664 0000000 0000000 00000011734 15160746067 0021045 0 ustar 00root root 0000000 0000000 get('{repo}/tree/{commitish_path}/', $treeController = function($repo, $commitish_path = '') use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
if (!$commitish_path) {
$commitish_path = $repository->getHead();
}
list($branch, $tree) = $app['util.routing']->parseCommitishPathParam($commitish_path, $repo);
list($branch, $tree) = $app['util.repository']->extractRef($repository, $branch, $tree);
$files = $repository->getTree($tree ? "$branch:\"$tree\"/" : $branch);
$breadcrumbs = $app['util.view']->getBreadcrumbs($tree);
$parent = null;
if (($slash = strrpos($tree, '/')) !== false) {
$parent = substr($tree, 0, $slash);
} elseif (!empty($tree)) {
$parent = '';
}
return $app['twig']->render('tree.twig', array(
'files' => $files->output(),
'repo' => $repo,
'branch' => $branch,
'path' => $tree ? $tree . '/' : $tree,
'parent' => $parent,
'breadcrumbs' => $breadcrumbs,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
'readme' => $app['util.repository']->getReadme($repo, $branch),
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('commitish_path', $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);
if (!$branch) {
$branch = $repository->getHead();
}
$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,
'repo' => $repo,
'branch' => $branch,
'path' => $tree,
'breadcrumbs' => $breadcrumbs,
'branches' => $repository->getBranches(),
'tags' => $repository->getTags(),
));
})->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch', '[\w-._\/]+')
->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');
$route->get('{repo}/{format}ball/{branch}', function($repo, $format, $branch) use ($app) {
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
$tree = $repository->getBranchTree($branch);
if (false === $tree) {
return $app->abort(404, 'Invalid commit or tree reference: ' . $branch);
}
$file = $app['cache.archives'] . DIRECTORY_SEPARATOR
. $repo . DIRECTORY_SEPARATOR
. substr($tree, 0, 2) . DIRECTORY_SEPARATOR
. substr($tree, 2)
. '.'
. $format;
if (!file_exists($file)) {
$repository->createArchive($tree, $file, $format);
}
return new StreamedResponse(function () use ($file) {
readfile($file);
}, 200, array(
'Content-type' => ('zip' === $format) ? 'application/zip' : 'application/x-tar',
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename="'.$repo.'-'.substr($tree, 0, 6).'.'.$format.'"',
'Content-Transfer-Encoding' => 'binary',
));
})->assert('format', '(zip|tar)')
->assert('repo', $app['util.routing']->getRepositoryRegex())
->assert('branch', '[\w-._\/]+')
->bind('archive');
return $route;
}
}
src/GitList/Exception/ 0000775 0000000 0000000 00000000000 15160746067 0015176 5 ustar 00root root 0000000 0000000 src/GitList/Exception/BlankDataException.php 0000664 0000000 0000000 00000000133 15160746067 0021404 0 ustar 00root root 0000000 0000000 setDefaultBranch($options['default_branch']);
}
/**
* 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->default_branch = $branch;
return $this;
}
/**
* Return name of default branch as a string.
*/
public function getDefaultBranch()
{
return $this->default_branch;
}
/**
* Creates a new repository on the specified path
*
* @param string $path Path where the new repository will be created
* @return Repository Instance of Repository
*/
public function createRepository($path, $bare = null)
{
if (file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) {
throw new \RuntimeException('A GIT repository already exists at ' . $path);
}
$repository = new Repository($path, $this);
return $repository->create($bare);
}
/**
* Opens a repository at the specified path
*
* @param string $path Path where the repository is located
* @return Repository Instance of Repository
*/
public function getRepository($path)
{
if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) {
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);
}
}
src/GitList/Git/Repository.php 0000664 0000000 0000000 00000025230 15160746067 0016655 0 ustar 00root root 0000000 0000000 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()
{
$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");
$logs = explode("\n", $logs);
// Read commit metadata
$format = new PrettyFormat;
$data = $format->parse($logs[0]);
$commit = new Commit;
$commit->importData($data[0]);
if ($commit->getParentsHash()) {
$command = 'diff ' . $commitHash . '~1..' . $commitHash;
$logs = explode("\n", $this->getClient()->run($this, $command));
} else {
$logs = array_slice($logs, 1);
}
$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) {
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
*
* @access public
* @return array Commit log
*/
public function getPaginatedCommits($file = null, $page = 0)
{
$page = 15 * $page;
$pager = "--skip=$page --max-count=15";
$command = "log $pager --pretty=format:\"- %H%h%T%P%an%ae%at%cn%ce%ct
\"";
if ($file) {
$command .= " $file";
}
try {
$logs = $this->getPrettyFormat($command);
} catch (\RuntimeException $e) {
return array();
}
foreach ($logs as $log) {
$commit = new Commit;
$commit->importData($log);
$commits[] = $commit;
}
return $commits;
}
public function searchCommitLog($query)
{
$query = escapeshellarg($query);
$command = "log --grep={$query} --pretty=format:\"- %H%h%T%P%an%ae%at%cn%ce%ct
\"";
try {
$logs = $this->getPrettyFormat($command);
} catch (\RuntimeException $e) {
return array();
}
foreach ($logs as $log) {
$commit = new Commit;
$commit->importData($log);
$commits[] = $commit;
}
return $commits;
}
public function searchTree($query, $branch)
{
$query = escapeshellarg($query);
try {
$results = $this->getClient()->run($this, "grep -I --line-number {$query} $branch");
} catch (\RuntimeException $e) {
return false;
}
$results = explode("\n", $results);
foreach ($results as $result) {
if ($result == '') {
continue;
}
preg_match_all('/([\w-._]+):([^:]+):([0-9]+):(.+)/', $result, $matches, PREG_SET_ORDER);
$data['branch'] = $matches[0][1];
$data['file'] = $matches[0][2];
$data['line'] = $matches[0][3];
$data['match'] = $matches[0][4];
$searchResults[] = $data;
}
return $searchResults;
}
public function getAuthorStatistics()
{
$logs = $this->getClient()->run($this, 'log --pretty=format:"%an||%ae" ' . $this->getHead());
if (empty($logs)) {
throw new \RuntimeException('No statistics available');
}
$logs = explode("\n", $logs);
$logs = array_count_values($logs);
arsort($logs);
foreach ($logs as $user => $count) {
$user = explode('||', $user);
$data[] = array('name' => $user[0], 'email' => $user[1], 'commits' => $count);
}
return $data;
}
public function getStatistics($branch)
{
// Calculate amount of files, extensions and file size
$logs = $this->getClient()->run($this, 'ls-tree -r -l ' . $branch);
$lines = explode("\n", $logs);
$files = array();
$data['extensions'] = array();
$data['size'] = 0;
$data['files'] = 0;
foreach ($lines as $key => $line) {
if (empty($line)) {
unset($lines[$key]);
continue;
}
$files[] = preg_split("/[\s]+/", $line);
}
foreach ($files as $file) {
if ($file[1] == 'blob') {
$data['files']++;
}
if (is_numeric($file[3])) {
$data['size'] += $file[3];
}
if (($pos = strrpos($file[4], '.')) !== FALSE) {
$data['extensions'][] = substr($file[4], $pos);
}
}
$data['extensions'] = array_count_values($data['extensions']);
arsort($data['extensions']);
return $data;
}
/**
* Create a TAR or ZIP archive of a git tree
*
* @param string $tree Tree-ish reference
* @param string $output Output File name
* @param string $format Archive format
*/
public function createArchive($tree, $output, $format = 'zip')
{
$fs = new Filesystem;
$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;
}
}
src/GitList/Provider/ 0000775 0000000 0000000 00000000000 15160746067 0015032 5 ustar 00root root 0000000 0000000 src/GitList/Provider/GitServiceProvider.php 0000664 0000000 0000000 00000001376 15160746067 0021331 0 ustar 00root root 0000000 0000000 share(function () use ($app) {
return new Repository($app);
});
}
public function boot(Application $app)
{
}
}
src/GitList/Provider/RoutingUtilServiceProvider.php 0000664 0000000 0000000 00000001101 15160746067 0023055 0 ustar 00root root 0000000 0000000 share(function () use ($app) {
return new Routing($app);
});
}
public function boot(Application $app)
{
}
}
src/GitList/Provider/ViewUtilServiceProvider.php 0000664 0000000 0000000 00000001043 15160746067 0022345 0 ustar 00root root 0000000 0000000 share(function () {
return new View;
});
}
public function boot(Application $app)
{
}
}
src/GitList/Util/ 0000775 0000000 0000000 00000000000 15160746067 0014155 5 ustar 00root root 0000000 0000000 src/GitList/Util/Repository.php 0000664 0000000 0000000 00000015173 15160746067 0017054 0 ustar 00root root 0000000 0000000 'php',
'c' => 'clike',
'h' => 'clike',
'cpp' => 'clike',
'm' => 'clike',
'mm' => 'clike',
'ino' => 'clike',
'cs' => 'text/x-csharp',
'java' => 'text/x-java',
'clj' => 'clojure',
'coffee' => 'coffeescript',
'css' => 'css',
'diff' => 'diff',
'ecl' => 'ecl',
'el' => 'erlang',
'go' => 'go',
'groovy' => 'groovy',
'hs' => 'haskell',
'lhs' => 'haskell',
'jsp' => 'htmlembedded',
'asp' => 'htmlembedded',
'aspx' => 'htmlembedded',
'html' => 'htmlmixed',
'tpl' => 'htmlmixed',
'js' => 'javascript',
'json' => 'javascript',
'less' => 'less',
'lua' => 'lua',
'md' => 'markdown',
'markdown' => 'markdown',
'sql' => 'mysql',
'pl' => 'perl',
'pm' => 'perl',
'pas' => 'pascal',
'ini' => 'properties',
'cfg' => 'properties',
'nt' => 'ntriples',
'py' => 'python',
'rb' => 'ruby',
'rst' => 'rst',
'r' => 'r',
'sh' => 'shell',
'ss' => 'scheme',
'scala' => 'text/x-scala',
'scm' => 'scheme',
'sls' => 'scheme',
'sps' => 'scheme',
'rs' => 'rust',
'st' => 'smalltalk',
'tex' => 'stex',
'vbs' => 'vbscript',
'v' => 'verilog',
'xml' => 'xml',
'xsd' => 'xml',
'xsl' => 'xml',
'xul' => 'xml',
'xlf' => 'xml',
'xliff' => 'xml',
'xaml' => 'xml',
'wxs' => 'xml',
'wxl' => 'xml',
'wxi' => 'xml',
'wsdl' => 'xml',
'svg' => 'xml',
'rss' => 'xml',
'rdf' => 'xml',
'plist' => 'xml',
'mxml' => 'xml',
'kml' => 'xml',
'glade' => 'xml',
'xq' => 'xquery',
'xqm' => 'xquery',
'xquery' => 'xquery',
'xqy' => 'xquery',
'yml' => 'yaml',
'yaml' => 'yaml',
'png' => 'image',
'jpg' => 'image',
'gif' => 'image',
'jpeg' => 'image',
'bmp' => 'image',
'csproj' => 'xml',
);
protected static $binaryTypes = array(
'exe', 'com', 'so', 'la', 'o', 'dll', 'pyc',
'jpg', 'jpeg', 'bmp', 'gif', 'png', 'xmp', 'pcx', 'svgz', 'ttf', 'tiff', 'oet',
'gz', 'tar', 'rar', 'zip', '7z', 'jar', 'class',
'odt', 'ods', 'pdf', 'doc', 'docx', 'dot', 'xls', 'xlsx',
);
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Returns the file type based on filename by treating the extension
*
* The file type is used by CodeMirror, a Javascript-based IDE implemented in
* GitList, to properly highlight the blob syntax (if it's a source-code)
*
* @param string $file File name
* @return mixed File type
*/
public function getFileType($file)
{
if (($pos = strrpos($file, '.')) !== false) {
$fileType = substr($file, $pos + 1);
} else {
return 'text';
}
if (isset($this->defaultFileTypes[$fileType])) {
return $this->defaultFileTypes[$fileType];
}
if (!empty($this->app['filetypes'])) {
if (isset($this->app['filetypes'][$fileType])) {
return $this->app['filetypes'][$fileType];
}
}
return 'text';
}
/**
* Returns whether the file is binary.
*
* @param string $file
*
* @return boolean
*/
public function isBinary($file)
{
if (($pos = strrpos($file, '.')) !== false) {
$fileType = substr($file, $pos + 1);
} else {
return false;
}
if (in_array($fileType, self::$binaryTypes)) {
return true;
}
if (!empty($this->app['binary_filetypes']) && array_key_exists($fileType, $this->app['binary_filetypes'])) {
return $this->app['binary_filetypes'][$fileType];
}
return false;
}
public function getReadme($repo, $branch = null)
{
$repository = $this->app['git']->getRepository($this->app['git.repos'] . $repo);
if ($branch === null) {
$branch = $repository->getHead();
}
$files = $repository->getTree($branch)->output();
foreach ($files as $file) {
if (preg_match('/^readme*/i', $file['name'])) {
return array(
'filename' => $file['name'],
'content' => $repository->getBlob("$branch:\"{$file['name']}\"")->output()
);
}
}
return array();
}
/**
* Returns an Array where the first value is the tree-ish and the second is the path
*
* @param \GitList\Git\Repository $repository
* @param string $branch
* @param string $tree
* @return array
*/
public function extractRef($repository, $branch='', $tree='')
{
$branch = trim($branch, '/');
$tree = trim($tree, '/');
$input = $branch . '/' . $tree;
// If the ref appears to be a SHA, just split the string
if (preg_match("/^([[:alnum:]]{40})(.+)/", $input, $matches)) {
$branch = $matches[1];
} else {
// Otherwise, attempt to detect the ref using a list of the project's branches and tags
$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($validRefs) > 1) {
preg_match('/([^\/]+)(.*)/', $input, $matches);
$branch = preg_replace('/^\/|\/$/', '', $matches[1]);
} else {
// Extract branch name
$branch = array_shift($validRefs);
}
}
return array($branch, $tree);
}
}
src/GitList/Util/Routing.php 0000664 0000000 0000000 00000010310 15160746067 0016310 0 ustar 00root root 0000000 0000000 app = $app;
}
/* @brief Return $commitish, $path parsed from $commitish_path, 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($commitish_path, $repo) {
$app = $this->app;
$repository = $app['git']->getRepository($app['git.repos'] . $repo);
$commitish = null;
$path = null;
$slash_pos = strpos($commitish_path, '/');
if (strlen($commitish_path) >= 40 &&
($slash_pos === FALSE ||
$slash_pos === 40)) {
// We may have a commit hash as our commitish.
$hash = substr($commitish_path, 0, 40);
if ($repository->hasCommit($hash)) {
$commitish = $hash;
}
}
if ($commitish === null) {
// DEBUG Can you have a repo with no branches? How should we handle
// that?
$branches = $repository->getBranches();
$tags = $repository->getTags();
if ($tags !== null && count($tags) > 0) {
$branches = array_merge($branches, $tags);
}
$matched_branch = null;
$matched_branch_name_len = 0;
foreach ($branches as $branch) {
if (strpos($commitish_path, $branch) === 0 &&
strlen($branch) > $matched_branch_name_len) {
$matched_branch = $branch;
$matched_branch_name_len = strlen($matched_branch);
}
}
$commitish = $matched_branch;
}
if ($commitish === null) {
$app->abort(404, "'$branch_path' does not appear to contain a " .
"commit-ish for '$repo.'");
}
$commitish_len = strlen($commitish);
$path = substr($commitish_path, $commitish_len);
if (strpos($path, '/') === 0) {
$path = substr($path, 1);
}
$commit_has_path = $repository->pathExists($commitish, $path);
if ($commit_has_path !== TRUE) {
$app->abort(404, "\"$path\" does not exist in \"$commitish\".");
}
return array($commitish, $path);
}
public function getBranchRegex() {
static $branch_regex = null;
if ($branch_regex === null) {
$branch_regex = '[\w-._\/]+';
}
return $branch_regex;
}
public function getCommitishPathRegex() {
static $commitish_path_regex = null;
if ($commitish_path_regex === null) {
$commitish_path_regex = '.+';
}
return $commitish_path_regex;
}
public function getRepositoryRegex()
{
static $regex = null;
if ($regex === null) {
$app = $this->app;
$quotedPaths = array_map(
function ($repo) use ($app) {
return preg_quote($app['util.routing']->getRelativePath($repo['path']), '#');
},
$this->app['git']->getRepositories($this->app['git.repos'])
);
usort($quotedPaths, function ($a, $b) { return strlen($b) - strlen($a); });
$regex = implode('|', $quotedPaths);
}
return $regex;
}
/**
* Strips the base path from a full repository path
*
* @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(strtr($relativePath, '\\', '/'), '/');
} else {
throw new \InvalidArgumentException(
sprintf("Path '%s' does not match configured repository directory", $repoPath)
);
}
}
}
src/GitList/Util/View.php 0000664 0000000 0000000 00000002407 15160746067 0015603 0 ustar 00root root 0000000 0000000 $path) {
$breadcrumbs[] = array(
'dir' => $path,
'path' => implode('/', array_slice($paths, 0, $i + 1)),
);
}
return $breadcrumbs;
}
public function getPager($pageNumber, $totalCommits)
{
$pageNumber = (empty($pageNumber)) ? 0 : $pageNumber;
$lastPage = intval($totalCommits / 15);
// If total commits are integral multiple of 15, the lastPage will be commits/15 - 1.
$lastPage = ($lastPage * 15 == $totalCommits) ? $lastPage - 1 : $lastPage;
$nextPage = $pageNumber + 1;
$previousPage = $pageNumber - 1;
return array('current' => $pageNumber,
'next' => $nextPage,
'previous' => $previousPage,
'last' => $lastPage,
'total' => $totalCommits,
);
}
}
tests/ 0000775 0000000 0000000 00000000000 15160746067 0012234 5 ustar 00root root 0000000 0000000 tests/InterfaceTest.php 0000664 0000000 0000000 00000033530 15160746067 0015511 0 ustar 00root root 0000000 0000000 mkdir(self::$tmpdir);
if (!is_writable(self::$tmpdir)) {
$this->markTestSkipped('There are no write permissions in order to create test repositories.');
}
$options['path'] = getenv('GIT_CLIENT') ?: '/usr/bin/git';
$options['hidden'] = array(self::$tmpdir . '/hiddenrepo');
$git = new Client($options);
self::$gitPath = $options['path'];
// GitTest repository fixture
$git->createRepository(self::$tmpdir . 'GitTest');
$repository = $git->getRepository(self::$tmpdir . 'GitTest');
file_put_contents(self::$tmpdir . 'GitTest/README.md', "## GitTest\nGitTest is a *test* repository!");
file_put_contents(self::$tmpdir . 'GitTest/test.php', "setConfig('user.name', 'Luke Skywalker');
$repository->setConfig('user.email', 'luke@rebel.org');
$repository->addAll();
$repository->commit("Initial commit");
$repository->createBranch('issue12');
$repository->createBranch('issue42');
$repository->createBranch('branch/name/wiith/slashes');
// foobar repository fixture
$git->createRepository(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();
$repository->commit("First commit");
// Nested repository fixture
$nested_dir = self::$tmpdir . 'nested/';
$fs->mkdir($nested_dir);
$git->createRepository($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');
$repository->setConfig('user.email', 'luke@rebel.org');
$repository->addAll();
$repository->commit("First commit");
$repository->createBranch("testing");
$repository->checkout("testing");
file_put_contents($nested_dir . 'NestedRepo/README.txt', 'NESTED TEST BRANCH README');
$repository->addAll();
$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,
),
));
$app = require 'boot.php';
return $app;
}
public function testInitialPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/');
$this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('title:contains("GitList")'));
$this->assertCount(1, $crawler->filter('div.repository-header: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: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(8)->attr('href'));
$this->assertEquals('/foobar/master/rss/', $crawler->filter('.repository-header a')->eq(9)->attr('href'));
}
public function testRepositoryPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/');
$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->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'));
$this->assertEquals('branch/name/wiith/slashes', $crawler->filter('.dropdown-menu li')->eq(1)->text());
$this->assertEquals('issue12', $crawler->filter('.dropdown-menu li')->eq(2)->text());
$this->assertEquals('issue42', $crawler->filter('.dropdown-menu li')->eq(3)->text());
$this->assertEquals('master', $crawler->filter('.dropdown-menu li')->eq(4)->text());
$crawler = $client->request('GET', '/foobar/');
$this->assertTrue($client->getResponse()->isOk());
$this->assertCount(1, $crawler->filter('.tree tr:contains("myfolder")'));
$this->assertCount(1, $crawler->filter('.tree tr:contains("testfolder")'));
$this->assertCount(1, $crawler->filter('.tree tr:contains("bar.json")'));
$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->assertEquals('master', $crawler->filter('.dropdown-menu li')->eq(1)->text());
}
public function testBlobPage()
{
$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'));
}
public function testRawPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/raw/master/test.php');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals("getResponse()->getContent());
}
public function testBlamePage()
{
$client = $this->createClient();
$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'));
$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'));
}
public function testHistoryPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/commits/master/test.php');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals('Initial commit', $crawler->filter('.table tbody tr td h4')->eq(0)->text());
$crawler = $client->request('GET', '/GitTest/commits/master/README.md');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals('Initial commit', $crawler->filter('.table tbody tr td h4')->eq(0)->text());
$crawler = $client->request('GET', '/foobar/commits/master/bar.json');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals('First commit', $crawler->filter('.table tbody tr td h4')->eq(0)->text());
}
public function testCommitsPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/commits');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals('Initial commit', $crawler->filter('.table tbody tr td h4')->eq(0)->text());
$crawler = $client->request('GET', '/foobar/commits');
$this->assertTrue($client->getResponse()->isOk());
$this->assertEquals('First commit', $crawler->filter('.table tbody tr td h4')->eq(0)->text());
}
public function testStatsPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/stats');
$this->assertTrue($client->getResponse()->isOk());
$this->assertRegexp('/.php: 1 files/', $crawler->filter('.table tbody')->eq(0)->text());
$this->assertRegexp('/.md: 1 files/', $crawler->filter('.table tbody')->eq(0)->text());
$this->assertRegexp('/Total files: 2/', $crawler->filter('.table tbody')->eq(0)->text());
$this->assertRegexp('/Luke Skywalker: 1 commits/', $crawler->filter('.table tbody')->eq(0)->text());
}
public function testRssPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/GitTest/master/rss/');
$this->assertTrue($client->getResponse()->isOk());
$this->assertRegexp('/Latest commits in GitTest:master/', $client->getResponse()->getContent());
$this->assertRegexp('/Initial commit/', $client->getResponse()->getContent());
}
public function testNestedRepoPage()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/nested/NestedRepo/');
$this->assertTrue($client->getResponse()->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();
$crawler = $client->request('GET', '/nested/NestedRepo/testing/');
$this->assertTrue($client->getResponse()->isOk());
$this->assertRegexp('/NESTED TEST BRANCH README/', $client->getResponse()->getContent());
}
public static function tearDownAfterClass()
{
$fs = new Filesystem();
$fs->remove(self::$tmpdir);
}
}
views/ 0000775 0000000 0000000 00000000000 15160746067 0012227 5 ustar 00root root 0000000 0000000 views/blame.twig 0000664 0000000 0000000 00000001275 15160746067 0014210 0 ustar 00root root 0000000 0000000 {% extends 'layout_page.twig' %}
{% set page = 'commits' %}
{% block title %}GitList{% endblock %}
{% block content %}
{% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Blame', path:''}]} %}
{% endblock %}
views/branch_menu.twig 0000664 0000000 0000000 00000001165 15160746067 0015407 0 ustar 00root root 0000000 0000000