Merge branch 'upstream_master'
Foxtons Web Design [Wed, 7 Apr 2010 12:42:16 +0000 (13:42 +0100)]
137 files changed:
.gitignore
ISSUES
Makefile.PL
README
lib/Gitalist.pm
lib/Gitalist/ActionRole/FilenameArgs.pm [new file with mode: 0644]
lib/Gitalist/Controller.pm [new file with mode: 0644]
lib/Gitalist/Controller/Fragment.pm [new file with mode: 0644]
lib/Gitalist/Controller/Fragment/Ref.pm [new file with mode: 0644]
lib/Gitalist/Controller/Fragment/Repository.pm [new file with mode: 0644]
lib/Gitalist/Controller/LegacyURI.pm [new file with mode: 0644]
lib/Gitalist/Controller/OPML.pm [new file with mode: 0644]
lib/Gitalist/Controller/Ref.pm [new file with mode: 0644]
lib/Gitalist/Controller/Repository.pm [new file with mode: 0644]
lib/Gitalist/Controller/Root.pm
lib/Gitalist/Git/Object/Commit.pm
lib/Gitalist/Git/Repository.pm
lib/Gitalist/URIStructure/Fragment/WithLog.pm [new file with mode: 0644]
lib/Gitalist/URIStructure/Ref.pm [new file with mode: 0644]
lib/Gitalist/URIStructure/Repository.pm [new file with mode: 0644]
lib/Gitalist/URIStructure/WithLog.pm [new file with mode: 0644]
lib/Gitalist/View/Default.pm
root/_chroma_hash.tt2 [deleted file]
root/_diff.tt2 [changed mode: 0644->0755]
root/_diff_tree.tt2 [changed mode: 0644->0755]
root/_header_feeds.tt2
root/_heads.tt2 [deleted file]
root/_history.tt2 [deleted file]
root/_log_pager.tt2
root/_refs.tt2
root/_shortlog.tt2 [deleted file]
root/_tree.tt2 [deleted file]
root/blame.tt2 [deleted file]
root/blobdiff.tt2 [deleted file]
root/blobdiff_plain.tt2 [deleted file]
root/commit.tt2 [deleted file]
root/commitdiff.tt2 [deleted file]
root/commitdiff_plain.tt2 [deleted file]
root/default.tt2 [deleted file]
root/favicon.ico
root/favicon.png [deleted file]
root/fragment/collectionofrepositories.tt2 [new file with mode: 0755]
root/fragment/ref/blame.tt2 [new file with mode: 0755]
root/fragment/ref/blob.tt2 [new file with mode: 0644]
root/fragment/ref/commit.tt2 [new file with mode: 0755]
root/fragment/ref/diff_fancy.tt2 [new file with mode: 0755]
root/fragment/ref/diff_plain.tt2 [new file with mode: 0644]
root/fragment/ref/history.tt2 [new file with mode: 0755]
root/fragment/ref/longlog.tt2 [new file with mode: 0644]
root/fragment/ref/shortlog.tt2 [new file with mode: 0644]
root/fragment/ref/tree.tt2 [new file with mode: 0755]
root/fragment/repository/heads.tt2 [new file with mode: 0755]
root/fragment/repository/longlog.tt2 [new file with mode: 0755]
root/fragment/repository/shortlog.tt2 [new file with mode: 0755]
root/fragment/repository/summary.tt2 [new file with mode: 0755]
root/fragment/repository/tags.tt2 [new file with mode: 0644]
root/git-logo.png [deleted file]
root/gitweb.css [deleted file]
root/gitweb.tt2 [deleted file]
root/heads.tt2 [deleted file]
root/history.tt2 [deleted file]
root/inc/chroma_hash.tt2 [new file with mode: 0755]
root/inc/footer_feeds.tt2 [moved from root/_footer_feeds.tt2 with 73% similarity]
root/inc/log_pager.tt2 [new file with mode: 0755]
root/index.tt2 [changed mode: 0644->0755]
root/log.tt2 [deleted file]
root/logo.svg [deleted file]
root/nav/actions.tt2
root/nav/path.tt2 [changed mode: 0644->0755]
root/nav/search.tt2 [changed mode: 0644->0755]
root/opml.tt2 [new file with mode: 0644]
root/ref/blame.tt2 [new file with mode: 0755]
root/ref/blob.tt2 [moved from root/blob.tt2 with 50% similarity, mode: 0755]
root/ref/commit.tt2 [new file with mode: 0644]
root/ref/diff_fancy.tt2 [new file with mode: 0755]
root/ref/diff_plain.tt2 [new file with mode: 0644]
root/ref/history.tt2 [new file with mode: 0644]
root/ref/longlog.tt2 [new file with mode: 0644]
root/ref/shortlog.tt2 [new file with mode: 0644]
root/ref/tree.tt2 [new file with mode: 0755]
root/repository/atom.tt2 [new file with mode: 0644]
root/repository/heads.tt2 [new file with mode: 0644]
root/repository/longlog.tt2 [new file with mode: 0644]
root/repository/reflog.tt2 [moved from root/reflog.tt2 with 100% similarity]
root/repository/rss.tt2 [new file with mode: 0644]
root/repository/search.tt2 [new file with mode: 0644]
root/repository/shortlog.tt2 [new file with mode: 0755]
root/repository/summary.tt2 [new file with mode: 0755]
root/repository/tags.tt2 [new file with mode: 0755]
root/search.tt2
root/search_help.tt2
root/shortlog.tt2 [deleted file]
root/static/css/960.css [new file with mode: 0644]
root/static/css/blueprint/screen.css
root/static/css/core.css [new file with mode: 0755]
root/static/css/site.css [changed mode: 0644->0755]
root/static/css/syntax/Diff.css [changed mode: 0644->0755]
root/static/git-favicon.png [moved from root/git-favicon.png with 100% similarity]
root/static/i/arrow_grey.gif [new file with mode: 0755]
root/static/i/arrow_grey_left.gif [new file with mode: 0755]
root/static/i/bg.png [new file with mode: 0755]
root/static/i/bg_bottom.png [new file with mode: 0755]
root/static/i/bg_content.png [new file with mode: 0755]
root/static/i/bg_top.png [new file with mode: 0755]
root/static/i/icons/Thumbs.db [new file with mode: 0644]
root/static/i/icons/attention.png [new file with mode: 0755]
root/static/i/icons/blame.png [new file with mode: 0755]
root/static/i/icons/blob.png [new file with mode: 0755]
root/static/i/icons/button_sha1.png [new file with mode: 0755]
root/static/i/icons/button_sha1_invert.png [new file with mode: 0755]
root/static/i/icons/commit.png [new file with mode: 0755]
root/static/i/icons/diff.png [new file with mode: 0755]
root/static/i/icons/diffcurrent.png [new file with mode: 0755]
root/static/i/icons/fulllog.gif [new file with mode: 0755]
root/static/i/icons/history.png [new file with mode: 0755]
root/static/i/icons/longlog.png [new file with mode: 0755]
root/static/i/icons/raw.png [new file with mode: 0755]
root/static/i/icons/shortlog.gif [new file with mode: 0755]
root/static/i/icons/shortlog.png [new file with mode: 0755]
root/static/i/icons/tree.gif [new file with mode: 0755]
root/static/i/icons/tree.png [new file with mode: 0755]
root/static/i/logo.png [new file with mode: 0755]
root/summary.tt2 [deleted file]
root/tags.tt2 [deleted file]
root/tree.tt2 [deleted file]
root/wrapper.tt2 [new file with mode: 0755]
t/01app.t
t/02git_object.t
t/03legacy_uri.t
t/app-mech-rootpage.t [new file with mode: 0644]
t/atom.t [new file with mode: 0644]
t/lib/TestGitalist.pm [new file with mode: 0644]
t/lib/gitalist_testing.conf [deleted file]
t/lib/repositories/repo1/refs/tags/0.01 [new file with mode: 0644]
t/opml.t [new file with mode: 0644]
t/rss.t [new file with mode: 0644]
t/script/gitalist_server.pl [new file with mode: 0755]

index b23a01a..b22aaa5 100644 (file)
@@ -7,7 +7,6 @@ META.yml
 Makefile
 Makefile.old
 blib*
-inc*
 pm_to_blib
 MANIFEST
 MANIFEST.bak
diff --git a/ISSUES b/ISSUES
index 057c37d..74d64a7 100644 (file)
--- a/ISSUES
+++ b/ISSUES
@@ -1,3 +1,7 @@
+* Root /search page doesn't do anything
+* Atom feed sucks shit (see t/atom.t)
+* OPML & RSS & Atom Feed dates incorrect format
+* Gitweb compat URIs are borked - there were some I just wholesale ripped out. Put back..
 * Sort out commitdiff so conflicted merges have an equivalent display to gitweb (d7f39bebabeb31ce9a7b4f1b6f3db9f391b78c3e as a reference)
 * Need to sanitise the input ...
 * The snapshot action does not properly support tgz - requests for this format will receive a tar.  tbz2 is not supported at all (yet).
index 517d200..d733e1c 100644 (file)
@@ -29,7 +29,7 @@ if ($ENV{GITALIST_RELEASE_TESTING}) {
     # Fill in provides info so that indexing works right (in the face of MX::Declare)
     # by just smashing filenames to package names and not trying to be smart..
     File::Find::find(sub {
-        return unless $File::Find::name =~ /\.pm$/;
+        return unless /^\w.*?\.pm$/;
         my $fn = $File::Find::name;
         my $ver = ExtUtils::MM_Unix->parse_version($fn);
 
@@ -54,10 +54,13 @@ requires 'Catalyst::Plugin::ConfigLoader';
 requires 'Catalyst::Plugin::StackTrace';
 requires 'Catalyst::Plugin::Static::Simple';
 requires 'Catalyst::Plugin::Unicode::Encoding';
+requires 'Catalyst::Plugin::SubRequest' => '0.15';
 requires 'Catalyst::Action::RenderView';
 requires 'Catalyst::Component::InstancePerContext';
+requires 'Catalyst::Controller::ActionRole';
+requires 'Catalyst::View::Component::SubInclude' => '0.07';
 requires 'Catalyst::View::TT';
-
+requires 'Try::Tiny';
 requires 'Template';
 requires 'Template::Provider::Encoding';
 requires 'Template::Plugin::Cycle';
@@ -66,6 +69,7 @@ requires 'Config::General';
 
 requires 'Moose';
 requires 'Moose::Autobox';
+requires 'MooseX::MultiMethods' => '0.10';
 requires 'MooseX::Declare' => '0.32';
 requires 'MooseX::Types::Common';
 requires 'MooseX::Types::Path::Class';
@@ -87,9 +91,6 @@ requires 'Path::Class' => '0.17';
 requires 'Sub::Exporter';
 requires 'Syntax::Highlight::Engine::Kate';
 requires 'Sys::Hostname';
-requires 'XML::Atom';
-requires 'XML::RSS';
-requires 'XML::OPML::SimpleGen';
 
 test_requires 'Test::More' => '0.88';
 test_requires 'Test::utf8' => '0.02';
@@ -104,6 +105,16 @@ if ($Module::Install::AUTHOR) {
         and die $!;
 }
 
+if ($ENV{GITALIST_RELEASE_TESTING}) {
+    author_tests 't/author';
+    author_requires 'Test::NoTabs';
+    author_requires 'Test::Pod' => '1.14';
+    author_requires 'Test::Pod::Coverage' => '1.04';
+    author_requires 'Test::WWW::Mechanize::Catalyst' => '0.51';
+    author_requires 'HTML::TreeBuilder::XPath';
+    author_requires 'WWW::Mechanize::TreeBuilder';
+}
+
 install_script glob('script/*.pl');
 auto_install;
 
diff --git a/README b/README
index 86853b2..dcdf72f 100644 (file)
--- a/README
+++ b/README
@@ -41,7 +41,7 @@ GETTING GITALIST
 
     The canonical repository for the master branch is:
 
-        it://git.shadowcat.co.uk/catagits/Gitalist.git
+        git://git.shadowcat.co.uk/catagits/Gitalist.git
 
     Gitalist is also mirrored to github, and a number of people have active
     forks with branches and/or new features in the master branch.
@@ -83,13 +83,13 @@ INITIAL CONFIGURATION
 
     Alternatively, if you only want to set a repository directory and are
     otherwise happy with the default configuration, then you can set the
-    "GITALIST_REPOS_DIR" environment variable, or pass the "--repos_dir"
-    flag to any of the scripts.
+    "GITALIST_REPO_DIR" environment variable, or pass the "--repos_dir" flag
+    to any of the scripts.
 
-        GITALIST_REPOS_DIR=/home/myuser/code/git gitalist_server.pl
+        GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl
         gitalist_server.pl --repos_dir home/myuser/code/git
 
-    The "GITALIST_REPOS_DIR" environment variable will override the
+    The "GITALIST_REPO_DIR" environment variable will override the
     repository directory set in configuration, and will itself be overridden
     by he "--repos_dir" flag.
 
index 41bfd04..1be66c8 100644 (file)
@@ -11,9 +11,10 @@ use Catalyst qw/
                 Unicode::Encoding
                 Static::Simple
                 StackTrace
+                SubRequest
 /;
 
-our $VERSION = '0.000005';
+our $VERSION = '0.000006_01';
 $VERSION = eval $VERSION;
 
 __PACKAGE__->config(
@@ -24,20 +25,35 @@ __PACKAGE__->config(
 
 __PACKAGE__->setup();
 
+after prepare_path => sub {
+    my ($ctx) = @_;
+    if ($ctx->req->param('a')) {
+        $ctx->request->uri->path('/legacy' . $ctx->request->uri->path);
+    }
+};
+
 around uri_for => sub {
   my ($orig, $c) = (shift, shift);
-  my $repository_name = $c->stash->{'Repository'} && $c->stash->{'Repository'}->name;
-  my $hash = ref($_[-1]) eq 'HASH' ? pop @_ : {};
-  my $params = Catalyst::Utils::merge_hashes(
-    { p => $hash->{p} || $repository_name },
-    $hash,
-  );
-  delete $params->{p} unless defined $params->{p} && length $params->{p};
-  (my $uri = $c->$orig(@_, $params))
-    =~ tr[&][;];
+  my $uri = $c->$orig(@_);
+  $$uri =~ tr[&][;] if defined $uri;
   return $uri;
 };
 
+around uri_for_action => sub {
+  my ($orig, $c) = (shift, shift);
+  my $uri = $c->$orig(@_);
+  $$uri =~ s[/fragment\b][] if defined $uri;
+  return $uri;
+};
+
+sub uri_with {
+  my ($self, @args) = @_;
+  my $uri = $self->request->uri_with(@args);
+  # Wow this awful.
+  $uri =~ s[/fragment\b][];
+  return $uri;  
+}
+
 1;
 
 __END__
@@ -90,7 +106,7 @@ Alternatively, you can get Gitalist using git.
 
 The canonical repository for the master branch is:
 
-    it://git.shadowcat.co.uk/catagits/Gitalist.git
+    git://git.shadowcat.co.uk/catagits/Gitalist.git
 
 Gitalist is also mirrored to github, and a number of people have active forks
 with branches and/or new features in the master branch.
@@ -130,13 +146,13 @@ You can then start the Gitalist demo server by setting C<< GITALIST_CONFIG >>. F
     GITALIST_CONFIG=/usr/local/etc/gitalist.conf gitalist_server.pl
 
 Alternatively, if you only want to set a repository directory and are otherwise happy with
-the default configuration, then you can set the C<< GITALIST_REPOS_DIR >> environment
+the default configuration, then you can set the C<< GITALIST_REPO_DIR >> environment
 variable, or pass the C<< --repos_dir >> flag to any of the scripts.
 
-    GITALIST_REPOS_DIR=/home/myuser/code/git gitalist_server.pl
+    GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl
     gitalist_server.pl --repos_dir home/myuser/code/git
 
-The C<< GITALIST_REPOS_DIR >> environment variable will override the repository directory set
+The C<< GITALIST_REPO_DIR >> environment variable will override the repository directory set
 in configuration, and will itself be overridden by he C<< --repos_dir >> flag.
 
 =head1 RUNNING
diff --git a/lib/Gitalist/ActionRole/FilenameArgs.pm b/lib/Gitalist/ActionRole/FilenameArgs.pm
new file mode 100644 (file)
index 0000000..f0ed177
--- /dev/null
@@ -0,0 +1,14 @@
+package Gitalist::ActionRole::FilenameArgs;
+use Moose::Role;
+use namespace::autoclean;
+
+requires 'execute';
+
+before 'execute' => sub {
+    my ($self, $controller, $c, @args) = @_;
+    $c->stash->{filename} = join('/', @args) || ''
+        unless $c->stash->{filename};
+};
+
+1;
+
diff --git a/lib/Gitalist/Controller.pm b/lib/Gitalist/Controller.pm
new file mode 100644 (file)
index 0000000..9839ac3
--- /dev/null
@@ -0,0 +1,7 @@
+package Gitalist::Controller;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller::ActionRole' }
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/Fragment.pm b/lib/Gitalist/Controller/Fragment.pm
new file mode 100644 (file)
index 0000000..5a4bfd2
--- /dev/null
@@ -0,0 +1,32 @@
+package Gitalist::Controller::Fragment;
+
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+
+sub base : Chained('/base') PathPart('fragment') CaptureArgs(0) {
+    my ($self, $c) = @_;
+    $c->stash(no_wrapper => 1);
+}
+
+sub collectionofrepositories : Chained('base') Args(0) {
+    my ($self, $c) = @_;
+    my @list = @{ $c->model()->repositories };
+    die 'No repositories found in '. $c->model->repo_dir
+      unless @list;
+
+    my $search = $c->req->param('s') || '';
+    if($search) {
+      @list = grep {
+           index($_->name, $search) > -1
+        or ( $_->description !~ /^Unnamed repository/ and index($_->description, $search) > -1 )
+      } @list
+    }
+
+    $c->stash(
+      repositories    => \@list,
+    );
+}
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/Fragment/Ref.pm b/lib/Gitalist/Controller/Fragment/Ref.pm
new file mode 100644 (file)
index 0000000..b4a2181
--- /dev/null
@@ -0,0 +1,123 @@
+package Gitalist::Controller::Fragment::Ref;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+with qw/
+    Gitalist::URIStructure::Ref
+    Gitalist::URIStructure::Fragment::WithLog
+/;
+
+sub base : Chained('/fragment/repository/find') PathPart('') CaptureArgs(0) {}
+
+sub _diff {
+    my ($self, $c) = @_;
+    my $commit = $c->stash->{Commit};
+    my %filename = $c->stash->{filename} ? (filename => $c->stash->{filename}) : ();
+    my($tree, $patch) = $c->stash->{Repository}->diff(
+        commit => $commit,
+        parent => $c->stash->{parent},
+        patch  => 1,
+        %filename,
+    );
+    $c->stash(
+      diff_tree => $tree,
+      diff      => $patch,
+      # XXX Hack hack hack, see View::SyntaxHighlight
+      blobs     => [map $_->{diff}, @$patch],
+      language  => 'Diff',
+      %filename,
+    );
+}
+
+after diff_fancy => sub {
+    my ($self, $c) = @_;
+    $self->_diff($c);
+    $c->forward('View::SyntaxHighlight');
+};
+
+after diff_plain => sub {
+    my ($self, $c) = @_;
+    $self->_diff($c);
+};
+
+after tree => sub {
+    my ( $self, $c ) = @_;
+    my $repository = $c->stash->{Repository};
+    my $commit  = $c->stash->{Commit};
+    my $tree    = $c->stash->{filename}
+      ? $repository->get_object($repository->hash_by_path($commit->sha1, $c->stash->{filename}))
+      : $repository->get_object($commit->tree_sha1)
+    ;
+    $c->stash(
+        tree      => $tree,
+        tree_list => [$repository->list_tree($tree->sha1)],
+        path      => $c->stash->{filename}, # FIXME?
+    );
+};
+
+after blame => sub {
+    my($self, $c) = @_;
+
+    my $repository = $c->stash->{Repository};
+                                                      # WTF?
+    my $blame = $c->stash->{Commit}->blame($c->stash->{filename}, $c->stash->{Commit}->sha1);
+    $c->stash(
+        blame    => $blame,
+        # XXX Hack hack hack, see View::SyntaxHighlight
+        language => ($c->stash->{filename} =~ /\.p[lm]$/i ? 'Perl' : ''),
+        blob     => join("\n", map $_->{line}, @$blame),
+    );
+
+    $c->forward('View::SyntaxHighlight')
+        unless $c->stash->{no_wrapper};
+};
+
+=head2 blob
+
+The blob action i.e the contents of a file.
+
+=cut
+
+after blob => sub {
+    my ( $self, $c ) = @_;
+    $c->stash(
+        # XXX Hack hack hack, see View::SyntaxHighlight
+        language => ($c->stash->{filename} =~ /\.p[lm]$/i ? 'Perl' : ''),
+    );
+
+    $c->forward('View::SyntaxHighlight')
+        unless $c->stash->{no_wrapper};
+};
+
+after history => sub {
+    my ($self, $c) = @_;
+    my $repository  = $c->stash->{Repository};
+    my $filename    = $c->stash->{filename};
+
+    my %logargs = (
+       sha1   => $c->stash->{Commit}->sha1,
+       count  => 25, #Gitalist->config->{paging}{log} || 25,
+       ($filename ? (file => $filename) : ())
+    );
+
+    my $file = $repository->get_object(
+        $repository->hash_by_path(
+            $repository->head_hash,
+            $filename
+        )
+    );
+
+    my $page = $c->req->param('pg') || 0;
+    $logargs{skip} = $c->req->param('pg') * $logargs{count}
+        if $c->req->param('pg');
+
+    $c->stash(
+       log_lines => [$repository->list_revs(%logargs)],
+       refs      => $repository->references,
+       filename  => $filename,
+       filetype  => $file->type,
+    );
+};
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/Fragment/Repository.pm b/lib/Gitalist/Controller/Fragment/Repository.pm
new file mode 100644 (file)
index 0000000..b73c1a5
--- /dev/null
@@ -0,0 +1,33 @@
+package Gitalist::Controller::Fragment::Repository;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+with qw/
+    Gitalist::URIStructure::Repository
+    Gitalist::URIStructure::Fragment::WithLog
+/;
+
+sub base : Chained('/fragment/base') PathPart('') CaptureArgs(0) {}
+
+after heads => sub {
+    my ($self, $c) = @_;
+    $c->stash(
+        heads => $c->stash->{Repository}->heads,
+    );
+};
+
+=head2 tags
+
+The current list of tags in the repo.
+
+=cut
+
+after tags => sub {
+  my ( $self, $c ) = @_;
+  $c->stash(
+    tags   => $c->stash->{Repository}->tags,
+  );
+};
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/LegacyURI.pm b/lib/Gitalist/Controller/LegacyURI.pm
new file mode 100644 (file)
index 0000000..a4c4420
--- /dev/null
@@ -0,0 +1,146 @@
+package Gitalist::Controller::LegacyURI;
+use Moose;
+use Moose::Autobox;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+
+my %LEGACY_DISPATCH = (
+    opml                     => sub { '/opml/opml' },
+    project_index            => sub { '/legacyuri/project_index' },
+    '(?:summary|heads|tags)' => sub {
+        my($c, $action, $repos) = @_;
+        return "/repository/$action", [$repos];
+    },
+    blob => sub {
+        my($c, $action, $repos) = @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h');
+        return '/ref/blob', [$repos, $ref], $c->req->param('f');
+    },
+    blob_plain               => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h');
+        return '/ref/raw', [$repos, $ref], $c->req->param('f');
+    },
+    blobdiff                 => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref     = $c->req->param('hb')  || $c->req->param('h');
+        my $compare = $c->req->param('hbp') || $c->req->param('hp');
+        return '/ref/diff_fancy', [$repos, $ref], $compare, $c->req->param('f');
+    },
+    blobdiff_plain           => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref     = $c->req->param('hb')  || $c->req->param('h');
+        my $compare = $c->req->param('hbp') || $c->req->param('hp');
+        return '/ref/diff_plain', [$repos, $ref], $compare, $c->req->param('f');
+    },
+    commit                   => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/commit', [$repos, $ref];
+    },
+    # XXX These can be consolidated with the blob equivalents.
+    commitdiff               => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref     = $c->req->param('hb')  || $c->req->param('h') || 'HEAD';
+        my $compare = $c->req->param('hbp') || $c->req->param('hp');
+        return '/ref/diff_fancy', [$repos, $ref], $compare, $c->req->param('f');
+    },
+    commitdiff_plain         => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref     = $c->req->param('hb')  || $c->req->param('h');
+        my $compare = $c->req->param('hbp') || $c->req->param('hp');
+        return '/ref/diff_plain', [$repos, $ref || 'HEAD'], $compare, $c->req->param('f');
+    },
+    history                  => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref     = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/history', [$repos, $ref], $c->req->param('f');
+    },
+    log                      => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/longlog', [$repos, $ref];
+    },
+    patch                    => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/patch', [$repos, $ref];
+    },
+    patches                  => sub {
+        my($c, $action, $repos) = @_;
+        # XXX Is the arg there wrong? It's just copying G::C::R::patch.
+        return '/ref/patches', [$repos, $c->req->param('h') || 'HEAD'], 1;
+    },
+    search_help              => sub {
+        return '/search_help';
+    },
+    shortlog                 => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/shortlog', [$repos, $ref];
+    },
+    snapshot                 => sub {
+        my($c, $action, $repos) =  @_;
+        my $ref = $c->req->param('h') || 'HEAD';
+        return '/ref/snapshot', [$repos, $ref], $c->req->param('sf');
+    },
+    tree                     => sub {
+        my($c, $action, $repos) = @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD';
+        return '/ref/tree', [$repos, $ref], $c->req->param('f');
+    },
+    '(?:atom|rss)'           => sub {
+        my($c, $action, $repos) =  @_;
+        # XXX No support for arbitrary branches or merges/nomerges option :(
+        return "/repository/$action", [$repos], $c->req->param('f');
+    },
+    blame                    => sub {
+        my($c, $action, $repos) = @_;
+        my $ref = $c->req->param('hb') || $c->req->param('h');
+        return '/ref/blame', [$repos, $ref], $c->req->param('f');
+    },
+);
+
+sub _legacy_uri {
+    my($self, $c, $repos, $action) = @_;
+
+    return
+        unless $action;
+
+    my @result  = grep { $action =~ /^$_$/ } keys %LEGACY_DISPATCH;
+    die "Matched too many actions for '$a' - @result"
+        if @result > 1;
+
+    return
+        unless $result[0];
+
+    my($real_action, $captures, @args) = $LEGACY_DISPATCH{$result[0]}->($c, $action, $repos);
+
+    return $real_action, $captures || [], grep defined, @args;
+}
+
+sub handler : Chained('/base') PathPart('legacy') Args() {
+    my ( $self, $c, $repos ) = @_;
+
+    $repos ||= $c->req->param('p');
+
+    my ($action, $captures, @args) = $self->_legacy_uri($c, $repos, $c->req->param('a'));
+
+    die("Not supported")
+        unless $action;
+
+    $c->res->redirect($c->uri_for_action($action, $captures, @args));
+    $c->res->status(301);
+}
+
+sub project_index : Chained('/base') Args(0) {
+      my ( $self, $c ) = @_;
+
+      $c->response->content_type('text/plain');
+      $c->response->body(
+          join "\n", map $_->name, $c->model()->repositories->flatten
+      ) or die 'No repositories found in '. $c->model->repo_dir;
+}
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/OPML.pm b/lib/Gitalist/Controller/OPML.pm
new file mode 100644 (file)
index 0000000..ece56f3
--- /dev/null
@@ -0,0 +1,29 @@
+package Gitalist::Controller::OPML;
+
+use Moose;
+use Moose::Autobox;
+use DateTime;
+use Sys::Hostname qw/hostname/;
+use XML::OPML::SimpleGen;
+
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+
+sub opml : Chained('/base') Args(0) {
+    my ($self, $c) = @_;
+
+    my $opml = XML::OPML::SimpleGen->new();
+
+    $c->stash(
+        title => lc(hostname()) . ' - ' . blessed($c)->config->{name},
+        Repositories => $c->model()->repositories,
+        now => DateTime->now,
+        template => 'opml.tt2',
+        no_wrapper => 1,
+    );
+
+    $c->response->content_type('application/rss');
+}
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/Ref.pm b/lib/Gitalist/Controller/Ref.pm
new file mode 100644 (file)
index 0000000..e43bd98
--- /dev/null
@@ -0,0 +1,74 @@
+package Gitalist::Controller::Ref;
+
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+with 'Gitalist::URIStructure::Ref';
+
+sub base : Chained('/repository/find') PathPart('') CaptureArgs(0) {}
+
+after commit => sub {
+  my($self, $c) = @_;
+
+  $c->stash->{diff_tree} = ($c->stash->{Repository}->diff(
+    commit => $c->stash->{Commit},
+  ))[0];
+};
+
+sub raw : Chained('find') Does('FilenameArgs') Args() {
+    my ($self, $c) = @_;
+    $c->forward('find_blob');
+
+    $c->response->content_type('text/plain; charset=utf-8');
+    $c->response->body(delete $c->stash->{blob});
+}
+
+=head2 snapshot
+
+Provides a snapshot of a given commit.
+
+=cut
+
+sub snapshot : Chained('find') PathPart('snapshot') Args() {
+    my ($self, $c, $format) = @_;
+    $format ||= 'tgz';
+    my @snap = $c->stash->{Repository}->snapshot(
+        sha1 => $c->stash->{Commit}->sha1,
+        format => $format
+    );
+    $c->response->status(200);
+    $c->response->headers->header( 'Content-Disposition' =>
+                                       "attachment; filename=$snap[0]");
+    $c->response->body($snap[1]);
+}
+
+=head2 patch
+
+A raw patch for a given commit.
+
+=cut
+
+sub patch : Chained('find') Args(0) {
+    my ($self, $c) = @_;
+    $c->detach('patches', [1]);
+}
+
+=head2 patches
+
+The patcheset for a given commit ???
+
+=cut
+
+sub patches : Chained('find') Args(1) {
+    my ($self, $c, $count) = @_;
+    $count ||= Gitalist->config->{patches}{max};
+    my $commit = $c->stash->{Commit};
+    my $parent = $c->req->param('hp') || undef; # FIXME
+    my $patch = $commit->get_patch( $parent, $count );
+    $c->response->body($patch);
+    $c->response->content_type('text/plain');
+    $c->response->status(200);
+}
+
+__PACKAGE__->meta->make_immutable;
diff --git a/lib/Gitalist/Controller/Repository.pm b/lib/Gitalist/Controller/Repository.pm
new file mode 100644 (file)
index 0000000..0aba16c
--- /dev/null
@@ -0,0 +1,131 @@
+package Gitalist::Controller::Repository;
+use Moose;
+use XML::Atom::Feed;
+use XML::Atom::Entry;
+use XML::RSS;
+use Sys::Hostname qw/hostname/;
+use namespace::autoclean;
+
+BEGIN { extends 'Gitalist::Controller' }
+with 'Gitalist::URIStructure::Repository';
+
+sub base : Chained('/base') PathPart('') CaptureArgs(0) {}
+
+=head2 search
+
+The action for the search form.
+
+=cut
+
+sub search : Chained('find') Args(0) {
+  my($self, $c) = @_;
+  my $repository = $c->stash->{Repository};
+  # Lifted from /shortlog.
+  my %logargs = (
+    sha1   => $repository->head_hash,
+#    count  => Gitalist->config->{paging}{log},
+#    ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
+    search => {
+      type   => $c->req->param('type'),
+      text   => $c->req->param('text'),
+      regexp => $c->req->param('regexp') || 0,
+    },
+  );
+
+  $c->stash(
+#      commit  => $commit,
+      results => [$repository->list_revs(%logargs)],
+         # This could be added - page      => $page,
+  );
+}
+
+=head2 tree
+
+Provide a simple redirect to C</ref/tree>.
+
+=cut
+
+sub tree : Chained('find') Args(0) {
+    my($self, $c) = @_;
+    $c->res->redirect($c->uri_for_action('/ref/tree', [$c->stash->{Repository}->name, 'HEAD']));
+    $c->res->status(301);
+}
+
+=head2 atom
+
+Provides an atom feed for a given repository.
+
+=cut
+
+sub atom : Chained('find') Does('FilenameArgs') Args() {
+    my ($self, $c) = @_;
+
+    my $host = lc hostname();
+    $c->stash(
+        title => $host . ' - ' . Gitalist->config->{name},
+        updated => DateTime->now
+    );
+
+    my $repository = $c->stash->{Repository};
+    my %logargs = (
+        sha1     => $repository->head_hash,
+        count    => Gitalist->config->{paging}{log} || 25,
+        ($c->stash->{filename} ? (file => $c->stash->{filename}) : ()),
+    );
+
+    my @revs;
+    my $mk_title = $c->stash->{short_cmt};
+    for my $commit ($repository->list_revs(%logargs)) {
+        my $entry = {};
+        $entry->{title} = $mk_title->($commit->comment);
+        $entry->{id} = $c->uri_for_action('/ref/commit', [$repository->name, $commit->sha1]);
+        # XXX FIXME Needs work ...
+        $entry->{content} = $commit->comment;
+        push(@revs, $entry);
+    }
+    $c->stash(
+        Commits => \@revs,
+        no_wrapper => 1,
+    );
+    $c->response->content_type('application/atom+xml');
+}
+
+=head2 rss
+
+Provides an RSS feed for a given repository.
+
+=cut
+
+sub rss : Chained('find') Does('FilenameArgs') Args() {
+  my ($self, $c) = @_;
+
+  my $repository = $c->stash->{Repository};
+
+  $c->stash(
+    title          => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
+    language       => 'en',
+    pubDate        => DateTime->now,
+    lastBuildDate  => DateTime->now,
+    no_wrapper     => 1,
+  );
+
+  my %logargs = (
+      sha1   => $repository->head_hash,
+      count  => Gitalist->config->{paging}{log} || 25,
+      ($c->stash->{filename} ? (file => $c->stash->{filename}) : ()),
+  );
+  my @revs;
+  my $mk_title = $c->stash->{short_cmt};
+  for my $commit ($repository->list_revs(%logargs)) {
+    # XXX FIXME Needs work ....
+    push(@revs, {
+        title       => $mk_title->($commit->comment),
+        permaLink   => $c->uri_for_action('/ref/commit', [$repository->name, $commit->sha1]),
+        description => $commit->comment,
+    });
+  }
+  $c->stash(Commits => \@revs);
+  $c->response->content_type('application/rss+xml');
+}
+
+__PACKAGE__->meta->make_immutable;
index b2e60be..14ef8ee 100644 (file)
@@ -2,645 +2,39 @@ package Gitalist::Controller::Root;
 
 use Moose;
 use Moose::Autobox;
