diff --git a/src/GitList/Controller/BlobController.php b/src/GitList/Controller/BlobController.php index 06c445b..9b4f331 100644 --- a/src/GitList/Controller/BlobController.php +++ b/src/GitList/Controller/BlobController.php @@ -15,6 +15,9 @@ class BlobController implements ControllerProviderInterface $route->get('{repo}/blob/{branch}/{file}', function($repo, $branch, $file) use ($app) { $repository = $app['git']->getRepository($app['git.repos'] . $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); @@ -39,11 +42,14 @@ class BlobController implements ControllerProviderInterface )); })->assert('file', '.+') ->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('blob'); $route->get('{repo}/raw/{branch}/{file}', function($repo, $branch, $file) use ($app) { $repository = $app['git']->getRepository($app['git.repos'] . $repo); + + list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file); + $blob = $repository->getBlob("$branch:\"$file\"")->output(); $headers = array(); @@ -59,7 +65,7 @@ class BlobController implements ControllerProviderInterface return new Response($blob, 200, $headers); })->assert('file', '.+') ->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('blob_raw'); return $route; diff --git a/src/GitList/Controller/CommitController.php b/src/GitList/Controller/CommitController.php index d7659d7..8aaa0f4 100644 --- a/src/GitList/Controller/CommitController.php +++ b/src/GitList/Controller/CommitController.php @@ -16,6 +16,9 @@ class CommitController implements ControllerProviderInterface $route->get('{repo}/commits/{branch}/{file}', function($repo, $branch, $file) use ($app) { $repository = $app['git']->getRepository($app['git.repos'] . $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']); @@ -38,7 +41,7 @@ class CommitController implements ControllerProviderInterface 'file' => $file, )); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->assert('file', '.+') ->value('branch', 'master') ->value('file', '') @@ -80,6 +83,9 @@ class CommitController implements ControllerProviderInterface $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.repository']->extractRef($repository, $branch, $file); + $blames = $repository->getBlame("$branch -- \"$file\""); return $app['twig']->render('blame.twig', array( @@ -92,7 +98,7 @@ class CommitController implements ControllerProviderInterface )); })->assert('repo', '[\w-._]+') ->assert('file', '.+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('blame'); return $route; diff --git a/src/GitList/Controller/MainController.php b/src/GitList/Controller/MainController.php index ca56ad1..399c0df 100644 --- a/src/GitList/Controller/MainController.php +++ b/src/GitList/Controller/MainController.php @@ -35,7 +35,7 @@ class MainController implements ControllerProviderInterface 'authors' => $authors, )); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->value('branch', 'master') ->bind('stats'); @@ -51,7 +51,7 @@ class MainController implements ControllerProviderInterface return new Response($html, 200, array('Content-Type' => 'application/rss+xml')); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('rss'); diff --git a/src/GitList/Controller/TreeController.php b/src/GitList/Controller/TreeController.php index 4abe17f..f85d5be 100644 --- a/src/GitList/Controller/TreeController.php +++ b/src/GitList/Controller/TreeController.php @@ -20,6 +20,9 @@ class TreeController implements ControllerProviderInterface if (!$branch) { $branch = $repository->getHead(); } + + list($branch, $tree) = $app['util.repository']->extractRef($repository, $branch, $tree); + $files = $repository->getTree($tree ? "$branch:\"$tree\"/" : $branch); $breadcrumbs = $app['util.view']->getBreadcrumbs($tree); @@ -42,7 +45,7 @@ class TreeController implements ControllerProviderInterface 'readme' => $app['util.repository']->getReadme($repo, $branch), )); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->assert('tree', '.+') ->bind('tree'); @@ -66,13 +69,13 @@ class TreeController implements ControllerProviderInterface 'tags' => $repository->getTags(), )); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('search'); $route->get('{repo}/{branch}/', function($repo, $branch) use ($app, $treeController) { return $treeController($repo, $branch); })->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('branch'); $route->get('{repo}/', function($repo) use ($app, $treeController) { @@ -109,7 +112,7 @@ class TreeController implements ControllerProviderInterface )); })->assert('format', '(zip|tar)') ->assert('repo', '[\w-._]+') - ->assert('branch', '[\w-._]+') + ->assert('branch', '[\w-._\/]+') ->bind('archive'); return $route; diff --git a/src/GitList/Util/Repository.php b/src/GitList/Util/Repository.php index b2b34cf..1cb59c2 100644 --- a/src/GitList/Util/Repository.php +++ b/src/GitList/Util/Repository.php @@ -114,7 +114,7 @@ class Repository */ public function getFileType($file) { - if (($pos = strrpos($file, '.')) !== FALSE) { + if (($pos = strrpos($file, '.')) !== false) { $fileType = substr($file, $pos + 1); } else { return 'text'; @@ -175,4 +175,46 @@ class Repository 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 + $valid_refs = array_merge((array)$repository->getBranches(), (array)$repository->getTags()); + foreach ($valid_refs as $k => $v) { + if (!preg_match("#{$v}/#", $input)) { + unset($valid_refs[$k]); + } + } + + //No exact ref match, so just try our best + if (count($valid_refs) > 1) { + preg_match('/([^\/]+)(.*)/', $input, $matches); + $branch = preg_replace('/^\/|\/$/', '', $matches[0]); + } + else { + //extract branch name + $branch = array_shift($valid_refs); + } + } + $tree = trim(str_replace($branch, "", $input), "/"); + + return array($branch, $tree); + } + } diff --git a/tests/InterfaceTest.php b/tests/InterfaceTest.php index f8afbc2..95c8b86 100644 --- a/tests/InterfaceTest.php +++ b/tests/InterfaceTest.php @@ -42,6 +42,7 @@ class InterfaceTest extends WebTestCase $repository->commit("Initial commit"); $repository->createBranch('issue12'); $repository->createBranch('issue42'); + $repository->createBranch('branch/name/wiith/slashes'); // foobar repository fixture $git->createRepository(self::$tmpdir . 'foobar'); @@ -94,9 +95,11 @@ class InterfaceTest extends WebTestCase $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('issue12', $crawler->filter('.dropdown-menu li')->eq(1)->text()); - $this->assertEquals('issue42', $crawler->filter('.dropdown-menu li')->eq(2)->text()); - $this->assertEquals('master', $crawler->filter('.dropdown-menu li')->eq(3)->text()); + + $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());