Merge remote branch 'broquaint/action-blame'
Tomas Doran [Fri, 20 Nov 2009 22:55:56 +0000 (22:55 +0000)]
* broquaint/action-blame: (28 commits)
  Starting to hook up "orphaned" actions.
  Fleshed out the /blame action a bit more.
  Initial cut at /blame action.
  Added the /tags action.
  Sort out to_utf8 usage.
  Removed the gitweb shim and its dependency.
  Added stubs for further missing actions.
  Moved header & footer logic into the templates.
  Correct filename in HTTP response.
  Moved snapshot method onto Project as both tree and commit sha1's are acceptable inputs.
  Moved snapshot method onto Project as both tree and commit sha1's are acceptable inputs.
  Take format from the right parameter, default to tgz, check against
  Minimally working snapshot action.
  Use anon symbols for safety / reentrancy, no idea if this works :)
  Introduce a run_cmd_fh method, to avoid slurping command output.
  Fixed syntax error in Root.pm & tidied up tests.
  Added simple /rss action.
  Fix legacy tests - atom action is no longer TODO.
  Rename variables and tidy to (hopefully) improve readability.
  Renamed method.
  ...

44 files changed:
Makefile.PL
TODO [deleted file]
gitalist.conf
lib/Gitalist/Controller/Root.pm
lib/Gitalist/Git/HasUtils.pm
lib/Gitalist/Git/Object.pm
lib/Gitalist/Git/Object/Blob.pm
lib/Gitalist/Git/Object/Commit.pm
lib/Gitalist/Git/Project.pm
lib/Gitalist/Git/Util.pm
lib/Gitalist/Util.pm [deleted file]
lib/Gitalist/View/Default.pm
lib/gitweb.pm
root/_chroma_hash.tt2
root/_diff_plain.tt2 [new file with mode: 0644]
root/_diff_tree.tt2
root/_footer_feeds.tt2 [new file with mode: 0644]
root/_header_feeds.tt2 [new file with mode: 0644]
root/_heads.tt2
root/_shortlog.tt2
root/_tree.tt2
root/blame.tt2 [new file with mode: 0644]
root/blob.tt2
root/blob_plain.tt2 [new file with mode: 0644]
root/blobdiff_plain.tt2 [new file with mode: 0644]
root/commit.tt2
root/commitdiff_plain.tt2 [new file with mode: 0644]
root/default.tt2
root/heads.tt2
root/log.tt2
root/logo.png [new file with mode: 0644]
root/logo.svg [new file with mode: 0644]
root/nav/actions.tt2
root/nav/search.tt2
root/static/css/blueprint/screen.css
root/static/css/site.css
root/summary.tt2
root/tags.tt2 [new file with mode: 0644]
root/tree.tt2
t/01app.t
t/02git_object.t
t/02git_project.t
t/03legacy_uri.t
t/gitalist.conf

index 54f98be..20d6539 100644 (file)
@@ -49,12 +49,14 @@ requires 'File::Copy::Recursive';
 requires 'File::Stat::ModeString';
 requires 'File::Which';
 requires 'HTML::Entities';
-requires 'IO::Capture::Stdout';
 requires 'IPC::Run';
 requires 'List::MoreUtils';
 requires 'Path::Class' => '0.17';
 requires 'Sub::Exporter';
 requires 'Syntax::Highlight::Engine::Kate';
+requires 'Sys::Hostname';
+requires 'XML::Atom';
+requires 'XML::RSS';
 
 author_requires 'Test::NoTabs';
 author_requires 'Test::Pod' => '1.14';
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 6a05e77..0000000
--- a/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-* Fix git_blob_plain i.e don't use the wrapper.
-* An action to find what branches have been merged, either as a list or through a search mechanism.
-* An action to find which branches a given commit is on.
-* Fix any not text/html bits e.g the patch action.
-* Simplify the creation of links.
index a3f08fc..7a68c35 100644 (file)
@@ -7,33 +7,11 @@ name Gitalist
 
 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"
-
-# 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>
+  <blame>
+    default = 1
+  </blame>
 </feature>
 
 # fs traversing limit for getting project list
@@ -42,5 +20,9 @@ project_maxdepth 2007
 
 <paging>
   log = 50
-  summary = 16
+  summary = 17
 </paging>
+
+<patches>
+  max = 16
+</patches>
index 69237a1..8d36844 100644 (file)
@@ -4,12 +4,13 @@ use namespace::autoclean;
 
 BEGIN { extends 'Catalyst::Controller' }
 
-#
-# Sets the actions in this controller to be registered with no prefix
-# so they function identically to actions created in MyApp.pm
-#
 __PACKAGE__->config->{namespace} = '';
 
+use Sys::Hostname ();
+use XML::Atom::Feed;
+use XML::Atom::Entry;
+use XML::RSS;
+
 =head1 NAME
 
 Gitalist::Controller::Root - Root Controller for Gitalist
@@ -22,42 +23,6 @@ Gitalist::Controller::Root - Root Controller for Gitalist
 
 =cut
 
