+ 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{'reftype'} = $tag{'type'};
+ $ref_item{'name'} = $tag{'name'};
+ $ref_item{'refid'} = $tag{'object'};
+ } elsif ($type eq "commit"){
+ %co = git_read_commit($ref_id);
+ $ref_item{'reftype'} = "commit";
+ $ref_item{'name'} = $ref_file;
+ $ref_item{'title'} = $co{'title'};
+ $ref_item{'refid'} = $ref_id;
+ $ref_item{'epoch'} = $co{'committer_epoch'};
+ $ref_item{'age'} = $co{'age_string'};
+ } else {
+ $ref_item{'reftype'} = $type;
+ $ref_item{'name'} = $ref_file;
+ $ref_item{'refid'} = $ref_id;
+ }
+
+ push @reflist, \%ref_item;
+ }
+ # sort tags by age
+ @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
+ return \@reflist;
+}
+
+sub git_shortlog_body {
+ # uses global variable $project
+ my ($revlist, $from, $to, $refs, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
+
+ print "<table class=\"shortlog\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $commit = $revlist->[$i];
+ #my $ref = defined $refs ? git_get_referencing($refs, $commit) : '';
+ my $ref = git_get_referencing($refs, $commit);
+ my %co = git_read_commit($commit);
+ my %ad = date_str($co{'author_epoch'});
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ # git_summary() used 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>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
+ "<td>";
+ if (length($co{'title_short'}) < length($co{'title'})) {
+ print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"),
+ -class => "list", -title => "$co{'title'}"},
+ "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
+ } else {
+ print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"),
+ -class => "list"},
+ "<b>" . esc_html($co{'title'}) . "$ref</b>");
+ }
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
+ "</td>\n" .
+ "</tr>\n";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"4\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_tags_body {
+ # uses global variable $project
+ my ($taglist, $from, $to, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+ print "<table class=\"tags\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $entry = $taglist->[$i];
+ my %tag = %$entry;
+ my $comment_lines = $tag{'comment'};
+ my $comment = shift @$comment_lines;
+ my $comment_short;
+ if (defined $comment) {
+ $comment_short = chop_str($comment, 30, 5);
+ }
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td><i>$tag{'age'}</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"),
+ -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+ "</td>\n" .
+ "<td>";
+ if (defined $comment) {
+ if (length($comment_short) < length($comment)) {
+ print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"),
+ -class => "list", -title => $comment}, $comment_short);
+ } else {
+ print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"),
+ -class => "list"}, $comment);
+ }
+ }
+ print "</td>\n" .
+ "<td class=\"selflink\">";
+ if ($tag{'type'} eq "tag") {
+ print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag");
+ } else {
+ print " ";
+ }
+ print "</td>\n" .
+ "<td class=\"link\">" . " | " .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
+ if ($tag{'reftype'} eq "commit") {
+ print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+ } elsif ($tag{'reftype'} eq "blob") {
+ print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw");
+ }
+ print "</td>\n" .
+ "</tr>";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"5\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_heads_body {
+ # uses global variable $project
+ my ($taglist, $head, $from, $to, $extra) = @_;
+ $from = 0 unless defined $from;
+ $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+ print "<table class=\"heads\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $entry = $taglist->[$i];
+ my %tag = %$entry;
+ my $curr = $tag{'id'} eq $head;
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ print "<td><i>$tag{'age'}</i></td>\n" .
+ ($tag{'id'} eq $head ? "<td class=\"current_head\">" : "<td>") .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"),
+ -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+ "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
+ "</td>\n" .
+ "</tr>";
+ }
+ if (defined $extra) {
+ print "<tr>\n" .
+ "<td colspan=\"3\">$extra</td>\n" .
+ "</tr>\n";
+ }
+ print "</table>\n";
+}
+
+sub git_summary {
+ my $descr = git_read_description($project) || "none";
+ my $head = git_read_head($project);
+ my %co = git_read_commit($head);
+ my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});
+
+ my $owner;
+ if (-f $projects_list) {
+ open (my $fd , $projects_list);
+ while (my $line = <$fd>) {
+ chomp $line;
+ my ($pr, $ow) = split ' ', $line;
+ $pr = unescape($pr);
+ $ow = unescape($ow);
+ if ($pr eq $project) {
+ $owner = decode("utf8", $ow, Encode::FB_DEFAULT);
+ last;
+ }
+ }
+ close $fd;
+ }
+ if (!defined $owner) {
+ $owner = get_file_owner("$projectroot/$project");
+ }
+
+ my $refs = read_info_ref();
+ git_header_html();
+ git_page_nav('summary','', $head);
+
+ print "<div class=\"title\"> </div>\n";
+ print "<table cellspacing=\"0\">\n" .
+ "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
+ "<tr><td>owner</td><td>$owner</td></tr>\n" .
+ "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
+ "</table>\n";
+
+ open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project)
+ or die_error(undef, "Open git-rev-list failed.");
+ my @revlist = map { chomp; $_ } <$fd>;
+ close $fd;
+ git_header_div('shortlog');
+ git_shortlog_body(\@revlist, 0, 15, $refs,
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "..."));
+
+ my $taglist = git_read_refs("refs/tags");
+ if (defined @$taglist) {
+ git_header_div('tags');
+ git_tags_body($taglist, 0, 15,
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "..."));
+ }
+
+ my $headlist = git_read_refs("refs/heads");
+ if (defined @$headlist) {
+ git_header_div('heads');
+ git_heads_body($taglist, $head, 0, 15,
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "..."));
+ }
+
+ git_footer_html();
+}
+
+sub git_print_page_path {
+ my $name = shift;
+ my $type = shift;
+
+ if (!defined $name) {
+ print "<div class=\"page_path\"><b>/</b></div>\n";
+ } elsif ($type =~ "blob") {
+ print "<div class=\"page_path\"><b>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "</b><br/></div>\n";
+ } else {
+ print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
+ }
+}
+
+sub git_tag {
+ my $head = git_read_head($project);
+ git_header_html();
+ git_page_nav('','', $head,undef,$head);
+ my %tag = git_read_tag($hash);
+ git_header_div('commit', esc_html($tag{'name'}), $hash);
+ print "<div class=\"title_text\">\n" .
+ "<table cellspacing=\"0\">\n" .
+ "<tr>\n" .
+ "<td>object</td>\n" .
+ "<td>" . $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'object'}) . "</td>\n" .
+ "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("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>" . esc_html($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 esc_html($line) . "<br/>\n";
+ }
+ print "</div>\n";
+ git_footer_html();
+}
+
+sub git_blame2 {
+ my $fd;
+ my $ftype;
+ die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame'));
+ die_error('404 Not Found', "File name not defined") if (!$file_name);
+ $hash_base ||= git_read_head($project);
+ die_error(undef, "Reading commit failed") unless ($hash_base);
+ my %co = git_read_commit($hash_base)
+ or die_error(undef, "Reading commit failed");
+ if (!defined $hash) {
+ $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
+ or die_error(undef, "Error looking up file");
+ }
+ $ftype = git_get_type($hash);
+ if ($ftype !~ "blob") {
+ die_error("400 Bad Request", "object is not a blob");
+ }
+ open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base)
+ or die_error(undef, "Open git-blame failed.");
+ git_header_html();
+ my $formats_nav =
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head");
+ git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+ git_header_div('commit', esc_html($co{'title'}), $hash_base);
+ git_print_page_path($file_name, $ftype);
+ my @rev_color = (qw(light dark));
+ my $num_colors = scalar(@rev_color);
+ my $current_color = 0;
+ my $last_rev;
+ print "<div class=\"page_body\">\n";
+ print "<table class=\"blame\">\n";
+ print "<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n";
+ while (<$fd>) {
+ /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/;
+ my $full_rev = $1;
+ my $rev = substr($full_rev, 0, 8);
+ my $lineno = $2;
+ my $data = $3;
+
+ if (!defined $last_rev) {
+ $last_rev = $full_rev;
+ } elsif ($last_rev ne $full_rev) {
+ $last_rev = $full_rev;
+ $current_color = ++$current_color % $num_colors;
+ }
+ print "<tr class=\"$rev_color[$current_color]\">\n";
+ print "<td class=\"sha1\">" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "</td>\n";
+ print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" . esc_html($lineno) . "</a></td>\n";
+ print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+ print "</tr>\n";
+ }
+ print "</table>\n";
+ print "</div>";
+ close $fd or print "Reading blob failed\n";
+ git_footer_html();
+}
+
+sub git_blame {
+ my $fd;
+ die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame'));
+ die_error('404 Not Found', "What file will it be, master?") if (!$file_name);
+ $hash_base ||= git_read_head($project);
+ die_error(undef, "Reading commit failed.") unless ($hash_base);
+ my %co = git_read_commit($hash_base)
+ or die_error(undef, "Reading commit failed.");
+ if (!defined $hash) {
+ $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
+ or die_error(undef, "Error lookup file.");
+ }
+ open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base)
+ or die_error(undef, "Open git-annotate failed.");
+ git_header_html();
+ my $formats_nav =
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head");
+ git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+ git_header_div('commit', esc_html($co{'title'}), $hash_base);
+ git_print_page_path($file_name);
+ print "<div class=\"page_body\">\n";
+ print <<HTML;
+<table class="blame">
+ <tr>
+ <th>Commit</th>
+ <th>Age</th>
+ <th>Author</th>
+ <th>Line</th>
+ <th>Data</th>
+ </tr>
+HTML
+ my @line_class = (qw(light dark));
+ my $line_class_len = scalar (@line_class);
+ my $line_class_num = $#line_class;
+ while (my $line = <$fd>) {
+ my $long_rev;
+ my $short_rev;
+ my $author;
+ my $time;
+ my $lineno;
+ my $data;
+ my $age;
+ my $age_str;
+ my $age_class;