X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/44fc0a5222b9fb27c7e196c1f3e17e0de6bfdb19987bc8a9618ad4bd6e712073..b431c2da9de0f1a5f2f57ebd90e34020fa7b095130d73cbe60cbe57d73cfcc4e:/gitweb.perl diff --git a/gitweb.perl b/gitweb.perl index a0a7e80..9a0859b 100755 --- a/gitweb.perl +++ b/gitweb.perl @@ -15,6 +15,7 @@ use CGI::Carp qw(fatalsToBrowser); use Encode; use Fcntl ':mode'; use File::Find qw(); +use File::Basename qw(basename); binmode STDOUT, ':utf8'; our $cgi = new CGI; @@ -34,7 +35,7 @@ our $projectroot = "++GITWEB_PROJECTROOT++"; our $git_temp = "/tmp/gitweb"; # target of the home link on top of all pages -our $home_link = $my_uri; +our $home_link = $my_uri || "/"; # string of the home link on top of all pages our $home_link_str = "++GITWEB_HOME_LINK_STR++"; @@ -66,6 +67,68 @@ our $default_text_plain_charset = undef; # (relative to the current git repository) our $mimetypes_file = undef; +# You define site-wide feature defaults here; override them with +# $GITWEB_CONFIG as necessary. +our %feature = +( + +# feature => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...] + +'blame' => {'sub' => \&feature_blame, 'override' => 0, 'default' => [0]}, +'snapshot' => {'sub' => \&feature_snapshot, 'override' => 0, 'default' => ['x-gzip', 'gz', 'gzip']}, + +); + +sub gitweb_check_feature { + my ($name) = @_; + return undef unless exists $feature{$name}; + my ($sub, $override, @defaults) = ($feature{$name}{'sub'}, + $feature{$name}{'override'}, + @{$feature{$name}{'default'}}); + if (!$override) { return @defaults; } + return $sub->(@defaults); +} + +# To enable system wide have in $GITWEB_CONFIG +# $feature{'blame'}{'default'} = [1]; +# To have project specific config enable override in $GITWEB_CONFIG +# $feature{'blame'}{'override'} = 1; +# and in project config gitweb.blame = 0|1; + +sub feature_blame { + my ($val) = git_get_project_config('blame', '--bool'); + + if ($val eq 'true') { + return 1; + } elsif ($val eq 'false') { + return 0; + } + + return $_[0]; +} + +# To disable system wide have in $GITWEB_CONFIG +# $feature{'snapshot'}{'default'} = [undef]; +# To have project specific config enable override in $GITWEB_CONFIG +# $feature{'blame'}{'override'} = 1; +# and in project config gitweb.snapshot = none|gzip|bzip2 + +sub feature_snapshot { + my ($ctype, $suffix, $command) = @_; + + my ($val) = git_get_project_config('snapshot'); + + if ($val eq 'gzip') { + return ('x-gzip', 'gz', 'gzip'); + } elsif ($val eq 'bzip2') { + return ('x-bzip2', 'bz2', 'bzip2'); + } elsif ($val eq 'none') { + return (); + } + + return ($ctype, $suffix, $command); +} + our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; require $GITWEB_CONFIG if -e $GITWEB_CONFIG; @@ -95,8 +158,9 @@ our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); if (defined $project) { $project =~ s|^/||; $project =~ s|/$||; + $project = undef unless $project; } -if (defined $project && $project) { +if (defined $project) { if (!validate_input($project)) { die_error(undef, "Invalid project parameter"); } @@ -182,6 +246,7 @@ my %actions = ( "tag" => \&git_tag, "tags" => \&git_tags, "tree" => \&git_tree, + "snapshot" => \&git_snapshot, ); $action = 'summary' if (!defined($action)); @@ -482,24 +547,21 @@ sub git_get_type { } sub git_get_project_config { - my $key = shift; + my ($key, $type) = @_; return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); - my $val = qx($GIT repo-config --get gitweb.$key); + my @x = ($GIT, 'repo-config'); + if (defined $type) { push @x, $type; } + push @x, "--get"; + push @x, "gitweb.$key"; + my $val = qx(@x); + chomp $val; return ($val); } -sub git_get_project_config_bool { - my $val = git_get_project_config (@_); - if ($val and $val =~ m/true|yes|on/) { - return (1); - } - return; # implicit false -} - # get hash of given path at given ref sub git_get_hash_by_path { my $base = shift; @@ -856,6 +918,33 @@ sub parse_ref { return %ref_item; } +sub parse_difftree_raw_line { + my $line = shift; + my %res; + + # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' + # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' + if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) { + $res{'from_mode'} = $1; + $res{'to_mode'} = $2; + $res{'from_id'} = $3; + $res{'to_id'} = $4; + $res{'status'} = $5; + $res{'similarity'} = $6; + if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied + ($res{'from_file'}, $res{'to_file'}) = map(unquote, split("\t", $7)); + } else { + $res{'file'} = unquote($7); + } + } + # 'c512b523472485aef4fff9e57b229d9d243c967f' + #elsif ($line =~ m/^([0-9a-fA-F]{40})$/) { + # $res{'commit'} = $1; + #} + + return wantarray ? %res : \%res; +} + ## ...................................................................... ## parse to array of hashes functions @@ -1174,17 +1263,83 @@ sub git_print_header_div { sub git_print_page_path { my $name = shift; my $type = shift; + my $hb = shift; if (!defined $name) { print "
/
\n"; } elsif (defined $type && $type eq 'blob') { - print "
" . - $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name)}, esc_html($name)) . "
\n"; + print "
"; + if (defined $hb) { + print $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hb, file_name=>$file_name)}, esc_html($name)); + } else { + print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name)}, esc_html($name)); + } + print "
\n"; } else { print "
" . esc_html($name) . "
\n"; } } +sub git_print_log { + my $log = shift; + + # remove leading empty lines + while (defined $log->[0] && $log->[0] eq "") { + shift @$log; + } + + # print log + my $signoff = 0; + my $empty = 0; + foreach my $line (@$log) { + # print only one empty line + # do not print empty line after signoff + if ($line eq "") { + next if ($empty || $signoff); + $empty = 1; + } else { + $empty = 0; + } + if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { + $signoff = 1; + print "" . esc_html($line) . "
\n"; + } else { + $signoff = 0; + print format_log_line_html($line) . "
\n"; + } + } +} + +sub git_print_simplified_log { + my $log = shift; + my $remove_title = shift; + + shift @$log if $remove_title; + # remove leading empty lines + while (defined $log->[0] && $log->[0] eq "") { + shift @$log; + } + + # simplify and print log + my $empty = 0; + foreach my $line (@$log) { + # remove signoff lines + if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { + next; + } + # print only one empty line + if ($line eq "") { + next if $empty; + $empty = 1; + } else { + $empty = 0; + } + print format_log_line_html($line) . "
\n"; + } + # end with single empty line + print "
\n" unless $empty; +} + ## ...................................................................... ## functions printing large fragments of HTML @@ -1328,6 +1483,10 @@ sub git_difftree_body { sub git_shortlog_body { # uses global variable $project my ($revlist, $from, $to, $refs, $extra) = @_; + + my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); + my $have_snapshot = (defined $ctype && defined $suffix); + $from = 0 unless defined $from; $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to); @@ -1352,8 +1511,11 @@ sub git_shortlog_body { print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . - $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . - "\n" . + $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff"); + if ($have_snapshot) { + print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot"); + } + print "\n" . "\n"; } if (defined $extra) { @@ -1785,7 +1947,10 @@ sub git_tag { sub git_blame2 { my $fd; my $ftype; - die_error(undef, "Permission denied") if (!git_get_project_config_bool ('blame')); + + if (!gitweb_check_feature('blame')) { + die_error('403 Permission denied', "Permission denied"); + } die_error('404 Not Found', "File name not defined") if (!$file_name); $hash_base ||= git_get_head_hash($project); die_error(undef, "Couldn't find base commit") unless ($hash_base); @@ -1807,7 +1972,7 @@ sub git_blame2 { " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "head"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, $ftype); + git_print_page_path($file_name, $ftype, $hash_base); my @rev_color = (qw(light2 dark2)); my $num_colors = scalar(@rev_color); my $current_color = 0; @@ -1843,7 +2008,10 @@ sub git_blame2 { sub git_blame { my $fd; - die_error('403 Permission denied', "Permission denied") if (!git_get_project_config_bool ('blame')); + + if (!gitweb_check_feature('blame')) { + die_error('403 Permission denied', "Permission denied"); + } die_error('404 Not Found', "File name not defined") if (!$file_name); $hash_base ||= git_get_head_hash($project); die_error(undef, "Couldn't find base commit") unless ($hash_base); @@ -1861,7 +2029,7 @@ sub git_blame { " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "head"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, 'blob'); + git_print_page_path($file_name, 'blob', $hash_base); print "
\n"; print < @@ -1996,7 +2164,7 @@ sub git_blob { die_error(undef, "No file name defined"); } } - my $have_blame = git_get_project_config_bool ('blame'); + my $have_blame = gitweb_check_feature('blame'); open my $fd, "-|", $GIT, "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); @@ -2024,7 +2192,7 @@ sub git_blob { "

\n" . "
$hash
\n"; } - git_print_page_path($file_name, "blob"); + git_print_page_path($file_name, "blob", $hash_base); print "
\n"; my $nr; while (my $line = <$fd>) { @@ -2061,7 +2229,7 @@ sub git_tree { git_header_html(); my %base_key = (); my $base = ""; - my $have_blame = git_get_project_config_bool ('blame'); + my $have_blame = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { $base_key{hash_base} = $hash_base; git_print_page_nav('tree','', $hash_base); @@ -2074,7 +2242,7 @@ sub git_tree { if (defined $file_name) { $base = esc_html("$file_name/"); } - git_print_page_path($file_name, 'tree'); + git_print_page_path($file_name, 'tree', $hash_base); print "
\n"; print "\n"; my $alternate = 0; @@ -2120,6 +2288,35 @@ sub git_tree { git_footer_html(); } +sub git_snapshot { + + my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); + my $have_snapshot = (defined $ctype && defined $suffix); + if (!$have_snapshot) { + die_error('403 Permission denied', "Permission denied"); + } + + if (!defined $hash) { + $hash = git_get_head_hash($project); + } + + my $filename = basename($project) . "-$hash.tar.$suffix"; + + print $cgi->header(-type => 'application/x-tar', + -content-encoding => $ctype, + '-content-disposition' => + "inline; filename=\"$filename\"", + -status => '200 OK'); + + open my $fd, "-|", "$GIT tar-tree $hash \'$project\' | $command" or + die_error(undef, "Execute git-tar-tree failed."); + binmode STDOUT, ':raw'; + print <$fd>; + binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi + close $fd; + +} + sub git_log { my $head = git_get_head_hash($project); if (!defined $hash) { @@ -2164,27 +2361,10 @@ sub git_log { "
\n" . "\n" . "" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]
\n" . - "\n" . - "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - foreach my $line (@$comment) { - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - next; - } - if ($line eq "") { - if ($empty) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - print format_log_line_html($line) . "
\n"; - } - if (!$empty) { - print "
\n"; - } + "
\n"; + + print "
\n"; + git_print_simplified_log($co{'comment'}); print "
\n"; } git_footer_html(); @@ -2214,6 +2394,10 @@ sub git_commit { } my $refs = git_get_references(); my $ref = format_ref_marker($refs, $co{'id'}); + + my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot'); + my $have_snapshot = (defined $ctype && defined $suffix); + my $formats_nav = ''; if (defined $file_name && defined $co{'parent'}) { my $parent = $co{'parent'}; @@ -2249,8 +2433,11 @@ sub git_commit { "" . - "" . + "" . "\n"; my $parents = $co{'parents'}; foreach my $par (@$parents) { @@ -2265,28 +2452,9 @@ sub git_commit { } print "
" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), class => "list"}, $co{'tree'}) . "" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, "tree") . - "" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, "tree"); + if ($have_snapshot) { + print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot"); + } + print "
". "
\n"; + print "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - my $signed = 0; - foreach my $line (@$comment) { - # print only one empty line - if ($line eq "") { - if ($empty || $signed) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - $signed = 1; - print "" . esc_html($line) . "
\n"; - } else { - $signed = 0; - print format_log_line_html($line) . "
\n"; - } - } + git_print_log($co{'comment'}); print "
\n"; git_difftree_body(\@difftree, $parent); @@ -2307,7 +2475,7 @@ sub git_blobdiff { "

\n" . "
$hash vs $hash_parent
\n"; } - git_print_page_path($file_name, "blob"); + git_print_page_path($file_name, "blob", $hash_base); print "
\n" . "
blob:" . $cgi->a({-href => href(action=>"blob", hash=>$hash_parent, hash_base=>$hash_base, file_name=>($file_parent || $file_name))}, $hash_parent) . @@ -2352,29 +2520,7 @@ sub git_commitdiff { git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); print "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - my $signed = 0; - my @log = @$comment; - # remove first and empty lines after that - shift @log; - while (defined $log[0] && $log[0] eq "") { - shift @log; - } - foreach my $line (@log) { - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - next; - } - if ($line eq "") { - if ($empty) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - print format_log_line_html($line) . "
\n"; - } + git_print_simplified_log($co{'comment'}, 1); # skip title print "
\n"; foreach my $line (@difftree) { # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' @@ -2405,7 +2551,7 @@ sub git_commitdiff { $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$hash_parent, file_name=>$file)}, $from_id) . " -> " . file_type($to_mode) . ":" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, $to_id) . + $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, $to_id); print "
\n"; git_diff_print($from_id, "a/$file", $to_id, "b/$file"); } @@ -2499,7 +2645,7 @@ sub git_history { if (defined $hash) { $ftype = git_get_type($hash); } - git_print_page_path($file_name, $ftype); + git_print_page_path($file_name, $ftype, $hash_base); open my $fd, "-|", $GIT, "rev-list", "--full-history", $hash_base, "--", $file_name;