-use Sys::Hostname ();
-use XML::Atom::Feed;
-use XML::Atom::Entry;
-use XML::RSS;
-use XML::OPML::SimpleGen;
-
+use Digest::MD5 qw(md5_hex);
 use Gitalist::Utils qw/ age_string /;
 
 use namespace::autoclean;
 
-BEGIN { extends 'Catalyst::Controller' }
+BEGIN { extends 'Gitalist::Controller' }
 
-__PACKAGE__->config->{namespace} = '';
+__PACKAGE__->config(namespace => '');
 
 sub root : Chained('/') PathPart('') CaptureArgs(0) {}
 
-sub _get_object {
-  my($self, $c, $haveh) = @_;
-
-  my $h = $haveh || $c->req->param('h') || '';
-  my $f = $c->req->param('f');
-
-  my $m = $c->stash->{Repository};
-  my $pd = $m->path;
-
-  # Either use the provided h(ash) parameter, the f(ile) parameter or just use HEAD.
-  my $hash = ($h =~ /[^a-f0-9]/ ? $m->head_hash($h) : $h)
-          || ($f && $m->hash_by_path($f))
-          || $m->head_hash
-          # XXX This could definitely use more context.
-          || Carp::croak("Couldn't find a hash for the commit object!");
-
-  my $obj = $m->get_object($hash)
-    or Carp::croak("Couldn't find a object for '$hash' in '$pd'!");
-
-  return $obj;
-}
-
 sub index : Chained('base') PathPart('') Args(0) {
-  my ( $self, $c ) = @_;
-
-  $c->detach($c->req->param('a'))
-    if $c->req->param('a');
-
-  my @list = @{ $c->model()->repositories };
-  die 'No repositories found in '. $c->model->repo_dir
-    unless @list;
-
-  my $search = $c->req->param('s') || '';
-  if($search) {
-    @list = grep {
-         index($_->name, $search) > -1
-      or ( $_->description !~ /^Unnamed repository/ and index($_->description, $search) > -1 )
-    } @list
-  }
-
-  $c->stash(
-    search_text => $search,
-    repositories    => \@list,
-    action      => 'index',
-  );
-}
-
-# FIXME - WTF is this for?
-sub repository_index : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-
-  my @list = @{ $c->model()->repositories };
-  die 'No repositories found in '. $c->model->repo_dir
-    unless @list;
-
-  $c->response->content_type('text/plain');
-  $c->response->body(
-    join "\n", map $_->name, @list
-  );
-  $c->response->status(200);
-}
-# FIXME - maintain compatibility with previous URI
-sub project_index : Chained('base') Args(0) {
-    my ( $self, $c) = @_;
-    $c->detach('repository_index');
-}
-
-=head2 summary
-
-A summary of what's happening in the repo.
-
-=cut
-
-sub summary : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  $c->detach('error_404') unless $repository;
-  my $commit = $self->_get_object($c);
-  my @heads  = @{$repository->heads};
-  my $maxitems = Gitalist->config->{paging}{summary} || 10;
-  $c->stash(
-    commit    => $commit,
-    log_lines => [$repository->list_revs(
-        sha1 => $commit->sha1,
-        count => $maxitems,
-    )],
-    refs      => $repository->references,
-    heads     => [ @heads[0 .. ($#heads < $maxitems ? $#heads : $maxitems)] ],
-    action    => 'summary',
-  );
-}
-
-=head2 heads
-
-The current list of heads (aka branches) in the repo.
-
-=cut
-
-sub heads : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  $c->stash(
-    commit => $self->_get_object($c),
-    heads  => $repository->heads,
-    action => 'heads',
-  );
-}
-
-=head2 tags
-
-The current list of tags in the repo.
-
-=cut
-
-sub tags : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  $c->stash(
-    commit => $self->_get_object($c),
-    tags   => $repository->tags,
-    action => 'tags',
-  );
-}
-
-sub blame : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  my $repository = $c->stash->{Repository};
-  my $h  = $c->req->param('h')
-       || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
-       || die "No file or sha1 provided.";
-  my $hb = $c->req->param('hb')
-       || $repository->head_hash
-       || die "Couldn't discern the corresponding head.";
-  my $filename = $c->req->param('f') || '';
-
-  my $blame = $repository->get_object($hb)->blame($filename, $h);
-  $c->stash(
-    blame    => $blame,
-    head     => $repository->get_object($hb),
-    filename => $filename,
-
-    # XXX Hack hack hack, see View::SyntaxHighlight
-    language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
-    blob     => join("\n", map $_->{line}, @$blame),
-  );
-
-  $c->forward('View::SyntaxHighlight')
-    unless $c->stash->{no_wrapper};
-}
-
-sub _blob_objs {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  my $h  = $c->req->param('h')
-       || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
-       || die "No file or sha1 provided.";
-  my $hb = $c->req->param('hb')
-       || $repository->head_hash
-       || die "Couldn't discern the corresponding head.";
-
-  my $filename = $c->req->param('f') || '';
-
-  my $blob = $repository->get_object($h);
-  $blob = $repository->get_object(
-    $repository->hash_by_path($h || $hb, $filename)
-  ) if $blob->type ne 'blob';
-
-  return $blob, $repository->get_object($hb), $filename;
-}
-
-=head2 blob
-
-The blob action i.e the contents of a file.
-
-=cut
-
-sub blob : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-
-  my($blob, $head, $filename) = $self->_blob_objs($c);
-  $c->stash(
-    blob     => $blob->content,
-    head     => $head,
-    filename => $filename,
-    # XXX Hack hack hack, see View::SyntaxHighlight
-    language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
-    action   => 'blob',
-  );
-
-  $c->forward('View::SyntaxHighlight')
-    unless $c->stash->{no_wrapper};
-}
-
-=head2 blob_plain
-
-The plain text version of blob, where file is rendered as is.
-
-=cut
-
-sub blob_plain : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  my($blob) = $self->_blob_objs($c);
-  $c->response->content_type('text/plain; charset=utf-8');
-  $c->response->body($blob->content);
-  $c->response->status(200);
-}
-
-=head2 blobdiff_plain
-
-The plain text version of blobdiff.
-
-=cut
-
-sub blobdiff_plain : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  $c->stash(no_wrapper => 1);
-  $c->response->content_type('text/plain; charset=utf-8');
-
-  $c->forward('blobdiff');
-}
-
-=head2 blobdiff
-
-Exposes a given diff of a blob.
-
-=cut
-
-sub blobdiff : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $commit = $self->_get_object($c, $c->req->param('hb'));
-  my $filename = $c->req->param('f')
-              || croak("No file specified!");
-  my($tree, $patch) = $c->stash->{Repository}->diff(
-    commit => $commit,
-    patch  => 1,
-    parent => $c->req->param('hpb') || undef,
-    file   => $filename,
-  );
-  $c->stash(
-    commit    => $commit,
-    diff      => $patch,
-    filename  => $filename,
-    # XXX Hack hack hack, see View::SyntaxHighlight
-    blobs     => [$patch->[0]->{diff}],
-    language  => 'Diff',
-    action    => 'blobdiff',
-  );
-
-  $c->forward('View::SyntaxHighlight')
-    unless $c->stash->{no_wrapper};
-}
-
-=head2 commit
-
-Exposes a given commit.
-
-=cut
-
-sub commit : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  my $commit = $self->_get_object($c);
-  $c->stash(
-      commit      => $commit,
-      diff_tree   => ($repository->diff(commit => $commit))[0],
-      refs      => $repository->references,
-      action      => 'commit',
-  );
-}
-
-=head2 commitdiff
-
-Exposes a given diff of a commit.
-
-=cut
-
-sub commitdiff : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $commit = $self->_get_object($c);
-  my($tree, $patch) = $c->stash->{Repository}->diff(
-      commit => $commit,
-      parent => $c->req->param('hp') || undef,
-      patch  => 1,
-  );
-  $c->stash(
-    commit    => $commit,
-    diff_tree => $tree,
-    diff      => $patch,
-    # XXX Hack hack hack, see View::SyntaxHighlight
-    blobs     => [map $_->{diff}, @$patch],
-    language  => 'Diff',
-    action    => 'commitdiff',
-  );
-
-  $c->forward('View::SyntaxHighlight')
-    unless $c->stash->{no_wrapper};
-}
-
-sub commitdiff_plain : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  $c->stash(no_wrapper => 1);
-  $c->response->content_type('text/plain; charset=utf-8');
-
-  $c->forward('commitdiff');
-}
-
-=head2 shortlog
-
-Expose an abbreviated log of a given sha1.
-
-=cut
-
-sub shortlog : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-
-  my $repository  = $c->stash->{Repository};
-  my $commit   = $self->_get_object($c, $c->req->param('hb'));
-  my $filename = $c->req->param('f') || '';
-
-  my %logargs = (
-      sha1   => $commit->sha1,
-      count  => Gitalist->config->{paging}{log} || 25,
-      ($filename ? (file => $filename) : ())
-  );
-
-  my $page = $c->req->param('pg') || 0;
-  $logargs{skip} = $c->req->param('pg') * $logargs{count}
-    if $c->req->param('pg');
-
-  $c->stash(
-      commit    => $commit,
-      log_lines => [$repository->list_revs(%logargs)],
-      refs      => $repository->references,
-      page      => $page,
-      filename  => $filename,
-      action    => 'shortlog',
-  );
-}
-
-=head2 log
-
-Calls shortlog internally. Perhaps that should be reversed ...
-
-=cut
-
-sub log : Chained('base') Args(0) {
-    $_[0]->shortlog($_[1]);
-    $_[1]->stash->{action} = 'log';
-}
-
-# For legacy support.
-sub history : Chained('base') Args(0) {
     my ( $self, $c ) = @_;
-    $self->shortlog($c);
-    my $repository = $c->stash->{Repository};
-    my $file = $repository->get_object(
-        $repository->hash_by_path(
-            $repository->head_hash,
-            $c->stash->{filename}
-        )
-    );
-     $c->stash( action => 'history',
-               filetype => $file->type,
-           );
-}
-
-=head2 tree
-
-The tree of a given commit.
-
-=cut
-
-sub tree : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my $repository = $c->stash->{Repository};
-  my $commit  = $self->_get_object($c, $c->req->param('hb'));
-  my $filename = $c->req->param('f') || '';
-  my $tree    = $filename
-    ? $repository->get_object($repository->hash_by_path($commit->sha1, $filename))
-    : $repository->get_object($commit->tree_sha1)
-  ;
-  $c->stash(
-      commit    => $commit,
-      tree      => $tree,
-      tree_list => [$repository->list_tree($tree->sha1)],
-      path      => $c->req->param('f') || '',
-      action    => 'tree',
-  );
-}
-
-=head2 reflog
-
-Expose the local reflog. This may go away.
-
-=cut
-
-sub reflog : Chained('base') Args(0) {
-  my ( $self, $c ) = @_;
-  my @log = $c->stash->{Repository}->reflog(
-      '--since=yesterday'
-  );
-
-  $c->stash(
-      log    => \@log,
-      action => 'reflog',
-  );
-}
-
-=head2 search
-
-The action for the search form.
-
-=cut
-
-sub search : Chained('base') Args(0) {
-  my($self, $c) = @_;
-  my $repository = $c->stash->{Repository};
-  my $commit  = $self->_get_object($c);
-  # Lifted from /shortlog.
-  my %logargs = (
-    sha1   => $commit->sha1,
-    count  => Gitalist->config->{paging}{log},
-    ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
-    search => {
-      type   => $c->req->param('type'),
-      text   => $c->req->param('text'),
-      regexp => $c->req->param('regexp') || 0,
-    },
-  );
-
-  $c->stash(
-      commit  => $commit,
-      results => [$repository->list_revs(%logargs)],
-      action  => 'search',
-         # This could be added - page      => $page,
-  );
-}
-
-=head2 search_help
-
-Provides some help for the search form.
-
-=cut
-
-sub search_help : Chained('base') Args(0) {
-    my ($self, $c) = @_;
-    $c->stash(template => 'search_help.tt2');
-}
-
-=head2 atom
-
-Provides an atom feed for a given repository.
-
-=cut
-
-sub atom : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  my $feed = XML::Atom::Feed->new;
-
-  my $host = lc Sys::Hostname::hostname();
-  $feed->title($host . ' - ' . Gitalist->config->{name});
-  $feed->updated(~~DateTime->now);
-
-  my $repository = $c->stash->{Repository};
-  my %logargs = (
-      sha1   => $repository->head_hash,
-      count  => Gitalist->config->{paging}{log} || 25,
-      ($c->req->param('f') ? (file => $c->req->param('f')) : ())
-  );
-
-  my $mk_title = $c->stash->{short_cmt};
-  for my $commit ($repository->list_revs(%logargs)) {
-    my $entry = XML::Atom::Entry->new;
-    $entry->title( $mk_title->($commit->comment) );
-    $entry->id($c->uri_for('commit', {h=>$commit->sha1}));
-    # XXX Needs work ...
-    $entry->content($commit->comment);
-    $feed->add_entry($entry);
-  }
-
-  $c->response->body($feed->as_xml);
-  $c->response->content_type('application/atom+xml');
-  $c->response->status(200);
+    $c->stash( search_text => $c->req->param('s') || '' ) # FIXME - XSS?
 }
 
