element
sub git_print_tree_entry {
my ($t, $basedir, $hash_base, $have_blame) = @_;
my %base_key = ();
- $base_key{hash_base} = $hash_base if defined $hash_base;
+ $base_key{'hash_base'} = $hash_base if defined $hash_base;
# The format of a table row is: mode list link. Where mode is
# the mode of the entry, list is the name of the entry, an href,
@@ -1963,16 +2026,23 @@ sub git_print_tree_entry {
print "" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key),
- -class => "list"}, esc_path($t->{'name'})) . " \n";
+ -class => "list"}, esc_path($t->{'name'}));
+ if (S_ISLNK(oct $t->{'mode'})) {
+ my $link_target = git_get_link_target($t->{'hash'});
+ if ($link_target) {
+ print " -> " . esc_path($link_target);
+ }
+ }
+ print "\n";
print "";
print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blob");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blob");
if ($have_blame) {
print " | " .
$cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blame");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blame");
}
if (defined $hash_base) {
print " | " .
@@ -1994,8 +2064,8 @@ sub git_print_tree_entry {
print " \n";
print "";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "tree");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "tree");
if (defined $hash_base) {
print " | " .
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
@@ -2063,7 +2133,11 @@ sub git_difftree_body {
# link to patch
$patchno++;
print $cgi->a({-href => "#patch$patchno"}, "patch");
+ print " | ";
}
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+ hash_base=>$hash, file_name=>$diff{'file'})},
+ "blob") . " | ";
print " \n";
} elsif ($diff{'status'} eq "D") { # deleted
@@ -2083,13 +2157,11 @@ sub git_difftree_body {
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'})},
- "blob") . " | ";
+ "blob") . " | ";
if ($have_blame) {
- print $cgi->a({-href =>
- href(action=>"blame",
- hash_base=>$parent,
- file_name=>$diff{'file'})},
- "blame") . " | ";
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
file_name=>$diff{'file'})},
@@ -2134,13 +2206,12 @@ sub git_difftree_body {
" | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'})},
- "blob") . " | ";
+ hash_base=>$hash, file_name=>$diff{'file'})},
+ "blob") . " | ";
if ($have_blame) {
- print $cgi->a({-href => href(action=>"blame",
- hash_base=>$hash,
- file_name=>$diff{'file'})},
- "blame") . " | ";
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
file_name=>$diff{'file'})},
@@ -2179,17 +2250,16 @@ sub git_difftree_body {
"diff") .
" | ";
}
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
- hash_base=>$parent, file_name=>$diff{'from_file'})},
- "blob") . " | ";
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+ hash_base=>$parent, file_name=>$diff{'to_file'})},
+ "blob") . " | ";
if ($have_blame) {
- print $cgi->a({-href => href(action=>"blame",
- hash_base=>$hash,
- file_name=>$diff{'to_file'})},
- "blame") . " | ";
+ print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+ file_name=>$diff{'to_file'})},
+ "blame") . " | ";
}
- print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
- file_name=>$diff{'from_file'})},
+ print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+ file_name=>$diff{'to_file'})},
"history");
print "\n";
@@ -2203,31 +2273,56 @@ sub git_patchset_body {
my ($fd, $difftree, $hash, $hash_parent) = @_;
my $patch_idx = 0;
- my $in_header = 0;
- my $patch_found = 0;
+ my $patch_line;
my $diffinfo;
my (%from, %to);
+ my ($from_id, $to_id);
print "\n";
- LINE:
- while (my $patch_line = <$fd>) {
+ # skip to first patch
+ while ($patch_line = <$fd>) {
chomp $patch_line;
- if ($patch_line =~ m/^diff /) { # "git diff" header
- # beginning of patch (in patchset)
- if ($patch_found) {
- # close extended header for previous empty patch
- if ($in_header) {
- print "
\n" # class="diff extended_header"
- }
- # close previous patch
- print "\n"; # class="patch"
- } else {
- # first patch in patchset
- $patch_found = 1;
+ last if ($patch_line =~ m/^diff /);
+ }
+
+ PATCH:
+ while ($patch_line) {
+ my @diff_header;
+
+ # git diff header
+ #assert($patch_line =~ m/^diff /) if DEBUG;
+ #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
+ push @diff_header, $patch_line;
+
+ # extended diff header
+ EXTENDED_HEADER:
+ while ($patch_line = <$fd>) {
+ chomp $patch_line;
+
+ last EXTENDED_HEADER if ($patch_line =~ m/^--- /);
+
+ if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
+ $from_id = $1;
+ $to_id = $2;
}
- print "\n";
+
+ push @diff_header, $patch_line;
+ }
+ #last PATCH unless $patch_line;
+ my $last_patch_line = $patch_line;
+
+ # check if current patch belong to current raw line
+ # and parse raw git-diff line if needed
+ if (defined $diffinfo &&
+ $diffinfo->{'from_id'} eq $from_id &&
+ $diffinfo->{'to_id'} eq $to_id) {
+ # this is split patch
+ print "
\n";
+ } else {
+ # advance raw git-diff output if needed
+ $patch_idx++ if defined $diffinfo;
# read and prepare patch information
if (ref($difftree->[$patch_idx]) eq "HASH") {
@@ -2248,100 +2343,112 @@ sub git_patchset_body {
hash=>$diffinfo->{'to_id'},
file_name=>$to{'file'});
}
- $patch_idx++;
-
- # print "git diff" header
- $patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
- if ($from{'href'}) {
- $patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
- 'a/' . esc_path($from{'file'}));
- } else { # file was added
- $patch_line .= 'a/' . esc_path($from{'file'});
- }
- $patch_line .= ' ';
- if ($to{'href'}) {
- $patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
- 'b/' . esc_path($to{'file'}));
- } else { # file was deleted
- $patch_line .= 'b/' . esc_path($to{'file'});
- }
-
- print "
$patch_line
\n";
- print "
\n";
- $in_header = 1;
- next LINE;
+ # this is first patch for raw difftree line with $patch_idx index
+ # we index @$difftree array from 0, but number patches from 1
+ print "
\n";
}
- if ($in_header) {
- if ($patch_line !~ m/^---/) {
- # match
- if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
- $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
- esc_path($from{'file'}));
- }
- if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
- $patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
- esc_path($to{'file'}));
- }
- # match
- if ($patch_line =~ m/\s(\d{6})$/) {
- $patch_line .= ' (' .
- file_type_long($1) .
- ') ';
+ # print "git diff" header
+ $patch_line = shift @diff_header;
+ $patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
+ if ($from{'href'}) {
+ $patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
+ 'a/' . esc_path($from{'file'}));
+ } else { # file was added
+ $patch_line .= 'a/' . esc_path($from{'file'});
+ }
+ $patch_line .= ' ';
+ if ($to{'href'}) {
+ $patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
+ 'b/' . esc_path($to{'file'}));
+ } else { # file was deleted
+ $patch_line .= 'b/' . esc_path($to{'file'});
+ }
+ print "$patch_line
\n";
+
+ # print extended diff header
+ print "\n" if (@diff_header > 0);
+ EXTENDED_HEADER:
+ foreach $patch_line (@diff_header) {
+ # match
+ if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
+ $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
+ esc_path($from{'file'}));
+ }
+ if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
+ $patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
+ esc_path($to{'file'}));
+ }
+ # match
+ if ($patch_line =~ m/\s(\d{6})$/) {
+ $patch_line .= ' (' .
+ file_type_long($1) .
+ ') ';
+ }
+ # match
+ if ($patch_line =~ m/^index/) {
+ my ($from_link, $to_link);
+ if ($from{'href'}) {
+ $from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
+ substr($diffinfo->{'from_id'},0,7));
+ } else {
+ $from_link = '0' x 7;
}
- # match
- if ($patch_line =~ m/^index/) {
- my ($from_link, $to_link);
- if ($from{'href'}) {
- $from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
- substr($diffinfo->{'from_id'},0,7));
- } else {
- $from_link = '0' x 7;
- }
- if ($to{'href'}) {
- $to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
- substr($diffinfo->{'to_id'},0,7));
- } else {
- $to_link = '0' x 7;
- }
- my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
- $patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
+ if ($to{'href'}) {
+ $to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
+ substr($diffinfo->{'to_id'},0,7));
+ } else {
+ $to_link = '0' x 7;
}
- print $patch_line . " \n";
-
- } else {
- #$in_header && $patch_line =~ m/^---/;
- print " \n"; # class="diff extended_header"
- $in_header = 0;
+ #affirm {
+ # my ($from_hash, $to_hash) =
+ # ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/);
+ # my ($from_id, $to_id) =
+ # ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
+ # ($from_hash eq $from_id) && ($to_hash eq $to_id);
+ #} if DEBUG;
+ my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
+ $patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
+ }
+ print $patch_line . " \n";
+ }
+ print " \n" if (@diff_header > 0); # class="diff extended_header"
+
+ # from-file/to-file diff header
+ $patch_line = $last_patch_line;
+ #assert($patch_line =~ m/^---/) if DEBUG;
+ if ($from{'href'}) {
+ $patch_line = '--- a/' .
+ $cgi->a({-href=>$from{'href'}, -class=>"path"},
+ esc_path($from{'file'}));
+ }
+ print "
$patch_line
\n";
- if ($from{'href'}) {
- $patch_line = '--- a/' .
- $cgi->a({-href=>$from{'href'}, -class=>"path"},
- esc_path($from{'file'}));
- }
- print "
$patch_line
\n";
+ $patch_line = <$fd>;
+ #last PATCH unless $patch_line;
+ chomp $patch_line;
- $patch_line = <$fd>;
- chomp $patch_line;
+ #assert($patch_line =~ m/^+++/) if DEBUG;
+ if ($to{'href'}) {
+ $patch_line = '+++ b/' .
+ $cgi->a({-href=>$to{'href'}, -class=>"path"},
+ esc_path($to{'file'}));
+ }
+ print "
$patch_line
\n";
- #$patch_line =~ m/^+++/;
- if ($to{'href'}) {
- $patch_line = '+++ b/' .
- $cgi->a({-href=>$to{'href'}, -class=>"path"},
- esc_path($to{'file'}));
- }
- print "
$patch_line
\n";
+ # the patch itself
+ LINE:
+ while ($patch_line = <$fd>) {
+ chomp $patch_line;
- }
+ next PATCH if ($patch_line =~ m/^diff /);
- next LINE;
+ print format_diff_line($patch_line, \%from, \%to);
}
- print format_diff_line($patch_line);
+ } continue {
+ print "
\n"; # class="patch"
}
- print "
\n" if $in_header; # extended header
-
- print "
\n" if $patch_found; # class="patch"
print "\n"; # class="patchset"
}
@@ -2362,6 +2469,7 @@ sub git_project_list_body {
($pr->{'age'}, $pr->{'age_string'}) = @aa;
if (!defined $pr->{'descr'}) {
my $descr = git_get_project_description($pr->{'path'}) || "";
+ $pr->{'descr_long'} = to_utf8($descr);
$pr->{'descr'} = chop_str($descr, 25, 5);
}
if (!defined $pr->{'owner'}) {
@@ -2397,7 +2505,7 @@ sub git_project_list_body {
} else {
print "" .
$cgi->a({-href => href(project=>undef, order=>'project'),
- -class => "header"}, "Project") .
+ -class => "header"}, "Project") .
" \n";
}
if ($order eq "descr") {
@@ -2406,7 +2514,7 @@ sub git_project_list_body {
} else {
print "" .
$cgi->a({-href => href(project=>undef, order=>'descr'),
- -class => "header"}, "Description") .
+ -class => "header"}, "Description") .
" \n";
}
if ($order eq "owner") {
@@ -2415,7 +2523,7 @@ sub git_project_list_body {
} else {
print "" .
$cgi->a({-href => href(project=>undef, order=>'owner'),
- -class => "header"}, "Owner") .
+ -class => "header"}, "Owner") .
" \n";
}
if ($order eq "age") {
@@ -2424,7 +2532,7 @@ sub git_project_list_body {
} else {
print "" .
$cgi->a({-href => href(project=>undef, order=>'age'),
- -class => "header"}, "Last Change") .
+ -class => "header"}, "Last Change") .
" \n";
}
print " \n" .
@@ -2449,13 +2557,15 @@ sub git_project_list_body {
}
print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-class => "list"}, esc_html($pr->{'path'})) . " \n" .
- "" . esc_html($pr->{'descr'}) . " \n" .
+ "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+ -class => "list", -title => $pr->{'descr_long'}},
+ esc_html($pr->{'descr'})) . " \n" .
"" . chop_str($pr->{'owner'}, 15) . " \n";
print "{'age'}) . "\">" .
$pr->{'age_string'} . " \n" .
"" .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " .
- $cgi->a({-href => '/git-browser/by-commit.html?r='.$pr->{'path'}}, "graphiclog") . " | " .
+ $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
@@ -2852,8 +2962,8 @@ sub git_tag {
print "";
my $comment = $tag{'comment'};
foreach my $line (@$comment) {
- chomp($line);
- print esc_html($line) . " \n";
+ chomp $line;
+ print esc_html($line, -nbsp=>1) . " \n";
}
print "
\n";
git_footer_html();
@@ -2922,7 +3032,7 @@ HTML
}
}
my $data = $_;
- chomp($data);
+ chomp $data;
my $rev = substr($full_rev, 0, 8);
my $author = $meta->{'author'};
my %date = parse_date($meta->{'author-time'},
@@ -3147,10 +3257,13 @@ sub git_blob {
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(undef, "Couldn't cat $file_name, $hash");
my $mimetype = blob_mimetype($fd, $file_name);
- if ($mimetype !~ m/^text\//) {
+ if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) {
close $fd;
return git_blob_plain($mimetype);
}
+ # we can have blame only for text/* mimetype
+ $have_blame &&= ($mimetype =~ m!^text/!);
+
git_header_html(undef, $expires);
my $formats_nav = '';
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -3187,13 +3300,24 @@ sub git_blob {
}
git_print_page_path($file_name, "blob", $hash_base);
print "\n";
- my $nr;
- while (my $line = <$fd>) {
- chomp $line;
- $nr++;
- $line = untabify($line);
- printf "
\n",
- $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+ if ($mimetype =~ m!^text/!) {
+ my $nr;
+ while (my $line = <$fd>) {
+ chomp $line;
+ $nr++;
+ $line = untabify($line);
+ printf "
\n",
+ $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+ }
+ } elsif ($mimetype =~ m!^image/!) {
+ print qq!
$hash,
+ hash_base=>$hash_base, file_name=>$file_name) .
+ qq!" />\n!;
}
close $fd
or print "Reading blob failed.\n";
@@ -3318,8 +3442,7 @@ sub git_snapshot {
my $filename = basename($project) . "-$hash.tar.$suffix";
print $cgi->header(
- -type => 'application/x-tar',
- -content_encoding => $ctype,
+ -type => "application/$ctype",
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
@@ -3393,6 +3516,7 @@ sub git_log {
}
sub git_commit {
+ $hash ||= $hash_base || "HEAD";
my %co = parse_commit($hash);
if (!%co) {
die_error(undef, "Unknown commit object");
@@ -3497,6 +3621,53 @@ sub git_commit {
git_footer_html();
}
+sub git_object {
+ # object is defined by:
+ # - hash or hash_base alone
+ # - hash_base and file_name
+ my $type;
+
+ # - hash or hash_base alone
+ if ($hash || ($hash_base && !defined $file_name)) {
+ my $object_id = $hash || $hash_base;
+
+ my $git_command = git_cmd_str();
+ open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
+ or die_error('404 Not Found', "Object does not exist");
+ $type = <$fd>;
+ chomp $type;
+ close $fd
+ or die_error('404 Not Found', "Object does not exist");
+
+ # - hash_base and file_name
+ } elsif ($hash_base && defined $file_name) {
+ $file_name =~ s,/+$,,;
+
+ system(git_cmd(), "cat-file", '-e', $hash_base) == 0
+ or die_error('404 Not Found', "Base object does not exist");
+
+ # here errors should not hapen
+ open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
+ or die_error(undef, "Open git-ls-tree failed");
+ my $line = <$fd>;
+ close $fd;
+
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
+ unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+ die_error('404 Not Found', "File or directory for given base does not exist");
+ }
+ $type = $2;
+ $hash = $3;
+ } else {
+ die_error('404 Not Found', "Not enough information to find object");
+ }
+
+ print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+ hash=>$hash, hash_base=>$hash_base,
+ file_name=>$file_name),
+ -status => '302 Found');
+}
+
sub git_blobdiff {
my $format = shift || 'html';
@@ -3670,6 +3841,7 @@ sub git_blobdiff_plain {
sub git_commitdiff {
my $format = shift || 'html';
+ $hash ||= $hash_base || "HEAD";
my %co = parse_commit($hash);
if (!%co) {
die_error(undef, "Unknown commit object");
@@ -3732,7 +3904,8 @@ sub git_commitdiff {
$hash_parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
- while (chomp(my $line = <$fd>)) {
+ while (my $line = <$fd>) {
+ chomp $line;
# empty line ends raw part of diff-tree output
last unless $line;
push @difftree, $line;
@@ -4089,70 +4262,237 @@ sub git_shortlog {
}
## ......................................................................
-## feeds (RSS, OPML)
+## feeds (RSS, Atom; OPML)
-sub git_rss {
- # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+sub git_feed {
+ my $format = shift || 'atom';
+ my ($have_blame) = gitweb_check_feature('blame');
+
+ # Atom: http://www.atomenabled.org/developers/syndication/
+ # RSS: http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+ if ($format ne 'rss' && $format ne 'atom') {
+ die_error(undef, "Unknown web feed format");
+ }
+
+ # log/feed of current (HEAD) branch, log of given branch, history of file/directory
+ my $head = $hash || 'HEAD';
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150",
- git_get_head_hash($project), "--"
+ $head, "--", (defined $file_name ? $file_name : ())
or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-rev-list failed");
- print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
- print <
+
+ my %latest_commit;
+ my %latest_date;
+ my $content_type = "application/$format+xml";
+ if (defined $cgi->http('HTTP_ACCEPT') &&
+ $cgi->Accept('text/xml') > $cgi->Accept($content_type)) {
+ # browser (feed reader) prefers text/xml
+ $content_type = 'text/xml';
+ }
+ if (defined($revlist[0])) {
+ %latest_commit = parse_commit($revlist[0]);
+ %latest_date = parse_date($latest_commit{'author_epoch'});
+ print $cgi->header(
+ -type => $content_type,
+ -charset => 'utf-8',
+ -last_modified => $latest_date{'rfc2822'});
+ } else {
+ print $cgi->header(
+ -type => $content_type,
+ -charset => 'utf-8');
+ }
+
+ # Optimization: skip generating the body if client asks only
+ # for Last-Modified date.
+ return if ($cgi->request_method() eq 'HEAD');
+
+ # header variables
+ my $title = "$site_name - $project/$action";
+ my $feed_type = 'log';
+ if (defined $hash) {
+ $title .= " - '$hash'";
+ $feed_type = 'branch log';
+ if (defined $file_name) {
+ $title .= " :: $file_name";
+ $feed_type = 'history';
+ }
+ } elsif (defined $file_name) {
+ $title .= " - $file_name";
+ $feed_type = 'history';
+ }
+ $title .= " $feed_type";
+ my $descr = git_get_project_description($project);
+ if (defined $descr) {
+ $descr = esc_html($descr);
+ } else {
+ $descr = "$project " .
+ ($format eq 'rss' ? 'RSS' : 'Atom') .
+ " feed";
+ }
+ my $owner = git_get_project_owner($project);
+ $owner = esc_html($owner);
+
+ #header
+ my $alt_url;
+ if (defined $file_name) {
+ $alt_url = href(-full=>1, action=>"history", hash=>$hash, file_name=>$file_name);
+ } elsif (defined $hash) {
+ $alt_url = href(-full=>1, action=>"log", hash=>$hash);
+ } else {
+ $alt_url = href(-full=>1, action=>"summary");
+ }
+ print qq!\n!;
+ if ($format eq 'rss') {
+ print <
-$project $my_uri $my_url
- ${\esc_html("$my_url?p=$project;a=summary")}
-$project log
-en
XML
+ print "$title \n" .
+ " $alt_url\n" .
+ "$descr \n" .
+ "en \n";
+ } elsif ($format eq 'atom') {
+ print <
+XML
+ print "$title \n" .
+ "$descr \n" .
+ ' ' . "\n" .
+ ' ' . "\n" .
+ "" . href(-full=>1) . " \n" .
+ # use project owner for feed author
+ "$owner \n";
+ if (defined $favicon) {
+ print "" . esc_url($favicon) . " \n";
+ }
+ if (defined $logo_url) {
+ # not twice as wide as tall: 72 x 27 pixels
+ print "" . esc_url($logo) . " \n";
+ }
+ if (! %latest_date) {
+ # dummy date to keep the feed valid until commits trickle in:
+ print "1970-01-01T00:00:00Z \n";
+ } else {
+ print "$latest_date{'iso-8601'} \n";
+ }
+ }
+ # contents
for (my $i = 0; $i <= $#revlist; $i++) {
my $commit = $revlist[$i];
my %co = parse_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)) {
+ if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
last;
}
- my %cd = parse_date($co{'committer_epoch'});
+ my %cd = parse_date($co{'author_epoch'});
+
+ # get list of changed files
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
- $co{'parent'}, $co{'id'}, "--"
+ $co{'parent'}, $co{'id'}, "--", (defined $file_name ? $file_name : ())
or next;
my @difftree = map { chomp; $_ } <$fd>;
close $fd
or next;
- print "- \n" .
- "
" .
- sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
- " \n" .
- "" . esc_html($co{'author'}) . " \n" .
- "$cd{'rfc2822'} \n" .
- "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . " \n" .
- " " . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" .
- "" . esc_html($co{'title'}) . " \n" .
- "" .
- "1, action=>"commit", hash=>$commit);
+ if ($format eq 'rss') {
+ print "- \n" .
+ "
" . esc_html($co{'title'}) . " \n" .
+ "" . esc_html($co{'author'}) . " \n" .
+ "$cd{'rfc2822'} \n" .
+ "$co_url \n" .
+ " $co_url\n" .
+ "" . esc_html($co{'title'}) . " \n" .
+ "" .
+ "\n" .
+ "" . esc_html($co{'title'}) . " \n" .
+ "$cd{'iso-8601'} \n" .
+ "\n" .
+ " " . esc_html($co{'author_name'}) . " \n";
+ if ($co{'author_email'}) {
+ print " " . esc_html($co{'author_email'}) . " \n";
+ }
+ print " \n" .
+ # use committer for contributor
+ "\n" .
+ " " . esc_html($co{'committer_name'}) . " \n";
+ if ($co{'committer_email'}) {
+ print " " . esc_html($co{'committer_email'}) . " \n";
+ }
+ print " \n" .
+ "$cd{'iso-8601'} \n" .
+ " \n" .
+ "$co_url \n" .
+ "\n" .
+ "\n";
+ }
my $comment = $co{'comment'};
+ print "
\n";
foreach my $line (@$comment) {
- $line = to_utf8($line);
- print "$line \n";
+ $line = esc_html($line);
+ print "$line\n";
}
- print " \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;
+ print " \n";
+ foreach my $difftree_line (@difftree) {
+ my %difftree = parse_difftree_raw_line($difftree_line);
+ next if !$difftree{'from_id'};
+
+ my $file = $difftree{'file'} || $difftree{'to_file'};
+
+ print "" .
+ "[" .
+ $cgi->a({-href => href(-full=>1, action=>"blobdiff",
+ hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'},
+ hash_base=>$co{'id'}, hash_parent_base=>$co{'parent'},
+ file_name=>$file, file_parent=>$difftree{'from_file'}),
+ -title => "diff"}, 'D');
+ if ($have_blame) {
+ print $cgi->a({-href => href(-full=>1, action=>"blame",
+ file_name=>$file, hash_base=>$commit),
+ -title => "blame"}, 'B');
+ }
+ # if this is not a feed of a file history
+ if (!defined $file_name || $file_name ne $file) {
+ print $cgi->a({-href => href(-full=>1, action=>"history",
+ file_name=>$file, hash=>$commit),
+ -title => "history"}, 'H');
}
- my $file = esc_path(unquote($7));
- $file = to_utf8($file);
- print "$file \n";
+ $file = esc_path($file);
+ print "] ".
+ "$file \n";
}
- print "]]>\n" .
- "\n" .
- "\n";
+ if ($format eq 'rss') {
+ print " ]]>\n" .
+ "\n" .
+ "\n";
+ } elsif ($format eq 'atom') {
+ print "\n
\n" .
+ " \n" .
+ "\n";
+ }
+ }
+
+ # end of feed
+ if ($format eq 'rss') {
+ print " \n\n";
+ } elsif ($format eq 'atom') {
+ print "\n";
}
- print "";
+}
+
+sub git_rss {
+ git_feed('rss');
+}
+
+sub git_atom {
+ git_feed('atom');
}
sub git_opml {