X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/b30bdbe02d076ed9cdf96e3866ccc886de70a89b69947f2b921f03cc78d2b040..0a1276bd4b16b1fdf71b6b852fc9ba6c094d67edbec5ca8f06c9170b6445284d:/gitweb.perl
diff --git a/gitweb.perl b/gitweb.perl
index 5a55826..04e1c56 100755
--- a/gitweb.perl
+++ b/gitweb.perl
@@ -18,6 +18,12 @@ use File::Find qw();
use File::Basename qw(basename);
binmode STDOUT, ':utf8';
+our $t0;
+if (eval { require Time::HiRes; 1; }) {
+ $t0 = [Time::HiRes::gettimeofday()];
+}
+our $number_of_git_cmds = 0;
+
BEGIN {
CGI->compile() if $ENV{'MOD_PERL'};
}
@@ -91,11 +97,13 @@ our $stylesheet = undef;
our $logo = "++GITWEB_LOGO++";
# URI of GIT favicon, assumed to be image/png type
our $favicon = "++GITWEB_FAVICON++";
+# URI of gitweb.js (JavaScript code for gitweb)
+our $javascript = "++GITWEB_JS++";
# URI and label (title) of GIT logo link
#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
#our $logo_label = "git documentation";
-our $logo_url = "http://git.or.cz/";
+our $logo_url = "http://git-scm.com/";
our $logo_label = "git homepage";
# source of projects list
@@ -161,7 +169,8 @@ our %known_snapshot_formats = (
# 'suffix' => filename suffix,
# 'format' => --format for git-archive,
# 'compressor' => [compressor command and arguments]
- # (array reference, optional)}
+ # (array reference, optional)
+ # 'disabled' => boolean (optional)}
#
'tgz' => {
'display' => 'tar.gz',
@@ -177,6 +186,14 @@ our %known_snapshot_formats = (
'format' => 'tar',
'compressor' => ['bzip2']},
+ 'txz' => {
+ 'display' => 'tar.xz',
+ 'type' => 'application/x-xz',
+ 'suffix' => '.tar.xz',
+ 'format' => 'tar',
+ 'compressor' => ['xz'],
+ 'disabled' => 1},
+
'zip' => {
'display' => 'zip',
'type' => 'application/x-zip',
@@ -189,6 +206,7 @@ our %known_snapshot_formats = (
our %known_snapshot_format_aliases = (
'gzip' => 'tgz',
'bzip2' => 'tbz2',
+ 'xz' => 'txz',
# backward compatibility: legacy gitweb config support
'x-gzip' => undef, 'gz' => undef,
@@ -204,6 +222,12 @@ our %avatar_size = (
'double' => 32
);
+# Used to set the maximum load that we will still respond to gitweb queries.
+# If server load exceed this value then return "503 server busy" error.
+# If gitweb cannot determined server load, it is taken to be 0.
+# Leave it undefined (or set to 'undef') to turn off load checking.
+our $maxload = 300;
+
# You define site-wide feature defaults here; override them with
# $GITWEB_CONFIG as necessary.
our %feature = (
@@ -288,6 +312,19 @@ our %feature = (
'override' => 0,
'default' => [1]},
+ # Enable showing size of blobs in a 'tree' view, in a separate
+ # column, similar to what 'ls -l' does. This cost a bit of IO.
+
+ # To disable system wide have in $GITWEB_CONFIG
+ # $feature{'show-sizes'}{'default'} = [0];
+ # To have project specific config enable override in $GITWEB_CONFIG
+ # $feature{'show-sizes'}{'override'} = 1;
+ # and in project config gitweb.showsizes = 0|1;
+ 'show-sizes' => {
+ 'sub' => sub { feature_bool('showsizes', @_) },
+ 'override' => 0,
+ 'default' => [1]},
+
# Make gitweb use an alternative format of the URLs which can be
# more readable and natural-looking: project name is embedded
# directly in the path and the query string contains other
@@ -395,6 +432,20 @@ our %feature = (
'sub' => \&feature_avatar,
'override' => 0,
'default' => ['']},
+
+ # Enable displaying how much time and how many git commands
+ # it took to generate and display page. Disabled by default.
+ # Project specific override is not supported.
+ 'timed' => {
+ 'override' => 0,
+ 'default' => [0]},
+
+ # Enable turning some links into links to actions which require
+ # JavaScript to run (like 'blame_incremental'). Not enabled by
+ # default. Project specific override is currently not supported.
+ 'javascript-actions' => {
+ 'override' => 0,
+ 'default' => [0]},
);
sub gitweb_get_feature {
@@ -406,7 +457,7 @@ sub gitweb_get_feature {
@{$feature{$name}{'default'}});
if (!$override) { return @defaults; }
if (!defined $sub) {
- warn "feature $name is not overrideable";
+ warn "feature $name is not overridable";
return @defaults;
}
return $sub->(@defaults);
@@ -495,7 +546,8 @@ sub filter_snapshot_fmts {
exists $known_snapshot_format_aliases{$_} ?
$known_snapshot_format_aliases{$_} : $_} @fmts;
@fmts = grep {
- exists $known_snapshot_formats{$_} } @fmts;
+ exists $known_snapshot_formats{$_} &&
+ !$known_snapshot_formats{$_}{'disabled'}} @fmts;
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -506,11 +558,38 @@ if (-e $GITWEB_CONFIG) {
do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
}
+# Get loadavg of system, to compare against $maxload.
+# Currently it requires '/proc/loadavg' present to get loadavg;
+# if it is not present it returns 0, which means no load checking.
+sub get_loadavg {
+ if( -e '/proc/loadavg' ){
+ open my $fd, '<', '/proc/loadavg'
+ or return 0;
+ my @load = split(/\s+/, scalar <$fd>);
+ close $fd;
+
+ # The first three columns measure CPU and IO utilization of the last one,
+ # five, and 10 minute periods. The fourth column shows the number of
+ # currently running processes and the total number of processes in the m/n
+ # format. The last column displays the last process ID used.
+ return $load[0] || 0;
+ }
+ # additional checks for load average should go here for things that don't export
+ # /proc/loadavg
+
+ return 0;
+}
+
# version of the core git binary
our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+$number_of_git_cmds++;
$projects_list ||= $projectroot;
+if (defined $maxload && get_loadavg() > $maxload) {
+ die_error(503, "The load average on the server is too high");
+}
+
# ======================================================================
# input validation and dispatch
@@ -545,12 +624,16 @@ our @cgi_param_mapping = (
snapshot_format => "sf",
extra_options => "opt",
search_use_regexp => "sr",
+ # this must be last entry (for manipulation from JavaScript)
+ javascript => "js"
);
our %cgi_param_mapping = @cgi_param_mapping;
# we will also need to know the possible actions, for validation
our %actions = (
"blame" => \&git_blame,
+ "blame_incremental" => \&git_blame_incremental,
+ "blame_data" => \&git_blame_data,
"blobdiff" => \&git_blobdiff,
"blobdiff_plain" => \&git_blobdiff_plain,
"blob" => \&git_blob,
@@ -941,10 +1024,13 @@ sub href {
if (defined $params{'hash_parent_base'}) {
$href .= esc_url($params{'hash_parent_base'});
# skip the file_parent if it's the same as the file_name
- delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
- if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
- $href .= ":/".esc_url($params{'file_parent'});
- delete $params{'file_parent'};
+ if (defined $params{'file_parent'}) {
+ if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
+ delete $params{'file_parent'};
+ } elsif ($params{'file_parent'} !~ /\.\./) {
+ $href .= ":/".esc_url($params{'file_parent'});
+ delete $params{'file_parent'};
+ }
}
$href .= "..";
delete $params{'hash_parent'};
@@ -1070,8 +1156,7 @@ sub to_utf8 {
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
my $str = shift;
- $str =~ s/([^A-Za-z0-9\-_.~()\/:@])/sprintf("%%%02X", ord($1))/eg;
- $str =~ s/\+/%2B/g;
+ $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg;
$str =~ s/ /\+/g;
return $str;
}
@@ -1511,10 +1596,10 @@ sub format_subject_html {
$long =~ s/[[:cntrl:]]/?/g;
return $cgi->a({-href => $href, -class => "list subject",
-title => to_utf8($long)},
- esc_html($short) . $extra);
+ esc_html($short)) . $extra;
} else {
return $cgi->a({-href => $href, -class => "list subject"},
- esc_html($long) . $extra);
+ esc_html($long)) . $extra;
}
}
@@ -1575,12 +1660,36 @@ sub git_get_avatar {
"" . $post_white;
} else {
return "";
}
}
+sub format_search_author {
+ my ($author, $searchtype, $displaytext) = @_;
+ my $have_search = gitweb_check_feature('search');
+
+ if ($have_search) {
+ my $performed = "";
+ if ($searchtype eq 'author') {
+ $performed = "authored";
+ } elsif ($searchtype eq 'committer') {
+ $performed = "committed";
+ }
+
+ return $cgi->a({-href => href(action=>"search", hash=>$hash,
+ searchtext=>$author,
+ searchtype=>$searchtype), class=>"list",
+ title=>"Search for commits $performed by $author"},
+ $displaytext);
+
+ } else {
+ return $displaytext;
+ }
+}
+
# format the author name of the given commit with the given tag
# the author name is chopped and escaped according to the other
# optional parameters (see chop_str).
@@ -1589,8 +1698,10 @@ sub format_author_html {
my $co = shift;
my $author = chop_and_escape_str($co->{'author_name'}, @_);
return "<$tag class=\"author\">" .
- git_get_avatar($co->{'author_email'}, -pad_after => 1) .
- $author . "$tag>";
+ format_search_author($co->{'author_name'}, "author",
+ git_get_avatar($co->{'author_email'}, -pad_after => 1) .
+ $author) .
+ "$tag>";
}
# format git diff header line, i.e. "diff --(git|combined|cc) ..."
@@ -1955,6 +2066,7 @@ sub get_feed_info {
# returns path to the core git executable and the --git-dir parameter as list
sub git_cmd {
+ $number_of_git_cmds++;
return $GIT, '--git-dir='.$git_dir;
}
@@ -1969,16 +2081,27 @@ sub quote_command {
# get HEAD ref of given project as hash
sub git_get_head_hash {
- my $project = shift;
+ return git_get_full_hash(shift, 'HEAD');
+}
+
+sub git_get_full_hash {
+ return git_get_hash(@_);
+}
+
+sub git_get_short_hash {
+ return git_get_hash(@_, '--short=7');
+}
+
+sub git_get_hash {
+ my ($project, $hash, @options) = @_;
my $o_git_dir = $git_dir;
my $retval = undef;
$git_dir = "$projectroot/$project";
- if (open my $fd, "-|", git_cmd(), "rev-parse", "--verify", "HEAD") {
- my $head = <$fd>;
+ if (open my $fd, '-|', git_cmd(), 'rev-parse',
+ '--verify', '-q', @options, $hash) {
+ $retval = <$fd>;
+ chomp $retval if defined $retval;
close $fd;
- if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
- $retval = $1;
- }
}
if (defined $o_git_dir) {
$git_dir = $o_git_dir;
@@ -2570,7 +2693,7 @@ sub parse_commit_text {
} elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
push @parents, $1;
} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
- $co{'author'} = $1;
+ $co{'author'} = to_utf8($1);
$co{'author_epoch'} = $2;
$co{'author_tz'} = $3;
if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -2580,10 +2703,9 @@ sub parse_commit_text {
$co{'author_name'} = $co{'author'};
}
} elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
- $co{'committer'} = $1;
+ $co{'committer'} = to_utf8($1);
$co{'committer_epoch'} = $2;
$co{'committer_tz'} = $3;
- $co{'committer_name'} = $co{'committer'};
if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
$co{'committer_name'} = $1;
$co{'committer_email'} = $2;
@@ -2751,16 +2873,31 @@ sub parse_ls_tree_line {
my %opts = @_;
my %res;
- #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+ if ($opts{'-l'}) {
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa 16717 panic.c'
+ $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
- $res{'mode'} = $1;
- $res{'type'} = $2;
- $res{'hash'} = $3;
- if ($opts{'-z'}) {
- $res{'name'} = $4;
+ $res{'mode'} = $1;
+ $res{'type'} = $2;
+ $res{'hash'} = $3;
+ $res{'size'} = $4;
+ if ($opts{'-z'}) {
+ $res{'name'} = $5;
+ } else {
+ $res{'name'} = unquote($5);
+ }
} else {
- $res{'name'} = unquote($4);
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
+ $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+
+ $res{'mode'} = $1;
+ $res{'type'} = $2;
+ $res{'hash'} = $3;
+ if ($opts{'-z'}) {
+ $res{'name'} = $4;
+ } else {
+ $res{'name'} = unquote($4);
+ }
}
return wantarray ? %res : \%res;
@@ -3118,7 +3255,7 @@ EOF
print "\n" .
"