-=head2 rss
-
-Provides an RSS feed for a given repository.
-
-=cut
-
-sub rss : Chained('base') Args(0) {
-  my ($self, $c) = @_;
-
-  my $repository = $c->stash->{Repository};
-
-  my $rss = XML::RSS->new(version => '2.0');
-  $rss->channel(
-    title          => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
-    link           => $c->uri_for('summary', {p=>$repository->name}),
-    language       => 'en',
-    description    => $repository->description,
-    pubDate        => DateTime->now,
-    lastBuildDate  => DateTime->now,
-  );
-
-  my %logargs = (
-      sha1   => $repository->head_hash,
-      count  => Gitalist->config->{paging}{log} || 25,
-      ($c->req->param('f') ? (file => $c->req->param('f')) : ())
-  );
-  my $mk_title = $c->stash->{short_cmt};
-  for my $commit ($repository->list_revs(%logargs)) {
-    # XXX Needs work ....
-    $rss->add_item(
-        title       => $mk_title->($commit->comment),
-        permaLink   => $c->uri_for(commit => {h=>$commit->sha1}),
-        description => $commit->comment,
-    );
-  }
-
-  $c->response->body($rss->as_string);
-  $c->response->content_type('application/rss+xml');
-  $c->response->status(200);
-}
-
-sub opml : Chained('base') Args(0) {
-  my($self, $c) = @_;
-
-  my $opml = XML::OPML::SimpleGen->new();
-
-  $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name});
-
-  my @list = @{ $c->model()->repositories };
-  die 'No repositories found in '. $c->model->repo_dir
-    unless @list;
-
-  for my $proj ( @list ) {
-    $opml->insert_outline(
-      text   => $proj->name. ' - '. $proj->description,
-      xmlUrl => $c->uri_for(rss => {p => $proj->name}),
-    );
-  }
-
-  $c->response->body($opml->as_string);
-  $c->response->content_type('application/rss');
-  $c->response->status(200);
-}
-
-=head2 patch
-
-A raw patch for a given commit.
-
-=cut
-
-sub patch : Chained('base') Args(0) {
-    my ($self, $c) = @_;
-    $c->detach('patches', [1]);
-}
-
-=head2 patches
-
-The patcheset for a given commit ???
-
-=cut
-
-sub patches : Chained('base') Args(0) {
-    my ($self, $c, $count) = @_;
-    $count ||= Gitalist->config->{patches}{max};
-    my $commit = $self->_get_object($c);
-    my $parent = $c->req->param('hp') || undef;
-    my $patch = $commit->get_patch( $parent, $count );
-    $c->response->body($patch);
-    $c->response->content_type('text/plain');
-    $c->response->status(200);
-}
-
-=head2 snapshot
-
-Provides a snapshot of a given commit.
-
-=cut
+# XXX Fragile much?
+sub css : Chained('/root') PathPart('core.css') Args(0) {
+    my ( $self, $c ) = @_;
 
-sub snapshot : Chained('base') Args(0) {
-    my ($self, $c) = @_;
-    my $format = $c->req->param('sf') || 'tgz';
-    die unless $format;
-    my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1;
-    my @snap = $c->stash->{Repository}->snapshot(
-        sha1 => $sha1,
-        format => $format
-    );
-    $c->response->status(200);
-    $c->response->headers->header( 'Content-Disposition' =>
-                                       "attachment; filename=$snap[0]");
-    $c->response->body($snap[1]);
+    $c->response->content_type('text/css');
+    $c->stash(template => 'static/css/core.css');
 }
 
-
 sub base : Chained('/root') PathPart('') CaptureArgs(0) {
   my($self, $c) = @_;
 
-  my $repository = $c->req->param('p');
-  if (defined $repository) {
-    eval {
-      $c->stash(Repository => $c->model()->get_repository($repository));
-    };
-    if ($@) {
-      $c->detach('/error_404');
-    }
-  }
-
-  my $a_repository = $c->stash->{Repository} || $c->model()->repositories->[0];
+  my $git_version = `git --version`;
+  chomp($git_version);
   $c->stash(
-    git_version => $a_repository->run_cmd('--version'),
+    git_version => $git_version,
     version     => $Gitalist::VERSION,
 
-    # XXX Move these to a plugin!
     time_since => sub {
       return 'never' unless $_[0];
       return age_string(time - $_[0]->epoch);
@@ -648,22 +42,33 @@ sub base : Chained('/root') PathPart('') CaptureArgs(0) {
     short_cmt => sub {
       my $cmt = shift;
       my($line) = split /\n/, $cmt;
-      $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
+      $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/ if defined $line;
       return $line;
     },
     abridged_description => sub {
         join(' ', grep { defined } (split / /, shift)[0..10]);
     },
+    uri_for_gravatar => sub { # FIXME - Cache these?
+        my $email = shift;
+        my $size = shift;
+        my $uri = 'http://www.gravatar.com/avatar/' . md5_hex($email);
+        $uri .= "?s=$size" if $size;
+        return $uri;
+    },
   );
 }
 
-sub end : ActionClass('RenderView') {
-    my ($self, $c) = @_;
-    # Give repository views the current HEAD.
-    if ($c->stash->{Repository}) {
-        $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
-    }
-}
+sub search : Chained('base') Args(0) {}
+
+=head2 search_help
+
+Provides some help for the search form.
+
+=cut
+
+sub search_help : Chained('base') Args(0) {}
+
+sub end : ActionClass('RenderView') {}
 
 sub error_404 : Action {
     my ($self, $c) = @_;
@@ -701,18 +106,8 @@ Provides the repository listing.
 
 Attempt to render a view, if needed.
 
-=head2 blame
-
-=head2 commitdiff_plain
-
 =head2 error_404
 
-=head2 history
-
-=head2 opml
-
-=head2 repository_index
-
 =head1 AUTHORS
 
 See L<Gitalist> for authors.
index ef36f05..7ac568b 100644 (file)
@@ -49,7 +49,7 @@ class Gitalist::Git::Object::Commit
 
         method diff ( Maybe[Bool] :$patch?,
                        Maybe[NonEmptySimpleStr] :$parent?,
-                       Maybe[NonEmptySimpleStr] :$file?
+                       Maybe[NonEmptySimpleStr] :$filename?
                    ) {
             $parent = $parent
                 ? $parent
@@ -57,7 +57,7 @@ class Gitalist::Git::Object::Commit
                         ? $self->parent_sha1
                             : '-c';
             my @etc = (
-                ( $file  ? ('--', $file) : () ),
+                ( $filename  ? ('--', $filename) : () ),
             );
 
             my @out = $self->_raw_diff(
@@ -105,7 +105,7 @@ class Gitalist::Git::Object::Commit
                 $line{sha1}   = $line{sha1dst};
                 $line{is_new} = $line{sha1src} =~ /^0+$/
                     if $line{sha1src};
-                @line{qw/status sim/} = $line{status} =~ /(R)(\d+)/
+                @line{qw/status sim/} = $line{status} =~ /(R)0*(\d+)/
                     if $line{status} =~ /^R/;
                 push @ret, \%line;
             }
index 1cb6ba7..7e873cd 100644 (file)
@@ -6,6 +6,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils {
     use MooseX::Types::Path::Class qw/Dir/;
     use MooseX::Types::Moose qw/Str Maybe Bool HashRef ArrayRef/;
     use Gitalist::Git::Types qw/SHA1/;
+    use MooseX::MultiMethods;
     use Moose::Autobox;
     use List::MoreUtils qw/any zip/;
     use DateTime;
@@ -74,6 +75,15 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils {
     }
 
     ## Public methods
+
+    multi method get_object_or_head (SHA1 $sha1) {
+        $self->get_object($sha1);
+    }
+    multi method get_object_or_head (NonEmptySimpleStr $ref) {
+        my $sha1 = $self->head_hash($ref);
+        $self->get_object($sha1);
+    }    
+    
     method head_hash (Str $head?) {
         my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' );
         confess("No such head: " . $head) unless defined $output;
@@ -176,11 +186,11 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils {
     method diff ( Gitalist::Git::Object :$commit!,
                   Bool :$patch?,
                   Maybe[NonEmptySimpleStr] :$parent?,
-                  NonEmptySimpleStr :$file?
+                  NonEmptySimpleStr :$filename?
               ) {
               return $commit->diff( patch => $patch,
                                     parent => $parent,
-                                    file => $file);
+                                    filename => $filename);
     }
 
     method reflog (@logargs) {
diff --git a/lib/Gitalist/URIStructure/Fragment/WithLog.pm b/lib/Gitalist/URIStructure/Fragment/WithLog.pm
new file mode 100644 (file)
index 0000000..ea3ae2a
--- /dev/null
@@ -0,0 +1,27 @@
+package Gitalist::URIStructure::Fragment::WithLog;
+use MooseX::MethodAttributes::Role;
+use namespace::autoclean;
+
+requires 'log';
+
+after log => sub {
+    my ($self, $c) = @_;
+    my $repository  = $c->stash->{Repository};
+
+    my %logargs = (
+       sha1   => $c->stash->{Commit}->sha1, # $commit->sha1
+       count  => 25, #Gitalist->config->{paging}{log} || 25,
+    );
+
+    my $page = $c->req->param('pg') || 0;
+    $logargs{skip} = abs $page * $logargs{count}
+        if $page;
+
+    $c->stash(
+       page      => $page,
+       log_lines => [$repository->list_revs(%logargs)],
+       refs      => $repository->references,
+    );
+};
+
+1;
diff --git a/lib/Gitalist/URIStructure/Ref.pm b/lib/Gitalist/URIStructure/Ref.pm
new file mode 100644 (file)
index 0000000..20337fe
--- /dev/null
@@ -0,0 +1,83 @@
+package Gitalist::URIStructure::Ref;
+use MooseX::MethodAttributes::Role;
+use Moose::Autobox;
+use namespace::autoclean;
+
+use Gitalist::Git::Types qw/SHA1/;
+
+requires 'base';
+
+with qw/
+    Gitalist::URIStructure::WithLog
+/;
+
+after 'base' => sub {
+    my ($self, $c) = @_;
+    confess("No repository in the stash")
+        unless $c->stash->{Repository};
+};
+
+sub find : Chained('base') PathPart('') CaptureArgs(1) {
+    my ($self, $c, $sha1part) = @_;
+    # FIXME - Should not be here!
+    $c->stash->{Commit} = $c->stash->{Repository}->get_object_or_head($sha1part)
+        or $c->detach('/error404', "Couldn't find a object for '$sha1part' in XXXX!");
+}
+
+sub diff : Chained('find') CaptureArgs(0) {}
+
+sub _set_diff_args {
+    my($self, $c, @rest) = @_;
+
+    # FIXME - This ain't pretty
+    $c->stash(parent   => shift @rest)
+        if @rest == 2
+        # Check that the single arg is unlikely to be a path.
+        or @rest && to_SHA1($rest[0]) && $c->stash->{Repository}->get_object_or_head($rest[0]);
+    $c->stash(filename => $rest[-1])
+      if @rest;
+}
+
+sub diff_fancy : Chained('diff') PathPart('') Args() {
+    my($self, $c, @rest) = @_;
+
+    $self->_set_diff_args($c, @rest);
+ }
+
+sub diff_plain : Chained('diff') PathPart('plain') Args() {
+    my($self, $c, $comparison, @rest) = @_;
+
+    $self->_set_diff_args($c, @rest);
+
+    $c->stash(no_wrapper => 1);
+    $c->response->content_type('text/plain; charset=utf-8');
+}
+
+sub commit : Chained('find') PathPart('commit') Args(0) {}
+
+sub tree : Chained('find') Does('FilenameArgs') Args() {}
+
+sub find_blob : Action {
+    my ($self, $c) = @_;
+    my($repo, $object) = @{$c->{stash}}{qw(Repository Commit)};
+    # FIXME - Eugh!
+    my $h  = $object->isa('Gitalist::Git::Object::Commit')
+           ? $repo->hash_by_path($object->sha1, $c->stash->{filename})
+           : $object->isa('Gitalist::Git::Object::Blob')
+             ? $object->sha1
+             : die "Unknown object type for '${\$object->sha1}'";
+    die "No file or sha1 provided."
+        unless $h;
+    $c->stash(blob => $repo->get_object($h)->content);
+}
+
+sub blob : Chained('find') Does('FilenameArgs') Args() {
+    my ($self, $c) = @_;
+    $c->forward('find_blob');
+}
+
+sub blame : Chained('find') Does('FilenameArgs') Args() {}
+
+sub history : Chained('find') Does('FilenameArgs') Args() {}
+
+1;
diff --git a/lib/Gitalist/URIStructure/Repository.pm b/lib/Gitalist/URIStructure/Repository.pm
new file mode 100644 (file)
index 0000000..976917e
--- /dev/null
@@ -0,0 +1,51 @@
+package Gitalist::URIStructure::Repository;
+use MooseX::MethodAttributes::Role;
+use Try::Tiny qw/try catch/;
+use namespace::autoclean;
+
+requires 'base';
+
+with qw/
+    Gitalist::URIStructure::WithLog
+/;
+
+sub find : Chained('base') PathPart('') CaptureArgs(1) {
+    my ($self, $c, $repos_name) = @_;
+    # XXX FIXME - This should be in the repository fragment controller, and the repository
+    #             controller should just check has_repository
+    try {
+        my $repos = $c->model()->get_repository($repos_name);
+        $c->stash(
+            Repository => $repos,
+            HEAD => $repos->head_hash,
+        );
+    }
+    catch {
+        $c->detach('/error_404');
+    };
+}
+
+before 'log' => sub {
+    my ($self, $c) = @_;
+    $c->stash->{Commit} = $c->stash->{Repository}->get_object($c->stash->{Repository}->head_hash);
+};
+
+sub object : Chained('find') PathPart('') Args(1) {
+    my ($self, $c, $sha1) = @_;
+
+    my $repo = $c->stash->{Repository};
+    my $obj  = $c->stash->{Commit} = $repo->get_object($sha1);
+    my($act) = (ref($obj) || '') =~ /::(\w+)$/;
+
+    $c->res->redirect($c->uri_for_action("/ref/\L$act", [$repo->name, $obj->sha1]));
+    $c->res->status(301);
+
+}
+
+sub summary : Chained('find') PathPart('') Args(0) {}
+
+sub heads : Chained('find') Args(0) {}
+
+sub tags : Chained('find') Args(0) {}
+
+1;
diff --git a/lib/Gitalist/URIStructure/WithLog.pm b/lib/Gitalist/URIStructure/WithLog.pm
new file mode 100644 (file)
index 0000000..318522d
--- /dev/null
@@ -0,0 +1,11 @@
+package Gitalist::URIStructure::WithLog;
+use MooseX::MethodAttributes::Role;
+use namespace::autoclean;
+
+sub log : Chained('find') PathPart('') CaptureArgs(0) {}
+
+sub shortlog : Chained('log') Args(0) {}
+
+sub longlog : Chained('log') PathPart('log') Args(0) {}
+
+1;
\ No newline at end of file
index 757f982..0c8d576 100644 (file)
@@ -1,16 +1,28 @@
 package Gitalist::View::Default;
 use Moose;
+use Moose::Autobox;
 use namespace::autoclean;
 
 extends 'Catalyst::View::TT';
+with 'Catalyst::View::Component::SubInclude';
 
 use Template::Plugin::Cycle;
 
 __PACKAGE__->config(
   TEMPLATE_EXTENSION => '.tt2',
-  WRAPPER            => 'default.tt2',
+  WRAPPER            => 'wrapper.tt2',
+  subinclude_plugin => 'SubRequest',
 );
 
+use Template::Stash;
+
+# define list method to flatten arrayrefs
+$Template::Stash::LIST_OPS->{ to_path } = sub {
+    my $path = join('%2F', shift->flatten, @_);
+    $path =~ s{/}{%2F}g;
+    return $path;
+};
+
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
 
 __END__
diff --git a/root/_chroma_hash.tt2 b/root/_chroma_hash.tt2
deleted file mode 100644 (file)
index d7a0bb7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[%- sha1 = sha1 || HEAD -%]
-<span class='chroma-hash'>[%-
-    hptr = 0;
-    WHILE hptr < sha1.length - 6;
-      sha1part = sha1.substr(hptr, 6);
-      "<span style='border-bottom: solid 4px #" _ sha1part _ ";'>" _ sha1part _ "</span>";
-      hptr = hptr + 6;
-    END;
--%]</span>
old mode 100644 (file)
new mode 100755 (executable)
index b3062eb..4b217d2
@@ -1,14 +1,27 @@
 [% INCLUDE inc/syntax_highlight_css.tt2 %]
+
+<h2>Differences</h2>
+
+<div class="differences">
+
 [% FOREACH item IN diff %]
-<div class='diff-head'>
- diff --git
- <a href='[% c.uri_for("blob", {h=item.src, f=item.file}) %]'>[% item.a %]</a>
- <a href='[% c.uri_for("blob", {h=item.dst, f=item.file}) %]'>[% item.b %]</a>
+
+<h4 id="diff[% loop.count %]" class='diff-head'>diff --git [%# FIXME %]
+ <a href='[% c.uri_for_action("/ref/blob", [Repository.name, Commit.sha1], item.file) %]' title="Blob">[% item.a %]</a>
+ <a href='[% c.uri_for_action("/ref/blob", [Repository.name, Commit.sha1], item.file) %]' title="Blob">[% item.b %]</a>
+</h4>
+
+
+
+
+<div class='diff-patch'>
+       <pre>[% blobs.${loop.index} %]</pre>
 </div>
+
 <div class='diff-index'>
- [% item.index %]
-</div>
-<div class='diff-patch'>
- <pre>[% blobs.${loop.index} %]</pre>
+       [% item.index.replace("index","<b>Index</b>") %]
 </div>
+
 [% END %]
+
+</div>
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 2057772..7126a75
@@ -1,23 +1,18 @@
+<h2>[% diff_tree.size %] file[% "s" IF diff_tree.size > 1 %] in this diff <span>([%- Commit.sha1 || HEAD -%])</span></h2>
+
 <table class='diff-tree listing'>
  <thead>
   <tr>
-   <th>file</th>
-   <th>status</th>
-   <th>actions</th>
+   <th>File</th>
+   <th>Status</th>
+   <th>Actions</th>
   </tr>
  </thead>
- <tfoot>
-  <tr>
-   <td>file</td>
-   <td>status</td>
-   <td>actions</td>
-  </tr>
- </tfoot>
  <tbody>
   [% FOREACH line IN diff_tree -%]
   <tr>
    <td class='file-name'>
-    [% line.file %]
+    <a href="#diff[% loop.count %]">[% line.file %]</a>
    </td>
    <td class='status'>
     [%
@@ -32,9 +27,9 @@
     %]
    </td>
    <td class='action-list'>
-     [% IF !line.is_new %]<a href="[% c.uri_for("blobdiff", {hp=commit.parent_sha1, h=commit.sha1, f=line.file}) %]">diff</a>[% END %]
-     <a href="[% c.uri_for("blob", {h=line.sha1, hb=commit.sha1, f=line.file}) %]">blob</a>
-     [% IF !line.is_new %]<a href="[% c.uri_for("shortlog", {hb=commit.sha1, f=line.file}) %]">history</a>[% END %]
+     [% IF !line.is_new %]<a href="[% c.uri_for_action("/ref/diff_fancy", [Repository.name, Commit.sha1], line.file.to_path) %]" title="Difference" class="button diff">diff</a>[% END %]
+     [% IF !line.is_new %]<a href="[% c.uri_for_action("/ref/history", [Repository.name, Commit.sha1], line.file.to_path) %]" title="History (Short Log)" class="button history">history</a>[% END %]
+       <a href="[% c.uri_for_action("/ref/blob", [Repository.name, Commit.sha1], line.file.to_path) %]" title="Blob" class="button blob">blob</a>
    </td>
   </tr>
   [% END %]
index 2183ab5..1fbcfaa 100644 (file)
@@ -3,13 +3,13 @@
   <link
     rel="alternate"
     title="[% Repository.name %] - [% title %] Atom feed"
-    href="[% c.uri_for('atom') %]"
+    href="[% c.uri_for_action('/repository/atom', [Repository.name]) %]"
     type="application/atom+xml"
   >
   <link
     rel="alternate"
     title="[% Repository.name %] - [% title %] RSS feed"
-    href="[% c.uri_for('rss') %]"
+    href="[% c.uri_for_action('/repository/rss', [Repository.name]) %]"
     type="application/rss+xml"
   >
   [% ELSE %]
     title="[% c.config.sitename %] Git repositories list"
     href="[% c.uri_for('repository_index') %]"
     type="text/plain; charset=utf-8"
-  > 
+  >
   <link
     rel="alternate"
     title="[% c.config.sitename %] Git repositories feeds"
-    href="[% c.uri_for('opml') %]"
+    href="[% c.uri_for_action('/opml/opml') %]"
     type="text/x-opml"
   >
   [% END %]
diff --git a/root/_heads.tt2 b/root/_heads.tt2
deleted file mode 100644 (file)
index 0eb77e7..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<table class='[% action %] listing'>
- <thead>
-  <tr>
-   <th>HEAD</th>
-   <th>age</th>
-   <th>branch</th>
-   <th>actions</th>
-  </tr>
- </thead>
- <tfoot>
-  <tr>
-   <td>HEAD</td>
-   <td>age</td>
-   <td>branch</td>
-   <td>actions</td>
-  </tr>
- </tfoot>
-
- <tbody>
- [% FOREACH head IN heads %]
-  <tr>
-   <td class='sha1' title='[% head.sha1 %]'>[% INCLUDE '_chroma_hash.tt2' sha1 = head.sha1.substr(0,7) %]</td>
-   <td class='time-since' title='[% head.last_change %]'>[% time_since(head.last_change) %]</td>
-   <td class='head[% head.sha1 == HEAD ? ' current' : '' %]'>[% head.name %]</td>
-   <td class='action-list'>
-     <a href="[% c.uri_for("shortlog", {h='refs/heads/' _ head.name}) %]">shortlog</a>
-     <a href="[% c.uri_for("log", {h='refs/heads/' _ head.name}) %]">log</a>
-     <a href="[% c.uri_for("tree", {h='refs/heads/' _ head.name, hb=head.name}) %]">tree</a>
-   </td>
-  </tr>
- [% END %]
- </tbody>
-</table>
diff --git a/root/_history.tt2 b/root/_history.tt2
deleted file mode 100644 (file)
index 83f09e1..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<table class='history listing'>
- <thead>
-  <tr>
-   <th>sha1</th>
-   <th>time</th>
-   <th>author</th>
-   <th>message</th>
-   <th>actions</th>
-  </tr>
- </thead>
-
- <tfoot>
-  <tr>
-   <td>sha1</td>
-   <td>time</td>
-   <td>author</td>
-   <td>message</td>
-   <td>actions</td>
-  </tr>
- </tfoot>
-
- <tbody>
- [% FOREACH line IN log_lines %]
-  <tr>
-   <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
-   <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
-   <td class='author'>[% line.author.name | html %]</td>
-   <td>
-     [% short_cmt(line.comment) | html %]
-     [% INCLUDE '_refs.tt2' object = line.0 %]
-   </td>
-   <td class='action-list'>
-   [% IF filetype == 'tree' %]
-     <a href="[% c.uri_for("tree", {h=line.sha1, hb=line.sha1}) %]">tree</a>
-    [% ELSIF filetype == 'blob' %]
-     <a href="[% c.uri_for("blob", {hb=line.sha1, f=filename}) %]">blob</a>
-    [% END %]
-     <a href="[% c.uri_for("commitdiff", {h=line.sha1}) %]">commitdiff</a>
-    [% IF filetype == 'blob' %]
-     <a href="[% c.uri_for("blobdiff", {hb="HEAD", hpb=line.sha1, f=filename}) %]">diff to current</a>
-     [% END %]
-   </td>
-  </tr>
- [% END %]
- </tbody>
-</table>
index c332d35..e98f0ee 100644 (file)
@@ -1,9 +1,9 @@
 <div class='pager'>
- <a href='[% c.uri_for(action, {h=HEAD}) %]'>HEAD</a> &sect;
+ <a href='[% c.uri_for_action('/' _ c.action, [Repository.name, 'HEAD']) %]'>HEAD</a> &sect;
  [% IF log_lines.first.sha1 != commit.sha1 %]
- <a href='[% c.request.uri_with(pg => page - 1) %]'>&laquo; prev</a>
+ <a href='[% c.request.uri_with(pg => (page||0) - 1) %]'>&laquo; prev</a>
  [% END %]
  [% IF log_lines.size == 50 %]
- <a href='[% c.request.uri_with(pg => page + 1) %]'>next &raquo;</a>
+ <a href='[% c.request.uri_with(pg => (page||0) + 1) %]'>next &raquo;</a>
  [% END %]
 </div>
index 71bb0ed..ec6db82 100644 (file)
@@ -1,7 +1,7 @@
 <span class='refs'>
  [% FOREACH ref IN refs.${object.sha1} %]
  <span class='[% ref.search('^remotes/') ? 'remote' : 'head' %]'>
-  <a href='[% c.uri_for("shortlog", {h='refs/' _ ref}) %]'>[% ref.replace('^(remote|head)s/', '') %]</a>
+  <a href='[% c.uri_for_action("/repository/shortlog", [Repository, object.sha1]) %]'>[% ref.replace('^(remote|head)s/', '') %]</a>
  </span>
  [% END %]
 </span>
diff --git a/root/_shortlog.tt2 b/root/_shortlog.tt2
deleted file mode 100644 (file)
index 19caaf1..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<table class='shortlog listing'>
- <thead>
-  <tr>
-   <th>sha1</th>
-   <th>time</th>
-   <th>author</th>
-   <th>message</th>
-   <th>actions</th>
-  </tr>
- </thead>
-
- <tfoot>
-  <tr>
-   <td>sha1</td>
-   <td>time</td>
-   <td>author</td>
-   <td>message</td>
-   <td>actions</td>
-  </tr>
- </tfoot>
-
- <tbody>
- [% FOREACH line IN log_lines %]
-  <tr>
-   <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
-   <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
-   <td class='author'>[% line.author.name | html %]</td>
-   <td>
-     [% short_cmt(line.comment) | html %]
-     [% INCLUDE '_refs.tt2' object = line %]
-   </td>
-   <td class='action-list'>
-     <a href="[% c.uri_for("commit", {h=line.sha1}) %]">commit</a>
-     <a href="[% c.uri_for("commitdiff", {h=line.sha1}) %]">commitdiff</a>
-     <a href="[% c.uri_for("tree", {h=line.sha1, hb=line.sha1}) %]">tree</a>
-   </td>
-  </tr>
- [% END %]
- </tbody>
-</table>
diff --git a/root/_tree.tt2 b/root/_tree.tt2
deleted file mode 100644 (file)
index 6a96cda..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<table class='tree listing'>
- <thead>
-  <tr>
-   <th>mode</th>
-   <th>file</th>
-   <th>actions</th>
-  </tr>
- </thead>
- <tfoot>
-  <tr>
-   <td>mode</td>
-   <td>file</td>
-   <td>actions</td>
-  </tr>
- </tfoot>
-
- <tbody>
-  [% FOREACH item IN tree_list %]
-  <tr>
-   <td class='file-mode'>[% item.modestr %]</td>
-   [% theact = item.type == 'tree' ? 'tree' : 'blob' -%]
-   [% fullpath = path ? path _ '/' _ item.file : item.file %]
-   <td class='file-name'>
-    <a href="[% c.uri_for(theact, {h=item.sha1, hb=commit.sha1, f=fullpath}) %]">[% item.file %]</a>
-   </td>
-   <td class='action-list'>
-     <a href="[% c.uri_for(theact, {h=item.sha1, hb=commit.sha1, f=fullpath}) %]">[% theact %]</a>
-         [% IF item.type == 'blob' %]
-         <a href="[% c.uri_for('blame', {h=commit.sha1, hb=commit.sha1, f=fullpath}) %]">blame</a>
-         [% END %]
-     <a href="[% c.uri_for('history', {h=item.sha1, hb=commit.sha1, f=fullpath}) %]">history</a>
-     [% IF item.type == 'blob' %]
-     <a href="[% c.uri_for('raw', {hb=commit.sha1, f=fullpath}) %]">raw</a>
-     [% END %]
-   </td>
-  </tr>
-  [% END %]
- </tbody>
-</table>
diff --git a/root/blame.tt2 b/root/blame.tt2
deleted file mode 100644 (file)
index 19d3e61..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-[% PROCESS 'nav/actions.tt2' object = head %]
-[% INCLUDE inc/syntax_highlight_css.tt2 %]
-
-<div class='content'>
-
-[% IF object.type == 'commit' %]
-<div class='commit-message'>[% short_cmt(head.comment) %]</div>
-[% END %]
-
-[% INCLUDE 'nav/path.tt2' %]
-
-<div id='blame'>
-<table>
- <thead>
-  <tr>
-   <th>author</th>
-   <th>date</th>
-   <th>sha1</th>
-   <th></th>
-   <th class='data'>data</th>
-  </tr>
- </thead>
-
- <tfoot>
-  <tr>
-   <td>author</td>
-   <td>date</td>
-   <td>sha1</td>
-   <td></td>
-   <td class='data'>data</td>
-  </tr>
- </tfoot>
-
- <tbody>
- [% blame_lines = blob.split("\n") %]
- [% FOR info IN blame %]
- <tr class=''>
-   [%-
-     linecolour = info.commit.sha1.substr(0,6);
-     IF info.commit.sha1 != lastsha1;
-       styleinfo = " style='border-top: solid 3px #" _ linecolour _ ";'"; -%]
-   <td nowrap class='author'[% styleinfo %]>[% info.commit.author %]</td>
-   <td nowrap class='date'[% styleinfo %]>[% info.commit.author_dt.ymd %]</td>
-   <td nowrap class='commit-info chroma-hash'[% styleinfo %]><a title="[% info.commit.author %] on [% info.commit.author_dt %]" href='[% c.uri_for('commit', {h=info.commit.sha1}) %]'>[% linecolour %]</td>
-     [%- ELSE -%]
-     <td nowrap class='author'></td>
-     <td nowrap class='date'></td>
-     <td nowrap class='commit-info'></td>
-     [%- END -%]
-   <td nowrap class='lineno' id='l[% info.meta.lineno %]' style='border-right: solid 3px #[% linecolour %]; border-left: solid 3px #[% linecolour %];'><tt><a href='[% c.uri_for('blame', {h=info.commit.sha1,f=filename}) %]#l[% info.meta.orig_lineno %]'>[% info.meta.lineno %]</a></tt></td>
-   <td nowrap class='data'><pre>[% blame_lines.${loop.index} %]</pre></td>
-   [% lastsha1 = info.commit.sha1 %]
- </tr>
- [% END %]
- </tbody>
-</table>
-</div>
-
-</div>
diff --git a/root/blobdiff.tt2 b/root/blobdiff.tt2
deleted file mode 100644 (file)
index b771f74..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[% PROCESS 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  <div class='commit-message'>
-  [% short_cmt(commit.comment) | html %] ...
-  </div>
-
-  [% INCLUDE '_diff.tt2' %]
-</div>
diff --git a/root/blobdiff_plain.tt2 b/root/blobdiff_plain.tt2
deleted file mode 100644 (file)
index 4b771f4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-[%- INCLUDE '_diff_plain.tt2' -%]
diff --git a/root/commit.tt2 b/root/commit.tt2
deleted file mode 100644 (file)
index 408eedc..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  <div class='commit-message'>
-  [%
-    short_cmt(commit.comment) | html;
-    INCLUDE '_refs.tt2' object = commit;
-  %]
-  </div>
-  <!-- [% USE dumper; dumper.dump(commit.parents) %] -->
-  <dl class='commit-info'>
-   <dt>author</dt>
-    <dd>[% commit.author.name | html %] &lt;[% commit.author.email %]&gt;<br/>
-       [% commit.authored_time %]</dd>
-   <dt>committer</dt>
-    <dd>[% commit.committer.name %] &lt;[% commit.committer.email %]&gt;<br/>
-       [% commit.committed_time %]</dd>
-   <dt>commit</dt>
-    <dd>[% commit.sha1 %]</dd>
-   <dt>tree</dt>
-    <dd>[% commit.tree_sha1 %]
-       <span class='action-list'><a href="[% c.uri_for("tree", {h=commit.tree_sha1, hb=commit.sha1}) %]">tree</a></span>
-    </dd>
-   [% FOREACH parent IN commit.parents %]
-   <dt>parent</dt>
-    <dd>[% parent.sha1  %]
-      <span class='action-list'>
-       <a href="[% c.uri_for("commit", {h=parent.sha1}) %]">commit</a>
-       <a href="[% c.uri_for("commitdiff", {hp=parent.sha1, h=commit.sha1}) %]">diff</a>
-      </span>
-     </dd>
-   [% END %]
-  </dl>
-
-  <pre class='commit-message'>[% commit.comment | html%]</pre>
-
-  [%
-    # In the case of merge commits there will be no diff tree.
-    IF diff_tree.size > 0;
-      INCLUDE '_diff_tree.tt2';
-    END;
-  %]
-</div>
diff --git a/root/commitdiff.tt2 b/root/commitdiff.tt2
deleted file mode 100644 (file)
index 9db50ec..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-[% PROCESS 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  <div class='commit-message'>
-  [% # XXX Wah, stuff like this doesn't work because end() isn't called as we forward to View::SyntaxHighlight
-  short_cmt(commit.comment) | html %]
-  </div>
-
-  <div class='author'>
-    [% commit.author.name | html %] [[% commit.authored_time %]]
-  </div>
-
-
-  [%
-  # In the case of merge commits there will be no diff tree.
-  IF diff_tree.size > 0;
-    INCLUDE '_diff_tree.tt2';
-  END;
-  IF diff.size > 0;
-    INCLUDE '_diff.tt2';
-  ELSE
-  %]
-  <div class='no-difference'>
-  [%
-    IF commit.parents > 1;
-      'Trivial merge';
-    ELSE;
-      'No differences found';
-    END;
-  %]
-  </div>
-  [% END %]
-</div>
diff --git a/root/commitdiff_plain.tt2 b/root/commitdiff_plain.tt2
deleted file mode 100644 (file)
index 4b771f4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-[%- INCLUDE '_diff_plain.tt2' -%]
diff --git a/root/default.tt2 b/root/default.tt2
deleted file mode 100644 (file)
index d38cdcb..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-[%- IF no_wrapper || template.name.match('\.(css|js|txt)'); content; ELSE; -%]
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <!-- git core binaries version [% git_version %] -->
-  <meta charset="utf-8">
-  <meta name="generator" content="gitweb/[% version %] git/[% git_version %][% mod_perl_version %]">
-  <meta name="robots" content="index, nofollow">
-  <title>[%-
-    title = BLOCK;
-      c.config.sitename;
-      IF Repository; ' - ' _ Repository.name | html; END;
-      IF action;  ' / ' _ action; END;
-      IF filename; ' - ' _ filename | html; END;
-      IF action && action == 'tree'; '/'; END;
-    END;
-    title;
-  -%] (Gitalist)</title>
-  [% INCLUDE '_header_feeds.tt2' %]
-  <link rel="stylesheet" href="[% c.uri_for('/static/css/blueprint/screen.css') %]" type="text/css" media="screen, projection">
-  <link rel="stylesheet" href="[% c.uri_for('/static/css/blueprint/print.css') %]" type="text/css" media="print">
-  <!--[if lt IE 8]>
-    <link rel="stylesheet" href="[% c.uri_for('/static/css/blueprint/ie.css') %]" type="text/css" media="screen, projection">
-  <![endif]-->  
-  <link rel="stylesheet" type="text/css" href="[% c.uri_for('/static/css/site.css') %]">
-  <link rel="shortcut icon" href="[% c.uri_for('git-favicon.png') %]" type="image/png">
-</head>
-
-<body>
-
-<div id='the-container'>
-
-[% site_header %]
-
-<div id="page-header">
-  <a title="git homepage" href="http://git-scm.org">
-   <img src="[% c.uri_for('/logo.png') %]" alt="git" class="logo">
-  </a>
-  <a href="[% c.uri_for('/', {p=''}) %]">A Gitalist</a>
-  [%- IF Repository %]
-  / <a href="[% c.uri_for('summary') %]">[% Repository.name %]</a>
-  [% IF action;  " / " _ action; END;
-  END %]
-[%
-  IF Repository;
-    INCLUDE 'nav/search.tt2';
-  END;
-# / git_header_html
-%]
-</div>
-
-<div id='body'>
-[%
-  IF action;
-    SET actmpl = action _ ".tt2";
-    INCLUDE $actmpl;
-  ELSE;
-    # The output of gitweb.cgi is injected at this point.
-    content;
-  END;
-%]
-</div>
-
-<div id="page-footer">
-[% IF Repository %]
-  [% Repository.description | html %]
-[% END %]
-[% INCLUDE '_footer_feeds.tt2' %]
-</div>
-
-</div>
-
-</body>
-</html>
-[%- END -%]
index 5ad723d..de637c0 100644 (file)
Binary files a/root/favicon.ico and b/root/favicon.ico differ
diff --git a/root/favicon.png b/root/favicon.png
deleted file mode 100644 (file)
index de637c0..0000000
Binary files a/root/favicon.png and /dev/null differ
diff --git a/root/fragment/collectionofrepositories.tt2 b/root/fragment/collectionofrepositories.tt2
new file mode 100755 (executable)
index 0000000..afcc4fd
--- /dev/null
@@ -0,0 +1,17 @@
+<tbody>
+[% FOR p IN repositories %]
+       [%- repos_link = c.uri_for_action('/repository/summary', [p.name]) -%]
+<tr [% "class='invert'" IF loop.count % 2 %]>
+    <td>[% loop.count %]</td>
+       <td><a href="[% repos_link %]/shortlog"><strong>[% p.name %]</strong></a></td>
+    <td><span title="[% p.description %]">[% abridged_description(p.description) %]</span></td>
+    <td class="time-since">[% time_since(p.last_change) %]</td>
+    <td>[% p.owner %]</td>
+    <td class="action-list">
+       <a href="[% c.uri_for_action('/repository/shortlog', [p.name]) %]" title="Short log" class="button shortlog">short log</a>
+       <a href="[% c.uri_for_action('/repository/longlog', [p.name]) %]" title="Long log" class="button longlog">long log</a>
+       <a href="[% c.uri_for_action('/ref/tree', [p.name, 'HEAD']) %]" title="Tree" class="button tree">tree</a>
+       </td>
+</tr>
+[% END %]
+</tbody>
diff --git a/root/fragment/ref/blame.tt2 b/root/fragment/ref/blame.tt2
new file mode 100755 (executable)
index 0000000..af465d4
--- /dev/null
@@ -0,0 +1,21 @@
+[% blame_lines = blob.split("\n") %]
+ [% FOR info IN blame %]
+ <tr class=''>
+   [%-
+     linecolour = info.commit.sha1.substr(0,6);
+     IF info.commit.sha1 != lastsha1;
+       styleinfo = " style='border-top: solid 3px #" _ linecolour _ ";'"; -%]
+   <td nowrap class='author'[% styleinfo %]>[% info.commit.author %]</td>
+   <td nowrap class='date'[% styleinfo %]>[% info.commit.author_dt.ymd %]</td>
+   <td nowrap class='sha1 commit-info chroma-hash'[% styleinfo %]><a title="[% info.commit.author %] on [% info.commit.author_dt %]" href='[% c.uri_for_action('/ref/commit', [Repository.name, info.commit.sha1]) %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = info.commit.sha1.substr(0, 7) %]</a>
+[%# linecolour %]</td>
+     [%- ELSE -%]
+     <td nowrap class='author'></td>
+     <td nowrap class='date'></td>
+     <td nowrap class='commit-info'></td>
+     [%- END -%]
+   <td nowrap class='lineno' id='l[% info.meta.lineno %]' style='border-right: solid 3px #[% linecolour %]; border-left: solid 3px #[% linecolour %];'><tt><a href='[% c.uri_for_action('/ref/blame', [Repository.name, info.commit.sha1], filename.to_path ) %]#l[% info.meta.orig_lineno %]'>[% info.meta.lineno %]</a></tt></td>
+   <td nowrap class='data'><pre>[% blame_lines.${loop.index} | html %]</pre></td>
+   [% lastsha1 = info.commit.sha1 %]
+ </tr>
+ [% END %]
diff --git a/root/fragment/ref/blob.tt2 b/root/fragment/ref/blob.tt2
new file mode 100644 (file)
index 0000000..592f8c5
--- /dev/null
@@ -0,0 +1 @@
+[% blob %]
diff --git a/root/fragment/ref/commit.tt2 b/root/fragment/ref/commit.tt2
new file mode 100755 (executable)
index 0000000..551d103
--- /dev/null
@@ -0,0 +1,67 @@
+<table class="listing">
+<thead>
+<tr>
+       <th>ID (sha1)</th>
+       <th>Last change</th>
+       <th>Message</th>
+       <th>By</th>
+       <th>Role</th>
+</tr>
+</thead>
+<tbody>
+<tr class="invert">
+       <td class='sha1' title='[% Commit.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7) %]</td>
+       <td class='time-since'>[% time_since(Commit.authored_time) %]</td>
+       <td rowspan="2">
+       [% Commit.comment | html%]
+       [%
+    INCLUDE '_refs.tt2' object = commit;
+  %]</td>
+       <td class='author'><a href="mailto:[% Commit.author.email %]" title="Email"><img style="float: left; padding-right: 10px" src="[% uri_for_gravatar(Commit.committer.email, 21) %]">[% Commit.author.name | html %]</a></td>
+       <td class='action-list'>Author</td>
+</tr>
+<tr class="invert">
+       <td></td>
+       <td class='time-since'>[% time_since(Commit.committed_time) %]</td>
+       <!-- spanned -->
+       <td class='author'><a href="mailto:[% Commit.committer.email %]" title="Email"><img style="float: left; padding-right: 10px" src="[% uri_for_gravatar(Commit.committer.email, 21) %]">[% Commit.committer.name %]</a></td>
+       <td class='action-list'>Committer</td>
+</tr>
+</tbody>
+</table>
+  <!-- [% USE dumper; dumper.dump(commit.parents) %] -->
+<table class="listing summary">
+       <tr>
+               <td class='sha1'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7), hide_sha1_output = 1 %] <div class="sha1_label">Commit</div></td>
+               <td>[% Commit.sha1 %]</td>
+               <td class='action-list'></td>
+       </tr>
+       <tr>
+               <td class='sha1'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.tree_sha1.substr(0, 7), hide_sha1_output = 1 %] <div class="sha1_label">Tree</div></td>
+               <td>[% Commit.tree_sha1 %]</td>
+               <td class='action-list'><a href="[% c.uri_for_action("/ref/tree", c.req.captures) %]" title="Tree" class="button tree">tree</a></td>
+       </tr>
+        [% FOREACH parent IN Commit.parents %]
+       <tr>
+               <td class='sha1'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = parent.sha1.substr(0, 7), hide_sha1_output = 1 %] <div class="sha1_label">Parent</div></td>
+               <td>[% parent.sha1 %]</td>
+               <td class='action-list'>
+                       <a href="[% c.uri_for_action('/ref/commit', [Repository.name, parent.sha1]) %]" title="Commit" class="button commit">commit</a>
+               <a href="[% c.uri_for_action('/ref/diff_fancy', [Repository.name, parent.sha1]) %]" title="Difference" class="button diff">diff</a>
+               </td>
+       </tr>
+[% END %]       
+</table>
+
+
+
+  [%
+    # In the case of merge commits there will be no diff tree.
+    IF diff_tree && diff_tree.size > 0;
+      INCLUDE '_diff_tree.tt2';
+    END;
+  %]
diff --git a/root/fragment/ref/diff_fancy.tt2 b/root/fragment/ref/diff_fancy.tt2
new file mode 100755 (executable)
index 0000000..7cba200
--- /dev/null
@@ -0,0 +1,20 @@
+[%
+  # In the case of merge commits there will be no diff tree.
+  IF (diff_tree.size||0) > 0;
+    INCLUDE '_diff_tree.tt2';
+  END;
+  IF (diff.size||0) > 0;
+    INCLUDE '_diff.tt2';
+  ELSE
+  %]
+  <p class='msg'>
+  [%
+    IF commit && commit.parents > 1;
+      'Trivial merge';
+    ELSE;
+      'No differences found';
+    END;
+  %]
+  </p>
+  [% END %]
+
diff --git a/root/fragment/ref/diff_plain.tt2 b/root/fragment/ref/diff_plain.tt2
new file mode 100644 (file)
index 0000000..0f58361
--- /dev/null
@@ -0,0 +1,5 @@
+[%- FOREACH item IN diff -%]
+diff --git [% item.a _ ' ' _ item.b %]
+[% item.index %]
+[% blobs.${loop.index} %]
+[%- END -%]
diff --git a/root/fragment/ref/history.tt2 b/root/fragment/ref/history.tt2
new file mode 100755 (executable)
index 0000000..92b22b1
--- /dev/null
@@ -0,0 +1,42 @@
+[% BLOCK history_table_headfoot %]
+[% SET cell = type == 'head' ? 'th' : 'td' %]
+<tr>
+ <[% cell %]>sha1</[% cell %]>
+ <[% cell %]>time</[% cell %]>
+ <[% cell %]>message</[% cell %]>
+ <[% cell %]>author</[% cell %]>
+ <[% cell %]>actions</[% cell %]>
+</tr>
+[% END %]
+ <table class='listing'>
+   <thead>[% PROCESS history_table_headfoot type = 'head' %]</thead
+
+   <tbody>
+   [% FOREACH line IN log_lines %]
+ <tr [% "class='invert'" IF loop.count % 2 %]>
+     <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
+     <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
+    
+     <td>
+       [% short_cmt(line.comment) | html %]
+       [% INCLUDE '_refs.tt2' object = line.0 %]
+     </td>
+         <td class='author'>[% line.author.name | html %]</td>
+     <td class='action-list'>
+     [% IF filetype == 'tree' %]
+       <a href="[% c.uri_for_action("/ref/tree", [Repository.name, line.sha1], filename) %]" title="Blob" class="button blob">blob</a>
+      [% ELSIF filetype == 'blob' %]
+       <a href="[% c.uri_for_action("/ref/blob", [Repository.name, line.sha1], filename) %]" title="Blob" class="button blob">blob</a>
+      [% END %]
+       <a href="[% c.uri_for_action("/ref/diff_fancy", [Repository.name, line.sha1]) %]" title="Commit diff" class="button diff">commitdiff</a>
+      [% IF filetype == 'blob' %]
+       <a href="[% c.uri_for_action("/ref/diff_fancy", [Repository.name, line.sha1], 'HEAD', filename) %]" title="Diff to current" class="button diffcurrent">diff to current</a>
+       [% END %]
+     </td>
+    </tr>
+   [% END %]
+   </tbody>
+  </table>
+[%
+  INCLUDE 'inc/log_pager.tt2';
+%]
diff --git a/root/fragment/ref/longlog.tt2 b/root/fragment/ref/longlog.tt2
new file mode 100644 (file)
index 0000000..5470847
--- /dev/null
@@ -0,0 +1 @@
+[% PROCESS 'fragment/repository/longlog.tt2' %]
diff --git a/root/fragment/ref/shortlog.tt2 b/root/fragment/ref/shortlog.tt2
new file mode 100644 (file)
index 0000000..0ba3aa1
--- /dev/null
@@ -0,0 +1 @@
+[% PROCESS 'fragment/repository/shortlog.tt2' %]
diff --git a/root/fragment/ref/tree.tt2 b/root/fragment/ref/tree.tt2
new file mode 100755 (executable)
index 0000000..68cfa12
--- /dev/null
@@ -0,0 +1,35 @@
+[% BLOCK tree_table_headfoot %]
+[% SET cell = type == 'head' ? 'th' : 'td' %]
+  <tr>
+   <[% cell %]>Mode</[% cell %]>
+   <[% cell %]>File</[% cell %]>
+   <[% cell %]>Actions</[% cell %]>
+  </tr>
+[% END %]
+<table class="listing">
+ <thead>[% PROCESS tree_table_headfoot type = 'head' %]</thead>
+ <tbody>
+  [% FOREACH item IN tree_list %]
+ <tr [% "class='invert'" IF loop.count % 2 %]>
+   <td class='file-mode'>[% item.modestr %]</td>
+   [%-
+        action_type = item.type == 'tree' ? 'tree' : 'blob';
+        action_for_link = item.type == 'tree' ? '/ref/tree' : '/ref/blob';
+        blob_or_tree_link = c.uri_for_action(action_for_link, c.req.captures, c.req.args.to_path(item.file))
+   -%]
+   <td class='file-name'>
+    <a href="[% blob_or_tree_link %]">[% item.file %]</a>
+   </td>
+   <td class='action-list'>
+       <a href="[% blob_or_tree_link %]">[% theact %]</a>
+         [% IF item.type == 'blob' %]
+       <a href="[% c.uri_for_action('/ref/blob', c.req.captures, c.req.args.to_path(item.file)) %]" title="Blob" class="button blob">Blob</a>
+       <a href="[% c.uri_for_action('/ref/raw', c.req.captures, c.req.args.to_path(item.file)) %]" title="Raw" class="button raw">raw</a>
+       <a href="[% c.uri_for_action('/ref/blame', c.req.captures, c.req.args.to_path(item.file)) %]" title="Blame" class="button blame">blame</a>
+         [% END %]
+     <a href="[% c.uri_for_action('/ref/history', c.req.captures, c.req.args.to_path(item.file)) %]" title="History (Short log)" class="button shortlog">Short log</a>
+   </td>
+  </tr>
+  [% END %]
+ </tbody>
+</table>
diff --git a/root/fragment/repository/heads.tt2 b/root/fragment/repository/heads.tt2
new file mode 100755 (executable)
index 0000000..69c5766
--- /dev/null
@@ -0,0 +1,27 @@
+[% BLOCK repository_heads_headfoot %]
+[% SET cell = type == 'head' ? 'th' : 'td' %]
+<tr class="header">
+   <[% cell %]>HEAD</[% cell %]>
+   <[% cell %]>Last change</[% cell %]>
+   <[% cell %]>Branch</[% cell %]>
+   <[% cell %]>Actions</[% cell %]>
+</tr>
+[% END %]
+
+<table class='[% action %] listing'>
+ <thead>[% PROCESS repository_heads_headfoot type = 'head' %]</thead>
+ <tbody>
+ [% FOREACH head IN heads %]
+  <tr>
+   <td class='sha1' title='[% head.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = head.sha1.substr(0,7) %]</td>
+   <td class='time-since' title='[% head.last_change %]'>[% time_since(head.last_change) %]</td>
+   <td class='head[% head.sha1 == HEAD ? ' current' : '' %]'>[% head.name %]</td>
+   <td class='action-list'>[%# FIXME %]
+     <a href="[% c.uri_for_action('/ref/shortlog', [Repository.name, head.name]) %]" title="Short log" class="button shortlog">shortlog</a>
+     <a href="[% c.uri_for_action('/ref/longlog', [Repository.name, head.name]) %]" title="Long log" class="button longlog">log</a>
+     <a href="[% c.uri_for_action('/ref/tree', [Repository.name, head.name]) %]" title="Tree" class="button tree">tree</a>
+   </td>
+  </tr>
+ [% END %]
+ </tbody>
+</table>
diff --git a/root/fragment/repository/longlog.tt2 b/root/fragment/repository/longlog.tt2
new file mode 100755 (executable)
index 0000000..3f11484
--- /dev/null
@@ -0,0 +1,5 @@
+[% INCLUDE 'fragment/repository/shortlog.tt2' longlogformat = 1 %]
+
+
+<h2>Branches</h2>
+[% subinclude('/fragment/repository/heads', [Repository.name]) %]
diff --git a/root/fragment/repository/shortlog.tt2 b/root/fragment/repository/shortlog.tt2
new file mode 100755 (executable)
index 0000000..68167b8
--- /dev/null
@@ -0,0 +1,38 @@
+[% BLOCK shortlog_table_headfoot %]
+[% SET cell = type == 'head' ? 'th' : 'td' %]
+<tr>
+       <[% cell %]>ID (sha1)</[% cell %]>
+       <[% cell %]>Last change</[% cell %]>
+       <[% cell %]>Message</[% cell %]>
+       <[% cell %]>By</[% cell %]>
+       <[% cell %]>Actions</[% cell %]>
+</tr>
+[% END %]
+
+<table class='listing'>
+<thead>[% PROCESS shortlog_table_headfoot type = 'head' %]</thead>
+<tbody>
+ [% FOREACH line IN log_lines %]
+ <tr [% "class='invert'" IF loop.count % 2 %]>
+       <td class='sha1' title='[% line.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
+       <td class='time-since' title='[% line.authored_time %]'>[% time_since(line.authored_time) %]</td>
+       <td>
+               [% IF longlogformat %]
+                       [% message = line.comment | html; message.replace("\n", "<br/>") %]
+               [% ELSE %]
+                       [% short_cmt(line.comment) | html %]
+                       [% INCLUDE '_refs.tt2' object = line %]
+               [% END %]
+       </td>
+       <td class='author'><img src="[% uri_for_gravatar(line.author.email, 21) %]" />[% line.author.name | html %]</td>
+       <td class='action-list'>
+               <a href="[% c.uri_for_action("/ref/commit", [Repository.name, line.sha1]) %]" title="Commit details" class="button commit">commit</a>
+               <a href="[% c.uri_for_action("/ref/diff_fancy", [Repository.name, line.sha1]) %]" title="Commit difference" class="button diff">commitdiff</a>
+               <a href="[% c.uri_for_action("/ref/tree", [Repository.name, line.sha1]) %]" title="Tree" class="button tree">tree</a>
+       </td>
+</tr>
+[% END %]
+</tbody>
+</table>
+
+[% INCLUDE 'inc/log_pager.tt2' %]
diff --git a/root/fragment/repository/summary.tt2 b/root/fragment/repository/summary.tt2
new file mode 100755 (executable)
index 0000000..a2a160c
--- /dev/null
@@ -0,0 +1,5 @@
+<dl>
+ <dt>description</dt><dd>[% Repository.description %]</dd>
+ <dt>owner</dt><dd>[% Repository.owner %]</dd>
+ <dt>last change</dt><dd>[% time_since(Repository.last_change) %]</dd>
+</dl>
diff --git a/root/fragment/repository/tags.tt2 b/root/fragment/repository/tags.tt2
new file mode 100644 (file)
index 0000000..3f47409
--- /dev/null
@@ -0,0 +1 @@
+[% INCLUDE 'fragment/repository/heads.tt2' heads = tags %]
diff --git a/root/git-logo.png b/root/git-logo.png
deleted file mode 100644 (file)
index 16ae8d5..0000000
Binary files a/root/git-logo.png and /dev/null differ
diff --git a/root/gitweb.css b/root/gitweb.css
deleted file mode 100644 (file)
index e5473aa..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-body {
-       font-family: sans-serif;
-       font-size: small;
-       border: solid #d9d8d1;
-       border-width: 1px;
-       margin: 10px;
-       background-color: #ffffff;
-       color: #000000;
-}
-
-a {
-       color: #0000cc;
-}
-
-a:hover, a:visited, a:active {
-       color: #880000;
-}
-
-span.cntrl {
-       border: dashed #aaaaaa;
-       border-width: 1px;
-       padding: 0px 2px 0px 2px;
-       margin:  0px 2px 0px 2px;
-}
-
-img.logo {
-       float: right;
-       border-width: 0px;
-}
-
-div.page_header {
-       height: 25px;
-       padding: 8px;
-       font-size: 150%;
-       font-weight: bold;
-       background-color: #d9d8d1;
-}
-
-div.page_header a:visited, a.header {
-       color: #0000cc;
-}
-
-div.page_header a:hover {
-       color: #880000;
-}
-
-div.page_nav {
-       padding: 8px;
-}
-
-div.page_nav a:visited {
-       color: #0000cc;
-}
-
-div.page_path {
-       padding: 8px;
-       font-weight: bold;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-}
-
-div.page_footer {
-       height: 17px;
-       padding: 4px 8px;
-       background-color: #d9d8d1;
-}
-
-div.page_footer_text {
-       float: left;
-       color: #555555;
-       font-style: italic;
-}
-
-div.page_body {
-       padding: 8px;
-       font-family: monospace;
-}
-
-div.title, a.title {
-       display: block;
-       padding: 6px 8px;
-       font-weight: bold;
-       background-color: #edece6;
-       text-decoration: none;
-       color: #000000;
-}
-
-div.readme {
-       padding: 8px;
-}
-
-a.title:hover {
-       background-color: #d9d8d1;
-}
-
-div.title_text {
-       padding: 6px 0px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       font-family: monospace;
-}
-
-div.log_body {
-       padding: 8px 8px 8px 150px;
-}
-
-span.age {
-       position: relative;
-       float: left;
-       width: 142px;
-       font-style: italic;
-}
-
-span.signoff {
-       color: #888888;
-}
-
-div.log_link {
-       padding: 0px 8px;
-       font-size: 70%;
-       font-family: sans-serif;
-       font-style: normal;
-       position: relative;
-       float: left;
-       width: 136px;
-}
-
-div.list_head {
-       padding: 6px 8px 4px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 0px;
-       font-style: italic;
-}
-
-div.author_date {
-       padding: 8px;
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px 0px;
-       font-style: italic;
-}
-
-a.list {
-       text-decoration: none;
-       color: #000000;
-}
-
-a.subject, a.name {
-       font-weight: bold;
-}
-
-table.tags a.subject {
-       font-weight: normal;
-}
-
-a.list:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-a.text {
-       text-decoration: none;
-       color: #0000cc;
-}
-
-a.text:visited {
-       text-decoration: none;
-       color: #880000;
-}
-
-a.text:hover {
-       text-decoration: underline;
-       color: #880000;
-}
-
-table {
-       padding: 8px 4px;
-       border-spacing: 0;
-}
-
-table.diff_tree {
-       font-family: monospace;
-}
-
-table.combined.diff_tree th {
-       text-align: center;
-}
-
-table.combined.diff_tree td {
-       padding-right: 24px;
-}
-
-table.combined.diff_tree th.link,
-table.combined.diff_tree td.link {
-       padding: 0px 2px;
-}
-
-table.combined.diff_tree td.nochange a {
-       color: #6666ff;
-}
-
-table.combined.diff_tree td.nochange a:hover,
-table.combined.diff_tree td.nochange a:visited {
-       color: #d06666;
-}
-
-table.blame {
-       border-collapse: collapse;
-}
-
-table.blame td {
-       padding: 0px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-th {
-       padding: 2px 5px;
-       font-size: 100%;
-       text-align: left;
-}
-
-tr.light:hover {
-       background-color: #edece6;
-}
-
-tr.dark {
-       background-color: #f6f6f0;
-}
-
-tr.dark2 {
-       background-color: #f6f6f0;
-}
-
-tr.dark:hover {
-       background-color: #edece6;
-}
-
-td {
-       padding: 2px 5px;
-       font-size: 100%;
-       vertical-align: top;
-}
-
-td.link, td.selflink {
-       padding: 2px 5px;
-       font-family: sans-serif;
-       font-size: 70%;
-}
-
-td.selflink {
-       padding-right: 0px;
-}
-
-td.sha1 {
-       font-family: monospace;
-}
-
-td.error {
-       color: red;
-       background-color: yellow;
-}
-
-td.current_head {
-       text-decoration: underline;
-}
-
-table.diff_tree span.file_status.new {
-       color: #008000;
-}
-
-table.diff_tree span.file_status.deleted {
-       color: #c00000;
-}
-
-table.diff_tree span.file_status.moved,
-table.diff_tree span.file_status.mode_chnge {
-       color: #777777;
-}
-
-table.diff_tree span.file_status.copied {
-  color: #70a070;
-}
-
-/* noage: "No commits" */
-table.repository_list td.noage {
-       color: #808080;
-       font-style: italic;
-}
-
-/* age2: 60*60*24*2 <= age */
-table.repository_list td.age2, table.blame td.age2 {
-       font-style: italic;
-}
-
-/* age1: 60*60*2 <= age < 60*60*24*2 */
-table.repository_list td.age1 {
-       color: #009900;
-       font-style: italic;
-}
-
-table.blame td.age1 {
-       color: #009900;
-       background: transparent;
-}
-
-/* age0: age < 60*60*2 */
-table.repository_list td.age0 {
-       color: #009900;
-       font-style: italic;
-       font-weight: bold;
-}
-
-table.blame td.age0 {
-       color: #009900;
-       background: transparent;
-       font-weight: bold;
-}
-
-td.pre, div.pre, div.diff {
-       font-family: monospace;
-       font-size: 12px;
-       white-space: pre;
-}
-
-td.mode {
-       font-family: monospace;
-}
-
-/* styling of diffs (patchsets): commitdiff and blobdiff views */
-div.diff.header,
-div.diff.extended_header {
-       white-space: normal;
-}
-
-div.diff.header {
-       font-weight: bold;
-
-       background-color: #edece6;
-
-       margin-top: 4px;
-       padding: 4px 0px 2px 0px;
-       border: solid #d9d8d1;
-       border-width: 1px 0px 1px 0px;
-}
-
-div.diff.header a.path {
-       text-decoration: underline;
-}
-
-div.diff.extended_header,
-div.diff.extended_header a.path,
-div.diff.extended_header a.hash {
-       color: #777777;
-}
-
-div.diff.extended_header .info {
-       color: #b0b0b0;
-}
-
-div.diff.extended_header {
-       background-color: #f6f5ee;
-       padding: 2px 0px 2px 0px;
-}
-
-div.diff a.list,
-div.diff a.path,
-div.diff a.hash {
-       text-decoration: none;
-}
-
-div.diff a.list:hover,
-div.diff a.path:hover,
-div.diff a.hash:hover {
-       text-decoration: underline;
-}
-
-div.diff.to_file a.path,
-div.diff.to_file {
-       color: #007000;
-}
-
-div.diff.add {
-       color: #008800;
-}
-
-div.diff.from_file a.path,
-div.diff.from_file {
-       color: #aa0000;
-}
-
-div.diff.rem {
-       color: #cc0000;
-}
-
-div.diff.chunk_header a,
-div.diff.chunk_header {
-       color: #990099;
-}
-
-div.diff.chunk_header {
-       border: dotted #ffe0ff;
-       border-width: 1px 0px 0px 0px;
-       margin-top: 2px;
-}
-
-div.diff.chunk_header span.chunk_info {
-       background-color: #ffeeff;
-}
-
-div.diff.chunk_header span.section {
-       color: #aa22aa;
-}
-
-div.diff.incomplete {
-       color: #cccccc;
-}
-
-div.diff.nodifferences {
-       font-weight: bold;
-       color: #600000;
-}
-
-div.index_include {
-       border: solid #d9d8d1;
-       border-width: 0px 0px 1px;
-       padding: 12px 8px;
-}
-
-div.search {
-       font-size: 100%;
-       font-weight: normal;
-       margin: 4px 8px;
-       float: right;
-       top: 56px;
-       right: 12px
-}
-
-td.linenr {
-       text-align: right;
-}
-
-a.linenr {
-       color: #999999;
-       text-decoration: none
-}
-
-a.rss_logo {
-       float: right;
-       padding: 3px 0px;
-       width: 35px;
-       line-height: 10px;
-       border: 1px solid;
-       border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
-       color: #ffffff;
-       background-color: #ff6600;
-       font-weight: bold;
-       font-family: sans-serif;
-       font-size: 70%;
-       text-align: center;
-       text-decoration: none;
-}
-
-a.rss_logo:hover {
-       background-color: #ee5500;
-}
-
-span.refs span {
-       padding: 0px 4px;
-       font-size: 70%;
-       font-weight: normal;
-       border: 1px solid;
-       background-color: #ffaaff;
-       border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-
-span.refs span.ref {
-       background-color: #aaaaff;
-       border-color: #ccccff #0033cc #0033cc #ccccff;
-}
-
-span.refs span.tag {
-       background-color: #ffffaa;
-       border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-
-span.refs span.head {
-       background-color: #aaffaa;
-       border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-
-span.atnight {
-       color: #cc0000;
-}
-
-span.match {
-       color: #e00000;
-}
-
-div.binary {
-       font-style: italic;
-}
diff --git a/root/gitweb.tt2 b/root/gitweb.tt2
deleted file mode 100644 (file)
index dd1a5ed..0000000
+++ /dev/null
@@ -1 +0,0 @@
-[% gitweb_output %]
diff --git a/root/heads.tt2 b/root/heads.tt2
deleted file mode 100644 (file)
index baa8e4b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  <div>
-  [% Repository.name %]
-  </div>
-
-  [% INCLUDE '_heads.tt2' %]
-</div>
diff --git a/root/history.tt2 b/root/history.tt2
deleted file mode 100644 (file)
index babb960..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-[%
-  INCLUDE '_log_pager.tt2';
-  INCLUDE '_history.tt2';
-  INCLUDE '_log_pager.tt2';
-%]
diff --git a/root/inc/chroma_hash.tt2 b/root/inc/chroma_hash.tt2
new file mode 100755 (executable)
index 0000000..b07a1ea
--- /dev/null
@@ -0,0 +1,13 @@
+[%- sha1 = sha1 || HEAD -%]
+[%-
+    hptr = 0;
+    WHILE hptr < sha1.length - 6;
+      sha1part = sha1.substr(hptr, 6);
+      hptr = hptr + 6;
+    END;
+-%]
+
+<div class='button sha1_holder[% "_invert" IF loop.count && loop.count % 2 %]' style="background-color:#[% sha1part %]"></div>
+[% IF !hide_sha1_output %]
+<div class="sha1_label">[% sha1part %]</div>
+[% END %]
similarity index 73%
rename from root/_footer_feeds.tt2
rename to root/inc/footer_feeds.tt2
index 3e99e51..b99a5ee 100644 (file)
     <a
       class="rss_logo"
       title="[% feed_title %]"
-      href="[% c.uri_for('rss', feed_args) %]">RSS</a>
+      href="[% c.uri_for_action('/repository/rss', [Repository.name]) %]">RSS</a>
     <a
       class="rss_logo"
       title="[% feed_title %]"
-      href="[% c.uri_for('atom', feed_args) %]">Atom</a>
+      href="[% c.uri_for_action('/repository/atom', [Repository.name]) %]">Atom</a>
   [% ELSE %]
     <a
       class="rss_logo"
-      href="[% c.uri_for('opml') %]">OPML</a>
+      href="[% c.uri_for_action('/opml/opml') %]">OPML</a>
     <a
       class="rss_logo"
-      href="[% c.uri_for('repository_index') %]">TXT</a>
+      href="[% c.uri_for('/repository_index') %]">TXT</a>
   [% END %]
diff --git a/root/inc/log_pager.tt2 b/root/inc/log_pager.tt2
new file mode 100755 (executable)
index 0000000..e58aaab
--- /dev/null
@@ -0,0 +1,11 @@
+<ul class="pager">
+       [% IF log_lines.first.sha1 != Commit.sha1 || (log_lines.size != 25 && page) %]
+       <li class="pager_prev"><a href='[% c.uri_with(pg => (page||0) + 1) %]'>Newer commits</a></li>
+       [% END %]
+       
+       [% IF log_lines.first.sha1 != Commit.sha1 && log_lines.size == 25 %]    [% END %]
+       
+       [% IF log_lines.size == 25 %]
+       <li class="pager_next"><a href='[% c.uri_with(pg => (page||0) - 1) %]'>Older commits</a></li>
+       [% END %]
+</ul>
old mode 100644 (file)
new mode 100755 (executable)
index 9d1544d..5df3c17
@@ -1,45 +1,20 @@
-<div class='content'>
-  <form method="get" action="/" enctype="application/x-www-form-urlencoded">
-   <p class="projsearch">Search:
-    <input type="text" name="s" value="[% search_text %]" />
-   </p>
-  </form>
+[% BLOCK repos_table_headfoot %]
+[% SET cell = type == 'head' ? 'th' : 'td' %]
+<tr>
+       <[% cell %]></[% cell %]>
+    <[% cell %]>Repository</[% cell %]>
+    <!-- XXX These do nothing presently -->
+    <[% cell %]><a class="header" href="/?o=descr">Description</a></[% cell %]>
+    <[% cell %]><a class="header" href="/?o=age">Last change</a></[% cell %]>
+    <[% cell %]><a class="header" href="/?o=owner">By</a></[% cell %]>
+    <[% cell %]>Actions</[% cell %]>
+</tr>
+[% END %]
 
-  <table class="repository_list">
-    <thead>
-       <tr>
-           <th>Repository</th>
-           <!-- XXX These do nothing presently -->
-           <th><a class="header" href="/?o=descr">Description</a></th>
-           <th><a class="header" href="/?o=owner">Owner</a></th>
-           <th><a class="header" href="/?o=age">Last Change</a></th>
-           <th></th>
-       </tr>
-    </thead>
-    <tfoot>
-       <tr>
-           <td>Repository</td>
-           <!-- XXX These do nothing presently -->
-           <td><a href="/?o=descr">Description</a></td>
-           <td><a href="/?o=owner">Owner</a></td>
-           <td><a href="/?o=age">Last Change</a></td>
-           <td></td>
-       </tr>
-    </tfoot>
+<table class="listing">
+<thead>
+       [% INCLUDE repos_table_headfoot type = 'head' %]
+</thead>
+[% subinclude('/fragment/collectionofrepositories') %]
+</table>
 
-    <tbody class='listing'>
-      [% FOR p IN repositories %]
-       <tr>
-           <td><a class="list" href="[% c.uri_for('/summary', {p=p.name}) %]">[% p.name %]</a></td>
-           <td><a class="list" title="[% p.description %]" href="[% c.uri_for('/summary', {p=p.name} ) %]">[% abridged_description(p.description)  %]</a></td>
-           <td>[% p.owner %]</td>
-           <td class="time-since">[% time_since(p.last_change) %]</td>
-           <td class="link"><a href="[% c.uri_for("summary", {p=p.name}) %]">summary</a>
-               | <a href="[% c.uri_for("shortlog", {p=p.name}) %]">shortlog</a>
-               | <a href="[% c.uri_for("log", {p=p.name}) %]">log</a>
-               | <a href="[% c.uri_for("tree", {p=p.name}) %]">tree</a></td>
-       </tr>
-      [% END %]
-    </tbody>
-  </table>
-</div>
diff --git a/root/log.tt2 b/root/log.tt2
deleted file mode 100644 (file)
index 3e3908a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  [% INCLUDE '_log_pager.tt2' %]
-
-  <div id='log'>
-  [% FOREACH line IN log_lines %]
-    <div class='entry'>
-    <div class="message">
-       [% message = line.comment | html;
-         message.replace("\n", "<br/>") %]
-       <div class='chroma-hash'>[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1 %]</div>
-      </div>
-      <div class="meta">
-       <table class='summary' cellspacing='0' cellpadding='0'>
-       <tr><td>author</td><td class='author'>[% line.author.name | html %]</td></tr>
-       <tr><td>authored time</td><td class='time'>[% line.authored_time %]</td></tr>
-       [% IF line.author.name != line.committer.name %]
-       <tr><td>committer</td><td class='author'>[% line.committer.name | html %]</td></tr>
-       <tr><td>committered time</td><td class='time'>[% line.committed_time %]</td></tr>
-       [% END %]
-       </table>
-       <span class="time-since">[% time_since(line.authored_time) %]</span>
-       <div class="action-list">
-        <a href="[% c.uri_for("commit", {h=line.sha1}) %]">commit</a>
-       | <a href="[% c.uri_for("commitdiff", {h=line.sha1}) %]">commitdiff</a>
-       | <a href="[% c.uri_for("tree", {h=line.sha1, hb=line.sha1}) %]">tree</a>
-       </div>
-      </div>
-    </div>
-  [% END %]
-  </div>
-
-  [% INCLUDE '_log_pager.tt2' %]
-</div>
diff --git a/root/logo.svg b/root/logo.svg
deleted file mode 100644 (file)
index f0367df..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="65"
-   height="23"
-   id="svg2404"
-   sodipodi:version="0.32"
-   inkscape:version="0.46"
-   version="1.0"
-   sodipodi:docname="logo.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape"
-   inkscape:export-filename="/home/dbrook/dev/gitalist/root/logo.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs2406">
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 11.502125 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="71.847527 : 11.502125 : 1"
-       inkscape:persp3d-origin="35.923763 : 7.6680832 : 1"
-       id="perspective2400" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     gridtolerance="10000"
-     guidetolerance="10"
-     objecttolerance="10"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="7.9195959"
-     inkscape:cx="35.923763"
-     inkscape:cy="1.045551"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:showpageshadow="false"
-     inkscape:window-width="1280"
-     inkscape:window-height="950"
-     inkscape:window-x="0"
-     inkscape:window-y="25">
-    <inkscape:grid
-       type="xygrid"
-       id="grid2449"
-       visible="true"
-       enabled="true" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata2409">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-20.793031,-18.938722)">
-    <text
-       xml:space="preserve"
-       style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
-       x="60.946594"
-       y="35.124443"
-       id="text2577"
-       sodipodi:linespacing="125%"><tspan
-         sodipodi:role="line"
-         id="tspan2579"
-         x="60.946594"
-         y="35.124443"
-         style="font-size:20px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Candara;-inkscape-font-specification:Candara Italic">git</tspan></text>
-    <path
-       style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3357"
-       d="M 2.9800098,4.6496528 C 3.2880818,4.6583433 3.5963683,4.6566553 3.9045415,4.6581792 C 4.2900822,4.6480495 4.6799534,4.6882469 5.0628669,4.6352919 C 5.3415627,4.5839381 5.6225883,4.5523136 5.9053688,4.5367547 C 6.1586148,4.525911 6.4121605,4.5264303 6.6655841,4.5263393 C 6.9517438,4.5294138 7.2379286,4.5284888 7.5240995,4.5281299 C 7.7848081,4.527744 8.0455169,4.5275665 8.3062258,4.5274685 C 8.558755,4.527395 8.8112843,4.5273988 9.0638136,4.5273983 C 9.3162235,4.5274 9.5686335,4.5274028 9.8210434,4.5274043 C 10.073574,4.5274055 10.326104,4.5274056 10.578634,4.5274057 C 10.785275,4.5093013 11.024398,4.4841054 11.21515,4.3956888 C 11.416398,4.3090351 11.636109,4.2943885 11.852029,4.2819999 C 11.974627,4.276229 12.097361,4.2751928 12.220063,4.2738955 L 10.717011,5.3706722 C 10.596134,5.3743956 10.475147,5.3777149 10.354734,5.3898714 C 10.143057,5.4148447 9.938646,5.4621628 9.739379,5.5391785 C 9.519194,5.5818357 9.300299,5.6371449 9.0738739,5.6206779 C 8.8213422,5.620678 8.5688106,5.6206781 8.3162789,5.6206801 C 8.0638608,5.6206826 7.8114426,5.6206872 7.5590244,5.6206935 C 7.3065562,5.6206971 7.054088,5.6207114 6.8016198,5.6206066 C 6.5416878,5.6204588 6.2817558,5.6202017 6.0218249,5.6194038 C 5.7348811,5.6185593 5.4479171,5.6167199 5.1609836,5.6200676 C 4.9100951,5.6212274 4.6589774,5.6224823 4.4085648,5.6397412 C 4.1302379,5.6620999 3.8546293,5.7044164 3.5772175,5.7351221 C 3.1892034,5.7518431 2.8004601,5.7331854 2.412181,5.7424426 C 2.0961821,5.7439843 1.7800839,5.7424472 1.464178,5.7509691 L 2.9800098,4.6496528 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3359"
-       d="M 15.250399,4.5523455 C 15.590061,4.6402633 15.939932,4.6849319 16.280783,4.7668885 C 16.564174,4.8441138 16.854443,4.8817339 17.146909,4.8990287 C 17.399102,4.9098761 17.651613,4.9082786 17.903977,4.9078487 C 18.196362,4.8994477 18.48183,4.9060295 18.769953,4.954769 C 19.026435,5.0019753 19.286577,5.0188065 19.546736,5.0279525 C 19.798361,5.0344119 20.050099,5.0335836 20.301785,5.033395 C 20.554174,5.0330364 20.806562,5.0327599 21.058951,5.0326104 C 21.363806,5.0344648 21.668672,5.0355902 21.973534,5.0351006 C 22.256547,5.0342111 22.53956,5.0333216 22.822574,5.0328558 C 23.076911,5.0325027 23.331247,5.0324842 23.585583,5.0324647 C 23.837759,5.0324579 24.089935,5.0324695 24.34211,5.0324751 C 24.556292,5.0323195 24.775741,4.98369 24.945116,4.8497032 C 22.230527,6.5415415 23.325761,5.7734246 23.936091,5.386159 C 25.664647,4.3818697 25.018714,4.6323031 25.743129,4.3594463 L 24.285165,5.5176185 C 23.679415,5.8500541 24.148817,5.5935527 25.670884,4.7083218 C 24.971451,5.1881669 24.19828,5.5365941 23.494352,6.0234103 C 23.285581,6.1061415 23.061433,6.1343034 22.837331,6.1257613 C 22.585146,6.1257668 22.332961,6.1257785 22.080776,6.1257712 C 21.8268,6.1257511 21.572824,6.1257319 21.318849,6.1253744 C 21.037899,6.1249056 20.75695,6.1240082 20.476001,6.1231311 C 20.168754,6.1226612 19.861503,6.1238295 19.554265,6.1259938 C 19.301825,6.1262062 19.049385,6.1266353 18.796945,6.1266198 C 18.543601,6.1261977 18.290119,6.1261239 18.037005,6.1139367 C 17.773731,6.0982577 17.511863,6.0695772 17.252032,6.0234681 C 16.968885,5.9867088 16.684036,5.9998178 16.399079,6.0011342 C 16.144185,6.0005097 15.888907,6.0003272 15.634679,5.9794826 C 15.340021,5.9501258 15.050044,5.8949689 14.763813,5.8185376 C 14.417768,5.7462516 14.070072,5.6758601 13.720196,5.6259461 L 15.250399,4.5523455 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3361"
-       d="M 29.263724,4.7095281 C 29.543559,4.8008603 29.827126,4.8860904 30.113393,4.9564493 C 30.34092,5.0605715 30.57723,5.1193953 30.825121,5.1432959 C 31.137783,5.1697008 31.451743,5.1480706 31.763125,5.1862517 C 32.097281,5.2339857 32.433502,5.2624205 32.770587,5.2773118 C 33.034287,5.2866749 33.298197,5.2861541 33.562022,5.2860801 C 33.813967,5.2857484 34.065913,5.2853866 34.317858,5.2851965 C 34.570186,5.2850362 34.822513,5.2850255 35.074841,5.2850147 C 35.352046,5.2851166 35.629234,5.2868659 35.906443,5.2865131 C 36.180245,5.2863591 36.454048,5.2843413 36.727845,5.2864149 C 37.04472,5.2979745 37.355267,5.2922269 37.667843,5.2407986 C 37.96856,5.1837264 38.279499,5.2040028 38.578247,5.1418173 C 38.839416,5.0907446 39.101827,5.0275269 39.35035,4.9310873 C 39.194946,4.9727956 39.826949,4.660365 39.66612,4.7539434 C 36.910653,6.3572034 37.855073,5.8664227 38.350928,5.4796749 L 40.075242,4.7799543 C 39.460002,5.369098 38.652116,5.7191844 37.883509,6.0714455 C 37.623385,6.1466605 37.363086,6.2223438 37.093333,6.255085 C 36.789944,6.2847538 36.484875,6.2995069 36.18379,6.3506077 C 35.865823,6.3867145 35.546458,6.3736197 35.226924,6.376897 C 34.95322,6.3789267 34.67951,6.3769901 34.405801,6.3767916 C 34.127221,6.3764318 33.848662,6.3781783 33.570085,6.3783053 C 33.317768,6.3783415 33.06545,6.3783969 32.813134,6.3786503 C 32.561135,6.3789145 32.309135,6.3794844 32.057136,6.3789155 C 31.792731,6.3776272 31.528163,6.3754747 31.264194,6.358621 C 30.928192,6.3348706 30.59417,6.2935806 30.259243,6.2585132 C 29.9424,6.2411928 29.623899,6.2556697 29.308234,6.216721 C 29.057606,6.1786221 28.823039,6.0968746 28.586692,6.0067869 C 28.293107,5.9246568 27.998707,5.8444301 27.708397,5.7515096 L 29.263724,4.7095281 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3363"
-       d="M 3.1168348,13.771312 C 3.4871277,13.830242 3.8517342,13.918437 4.2191663,13.993351 C 4.8365888,14.10372 5.4653965,14.120181 6.0908892,14.134004 C 6.6183258,14.131945 7.1429297,14.196644 7.6688677,14.229611 C 8.0515007,14.250739 8.4347714,14.254448 8.8178923,14.254653 C 9.1096113,14.253798 9.4013255,14.251899 9.6930442,14.250937 C 9.9740054,14.249674 10.254968,14.250305 10.535904,14.254183 C 10.982187,14.259328 11.428493,14.259977 11.87479,14.257146 C 12.207864,14.240609 12.545321,14.278998 12.875726,14.230274 C 13.068494,14.196492 13.262384,14.171244 13.457002,14.151266 L 11.967231,15.259246 C 11.774069,15.282733 11.58171,15.312125 11.388239,15.333036 C 11.055505,15.349359 10.72238,15.336526 10.389376,15.336362 C 9.9403422,15.333461 9.4913003,15.334285 9.0422815,15.339402 C 8.7580303,15.343276 8.4737547,15.344192 8.189477,15.343616 C 7.9007121,15.343348 7.611941,15.342724 7.3231966,15.33893 C 6.9406694,15.333468 6.5578076,15.328364 6.1762486,15.298076 C 5.6614564,15.257676 5.1481752,15.190115 4.6309779,15.18897 C 3.9920801,15.170244 3.3496573,15.15468 2.7193715,15.038168 C 2.3411972,14.963105 1.9625517,14.890673 1.5798912,14.842263 L 3.1168348,13.771312 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3365"
-       d="M 8.6634088,9.7750099 C 8.6458015,10.16178 8.6464925,10.549292 8.6442839,10.93642 C 8.6582983,11.440766 8.6301257,11.942307 8.5881454,12.44451 C 8.5337827,12.858986 8.4688526,13.271539 8.4289363,13.687806 C 8.4021237,14.020874 8.401948,14.355087 8.3983622,14.68895 C 8.3981656,15.018792 8.3983046,15.348617 8.4006285,15.678453 C 8.4028493,15.95185 8.4038991,16.225253 8.4044537,16.498657 C 8.4048478,16.751439 8.40482,17.004221 8.4048186,17.257003 C 8.4298921,17.507752 8.4784656,17.755037 8.5041009,18.005961 C 8.5434103,18.243846 8.5067663,18.474507 8.4645558,18.708057 C 8.4302073,18.897923 8.4195037,19.090656 8.4113731,19.283059 L 6.7503165,20.12233 C 6.7554353,19.923404 6.76162,19.724053 6.7892032,19.526699 C 6.8239609,19.305546 6.8946704,19.092062 6.8549589,18.865832 C 6.8329143,18.608918 6.7901946,18.355593 6.747484,18.101454 C 6.7474896,17.848568 6.7474797,17.595682 6.7478122,17.342797 C 6.7482679,17.068341 6.7491362,16.793887 6.7506446,16.519436 C 6.7520929,16.188741 6.7505754,15.858071 6.7512435,15.527378 C 6.7502863,15.188506 6.7528272,14.849176 6.7774631,14.511059 C 6.8113889,14.090591 6.8881912,13.675821 6.9548568,13.259611 C 6.9964813,12.762612 7.0358084,12.266987 7.0130806,11.767559 C 7.0108963,11.386934 7.0118141,11.005886 6.9939556,10.625639 L 8.6634088,9.7750099 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3385"
-       d="M 16.418357,13.872083 C 17.620556,13.906529 18.81114,14.095792 20.007704,14.208839 C 21.525842,14.340227 23.049286,14.380783 24.572309,14.397281 L 23.184327,15.399419 C 21.680967,15.324869 20.177069,15.257697 18.675006,15.160028 C 17.424243,15.066311 16.171754,14.895255 14.915705,14.963823 L 16.418357,13.872083 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3389"
-       d="M 20.665339,9.7587118 C 20.722349,10.222553 20.732976,10.690339 20.742278,11.157055 C 20.752317,11.867666 20.748409,12.578364 20.751788,13.289015 C 20.710129,14.254618 20.729215,15.221865 20.751486,16.187776 C 20.770064,16.589489 20.837044,16.986383 20.87711,17.386039 C 20.897733,17.623421 20.899385,17.86205 20.90283,18.100172 L 19.250272,18.944315 C 19.248893,18.710214 19.246043,18.476078 19.230695,18.242386 C 19.202752,17.829538 19.153408,17.417928 19.150089,17.003664 C 19.172458,16.037771 19.193452,15.070443 19.150914,14.104876 C 19.15304,13.397944 19.150029,12.690864 19.130336,11.984165 C 19.11369,11.535866 19.104462,11.082675 19.013357,10.641937 L 20.665339,9.7587118 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3395"
-       d="M 27.748495,13.422094 C 28.386624,13.611665 29.070496,13.603923 29.730064,13.632045 C 30.989831,13.655712 32.249313,13.668109 33.508743,13.707449 C 33.974488,13.69315 34.430129,13.792746 34.889985,13.844471 C 35.200366,13.870981 35.509644,13.791535 35.813309,13.73547 C 36.045716,13.691233 36.280557,13.66388 36.515949,13.64216 L 35.029661,14.744938 C 34.799369,14.770068 34.569725,14.800725 34.342514,14.846717 C 34.024589,14.908428 33.699995,14.967203 33.376203,14.914662 C 32.942158,14.850743 32.513228,14.745497 32.071717,14.741097 C 30.826118,14.660344 29.579915,14.646151 28.331923,14.64196 C 27.620099,14.632436 26.882897,14.670449 26.193168,14.464075 L 27.748495,13.422094 z"
-       transform="translate(20.793031,18.938722)" />
-    <path
-       style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-opacity:1"
-       id="path3397"
-       d="M 32.526448,9.4122312 C 32.500107,9.9556963 32.498962,10.500074 32.495247,11.044059 C 32.492249,11.791325 32.489686,12.538611 32.493317,13.285877 C 32.491367,13.826812 32.50501,14.367614 32.514782,14.908416 C 32.513335,15.237763 32.576715,15.561888 32.603737,15.889377 C 32.627479,16.294591 32.622776,16.699633 32.671404,17.102956 C 32.697134,17.385271 32.74629,17.660115 32.815308,17.934389 C 32.864178,18.127769 32.880543,18.326507 32.892246,18.524917 L 31.23792,19.37883 C 31.229282,19.19133 31.217981,19.003211 31.175925,18.819622 C 31.108969,18.53489 31.057669,18.25152 31.039661,17.958523 C 30.993406,17.553538 30.991036,17.14824 30.966344,16.741621 C 30.935983,16.409373 30.868687,16.079151 30.872291,15.745307 C 30.882015,15.197212 30.895594,14.649118 30.893755,14.100895 C 30.897365,13.354284 30.894848,12.607653 30.891825,11.861043 C 30.888157,11.327662 30.887401,10.793844 30.860624,10.261011 L 32.526448,9.4122312 z"
-       transform="translate(20.793031,18.938722)" />
-  </g>
-</svg>
index 52ca63e..7845ca9 100644 (file)
@@ -1,20 +1,26 @@
+[%- SET object = commit || head -%]
 <div class='actions'>
+  <div class="clear"></div>
+  <div class='grid_8'>
   <!-- This should probably be a real LIst -->
-    <a href="[% c.uri_for('summary') %]">summary</a> &bull;
-    <a href="[% c.uri_for('shortlog', {h=object.sha1}) %]">shortlog</a> &bull;
-    <a href="[% c.uri_for('log', {h=object.sha1}) %]">log</a> &bull;
-    <a href="[% c.uri_for('commit', {h=object.sha1}) %]">commit</a> &bull;
-    <a href="[% c.uri_for('commitdiff', {h=object.sha1}) %]">commitdiff</a>
-    [% IF object.type == 'commit' %] &bull;
-    <a href="[% c.uri_for('tree', {h=object.tree_sha1, hb=object.sha1}) %]">tree</a>
+    <a href="[% c.uri_for_action('/repository/summary', [c.req.captures.0]) %]">summary</a> &bull;
+    [% IF c.req.captures.size == 1; SET path = 'repository'; ELSE; SET path = 'ref'; END %]
+    <a href="[% c.uri_for_action('/' _ path _ '/shortlog', c.req.captures) %]">shortlog</a> &bull;
+    <a href="[% c.uri_for_action('/' _ path _ '/longlog', c.req.captures) %]">log</a>
+    [% IF Commit %]
+        &sect;
+        <a href="[% c.uri_for_action('/ref/commit', [c.req.captures.0, Commit.sha1]) %]">commit</a> &bull;
+        <a href="[% c.uri_for_action('/ref/diff_fancy', [c.req.captures.0, Commit.sha1]) %]">commitdiff</a> &bull;
+        <a href="[% c.uri_for_action('/ref/tree', [c.req.captures.0, Commit.sha1]) %]">tree</a>
     [% END %]
-    [% IF filename %]
+    [% IF filename && c.action != 'ref/tree' %]
     &sect;
-    <a href="[% c.uri_for('blob', {h=object.sha1,f=filename}) %]">blob</a> &bull;
-    <a href="[% c.uri_for('blob_plain', {h=object.sha1,f=filename}) %]">raw</a> &bull;
-    <a href="[% c.uri_for('blame', {h=object.sha1,f=filename}) %]">blame</a> &bull;
-    <a href="[% c.uri_for('shortlog', {h=object.sha1,f=filename}) %]">history</a> &bull;
-    <a href="[% c.uri_for(action, {f=filename}) %]">HEAD</a>
+    <a href="[% c.uri_for_action('/ref/blob', [c.req.captures.0, Commit.sha1], filename) %]">blob</a> &bull;
+    <a href="[% c.uri_for_action('/ref/raw', [c.req.captures.0, Commit.sha1], filename) %]">raw</a> &bull;
+    <a href="[% c.uri_for_action('/ref/blame', [c.req.captures.0, Commit.sha1], filename)  %]">blame</a> &bull;
+    <a href="[% c.uri_for_action('/ref/history', [c.req.captures.0, Commit.sha1], filename)  %]">history</a> &bull;
+    <a href="[% c.uri_for(c.controller.action_for('commit'), [c.req.captures.0, Repository.head_hash]) %]">HEAD</a>
     [% END %]
-    <div class='chroma-hash'>[% INCLUDE '_chroma_hash.tt2' sha1 = object.sha1 %]</div>
+  </div>
+  <div class='grid_4'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = object.sha1 %]</div>
 </div>
old mode 100644 (file)
new mode 100755 (executable)
index eef339c..3567c09
@@ -1,7 +1,6 @@
-<div class='path'>
- <a href="[% c.uri_for("tree", {hb=head.sha1}) %]">[% Repository.name %]</a>
+ <a href="[% c.uri_for_action("/ref/tree", [Repository.name, Commit.sha1]) %]">[% Repository.name %] (tree)</a>
  [% FOREACH part IN filename.split('/') %]
   [% path = loop.first ? part : path _ '/' _ part %]
-  / <a href="[% c.uri_for(loop.last ? 'blob' : 'tree', {hb=head.sha1,f=path}) %]">[% part %]</a>
+  / <a href="[% action_name = loop.last ? '/ref/blob' : '/ref/tree'; c.uri_for_action(action_name, [Repository.name,Commit.sha1], path.to_path ) %]">[% part %]</a>
  [% END %]
-</div>
+
old mode 100644 (file)
new mode 100755 (executable)
index 35c3d88..8e15e07
@@ -1,9 +1,9 @@
+[% IF Repository %]
 <div id="page-search">
-  <form method="get" action="[% c.uri_for('search') %]" enctype="application/x-www-form-urlencoded">
-  <input name="p" type="hidden" value="[% Repository.name %]">
-  <input name="a" type="hidden" value="search">
-  <input name="h" type="hidden" value="[% commit.sha1 %]">
-  <input name="f" type="hidden" value="[% c.req.param('f') %]">
+  <form method="get" action="[% c.uri_for_action('/repository/search', [Repository.name]) %]" enctype="application/x-www-form-urlencoded">
+  Search
+  <input type="text" name="text" value="[% c.req.param('s') %]" />
   <select name="type">
       <option value="commit">commit</option>
       <option value="author">author</option>
@@ -12,8 +12,9 @@
       <option value="grep">grep</option>
       <option value="pickaxe">pickaxe</option>
       -->
-  </select><sup><a href="[% c.uri_for('search_help') %]">?</a></sup> search:
-  <input type="text" name="text" value="[% c.req.param('s') %]">
+  </select>
   <span title="Extended regular expression"><label><input type="checkbox" name="regexp" value="1">re</label></span>
+   <sup><a href="[% c.uri_for_action('/search_help') %]">?</a></sup> 
   </form>
 </div>
+[% END %]
diff --git a/root/opml.tt2 b/root/opml.tt2
new file mode 100644 (file)
index 0000000..35f22e3
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<opml version="1.1">
+  <body>[% count = 1 %]
+    [% FOR Repository = Repositories %]
+    <outline id="[% count %]"
+             description=""
+             text="[% Repository.name | html_entity %] - [% Repository.description | html_entity %]"
+             title=""
+             type="rss"
+             version="RSS"
+             xmlUrl="[% c.uri_for_action('/repository/rss', [ Repository.name ])%]" />
+    [% SET count = count + 1; END %]
+    </body>
+    <head>
+        <dateCreated>[% now %]</dateCreated>
+        <dateModified>[% now %]</dateModified>
+        <title>[% title | html_entity %]</title>
+    </head>
+</opml>
diff --git a/root/ref/blame.tt2 b/root/ref/blame.tt2
new file mode 100755 (executable)
index 0000000..f943636
--- /dev/null
@@ -0,0 +1,37 @@
+[%- BLOCK blame_table_headfoot %]
+<tr>
+ <th>Author</th>
+ <th>Date</th>
+ <th width="80" nowrap="nowrap">ID (sha1)</thd>
+ <th></tdh>
+ <th class='data'>Data</th>
+</tr>
+[% END -%]
+
+[%- INCLUDE inc/syntax_highlight_css.tt2 -%]
+
+
+
+
+
+
+
+<div id='blame'>
+<table class="listing">
+ <thead>
+  [% PROCESS blame_table_headfoot %]
+ </thead>
+
+ <tbody>
+ [% subinclude('/fragment/ref/blame', c.req.captures, c.req.arguments.to_path ) %]
+ </tbody>
+</table>
+</div>
+
+<h2>[% INCLUDE 'nav/path.tt2' %]</h2>
+
+
+[% IF object.type == 'commit' %]
+<div class='commit-message'>[% short_cmt(head.comment) %]</div>
+[% END %]
+
old mode 100644 (file)
new mode 100755 (executable)
similarity index 50%
rename from root/blob.tt2
rename to root/ref/blob.tt2
index 06077b2..ad81803
@@ -1,14 +1,14 @@
 [% INCLUDE inc/syntax_highlight_css.tt2 %]
-[% PROCESS 'nav/actions.tt2' object = head %]
 
-<div class='content'>
+<h3>[% INCLUDE 'nav/path.tt2' %]</h3>
+
   [% IF object.type == 'commit' %]
    <div class='commit-message'>
     [% short_cmt(head.comment) %]
    </div>
   [% END %]
-  [% INCLUDE 'nav/path.tt2' %]
-  <div>
-   <pre class='blob'>[% blob %]</pre>
-  </div>
-</div>
+       <pre class='blob'>[% subinclude('/fragment/ref/blob', c.req.captures, c.req.args.to_path) | html %]</pre>
+
+
\ No newline at end of file
diff --git a/root/ref/commit.tt2 b/root/ref/commit.tt2
new file mode 100644 (file)
index 0000000..2d79fa7
--- /dev/null
@@ -0,0 +1,4 @@
+
+<div class='content'>
+  [% subinclude('/fragment/ref/commit', c.req.captures) %]
+</div>
diff --git a/root/ref/diff_fancy.tt2 b/root/ref/diff_fancy.tt2
new file mode 100755 (executable)
index 0000000..312c097
--- /dev/null
@@ -0,0 +1,25 @@
+
+<table class='listing'>
+<thead>
+<tr>
+       <th>ID (sha1)</th>
+       <th>Last change</th>
+       <th>Message</th>
+       <th>By</th>
+</tr>
+</thead>
+<tbody>
+<tr class="invert">
+       <td class='sha1' title='[% Commit.sha1 %]'>[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7) %]</td>
+       <td class='time-since' title='[% Commit.authored_time %]'>[% time_since(Commit.authored_time) %]</td>
+       <td>[% short_cmt(Commit.comment) | html %]</td>
+       <td class='author'><img src="[% uri_for_gravatar(Commit.committer.email, 21) %]" />[% Commit.author.name | html %]</td>
+</tr>
+</tbody>
+</table>
+
+
+  [%
+    # What I really want is @{ c.req.args }
+    subinclude('/fragment/' _ c.action, c.req.captures, c.req.args.0 c.req.args.1)
+  %]
diff --git a/root/ref/diff_plain.tt2 b/root/ref/diff_plain.tt2
new file mode 100644 (file)
index 0000000..0f98a04
--- /dev/null
@@ -0,0 +1,2 @@
+[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.args.0 c.req.args.1) %]
+
diff --git a/root/ref/history.tt2 b/root/ref/history.tt2
new file mode 100644 (file)
index 0000000..5820d4d
--- /dev/null
@@ -0,0 +1,2 @@
+[%# FIXME - Pager links are broken because I can't figure out how to pass the parameters %]
+[% subinclude('/fragment/ref/history', c.req.captures, c.req.args.to_path) %]
diff --git a/root/ref/longlog.tt2 b/root/ref/longlog.tt2
new file mode 100644 (file)
index 0000000..e927a16
--- /dev/null
@@ -0,0 +1 @@
+[% PROCESS 'repository/longlog.tt2' %]
diff --git a/root/ref/shortlog.tt2 b/root/ref/shortlog.tt2
new file mode 100644 (file)
index 0000000..4175b41
--- /dev/null
@@ -0,0 +1 @@
+[% PROCESS 'repository/shortlog.tt2' %]
diff --git a/root/ref/tree.tt2 b/root/ref/tree.tt2
new file mode 100755 (executable)
index 0000000..b477e0c
--- /dev/null
@@ -0,0 +1,8 @@
+  [%
+    IF path;
+      INCLUDE 'nav/path.tt2' filename = path;
+    END;
+
+    subinclude('/fragment/ref/tree', c.req.captures, c.req.args.to_path);
+  %]
+
diff --git a/root/repository/atom.tt2 b/root/repository/atom.tt2
new file mode 100644 (file)
index 0000000..bd143a1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://purl.org/atom/ns#">
+  <title>[% title | html_entity %]</title>
+  <modified>[% updated %]</modified>
+  [% FOREACH Commit = Commits %]
+  <entry>
+    <title>[% Commit.title | html_entity %]</title>
+    <id>[% Commit.id %]</id>
+    <content mode="xml">
+      <div xmlns="http://www.w3.org/1999/xhtml">[% Commit.content | html_entity %]</div>
+    </content>
+  </entry>
+  [% END %]
+</feed>
diff --git a/root/repository/heads.tt2 b/root/repository/heads.tt2
new file mode 100644 (file)
index 0000000..1d8d910
--- /dev/null
@@ -0,0 +1 @@
+[% subinclude('/fragment/repository/heads', c.req.captures) %]
diff --git a/root/repository/longlog.tt2 b/root/repository/longlog.tt2
new file mode 100644 (file)
index 0000000..a0c9f4b
--- /dev/null
@@ -0,0 +1 @@
+[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.parameters) %]
similarity index 100%
rename from root/reflog.tt2
rename to root/repository/reflog.tt2
diff --git a/root/repository/rss.tt2 b/root/repository/rss.tt2
new file mode 100644 (file)
index 0000000..bd2c766
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0" xmlns:blogChannel="http://backend.userland.com/blogChannelModule">
+
+<channel>
+<title>[% title | html_entity %]</title>
+<link>[% c.uri_for_action('/repository/summary', [Repository.name]) %]</link>
+<description>[% Repository.description | html_entity %]</description>
+<language>[% lang %]</language>
+<pubDate>[% pubDate %]</pubDate>
+<lastBuildDate>[% lastBuildDate %]</lastBuildDate>
+[% FOREACH Commit = Commits %]
+<item>
+<title>[% Commit.title | html_entity %]</title>
+<description>[% Commit.description | html_entity %]</description>
+<guid isPermaLink="true">[% Commit.permaLink %]</guid>
+</item>
+[% END %]
+</channel>
+</rss>
diff --git a/root/repository/search.tt2 b/root/repository/search.tt2
new file mode 100644 (file)
index 0000000..76b3e6a
--- /dev/null
@@ -0,0 +1,30 @@
+<div class='content'>
+  [%# XXX Nabbed the HTML below from gitweb's log action. %]
+  [% FOREACH result IN results %]
+  <div class="header">
+   <a class="title" href="[% c.uri_for_action('/ref/commit', [ Repository.name, result.sha1 ]) %]">
+    <span class="age">[% result.authored_time %]</span>
+    [% short_cmt(result.comment) | html %]
+   </a>
+  </div>
+
+  <div class="title_text">
+   <div class="log_link">
+     <a href="[% c.uri_for_action('/ref/commit', [Repository.name, result.sha1]) %]">commit</a>
+   | <a href="[% c.uri_for_action('/ref/diff_fancy', [Repository.name, result.sha1]) %]">commitdiff</a>
+   | <a href="[% c.uri_for_action('/ref/tree', [Repository.name, result.tree_sha1]); %][%# hb=line.sha1 %]">tree</a>
+   </div>
+   <i>[% result.author.name | html %] [% line.authored_time %]</i>
+  </div>
+
+  <div class="log_body">
+   [%
+     # XXX This is fragile at best.
+     html_comment = result.comment | html;
+     html_comment.replace(
+       c.req.param('text'), '<span class="match">' _ c.req.param('text') _ '</span>'
+     );
+   %]
+  </div>
+  [% END %]
+</div>
diff --git a/root/repository/shortlog.tt2 b/root/repository/shortlog.tt2
new file mode 100755 (executable)
index 0000000..90f7ea4
--- /dev/null
@@ -0,0 +1,2 @@
+[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.parameters) %]
+
diff --git a/root/repository/summary.tt2 b/root/repository/summary.tt2
new file mode 100755 (executable)
index 0000000..a0646e8
--- /dev/null
@@ -0,0 +1,15 @@
+
+
+  <!-- [% subinclude('/fragment/repository/summary', c.req.captures) %]
+
+ <h2><a href='[% c.uri_for(c.controller.action_for('shortlog'), c.req.captures) %]'>shortlog</a></h2> -->
+  [% subinclude('/fragment/repository/shortlog', c.req.captures) %]
+
+  <h2><a href='[% c.uri_for(c.controller.action_for('heads'), c.req.captures) %]'>Branches</a></h2>
+  [% subinclude('/fragment/repository/heads', c.req.captures) %]
+
+  [% IF Repository.tags.size > 0 %]
+  <h2><a href='[% c.uri_for(c.controller.action_for('tags'), c.req.captures) %]'>Tags</a></h2>
+  [% subinclude('/fragment/repository/tags', c.req.captures) %]
+  [% END %]
+
diff --git a/root/repository/tags.tt2 b/root/repository/tags.tt2
new file mode 100755 (executable)
index 0000000..3518081
--- /dev/null
@@ -0,0 +1,7 @@
+<div class='content'>
+  <div>
+  [% Repository.name %]
+  </div>
+
+  [% subinclude('/fragment/repository/tags', c.req.captures) %]
+</div>
index 2504b82..06d845c 100644 (file)
@@ -1,35 +1 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-<div class='content'>
-  [%# INCLUDE '_log_pager.tt2' %]
-
-  [%# XXX Nabbed the HTML below from gitweb's log action. %]
-  [% FOREACH result IN results %]
-  <div class="header">
-   <a class="title" href="[% c.uri_for('commit', {h=result.sha1}) %]">
-    <span class="age">[% result.authored_time %]</span>
-    [% short_cmt(result.comment) | html %]
-   </a>
-  </div>
-
-  <div class="title_text">
-   <div class="log_link">
-     <a href="[% c.uri_for("", {h=result.sha1}) %]">commit</a>
-   | <a href="[% c.uri_for("", {h=result.sha1}) %]">commitdiff</a>
-   | <a href="[% c.uri_for("", {h=result.tree_sha1, hb=line.sha1}) %]">tree</a>
-   </div>
-   <i>[% result.author.name | html %] [% line.authored_time %]</i>
-  </div>
-
-  <div class="log_body">
-   [%
-     # XXX This is fragile at best.
-     html_comment = result.comment | html;
-     html_comment.replace(
-       c.req.param('text'), '<span class="match">' _ c.req.param('text') _ '</span>'
-     );
-   %]
-  </div>
-  [% END %]
-
-  [%# INCLUDE '_log_pager.tt2' %]
-</div>
+search
index c23a414..60a97a1 100644 (file)
@@ -1,5 +1,3 @@
-[% PROCESS 'nav/actions.tt2' object = head %]
-
 <p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
 regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
 the pattern entered is recognized as the POSIX extended
diff --git a/root/shortlog.tt2 b/root/shortlog.tt2
deleted file mode 100644 (file)
index 6f9636e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-[%
-  INCLUDE '_log_pager.tt2';
-  INCLUDE '_shortlog.tt2';
-  INCLUDE '_log_pager.tt2';
-%]
-</div>
diff --git a/root/static/css/960.css b/root/static/css/960.css
new file mode 100644 (file)
index 0000000..e77dff3
--- /dev/null
@@ -0,0 +1,269 @@
+
+/* Containers
+----------------------------------------------------------------------------------------------------*/
+.container_12 {
+       margin-left: auto;
+       margin-right: auto;
+       width: 960px;
+}
+
+/* Grid >> Global
+----------------------------------------------------------------------------------------------------*/
+.grid_1,
+.grid_2,
+.grid_3,
+.grid_4,
+.grid_5,
+.grid_6,
+.grid_7,
+.grid_8,
+.grid_9,
+.grid_10,
+.grid_11,
+.grid_12{
+       display:inline;
+       float: left;
+       position: relative;
+       margin-left: 10px;
+       margin-right: 10px;
+}
+
+/* Grid >> Children (Alpha ~ First, Omega ~ Last)
+----------------------------------------------------------------------------------------------------*/
+
+.alpha {
+       margin-left: 0;
+}
+
+.omega {
+       margin-right: 0;
+}
+
+/* Grid >> 2 Columns
+----------------------------------------------------------------------------------------------------*/
+    .container_12 .grid_1 {
+       width:60px;
+}
+    .container_12 .grid_2 {
+       width:140px;
+}
+    .container_12 .grid_3 {
+       width:220px;
+}
+    .container_12 .grid_4 {
+       width:300px;
+}
+    .container_12 .grid_5 {
+       width:380px;
+}
+    .container_12 .grid_6 {
+       width:460px;
+}
+    .container_12 .grid_7 {
+       width:540px;
+}
+    .container_12 .grid_8 {
+       width:620px;
+}
+    .container_12 .grid_9 {
+       width:700px;
+}
+    .container_12 .grid_10 {
+       width:780px;
+}
+    .container_12 .grid_11 {
+       width:860px;
+}
+    .container_12 .grid_12 {
+       width:940px;
+}
+
+
+
+
+/* Prefix Extra Space >> 2 Columns
+----------------------------------------------------------------------------------------------------*/
+    .container_12 .prefix_1 {
+       padding-left:80px;
+}
+    .container_12 .prefix_2 {
+       padding-left:160px;
+}
+    .container_12 .prefix_3 {
+       padding-left:240px;
+}
+    .container_12 .prefix_4 {
+       padding-left:320px;
+}
+    .container_12 .prefix_5 {
+       padding-left:400px;
+}
+    .container_12 .prefix_6 {
+       padding-left:480px;
+}
+    .container_12 .prefix_7 {
+       padding-left:560px;
+}
+    .container_12 .prefix_8 {
+       padding-left:640px;
+}
+    .container_12 .prefix_9 {
+       padding-left:720px;
+}
+    .container_12 .prefix_10 {
+       padding-left:800px;
+}
+    .container_12 .prefix_11 {
+       padding-left:880px;
+}
+
+
+/* Suffix Extra Space >> 2 Columns
+----------------------------------------------------------------------------------------------------*/
+    .container_12 .suffix_1 {
+       padding-right:80px;
+}
+    .container_12 .suffix_2 {
+       padding-right:160px;
+}
+    .container_12 .suffix_3 {
+       padding-right:240px;
+}
+    .container_12 .suffix_4 {
+       padding-right:320px;
+}
+    .container_12 .suffix_5 {
+       padding-right:400px;
+}
+    .container_12 .suffix_6 {
+       padding-right:480px;
+}
+    .container_12 .suffix_7 {
+       padding-right:560px;
+}
+    .container_12 .suffix_8 {
+       padding-right:640px;
+}
+    .container_12 .suffix_9 {
+       padding-right:720px;
+}
+    .container_12 .suffix_10 {
+       padding-right:800px;
+}
+    .container_12 .suffix_11 {
+       padding-right:880px;
+}
+
+
+/* Push Space >> 2 Columns
+----------------------------------------------------------------------------------------------------*/
+    .container_12 .push_1 {
+       left:80px;
+}
+    .container_12 .push_2 {
+       left:160px;
+}
+    .container_12 .push_3 {
+       left:240px;
+}
+    .container_12 .push_4 {
+       left:320px;
+}
+    .container_12 .push_5 {
+       left:400px;
+}
+    .container_12 .push_6 {
+       left:480px;
+}
+    .container_12 .push_7 {
+       left:560px;
+}
+    .container_12 .push_8 {
+       left:640px;
+}
+    .container_12 .push_9 {
+       left:720px;
+}
+    .container_12 .push_10 {
+       left:800px;
+}
+    .container_12 .push_11 {
+       left:880px;
+}
+
+
+
+
+/* Pull Space >> 2 Columns
+----------------------------------------------------------------------------------------------------*/
+    .container_12 .pull_1 {
+       right:80px;
+}
+    .container_12 .pull_2 {
+       right:160px;
+}
+    .container_12 .pull_3 {
+       right:240px;
+}
+    .container_12 .pull_4 {
+       right:320px;
+}
+    .container_12 .pull_5 {
+       right:400px;
+}
+    .container_12 .pull_6 {
+       right:480px;
+}
+    .container_12 .pull_7 {
+       right:560px;
+}
+    .container_12 .pull_8 {
+       right:640px;
+}
+    .container_12 .pull_9 {
+       right:720px;
+}
+    .container_12 .pull_10 {
+       right:800px;
+}
+    .container_12 .pull_11 {
+       right:880px;
+}
+
+
+/* Clear Floated Elements
+----------------------------------------------------------------------------------------------------*/
+
+
+.clear {
+       clear: both;
+       display: block;
+       overflow: hidden;
+       visibility: hidden;
+       width: 0;
+       height: 0;
+}
+
+
+.clearfix:after {
+       clear: both;
+       content: ' ';
+       display: block;
+       font-size: 0;
+       line-height: 0;
+       visibility: hidden;
+       width: 0;
+       height: 0;
+}
+
+.clearfix {
+       display: inline-block;
+}
+
+* html .clearfix {
+       height: 1%;
+}
+
+.clearfix {
+       display: block;
+}
\ No newline at end of file
index 8d05e3c..ef9286f 100644 (file)
@@ -36,7 +36,7 @@ p {margin:0 0 1.5em;}
 p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;}
 p img.right {float:right;margin:1.5em 0 1.5em 1.5em;}
 a:focus, a:hover {color:#000;}
-a {color:#009;text-decoration:underline;}
+a {text-decoration:underline;}
 blockquote {margin:1.5em;color:#666;font-style:italic;}
 strong {font-weight:bold;}
 em, dfn {font-style:italic;}
diff --git a/root/static/css/core.css b/root/static/css/core.css
new file mode 100755 (executable)
index 0000000..698445d
--- /dev/null
@@ -0,0 +1,444 @@
+#debug_holder{
+       
+       display:none;
+       
+       clear:both;
+       padding-top:30px;
+       margin:30px 0;
+}
+
+#debug_holder pre{
+       margin:0;
+       padding:10px;
+       border:1px solid #ddd;
+       background-color:#f0f0f0;
+}
+
+
+body{
+       background:#FAFAFA url(/static/i/bg.png) repeat-x left top;
+       padding:0;
+       margin:0;
+       font-family:Arial, Verdana, sans-serif;
+       font-size:80%;
+}
+a img{
+       border:0;
+}
+
+
+/* structure */
+.sub_holder{
+       width:970px;
+       margin:0 auto;
+       text-align:left;
+}
+
+#header_holder{
+       margin-top:20px;
+}
+#header{
+       height:60px;
+}
+
+#content_holder{
+       background:transparent url([% c.uri_for('/static/i/bg_content.png') %]) repeat-y center center;
+}
+#content{
+       min-height:200px;
+       padding:0 12px 40px 10px;
+       background:transparent url([% c.uri_for('/static/i/bg_bottom.png') %]) no-repeat center bottom;
+}
+#content_inner{
+       padding-bottom:30px;
+       background:transparent url([% c.uri_for('/static/i/bg_top.png') %]) no-repeat;
+}
+#logo{
+       margin-left:-10px;
+       float:left;
+}
+#header .search{
+       margin-right:10px;
+       float:right;
+}
+#git_logo{
+       float:left;
+       margin-left:15px;
+}
+#feeds{
+       float:right;
+       margin-right:15px;
+}
+#footer_holder{
+       margin-bottom:100px;
+}
+#footer p{
+       margin-left:15px;
+}
+
+
+/* nav tabs */
+#nav_logs{
+       width:80%;
+       clear:both;
+       float:right;
+       margin:-5px 10px 0 0;
+}
+#nav_logs ul{
+       margin:0;
+       padding:0;
+}
+#nav_logs li{
+       display:block;
+       float:right;
+       list-style:none;
+       margin:0;
+       padding:0;
+}
+#nav_logs li a{
+       display:block;
+       margin-left:20px;
+       padding:10px 15px 10px 48px;
+       color:#ffffff;
+       font-size:1.4em;
+       text-transform:uppercase;
+       text-decoration:none;
+}
+a#log_short{
+       background:#666 url([% c.uri_for('/static/i/icons/shortlog.gif') %]) no-repeat 15px center;
+}
+a#log_full{
+       background:#666 url([% c.uri_for('/static/i/icons/fulllog.gif') %]) no-repeat 15px center;
+}
+a#tree{
+       background:#666 url([% c.uri_for('/static/i/icons/tree.gif') %]) no-repeat 15px center;
+}
+#nav_logs li a:hover{
+       text-decoration:underline;
+       background-color:#DC143C;
+}
+#nav_logs li.selected a{
+       background-color:#333;
+}
+#nav_logs #branch_selector{
+       padding:10px 15px 10px 48px;
+       font-size:1.3em;
+       font-weight:bold;
+       color:#666;
+}
+
+
+
+
+
+/* formating */
+h1{
+       margin:0;
+       padding:20px 0;
+       clear:both;
+       font-weight:normal;
+       font-size:1.85em;
+       color:#fff;
+}
+h1 a{
+       margin-right:10px;
+       color:#fff;
+}
+h1 a:hover{
+       color:#EAF2F5;
+}
+h2{
+       font-size:1.85em;
+       font-weight:normal;
+       color:#666;
+       margin:30px 15px 20px;
+}
+h2 span{
+       color:#ccc;
+}
+h3{
+       margin:0;
+       color:#fff;
+       padding:9px 5px 9px 10px;
+       font-size:1em;
+}
+h3 a{
+       color:#ffffff;
+}
+h4 a{
+       color:#ffffff;
+}
+h4 a:hover{
+       color:#EAF2F5;
+}
+p,
+td,
+a{
+       color:#666;
+}
+a:hover{
+       color:#DC143C;
+}
+
+
+.button{
+       display:block;
+       float:left;
+       vertical-align:middle;
+       margin-right:8px;
+       text-indent:-999999px;
+       width:21px;
+       height:21px;
+       outline:none;
+}
+a.commit{
+       background:transparent url([% c.uri_for('/static/i/icons/commit.png') %]) no-repeat;
+}
+a.diff{
+       background:transparent url([% c.uri_for('/static/i/icons/diff.png') %]) no-repeat;
+}
+a.diffcurrent{
+       background:transparent url([% c.uri_for('/static/i/icons/diffcurrent.png') %]) no-repeat;
+}
+a.tree{
+       background:transparent url([% c.uri_for('/static/i/icons/tree.png') %]) no-repeat;
+}
+a.shortlog{
+       background:transparent url([% c.uri_for('/static/i/icons/shortlog.png') %]) no-repeat;
+}
+a.longlog{
+       background:transparent url([% c.uri_for('/static/i/icons/longlog.png') %]) no-repeat;
+}
+a.blob{
+       background:transparent url([% c.uri_for('/static/i/icons/blob.png') %]) no-repeat;
+}
+a.blame{
+       background:transparent url([% c.uri_for('/static/i/icons/blame.png') %]) no-repeat;
+}
+a.history{
+       background:transparent url([% c.uri_for('/static/i/icons/history.png') %]) no-repeat;
+}
+a.raw{
+       background:transparent url([% c.uri_for('/static/i/icons/raw.png') %]) no-repeat;
+}
+.sha1_holder{
+       background:transparent url([% c.uri_for('/static/i/icons/button_sha1.png') %]) no-repeat;
+}
+.sha1_holder_invert{
+       background:transparent url([% c.uri_for('/static/i/icons/button_sha1_invert.png') %]) no-repeat;
+}
+.sha1_label{
+       padding-top:2px;
+       float:left;
+}
+.msg{
+       padding:5px 10px 5px 35px;
+       background:#f0f0f0 url([% c.uri_for('/static/i/icons/attention.png') %]) no-repeat 10px center;
+       border:1px solid #ddd;
+       margin:30px 15px;
+}
+
+a.rss_logo {
+  float: right;
+  padding: 3px 0px;
+  width: 35px;
+  line-height: 10px;
+  border: 1px solid;
+  border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
+  color: #ffffff;
+  background-color: #ff6600;
+  font-weight: bold;
+  font-family: sans-serif;
+  font-size: 70%;
+  font-style: normal;
+  text-align: center;
+  text-decoration: none;
+  margin: 3px;
+}
+
+a.rss_logo:hover {
+  background-color: #ee5500;
+}
+
+
+
+/* paging */
+.pager{
+       width:100%;
+       float:left;
+       margin:10px 0;
+       padding:0;
+}
+.pager li{
+       margin:0;
+       padding:0;
+       display:block;
+       list-style:none;
+}
+.pager li a{
+       display:block;
+       padding:4px 6px;
+       color:#fff;
+       border:1px solid #ddd;
+       background-color:#333;
+}
+.pager .pager_prev a{
+       margin-left:20px;
+       float:left;
+}
+.pager .pager_next a{
+       margin-right:20px;
+       float:right;
+}
+
+
+
+
+/* table listings */
+th{
+       padding:9px 5px 9px 10px;
+       text-align:left;
+       color:#fff;
+}
+th a{
+       color:#fff;
+}
+.summary tr{
+       background-color:#FAFAFA;
+       border-bottom:1px solid #fff;
+}
+.summary td{
+       vertical-align:middle !important;
+}
+tr.invert{
+       background-color:#f0f0f0;
+}
+tr.header{
+       background-color:#666;
+}
+.sha1{
+       width:80px;
+}
+.file-mode{
+       width:80px;
+}
+.time-since{
+       width:95px;
+       font-weight:bold;
+}
+.author{
+       width:180px;
+}
+.author img{
+       margin-right:5px;
+       vertical-align:middle;
+}
+table.listing{
+       width:970px;
+       border-collapse:collapse;
+}
+.listing td{
+       vertical-align:top;
+       padding:9px 5px 9px 10px;
+}
+.action-list{
+       width:120px;
+}
+
+.diff-tree{
+       background-color:#f0f0f0;
+}
+.diff-tree th{
+       background-color:#666;
+}
+.diff-tree tr{
+       border-bottom:1px solid #fff;
+}
+.diff-tree td{
+       padding:6px 5px 4px 10px;
+}
+.differences{
+       margin-top:0;
+       padding:0px 15px;
+}
+.differences pre{
+       line-height:140%;
+       font-size:12px;
+       overflow:auto;
+       margin:0;
+       padding:10px;
+       border:1px solid #ddd;
+       background-color:#f0f0f0;
+}
+.diff-head{
+       background-color:#666;
+       color:#fff;
+       margin:10px 0 0 0 !important;
+       border:1px solid #ddd;
+       border-bottom:none;
+       font-family: monospace;
+       padding:10px;
+       font-size:13px;
+}
+.diff-index {
+       margin:0 0 30px 0;
+       border:1px solid #ddd;
+       border-top:none;
+       background-color:#ccc;
+       padding:5px 10px;
+}
+
+
+/* /blame */
+#blame pre, #blame tt {
+  margin: 0;
+  font-size: 12px;
+}
+#blame .commit-info {
+
+}
+#blame .lineno {
+  text-align: right;
+  padding: 0 8px;
+}
+#blame a {
+ atext-decoration: none;
+}
+#blame {
+    overflow-x: scroll;
+}
+#blame tr.alt {
+  background-color: #f7f7f7;
+}
+#blame tbody tr:hover {
+  background-color: #fefeaa;
+}
+#blame td {
+       vertical-align:middle;
+       padding: 3px;
+}
+#blame td.lineno {
+  background-color: #eee;
+}
+#blame td.date, #blame td.author, #blame td.commit-info {
+}
+#blame tbody td.data {
+  padding-left: 5px;
+  background-color: #333;
+  color: #ddd;
+}
+
+/* /blob */
+pre.blob {
+  background-color: #333;
+  color: #ddd;
+  border-left: solid 3px #c33;
+  padding: 5px;
+  padding-left: 15px;
+  margin: 20px 15px 20px;
+  overflow:auto;
+  font-size:12px;
+}
+
+/* /blobdiff etc */
+
old mode 100644 (file)
new mode 100755 (executable)
index f65110b..7449ebd
@@ -1,38 +1,34 @@
-#the-container {
-  margin: 1em 100px;
-  text-align: center;
-  background-color: white;
-}
-#body {
-  text-align: justify;
-}
-div.content {
-  padding: 0 7px;
-}
-
 /*
 14d2f2ca3732551d1585e7590e60b82492f3
 ^^ A rather nice chroma hash
 */
 body {
-  background-color: #555;
+  /* From http://jasonsantamaria.com/ via http://www.inspirationbit.com/striking-web-sites-with-font-stacks-that-inspire/ */
+  font-family: "Lucida Sans Unicode","Lucida Grande","Lucida Sans",Verdana,Arial,sans-serif;
+}
+#the-container {
+}
+
+a {
+  color: mediumblue;
 }
 
 thead, tfoot {
-  /* FTR all table columns should be sortable hence the colour to indicate clickableness. */
-  color: navy;
+  /* FTR all table columns should be sortable hence the colour to indicate clickableness.
+  color: mediumblue; XXX uncomment this when sorting is implemented*/
+  color: slategray;
   font-size: small;
   font-weight: bold !important;
 }
 thead th {
-  border-bottom: solid 1px #777;
+  border-bottom: solid 1px lightsteelblue;
 }
 tfoot td {
-  border-top: solid 1px #777;
+  border-top: solid 1px lightsteelblue
 }
 
 .listing tbody tr:nth-child(even) {
-  background-color: #f7f7f7;
+  background-color: whitesmoke;
 }
 .listing tbody tr:hover {
   background-color: #fefeaa;
@@ -43,7 +39,7 @@ tfoot td {
   font-size:   1em;
   font-style: normal;
 }
-div.chroma-hash {
+.actions span.chroma-hash {
   float: right;
 }
 
@@ -52,7 +48,8 @@ div.chroma-hash {
 }
 .author, .head {
   font-weight: bold;
-  font-family: "Trebuchet MS", "Lucida Grande", serif;
+  /* From http://designr.it/ via http://www.inspirationbit.com/striking-web-sites-with-font-stacks-that-inspire/ */
+  font-family: Baskerville,Georgia,Cambria,Times,Times New Roman,serif;
 }
 
 .file-name, .file-mode {
@@ -72,36 +69,33 @@ div.chroma-hash {
 /* header */
 #page-header {
   height: 25px;
-  padding: 8px;
   font-size: 1.5em;
   font-weight: bold;
-  border-bottom: solid 1px #777;
+  border-bottom: solid 3px limegreen;
   text-align: justify;
+  padding: 8px 0;
 }
 
 img.logo {
-  float: right;
   border-width: 0px;
-  margin-top: 4px;
+  margin-top: 6px;
+  float: right;
+  padding-left: 15px;
 }
 
 /* footer */
 #page-footer {
   height: 20px;
-  padding: 8px;
   margin-top: 10px;
   font-style: italic;
-  background-color: #d9d8d1;
-  border-top: solid 1px #777;
+  border-top: solid 3px crimson;
 }
 
 /* actions include */
 .actions {
-  padding-bottom: 4px 0;
   font-style: italic;
-  border-bottom: solid 1px #777;
-  margin-bottom: 10px;
-  padding: 5px 7px;
+  border-bottom: solid 1px limegreen;
+  height: 25px;
 }
 
 /* pager include */
@@ -112,9 +106,7 @@ img.logo {
 /* search include */
 #page-search {
   text-align: right;
-  float: right;
   font-size: 0.7em;
-  padding-right: 20px;
 }
 
 /* shortlog include */
@@ -301,6 +293,7 @@ a.rss_logo {
   font-style: normal;
   text-align: center;
   text-decoration: none;
+  margin: 3px 0;
 }
 
 a.rss_logo:hover {
old mode 100644 (file)
new mode 100755 (executable)
index a76b6fb..0303541
@@ -1,16 +1,21 @@
 span.Keyword {
-  color: #777;
+  color: #aaa;
   font-weight: bold;
 }
 span.DataType {
+       background-color:#F3E6FF;
   color: purple;
 }
 span.Normal {
-  color: gray;
 }
 span.String {
-  color: green;
+       background-color:#CCF5CC;
+       padding:2px 0;
+       color: #006F00;
 }
 span.Others {
-  color: red;
+       background-color:#F3CCCC;
+       padding:2px 0;
+       color: #750000;
 }
diff --git a/root/static/i/arrow_grey.gif b/root/static/i/arrow_grey.gif
new file mode 100755 (executable)
index 0000000..3a4429e
Binary files /dev/null and b/root/static/i/arrow_grey.gif differ
diff --git a/root/static/i/arrow_grey_left.gif b/root/static/i/arrow_grey_left.gif
new file mode 100755 (executable)
index 0000000..42e070a
Binary files /dev/null and b/root/static/i/arrow_grey_left.gif differ
diff --git a/root/static/i/bg.png b/root/static/i/bg.png
new file mode 100755 (executable)
index 0000000..1c48ca4
Binary files /dev/null and b/root/static/i/bg.png differ
diff --git a/root/static/i/bg_bottom.png b/root/static/i/bg_bottom.png
new file mode 100755 (executable)
index 0000000..cda2f73
Binary files /dev/null and b/root/static/i/bg_bottom.png differ
diff --git a/root/static/i/bg_content.png b/root/static/i/bg_content.png
new file mode 100755 (executable)
index 0000000..c6dc6b5
Binary files /dev/null and b/root/static/i/bg_content.png differ
diff --git a/root/static/i/bg_top.png b/root/static/i/bg_top.png
new file mode 100755 (executable)
index 0000000..39b765f
Binary files /dev/null and b/root/static/i/bg_top.png differ
diff --git a/root/static/i/icons/Thumbs.db b/root/static/i/icons/Thumbs.db
new file mode 100644 (file)
index 0000000..bdf3302
Binary files /dev/null and b/root/static/i/icons/Thumbs.db differ
diff --git a/root/static/i/icons/attention.png b/root/static/i/icons/attention.png
new file mode 100755 (executable)
index 0000000..db00074
Binary files /dev/null and b/root/static/i/icons/attention.png differ
diff --git a/root/static/i/icons/blame.png b/root/static/i/icons/blame.png
new file mode 100755 (executable)
index 0000000..7a6730b
Binary files /dev/null and b/root/static/i/icons/blame.png differ
diff --git a/root/static/i/icons/blob.png b/root/static/i/icons/blob.png
new file mode 100755 (executable)
index 0000000..c5261b8
Binary files /dev/null and b/root/static/i/icons/blob.png differ
diff --git a/root/static/i/icons/button_sha1.png b/root/static/i/icons/button_sha1.png
new file mode 100755 (executable)
index 0000000..77915e0
Binary files /dev/null and b/root/static/i/icons/button_sha1.png differ
diff --git a/root/static/i/icons/button_sha1_invert.png b/root/static/i/icons/button_sha1_invert.png
new file mode 100755 (executable)
index 0000000..2f1724b
Binary files /dev/null and b/root/static/i/icons/button_sha1_invert.png differ
diff --git a/root/static/i/icons/commit.png b/root/static/i/icons/commit.png
new file mode 100755 (executable)
index 0000000..692f11e
Binary files /dev/null and b/root/static/i/icons/commit.png differ
diff --git a/root/static/i/icons/diff.png b/root/static/i/icons/diff.png
new file mode 100755 (executable)
index 0000000..9883936
Binary files /dev/null and b/root/static/i/icons/diff.png differ
diff --git a/root/static/i/icons/diffcurrent.png b/root/static/i/icons/diffcurrent.png
new file mode 100755 (executable)
index 0000000..8f4827f
Binary files /dev/null and b/root/static/i/icons/diffcurrent.png differ
diff --git a/root/static/i/icons/fulllog.gif b/root/static/i/icons/fulllog.gif
new file mode 100755 (executable)
index 0000000..536da14
Binary files /dev/null and b/root/static/i/icons/fulllog.gif differ
diff --git a/root/static/i/icons/history.png b/root/static/i/icons/history.png
new file mode 100755 (executable)
index 0000000..690a489
Binary files /dev/null and b/root/static/i/icons/history.png differ
diff --git a/root/static/i/icons/longlog.png b/root/static/i/icons/longlog.png
new file mode 100755 (executable)
index 0000000..4859ac9
Binary files /dev/null and b/root/static/i/icons/longlog.png differ
diff --git a/root/static/i/icons/raw.png b/root/static/i/icons/raw.png
new file mode 100755 (executable)
index 0000000..f2854f6
Binary files /dev/null and b/root/static/i/icons/raw.png differ
diff --git a/root/static/i/icons/shortlog.gif b/root/static/i/icons/shortlog.gif
new file mode 100755 (executable)
index 0000000..0f1e9b0
Binary files /dev/null and b/root/static/i/icons/shortlog.gif differ
diff --git a/root/static/i/icons/shortlog.png b/root/static/i/icons/shortlog.png
new file mode 100755 (executable)
index 0000000..9c48a2a
Binary files /dev/null and b/root/static/i/icons/shortlog.png differ
diff --git a/root/static/i/icons/tree.gif b/root/static/i/icons/tree.gif
new file mode 100755 (executable)
index 0000000..49d75b8
Binary files /dev/null and b/root/static/i/icons/tree.gif differ
diff --git a/root/static/i/icons/tree.png b/root/static/i/icons/tree.png
new file mode 100755 (executable)
index 0000000..139a962
Binary files /dev/null and b/root/static/i/icons/tree.png differ
diff --git a/root/static/i/logo.png b/root/static/i/logo.png
new file mode 100755 (executable)
index 0000000..cb0e1f8
Binary files /dev/null and b/root/static/i/logo.png differ
diff --git a/root/summary.tt2 b/root/summary.tt2
deleted file mode 100644 (file)
index be4b3e1..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-[% PROCESS 'nav/actions.tt2' object = commit %]
-
-<div class='summary content'>
-<!-- <div id='stats'>
-  <dt>Number of commits in the last day</dt><dd>N</dd>
-  <dt>Most frequent commiter in the last week</dt><dd>Foo</dd>
-  <dt>Hottest file in the repo</dt><dd>Bar.baz</dd>
-  <dt>Current hue</dt><dd>either HEAD or some arbitrary munging</dd>
- </div> -->
-
- <dl>
-  <dt>description</dt><dd>[% Repository.description %]</dd>
-  <dt>owner</dt><dd>[% Repository.owner %]</dd>
-  <dt>last change</dt><dd>[% Repository.last_change %]</dd>
- </dl>
-
-  <h2><a href='[% c.uri_for("shortlog") %]'>shortlog</a></h2>
-  [% INCLUDE '_shortlog.tt2' %]
-
-  <h2><a href='[% c.uri_for("heads") %]'>branches</a></h2>
-  [% INCLUDE '_heads.tt2' %]
-</div>
diff --git a/root/tags.tt2 b/root/tags.tt2
deleted file mode 100644 (file)
index 6f47285..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-<div class='content'>
-  <div>
-  [% Repository.name %]
-  </div>
-
-  [% INCLUDE '_heads.tt2' heads = tags %]
-</div>
diff --git a/root/tree.tt2 b/root/tree.tt2
deleted file mode 100644 (file)
index bc06ea5..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-[% INCLUDE 'nav/actions.tt2' object = commit %]
-
-<div class='content'>
-  <div class='commit-message'>
-  [% short_cmt(commit.comment) | html %] ...
-  </div>
-
-  [%
-    IF path;
-      INCLUDE 'nav/path.tt2' filename = path, head = commit;
-    END;
-
-    INCLUDE '_tree.tt2';
-  %]
-</div>
diff --git a/root/wrapper.tt2 b/root/wrapper.tt2
new file mode 100755 (executable)
index 0000000..e42e8d9
--- /dev/null
@@ -0,0 +1,153 @@
+[%- IF no_wrapper || template.name.match('\.(css|js|txt)'); content; ELSE; -%]
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <!-- git core binaries version [% git_version %] -->
+  <meta charset="utf-8" />
+  <meta name="generator" content="gitweb/[% version %] git/[% git_version %]" />
+  <meta name="robots" content="index, nofollow" />
+  <title>[%# FIXME - MING %][%-
+    title = BLOCK;
+      c.config.sitename;
+      IF Repository; ' - ' _ Repository.name | html; END;
+      IF c.action;   ' / ' _ c.action; END;
+      IF filename;   ' - ' _ filename | html; END;
+      IF c.action && c.action == 'tree'; '/'; END;
+    END;
+    title;
+  -%] (Gitalist)</title>
+  [% INCLUDE '_header_feeds.tt2' %]
+   
+       <link rel="stylesheet" type="text/css" href="[% c.uri_for('/core.css') %]" />
+       <link rel="shortcut icon" href="[% c.uri_for('/static/git-favicon.png') %]" type="image/png" />
+</head>
+
+<body>
+
+<div id="header_holder">
+       <div class="sub_holder">
+               <div id="header">
+                       <a href="[% c.uri_for('/') %]" id="logo"><img src="[% c.uri_for('/static/i/logo.png') %]" alt="Gitalist" /></a>
+
+                       <div class="search">
+                               [% IF Repository; INCLUDE 'nav/search.tt2'; ELSE; %]
+                               
+                               <form method="get" action="[% c.uri_for_action('/search') %]" enctype="application/x-www-form-urlencoded">
+          <p class="projsearch">Search:
+           <input type="text" name="s" value="[% search_text %]" />
+          </p>
+         </form>
+                       [% END %]
+                       </div>
+               </div>
+               
+               
+               <div id="nav_logs" [% 'style="visibility:hidden"' IF c.action.name == "index" %]>
+               
+                       [% IF c.req.captures.size == 1; SET path = 'repository'; ELSE; SET path = 'ref'; END %]
+                       
+                       <ul>
+                               <li[% ' class="selected"' IF c.action.name.match('tree') %]><a href="[% c.uri_for_action('/ref/tree', c.req.captures) %]" id="tree">Tree</a></li>
+
+                               <li[% ' class="selected"' IF c.action.name.match('longlog') %]><a href="[% c.uri_for_action('/' _ path _ '/longlog', c.req.captures) %]" id="log_full">Long log</a></li>
+                               
+                               <li[% ' class="selected"' IF c.action.name.match('shortlog') %]><a href="[% c.uri_for_action('/' _ path _ '/shortlog', c.req.captures) %]" id="log_short">Short log</a></li>
+
+                               
+                               <li id="branch_selector">
+                                       [%# FIXME: convert to c.uri_for_action  #%]
+                                       
+                                       Branches 
+                                       <select onchange="document.location.href='/[% Repository.name %]/'+this.options[this.selectedIndex].text + '/shortlog';">
+                                               <option value="">HEAD</option>
+                                       
+                                       [% FOREACH branch_head IN Repository.heads %]
+                                               <option [% "selected" IF c.req.path.search(branch_head.name) %]>[% branch_head.name %]</option>
+                                       [% END %]
+                                       
+                                       </select>
+                               </li>
+                               
+                       </ul>
+               </div>
+       
+               <h1>
+                       <a href="[% c.uri_for('/') %]">Home</a>
+               
+                       [%- IF Repository %]
+               / <a href="[% c.uri_for_action('/repository/summary', [Repository.name]) %]/shortlog">[% Repository.name %]</a>
+                       [%- END %]
+                       /
+                       [%- IF Repository %]
+                               [%# FIXME: output branch name in a nicer way!!!  #%]
+                               [% FOREACH branch_head IN Repository.heads %]
+                                       [% IF c.req.path.search(branch_head.name) %]
+                               <a href="[% c.uri_for_action('/repository/summary', [Repository.name]) %]/[% branch_head.name %]/shortlog">[% branch_head.name %]</a> /
+                                       [% END %]
+                               [% END %]
+                       [%- END %]
+                       
+                       [%- 
+                               c.action.name
+                                       .replace("_"," ")
+                                       .replace("log", " log")
+                                       .replace("fancy","")
+                                       .replace("index","Repositories") 
+                                       FILTER ucfirst 
+                       -%]
+                       
+       
+                       
+                       </h1>
+       
+       </div>
+</div>
+
+
+<div id="content_holder">
+       <div id="content" class="sub_holder">
+               <div id="content_inner">
+
+       
+
+ [% content %]
+
+               </div>
+       </div>
+</div>
+
+<div id="footer_holder">
+       <div id="footer" class="sub_holder">
+               
+               <p>[% IF Repository; Repository.description | html; END; %]</p>
+       
+               <a title="git homepage" href="http://git-scm.org"><img src="[% c.uri_for('/logo.png') %]" id="git_logo" alt="git" /></a>
+               
+               <div id="feeds">
+               [% INCLUDE 'inc/footer_feeds.tt2' %]
+               </div>
+       </div>
+
+</div>
+
+
+
+<div id="debug_holder">
+       <div id="debug" class="sub_holder">
+
+       <p>Debug:</p>
+       
+       [% USE Dumper %]
+       <pre>
+       [% Repository.path %]
+       [%# Dumper.dump(c.req.path) %]
+       </pre>
+       
+       </div>
+</div>
+
+</body>
+</html>
+[%- END -%]
index 2ed2537..f52f36e 100644 (file)
--- a/t/01app.t
+++ b/t/01app.t
@@ -1,63 +1,58 @@
 #!/usr/bin/env perl
-use strict;
-use warnings;
-use Test::More;
 use FindBin qw/$Bin/;
-
-BEGIN {
-    $ENV{GITALIST_CONFIG} = $Bin;
-    $ENV{GITALIST_REPO_DIR} = '';
-    use_ok 'Catalyst::Test', 'Gitalist';
-}
-
-ok( request('/')->is_success, 'Request should succeed' );
-
-for my $p (qw/ repo1 nodescription /) {
-    my $path = '/summary?p=' . $p;
+use lib "$Bin/lib";
+use TestGitalist;
+
+for my $p ('', qw{
+    repo1 nodescription bare.git opml search
+    fragment/collectionofrepositories
+}) {
+    my $path = '/' . $p;
     ok( request($path)->is_success, "$path should succeed");
 }
 
-my $response = request('/summary?p=DoesNotExist');
+my $response = request('/DoesNotExist');
 is $response->code, 404, 'invalid repository 404s';
 like $response->content, qr/Page not found/, 'invalid repository handled correctly';
 
-is request('/summary?p=../../../')->code, 404, 'directory traversal failed';
+# URI tests for repo1
+foreach my $test (map {curry_test_uri($_)} ('fragment/repo1', 'repo1') ) {
+  $test->('');
+  $test->('shortlog');
+  $test->('log');
+  $test->('heads');
+  $test->('tags');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/tree');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/tree/dir1');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/diff');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/diff/plain');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/history/dir1');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/history/dir1');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/blame/file1');
+  $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/blob/file1');
+}
 
 {
   # URI tests for repo1
   local *test = curry_test_uri('repo1');
-  test('/summary');
-  test('/shortlog');
-  test('/log');
-  test('/reflog');
-  test('/commit');
-  test('/commitdiff', 'h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-  test('/tree', 'h=145dc3ef5d307be84cb9b325d70bd08aeed0eceb;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-  test('/search', 'h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818&f=&type=commit&text=added');
-  test('/blobdiff', 'f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-  test('/blob', 'f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-  test('/patch');
-  test('/patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e');
-  test('/patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-  test('/patches');
-  test('/patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e');
-  test('/patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+  test('search');
+
+  test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patch');
+  test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patches/1');
+  test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patches/2');
+  test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/raw/file1');
+
+  TODO: {
+      local $TODO = "FIXME";
+      test('search', 'type=commit&text=added');
+
+      # FIXME - What's the difference here?
+      #test('patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e');
+      #test('patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+      #test('patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e');
+      #test('patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+  }
 }
 
 done_testing;
-
-sub test_uri {
-    my ($p, $uri, $qs) = @_;
-    $qs ||= '';
-    my $request = "$uri?p=repo1;$qs";
-    my $response = request($request);
-    ok($response->is_success, "ok $p - $uri - $qs");
-}
-
-sub curry_test_uri {
-    my $p = shift;
-    sub {
-        my ($uri, $qs) = @_;
-        test_uri($p, $uri, $qs);
-    };
-};
index 3cdc2cc..bb49cf7 100644 (file)
@@ -59,7 +59,7 @@ my $commit_obj = Gitalist::Git::Object::Commit->new(
 isa_ok($commit_obj, 'Gitalist::Git::Object::Commit', "commit object");
 my ($tree, $patch) = $commit_obj->diff(
     parent => undef,
-    file => undef,
+    filename => undef,
     patch => 1,
 );
 $patch = $patch->[0];
index 3017ef7..839d794 100644 (file)
@@ -1,22 +1,34 @@
 #!/usr/bin/env perl
-use strict;
-use warnings;
-use Test::More;
 use FindBin qw/$Bin/;
-
-BEGIN {
-    $ENV{GITALIST_CONFIG} = $Bin;
-    no warnings;
-    $ENV{GITALIST_REPO_DIR} = undef;
-    use warnings;
-    use_ok 'Catalyst::Test', 'Gitalist';
-}
+use lib "$Bin/lib";
+use TestGitalist qw/request curry_test_uri done_testing ok is $TODO/;
 
 ok( request('/')->is_success, 'Request should succeed' );
 
+sub test {
+    my ($uri, $qs) = @_;
+    my $request = "/$uri";
+    $request =~ s{/+}{/}g;
+    $request .= "?$qs" if defined $qs;
+    my $response = request($request);
+    $uri = $response->header('Location') || '';
+    is($response->code, 301, "ok $request 301 to " . $uri)
+        or return $response;
+    $response = request($uri);
+    ok($response->is_success, "ok $uri");
+    return $response;
+}
+# FIXME
 # URI tests for repo1
-local *test = curry_test_uri('repo1');
+#local *test = curry_test_uri('repo1');
+
+test('/', 'a=project_index');
+test('/', 'a=opml');
 
+no warnings 'redefine';
+local *test = curry_test_uri('repo1', \&test);
+test('/', 'a=project_index');
+test('/', 'a=opml');
 test('/', 'a=summary');
 test('/', 'a=heads');
 test('/', 'a=tags');
@@ -50,6 +62,7 @@ test('/', 'a=blob_plain;f=file1;hb=HEAD');
 test('/', 'a=blob_plain;f=file1;hb=master');
 test('/', 'a=blob_plain;f=file1;hb=refs/heads/master');
 
+
 test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3f7567c7bdf7e7ebf410926493b92d398333116e;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
@@ -98,7 +111,6 @@ test('/', 'a=commitdiff_plain;h=master;hp=3f7567c7bdf7e7ebf410926493b92d39833311
 test('/', 'a=commitdiff_plain;h=refs/heads/master');
 test('/', 'a=commitdiff_plain;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
-
 test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
 test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD');
 test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master');
@@ -202,7 +214,6 @@ test('/', 'a=tree;hb=HEAD');
 test('/', 'a=tree;hb=master');
 test('/', 'a=tree;hb=refs/heads/master');
 
-
 test('/', 'a=atom');
 test('/', 'a=atom;f=dir1');
 test('/', 'a=atom;f=dir1/file2');
@@ -233,10 +244,6 @@ test('/', 'a=rss;h=refs/heads/master');
 test('/', 'a=rss;h=refs/heads/master;opt=--no-merges');
 test('/', 'a=rss;opt=--no-merges');
 
-test('/', 'a=project_index');
-
-test('/', 'a=opml');
-
 test('/', 'a=blame;f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
 test('/', 'a=blame;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
@@ -248,19 +255,3 @@ test('/', 'a=blame;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blame;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
 done_testing;
-
-sub test_uri {
-    my ($p, $uri, $qs) = @_;
-    $qs ||= '';
-    my $request = "$uri?p=repo1;$qs";
-    my $response = request($request);
-    ok($response->is_success, "ok $request");
-}
-
-sub curry_test_uri {
-    my $p = shift;
-    sub {
-        my ($uri, $qs) = @_;
-        test_uri($p, $uri, $qs);
-    };
-};
diff --git a/t/app-mech-rootpage.t b/t/app-mech-rootpage.t
new file mode 100644 (file)
index 0000000..cc6f71f
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+use FindBin qw/$Bin/;
+use lib "$Bin/lib";
+use TestGitalist;
+plan 'skip_all' => "One or more of the following modules aren't present: Test::WWW::Mechanize::Catalyst WWW::Mechanize::TreeBuilder HTML::TreeBuilder::XPath" unless MECH();
+
+MECH->get_ok('/');
+{
+    my $nodeset = MECH->findnodes('/html/body//tr[@class="reposrow"]');
+    foreach my $row ($nodeset->get_nodelist) {
+        my $uri = $row->findnodes('.//a')->[0]->attr('href');
+        my ($repos_name) = $uri =~ m{^http://localhost/([\w\.]+)$};
+        ok $repos_name, "Repos name $repos_name";
+        like $row->findnodes('.//a')->[1]->as_text, qr{^[\w\s/;',\.]+$}, 'Have description'
+            unless $repos_name eq 'nodescription';
+        like $row->findnodes('.//td[@class="time-since"')->[0]->as_text, qr/^(never|\d\s+(years|months)\s+ago)$/,
+            'Last change looks ok';
+        my ($summary, $shortlog, $log, $tree) = $row->findnodes('.//td[@class="link"]/a')->get_nodelist;
+        like $summary->as_text, qr/summary/i, 'summary text ok';
+        is $summary->attr('href'), $uri, 'summary href correct';
+        like $shortlog->as_text, qr/shortlog/i, 'shortlog text ok';
+        is $shortlog->attr('href'), "$uri/shortlog", 'shortlog href ok';
+        like $log->as_text, qr/log/, 'log text ok';
+        is $log->attr('href'), "$uri/log", 'log href ok';
+        like $tree->as_text, qr/tree/, 'tree text ok';
+        TODO: {
+            local $TODO = 'Bork';
+            is $tree->attr('href'), "$uri/tree", 'tree href ok';
+        }
+    }
+}
+
+done_testing;
diff --git a/t/atom.t b/t/atom.t
new file mode 100644 (file)
index 0000000..13c835f
--- /dev/null
+++ b/t/atom.t
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+use FindBin qw/$Bin/;
+use lib "$Bin/lib";
+use TestGitalist;
+
+my $res = request('/repo1/atom');
+ok $res->is_success;
+is $res->content_type, 'application/atom+xml';
+TODO: {
+    local $TODO = "Does not work yet. Need similar info to RSS feed";
+    like $res->content, qr{link>http://localhost/repo1</link};
+    like $res->content, qr{description>some test repository</description};
+}
+like $res->content, qr{add dir1/file2</div};
+like $res->content, qr{<id>http://localhost/repo1/36c6c6708b8360d7023e8a1649c45bcf9b3bd818/commit</id};
+like $res->content, qr{title>add dir1/file2</title};
+
+done_testing;
diff --git a/t/lib/TestGitalist.pm b/t/lib/TestGitalist.pm
new file mode 100644 (file)
index 0000000..dcdc539
--- /dev/null
@@ -0,0 +1,95 @@
+package TestGitalist;
+use strict;
+use warnings;
+use Exporter ();
+use FindBin qw/$Bin/;
+BEGIN {
+    $ENV{GITALIST_CONFIG} = $Bin;
+    $ENV{GITALIST_REPO_DIR} = '';
+}
+use Catalyst::Test qw/Gitalist/;
+use Test::More;
+use Test::Exception;
+
+our @EXPORT = (@Test::More::EXPORT, @Test::Exception::EXPORT, qw/
+    test_uri
+    curry_test_uri
+    MECH
+    request
+    get
+    ctx_request
+    content_like
+    action_ok
+    action_redirect
+    action_notfound
+    contenttype_is
+/);
+
+sub import {
+    my $into = caller();
+    strict->import;
+    warnings->import;
+    goto \&Exporter::import;
+}
+
+use constant ();
+BEGIN {
+    my $mech = eval {
+        require Test::WWW::Mechanize::Catalyst;
+        require WWW::Mechanize::TreeBuilder;
+        my $mech = Test::WWW::Mechanize::Catalyst->new(catalyst_app => 'Gitalist');
+        WWW::Mechanize::TreeBuilder->meta->apply($mech,
+           tree_class => 'HTML::TreeBuilder::XPath',
+        );
+        return $mech;
+    };
+    constant->import('MECH', $mech );
+}
+
+# Rechecking the same link multiple times is slow and lame!
+# Nicked this from WWW::Mechanize and memoized it...
+my %seen_links;
+sub Test::WWW::Mechanize::Catalyst::page_links_ok {
+    my $self = shift;
+    my $desc = shift;
+
+    $desc = 'All links ok' unless defined $desc;
+
+    my @links = $self->followable_links();
+    my @urls = Test::WWW::Mechanize::_format_links(\@links);
+
+    my @failures = $self->_check_links_status( [ grep { ! $seen_links{$_}++ } @urls ] );
+    my $ok = (@failures==0);
+
+    ok( $ok, $desc );
+    diag( $_ ) for @failures;
+
+    return $ok;
+}
+
+
+sub test_uri {
+    my ($uri, $qs) = @_;
+    my $request = "/$uri"; 
+    $request .= "?$qs" if defined $qs;
+    my $response = request($request);
+    ok($response->is_success || $response->is_redirect, "ok $request");
+    if (MECH) {
+        my $res = MECH()->get($request);
+        ok $res->is_success, "ok mech $request (" . $res->code . ')';
+        MECH()->page_links_ok("All links ok from $request")
+            if $res->content_type =~ m|text/html|;
+    }
+    return $response;
+}
+
+sub curry_test_uri {
+    my $prefix = shift;
+    my $to_curry = shift || \&test_uri;
+    sub {
+        my $uri = shift;
+        $to_curry->("$prefix/$uri", @_);
+    };
+}
+
+1;
diff --git a/t/lib/gitalist_testing.conf b/t/lib/gitalist_testing.conf
deleted file mode 100644 (file)
index 8e0aecf..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-name Gitalist
-
-#git /path/to/git
-projectroot __path_to(t/lib/repositories)__
-repo_dir __path_to(t/lib/repositories)__
-
-sitename "Gitalist presently"
-
-# URI and label (title) of GIT logo link
-logo_url   git-scm.org
-logo_label "git homepage"
-logo_img   /git-logo.png
-
-home_link /
-home_link_str "A Gitalist"
-
-# XXX let's not hardcode these yeah?
-version 1.6.3.3
-
-# show repository only if this file exists
-# (only effective if this variable evaluates to true)
-# export_ok 
-
-# XXX Code in config FAIL
-# show repository only if this subroutine returns true
-# when given the path to the project, for example:
-#    sub { return -e "$_[0]/git-daemon-export-ok"; }
-# export_auth_hook
-
-# stylesheet path/to/your/stylesheet.css
-logo /git-logo.png
-favicon /git-favicon.png
-
-# $feature{'blame'}{'default'} = [1];
-<feature>
-       <blame>
-               default = 1
-       </blame>
-</feature>
-
-# fs traversing limit for getting project list
-# the number is relative to the projectroot
-project_maxdepth 2007
diff --git a/t/lib/repositories/repo1/refs/tags/0.01 b/t/lib/repositories/repo1/refs/tags/0.01
new file mode 100644 (file)
index 0000000..7c4a265
--- /dev/null
@@ -0,0 +1 @@
+36c6c6708b8360d7023e8a1649c45bcf9b3bd818
diff --git a/t/opml.t b/t/opml.t
new file mode 100644 (file)
index 0000000..d468b44
--- /dev/null
+++ b/t/opml.t
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+use FindBin qw/$Bin/;
+use lib "$Bin/lib";
+use TestGitalist;
+
+my $res = request('/opml');
+ok $res->is_success;
+
+is $res->content_type, 'application/rss';
+like $res->content, qr{Gitalist</title>};
+like $res->content, qr{xmlUrl="http://localhost/bare.git/rss"};
+like $res->content, qr{text="repo1 - some test repository"};
+like $res->content, qr{xmlUrl="http://localhost/repo1/rss"};
+
+done_testing;
diff --git a/t/rss.t b/t/rss.t
new file mode 100644 (file)
index 0000000..2d6a143
--- /dev/null
+++ b/t/rss.t
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+use FindBin qw/$Bin/;
+use lib "$Bin/lib";
+use TestGitalist;
+
+my $res = request('/repo1/rss');
+ok $res->is_success;
+is $res->content_type, 'application/rss+xml';
+like $res->content, qr{link>http://localhost/repo1</link};
+like $res->content, qr{description>some test repository</description};
+like $res->content, qr{title>add dir1/file2</title};
+like $res->content, qr{description>add dir1/file2</description};
+like $res->content, qr{guid isPermaLink="true">http://localhost/repo1/36c6c6708b8360d7023e8a1649c45bcf9b3bd818/commit</guid};
+
+done_testing;
diff --git a/t/script/gitalist_server.pl b/t/script/gitalist_server.pl
new file mode 100755 (executable)
index 0000000..344bbeb
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+
+use FindBin qw/$Bin/;
+use lib "$Bin/../../lib";
+
+BEGIN {
+    $ENV{GITALIST_CONFIG} = "$Bin/../gitalist.conf";
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Gitalist', 'Server');
+
+1;
+