#!/usr/bin/perl
-# gitweb.pl - simple web interface to track changes in git repositories
+# gitweb - simple web interface to track changes in git repositories
#
# (C) 2005, Kay Sievers <kay.sievers@vrfy.org>
# (C) 2005, Christian Gierke <ch@gierke.de>
use Fcntl ':mode';
my $cgi = new CGI;
-my $version = "203";
+my $version = "240";
my $my_url = $cgi->url();
my $my_uri = $cgi->url(-absolute => 1);
my $rss_link = "";
# absolute fs-path which will be prepended to the project path
my $projectroot = "/pub/scm";
+$projectroot = "/home/kay/public_html/pub/scm";
# location of the git-core binaries
my $gitbin = "/usr/bin";
if ($action eq "git-logo.png") {
git_logo();
exit;
+ } elsif ($action eq "opml") {
+ git_opml();
+ exit;
+ }
+}
+
+my $order = $cgi->param('o');
+if (defined $order) {
+ if ($order =~ m/[^a-zA-Z0-9_]/) {
+ undef $order;
+ die_error(undef, "Invalid order parameter.");
}
-} else {
- $action = "summary";
}
my $project = $cgi->param('p');
die_error(undef, "No such project.");
}
$rss_link = "<link rel=\"alternate\" title=\"$project log\" href=\"$my_uri?p=$project;a=rss\" type=\"application/rss+xml\"/>";
- $ENV{'GIT_OBJECT_DIRECTORY'} = "$projectroot/$project/objects";
+ $ENV{'GIT_DIR'} = "$projectroot/$project";
} else {
git_project_list();
exit;
}
my $hash = $cgi->param('h');
-if (defined $hash && !($hash =~ m/^[0-9a-fA-F]{40}$/)) {
- undef $hash;
- die_error(undef, "Invalid hash parameter.");
+if (defined $hash) {
+ if (!($hash =~ m/^[0-9a-fA-F]{40}$/)) {
+ if ($hash =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
+ undef $hash;
+ die_error(undef, "Non-canonical hash parameter.");
+ }
+ if ($hash =~ m/[^a-zA-Z0-9_\.\/\-\+\#\~\:\!]/) {
+ undef $hash;
+ die_error(undef, "Invalid character in hash parameter.");
+ }
+ # replace branch-name with hash
+ my $branchlist = git_read_refs("refs/heads");
+ foreach my $entry (@$branchlist) {
+ my %branch = %$entry;
+ if ($branch{'name'} eq $hash) {
+ $hash = $branch{'id'};
+ last;
+ }
+ }
+ }
}
my $hash_parent = $cgi->param('hp');
die_error(undef, "Invalid parent hash parameter.");
}
-my $time_back = $cgi->param('t');
-if (defined $time_back) {
- if ($time_back =~ m/^[^0-9]+$/) {
- undef $time_back;
- die_error(undef, "Invalid time parameter.");
+my $page = $cgi->param('pg');
+if (defined $page) {
+ if ($page =~ m/^[^0-9]+$/) {
+ undef $page;
+ die_error(undef, "Invalid page parameter.");
}
}
-
my $searchtext = $cgi->param('s');
if (defined $searchtext) {
if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
$searchtext = quotemeta $searchtext;
}
-if ($action eq "summary") {
+if (!defined $action || $action eq "summary") {
git_summary();
exit;
} elsif ($action eq "branches") {
} elsif ($action eq "shortlog") {
git_shortlog();
exit;
+} elsif ($action eq "tag") {
+ git_tag();
+ exit;
} else {
undef $action;
die_error(undef, "Unknown action.");
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<!-- git web interface v$version, (C) 2005, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke <ch\@gierke.de> -->
<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+<meta name="robots" content="index, nofollow"/>
<title>$title</title>
$rss_link
<style type="text/css">
a { color:#0000cc; }
a:hover, a:visited, a:active { color:#880000; }
div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
-div.page_header a:visited { color:#0000cc; }
+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.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
a.list { text-decoration:none; color:#000000; }
-a.list:hover { color:#880000; }
+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; }
th { padding:2px 5px; font-size:12px; text-align:left; }
+tr.light:hover { background-color:#edece6; }
+tr.dark { background-color:#f6f6f0; }
+tr.dark:hover { background-color:#edece6; }
td { padding:2px 5px; font-size:12px; vertical-align:top; }
td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
div.pre { font-family:monospace; font-size:12px; white-space:pre; }
div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
-input.search { margin:4px 8px; position:absolute; top:56px; right:12px }
-a.rss_logo { float:right; padding:3px 0px; width:35px; line-height:10px;
+div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
+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:10px;
<body>
EOF
print "<div class=\"page_header\">\n" .
- "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\">" .
+ "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
"<img src=\"$my_uri?a=git-logo.png\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
"</a>\n";
print $cgi->a({-href => $home_link}, "projects") . " / ";
$searchtext = "";
}
$cgi->param("a", "search");
- # post search form, but fake get parameter in browser
- #print $cgi->startform(-name => "search", -action => "$my_uri",
- # -onsubmit => "document.search.action='?p=$project;a=search;s='+document.search.s.value") .
- # $cgi->hidden(-name => "p") . "\n" .
- # $cgi->hidden(-name => "a") . "\n" .
- # $cgi->textfield(-name => "s", -value => $searchtext, -class => "search") .
- # $cgi->end_form() . "\n";
+ print $cgi->startform(-method => "get", -action => "$my_uri") .
+ "<div class=\"search\">\n" .
+ $cgi->hidden(-name => "p") . "\n" .
+ $cgi->hidden(-name => "a") . "\n" .
+ $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+ "</div>" .
+ $cgi->end_form() . "\n";
}
print "</div>\n";
}
print "<div class=\"page_footer_text\">" . escapeHTML($descr) . "</div>\n";
}
print $cgi->a({-href => "$my_uri?p=$project;a=rss", -class => "rss_logo"}, "RSS") . "\n";
+ } else {
+ print $cgi->a({-href => "$my_uri?a=opml", -class => "rss_logo"}, "OPML") . "\n";
}
print "</div>\n" .
"</body>\n" .
open my $fd, "-|", "$gitbin/git-cat-file -t $hash" or return;
my $type = <$fd>;
- close $fd;
+ close $fd or return;
chomp $type;
return $type;
}
sub git_read_tag {
my $tag_id = shift;
my %tag;
+ my @comment;
open my $fd, "-|", "$gitbin/git-cat-file tag $tag_id" or return;
+ $tag{'id'} = $tag_id;
while (my $line = <$fd>) {
chomp $line;
if ($line =~ m/^object ([0-9a-fA-F]{40})$/) {
$tag{'type'} = $1;
} elsif ($line =~ m/^tag (.+)$/) {
$tag{'name'} = $1;
+ } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
+ $tag{'author'} = $1;
+ $tag{'epoch'} = $2;
+ $tag{'tz'} = $3;
+ } elsif ($line =~ m/--BEGIN/) {
+ push @comment, $line;
+ last;
+ } elsif ($line eq "") {
+ last;
}
}
+ push @comment, <$fd>;
+ $tag{'comment'} = \@comment;
close $fd or return;
if (!defined $tag{'name'}) {
return
return %tag
}
+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;
+}
+
sub git_read_commit {
my $commit_id = shift;
my $commit_text = shift;
my $age = time - $co{'committer_epoch'};
$co{'age'} = $age;
- if ($age > 60*60*24*365*2) {
- $co{'age_string'} = (int $age/60/60/24/365);
- $co{'age_string'} .= " years ago";
- } elsif ($age > 60*60*24*(365/12)*2) {
- $co{'age_string'} = int $age/60/60/24/(365/12);
- $co{'age_string'} .= " months ago";
- } elsif ($age > 60*60*24*7*2) {
- $co{'age_string'} = int $age/60/60/24/7;
- $co{'age_string'} .= " weeks ago";
- } elsif ($age > 60*60*24*2) {
- $co{'age_string'} = int $age/60/60/24;
- $co{'age_string'} .= " days ago";
- } elsif ($age > 60*60*2) {
- $co{'age_string'} = int $age/60/60;
- $co{'age_string'} .= " hours ago";
- } elsif ($age > 60*2) {
- $co{'age_string'} = int $age/60;
- $co{'age_string'} .= " min ago";
- } elsif ($age > 2) {
- $co{'age_string'} = int $age;
- $co{'age_string'} .= " sec ago";
+ $co{'age_string'} = age_string($age);
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'});
+ if ($age > 60*60*24*7*2) {
+ $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
+ $co{'age_string_age'} = $co{'age_string'};
} else {
- $co{'age_string'} .= " right now";
+ $co{'age_string_date'} = $co{'age_string'};
+ $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
}
return %co;
}
}
}
+sub format_log_line_html {
+ my $line = shift;
+
+ $line = escapeHTML($line);
+ $line =~ s/ / /g;
+ if ($line =~ m/([0-9a-fA-F]{40})/) {
+ my $hash_text = $1;
+ if (git_get_type($hash_text) eq "commit") {
+ my $link = $cgi->a({-class => "text", -href => "$my_uri?p=$project;a=commit;h=$hash_text"}, $hash_text);
+ $line =~ s/$hash_text/$link/;
+ }
+ }
+ return $line;
+}
+
sub date_str {
my $epoch = shift;
my $tz = shift || "-0000";
return $owner;
}
-sub git_project_list {
+sub git_read_projects {
my @list;
if (-d $projects_list) {
}
close $fd;
}
+ @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
+ return @list;
+}
+sub git_project_list {
+ my @list = git_read_projects();
+ my @projects;
if (!@list) {
die_error(undef, "No project found.");
}
- @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
-
+ foreach my $pr (@list) {
+ my $head = git_read_hash("$pr->{'path'}/HEAD");
+ if (!defined $head) {
+ next;
+ }
+ $ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}";
+ my %co = git_read_commit($head);
+ if (!%co) {
+ next;
+ }
+ $pr->{'commit'} = \%co;
+ if (!defined $pr->{'descr'}) {
+ my $descr = git_read_description($pr->{'path'}) || "";
+ $pr->{'descr'} = chop_str($descr, 25, 5);
+ }
+ if (!defined $pr->{'owner'}) {
+ $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || "";
+ }
+ push @projects, $pr;
+ }
git_header_html();
if (-f $home_text) {
print "<div class=\"index_include\">\n";
print "</div>\n";
}
print "<table cellspacing=\"0\">\n" .
- "<tr>\n" .
- "<th>Project</th>\n" .
- "<th>Description</th>\n" .
- "<th>Owner</th>\n" .
- "<th>last change</th>\n" .
- "<th></th>\n" .
+ "<tr>\n";
+ if (!defined($order) || (defined($order) && ($order eq "project"))) {
+ @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
+ print "<th>Project</th>\n";
+ } else {
+ print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?o=project"}, "Project") . "</th>\n";
+ }
+ if (defined($order) && ($order eq "descr")) {
+ @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
+ print "<th>Description</th>\n";
+ } else {
+ print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?o=descr"}, "Description") . "</th>\n";
+ }
+ if (defined($order) && ($order eq "owner")) {
+ @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
+ print "<th>Owner</th>\n";
+ } else {
+ print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?o=owner"}, "Owner") . "</th>\n";
+ }
+ if (defined($order) && ($order eq "age")) {
+ @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects;
+ print "<th>Last Change</th>\n";
+ } else {
+ print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?o=age"}, "Last Change") . "</th>\n";
+ }
+ print "<th></th>\n" .
"</tr>\n";
my $alternate = 0;
- foreach my $pr (@list) {
- my %proj = %$pr;
- my $head = git_read_hash("$proj{'path'}/HEAD");
- if (!defined $head) {
- next;
- }
- $ENV{'GIT_OBJECT_DIRECTORY'} = "$projectroot/$proj{'path'}/objects";
- my %co = git_read_commit($head);
- if (!%co) {
- next;
- }
- my $descr = git_read_description($proj{'path'}) || "";
- $descr = chop_str($descr, 25, 5);
- # get directory owner if not already specified
- if (!defined $proj{'owner'}) {
- $proj{'owner'} = get_file_owner("$projectroot/$proj{'path'}") || "";
- }
+ foreach my $pr (@projects) {
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
- print "<td>" . $cgi->a({-href => "$my_uri?p=$proj{'path'};a=summary", -class => "list"}, escapeHTML($proj{'path'})) . "</td>\n" .
- "<td>$descr</td>\n" .
- "<td><i>" . chop_str($proj{'owner'}, 15) . "</i></td>\n";
+ print "<td>" . $cgi->a({-href => "$my_uri?p=$pr->{'path'};a=summary", -class => "list"}, escapeHTML($pr->{'path'})) . "</td>\n" .
+ "<td>$pr->{'descr'}</td>\n" .
+ "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
my $colored_age;
- if ($co{'age'} < 60*60*2) {
- $colored_age = "<span style =\"color: #009900;\"><b><i>$co{'age_string'}</i></b></span>";
- } elsif ($co{'age'} < 60*60*24*2) {
- $colored_age = "<span style =\"color: #009900;\"><i>$co{'age_string'}</i></span>";
+ if ($pr->{'commit'}{'age'} < 60*60*2) {
+ $colored_age = "<span style =\"color: #009900;\"><b><i>$pr->{'commit'}{'age_string'}</i></b></span>";
+ } elsif ($pr->{'commit'}{'age'} < 60*60*24*2) {
+ $colored_age = "<span style =\"color: #009900;\"><i>$pr->{'commit'}{'age_string'}</i></span>";
} else {
- $colored_age = "<i>$co{'age_string'}</i>";
+ $colored_age = "<i>$pr->{'commit'}{'age_string'}</i>";
}
print "<td>$colored_age</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$proj{'path'};a=summary"}, "summary") .
- " | " . $cgi->a({-href => "$my_uri?p=$proj{'path'};a=shortlog"}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?p=$proj{'path'};a=log"}, "log") .
+ $cgi->a({-href => "$my_uri?p=$pr->{'path'};a=summary"}, "summary") .
+ " | " . $cgi->a({-href => "$my_uri?p=$pr->{'path'};a=shortlog"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$pr->{'path'};a=log"}, "log") .
"</td>\n" .
"</tr>\n";
}
my $ref_dir = shift;
my @reflist;
+ my @refs;
opendir my $dh, "$projectroot/$project/$ref_dir";
- my @refs = grep !m/^\./, readdir $dh;
+ while (my $dir = readdir($dh)) {
+ if ($dir =~ m/^\./) {
+ next;
+ }
+ if (-d "$projectroot/$project/$ref_dir/$dir") {
+ opendir my $dh2, "$projectroot/$project/$ref_dir/$dir";
+ my @subdirs = grep !m/^\./, readdir $dh2;
+ closedir($dh2);
+ foreach my $subdir (@subdirs) {
+ push @refs, "$dir/$subdir"
+ }
+ next;
+ }
+ push @refs, $dir;
+ }
closedir($dh);
foreach my $ref_file (@refs) {
my $ref_id = git_read_hash("$project/$ref_dir/$ref_file");
my $type = git_get_type($ref_id) || next;
my %ref_item;
my %co;
+ $ref_item{'type'} = $type;
+ $ref_item{'id'} = $ref_id;
+ $ref_item{'epoch'} = 0;
+ $ref_item{'age'} = "unknown";
if ($type eq "tag") {
my %tag = git_read_tag($ref_id);
+ $ref_item{'comment'} = $tag{'comment'};
if ($tag{'type'} eq "commit") {
%co = git_read_commit($tag{'object'});
+ $ref_item{'epoch'} = $co{'committer_epoch'};
+ $ref_item{'age'} = $co{'age_string'};
+ } elsif (defined($tag{'epoch'})) {
+ my $age = time - $tag{'epoch'};
+ $ref_item{'epoch'} = $tag{'epoch'};
+ $ref_item{'age'} = age_string($age);
}
- $ref_item{'type'} = $tag{'type'};
+ $ref_item{'reftype'} = $tag{'type'};
$ref_item{'name'} = $tag{'name'};
- $ref_item{'id'} = $tag{'object'};
+ $ref_item{'refid'} = $tag{'object'};
} elsif ($type eq "commit"){
%co = git_read_commit($ref_id);
- $ref_item{'type'} = "commit";
+ $ref_item{'reftype'} = "commit";
$ref_item{'name'} = $ref_file;
$ref_item{'title'} = $co{'title'};
- $ref_item{'id'} = $ref_id;
+ $ref_item{'refid'} = $ref_id;
+ $ref_item{'epoch'} = $co{'committer_epoch'};
+ $ref_item{'age'} = $co{'age_string'};
}
- $ref_item{'epoch'} = $co{'committer_epoch'} || 0;
- $ref_item{'age'} = $co{'age_string'} || "unknown";
push @reflist, \%ref_item;
}
my %co = git_read_commit($commit);
my %ad = date_str($co{'author_epoch'});
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
if ($i-- > 0) {
my $alternate = 0;
foreach my $entry (@$taglist) {
my %tag = %$entry;
+ my $comment_lines = $tag{'comment'};
+ my $comment = shift @$comment_lines;
+ if (defined($comment)) {
+ $comment = chop_str($comment, 30, 5);
+ }
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
if ($i-- > 0) {
print "<td><i>$tag{'age'}</i></td>\n" .
"<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=$tag{'type'};h=$tag{'id'}", -class => "list"}, "<b>" .
- escapeHTML($tag{'name'}) . "</b>") .
+ $cgi->a({-href => "$my_uri?p=$project;a=$tag{'reftype'};h=$tag{'refid'}", -class => "list"},
+ "<b>" . escapeHTML($tag{'name'}) . "</b>") .
"</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=$tag{'type'};h=$tag{'id'}"}, $tag{'type'});
- if ($tag{'type'} eq "commit") {
- print " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'id'}"}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}"}, "log");
+ "<td>";
+ if (defined($comment)) {
+ print $cgi->a({-class => "list", -href => "$my_uri?p=$project;a=tag;h=$tag{'id'}"}, $comment);
+ }
+ print "</td>\n" .
+ "<td class=\"link\">";
+ if ($tag{'type'} eq "tag") {
+ print $cgi->a({-href => "$my_uri?p=$project;a=tag;h=$tag{'id'}"}, "tag") . " | ";
+ }
+ print $cgi->a({-href => "$my_uri?p=$project;a=$tag{'reftype'};h=$tag{'refid'}"}, $tag{'reftype'});
+ if ($tag{'reftype'} eq "commit") {
+ print " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'refid'}"}, "log");
}
print "</td>\n" .
"</tr>";
foreach my $entry (@$branchlist) {
my %tag = %$entry;
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
if ($i-- > 0) {
print "<td><i>$tag{'age'}</i></td>\n" .
"<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'id'}", -class => "list"},
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}", -class => "list"},
"<b>" . escapeHTML($tag{'name'}) . "</b>") .
"</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'id'}"}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}"}, "log") .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'name'}"}, "log") .
"</td>\n" .
"</tr>";
} else {
git_footer_html();
}
+sub git_tag {
+ my $head = git_read_hash("$project/HEAD");
+ git_header_html();
+ print "<div class=\"page_nav\">\n" .
+ $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$head"}, "commit") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$head"}, "commitdiff") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;hb=$head"}, "tree") . "<br/>\n" .
+ "<br/>\n" .
+ "</div>\n";
+ my %tag = git_read_tag($hash);
+ print "<div>\n" .
+ $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($tag{'name'})) . "\n" .
+ "</div>\n";
+ print "<div class=\"title_text\">\n" .
+ "<table cellspacing=\"0\">\n" .
+ "<tr>\n" .
+ "<td>object</td>\n" .
+ "<td>" . $cgi->a({-class => "list", -href => "$my_uri?p=$project;a=$tag{'type'};h=$tag{'object'}"}, $tag{'object'}) . "</td>\n" .
+ "<td class=\"link\">" . $cgi->a({-href => "$my_uri?p=$project;a=$tag{'type'};h=$tag{'object'}"}, $tag{'type'}) . "</td>\n" .
+ "</tr>\n";
+ if (defined($tag{'author'})) {
+ my %ad = date_str($tag{'epoch'}, $tag{'tz'});
+ print "<tr><td>author</td><td>" . escapeHTML($tag{'author'}) . "</td></tr>\n";
+ print "<tr><td></td><td>" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "</td></tr>\n";
+ }
+ print "</table>\n\n" .
+ "</div>\n";
+ print "<div class=\"page_body\">";
+ my $comment = $tag{'comment'};
+ foreach my $line (@$comment) {
+ print escapeHTML($line) . "<br/>\n";
+ }
+ print "</div>\n";
+ git_footer_html();
+}
+
sub git_tags {
my $head = git_read_hash("$project/HEAD");
git_header_html();
if (defined @$taglist) {
foreach my $entry (@$taglist) {
my %tag = %$entry;
+ my $comment_lines = $tag{'comment'};
+ my $comment = shift @$comment_lines;
+ if (defined($comment)) {
+ $comment = chop_str($comment, 30, 5);
+ }
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
print "<td><i>$tag{'age'}</i></td>\n" .
"<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}", -class => "list"},
+ $cgi->a({-href => "$my_uri?p=$project;a=$tag{'reftype'};h=$tag{'refid'}", -class => "list"},
"<b>" . escapeHTML($tag{'name'}) . "</b>") .
"</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=$tag{'type'};h=$tag{'id'}"}, $tag{'type'});
- if ($tag{'type'} eq "commit") {
- print " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'id'}"}, "shortlog") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}"}, "log");
+ "<td>";
+ if (defined($comment)) {
+ print $cgi->a({-class => "list", -href => "$my_uri?p=$project;a=tag;h=$tag{'id'}"}, $comment);
+ }
+ print "</td>\n" .
+ "<td class=\"link\">";
+ if ($tag{'type'} eq "tag") {
+ print $cgi->a({-href => "$my_uri?p=$project;a=tag;h=$tag{'id'}"}, "tag") . " | ";
+ }
+ print $cgi->a({-href => "$my_uri?p=$project;a=$tag{'reftype'};h=$tag{'refid'}"}, $tag{'reftype'});
+ if ($tag{'reftype'} eq "commit") {
+ print " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'refid'}"}, "log");
}
print "</td>\n" .
"</tr>";
foreach my $entry (@$taglist) {
my %tag = %$entry;
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
print "<td><i>$tag{'age'}</i></td>\n" .
"<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}", -class => "list"}, "<b>" . escapeHTML($tag{'name'}) . "</b>") .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}", -class => "list"}, "<b>" . escapeHTML($tag{'name'}) . "</b>") .
"</td>\n" .
"<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'id'}"}, "shortog") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'id'}"}, "log") .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$tag{'name'}"}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$tag{'name'}"}, "log") .
"</td>\n" .
"</tr>";
}
$line =~ s/\t/$spaces/;
}
}
- printf "<div class=\"pre\"><span style=\"color:#999999;\">%4i</span> %s</div>\n", $nr, escapeHTML($line);
+ printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, escapeHTML($line);
}
close $fd or print "Reading blob failed.\n";
print "</div>";
my $t_name = $4;
$file_key = ";f=$base$t_name";
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
print "<td style=\"font-family:monospace\">" . mode_str($t_mode) . "</td>\n";
if ($t_type eq "blob") {
print "<td class=\"list\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$t_hash" . $base_key . $file_key, -class => "list"}, $t_name) .
- "</td>\n";
- print "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$t_hash" . $base_key . $file_key, -class => "list"}, $t_name) .
+ "</td>\n" .
+ "<td class=\"link\">" .
$cgi->a({-href => "$my_uri?p=$project;a=blob;h=$t_hash" . $base_key . $file_key}, "blob") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=history;h=$hash_base" . $file_key}, "history") .
"</td>\n";
print "<td class=\"list\">" .
$cgi->a({-href => "$my_uri?p=$project;a=tree;h=$t_hash" . $base_key . $file_key}, $t_name) .
"</td>\n" .
- "<td></td>\n";
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$t_hash" . $base_key . $file_key}, "tree") .
+ "</td>\n";
}
print "</tr>\n";
}
sub git_rss {
# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
- open my $fd, "-|", "$gitbin/git-rev-list --max-count=20 " . git_read_hash("$project/HEAD") or die_error(undef, "Open failed.");
+ open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_hash("$project/HEAD") or die_error(undef, "Open failed.");
my (@revlist) = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading rev-list failed.");
-
print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
"<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n";
print "<channel>\n";
print "<title>$project</title>\n".
- "<link>" . escapeHTML("$my_url/$project/log") . "</link>\n".
+ "<link>" . escapeHTML("$my_url?p=$project;a=summary") . "</link>\n".
"<description>$project log</description>\n".
"<language>en</language>\n";
- foreach my $commit (@revlist) {
+ for (my $i = 0; $i <= $#revlist; $i++) {
+ my $commit = $revlist[$i];
my %co = git_read_commit($commit);
+ # we read 150, we always show 30 and the ones more recent than 48 hours
+ if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
+ last;
+ }
my %cd = date_str($co{'committer_epoch'});
+ open $fd, "-|", "$gitbin/git-diff-tree -r $co{'parent'} $co{'id'}" or next;
+ my @difftree = map { chomp; $_ } <$fd>;
+ close $fd or next;
print "<item>\n" .
"<title>" .
sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . escapeHTML($co{'title'}) .
"</title>\n" .
+ "<author>" . escapeHTML($co{'author'}) . "</author>\n" .
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
+ "<guid isPermaLink=\"true\">" . escapeHTML("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
"<link>" . escapeHTML("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
"<description>" . escapeHTML($co{'title'}) . "</description>\n" .
"<content:encoded>" .
foreach my $line (@$comment) {
print "$line<br/>\n";
}
+ print "<br/>\n";
+ foreach my $line (@difftree) {
+ if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
+ next;
+ }
+ my $file = $7;
+ print "$file<br/>\n";
+ }
print "]]>\n" .
"</content:encoded>\n" .
"</item>\n";
print "</channel></rss>";
}
+sub git_opml {
+ my @list = git_read_projects();
+
+ print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
+ print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
+ "<opml version=\"1.0\">\n".
+ "<head>".
+ " <title>Git OPML Export</title>\n".
+ "</head>\n".
+ "<body>\n".
+ "<outline text=\"git RSS feeds\">\n";
+
+ foreach my $pr (@list) {
+ my %proj = %$pr;
+ my $head = git_read_hash("$proj{'path'}/HEAD");
+ if (!defined $head) {
+ next;
+ }
+ $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}";
+ my %co = git_read_commit($head);
+ if (!%co) {
+ next;
+ }
+
+ my $path = escapeHTML(chop_str($proj{'path'}, 25, 5));
+ my $rss = "$my_url?p=$proj{'path'};a=rss";
+ my $html = "$my_url?p=$proj{'path'};a=summary";
+ print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
+ }
+ print "</outline>\n".
+ "</body>\n".
+ "</opml>\n";
+}
+
sub git_log {
my $head = git_read_hash("$project/HEAD");
if (!defined $hash) {
$hash = $head;
}
- my $limit_option = "";
- if (!defined $time_back) {
- $limit_option = "--max-count=100";
- } elsif ($time_back > 0) {
- my $date = time - $time_back*24*60*60;
- $limit_option = "--max-age=$date";
+ if (!defined $page) {
+ $page = 0;
}
- open my $fd, "-|", "$gitbin/git-rev-list $limit_option $hash" or die_error(undef, "Open failed.");
- my (@revlist) = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading rev-list failed.");
-
git_header_html();
print "<div class=\"page_nav\">\n";
print $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$hash;hb=$hash"}, "tree") . "<br/>\n";
- if ($hash ne $head) {
- print $cgi->a({-href => "$my_uri?p=$project;a=log"}, "HEAD") . " ⋅ ";
- }
- print $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash"}, "100") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=log;t=1;h=$hash"}, "day") .
- " ⋅ " .$cgi->a({-href => "$my_uri?p=$project;a=log;t=7;h=$hash"}, "week") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=log;t=31;h=$hash"}, "month") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=log;t=365;h=$hash"}, "year") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=log;t=0;h=$hash"}, "all");
+
+ my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
+ open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
+ my (@revlist) = map { chomp; $_ } <$fd>;
+ close $fd;
+
+ if ($hash ne $head || $page) {
+ print $cgi->a({-href => "$my_uri?p=$project;a=log"}, "HEAD");
+ } else {
+ print "HEAD";
+ }
+ if ($page > 0) {
+ print " ⋅ " .
+ $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash;pg=" . ($page-1), -accesskey => "p", -title => "Alt-p"}, "prev");
+ } else {
+ print " ⋅ prev";
+ }
+ if ($#revlist >= (100 * ($page+1)-1)) {
+ print " ⋅ " .
+ $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash;pg=" . ($page+1), -accesskey => "n", -title => "Alt-n"}, "next");
+ } else {
+ print " ⋅ next";
+ }
print "<br/>\n" .
"</div>\n";
if (!@revlist) {
my %co = git_read_commit($hash);
print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
}
- foreach my $commit (@revlist) {
+ for (my $i = ($page * 100); $i <= $#revlist; $i++) {
+ my $commit = $revlist[$i];
my %co = git_read_commit($commit);
next if !%co;
my %ad = date_str($co{'author_epoch'});
} else {
$empty = 0;
}
- print escapeHTML($line) . "<br/>\n";
+ print format_log_line_html($line) . "<br/>\n";
}
if (!$empty) {
print "<br/>\n";
my @difftree;
my $root = "";
- if (!defined $co{'parent'}) {
+ my $parent = $co{'parent'};
+ if (!defined $parent) {
$root = " --root";
+ $parent = "";
}
- open my $fd, "-|", "$gitbin/git-diff-tree -r $root $co{'parent'} $hash" or die_error(undef, "Open failed.");
+ open my $fd, "-|", "$gitbin/git-diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
@difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading diff-tree failed.");
git_header_html();
"<td style=\"font-family:monospace\">" . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$par", class => "list"}, $par) . "</td>" .
"<td class=\"link\">" .
$cgi->a({-href => "$my_uri?p=$project;a=commit;h=$par"}, "commit") .
- " |" . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash;hp=$par"}, "commitdiff") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash;hp=$par"}, "commitdiff") .
"</td>" .
"</tr>\n";
}
print "<span style=\"color: #888888\">" . escapeHTML($line) . "</span><br/>\n";
} else {
$signed = 0;
- print escapeHTML($line) . "<br/>\n";
+ print format_log_line_html($line) . "<br/>\n";
}
}
print "</div>\n";
foreach my $line (@difftree) {
# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
- $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
+ if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
+ next;
+ }
my $from_mode = $1;
my $to_mode = $2;
my $from_id = $3;
my $to_id = $4;
my $status = $5;
- my $file = $6;
- #print "$line ($status)<br/>\n";
+ my $similarity = $6;
+ my $file = $7;
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
- if ($status eq "N") {
+ if ($status eq "A") {
my $mode_chng = "";
if (S_ISREG(oct $to_mode)) {
$mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777);
}
print " | " . $cgi->a({-href => "$my_uri?p=$project;a=history;h=$hash;f=$file"}, "history") . "\n";
print "</td>\n";
+ } elsif ($status eq "R") {
+ my ($from_file, $to_file) = split "\t", $file;
+ my $mode_chng = "";
+ if ($from_mode != $to_mode) {
+ $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777);
+ }
+ print "<td>" .
+ $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file", -class => "list"}, escapeHTML($to_file)) . "</td>\n" .
+ "<td><span style=\"color: #777777;\">[moved from " .
+ $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file", -class => "list"}, escapeHTML($from_file)) .
+ " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"}, "blob");
+ if ($to_id ne $from_id) {
+ print " | " . $cgi->a({-href => "$my_uri?p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file"}, "diff");
+ }
+ print "</td>\n";
}
print "</tr>\n";
}
} else {
$empty = 0;
}
- print escapeHTML($line) . "<br/>\n";
+ print format_log_line_html($line) . "<br/>\n";
}
print "<br/>\n";
foreach my $line (@difftree) {
my $to_id = $4;
my $status = $5;
my $file = $6;
- if ($status eq "N") {
+ if ($status eq "A") {
print "<div class=\"diff_info\">" . file_type($to_mode) . ":" .
$cgi->a({-href => "$my_uri?p=$project;a=blob;h=$to_id;hb=$hash;f=$file"}, $to_id) . "(new)" .
"</div>\n";
my (@difftree) = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading diff-tree failed.");
+ # try to figure out the next tag after this commit
+ my $tagname;
+ my %taghash;
+ my $tags = git_read_refs("refs/tags");
+ foreach my $entry (@$tags) {
+ my %tag = %$entry;
+ $taghash{$tag{'refid'}} = $tag{'name'};
+ }
+ open $fd, "-|", "$gitbin/git-rev-list HEAD";
+ while (my $commit = <$fd>) {
+ chomp $commit;
+ if ($taghash{$commit}) {
+ $tagname = $taghash{$commit};
+ }
+ if ($commit eq $hash) {
+ last;
+ }
+ }
+ close $fd;
+
print $cgi->header(-type => "text/plain", -charset => 'utf-8');
+ my %co = git_read_commit($hash);
+ my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
+ my $comment = $co{'comment'};
+ print "From: $co{'author'}\n" .
+ "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n".
+ "Subject: $co{'title'}\n";
+ if (defined $tagname) {
+ print "X-Git-Tag: $tagname\n";
+ }
+ print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" .
+ "\n";
+
+ foreach my $line (@$comment) {;
+ print " $line\n";
+ }
+ print "---\n\n";
+
foreach my $line (@difftree) {
$line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
my $from_id = $3;
my $to_id = $4;
my $status = $5;
my $file = $6;
- if ($status eq "N") {
+ if ($status eq "A") {
git_diff_print(undef, "/dev/null", $to_id, "b/$file", "plain");
} elsif ($status eq "D") {
git_diff_print($from_id, "a/$file", undef, "/dev/null", "plain");
print "<table cellspacing=\"0\">\n";
my $alternate = 0;
while (my $line = <$fd>) {
- if ($line =~ m/^([0-9a-fA-F]{40}) /){
+ if ($line =~ m/^([0-9a-fA-F]{40})/){
$commit = $1;
next;
}
next;
}
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
- print "<td><i>$co{'age_string'}</i></td>\n" .
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
"<td><i>" . escapeHTML(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
"<td>" . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"}, "<b>" .
escapeHTML(chop_str($co{'title'}, 50)) . "</b>") . "</td>\n" .
my $blob = git_get_hash_by_path($hash, $file_name);
my $blob_parent = git_get_hash_by_path($commit, $file_name);
if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
- print " | " . $cgi->a({-href => "$my_uri?p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name"}, "diff to current");
+ print " | " .
+ $cgi->a({-href => "$my_uri?p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name"},
+ "diff to current");
}
print "</td>\n" .
"</tr>\n";
if (!%co) {
die_error(undef, "Unknown commit object.");
}
+ # pickaxe may take all resources of your box and run for several minutes
+ # with every query - so decide by yourself how public you make this feature :)
+ my $commit_search = 1;
+ my $author_search = 0;
+ my $committer_search = 0;
+ my $pickaxe_search = 0;
+ if ($searchtext =~ s/^author\\://i) {
+ $author_search = 1;
+ } elsif ($searchtext =~ s/^committer\\://i) {
+ $committer_search = 1;
+ } elsif ($searchtext =~ s/^pickaxe\\://i) {
+ $commit_search = 0;
+ $pickaxe_search = 1;
+ }
git_header_html();
print "<div class=\"page_nav\">\n" .
$cgi->a({-href => "$my_uri?p=$project;a=summary;h=$hash"}, "summary") .
$cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
"</div>\n";
print "<table cellspacing=\"0\">\n";
- $/ = "\0";
- open my $fd, "-|", "$gitbin/git-rev-list --header $hash";
my $alternate = 0;
- while (my $commit_text = <$fd>) {
- if (!grep m/$searchtext/, $commit_text) {
- next;
- }
- my @commit_lines = split "\n", $commit_text;
- my $commit = shift @commit_lines;
- my %co = git_read_commit($commit, \@commit_lines);
- if (!%co) {
- next;
- }
- if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
- } else {
- print "<tr>\n";
- }
- $alternate ^= 1;
- print "<td><i>$co{'age_string'}</i></td>\n" .
- "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"}, "<b>" . escapeHTML(chop_str($co{'title'}, 50)) . "</b><br/>");
- my $comment = $co{'comment'};
- foreach my $line (@$comment) {
- if ($line =~ m/^(.*)($searchtext)(.*)$/) {
- my $lead = escapeHTML($1) || "";
- $lead = chop_str($lead, 30, 10);
- my $match = escapeHTML($2) || "";
- my $trail = escapeHTML($3) || "";
- $trail = chop_str($trail, 30, 10);
- my $text = "$lead<span style=\"color:#e00000\">$match</span>$trail";
- print chop_str($text, 80, 5) . "<br/>\n";
+ if ($commit_search) {
+ $/ = "\0";
+ open my $fd, "-|", "$gitbin/git-rev-list --header $hash";
+ while (my $commit_text = <$fd>) {
+ if (!grep m/$searchtext/i, $commit_text) {
+ next;
}
- }
- print "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit"}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$commit"}, "tree");
- print "</td>\n" .
- "</tr>\n";
- }
- close $fd;
-
- $/ = "\n";
- open $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -S$searchtext";
- undef %co;
- my @files;
- while (my $line = <$fd>) {
- if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
- my %set;
- $set{'file'} = $6;
- $set{'from_id'} = $3;
- $set{'to_id'} = $4;
- $set{'id'} = $set{'to_id'};
- if ($set{'id'} =~ m/0{40}/) {
- $set{'id'} = $set{'from_id'};
+ if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) {
+ next;
}
- if ($set{'id'} =~ m/0{40}/) {
+ if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
next;
}
- push @files, \%set;
- } elsif ($line =~ m/^([0-9a-fA-F]{40}) /){
- if (%co) {
- if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
- } else {
- print "<tr>\n";
+ my @commit_lines = split "\n", $commit_text;
+ my $commit = shift @commit_lines;
+ my %co = git_read_commit($commit, \@commit_lines);
+ if (!%co) {
+ next;
+ }
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"}, "<b>" . escapeHTML(chop_str($co{'title'}, 50)) . "</b><br/>");
+ my $comment = $co{'comment'};
+ foreach my $line (@$comment) {
+ if ($line =~ m/^(.*)($searchtext)(.*)$/i) {
+ my $lead = escapeHTML($1) || "";
+ $lead = chop_str($lead, 30, 10);
+ my $match = escapeHTML($2) || "";
+ my $trail = escapeHTML($3) || "";
+ $trail = chop_str($trail, 30, 10);
+ my $text = "$lead<span style=\"color:#e00000\">$match</span>$trail";
+ print chop_str($text, 80, 5) . "<br/>\n";
}
- $alternate ^= 1;
- print "<td><i>$co{'age_string'}</i></td>\n" .
- "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
- "<td>" .
- $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$co{'id'}", -class => "list"}, "<b>" .
- escapeHTML(chop_str($co{'title'}, 50)) . "</b><br/>");
- while (my $setref = shift @files) {
- my %set = %$setref;
- print $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}", class => "list"},
- escapeHTML($set{'file'})) . "<br/>\n";
+ }
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit"}, "commit") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$commit"}, "tree");
+ print "</td>\n" .
+ "</tr>\n";
+ }
+ close $fd;
+ }
+
+ if ($pickaxe_search) {
+ $/ = "\n";
+ open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -S$searchtext";
+ undef %co;
+ my @files;
+ while (my $line = <$fd>) {
+ if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
+ my %set;
+ $set{'file'} = $6;
+ $set{'from_id'} = $3;
+ $set{'to_id'} = $4;
+ $set{'id'} = $set{'to_id'};
+ if ($set{'id'} =~ m/0{40}/) {
+ $set{'id'} = $set{'from_id'};
}
- print "</td>\n" .
- "<td class=\"link\">" .
- $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$co{'id'}"}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}"}, "tree");
- print "</td>\n" .
- "</tr>\n";
+ if ($set{'id'} =~ m/0{40}/) {
+ next;
+ }
+ push @files, \%set;
+ } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
+ if (%co) {
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$co{'id'}", -class => "list"}, "<b>" .
+ escapeHTML(chop_str($co{'title'}, 50)) . "</b><br/>");
+ while (my $setref = shift @files) {
+ my %set = %$setref;
+ print $cgi->a({-href => "$my_uri?p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}", class => "list"},
+ "<span style=\"color:#e00000\">" . escapeHTML($set{'file'}) . "</span>") .
+ "<br/>\n";
+ }
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$co{'id'}"}, "commit") .
+ " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}"}, "tree");
+ print "</td>\n" .
+ "</tr>\n";
+ }
+ %co = git_read_commit($1);
}
- %co = git_read_commit($1);
}
+ close $fd;
}
print "</table>\n";
- close $fd;
git_footer_html();
}
if (!defined $hash) {
$hash = $head;
}
-
+ if (!defined $page) {
+ $page = 0;
+ }
git_header_html();
print "<div class=\"page_nav\">\n" .
$cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
" | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$hash;hb=$hash"}, "tree") . "<br/>\n";
- if ($hash ne $head) {
- print $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "HEAD") . " ⋅ ";
- }
- print $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash"}, "100") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;t=1;h=$hash"}, "day") .
- " ⋅ " .$cgi->a({-href => "$my_uri?p=$project;a=shortlog;t=7;h=$hash"}, "week") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;t=31;h=$hash"}, "month") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;t=365;h=$hash"}, "year") .
- " ⋅ " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;t=0;h=$hash"}, "all") .
- "<br/>\n" .
- "</div>\n";
- my $limit = "";
- if (defined $time_back) {
- if ($time_back) {
- $limit = sprintf(" --max-age=%i", time - 60*60*24*$time_back);
- }
- } else {
- $limit = " --max-count=100";
- }
+
+ my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
my (@revlist) = map { chomp; $_ } <$fd>;
close $fd;
+
+ if ($hash ne $head || $page) {
+ print $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "HEAD");
+ } else {
+ print "HEAD";
+ }
+ if ($page > 0) {
+ print " ⋅ " .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash;pg=" . ($page-1), -accesskey => "p", -title => "Alt-p"}, "prev");
+ } else {
+ print " ⋅ prev";
+ }
+ if ($#revlist >= (100 * ($page+1)-1)) {
+ print " ⋅ " .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash;pg=" . ($page+1), -accesskey => "n", -title => "Alt-n"}, "next");
+ } else {
+ print " ⋅ next";
+ }
+ print "<br/>\n" .
+ "</div>\n";
print "<div>\n" .
$cgi->a({-href => "$my_uri?p=$project;a=summary", -class => "title"}, " ") .
"</div>\n";
print "<table cellspacing=\"0\">\n";
- if (!@revlist) {
- my %co = git_read_commit($hash);
- print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
- }
my $alternate = 0;
- foreach my $commit (@revlist) {
+ for (my $i = ($page * 100); $i <= $#revlist; $i++) {
+ my $commit = $revlist[$i];
my %co = git_read_commit($commit);
my %ad = date_str($co{'author_epoch'});
if ($alternate) {
- print "<tr style=\"background-color:#f6f5ed\">\n";
+ print "<tr class=\"dark\">\n";
} else {
- print "<tr>\n";
+ print "<tr class=\"light\">\n";
}
$alternate ^= 1;
- print "<td><i>$co{'age_string'}</i></td>\n" .
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
"<td><i>" . escapeHTML(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
"<td>" . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"}, "<b>" .
escapeHTML($co{'title_short'}) . "</b>") . "</td>\n" .
"</td>\n" .
"</tr>";
}
+ if ($#revlist >= (100 * ($page+1)-1)) {
+ print "<tr>\n" .
+ "<td>" .
+ $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash;pg=" . ($page+1), -title => "Alt-n"}, "next") .
+ "</td>\n" .
+ "</tr>\n";
+ }
print "</table\n>";
git_footer_html();
}