1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,28 @@ |
1 |
+cache/ |
|
2 |
+*.diff |
|
3 |
+*.err |
|
4 |
+*.orig |
|
5 |
+*.log |
|
6 |
+*.rej |
|
7 |
+*.swo |
|
8 |
+*.swp |
|
9 |
+*.zip |
|
10 |
+*.vi |
|
11 |
+*~ |
|
12 |
+*.sass-cache |
|
13 |
+.DS_Store |
|
14 |
+._* |
|
15 |
+Thumbs.db |
|
16 |
+.cache |
|
17 |
+.project |
|
18 |
+.settings |
|
19 |
+.tmproj |
|
20 |
+*.esproj |
|
21 |
+nbproject |
|
22 |
+*.sublime-project |
|
23 |
+*.sublime-workspace |
|
24 |
+.hg |
|
25 |
+.svn |
|
26 |
+.CVS |
|
27 |
+.idea |
|
28 |
+node_modules |
0 | 6 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+Copyright (c) 2012, Klaus Silveira and contributors |
|
2 |
+All rights reserved. |
|
3 |
+ |
|
4 |
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
|
5 |
+ |
|
6 |
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
|
7 |
+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. |
|
8 |
+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. |
|
9 |
+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. |
0 | 10 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,53 @@ |
1 |
+# GitList: an elegant and modern git repository viewer |
|
2 |
+[](http://travis-ci.org/klaussilveira/GitList) |
|
3 |
+ |
|
4 |
+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/). |
|
5 |
+ |
|
6 |
+## Features |
|
7 |
+* Multiple repository support |
|
8 |
+* Multiple branch support |
|
9 |
+* Multiple tag support |
|
10 |
+* Commit history, blame, diff |
|
11 |
+* RSS feeds |
|
12 |
+* Syntax highlighting |
|
13 |
+* Repository statistics |
|
14 |
+ |
|
15 |
+## Authors and contributors |
|
16 |
+* [Klaus Silveira](http://www.klaussilveira.com) (Creator, developer) |
|
17 |
+ |
|
18 |
+## License |
|
19 |
+[New BSD license](http://www.opensource.org/licenses/bsd-license.php) |
|
20 |
+ |
|
21 |
+## Todo |
|
22 |
+* improve the current test code coverage |
|
23 |
+* test the interface |
|
24 |
+* error handling can be greatly improved during parsing |
|
25 |
+* submodule support |
|
26 |
+* multilanguage support |
|
27 |
+ |
|
28 |
+## Requirements |
|
29 |
+In order to run GitList on your server, you'll need: |
|
30 |
+ |
|
31 |
+* git |
|
32 |
+* Apache and mod_rewrite enabled |
|
33 |
+* PHP 5.3.3 |
|
34 |
+ |
|
35 |
+## Installing |
|
36 |
+Download the GitList latest package and decompress to your `/var/www/gitlist` folder, or anywhere else you want to place GitList. You can also clone the repository: |
|
37 |
+ |
|
38 |
+``` |
|
39 |
+git clone https://github.com/klaussilveira/gitlist.git /var/www/gitlist |
|
40 |
+``` |
|
41 |
+ |
|
42 |
+Now 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). Now, let's create the cache folder and give the correct permissions: |
|
43 |
+ |
|
44 |
+``` |
|
45 |
+cd /var/www/gitlist |
|
46 |
+mkdir cache |
|
47 |
+chmod 777 cache |
|
48 |
+``` |
|
49 |
+ |
|
50 |
+That's it, installation complete! |
|
51 |
+ |
|
52 |
+## Further information |
|
53 |
+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/Customizing) page. Don't forget to report issues and suggest new features! :) |
0 | 7 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,32 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('{repo}/blob/{branch}/{file}/', function($repo, $branch, $file) use($app) { |
|
4 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
5 |
+ $blob = $repository->getBlob("$branch:$file"); |
|
6 |
+ $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/tree/$branch/$file"); |
|
7 |
+ $fileType = $app['utils']->getFileType($file); |
|
8 |
+ |
|
9 |
+ return $app['twig']->render('file.twig', array( |
|
10 |
+ 'baseurl' => $app['baseurl'], |
|
11 |
+ 'page' => 'files', |
|
12 |
+ 'file' => $file, |
|
13 |
+ 'fileType' => $fileType, |
|
14 |
+ 'blob' => $blob->output(), |
|
15 |
+ 'repo' => $repo, |
|
16 |
+ 'branch' => $branch, |
|
17 |
+ 'breadcrumbs' => $breadcrumbs, |
|
18 |
+ 'branches' => $repository->getBranches(), |
|
19 |
+ 'tags' => $repository->getTags(), |
|
20 |
+ )); |
|
21 |
+})->assert('file', '.+') |
|
22 |
+ ->assert('repo', '[\w-._]+') |
|
23 |
+ ->assert('branch', '[\w-._]+'); |
|
24 |
+ |
|
25 |
+$app->get('{repo}/raw/{branch}/{file}', function($repo, $branch, $file) use($app) { |
|
26 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
27 |
+ $blob = $repository->getBlob("$branch:$file")->output(); |
|
28 |
+ |
|
29 |
+ return new Symfony\Component\HttpFoundation\Response($blob, 200, array('Content-Type' => 'text/plain')); |
|
30 |
+})->assert('file', '.+') |
|
31 |
+ ->assert('repo', '[\w-._]+') |
|
32 |
+ ->assert('branch', '[\w-._]+'); |
|
0 | 33 |
\ No newline at end of file |
1 | 34 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,79 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('{repo}/commits/{branch}', function($repo, $branch) use($app) { |
|
4 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
5 |
+ $commits = $repository->getCommits(); |
|
6 |
+ |
|
7 |
+ foreach ($commits as $commit) { |
|
8 |
+ $date = $commit->getDate(); |
|
9 |
+ $date = $date->format('m/d/Y'); |
|
10 |
+ $categorized[$date][] = $commit; |
|
11 |
+ } |
|
12 |
+ |
|
13 |
+ return $app['twig']->render('commits.twig', array( |
|
14 |
+ 'baseurl' => $app['baseurl'], |
|
15 |
+ 'page' => 'commits', |
|
16 |
+ 'repo' => $repo, |
|
17 |
+ 'branch' => $branch, |
|
18 |
+ 'branches' => $repository->getBranches(), |
|
19 |
+ 'tags' => $repository->getTags(), |
|
20 |
+ 'commits' => $categorized, |
|
21 |
+ )); |
|
22 |
+})->assert('repo', '[\w-._]+') |
|
23 |
+ ->assert('branch', '[\w-._]+') |
|
24 |
+ ->value('branch', 'master'); |
|
25 |
+ |
|
26 |
+$app->get('{repo}/commits/{branch}/{file}/', function($repo, $branch, $file) use($app) { |
|
27 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
28 |
+ $commits = $repository->getCommits($file); |
|
29 |
+ |
|
30 |
+ foreach ($commits as $commit) { |
|
31 |
+ $date = $commit->getDate(); |
|
32 |
+ $date = $date->format('m/d/Y'); |
|
33 |
+ $categorized[$date][] = $commit; |
|
34 |
+ } |
|
35 |
+ |
|
36 |
+ return $app['twig']->render('commits.twig', array( |
|
37 |
+ 'baseurl' => $app['baseurl'], |
|
38 |
+ 'page' => 'commits', |
|
39 |
+ 'repo' => $repo, |
|
40 |
+ 'branch' => $branch, |
|
41 |
+ 'branches' => $repository->getBranches(), |
|
42 |
+ 'tags' => $repository->getTags(), |
|
43 |
+ 'commits' => $categorized, |
|
44 |
+ )); |
|
45 |
+})->assert('repo', '[\w-._]+') |
|
46 |
+ ->assert('file', '.+') |
|
47 |
+ ->assert('branch', '[\w-._]+'); |
|
48 |
+ |
|
49 |
+$app->get('{repo}/commit/{commit}/', function($repo, $commit) use($app) { |
|
50 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
51 |
+ $commit = $repository->getCommit($commit); |
|
52 |
+ |
|
53 |
+ return $app['twig']->render('commit.twig', array( |
|
54 |
+ 'baseurl' => $app['baseurl'], |
|
55 |
+ 'page' => 'commits', |
|
56 |
+ 'branch' => 'master', |
|
57 |
+ 'repo' => $repo, |
|
58 |
+ 'commit' => $commit, |
|
59 |
+ )); |
|
60 |
+})->assert('repo', '[\w-._]+') |
|
61 |
+ ->assert('commit', '[a-f0-9]+'); |
|
62 |
+ |
|
63 |
+$app->get('{repo}/blame/{branch}/{file}/', function($repo, $branch, $file) use($app) { |
|
64 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
65 |
+ $blames = $repository->getBlame($file); |
|
66 |
+ |
|
67 |
+ return $app['twig']->render('blame.twig', array( |
|
68 |
+ 'baseurl' => $app['baseurl'], |
|
69 |
+ 'page' => 'commits', |
|
70 |
+ 'file' => $file, |
|
71 |
+ 'repo' => $repo, |
|
72 |
+ 'branch' => $branch, |
|
73 |
+ 'branches' => $repository->getBranches(), |
|
74 |
+ 'tags' => $repository->getTags(), |
|
75 |
+ 'blames' => $blames, |
|
76 |
+ )); |
|
77 |
+})->assert('repo', '[\w-._]+') |
|
78 |
+ ->assert('file', '.+') |
|
79 |
+ ->assert('branch', '[\w-._]+'); |
0 | 80 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,10 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('/', function() use($app) { |
|
4 |
+ $repositories = $app['git']->getRepositories($app['git.repos']); |
|
5 |
+ |
|
6 |
+ return $app['twig']->render('index.twig', array( |
|
7 |
+ 'baseurl' => $app['baseurl'], |
|
8 |
+ 'repositories' => $repositories, |
|
9 |
+ )); |
|
10 |
+}); |
|
0 | 11 |
\ No newline at end of file |
1 | 12 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,16 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('{repo}/{branch}/rss/', function($repo, $branch) use($app) { |
|
4 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
5 |
+ $commits = $repository->getCommits(); |
|
6 |
+ |
|
7 |
+ $html = $app['twig']->render('rss.twig', array( |
|
8 |
+ 'baseurl' => $app['baseurl'], |
|
9 |
+ 'repo' => $repo, |
|
10 |
+ 'branch' => $branch, |
|
11 |
+ 'commits' => $commits, |
|
12 |
+ )); |
|
13 |
+ |
|
14 |
+ return new Symfony\Component\HttpFoundation\Response($html, 200, array('Content-Type' => 'application/rss+xml')); |
|
15 |
+})->assert('repo', '[\w-._]+') |
|
16 |
+ ->assert('branch', '[\w-._]+'); |
|
0 | 17 |
\ No newline at end of file |
1 | 18 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,20 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('{repo}/stats/{branch}', function($repo, $branch) use($app) { |
|
4 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
5 |
+ $stats = $repository->getStatistics($branch); |
|
6 |
+ $authors = $repository->getAuthorStatistics(); |
|
7 |
+ |
|
8 |
+ return $app['twig']->render('stats.twig', array( |
|
9 |
+ 'baseurl' => $app['baseurl'], |
|
10 |
+ 'page' => 'stats', |
|
11 |
+ 'repo' => $repo, |
|
12 |
+ 'branch' => $branch, |
|
13 |
+ 'branches' => $repository->getBranches(), |
|
14 |
+ 'tags' => $repository->getTags(), |
|
15 |
+ 'stats' => $stats, |
|
16 |
+ 'authors' => $authors, |
|
17 |
+ )); |
|
18 |
+})->assert('repo', '[\w-._]+') |
|
19 |
+ ->assert('branch', '[\w-._]+') |
|
20 |
+ ->value('branch', 'master'); |
|
0 | 21 |
\ No newline at end of file |
1 | 22 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,67 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$app->get('{repo}/', function($repo) use($app) { |
|
4 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
5 |
+ $tree = $repository->getTree('master'); |
|
6 |
+ $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/"); |
|
7 |
+ |
|
8 |
+ return $app['twig']->render('tree.twig', array( |
|
9 |
+ 'baseurl' => $app['baseurl'], |
|
10 |
+ 'page' => 'files', |
|
11 |
+ 'files' => $tree->output(), |
|
12 |
+ 'repo' => $repo, |
|
13 |
+ 'branch' => 'master', |
|
14 |
+ 'path' => '', |
|
15 |
+ 'parent' => '', |
|
16 |
+ 'breadcrumbs' => $breadcrumbs, |
|
17 |
+ 'branches' => $repository->getBranches(), |
|
18 |
+ 'tags' => $repository->getTags(), |
|
19 |
+ )); |
|
20 |
+})->assert('repo', '[\w-._]+'); |
|
21 |
+ |
|
22 |
+$app->get('{repo}/tree/{branch}/', function($repo, $branch) use($app) { |
|
23 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
24 |
+ $tree = $repository->getTree($branch); |
|
25 |
+ $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/"); |
|
26 |
+ |
|
27 |
+ return $app['twig']->render('tree.twig', array( |
|
28 |
+ 'baseurl' => $app['baseurl'], |
|
29 |
+ 'page' => 'files', |
|
30 |
+ 'files' => $tree->output(), |
|
31 |
+ 'repo' => $repo, |
|
32 |
+ 'branch' => $branch, |
|
33 |
+ 'path' => '', |
|
34 |
+ 'parent' => '', |
|
35 |
+ 'breadcrumbs' => $breadcrumbs, |
|
36 |
+ 'branches' => $repository->getBranches(), |
|
37 |
+ 'tags' => $repository->getTags(), |
|
38 |
+ )); |
|
39 |
+})->assert('repo', '[\w-._]+') |
|
40 |
+ ->assert('branch', '[\w-._]+'); |
|
41 |
+ |
|
42 |
+$app->get('{repo}/tree/{branch}/{tree}/', function($repo, $branch, $tree) use($app) { |
|
43 |
+ $repository = $app['git']->getRepository($app['git.repos'] . $repo); |
|
44 |
+ $files = $repository->getTree("$branch:$tree/"); |
|
45 |
+ $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/tree/$branch/$tree"); |
|
46 |
+ |
|
47 |
+ if (($slash = strrpos($tree, '/')) !== false) { |
|
48 |
+ $parent = '/' . substr($tree, 0, $slash); |
|
49 |
+ } else { |
|
50 |
+ $parent = '/'; |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ return $app['twig']->render('tree.twig', array( |
|
54 |
+ 'baseurl' => $app['baseurl'], |
|
55 |
+ 'page' => 'files', |
|
56 |
+ 'files' => $files->output(), |
|
57 |
+ 'repo' => $repo, |
|
58 |
+ 'branch' => $branch, |
|
59 |
+ 'path' => "$tree/", |
|
60 |
+ 'parent' => $parent, |
|
61 |
+ 'breadcrumbs' => $breadcrumbs, |
|
62 |
+ 'branches' => $repository->getBranches(), |
|
63 |
+ 'tags' => $repository->getTags(), |
|
64 |
+ )); |
|
65 |
+})->assert('tree', '.+') |
|
66 |
+ ->assert('repo', '[\w-._]+') |
|
67 |
+ ->assert('branch', '[\w-._]+'); |
|
0 | 68 |
\ No newline at end of file |
1 | 69 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,52 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+/** |
|
4 |
+ * GitList 0.1 |
|
5 |
+ * https://github.com/klaussilveira/gitlist |
|
6 |
+ */ |
|
7 |
+ |
|
8 |
+$config = parse_ini_file('config.ini', true); |
|
9 |
+ |
|
10 |
+if (empty($config['git']['repositories'])) { |
|
11 |
+ die("Please, edit the config.ini file and provide your repositories directory"); |
|
12 |
+} |
|
13 |
+ |
|
14 |
+require_once __DIR__.'/vendor/silex.phar'; |
|
15 |
+ |
|
16 |
+$app = new Silex\Application(); |
|
17 |
+$app['baseurl'] = $config['app']['baseurl']; |
|
18 |
+ |
|
19 |
+// Register Git and Twig libraries |
|
20 |
+$app['autoloader']->registerNamespace('Git', __DIR__.'/lib'); |
|
21 |
+$app['autoloader']->registerNamespace('Application', __DIR__.'/lib'); |
|
22 |
+$app->register(new Silex\Provider\TwigServiceProvider(), array( |
|
23 |
+ 'twig.path' => __DIR__.'/views', |
|
24 |
+ 'twig.class_path' => __DIR__.'/vendor', |
|
25 |
+ 'twig.options' => array('cache' => __DIR__.'/cache'), |
|
26 |
+)); |
|
27 |
+$app->register(new Git\GitServiceProvider(), array( |
|
28 |
+ 'git.client' => $config['git']['client'], |
|
29 |
+ 'git.repos' => $config['git']['repositories'], |
|
30 |
+)); |
|
31 |
+$app->register(new Application\UtilsServiceProvider()); |
|
32 |
+ |
|
33 |
+// Add the md5() function to Twig scope |
|
34 |
+$app['twig']->addFilter('md5', new Twig_Filter_Function('md5')); |
|
35 |
+ |
|
36 |
+// Load controllers |
|
37 |
+include 'controllers/indexController.php'; |
|
38 |
+include 'controllers/treeController.php'; |
|
39 |
+include 'controllers/blobController.php'; |
|
40 |
+include 'controllers/commitController.php'; |
|
41 |
+include 'controllers/statsController.php'; |
|
42 |
+include 'controllers/rssController.php'; |
|
43 |
+ |
|
44 |
+// Error handling |
|
45 |
+$app->error(function (\Exception $e, $code) use ($app) { |
|
46 |
+ return $app['twig']->render('error.twig', array( |
|
47 |
+ 'baseurl' => $app['baseurl'], |
|
48 |
+ 'message' => $e->getMessage(), |
|
49 |
+ )); |
|
50 |
+}); |
|
51 |
+ |
|
52 |
+$app->run(); |
0 | 53 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,206 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Application; |
|
4 |
+ |
|
5 |
+/** |
|
6 |
+ * General helper class, mostly used for string parsing inside the application controllers |
|
7 |
+ */ |
|
8 |
+class Utils |
|
9 |
+{ |
|
10 |
+ /** |
|
11 |
+ * Builds a breadcrumb array based on a path spec |
|
12 |
+ * |
|
13 |
+ * @param string $spec Path spec |
|
14 |
+ * @return array Array with parts of the breadcrumb |
|
15 |
+ */ |
|
16 |
+ public function getBreadcrumbs($spec) |
|
17 |
+ { |
|
18 |
+ $paths = explode('/', $spec); |
|
19 |
+ $last = ''; |
|
20 |
+ |
|
21 |
+ foreach ($paths as $path) { |
|
22 |
+ $dir['dir'] = $path; |
|
23 |
+ $dir['path'] = "$last/$path"; |
|
24 |
+ $breadcrumbs[] = $dir; |
|
25 |
+ $last .= '/' . $path; |
|
26 |
+ } |
|
27 |
+ |
|
28 |
+ if (isset($paths[2])) { |
|
29 |
+ $breadcrumbs[0]['path'] .= '/' . $paths[1] . '/' . $paths[2]; |
|
30 |
+ } |
|
31 |
+ |
|
32 |
+ unset($breadcrumbs[1], $breadcrumbs[2]); |
|
33 |
+ return $breadcrumbs; |
|
34 |
+ } |
|
35 |
+ |
|
36 |
+ /** |
|
37 |
+ * Returns the file type based on filename by treating the extension |
|
38 |
+ * |
|
39 |
+ * The file type is used by CodeMirror, a Javascript-based IDE implemented in |
|
40 |
+ * GitList, to properly highlight the blob syntax (if it's a source-code) |
|
41 |
+ * |
|
42 |
+ * @param string $spec File name |
|
43 |
+ * @return string File type |
|
44 |
+ */ |
|
45 |
+ public function getFileType($file) |
|
46 |
+ { |
|
47 |
+ if (($pos = strrpos($file, '.')) !== FALSE) { |
|
48 |
+ $fileType = substr($file, $pos + 1); |
|
49 |
+ } else { |
|
50 |
+ return 'text'; |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ switch ($fileType) { |
|
54 |
+ case 'php': |
|
55 |
+ return 'php'; |
|
56 |
+ case 'c': |
|
57 |
+ return 'clike'; |
|
58 |
+ case 'h': |
|
59 |
+ return 'clike'; |
|
60 |
+ case 'cpp': |
|
61 |
+ return 'clike'; |
|
62 |
+ case 'cs': |
|
63 |
+ return 'clike'; |
|
64 |
+ case 'm': |
|
65 |
+ return 'clike'; |
|
66 |
+ case 'mm': |
|
67 |
+ return 'clike'; |
|
68 |
+ case 'java': |
|
69 |
+ return 'clike'; |
|
70 |
+ case 'clj': |
|
71 |
+ return 'clojure'; |
|
72 |
+ case 'coffee': |
|
73 |
+ return 'coffeescript'; |
|
74 |
+ case 'css': |
|
75 |
+ return 'css'; |
|
76 |
+ case 'diff': |
|
77 |
+ return 'diff'; |
|
78 |
+ case 'ecl': |
|
79 |
+ return 'ecl'; |
|
80 |
+ case 'el': |
|
81 |
+ return 'erlang'; |
|
82 |
+ case 'go': |
|
83 |
+ return 'go'; |
|
84 |
+ case 'groovy': |
|
85 |
+ return 'groovy'; |
|
86 |
+ case 'hs': |
|
87 |
+ return 'haskell'; |
|
88 |
+ case 'lhs': |
|
89 |
+ return 'haskell'; |
|
90 |
+ case 'jsp': |
|
91 |
+ return 'htmlembedded'; |
|
92 |
+ case 'asp': |
|
93 |
+ return 'htmlembedded'; |
|
94 |
+ case 'aspx': |
|
95 |
+ return 'htmlembedded'; |
|
96 |
+ case 'html': |
|
97 |
+ return 'htmlmixed'; |
|
98 |
+ case 'tpl': |
|
99 |
+ return 'htmlmixed'; |
|
100 |
+ case 'js': |
|
101 |
+ return 'javascript'; |
|
102 |
+ case 'json': |
|
103 |
+ return 'javascript'; |
|
104 |
+ case 'less': |
|
105 |
+ return 'less'; |
|
106 |
+ case 'lua': |
|
107 |
+ return 'lua'; |
|
108 |
+ case 'md': |
|
109 |
+ return 'markdown'; |
|
110 |
+ case 'markdown': |
|
111 |
+ return 'markdown'; |
|
112 |
+ case 'sql': |
|
113 |
+ return 'mysql'; |
|
114 |
+ case 'pl': |
|
115 |
+ return 'perl'; |
|
116 |
+ case 'pm': |
|
117 |
+ return 'perl'; |
|
118 |
+ case 'pas': |
|
119 |
+ return 'pascal'; |
|
120 |
+ case 'ini': |
|
121 |
+ return 'properties'; |
|
122 |
+ case 'cfg': |
|
123 |
+ return 'properties'; |
|
124 |
+ case 'nt': |
|
125 |
+ return 'ntriples'; |
|
126 |
+ case 'py': |
|
127 |
+ return 'python'; |
|
128 |
+ case 'rb': |
|
129 |
+ return 'ruby'; |
|
130 |
+ case 'rst': |
|
131 |
+ return 'rst'; |
|
132 |
+ case 'r': |
|
133 |
+ return 'r'; |
|
134 |
+ case 'sh': |
|
135 |
+ return 'shell'; |
|
136 |
+ case 'ss': |
|
137 |
+ return 'scheme'; |
|
138 |
+ case 'scm': |
|
139 |
+ return 'scheme'; |
|
140 |
+ case 'sls': |
|
141 |
+ return 'scheme'; |
|
142 |
+ case 'sps': |
|
143 |
+ return 'scheme'; |
|
144 |
+ case 'rs': |
|
145 |
+ return 'rust'; |
|
146 |
+ case 'st': |
|
147 |
+ return 'smalltalk'; |
|
148 |
+ case 'tex': |
|
149 |
+ return 'stex'; |
|
150 |
+ case 'vbs': |
|
151 |
+ return 'vbscript'; |
|
152 |
+ case 'v': |
|
153 |
+ return 'verilog'; |
|
154 |
+ case 'xml': |
|
155 |
+ return 'xml'; |
|
156 |
+ case 'xsd': |
|
157 |
+ return 'xml'; |
|
158 |
+ case 'xsl': |
|
159 |
+ return 'xml'; |
|
160 |
+ case 'xul': |
|
161 |
+ return 'xml'; |
|
162 |
+ case 'xlf': |
|
163 |
+ return 'xml'; |
|
164 |
+ case 'xliff': |
|
165 |
+ return 'xml'; |
|
166 |
+ case 'xaml': |
|
167 |
+ return 'xml'; |
|
168 |
+ case 'wxs': |
|
169 |
+ return 'xml'; |
|
170 |
+ case 'wxl': |
|
171 |
+ return 'xml'; |
|
172 |
+ case 'wxi': |
|
173 |
+ return 'xml'; |
|
174 |
+ case 'wsdl': |
|
175 |
+ return 'xml'; |
|
176 |
+ case 'svg': |
|
177 |
+ return 'xml'; |
|
178 |
+ case 'rss': |
|
179 |
+ return 'xml'; |
|
180 |
+ case 'rdf': |
|
181 |
+ return 'xml'; |
|
182 |
+ case 'plist': |
|
183 |
+ return 'xml'; |
|
184 |
+ case 'mxml': |
|
185 |
+ return 'xml'; |
|
186 |
+ case 'kml': |
|
187 |
+ return 'xml'; |
|
188 |
+ case 'glade': |
|
189 |
+ return 'xml'; |
|
190 |
+ case 'xq': |
|
191 |
+ return 'xquery'; |
|
192 |
+ case 'xqm': |
|
193 |
+ return 'xquery'; |
|
194 |
+ case 'xquery': |
|
195 |
+ return 'xquery'; |
|
196 |
+ case 'xqy': |
|
197 |
+ return 'xquery'; |
|
198 |
+ case 'yml': |
|
199 |
+ return 'yaml'; |
|
200 |
+ case 'yaml': |
|
201 |
+ return 'yaml'; |
|
202 |
+ default: |
|
203 |
+ return 'text'; |
|
204 |
+ } |
|
205 |
+ } |
|
206 |
+} |
|
0 | 207 |
\ No newline at end of file |
1 | 208 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,22 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Application; |
|
4 |
+ |
|
5 |
+use Silex\Application; |
|
6 |
+use Silex\ServiceProviderInterface; |
|
7 |
+ |
|
8 |
+class UtilsServiceProvider implements ServiceProviderInterface |
|
9 |
+{ |
|
10 |
+ /** |
|
11 |
+ * Register the Utils class on the Application ServiceProvider |
|
12 |
+ * |
|
13 |
+ * @param Application $app Silex Application |
|
14 |
+ * @return Utils Instance of the Utils class |
|
15 |
+ */ |
|
16 |
+ public function register(Application $app) |
|
17 |
+ { |
|
18 |
+ $app['utils'] = function () use ($app) { |
|
19 |
+ return new Utils; |
|
20 |
+ }; |
|
21 |
+ } |
|
22 |
+} |
|
0 | 23 |
\ No newline at end of file |
1 | 24 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,122 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git; |
|
4 |
+ |
|
5 |
+class Client |
|
6 |
+{ |
|
7 |
+ protected $path; |
|
8 |
+ |
|
9 |
+ public function __construct($path) |
|
10 |
+ { |
|
11 |
+ $this->setPath($path); |
|
12 |
+ } |
|
13 |
+ |
|
14 |
+ /** |
|
15 |
+ * Creates a new repository on the specified path |
|
16 |
+ * |
|
17 |
+ * @param string $path Path where the new repository will be created |
|
18 |
+ * @return Repository Instance of Repository |
|
19 |
+ */ |
|
20 |
+ public function createRepository($path) |
|
21 |
+ { |
|
22 |
+ if (file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { |
|
23 |
+ throw new \RuntimeException('A GIT repository already exists at ' . $path); |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ $repository = new Repository($path, $this); |
|
27 |
+ return $repository->create(); |
|
28 |
+ } |
|
29 |
+ |
|
30 |
+ /** |
|
31 |
+ * Opens a repository at the specified path |
|
32 |
+ * |
|
33 |
+ * @param string $path Path where the repository is located |
|
34 |
+ * @return Repository Instance of Repository |
|
35 |
+ */ |
|
36 |
+ public function getRepository($path) |
|
37 |
+ { |
|
38 |
+ if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { |
|
39 |
+ throw new \RuntimeException('There is no GIT repository at ' . $path); |
|
40 |
+ } |
|
41 |
+ |
|
42 |
+ return new Repository($path, $this); |
|
43 |
+ } |
|
44 |
+ |
|
45 |
+ /** |
|
46 |
+ * Searches for valid repositories on the specified path |
|
47 |
+ * |
|
48 |
+ * @param string $path Path where repositories will be searched |
|
49 |
+ * @return array Found repositories, containing their name, path and description |
|
50 |
+ */ |
|
51 |
+ public function getRepositories($path) |
|
52 |
+ { |
|
53 |
+ $dir = new \DirectoryIterator($path); |
|
54 |
+ |
|
55 |
+ foreach ($dir as $file) { |
|
56 |
+ $isBare = file_exists($file->getPathname() . '/HEAD'); |
|
57 |
+ $isRepository = file_exists($file->getPathname() . '/.git/HEAD'); |
|
58 |
+ |
|
59 |
+ if ($file->isDir() && !$file->isDot() && $isRepository || $isBare) { |
|
60 |
+ if ($isBare) { |
|
61 |
+ $description = file_get_contents($file->getPathname() . '/description'); |
|
62 |
+ } else { |
|
63 |
+ $description = file_get_contents($file->getPathname() . '/.git/description'); |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ $repositories[] = array('name' => $file->getFilename(), 'path' => $file->getPathname(), 'description' => $description); |
|
67 |
+ } |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ if (!isset($repositories)) { |
|
71 |
+ throw new \RuntimeException('There are no GIT repositories in ' . $path); |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ sort($repositories); |
|
75 |
+ |
|
76 |
+ return $repositories; |
|
77 |
+ } |
|
78 |
+ |
|
79 |
+ /** |
|
80 |
+ * Execute a git command on the repository being manipulated |
|
81 |
+ * |
|
82 |
+ * This method will start a new process on the current machine and |
|
83 |
+ * run git commands. Once the command has been run, the method will |
|
84 |
+ * return the command line output. |
|
85 |
+ * |
|
86 |
+ * @param Repository $repository Repository where the command will be run |
|
87 |
+ * @param string $command Git command to be run |
|
88 |
+ * @return string Returns the command output |
|
89 |
+ */ |
|
90 |
+ public function run(Repository $repository, $command) |
|
91 |
+ { |
|
92 |
+ $descriptors = array(0 => array("pipe", "r"), 1 => array("pipe", "w")); |
|
93 |
+ $process = proc_open($this->getPath() . ' ' . $command, $descriptors, $pipes, $repository->getPath()); |
|
94 |
+ |
|
95 |
+ if (is_resource($process)) { |
|
96 |
+ $stdout = stream_get_contents($pipes[1]); |
|
97 |
+ fclose($pipes[1]); |
|
98 |
+ proc_close($process); |
|
99 |
+ return $stdout; |
|
100 |
+ } |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ /** |
|
104 |
+ * Get the current Git binary path |
|
105 |
+ * |
|
106 |
+ * @return string Path where the Git binary is located |
|
107 |
+ */ |
|
108 |
+ protected function getPath() |
|
109 |
+ { |
|
110 |
+ return $this->path; |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ /** |
|
114 |
+ * Set the current Git binary path |
|
115 |
+ * |
|
116 |
+ * @param string $path Path where the Git binary is located |
|
117 |
+ */ |
|
118 |
+ protected function setPath($path) |
|
119 |
+ { |
|
120 |
+ $this->path = $path; |
|
121 |
+ } |
|
122 |
+} |
0 | 123 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,35 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Commit; |
|
4 |
+ |
|
5 |
+class Author |
|
6 |
+{ |
|
7 |
+ protected $name; |
|
8 |
+ protected $email; |
|
9 |
+ |
|
10 |
+ public function __construct($name, $email) |
|
11 |
+ { |
|
12 |
+ $this->setName($name); |
|
13 |
+ $this->setEmail($email); |
|
14 |
+ } |
|
15 |
+ |
|
16 |
+ public function getName() |
|
17 |
+ { |
|
18 |
+ return $this->name; |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ public function setName($name) |
|
22 |
+ { |
|
23 |
+ $this->name = $name; |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ public function getEmail() |
|
27 |
+ { |
|
28 |
+ return $this->email; |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ public function setEmail($email) |
|
32 |
+ { |
|
33 |
+ $this->email = $email; |
|
34 |
+ } |
|
35 |
+} |
|
0 | 36 |
\ No newline at end of file |
1 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,148 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Commit; |
|
4 |
+ |
|
5 |
+class Commit |
|
6 |
+{ |
|
7 |
+ protected $hash; |
|
8 |
+ protected $shortHash; |
|
9 |
+ protected $treeHash; |
|
10 |
+ protected $parentHash; |
|
11 |
+ protected $author; |
|
12 |
+ protected $date; |
|
13 |
+ protected $commiter; |
|
14 |
+ protected $commiterDate; |
|
15 |
+ protected $message; |
|
16 |
+ protected $diffs; |
|
17 |
+ |
|
18 |
+ public function importData(array $data) |
|
19 |
+ { |
|
20 |
+ $this->setHash($data['hash']); |
|
21 |
+ $this->setShortHash($data['short_hash']); |
|
22 |
+ $this->setTreeHash($data['tree']); |
|
23 |
+ $this->setParentHash($data['parent']); |
|
24 |
+ |
|
25 |
+ $this->setAuthor( |
|
26 |
+ new Author($data['author'], $data['author_email']) |
|
27 |
+ ); |
|
28 |
+ |
|
29 |
+ $this->setDate( |
|
30 |
+ new \DateTime('@' . $data['date']) |
|
31 |
+ ); |
|
32 |
+ |
|
33 |
+ $this->setCommiter( |
|
34 |
+ new Author($data['commiter'], $data['commiter_email']) |
|
35 |
+ ); |
|
36 |
+ |
|
37 |
+ $this->setCommiterDate( |
|
38 |
+ new \DateTime('@' . $data['commiter_date']) |
|
39 |
+ ); |
|
40 |
+ |
|
41 |
+ $this->setMessage($data['message']); |
|
42 |
+ } |
|
43 |
+ |
|
44 |
+ public function getHash() |
|
45 |
+ { |
|
46 |
+ return $this->hash; |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ public function setHash($hash) |
|
50 |
+ { |
|
51 |
+ $this->hash = $hash; |
|
52 |
+ } |
|
53 |
+ |
|
54 |
+ public function getShortHash() |
|
55 |
+ { |
|
56 |
+ return $this->shortHash; |
|
57 |
+ } |
|
58 |
+ |
|
59 |
+ public function setShortHash($shortHash) |
|
60 |
+ { |
|
61 |
+ $this->shortHash = $shortHash; |
|
62 |
+ } |
|
63 |
+ |
|
64 |
+ public function getTreeHash() |
|
65 |
+ { |
|
66 |
+ return $this->treeHash; |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ public function setTreeHash($treeHash) |
|
70 |
+ { |
|
71 |
+ $this->treeHash = $treeHash; |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ public function getParentHash() |
|
75 |
+ { |
|
76 |
+ return $this->parentHash; |
|
77 |
+ } |
|
78 |
+ |
|
79 |
+ public function setParentHash($parentHash) |
|
80 |
+ { |
|
81 |
+ $this->parentHash = $parentHash; |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ public function getAuthor() |
|
85 |
+ { |
|
86 |
+ return $this->author; |
|
87 |
+ } |
|
88 |
+ |
|
89 |
+ public function setAuthor($author) |
|
90 |
+ { |
|
91 |
+ $this->author = $author; |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ public function getDate() |
|
95 |
+ { |
|
96 |
+ return $this->date; |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ public function setDate($date) |
|
100 |
+ { |
|
101 |
+ $this->date = $date; |
|
102 |
+ } |
|
103 |
+ |
|
104 |
+ public function getCommiter() |
|
105 |
+ { |
|
106 |
+ return $this->commiter; |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ public function setCommiter($commiter) |
|
110 |
+ { |
|
111 |
+ $this->commiter = $commiter; |
|
112 |
+ } |
|
113 |
+ |
|
114 |
+ public function getCommiterDate() |
|
115 |
+ { |
|
116 |
+ return $this->commiterDate; |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ public function setCommiterDate($commiterDate) |
|
120 |
+ { |
|
121 |
+ $this->commiterDate = $commiterDate; |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ public function getMessage() |
|
125 |
+ { |
|
126 |
+ return $this->message; |
|
127 |
+ } |
|
128 |
+ |
|
129 |
+ public function setMessage($message) |
|
130 |
+ { |
|
131 |
+ $this->message = $message; |
|
132 |
+ } |
|
133 |
+ |
|
134 |
+ public function getDiffs() |
|
135 |
+ { |
|
136 |
+ return $this->diffs; |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ public function setDiffs($diffs) |
|
140 |
+ { |
|
141 |
+ $this->diffs = $diffs; |
|
142 |
+ } |
|
143 |
+ |
|
144 |
+ public function getChangedFiles() |
|
145 |
+ { |
|
146 |
+ return sizeof($this->diffs); |
|
147 |
+ } |
|
148 |
+} |
|
0 | 149 |
\ No newline at end of file |
1 | 150 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,23 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git; |
|
4 |
+ |
|
5 |
+use Silex\Application; |
|
6 |
+use Silex\ServiceProviderInterface; |
|
7 |
+ |
|
8 |
+class GitServiceProvider implements ServiceProviderInterface |
|
9 |
+{ |
|
10 |
+ /** |
|
11 |
+ * Register the Git\Client on the Application ServiceProvider |
|
12 |
+ * |
|
13 |
+ * @param Application $app Silex Application |
|
14 |
+ * @return Git\Client Instance of the Git\Client |
|
15 |
+ */ |
|
16 |
+ public function register(Application $app) |
|
17 |
+ { |
|
18 |
+ $app['git'] = function () use ($app) { |
|
19 |
+ $default = $app['git.client'] ? $app['git.client'] : '/usr/bin/git'; |
|
20 |
+ return new Client($app['git.client']); |
|
21 |
+ }; |
|
22 |
+ } |
|
23 |
+} |
|
0 | 24 |
\ No newline at end of file |
1 | 25 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,67 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Model; |
|
4 |
+ |
|
5 |
+use Git\Client; |
|
6 |
+use Git\Repository; |
|
7 |
+use Git\ScopeAware; |
|
8 |
+ |
|
9 |
+class Blob extends ScopeAware |
|
10 |
+{ |
|
11 |
+ protected $mode; |
|
12 |
+ protected $hash; |
|
13 |
+ protected $name; |
|
14 |
+ protected $size; |
|
15 |
+ |
|
16 |
+ public function __construct($hash, Client $client, Repository $repository) { |
|
17 |
+ $this->setClient($client); |
|
18 |
+ $this->setRepository($repository); |
|
19 |
+ $this->setHash($hash); |
|
20 |
+ } |
|
21 |
+ |
|
22 |
+ public function output() |
|
23 |
+ { |
|
24 |
+ $data = $this->getClient()->run($this->getRepository(), 'show ' . $this->getHash()); |
|
25 |
+ return $data; |
|
26 |
+ } |
|
27 |
+ |
|
28 |
+ public function getMode() |
|
29 |
+ { |
|
30 |
+ return $this->mode; |
|
31 |
+ } |
|
32 |
+ |
|
33 |
+ public function setMode($mode) |
|
34 |
+ { |
|
35 |
+ $this->mode = $mode; |
|
36 |
+ } |
|
37 |
+ |
|
38 |
+ public function getHash() |
|
39 |
+ { |
|
40 |
+ return $this->hash; |
|
41 |
+ } |
|
42 |
+ |
|
43 |
+ public function setHash($hash) |
|
44 |
+ { |
|
45 |
+ $this->hash = $hash; |
|
46 |
+ } |
|
47 |
+ |
|
48 |
+ public function getName() |
|
49 |
+ { |
|
50 |
+ return $this->name; |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ public function setName($name) |
|
54 |
+ { |
|
55 |
+ $this->name = $name; |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ public function getSize() |
|
59 |
+ { |
|
60 |
+ return $this->size; |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ public function setSize($size) |
|
64 |
+ { |
|
65 |
+ $this->size = $size; |
|
66 |
+ } |
|
67 |
+} |
|
0 | 68 |
\ No newline at end of file |
1 | 69 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,60 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Model; |
|
4 |
+ |
|
5 |
+use Git\Model\Line; |
|
6 |
+ |
|
7 |
+class Diff |
|
8 |
+{ |
|
9 |
+ protected $lines; |
|
10 |
+ protected $index; |
|
11 |
+ protected $old; |
|
12 |
+ protected $new; |
|
13 |
+ protected $file; |
|
14 |
+ |
|
15 |
+ public function addLine($line) |
|
16 |
+ { |
|
17 |
+ $this->lines[] = new Line($line); |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ public function getLines() |
|
21 |
+ { |
|
22 |
+ return $this->lines; |
|
23 |
+ } |
|
24 |
+ |
|
25 |
+ public function setIndex($index) |
|
26 |
+ { |
|
27 |
+ $this->index = $index; |
|
28 |
+ } |
|
29 |
+ |
|
30 |
+ public function getIndex() |
|
31 |
+ { |
|
32 |
+ return $this->index; |
|
33 |
+ } |
|
34 |
+ |
|
35 |
+ public function setOld($old) |
|
36 |
+ { |
|
37 |
+ $this->old = $old; |
|
38 |
+ } |
|
39 |
+ |
|
40 |
+ public function getOld() |
|
41 |
+ { |
|
42 |
+ return $this->old; |
|
43 |
+ } |
|
44 |
+ |
|
45 |
+ public function setNew($new) |
|
46 |
+ { |
|
47 |
+ $this->new = $new; |
|
48 |
+ $this->file = substr($new, 6); |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ public function getNew() |
|
52 |
+ { |
|
53 |
+ return $this->new; |
|
54 |
+ } |
|
55 |
+ |
|
56 |
+ public function getFile() |
|
57 |
+ { |
|
58 |
+ return $this->file; |
|
59 |
+ } |
|
60 |
+} |
|
0 | 61 |
\ No newline at end of file |
1 | 62 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,48 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Model; |
|
4 |
+ |
|
5 |
+class Line |
|
6 |
+{ |
|
7 |
+ protected $line; |
|
8 |
+ protected $type; |
|
9 |
+ |
|
10 |
+ public function __construct($data) |
|
11 |
+ { |
|
12 |
+ if (!empty($data)) { |
|
13 |
+ if ($data[0] == '@') { |
|
14 |
+ $this->setType('chunk'); |
|
15 |
+ } |
|
16 |
+ |
|
17 |
+ if ($data[0] == '-') { |
|
18 |
+ $this->setType('old'); |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ if ($data[0] == '+') { |
|
22 |
+ $this->setType('new'); |
|
23 |
+ } |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ $this->setLine($data); |
|
27 |
+ } |
|
28 |
+ |
|
29 |
+ public function getLine() |
|
30 |
+ { |
|
31 |
+ return $this->line; |
|
32 |
+ } |
|
33 |
+ |
|
34 |
+ public function setLine($line) |
|
35 |
+ { |
|
36 |
+ $this->line = $line; |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ public function getType() |
|
40 |
+ { |
|
41 |
+ return $this->type; |
|
42 |
+ } |
|
43 |
+ |
|
44 |
+ public function setType($type) |
|
45 |
+ { |
|
46 |
+ $this->type = $type; |
|
47 |
+ } |
|
48 |
+} |
|
0 | 49 |
\ No newline at end of file |
1 | 50 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,44 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Model; |
|
4 |
+ |
|
5 |
+use Git\Client; |
|
6 |
+use Git\Repository; |
|
7 |
+use Git\ScopeAware; |
|
8 |
+ |
|
9 |
+class Symlink |
|
10 |
+{ |
|
11 |
+ protected $mode; |
|
12 |
+ protected $name; |
|
13 |
+ protected $path; |
|
14 |
+ |
|
15 |
+ public function getMode() |
|
16 |
+ { |
|
17 |
+ return $this->mode; |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ public function setMode($mode) |
|
21 |
+ { |
|
22 |
+ $this->mode = $mode; |
|
23 |
+ } |
|
24 |
+ |
|
25 |
+ public function getName() |
|
26 |
+ { |
|
27 |
+ return $this->name; |
|
28 |
+ } |
|
29 |
+ |
|
30 |
+ public function setName($name) |
|
31 |
+ { |
|
32 |
+ $this->name = $name; |
|
33 |
+ } |
|
34 |
+ |
|
35 |
+ public function getPath() |
|
36 |
+ { |
|
37 |
+ return $this->path; |
|
38 |
+ } |
|
39 |
+ |
|
40 |
+ public function setPath($path) |
|
41 |
+ { |
|
42 |
+ $this->path = $path; |
|
43 |
+ } |
|
44 |
+} |
|
0 | 45 |
\ No newline at end of file |
1 | 46 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,172 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git\Model; |
|
4 |
+ |
|
5 |
+use Git\Client; |
|
6 |
+use Git\Repository; |
|
7 |
+use Git\ScopeAware; |
|
8 |
+ |
|
9 |
+class Tree extends ScopeAware implements \RecursiveIterator |
|
10 |
+{ |
|
11 |
+ protected $mode; |
|
12 |
+ protected $hash; |
|
13 |
+ protected $name; |
|
14 |
+ protected $data; |
|
15 |
+ protected $position = 0; |
|
16 |
+ |
|
17 |
+ public function __construct($hash, Client $client, Repository $repository) { |
|
18 |
+ $this->setClient($client); |
|
19 |
+ $this->setRepository($repository); |
|
20 |
+ $this->setHash($hash); |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ public function parse() |
|
24 |
+ { |
|
25 |
+ $data = $this->getClient()->run($this->getRepository(), 'ls-tree -l ' . $this->getHash()); |
|
26 |
+ $lines = explode("\n", $data); |
|
27 |
+ $files = array(); |
|
28 |
+ $root = array(); |
|
29 |
+ |
|
30 |
+ foreach ($lines as $key => $line) { |
|
31 |
+ if (empty($line)) { |
|
32 |
+ unset($lines[$key]); |
|
33 |
+ continue; |
|
34 |
+ } |
|
35 |
+ |
|
36 |
+ $files[] = preg_split("/[\s]+/", $line); |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ foreach ($files as $file) { |
|
40 |
+ if ($file[1] == 'commit') { |
|
41 |
+ // submodule |
|
42 |
+ continue; |
|
43 |
+ } |
|
44 |
+ |
|
45 |
+ if ($file[0] == '120000') { |
|
46 |
+ $show = $this->getClient()->run($this->getRepository(), 'show ' . $file[2]); |
|
47 |
+ $tree = new Symlink; |
|
48 |
+ $tree->setMode($file[0]); |
|
49 |
+ $tree->setName($file[4]); |
|
50 |
+ $tree->setPath($show); |
|
51 |
+ $root[] = $tree; |
|
52 |
+ continue; |
|
53 |
+ } |
|
54 |
+ |
|
55 |
+ if ($file[1] == 'blob') { |
|
56 |
+ $blob = new Blob($file[2], $this->getClient(), $this->getRepository()); |
|
57 |
+ $blob->setMode($file[0]); |
|
58 |
+ $blob->setName($file[4]); |
|
59 |
+ $blob->setSize($file[3]); |
|
60 |
+ $root[] = $blob; |
|
61 |
+ continue; |
|
62 |
+ } |
|
63 |
+ |
|
64 |
+ $tree = new Tree($file[2], $this->getClient(), $this->getRepository()); |
|
65 |
+ $tree->setMode($file[0]); |
|
66 |
+ $tree->setName($file[4]); |
|
67 |
+ $root[] = $tree; |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ $this->data = $root; |
|
71 |
+ } |
|
72 |
+ |
|
73 |
+ public function output() |
|
74 |
+ { |
|
75 |
+ $files = $folders = array(); |
|
76 |
+ |
|
77 |
+ foreach ($this as $node) { |
|
78 |
+ if ($node instanceof Blob) { |
|
79 |
+ $file['type'] = 'blob'; |
|
80 |
+ $file['name'] = $node->getName(); |
|
81 |
+ $file['size'] = $node->getSize(); |
|
82 |
+ $file['mode'] = $node->getMode(); |
|
83 |
+ $file['hash'] = $node->getHash(); |
|
84 |
+ $files[] = $file; |
|
85 |
+ continue; |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ if ($node instanceof Tree) { |
|
89 |
+ $folder['type'] = 'folder'; |
|
90 |
+ $folder['name'] = $node->getName(); |
|
91 |
+ $folder['size'] = ''; |
|
92 |
+ $folder['mode'] = $node->getMode(); |
|
93 |
+ $folder['hash'] = $node->getHash(); |
|
94 |
+ $folders[] = $folder; |
|
95 |
+ continue; |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ if ($node instanceof Symlink) { |
|
99 |
+ $folder['type'] = 'symlink'; |
|
100 |
+ $folder['name'] = $node->getName(); |
|
101 |
+ $folder['size'] = ''; |
|
102 |
+ $folder['mode'] = $node->getMode(); |
|
103 |
+ $folder['hash'] = ''; |
|
104 |
+ $folder['path'] = $node->getPath(); |
|
105 |
+ $folders[] = $folder; |
|
106 |
+ } |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ // Little hack to make folders appear before files |
|
110 |
+ $files = array_merge($folders, $files); |
|
111 |
+ |
|
112 |
+ return $files; |
|
113 |
+ } |
|
114 |
+ |
|
115 |
+ public function valid() { |
|
116 |
+ return isset($this->data[$this->position]); |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ public function hasChildren() { |
|
120 |
+ return is_array($this->data[$this->position]); |
|
121 |
+ } |
|
122 |
+ |
|
123 |
+ public function next() { |
|
124 |
+ $this->position++; |
|
125 |
+ } |
|
126 |
+ |
|
127 |
+ public function current() { |
|
128 |
+ return $this->data[$this->position]; |
|
129 |
+ } |
|
130 |
+ |
|
131 |
+ public function getChildren() { |
|
132 |
+ return $this->data[$this->position]; |
|
133 |
+ } |
|
134 |
+ |
|
135 |
+ public function rewind() { |
|
136 |
+ $this->position = 0; |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ public function key() { |
|
140 |
+ return $this->position; |
|
141 |
+ } |
|
142 |
+ |
|
143 |
+ public function getMode() |
|
144 |
+ { |
|
145 |
+ return $this->mode; |
|
146 |
+ } |
|
147 |
+ |
|
148 |
+ public function setMode($mode) |
|
149 |
+ { |
|
150 |
+ $this->mode = $mode; |
|
151 |
+ } |
|
152 |
+ |
|
153 |
+ public function getHash() |
|
154 |
+ { |
|
155 |
+ return $this->hash; |
|
156 |
+ } |
|
157 |
+ |
|
158 |
+ public function setHash($hash) |
|
159 |
+ { |
|
160 |
+ $this->hash = $hash; |
|
161 |
+ } |
|
162 |
+ |
|
163 |
+ public function getName() |
|
164 |
+ { |
|
165 |
+ return $this->name; |
|
166 |
+ } |
|
167 |
+ |
|
168 |
+ public function setName($name) |
|
169 |
+ { |
|
170 |
+ $this->name = $name; |
|
171 |
+ } |
|
172 |
+} |
|
0 | 173 |
\ No newline at end of file |
1 | 174 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,465 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git; |
|
4 |
+ |
|
5 |
+use Git\Commit\Commit; |
|
6 |
+use Git\Model\Tree; |
|
7 |
+use Git\Model\Blob; |
|
8 |
+use Git\Model\Diff; |
|
9 |
+ |
|
10 |
+class Repository |
|
11 |
+{ |
|
12 |
+ protected $path; |
|
13 |
+ protected $client; |
|
14 |
+ |
|
15 |
+ public function __construct($path, Client $client) |
|
16 |
+ { |
|
17 |
+ $this->setPath($path); |
|
18 |
+ $this->setClient($client); |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ public function setClient(Client $client) |
|
22 |
+ { |
|
23 |
+ $this->client = $client; |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ public function getClient() |
|
27 |
+ { |
|
28 |
+ return $this->client; |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ public function create() |
|
32 |
+ { |
|
33 |
+ mkdir($this->getPath()); |
|
34 |
+ $this->getClient()->run($this, 'init'); |
|
35 |
+ |
|
36 |
+ return $this; |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ public function getConfig($key) |
|
40 |
+ { |
|
41 |
+ $key = $this->getClient()->run($this, 'config ' . $key); |
|
42 |
+ return trim($key); |
|
43 |
+ } |
|
44 |
+ |
|
45 |
+ public function setConfig($key, $value) |
|
46 |
+ { |
|
47 |
+ $this->getClient()->run($this, "config $key \"$value\""); |
|
48 |
+ |
|
49 |
+ return $this; |
|
50 |
+ } |
|
51 |
+ |
|
52 |
+ /** |
|
53 |
+ * Add untracked files |
|
54 |
+ * |
|
55 |
+ * @access public |
|
56 |
+ * @param mixed $files Files to be added to the repository |
|
57 |
+ */ |
|
58 |
+ public function add($files = '.') |
|
59 |
+ { |
|
60 |
+ if(is_array($files)) { |
|
61 |
+ $files = implode(' ', $files); |
|
62 |
+ } |
|
63 |
+ |
|
64 |
+ $this->getClient()->run($this, "add $files"); |
|
65 |
+ |
|
66 |
+ return $this; |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ /** |
|
70 |
+ * Add all untracked files |
|
71 |
+ * |
|
72 |
+ * @access public |
|
73 |
+ */ |
|
74 |
+ public function addAll() |
|
75 |
+ { |
|
76 |
+ $this->getClient()->run($this, "add -A"); |
|
77 |
+ |
|
78 |
+ return $this; |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ /** |
|
82 |
+ * Commit changes to the repository |
|
83 |
+ * |
|
84 |
+ * @access public |
|
85 |
+ * @param string $message Description of the changes made |
|
86 |
+ */ |
|
87 |
+ public function commit($message) |
|
88 |
+ { |
|
89 |
+ $this->getClient()->run($this, "commit -m '$message'"); |
|
90 |
+ |
|
91 |
+ return $this; |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ /** |
|
95 |
+ * Checkout a branch |
|
96 |
+ * |
|
97 |
+ * @access public |
|
98 |
+ * @param string $branch Branch to be checked out |
|
99 |
+ */ |
|
100 |
+ public function checkout($branch) |
|
101 |
+ { |
|
102 |
+ $this->getClient()->run($this, "checkout $branch"); |
|
103 |
+ |
|
104 |
+ return $this; |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ /** |
|
108 |
+ * Pull repository changes |
|
109 |
+ * |
|
110 |
+ * @access public |
|
111 |
+ */ |
|
112 |
+ public function pull() |
|
113 |
+ { |
|
114 |
+ $this->getClient()->run($this, "pull"); |
|
115 |
+ |
|
116 |
+ return $this; |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ /** |
|
120 |
+ * Update remote references |
|
121 |
+ * |
|
122 |
+ * @access public |
|
123 |
+ * @param string $repository Repository to be pushed |
|
124 |
+ * @param string $refspec Refspec for the push |
|
125 |
+ */ |
|
126 |
+ public function push($repository = null, $refspec = null) |
|
127 |
+ { |
|
128 |
+ $command = "push"; |
|
129 |
+ |
|
130 |
+ if($repository) { |
|
131 |
+ $command .= " $repository"; |
|
132 |
+ } |
|
133 |
+ |
|
134 |
+ if($refspec) { |
|
135 |
+ $command .= " $refspec"; |
|
136 |
+ } |
|
137 |
+ |
|
138 |
+ $this->getClient()->run($this, $command); |
|
139 |
+ |
|
140 |
+ return $this; |
|
141 |
+ } |
|
142 |
+ |
|
143 |
+ /** |
|
144 |
+ * Show a list of the repository branches |
|
145 |
+ * |
|
146 |
+ * @access public |
|
147 |
+ * @return array List of branches |
|
148 |
+ */ |
|
149 |
+ public function getBranches() |
|
150 |
+ { |
|
151 |
+ $branches = $this->getClient()->run($this, "branch"); |
|
152 |
+ $branches = explode("\n", $branches); |
|
153 |
+ $branches = array_filter(preg_replace('/[\*\s]/', '', $branches)); |
|
154 |
+ |
|
155 |
+ return $branches; |
|
156 |
+ } |
|
157 |
+ |
|
158 |
+ /** |
|
159 |
+ * Show the current repository branch |
|
160 |
+ * |
|
161 |
+ * @access public |
|
162 |
+ * @return string Current repository branch |
|
163 |
+ */ |
|
164 |
+ public function getCurrentBranch() |
|
165 |
+ { |
|
166 |
+ $branches = $this->getClient()->run($this, "branch"); |
|
167 |
+ $branches = explode("\n", $branches); |
|
168 |
+ |
|
169 |
+ foreach($branches as $branch) { |
|
170 |
+ if($branch[0] == '*') { |
|
171 |
+ return substr($branch, 2); |
|
172 |
+ } |
|
173 |
+ } |
|
174 |
+ } |
|
175 |
+ |
|
176 |
+ /** |
|
177 |
+ * Check if a specified branch exists |
|
178 |
+ * |
|
179 |
+ * @access public |
|
180 |
+ * @param string $branch Branch to be checked |
|
181 |
+ * @return boolean True if the branch exists |
|
182 |
+ */ |
|
183 |
+ public function hasBranch($branch) |
|
184 |
+ { |
|
185 |
+ $branches = $this->getBranches(); |
|
186 |
+ $status = in_array($branch, $branches); |
|
187 |
+ return $status; |
|
188 |
+ } |
|
189 |
+ |
|
190 |
+ /** |
|
191 |
+ * Create a new repository branch |
|
192 |
+ * |
|
193 |
+ * @access public |
|
194 |
+ * @param string $branch Branch name |
|
195 |
+ */ |
|
196 |
+ public function createBranch($branch) |
|
197 |
+ { |
|
198 |
+ $this->getClient()->run($this, "branch $branch"); |
|
199 |
+ } |
|
200 |
+ |
|
201 |
+ /** |
|
202 |
+ * Show a list of the repository tags |
|
203 |
+ * |
|
204 |
+ * @access public |
|
205 |
+ * @return array List of tags |
|
206 |
+ */ |
|
207 |
+ public function getTags() |
|
208 |
+ { |
|
209 |
+ $tags = $this->getClient()->run($this, "tag"); |
|
210 |
+ $tags = explode("\n", $tags); |
|
211 |
+ |
|
212 |
+ if (empty($tags[0])) { |
|
213 |
+ return NULL; |
|
214 |
+ } |
|
215 |
+ |
|
216 |
+ return $tags; |
|
217 |
+ } |
|
218 |
+ |
|
219 |
+ /** |
|
220 |
+ * Show the repository commit log |
|
221 |
+ * |
|
222 |
+ * @access public |
|
223 |
+ * @return array Commit log |
|
224 |
+ */ |
|
225 |
+ public function getCommits($file = null) |
|
226 |
+ { |
|
227 |
+ $command = 'log --pretty=format:\'"%h": {"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\''; |
|
228 |
+ |
|
229 |
+ if ($file) { |
|
230 |
+ $command .= " $file"; |
|
231 |
+ } |
|
232 |
+ |
|
233 |
+ $logs = $this->getClient()->run($this, $command); |
|
234 |
+ $logs = str_replace("\n", ',', $logs); |
|
235 |
+ $logs = json_decode("{ $logs }", true); |
|
236 |
+ |
|
237 |
+ foreach ($logs as $log) { |
|
238 |
+ $log['message'] = str_replace('-', ' ', $log['message']); |
|
239 |
+ $commit = new Commit; |
|
240 |
+ $commit->importData($log); |
|
241 |
+ $commits[] = $commit; |
|
242 |
+ } |
|
243 |
+ |
|
244 |
+ return $commits; |
|
245 |
+ } |
|
246 |
+ |
|
247 |
+ public function getRelatedCommits($hash) |
|
248 |
+ { |
|
249 |
+ $logs = $this->getClient()->run($this, 'log --pretty=format:\'"%h": {"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\''); |
|
250 |
+ $logs = str_replace("\n", ',', $logs); |
|
251 |
+ $logs = json_decode("{ $logs }", true); |
|
252 |
+ |
|
253 |
+ foreach ($logs as $log) { |
|
254 |
+ $log['message'] = str_replace('-', ' ', $log['message']); |
|
255 |
+ $logTree = $this->getClient()->run($this, 'diff-tree -t -r ' . $log['hash']); |
|
256 |
+ $lines = explode("\n", $logTree); |
|
257 |
+ array_shift($lines); |
|
258 |
+ $files = array(); |
|
259 |
+ |
|
260 |
+ foreach ($lines as $key => $line) { |
|
261 |
+ if (empty($line)) { |
|
262 |
+ unset($lines[$key]); |
|
263 |
+ continue; |
|
264 |
+ } |
|
265 |
+ |
|
266 |
+ $files[] = preg_split("/[\s]+/", $line); |
|
267 |
+ } |
|
268 |
+ |
|
269 |
+ // Now let's find the commits who have our hash within them |
|
270 |
+ foreach ($files as $file) { |
|
271 |
+ if ($file[1] == 'commit') { |
|
272 |
+ continue; |
|
273 |
+ } |
|
274 |
+ |
|
275 |
+ if ($file[3] == $hash) { |
|
276 |
+ $commit = new Commit; |
|
277 |
+ $commit->importData($log); |
|
278 |
+ $commits[] = $commit; |
|
279 |
+ break; |
|
280 |
+ } |
|
281 |
+ } |
|
282 |
+ } |
|
283 |
+ |
|
284 |
+ return $commits; |
|
285 |
+ } |
|
286 |
+ |
|
287 |
+ public function getCommit($commit) |
|
288 |
+ { |
|
289 |
+ $logs = $this->getClient()->run($this, 'show --pretty=format:\'{"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\' ' . $commit); |
|
290 |
+ $logs = explode("\n", $logs); |
|
291 |
+ |
|
292 |
+ // Read commit metadata |
|
293 |
+ $data = json_decode($logs[0], true); |
|
294 |
+ $data['message'] = str_replace('-', ' ', $data['message']); |
|
295 |
+ $commit = new Commit; |
|
296 |
+ $commit->importData($data); |
|
297 |
+ unset($logs[0]); |
|
298 |
+ |
|
299 |
+ // Read diff logs |
|
300 |
+ foreach ($logs as $log) { |
|
301 |
+ if ('diff' === substr($log, 0, 4)) { |
|
302 |
+ if (isset($diff)) { |
|
303 |
+ $diffs[] = $diff; |
|
304 |
+ } |
|
305 |
+ |
|
306 |
+ $diff = new Diff; |
|
307 |
+ continue; |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ if ('index' === substr($log, 0, 5)) { |
|
311 |
+ $diff->setIndex($log); |
|
312 |
+ continue; |
|
313 |
+ } |
|
314 |
+ |
|
315 |
+ if ('---' === substr($log, 0, 3)) { |
|
316 |
+ $diff->setOld($log); |
|
317 |
+ continue; |
|
318 |
+ } |
|
319 |
+ |
|
320 |
+ if ('+++' === substr($log, 0, 3)) { |
|
321 |
+ $diff->setNew($log); |
|
322 |
+ continue; |
|
323 |
+ } |
|
324 |
+ |
|
325 |
+ $diff->addLine($log); |
|
326 |
+ } |
|
327 |
+ |
|
328 |
+ if (isset($diff)) { |
|
329 |
+ $diffs[] = $diff; |
|
330 |
+ } |
|
331 |
+ |
|
332 |
+ $commit->setDiffs($diffs); |
|
333 |
+ |
|
334 |
+ return $commit; |
|
335 |
+ } |
|
336 |
+ |
|
337 |
+ public function getAuthorStatistics() |
|
338 |
+ { |
|
339 |
+ $logs = $this->getClient()->run($this, 'log --pretty=format:\'%an||%ae\''); |
|
340 |
+ $logs = explode("\n", $logs); |
|
341 |
+ $logs = array_count_values($logs); |
|
342 |
+ arsort($logs); |
|
343 |
+ |
|
344 |
+ foreach ($logs as $user => $count) { |
|
345 |
+ $user = explode('||', $user); |
|
346 |
+ $data[] = array('name' => $user[0], 'email' => $user[1], 'commits' => $count); |
|
347 |
+ } |
|
348 |
+ |
|
349 |
+ return $data; |
|
350 |
+ } |
|
351 |
+ |
|
352 |
+ public function getStatistics($branch) |
|
353 |
+ { |
|
354 |
+ // Calculate amount of files, extensions and file size |
|
355 |
+ $logs = $this->getClient()->run($this, 'ls-tree -r -l ' . $branch); |
|
356 |
+ $lines = explode("\n", $logs); |
|
357 |
+ $files = array(); |
|
358 |
+ $data['extensions'] = array(); |
|
359 |
+ $data['size'] = 0; |
|
360 |
+ $data['files'] = 0; |
|
361 |
+ |
|
362 |
+ foreach ($lines as $key => $line) { |
|
363 |
+ if (empty($line)) { |
|
364 |
+ unset($lines[$key]); |
|
365 |
+ continue; |
|
366 |
+ } |
|
367 |
+ |
|
368 |
+ $files[] = preg_split("/[\s]+/", $line); |
|
369 |
+ } |
|
370 |
+ |
|
371 |
+ foreach ($files as $file) { |
|
372 |
+ if ($file[1] == 'blob') { |
|
373 |
+ $data['files']++; |
|
374 |
+ } |
|
375 |
+ |
|
376 |
+ if (is_numeric($file[3])) { |
|
377 |
+ $data['size'] += $file[3]; |
|
378 |
+ } |
|
379 |
+ |
|
380 |
+ if (($pos = strrpos($file[4], '.')) !== FALSE) { |
|
381 |
+ $data['extensions'][] = substr($file[4], $pos); |
|
382 |
+ } |
|
383 |
+ } |
|
384 |
+ |
|
385 |
+ $data['extensions'] = array_count_values($data['extensions']); |
|
386 |
+ arsort($data['extensions']); |
|
387 |
+ |
|
388 |
+ return $data; |
|
389 |
+ } |
|
390 |
+ |
|
391 |
+ /** |
|
392 |
+ * Get the Tree for the provided folder |
|
393 |
+ * |
|
394 |
+ * @param string $tree Folder that will be parsed |
|
395 |
+ * @return Tree Instance of Tree for the provided folder |
|
396 |
+ */ |
|
397 |
+ public function getTree($tree) |
|
398 |
+ { |
|
399 |
+ $tree = new Tree($tree, $this->getClient(), $this); |
|
400 |
+ $tree->parse(); |
|
401 |
+ return $tree; |
|
402 |
+ } |
|
403 |
+ |
|
404 |
+ /** |
|
405 |
+ * Get the Blob for the provided file |
|
406 |
+ * |
|
407 |
+ * @param string $blob File that will be parsed |
|
408 |
+ * @return Blob Instance of Blob for the provided file |
|
409 |
+ */ |
|
410 |
+ public function getBlob($blob) |
|
411 |
+ { |
|
412 |
+ return new Blob($blob, $this->getClient(), $this); |
|
413 |
+ } |
|
414 |
+ |
|
415 |
+ /** |
|
416 |
+ * Blames the provided file and parses the output |
|
417 |
+ * |
|
418 |
+ * @param string $file File that will be blamed |
|
419 |
+ * @return array Commits hashes containing the lines |
|
420 |
+ */ |
|
421 |
+ public function getBlame($file) |
|
422 |
+ { |
|
423 |
+ $logs = $this->getClient()->run($this, "blame -s $file"); |
|
424 |
+ $logs = explode("\n", $logs); |
|
425 |
+ |
|
426 |
+ foreach ($logs as $log) { |
|
427 |
+ if ($log == '') { |
|
428 |
+ continue; |
|
429 |
+ } |
|
430 |
+ |
|
431 |
+ $split = preg_split("/[a-zA-Z0-9^]{8}[\s]+[0-9]+\)/", $log); |
|
432 |
+ preg_match_all("/([a-zA-Z0-9^]{8})[\s]+([0-9]+)\)/", $log, $match); |
|
433 |
+ |
|
434 |
+ $commit = $match[1][0]; |
|
435 |
+ |
|
436 |
+ if (!isset($blame[$commit]['line'])) { |
|
437 |
+ $blame[$commit]['line'] = ''; |
|
438 |
+ } |
|
439 |
+ |
|
440 |
+ $blame[$commit]['line'] .= PHP_EOL . $split[1]; |
|
441 |
+ } |
|
442 |
+ |
|
443 |
+ return $blame; |
|
444 |
+ } |
|
445 |
+ |
|
446 |
+ /** |
|
447 |
+ * Get the current Repository path |
|
448 |
+ * |
|
449 |
+ * @return string Path where the repository is located |
|
450 |
+ */ |
|
451 |
+ public function getPath() |
|
452 |
+ { |
|
453 |
+ return $this->path; |
|
454 |
+ } |
|
455 |
+ |
|
456 |
+ /** |
|
457 |
+ * Set the current Repository path |
|
458 |
+ * |
|
459 |
+ * @param string $path Path where the repository is located |
|
460 |
+ */ |
|
461 |
+ public function setPath($path) |
|
462 |
+ { |
|
463 |
+ $this->path = $path; |
|
464 |
+ } |
|
465 |
+} |
0 | 466 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,29 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Git; |
|
4 |
+ |
|
5 |
+class ScopeAware |
|
6 |
+{ |
|
7 |
+ protected $client; |
|
8 |
+ protected $repository; |
|
9 |
+ |
|
10 |
+ public function setClient(Client $client) |
|
11 |
+ { |
|
12 |
+ $this->client = $client; |
|
13 |
+ } |
|
14 |
+ |
|
15 |
+ public function getClient() |
|
16 |
+ { |
|
17 |
+ return $this->client; |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ public function getRepository() |
|
21 |
+ { |
|
22 |
+ return $this->repository; |
|
23 |
+ } |
|
24 |
+ |
|
25 |
+ public function setRepository($repository) |
|
26 |
+ { |
|
27 |
+ $this->repository = $repository; |
|
28 |
+ } |
|
29 |
+} |
|
0 | 30 |
\ No newline at end of file |
1 | 31 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,18 @@ |
1 |
+<?xml version="1.0" encoding="UTF-8"?> |
|
2 |
+ |
|
3 |
+<phpunit backupGlobals="false" |
|
4 |
+ backupStaticAttributes="false" |
|
5 |
+ colors="true" |
|
6 |
+ convertErrorsToExceptions="true" |
|
7 |
+ convertNoticesToExceptions="true" |
|
8 |
+ convertWarningsToExceptions="true" |
|
9 |
+ processIsolation="false" |
|
10 |
+ stopOnFailure="false" |
|
11 |
+ syntaxCheck="true" |
|
12 |
+> |
|
13 |
+ <testsuites> |
|
14 |
+ <testsuite name="GitList Test Suite"> |
|
15 |
+ <directory>./tests/</directory> |
|
16 |
+ </testsuite> |
|
17 |
+ </testsuites> |
|
18 |
+</phpunit> |