diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 8a3d31631c..646b6e7331 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -149,6 +149,9 @@ then you just add a line saying Signed-off-by: Random J Developer +This line can be automatically added by git if you run the git-commit +command with the -s option. + Some people also put extra tags at the end. They'll just be ignored for now, but you can do this to mark internal company procedures or just point out some special detail about the sign-off. diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 974b47f19c..7177c6e86b 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -334,11 +334,13 @@ div.diff.extended_header { padding: 2px 0px 2px 0px; } +div.diff a.list, div.diff a.path, div.diff a.hash { text-decoration: none; } +div.diff a.list:hover, div.diff a.path:hover, div.diff a.hash:hover { text-decoration: underline; @@ -362,14 +364,25 @@ div.diff.rem { color: #cc0000; } +div.diff.chunk_header a, div.diff.chunk_header { color: #990099; +} +div.diff.chunk_header { border: dotted #ffe0ff; border-width: 1px 0px 0px 0px; margin-top: 2px; } +div.diff.chunk_header span.chunk_info { + background-color: #ffeeff; +} + +div.diff.chunk_header span.section { + color: #aa22aa; +} + div.diff.incomplete { color: #cccccc; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 758759576c..ce185d9037 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -425,6 +425,7 @@ my %actions = ( "history" => \&git_history, "log" => \&git_log, "rss" => \&git_rss, + "atom" => \&git_atom, "search" => \&git_search, "search_help" => \&git_search_help, "shortlog" => \&git_shortlog, @@ -459,7 +460,8 @@ exit; sub href(%) { my %params = @_; - my $href = $my_uri; + # default is to use -absolute url() i.e. $my_uri + my $href = $params{-full} ? $my_url : $my_uri; # XXX: Warning: If you touch this, check the search form for updating, # too. @@ -874,8 +876,10 @@ sub format_subject_html { } } +# format patch (diff) line (rather not to be used for diff headers) sub format_diff_line { my $line = shift; + my ($from, $to) = @_; my $char = substr($line, 0, 1); my $diff_class = ""; @@ -891,6 +895,25 @@ sub format_diff_line { $diff_class = " incomplete"; } $line = untabify($line); + if ($from && $to && $line =~ m/^\@{2} /) { + my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) = + $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/; + + $from_lines = 0 unless defined $from_lines; + $to_lines = 0 unless defined $to_lines; + + if ($from->{'href'}) { + $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start", + -class=>"list"}, $from_text); + } + if ($to->{'href'}) { + $to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start", + -class=>"list"}, $to_text); + } + $line = "@@ $from_text $to_text @@" . + "" . esc_html($section, -nbsp=>1) . ""; + return "
$line
\n"; + } return "
" . esc_html($line, -nbsp=>1) . "
\n"; } @@ -1176,10 +1199,12 @@ sub parse_date { $date{'mday'} = $mday; $date{'day'} = $days[$wday]; $date{'month'} = $months[$mon]; - $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", - $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; + $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", + $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; + $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", + 1900+$year, $mon, $mday, $hour ,$min, $sec; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); @@ -1187,9 +1212,9 @@ sub parse_date { $date{'hour_local'} = $hour; $date{'minute_local'} = $min; $date{'tz_local'} = $tz; - $date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s", - 1900+$year, $mon+1, $mday, - $hour, $min, $sec, $tz); + $date{'iso-tz'} = sprintf("%04d-%02d-%02d %02d:%02d:%02d %s", + 1900+$year, $mon+1, $mday, + $hour, $min, $sec, $tz); return %date; } @@ -1650,14 +1675,17 @@ EOF } } if (defined $project) { - printf(''."\n", + printf(''."\n", esc_param($project), href(action=>"rss")); + printf(''."\n", + esc_param($project), href(action=>"atom")); } else { printf(''."\n", $site_name, href(project=>undef, action=>"project_index")); - printf(''."\n", $site_name, href(project=>undef, action=>"opml")); } @@ -1723,7 +1751,9 @@ sub git_footer_html { print "\n"; } print $cgi->a({-href => href(action=>"rss"), - -class => "rss_logo"}, "RSS") . "\n"; + -class => "rss_logo"}, "RSS") . " "; + print $cgi->a({-href => href(action=>"atom"), + -class => "rss_logo"}, "Atom") . "\n"; } else { print $cgi->a({-href => href(project=>undef, action=>"opml"), -class => "rss_logo"}, "OPML") . " "; @@ -2062,7 +2092,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 @@ -2082,13 +2116,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'})}, @@ -2133,13 +2165,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'})}, @@ -2178,17 +2209,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"; @@ -2202,31 +2232,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") { @@ -2247,100 +2302,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) . - ')'; - } - # 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!; - } - print $patch_line . "
\n"; - - } else { - #$in_header && $patch_line =~ m/^---/; - print "
\n"; # class="diff extended_header" - $in_header = 0; + # 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'}) { - $patch_line = '--- a/' . - $cgi->a({-href=>$from{'href'}, -class=>"path"}, - esc_path($from{'file'})); + $from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"}, + substr($diffinfo->{'from_id'},0,7)); + } else { + $from_link = '0' x 7; } - print "
$patch_line
\n"; - - $patch_line = <$fd>; - chomp $patch_line; - - #$patch_line =~ m/^+++/; if ($to{'href'}) { - $patch_line = '+++ b/' . - $cgi->a({-href=>$to{'href'}, -class=>"path"}, - esc_path($to{'file'})); + $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"; - + #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" - next LINE; + # 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"; + + $patch_line = <$fd>; + #last PATCH unless $patch_line; + 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"; + + # the patch itself + LINE: + while ($patch_line = <$fd>) { + chomp $patch_line; + + next PATCH if ($patch_line =~ m/^diff /); + + 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" } @@ -3392,6 +3459,7 @@ sub git_log { } sub git_commit { + $hash ||= $hash_base || "HEAD"; my %co = parse_commit($hash); if (!%co) { die_error(undef, "Unknown commit object"); @@ -3669,6 +3737,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"); @@ -3731,7 +3800,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; @@ -4088,26 +4158,125 @@ 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{'committer_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_url) . "\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); @@ -4116,42 +4285,100 @@ XML last; } my %cd = parse_date($co{'committer_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" . + "" . esc_html($co{'author_name'}) . "\n" . + # use committer for contributor + "" . esc_html($co{'committer_name'}) . "\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'); } - my $file = esc_path(unquote($7)); - $file = to_utf8($file); - print "$file
    \n"; + # 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'); + } + $file = esc_path($file); + print "] ". + "$file
  • \n"; + } + if ($format eq 'rss') { + print "
]]>\n" . + "\n" . + "\n"; + } elsif ($format eq 'atom') { + print "\n
\n" . + "
\n" . + "\n"; } - print "]]>\n" . - "
\n" . - "
\n"; } - print "
"; + + # end of feed + if ($format eq 'rss') { + print "\n\n"; + } elsif ($format eq 'atom') { + print "\n"; + } +} + +sub git_rss { + git_feed('rss'); +} + +sub git_atom { + git_feed('atom'); } sub git_opml {