-=head2 index
-
-=cut
-
-use IO::Capture::Stdout;
-
-=head2 run_gitweb
-
-The C<gitweb> shim. It should now only be explicitly accessible by
-modifying the URL.
-
-=cut
-
-sub run_gitweb {
-  my ( $self, $c ) = @_;
-
-  # XXX A slippery slope to be sure.
-  if($c->req->param('a')) {
-    my $capture = IO::Capture::Stdout->new();
-    $capture->start();
-    eval {
-      my $action = gitweb::main($c);
-      $action->();
-    };
-    $capture->stop();
-
-    use Data::Dumper;
-    die Dumper($@)
-      if $@;
-
-    my $output = join '', $capture->read;
-    $c->stash->{gitweb_output} = $output;
-    $c->stash->{template} = 'gitweb.tt2';
-  }
-}
-
 sub _get_object {
   my($self, $c, $haveh) = @_;
 
@@ -126,7 +91,6 @@ sub summary : Local {
   my $maxitems = Gitalist->config->{paging}{summary} || 10;
   $c->stash(
     commit    => $commit,
-#    info      => $project->info,
     log_lines => [$project->list_revs(
         sha1 => $commit->sha1,
         count => $maxitems,
@@ -153,6 +117,42 @@ sub heads : Local {
   );
 }
 
+=head2 tags
+
+The current list of tags in the repo.
+
+=cut
+
+sub tags : Local {
+  my ( $self, $c ) = @_;
+  my $project = $c->stash->{Project};
+  $c->stash(
+    commit => $self->_get_object($c),
+    tags   => $project->tags,
+    action => 'tags',
+  );
+}
+
+sub blame : Local {
+  my($self, $c) = @_;
+
+  my $project = $c->stash->{Project};
+  my $h  = $c->req->param('h')
+       || $project->hash_by_path($c->req->param('hb'), $c->req->param('f'))
+       || die "No file or sha1 provided.";
+  my $hb = $c->req->param('hb')
+       || $project->head_hash
+       || die "Couldn't discern the corresponding head.";
+  my $filename = $c->req->param('f') || '';
+
+  $c->stash(
+    blame    => $project->get_object($hb)->blame($filename),
+    head     => $project->get_object($hb),
+    filename => $filename,
+  );
+  
+}
+
 =head2 blob
 
 The blob action i.e the contents of a file.
@@ -180,7 +180,27 @@ sub blob : Local {
     action   => 'blob',
   );
 
-  $c->forward('View::SyntaxHighlight');
+  $c->forward('View::SyntaxHighlight')
+    unless $c->stash->{no_wrapper};
+}
+
+sub blob_plain : Local {
+  my($self, $c) = @_;
+
+  $c->stash(no_wrapper => 1);
+  $c->response->content_type('text/plain; charset=utf-8');
+
+  $c->forward('blob');
+}
+
+sub blobdiff_plain : Local {
+  my($self, $c) = @_;
+
+  $c->stash(no_wrapper => 1);
+  $c->response->content_type('text/plain; charset=utf-8');
+
+  $c->forward('blobdiff');
+
 }
 
 =head2 blobdiff
@@ -196,20 +216,22 @@ sub blobdiff : Local {
               || croak("No file specified!");
   my($tree, $patch) = $c->stash->{Project}->diff(
     commit => $commit,
-    parent => $c->req->param('hpb') || '',
-    file   => $filename,
     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');
+  $c->forward('View::SyntaxHighlight')
+    unless $c->stash->{no_wrapper};
 }
 
 =head2 commit
@@ -254,7 +276,17 @@ sub commitdiff : Local {
     action    => 'commitdiff',
   );
 
-  $c->forward('View::SyntaxHighlight');
+  $c->forward('View::SyntaxHighlight')
+    unless $c->stash->{no_wrapper};
+}
+
+sub commitdiff_plain : Local {
+  my($self, $c) = @_;
+
+  $c->stash(no_wrapper => 1);
+  $c->response->content_type('text/plain; charset=utf-8');
+
+  $c->forward('commitdiff');
 }
 
 =head2 shortlog
@@ -265,12 +297,15 @@ Expose an abbreviated log of a given sha1.
 
 sub shortlog : Local {
   my ( $self, $c ) = @_;
-  my $project = $c->stash->{Project};
-  my $commit  = $self->_get_object($c);
+
+  my $project  = $c->stash->{Project};
+  my $commit   = $self->_get_object($c);
+  my $filename = $c->req->param('f') || '';
+
   my %logargs = (
       sha1   => $commit->sha1,
       count  => Gitalist->config->{paging}{log} || 25,
-      ($c->req->param('f') ? (file => $c->req->param('f')) : ())
+      ($filename ? (file => $filename) : ())
   );
 
   my $page = $c->req->param('pg') || 0;
@@ -281,8 +316,9 @@ sub shortlog : Local {
       commit    => $commit,
       log_lines => [$project->list_revs(%logargs)],
       refs      => $project->references,
-      action    => 'shortlog',
       page      => $page,
+      filename  => $filename,
+      action    => 'shortlog',
   );
 }
 
@@ -296,6 +332,11 @@ sub log : Local {
     $_[1]->stash->{action} = 'log';
 }
 
+# For legacy support.
+sub history : Local {
+  $_[0]->shortlog(@_[1 .. $#_]);
+}
+
 =head2 tree
 
 The tree of a given commit.
@@ -334,6 +375,12 @@ sub reflog : Local {
   );
 }
 
+=head2 search
+
+The action for the search form.
+
+=cut
+
 sub search : Local {
   my($self, $c) = @_;
   $c->stash(current_action => 'GitRepos');
@@ -344,11 +391,11 @@ sub search : Local {
     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,
-    }
+    search => {
+      type   => $c->req->param('type'),
+      text   => $c->req->param('text'),
+      regexp => $c->req->param('regexp') || 0,
+    },
   );
 
   $c->stash(
@@ -359,49 +406,142 @@ sub search : Local {
   );
 }
 
+=head2 search_help
+
+Provides some help for the search form.
+
+=cut
+
 sub search_help : Local {
     my ($self, $c) = @_;
     $c->stash(template => 'search_help.tt2');
 }
 
+=head2 atom
+
+Provides an atom feed for a given project.
+
+=cut
+
 sub atom : Local {
-    # FIXME - implement atom
-    Carp::croak "Not implemented.";
+  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 $project = $c->stash->{Project};
+  my %logargs = (
+      sha1   => $project->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 ($project->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);
 }
 
+=head2 rss
+
+Provides an RSS feed for a given project.
+
+=cut
+
 sub rss : Local {
-    # FIXME - implement rss
-    Carp::croak "Not implemented.";
-}
+  my ($self, $c) = @_;
 
-sub blobdiff_plain : Local {
-    # FIXME - implement blobdiff_plain
-    Carp::croak "Not implemented.";
-}
+  my $project = $c->stash->{Project};
 
-sub blob_plain : Local {
-    # FIXME - implement blobdiff_plain
-    Carp::croak "Not implemented.";
+  my $rss = XML::RSS->new(version => '2.0');
+  $rss->channel(
+    title          => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
+    link           => $c->uri_for('summary', {p=>$project->name}),
+    language       => 'en',
+    description    => $project->description,
+    pubDate        => DateTime->now,
+    lastBuildDate  => DateTime->now,
+  );
+
+  my %logargs = (
+      sha1   => $project->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 ($project->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);
 }
 
+=head2 patch
+
+A raw patch for a given commit.
+
+=cut
+
 sub patch : Local {
-    # FIXME - implement patches
-    Carp::croak "Not implemented.";
+    my ($self, $c) = @_;
+    $c->detach('patches', [1]);
 }
 
+=head2 patches
+
+The patcheset for a given commit ???
+
+=cut
+
 sub patches : Local {
-    # FIXME - implement patches
-    Carp::croak "Not implemented.";
+    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);
 }
 
-sub snapshot : Local {
-    # FIXME - implement snapshot
-    Carp::croak "Not implemented.";
-}
+=head2 snapshot
 
-sub commitdiff_plain : Local {
-    # FIXME - implement commitdiff_plain
-    Carp::croak "Not implemented.";
+Provides a snapshot of a given commit.
+
+=cut
+
+sub snapshot : Local {
+    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->{Project}->snapshot(
+        sha1 => $sha1,
+        format => $format
+    );
+    $c->response->status(200);
+    $c->response->headers->header( 'Content-Disposition' =>
+                                       "attachment; filename=$snap[0]");
+    $c->response->body($snap[1]);
 }
 
 =head2 auto
@@ -413,8 +553,22 @@ Populate the header and footer. Perhaps not the best location.
 sub auto : Private {
   my($self, $c) = @_;
 
-  # XXX Move these to a plugin!
+  my $project = $c->req->param('p');
+  if (defined $project) {
+    eval {
+      $c->stash(Project => $c->model('GitRepos')->project($project));
+    };
+    if ($@) {
+      $c->detach('error_404');
+    }
+  }
+
+  my $a_project = $c->stash->{Project} || $c->model()->projects->[0];
   $c->stash(
+    git_version => $a_project->run_cmd('--version'),
+    version     => $Gitalist::VERSION,
+
+    # XXX Move these to a plugin!
     time_since => sub {
       return 'never' unless $_[0];
       return age_string(time - $_[0]->epoch);
@@ -429,193 +583,15 @@ sub auto : Private {
         join(' ', grep { defined } (split / /, shift)[0..10]);
     },
   );
-
-  # Yes, this is hideous.
-  $self->header($c);
-  $self->footer($c);
-}
-
-# XXX This could probably be dropped altogether.
-use Gitalist::Util qw(to_utf8);
-# Formally git_header_html
-sub header {
-  my($self, $c) = @_;
-
-  my $title = $c->config->{sitename};
-
-  my $project   = $c->req->param('project')  || $c->req->param('p');
-  my $action    = $c->req->param('action')   || $c->req->param('a');
-  my $file_name = $c->req->param('filename') || $c->req->param('f');
-  if(defined $project) {
-    $title .= " - " . to_utf8($project);
-    if (defined $action) {
-      $title .= "/$action";
-      if (defined $file_name) {
-        $title .= " - " . $file_name;
-        if ($action eq "tree" && $file_name !~ m|/$|) {
-          $title .= "/";
-        }
-      }
-    }
-  }
-
-  $c->stash->{version}     = $Gitalist::VERSION;
-  # check git's version by running it on the first project in the list.
-  $c->stash->{title}       = $title;
-
-  $c->stash->{stylesheet} = $c->config->{stylesheet} || 'gitweb.css';
-
-  $c->stash->{project} = $project;
-  my @links;
-  if($project) {
-    my %href_params = $self->feed_info($c);
-    $href_params{'-title'} ||= 'log';
-
-    foreach my $format qw(RSS Atom) {
-      my $type = lc($format);
-      push @links, {
-        rel   => 'alternate',
-        title => "$project - $href_params{'-title'} - $format feed",
-
-        # XXX A bit hacky and could do with using gitweb::href() features
-        href  => "?a=$type;p=$project",
-        type  => "application/$type+xml"
-        }, {
-        rel   => 'alternate',
-
-        # XXX This duplication also feels a bit awkward
-        title => "$project - $href_params{'-title'} - $format feed (no merges)",
-        href  => "?a=$type;p=$project;opt=--no-merges",
-        type  => "application/$type+xml"
-        };
-    }
-  } else {
-    push @links, {
-      rel => "alternate",
-      title => $c->config->{sitename}." projects list",
-      href => '?a=project_index',
-      type => "text/plain; charset=utf-8"
-      }, {
-      rel => "alternate",
-      title => $c->config->{sitename}." projects feeds",
-      href => '?a=opml',
-      type => "text/plain; charset=utf-8"
-      };
-  }
-
-  $c->stash->{favicon} = $c->config->{favicon};
-
-  # </head><body>
-
-  $c->stash(
-    logo_url      => $c->config->{logo_url},
-    logo_label    => $c->config->{logo_label},
-    logo_img      => $c->config->{logo},
-    home_link     => $c->config->{home_link},
-    home_link_str => $c->config->{home_link_str},
-    );
-
-  if (defined $project) {
-      eval {
-          $c->stash(Project => $c->model('GitRepos')->project($project));
-      };
-      if ($@) {
-          $c->detach('error_404');
-      }
-      $c->stash(
-          search_text => ( $c->req->param('s') ||
-                               $c->req->param('searchtext') || ''),
-          search_hash => ( $c->req->param('hb') || $c->req->param('hashbase')
-                               || $c->req->param('h')  || $c->req->param('hash')
-                                   || 'HEAD' ),
-      );
-  }
-  my $a_project = $c->stash->{Project} || $c->model()->projects->[0];
-  $c->stash->{git_version} = $a_project->run_cmd('--version');
 }
 
-# Formally git_footer_html
-sub footer {
-  my($self, $c) = @_;
-
-  my $feed_class = 'rss_logo';
-
-  my @feeds;
-  my $project = $c->req->param('project')  || $c->req->param('p');
-  if(defined $project) {
-    (my $pstr = $project) =~ s[/?\.git$][];
-    my $descr = $c->stash->{project_description}
-            = $c->stash->{Project} ? $c->stash->{Project}->description : '';
-
-    my %href_params = $self->feed_info($c);
-    if (!%href_params) {
-      $feed_class .= ' generic';
-    }
-    $href_params{'-title'} ||= 'log';
-
-    @feeds = [
-      map +{
-        class => $feed_class,
-        title => "$href_params{'-title'} $_ feed",
-        href  => "/?p=$project;a=\L$_",
-        name  => lc $_,
-        }, qw(RSS Atom)
-      ];
-  } else {
-    @feeds = [
-      map {
-        class => $feed_class,
-          title => '',
-          href  => "/?a=$_->[0]",
-          name  => $_->[1],
-        }, [opml=>'OPML'],[project_index=>'TXT'],
-      ];
-  }
+sub project_index : Local {
+    # FIXME - implement snapshot
+    Carp::croak "Not implemented.";
 }
-
-# XXX This feels wrong here, should probably be refactored.
-# returns hash to be passed to href to generate gitweb URL
-# in -title key it returns description of link
-sub feed_info {
-  my($self, $c) = @_;
-
-  my $format = shift || 'Atom';
-  my %res = (action => lc($format));
-
-  # feed links are possible only for project views
-  return unless $c->req->param('project');
-
-  # some views should link to OPML, or to generic project feed,
-  # or don't have specific feed yet (so they should use generic)
-  return if $c->req->param('action') =~ /^(?:tags|heads|forks|tag|search)$/x;
-
-  my $branch;
-  my $hash = $c->req->param('h')  || $c->req->param('hash');
-  my $hash_base = $c->req->param('hb') || $c->req->param('hashbase');
-
-  # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
-  # from tag links; this also makes possible to detect branch links
-  if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
-    (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
-    $branch = $1;
-  }
-
-  # find log type for feed description (title)
-  my $type = 'log';
-  my $file_name = $c->req->param('f') || $c->req->param('filename');
-  if (defined $file_name) {
-    $type  = "history of $file_name";
-    $type .= "/" if $c->req->param('action') eq 'tree';
-    $type .= " on '$branch'" if (defined $branch);
-  } else {
-    $type = "log of $branch" if (defined $branch);
-  }
-
-  $res{-title} = $type;
-  $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
-  $res{'file_name'} = $file_name;
-
-  return %res;
+sub opml : Local {
+    # FIXME - implement snapshot
+    Carp::croak "Not implemented.";
 }
 
 =head2 end
@@ -642,36 +618,44 @@ sub error_404 :Private {
 }
 
 sub age_string {
-       my $age = shift;
-       my $age_str;
-
-       if ($age > 60*60*24*365*2) {
-               $age_str = (int $age/60/60/24/365);
-               $age_str .= " years ago";
-       } elsif ($age > 60*60*24*(365/12)*2) {
-               $age_str = int $age/60/60/24/(365/12);
-               $age_str .= " months ago";
-       } elsif ($age > 60*60*24*7*2) {
-               $age_str = int $age/60/60/24/7;
-               $age_str .= " weeks ago";
-       } elsif ($age > 60*60*24*2) {
-               $age_str = int $age/60/60/24;
-               $age_str .= " days ago";
-       } elsif ($age > 60*60*2) {
-               $age_str = int $age/60/60;
-               $age_str .= " hours ago";
-       } elsif ($age > 60*2) {
-               $age_str = int $age/60;
-               $age_str .= " min ago";
-       } elsif ($age > 2) {
-               $age_str = int $age;
-               $age_str .= " sec ago";
-       } else {
-               $age_str .= " right now";
-       }
-       return $age_str;
+  my $age = shift;
+  my $age_str;
+
+  if ( $age > 60 * 60 * 24 * 365 * 2 ) {
+    $age_str  = ( int $age / 60 / 60 / 24 / 365 );
+    $age_str .= " years ago";
+  }
+  elsif ( $age > 60 * 60 * 24 * ( 365 / 12 ) * 2 ) {
+    $age_str  = int $age / 60 / 60 / 24 / ( 365 / 12 );
+    $age_str .= " months ago";
+  }
+  elsif ( $age > 60 * 60 * 24 * 7 * 2 ) {
+    $age_str  = int $age / 60 / 60 / 24 / 7;
+    $age_str .= " weeks ago";
+  }
+  elsif ( $age > 60 * 60 * 24 * 2 ) {
+    $age_str  = int $age / 60 / 60 / 24;
+    $age_str .= " days ago";
+  }
+  elsif ( $age > 60 * 60 * 2 ) {
+    $age_str  = int $age / 60 / 60;
+    $age_str .= " hours ago";
+  }
+  elsif ( $age > 60 * 2 ) {
+    $age_str  = int $age / 60;
+    $age_str .= " min ago";
+  }
+  elsif ( $age > 2 ) {
+    $age_str  = int $age;
+    $age_str .= " sec ago";
+  }
+  else {
+    $age_str .= " right now";
+  }
+  return $age_str;
 }
 
+
 =head1 AUTHOR
 
 Dan Brook
index 2274304..8815b6a 100644 (file)
@@ -14,6 +14,7 @@ has _util => ( isa => 'Gitalist::Git::Util',
                is => 'ro',
                lazy_build => 1,
                handles => [ 'run_cmd',
+                            'run_cmd_fh',
                             'run_cmd_list',
                             'get_gpp_object',
                             'gpp',
index bec7c03..154f012 100644 (file)
@@ -13,6 +13,7 @@ class Gitalist::Git::Object {
                      weak_ref => 1,
                      handles => {
                          _run_cmd => 'run_cmd',
+                         _run_cmd_fh => 'run_cmd_fh',
                          _run_cmd_list => 'run_cmd_list',
                          _get_gpp_object => 'get_gpp_object',
                      },
index 4629c0b..91e43fb 100644 (file)
@@ -2,5 +2,5 @@ package Gitalist::Git::Object::Blob;
 use MooseX::Declare;
 
 class Gitalist::Git::Object::Blob extends Gitalist::Git::Object {
-        has '+type' => ( default => 'blob' );
+  has '+type' => ( default => 'blob' );
 }
index 9b1de8a..59af17e 100644 (file)
@@ -6,6 +6,7 @@ class Gitalist::Git::Object::Commit
     with Gitalist::Git::Object::HasTree {
         use MooseX::Types::Moose qw/Str Int Bool Maybe ArrayRef/;
         use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
+        use Moose::Autobox;
         use List::MoreUtils qw/any zip/;
         our $SHA1RE = qr/[0-9a-fA-F]{40}/;
 
@@ -22,7 +23,31 @@ class Gitalist::Git::Object::Commit
                                       ],
                          );
 
-         method diff ( Maybe[Bool] :$patch?,
+        method get_patch ( Maybe[NonEmptySimpleStr] $parent_hash?,
+                           Int $patch_count?) {
+            # assembling the git command to execute...
+            my @cmd = qw/format-patch --encoding=utf8 --stdout/;
+
+            # patch, or patch set?
+            push @cmd,
+                defined $patch_count
+                ? "-$patch_count -n" : "-1";
+
+            # refspec
+            if (defined $parent_hash) {
+                #  if a parent is specified: hp..h
+                push @cmd, "$parent_hash.." . $self->sha1;
+            } else {
+                #  if not, but a merge commit: --cc h
+                #  otherwise: --root h
+                push @cmd, $self->parents->length > 1
+                    ? '--cc' : '--root';
+                push @cmd, $self->sha1;
+            }
+            return $self->_run_cmd_fh( @cmd );
+        }
+
+        method diff ( Maybe[Bool] :$patch?,
                        Maybe[NonEmptySimpleStr] :$parent?,
                        Maybe[NonEmptySimpleStr] :$file?
                    ) {
@@ -115,4 +140,47 @@ class Gitalist::Git::Object::Commit
             return @ret;
         }
 
+
+  # XXX A prime candidate for caching.
+  method blame ( NonEmptySimpleStr $filename ) {
+    my @blameout = $self->_run_cmd_list(
+      blame => '-p', $self->sha1, '--', $filename
+    );
+
+    my(%commitdata, @filedata);
+    while(defined(local $_ = shift @blameout)) {
+      my ($sha1, $orig_lineno, $lineno, $group_size) =
+        /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+
+      $commitdata{$sha1} = {}
+        unless exists $commitdata{$sha1};
+
+      my $commit = $commitdata{$sha1};
+      my $line;
+      until(($line = shift @blameout) =~ s/^\t//) {
+        $commit->{$1} = $2
+         if $line =~ /^(\S+) (.*)/;
+      }
+
+      unless(exists $commit->{author_dt}) {
+        for my $t (qw/author committer/) {
+          my $dt = DateTime->from_epoch(epoch => $commit->{"$t-time"});
+          $dt->set_time_zone($commit->{"$t-tz"});
+          $commit->{"$t\_dt"} = $dt;
+        }
+      }
+
+      push @filedata, {
+        line => $line,
+        commit => { sha1 => $sha1, %$commit },
+        meta => {
+          orig_lineno => $orig_lineno,
+          lineno => $lineno,
+          ( $group_size ? (group_size => $group_size) : () ),
+        },
+      };
     }
+
+    return \@filedata;
+  }
+}
index 5129f12..981e824 100644 (file)
@@ -24,6 +24,7 @@ class Gitalist::Git::Project with Gitalist::Git::HasUtils {
     use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
     use MooseX::Types::Path::Class qw/Dir/;
     use MooseX::Types::Moose qw/Str Maybe Bool HashRef ArrayRef/;
+    use Moose::Autobox;
     use List::MoreUtils qw/any zip/;
     use DateTime;
     use Gitalist::Git::Object::Blob;
@@ -116,6 +117,14 @@ ArrayRef of hashes containing the name and sha1 of all heads.
     has heads => ( isa => ArrayRef[HashRef],
                    is => 'ro',
                    lazy_build => 1);
+=head2 tags
+
+ArrayRef of hashes containing the name and sha1 of all tags.
+
+=cut
+    has tags => ( isa => ArrayRef[HashRef],
+                   is => 'ro',
+                   lazy_build => 1);
 
 =head2 references
 
@@ -238,6 +247,34 @@ Returns a list of revs for the given head ($sha1).
         return @revs;
     }
 
+=head2 snapshot($sha1, $format)
+
+Generate an archived snapshot of the repository.
+$sha1 should be a commit or tree.
+Returns a filehandle to read from.
+
+=cut
+
+method snapshot (NonEmptySimpleStr :$sha1,
+                 NonEmptySimpleStr :$format
+               ) {
+    # TODO - only valid formats are 'tar' and 'zip'
+    my $formats = { tgz => 'tar', zip => 'zip' };
+    unless ($formats->exists($format)) {
+        die("No such format: $format");
+    }
+    $format = $formats->{$format};
+    my $name = $self->name;
+    $name =~ s,([^/])/*\.git$,$1,;
+    my $filename = $name;
+    $filename .= "-$sha1.$format";
+    $name =~ s/\047/\047\\\047\047/g;
+
+    my @cmd = ('archive', "--format=$format", "--prefix=$name/", $sha1);
+    return ($filename, $self->run_cmd_fh(@cmd));
+    # TODO - support compressed archives
+}
+
 =head2 diff($commit, $patch?, $parent?, $file?)
 
 Generate a diff from a given L<Gitalist::Git::Object>.
@@ -358,6 +395,32 @@ FIXME Should this return objects?
         return \@ret;
     }
 
+    method _build_tags {
+        my @revlines = $self->run_cmd_list('for-each-ref',
+          '--sort=-creatordate',
+          '--format=%(objectname) %(objecttype) %(refname) %(*objectname) %(*objecttype) %(subject)%00%(creator)',
+         'refs/tags'
+        );
+        my @ret;
+        for my $line (@revlines) {
+            my($refinfo, $creatorinfo) = split /\0/, $line;
+           my($rev, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
+            my($creator, $epoch, $tz) = ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
+            $name =~ s!^refs/tags/!!;
+
+            push @ret, { sha1 => $rev, name => $name };
+
+            #FIXME: That isn't the time I'm looking for..
+            if($epoch and $tz) {
+                my $dt = DateTime->from_epoch(epoch => $epoch);
+                $dt->set_time_zone($tz);
+                $ret[-1]->{last_change} = $dt;
+            }
+        }
+
+        return \@ret;
+    }
+
     method _build_references {
        # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
        # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
index 988ed03..966c144 100644 (file)
@@ -4,6 +4,7 @@ class Gitalist::Git::Util {
     use File::Which;
     use Git::PurePerl;
     use IPC::Run qw(run);
+    use Symbol qw(geniosym);
     use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
 
     has project => (
@@ -46,6 +47,19 @@ EOR
         return $out;
     }
 
+    method run_cmd_fh (@args) {
+        my ($in, $out, $err) = (geniosym, geniosym, geniosym);
+        unshift @args, ('--git-dir' => $self->gitdir)
+            if $self->has_project;
+#        print STDERR 'RUNNING: ', $self->_git, qq[ @args], $/;
+        run [$self->_git, @args],
+            '<pipe', $in,
+            '>pipe', $out,
+            '2>pipe', $err
+                or die "cmd returned *?";
+        return ($out, $err);
+    }
+
     method run_cmd_list (@args) {
         my $cmdout = $self->run_cmd(@args);
         return $cmdout ? split(/\n/, $cmdout) : ();
diff --git a/lib/Gitalist/Util.pm b/lib/Gitalist/Util.pm
deleted file mode 100644 (file)
index cbaad56..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package Gitalist::Util;
-
-use Sub::Exporter -setup => {
-   exports => ['to_utf8']
-};
-
-=pod
-
-=head1 NAME
-
-Gitalist::Util - Your usual catch all utility function package.
-
-=cut
-
-# decode sequences of octets in utf8 into Perl's internal form,
-# which is utf-8 with utf8 flag set if needed.  gitweb writes out
-# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
-sub to_utf8 {
-       my $str = shift;
-       if (utf8::valid($str)) {
-               utf8::decode($str);
-               return $str;
-       } else {
-               return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
-       }
-}
-
-1;
index 278f419..4343f3f 100644 (file)
@@ -26,8 +26,8 @@ it under the same terms as Perl itself.
 =cut
 
 __PACKAGE__->config(
-       TEMPLATE_EXTENSION => '.tt2',
-       WRAPPER            => 'default.tt2',
+  TEMPLATE_EXTENSION => '.tt2',
+  WRAPPER            => 'default.tt2',
 );
 
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
index d110b76..9e689d1 100755 (executable)
@@ -20,8 +20,6 @@ use File::Basename qw(basename);
 use FindBin;
 binmode STDOUT, ':utf8';
 
-use Gitalist::Util qw(to_utf8);
-
 BEGIN {
        CGI->compile();
 }
@@ -1159,6 +1157,19 @@ sub project_in_list {
        return @list && scalar(grep { $_->{'path'} eq $project } @list);
 }
 
+# decode sequences of octets in utf8 into Perl's internal form,
+# which is utf-8 with utf8 flag set if needed.  gitweb writes out
+# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
+sub to_utf8 {
+       my $str = shift;
+       if (utf8::valid($str)) {
+               utf8::decode($str);
+               return $str;
+       } else {
+               return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
+       }
+}
+
 ## ----------------------------------------------------------------------
 ## HTML aware string manipulation
 
index aa10f7a..d7a0bb7 100644 (file)
@@ -1,11 +1,9 @@
-[% sha1 = sha1 || HEAD %]
-<span class='chroma-hash'>
-  [%
+[%- 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>
+-%]</span>
diff --git a/root/_diff_plain.tt2 b/root/_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 -%]
index 8f70e2f..2057772 100644 (file)
@@ -1,4 +1,4 @@
-<table class='diff-tree'>
+<table class='diff-tree listing'>
  <thead>
   <tr>
    <th>file</th>
@@ -16,7 +16,7 @@
  <tbody>
   [% FOREACH line IN diff_tree -%]
   <tr>
-   <td class='filename'>
+   <td class='file-name'>
     [% line.file %]
    </td>
    <td class='status'>
diff --git a/root/_footer_feeds.tt2 b/root/_footer_feeds.tt2
new file mode 100644 (file)
index 0000000..0e50cf8
--- /dev/null
@@ -0,0 +1,32 @@
+  [% IF Project %]
+    [%-
+      has_branch = c.req.param('h').match('^(refs/heads/.*)') || c.req.param('hb').match('^(refs/heads/.*)');
+      branch     = has_branch.0 || '';
+      feed_title = 'log';
+      feed_fn    = c.req.param('f');
+      IF feed_fn;
+        title = 'history of ' _ c.req.param('filename');
+        title = title _ (branch ? ' on ' _ branch : '');
+      ELSE;
+        title = title _ (branch ? ' of ' _ branch : '');
+      END;
+
+      feed_args = { h = branch || c.req.param('h') || c.req.param('hb') || 'HEAD' };
+      IF feed_fn; feed_args.f = feed_fn; END;
+    -%]
+    <a
+      class="rss_logo"
+      title="[% feed_title %]"
+      href="[% c.uri_for('rss', feed_args) %]">RSS</a>
+    <a
+      class="rss_logo"
+      title="[% feed_title %]"
+      href="[% c.uri_for('atom', feed_args) %]">Atom</a>
+  [% ELSE %]
+    <a
+      class="rss_logo"
+      href="[% c.uri_for('opml') %]">OPML</a>
+    <a
+      class="rss_logo"
+      href="[% c.uri_for('project_index') %]">TXT</a>
+  [% END %]
diff --git a/root/_header_feeds.tt2 b/root/_header_feeds.tt2
new file mode 100644 (file)
index 0000000..5982b16
--- /dev/null
@@ -0,0 +1,28 @@
+  [% IF Project %]
+  <!-- XXX Missing the no-merges links -->
+  <link
+    rel="alternate"
+    title="[% Project.name %] - [% title %] Atom feed"
+    href="[% c.uri_for('atom') %]"
+    type="application/atom+xml"
+  >
+  <link
+    rel="alternate"
+    title="[% Project.name %] - [% title %] RSS feed"
+    href="[% c.uri_for('rss') %]"
+    type="application/rss+xml"
+  >
+  [% ELSE %]
+  <link
+    rel="alternate"
+    title="[% c.config.sitename %] Git projects list"
+    href="[% c.uri_for('project_index') %]"
+    type="text/plain; charset=utf-8"
+  > 
+  <link
+    rel="alternate"
+    title="[% c.config.sitename %] Git projects feeds"
+    href="[% c.uri_for('opml') %]"
+    type="text/x-opml"
+  >
+  [% END %]
index 0a1c45b..0eb77e7 100644 (file)
@@ -1,4 +1,4 @@
-<table class='heads'>
+<table class='[% action %] listing'>
  <thead>
   <tr>
    <th>HEAD</th>
@@ -19,8 +19,8 @@
  <tbody>
  [% FOREACH head IN heads %]
   <tr>
-   <td>[% INCLUDE '_chroma_hash.tt2' sha1 = head.sha1.substr(0,7) %]</td>
-   <td class='time-since'>[% time_since(head.last_change) %]</td>
+   <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>
index 2c1dde1..19caaf1 100644 (file)
@@ -1,4 +1,4 @@
-<table class='shortlog'>
+<table class='shortlog listing'>
  <thead>
   <tr>
    <th>sha1</th>
@@ -22,8 +22,8 @@
  <tbody>
  [% FOREACH line IN log_lines %]
   <tr>
-   <td class='sha1'>[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %]</td>
-   <td class='time-since'>[% time_since(line.authored_time) %]</td>
+   <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 %]
index 58d68d8..d6c8f41 100644 (file)
@@ -1,4 +1,4 @@
-<table>
+<table class='tree listing'>
  <thead>
   <tr>
    <th>mode</th>
@@ -17,9 +17,9 @@
  <tbody>
   [% FOREACH item IN tree_list %]
   <tr>
-   <td>[% item.modestr %]</td>
+   <td class='file-mode'>[% item.modestr %]</td>
    [% theact = item.type == 'tree' ? 'tree' : 'blob' -%]
-   <td class='filename'>
+   <td class='file-name'>
     <a href="[% c.uri_for(theact, {h=item.sha1, hb=commit.sha1, f=(path ? path _ '/' _ item.file : item.file)}) %]">[% item.file %]</a>
    </td>
    <td class='action-list'>
diff --git a/root/blame.tt2 b/root/blame.tt2
new file mode 100644 (file)
index 0000000..23c7769
--- /dev/null
@@ -0,0 +1,38 @@
+[% PROCESS 'nav/actions.tt2' object = head %]
+[% IF object.type == 'commit' %]
+<div class='commit-message'>
+[% head.comment.substr(0, 85) %] ...
+</div>
+[% END %]
+
+<h3>BLOB PATH</h3>
+
+<div id='blame'>
+<table>
+ <thead>
+  <tr>
+   <th>sha1</th>
+   <th>line</th>
+   <th>data</th>
+  </tr>
+ </thead>
+
+ <tfoot>
+  <tr>
+   <td>sha1</td>
+   <td>line</td>
+   <td>data</td>
+  </tr>
+ </tfoot>
+
+ <tbody>
+ [% FOR info IN blame %]
+ <tr class=''>
+   <td class='commit-info'><a title="[% info.commit.author %] on [% info.commit.author_dt %]" href='[% c.uri_for('commit', {h=info.commit.sha1}) %]'>[% INCLUDE '_chroma_hash.tt2' sha1 = info.commit.sha1.substr(0,7) -%]</a></td>
+   <td class='lineno'><tt>[% info.meta.lineno %]</tt></td>
+   <td><pre>[% info.line | html %]</pre></td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+</div>
index 4798ee1..3a49fed 100644 (file)
@@ -7,7 +7,7 @@
 </div>
 [% END %]
 <div class='path'>
- <a href="[% c.uri_for("tree", {hb=head.sha1}) %]">[% project %]</a>
+ <a href="[% c.uri_for("tree", {hb=head.sha1}) %]">[% Project.name %]</a>
  [% # XXX The last part should link to blob_plain (or something) but doesn't ATM
     FOREACH part IN filename.split('/') %]
  / <a href="[% c.uri_for("tree", {hb=head.sha1}) %]">[% part %]</a>
diff --git a/root/blob_plain.tt2 b/root/blob_plain.tt2
new file mode 100644 (file)
index 0000000..592f8c5
--- /dev/null
@@ -0,0 +1 @@
+[% blob %]
diff --git a/root/blobdiff_plain.tt2 b/root/blobdiff_plain.tt2
new file mode 100644 (file)
index 0000000..4b771f4
--- /dev/null
@@ -0,0 +1 @@
+[%- INCLUDE '_diff_plain.tt2' -%]
index 8e114d0..2922e9e 100644 (file)
@@ -6,7 +6,7 @@
   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/>
@@ -24,9 +24,9 @@
  <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>
+     <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>
diff --git a/root/commitdiff_plain.tt2 b/root/commitdiff_plain.tt2
new file mode 100644 (file)
index 0000000..4b771f4
--- /dev/null
@@ -0,0 +1 @@
+[%- INCLUDE '_diff_plain.tt2' -%]
index 09a3104..af0ed15 100644 (file)
@@ -1,34 +1,30 @@
-[%- # git_header_html
--%]
+[%- IF no_wrapper || template.name.match('\.(css|js|txt)'); content; ELSE; -%]
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <!-- git web interface version [% version %], (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
   <!-- git core binaries version [% git_version %] -->
-  <meta charset="utf-8"/>
+  <meta charset="utf-8">
   <meta name="generator" content="gitweb/[% version %] git/[% git_version %][% mod_perl_version %]"/>
   <meta name="robots" content="index, nofollow"/>
-  <title>[% title | html %] (Gitalist)</title>
-  [% IF baseurl %]
-  <base href="[% baseurl %]" />
-  [% END %]
+  <title>[%-
+    title = BLOCK;
+      c.config.sitename;
+      IF Project; ' - ' _ Project.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') %]"/>
-  [% FOR link IN links %]
-            <link rel="[% link.rel %]"
-                  title="[% link.title %]"
-                  href="[% link.href %]"
-                  type="[% link.type %]"
-            />
-  [% END %]
-  [% IF favicon %]
-  <link rel="shortcut icon" href="[% favicon %]" type="image/png" />
-  [% END %]
+  <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>
 [% site_header %]
 
 <div id="page-header">
-  <a title="[% logo_label | url %]" href="[% logo_url | url %]"><img src="[% logo_img %]" alt="git" class="logo"/></a>
-  <a href="[% home_link | url %]">[% home_link_str %]</a>
-  [%- IF project %]
-  / <a href="[% c.uri_for('summary') %]">[% project %]</a>
+  <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('/') %]">A Gitalist</a>
+  [%- IF Project %]
+  / <a href="[% c.uri_for('summary') %]">[% Project.name %]</a>
   [% IF action;  " / " _ action; END;
   END %]
 [%
-  IF project;
+  IF Project;
     INCLUDE 'nav/search.tt2';
   END;
 # / git_header_html
 %]
 </div>
 
-[%- # git_footer_html
--%]
 <div id="page-footer">
-[% IF project AND project_description %]
-  <div class="page_footer_text">[% project_description | html %]</div>
-[% END %]
-[% FOR feed IN feeds %]
-    <a class="[% feed.class %]" title="[% feed.title %]" href="[% feed.href %]">[% feed.name %]</a>
+[% IF Project %]
+  [% Project.description | html %]
 [% END %]
+[% INCLUDE '_footer_feeds.tt2' %]
 </div>
 
-[% site_footer %]
-
 </body>
 </html>
-[%- # / git_footer_html
--%]
+[%- END -%]
index 86c8cec..5161cbd 100644 (file)
@@ -1,7 +1,7 @@
 [% INCLUDE 'nav/actions.tt2' object = commit %]
 
 <div>
-[% project %]
+[% Project.name %]
 </div>
 
 [% INCLUDE '_heads.tt2' %]
index e8c771b..cc0ff56 100644 (file)
     </div>
     <div class="meta">
      <table class='summary' cellspacing='0' cellpadding='0'>
-      <tr><td>author</td><td>[% line.author.name | html %]</td></tr>
-      <tr><td>authored time</td><td>[% line.authored_time %]</td></tr>
+      <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>[% line.committer.name | html %]</td></tr>
-      <tr><td>committered time</td><td>[% line.committed_time %]</td></tr>
+      <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>
diff --git a/root/logo.png b/root/logo.png
new file mode 100644 (file)
index 0000000..a57544b
Binary files /dev/null and b/root/logo.png differ
diff --git a/root/logo.svg b/root/logo.svg
new file mode 100644 (file)
index 0000000..f0367df
--- /dev/null
@@ -0,0 +1,133 @@
+<?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 a5a5767..0fe60b2 100644 (file)
@@ -3,9 +3,16 @@
     <a href="[% c.uri_for('shortlog', {h=object.sha1}) %]">shortlog</a> â€¢
     <a href="[% c.uri_for('log', {h=object.sha1}) %]">log</a> â€¢
     <a href="[% c.uri_for('commit', {h=object.sha1}) %]">commit</a> â€¢
-    <a href="[% c.uri_for('commitdiff', {h=object.sha1}) %]">commitdiff</a> â€¢
-    [% IF object.type == 'commit' %]
+    <a href="[% c.uri_for('commitdiff', {h=object.sha1}) %]">commitdiff</a>
+    [% IF object.type == 'commit' %] â€¢
     <a href="[% c.uri_for('tree', {h=object.tree_sha1, hb=object.sha1}) %]">tree</a>
     [% END %]
+    [% IF filename %]
+    Â§
+    <a href="[% c.uri_for('blob', {h=object.sha1,f=filename}) %]">blob</a> â€¢
+    <a href="[% c.uri_for('blame', {h=object.sha1,f=filename}) %]">blame</a> â€¢
+    <a href="[% c.uri_for('shortlog', {h=object.sha1,f=filename}) %]">history</a> â€¢
+    <a href="[% c.uri_for(action, {f=filename}) %]">HEAD</a>
+    [% END %]
     <div class='chroma-hash'>[% INCLUDE '_chroma_hash.tt2' sha1 = object.sha1 %]</div>
 </div>
index 09f30e4..42aeaea 100644 (file)
@@ -1,6 +1,6 @@
 <div id="page-search">
   <form method="get" action="[% c.uri_for('search') %]" enctype="application/x-www-form-urlencoded">
-  <input name="p" type="hidden" value="[% project %]" />
+  <input name="p" type="hidden" value="[% Project.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') %]" />
@@ -12,8 +12,8 @@
       <option value="grep">grep</option>
       <option value="pickaxe">pickaxe</option>
       -->
-  </select><sup><a href="/search_help?p=[% project %]">?</a></sup> search:
-  <input type="text" name="text" value="[% search_text %]"/>
+  </select><sup><a href="[% c.uri_for('search_help') %]">?</a></sup> search:
+  <input type="text" name="text" value="[% c.req.param('s') %]"/>
   <span title="Extended regular expression"><label><input type="checkbox" name="regexp" value="1" />re</label></span>
   </form>
 </div>
index b6c4f12..8d05e3c 100644 (file)
@@ -46,7 +46,7 @@ abbr, acronym {border-bottom:1px dotted #666;}
 address {margin:0 0 1.5em;font-style:italic;}
 del {color:#666;}
 pre {margin:1.5em 0;white-space:pre;}
-pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;}
+pre, code, tt {font:1.1em 'andale mono', 'lucida console', monospace;line-height:1.5;}
 li ul, li ol {margin:0;}
 ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;}
 ul {list-style-type:disc;}
index fd4b2fc..8fb3d80 100644 (file)
@@ -15,6 +15,13 @@ tfoot td {
   border-top: solid 1px #777;
 }
 
+.listing tbody tr:nth-child(even) {
+  background-color: #f7f7f7;
+}
+.listing tbody tr:hover {
+  background-color: #fefeaa;
+}
+
 span.chroma-hash {
   font-family: monospace;
   font-size:   1em;
@@ -32,6 +39,20 @@ div.chroma-hash {
   font-family: "Trebuchet MS", "Lucida Grande", serif;
 }
 
+.file-name, .file-mode {
+  font-family: monospace;
+}
+.action-list {
+  font-size: smaller;
+}
+
+.path {
+  border-bottom: solid 1px #ddd;
+  padding: 3px 0;
+  font-weight: bold;
+}
+
+
 /* header */
 #page-header {
   height: 25px;
@@ -44,6 +65,7 @@ div.chroma-hash {
 img.logo {
   float: right;
   border-width: 0px;
+  margin-top: 4px;
 }
 
 /* footer */
@@ -90,6 +112,7 @@ table.shortlog tbody td {
 /* /commit page */
 .commit-message {
   font-family: monospace;
+  font-size: 1.2em;
 }
 div.commit-message {
   margin-bottom: 10px;
@@ -106,19 +129,6 @@ pre.commit-message {
   font-family: monospace;
 }
 
-.filename {
-  font-family: monospace;
-}
-.action-list {
-  font-size: smaller;
-}
-
-.path {
-  border-bottom: solid 1px #ddd;
-  padding: 3px 0;
-  font-weight: bold;
-}
-
 /* /heads */
 .heads .head {
   font-weight: bold;
@@ -130,6 +140,27 @@ table.heads {
   width: 75%;
 }
 
+/* /blame */
+#blame pre, #blame tt {
+  margin: 0;
+  font-size: 0.9em;
+}
+#blame .commit-info {
+}
+#blame .lineno {
+  text-align: right;
+  padding: 0 8px;
+}
+#blame a {
+  text-decoration: none;
+}
+#blame tr.alt {
+  background-color: #f7f7f7;
+}
+#blame tbody tr:hover {
+  background-color: #fefeaa;
+}
+
 /* /blob */
 pre.blob {
   background-color: #333;
@@ -152,6 +183,11 @@ pre.blob {
   border-bottom: solid 1px green;
 }
 
+/* /commitdiff */
+.diff-patch {
+  font-size: 0.8em;
+}
+
 /* /log */
 #log .entry {
   border: solid 1px grey;
@@ -160,7 +196,7 @@ pre.blob {
 }
 #log .meta {
   border-top: dotted 1px #ddd;
-  color: #755;
+  color: #311;
 }
 #log table.summary {
   width: 33%;
@@ -169,6 +205,7 @@ pre.blob {
 #log .message {
   font-family: monospace;
   font-size: 1.15em;
+  /* XXX Need to enforce find the CSS switch to force the chroma-hash below the message box */
 }
 #log .age {
   float: right;
@@ -176,10 +213,17 @@ pre.blob {
   font-style: italic;
 }
 
-/**
- * from gitweb.css
- * XXX These can be rejigged once gitweb.css has gone away.
- */
+/* /summary */
+#stats {
+  float: right;
+}
+
+/* /tree */
+table.tree {
+  width: 65%;
+}
+
+/* Formerly of gitweb.css */
 
 span.refs span {
   padding: 0px 4px;
@@ -209,3 +253,24 @@ span.refs span.head {
 span.refs a {
   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%;
+  font-style: normal;
+  text-align: center;
+  text-decoration: none;
+}
+
+a.rss_logo:hover {
+  background-color: #ee5500;
+}
index 20f078f..7b6adb4 100644 (file)
@@ -1,11 +1,19 @@
 [% PROCESS 'nav/actions.tt2' object = head %]
 
 <div class='summary'>
-<dl>
-<dt>description</dt><dd>[% Project.description %]</dd>
-<dt>owner</dt><dd>[% Project.owner %]</dd>
-<dt>last change</dt><dd>[% Project.last_change %]</dd>
-</dl>
+<!-- <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>[% Project.description %]</dd>
+  <dt>owner</dt><dd>[% Project.owner %]</dd>
+  <dt>last change</dt><dd>[% Project.last_change %]</dd>
+ </dl>
+
 </div>
 
 <h2><a href='[% c.uri_for("shortlog") %]'>shortlog</a></h2>
diff --git a/root/tags.tt2 b/root/tags.tt2
new file mode 100644 (file)
index 0000000..768e842
--- /dev/null
@@ -0,0 +1,7 @@
+[% INCLUDE 'nav/actions.tt2' object = commit %]
+
+<div>
+[% Project.name %]
+</div>
+
+[% INCLUDE '_heads.tt2' heads = tags %]
index a69a6fa..b82a117 100644 (file)
@@ -6,7 +6,7 @@
 
 [% IF path -%]
 <div>
- <a href='[% c.uri_for("tree", {hb=commit.sha1}) %]'>[% project %]</a>
+ <a href='[% c.uri_for("tree", {hb=commit.sha1}) %]'>[% Project.name %]</a>
  [% fullpath = ''-%]
  [% FOREACH part IN path.split('/') -%]
  / <a href='[% c.uri_for("tree", {h=tree.sha1, hb=commit.sha1, f=fullpath _ part}) %]'>[% part %]</a>
index 9ebc7a4..422e82a 100644 (file)
--- a/t/01app.t
+++ b/t/01app.t
@@ -31,6 +31,12 @@ is request('/summary?p=DoesNotExist')->code, 404,
   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');
 }
 
 done_testing;
index 479a890..4ce679c 100644 (file)
@@ -77,3 +77,23 @@ is($patch->{diff}, '--- a/file1
 +bar
 ', 'patch->{diff} is correct');
 is($patch->{dst}, '5716ca5987cbf97d6bb54920bea6adde242d87e6', 'patch->{dst} is correct');
+
+ok(index($commit_obj->get_patch, 'From 3f7567c7bdf7e7ebf410926493b92d398333116e Mon Sep 17 00:00:00 2001
+From: Florian Ragwitz <rafl@debian.org>
+Date: Tue, 6 Mar 2007 20:39:45 +0100
+Subject: [PATCH] bar
+
+---
+ file1 |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/file1 b/file1
+index 257cc56..5716ca5 100644
+--- a/file1
++++ b/file1
+@@ -1 +1 @@
+-foo
++bar
+--') == 0, 'commit_obj->get_patch can return a patch');
+
+like($commit_obj->get_patch(undef, 3), qr!PATCH 2/2!, 'commit_obj->get_patch can return a patchset');
index c59b77e..0fe47b3 100644 (file)
@@ -56,6 +56,6 @@ like($proj->head_hash('HEAD'), qr/^([0-9a-fA-F]{40})$/, 'head_hash');
 {
     my @tree = $proj->list_tree('3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
     is(scalar @tree, 1, "tree array contains one entry.");
-    isa_ok(@tree[0], 'Gitalist::Git::Object', 'tree element 0');
+    isa_ok($tree[0], 'Gitalist::Git::Object', 'tree element 0');
 }
 
index c0a25cc..a895bda 100644 (file)
@@ -34,19 +34,17 @@ test('/', 'a=blob;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=refs/hea
 test('/', 'a=blob;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blob;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
-TODO: {
-    local $TODO = "Action: blob_plain is not yet implemented.";
-    test('/', 'a=blob_plain;f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=blob_plain;f=dir1/file2;hb=HEAD');
-    test('/', 'a=blob_plain;f=dir1/file2;hb=master');
-    test('/', 'a=blob_plain;f=dir1/file2;hb=refs/heads/master');
-    test('/', 'a=blob_plain;f=file1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=blob_plain;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=blob_plain;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    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=blob_plain;f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=blob_plain;f=dir1/file2;hb=HEAD');
+test('/', 'a=blob_plain;f=dir1/file2;hb=master');
+test('/', 'a=blob_plain;f=dir1/file2;hb=refs/heads/master');
+test('/', 'a=blob_plain;f=file1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=blob_plain;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=blob_plain;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
+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');
@@ -54,13 +52,10 @@ test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257c
 test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 
-TODO: {
-    local $TODO = "Action: blobdiff_plain is not yet implemented.";
-    test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3f7567c7bdf7e7ebf410926493b92d398333116e;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-}
+test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3f7567c7bdf7e7ebf410926493b92d398333116e;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=blobdiff_plain;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
 
 test('/', 'a=commit');
 test('/', 'a=commit;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
@@ -86,45 +81,40 @@ test('/', 'a=commitdiff;h=master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
 test('/', 'a=commitdiff;h=refs/heads/master');
 test('/', 'a=commitdiff;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
-TODO: {
-    local $TODO = "Action: commitdiff_plain is not yet implemented.";
-    test('/', 'a=commitdiff_plain');
-    test('/', 'a=commitdiff_plain;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=commitdiff_plain;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=commitdiff_plain;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=commitdiff_plain;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=commitdiff_plain;h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=commitdiff_plain;h=HEAD');
-    test('/', 'a=commitdiff_plain;h=HEAD;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=commitdiff_plain;h=master');
-    test('/', 'a=commitdiff_plain;h=master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=commitdiff_plain;h=refs/heads/master');
-    test('/', 'a=commitdiff_plain;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-}
+test('/', 'a=commitdiff_plain');
+test('/', 'a=commitdiff_plain;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=commitdiff_plain;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=commitdiff_plain;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=commitdiff_plain;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=commitdiff_plain;h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=commitdiff_plain;h=HEAD');
+test('/', 'a=commitdiff_plain;h=HEAD;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=commitdiff_plain;h=master');
+test('/', 'a=commitdiff_plain;h=master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=commitdiff_plain;h=refs/heads/master');
+test('/', 'a=commitdiff_plain;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
-TODO: {
-    local $TODO = "Action: history is not yet implemented.";
-    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');
-    test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master');
-    test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=HEAD');
-    test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=master');
-    test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=refs/heads/master');
-    test('/', 'a=history;f=dir1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=history;f=dir1;hb=HEAD');
-    test('/', 'a=history;f=dir1;hb=master');
-    test('/', 'a=history;f=dir1;hb=refs/heads/master');
-    test('/', 'a=history;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=HEAD');
-    test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=master');
-    test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=refs/heads/master');
-    test('/', 'a=history;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=history;h=refs/heads/master');
-}
+
+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');
+test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master');
+test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=HEAD');
+test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=master');
+test('/', 'a=history;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=refs/heads/master');
+test('/', 'a=history;f=dir1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=history;f=dir1;hb=HEAD');
+test('/', 'a=history;f=dir1;hb=master');
+test('/', 'a=history;f=dir1;hb=refs/heads/master');
+test('/', 'a=history;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=HEAD');
+test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=master');
+test('/', 'a=history;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=refs/heads/master');
+test('/', 'a=history;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=history;h=refs/heads/master');
 
 test('/', 'a=log');
 test('/', 'a=log;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
@@ -134,35 +124,29 @@ test('/', 'a=log;h=HEAD');
 test('/', 'a=log;h=master');
 test('/', 'a=log;h=refs/heads/master');
 
-TODO: {
-    local $TODO = "Action: patch is not yet implemented.";
-    test('/', 'a=patch');
-    test('/', 'a=patch;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=patch;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patch;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=patch;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patch;h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=patch;h=HEAD');
-    test('/', 'a=patch;h=HEAD;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patch;h=master');
-    test('/', 'a=patch;h=master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patch;h=refs/heads/master');
-    test('/', 'a=patch;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patch;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=patch;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=patch;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
-}
+test('/', 'a=patch');
+test('/', 'a=patch;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=patch;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patch;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=patch;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patch;h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=patch;h=HEAD');
+test('/', 'a=patch;h=HEAD;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patch;h=master');
+test('/', 'a=patch;h=master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patch;h=refs/heads/master');
+test('/', 'a=patch;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patch;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=patch;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=patch;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
 
-TODO: {
-    local $TODO = "Action: patches is not yet implemented.";
-    test('/', 'a=patches');
-    test('/', 'a=patches;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
-    test('/', 'a=patches;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
-    test('/', 'a=patches;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
-    test('/', 'a=patches;h=HEAD');
-    test('/', 'a=patches;h=master');
-    test('/', 'a=patches;h=refs/heads/master');
-}
+test('/', 'a=patches');
+test('/', 'a=patches;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+test('/', 'a=patches;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+test('/', 'a=patches;h=3f7567c7bdf7e7ebf410926493b92d398333116e');
+test('/', 'a=patches;h=HEAD');
+test('/', 'a=patches;h=master');
+test('/', 'a=patches;h=refs/heads/master');
 
 test('/', 'a=search_help');
 
@@ -174,19 +158,16 @@ test('/', 'a=shortlog;h=HEAD');
 test('/', 'a=shortlog;h=master');
 test('/', 'a=shortlog;h=refs/heads/master');
 
-TODO: {
-    local $TODO = "Action: snapshot is not yet implemented.";
-    test('/', 'a=snapshot;h=145dc3ef5d307be84cb9b325d70bd08aeed0eceb;sf=tgz');
-    test('/', 'a=snapshot;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;sf=tgz');
-    test('/', 'a=snapshot;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac;sf=tgz');
-    test('/', 'a=snapshot;h=3f7567c7bdf7e7ebf410926493b92d398333116e;sf=tgz');
-    test('/', 'a=snapshot;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;sf=tgz');
-    test('/', 'a=snapshot;h=82b5fee28277349b6d46beff5fdf6a7152347ba0;sf=tgz');
-    test('/', 'a=snapshot;h=9062594aebb5df0de7fb92413f17a9eced196c22;sf=tgz');
-    test('/', 'a=snapshot;h=HEAD;sf=tgz');
-    test('/', 'a=snapshot;h=master;sf=tgz');
-    test('/', 'a=snapshot;h=refs/heads/master;sf=tgz');
-}
+test('/', 'a=snapshot;h=145dc3ef5d307be84cb9b325d70bd08aeed0eceb;sf=tgz');
+test('/', 'a=snapshot;h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;sf=tgz');
+test('/', 'a=snapshot;h=3bc0634310b9c62222bb0e724c11ffdfb297b4ac;sf=tgz');
+test('/', 'a=snapshot;h=3f7567c7bdf7e7ebf410926493b92d398333116e;sf=tgz');
+test('/', 'a=snapshot;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;sf=tgz');
+test('/', 'a=snapshot;h=82b5fee28277349b6d46beff5fdf6a7152347ba0;sf=tgz');
+test('/', 'a=snapshot;h=9062594aebb5df0de7fb92413f17a9eced196c22;sf=tgz');
+test('/', 'a=snapshot;h=HEAD;sf=tgz');
+test('/', 'a=snapshot;h=master;sf=tgz');
+test('/', 'a=snapshot;h=refs/heads/master;sf=tgz');
 
 test('/', 'a=tree');
 test('/', 'a=tree;f=dir1;h=729a7c3f6ba5453b42d16a43692205f67fb23bc1;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
@@ -217,40 +198,68 @@ 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');
+test('/', 'a=atom;f=dir1/file2;opt=--no-merges');
+test('/', 'a=atom;f=dir1;h=refs/heads/master');
+test('/', 'a=atom;f=dir1;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=atom;f=dir1;opt=--no-merges');
+test('/', 'a=atom;f=file1');
+test('/', 'a=atom;f=file1;h=refs/heads/master');
+test('/', 'a=atom;f=file1;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=atom;f=file1;opt=--no-merges');
+test('/', 'a=atom;h=refs/heads/master');
+test('/', 'a=atom;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=atom;opt=--no-merges');
+
+test('/', 'a=rss');
+test('/', 'a=rss;f=dir1');
+test('/', 'a=rss;f=dir1/file2');
+test('/', 'a=rss;f=dir1/file2;opt=--no-merges');
+test('/', 'a=rss;f=dir1;h=refs/heads/master');
+test('/', 'a=rss;f=dir1;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=rss;f=dir1;opt=--no-merges');
+test('/', 'a=rss;f=file1');
+test('/', 'a=rss;f=file1;h=refs/heads/master');
+test('/', 'a=rss;f=file1;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=rss;f=file1;opt=--no-merges');
+test('/', 'a=rss;h=refs/heads/master');
+test('/', 'a=rss;h=refs/heads/master;opt=--no-merges');
+test('/', 'a=rss;opt=--no-merges');
+
 TODO: {
-    local $TODO = "Action: atom is not yet implemented.";
-    test('/', 'a=atom');
-    test('/', 'a=atom;f=dir1');
-    test('/', 'a=atom;f=dir1/file2');
-    test('/', 'a=atom;f=dir1/file2;opt=--no-merges');
-    test('/', 'a=atom;f=dir1;h=refs/heads/master');
-    test('/', 'a=atom;f=dir1;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=atom;f=dir1;opt=--no-merges');
-    test('/', 'a=atom;f=file1');
-    test('/', 'a=atom;f=file1;h=refs/heads/master');
-    test('/', 'a=atom;f=file1;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=atom;f=file1;opt=--no-merges');
-    test('/', 'a=atom;h=refs/heads/master');
-    test('/', 'a=atom;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=atom;opt=--no-merges');
+  local $TODO = 'The project_index action is yet to be implemented';
+  test('/', 'a=project_index');
+}
+TODO: {
+  local $TODO = 'The opml action is yet to be implemented';
+  test('/', 'a=opml');
 }
-
 TODO: {
-    local $TODO = "Action: rss is not yet implemented.";
-    test('/', 'a=rss');
-    test('/', 'a=rss;f=dir1');
-    test('/', 'a=rss;f=dir1/file2');
-    test('/', 'a=rss;f=dir1/file2;opt=--no-merges');
-    test('/', 'a=rss;f=dir1;h=refs/heads/master');
-    test('/', 'a=rss;f=dir1;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=rss;f=dir1;opt=--no-merges');
-    test('/', 'a=rss;f=file1');
-    test('/', 'a=rss;f=file1;h=refs/heads/master');
-    test('/', 'a=rss;f=file1;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=rss;f=file1;opt=--no-merges');
-    test('/', 'a=rss;h=refs/heads/master');
-    test('/', 'a=rss;h=refs/heads/master;opt=--no-merges');
-    test('/', 'a=rss;opt=--no-merges');
+  local $TODO = 'The tags action is yet to be implemented';
+  test('/', 'a=tags');
+}
+TODO: {
+  local $TODO = 'The blame action is yet to be implemented';
+
+  test('/', 'a=blame;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+  test('/', 'a=blame;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD');
+  test('/', 'a=blame;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master');
+  test('/', 'a=blame;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master');
+  test('/', 'a=blame;f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+  test('/', 'a=blame;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99');
+  test('/', 'a=blame;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=257cc5642cb1a054f08cc83f2d943e56fd3ebe99');
+  test('/', 'a=blame;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=5716ca5987cbf97d6bb54920bea6adde242d87e6');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=HEAD');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=master');
+  test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=refs/heads/master');
+  test('/', 'a=blame;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac');
+  test('/', 'a=blame;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e');
 }
 
 done_testing;
index 2f02d50..6617816 100644 (file)
@@ -43,3 +43,7 @@ project_maxdepth 2007
   log = 50
   summary = 16
 </paging>
+
+<patches>
+  max = 16
+</patches>