'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 = (
'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 {
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
print "</head>\n" .
"<body>\n";
- if (-f $site_header) {
+ if (defined $site_header && -f $site_header) {
insert_file($site_header);
}
print "</div>\n"; # class="page_footer"
}
- if (-f $site_footer) {
+ if (defined $site_footer && -f $site_footer) {
insert_file($site_footer);
}
print qq!<script type="text/javascript" src="$javascript"></script>\n!;
- if ($action eq 'blame_incremental') {
+ if (defined $action &&
+ $action eq 'blame_incremental') {
print qq!<script type="text/javascript">\n!.
qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
qq! "!. href() .qq!");\n!.
qq!</script>\n!;
- } else {
+ } elsif (gitweb_check_feature('javascript-actions')) {
print qq!<script type="text/javascript">\n!.
qq!window.onload = fixLinks;\n!.
qq!</script>\n!;
# 500: The server isn't configured properly, or
# an internal error occurred (e.g. failed assertions caused by bugs), or
# an unknown error occurred (e.g. the git binary died unexpectedly).
+# 503: The server is currently unavailable (because it is overloaded,
+# or down for maintenance). Generally, this is a temporary state.
sub die_error {
my $status = shift || 500;
my $error = shift || "Internal server error";
- my %http_responses = (400 => '400 Bad Request',
- 403 => '403 Forbidden',
- 404 => '404 Not Found',
- 500 => '500 Internal Server Error');
+ my %http_responses = (
+ 400 => '400 Bad Request',
+ 403 => '403 Forbidden',
+ 404 => '404 Not Found',
+ 500 => '500 Internal Server Error',
+ 503 => '503 Service Unavailable',
+ );
git_header_html($http_responses{$status});
print <<EOF;
<div class="page_body">
}
sub format_paging_nav {
- my ($action, $hash, $head, $page, $has_next_link) = @_;
+ my ($action, $page, $has_next_link) = @_;
my $paging_nav;
- if ($hash ne $head || $page) {
- $paging_nav .= $cgi->a({-href => href(action=>$action)}, "HEAD");
- } else {
- $paging_nav .= "HEAD";
- }
-
if ($page > 0) {
- $paging_nav .= " ⋅ " .
+ $paging_nav .=
+ $cgi->a({-href => href(-replay=>1, page=>undef)}, "first") .
+ " ⋅ " .
$cgi->a({-href => href(-replay=>1, page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev");
} else {
- $paging_nav .= " ⋅ prev";
+ $paging_nav .= "first ⋅ prev";
}
if ($has_next_link) {
}
sub print_local_time {
+ print format_local_time(@_);
+}
+
+sub format_local_time {
+ my $localtime = '';
my %date = @_;
if ($date{'hour_local'} < 6) {
- printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
+ $localtime .= sprintf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
} else {
- printf(" (%02d:%02d %s)",
+ $localtime .= sprintf(" (%02d:%02d %s)",
$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
}
+
+ return $localtime;
}
# Outputs the author name and date in long form
sub git_history_body {
# Warning: assumes constant type (blob or tree) during history
- my ($commitlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_;
+ my ($commitlist, $from, $to, $refs, $extra,
+ $file_name, $file_hash, $ftype) = @_;
$from = 0 unless defined $from;
$to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
if ($ftype eq 'blob') {
- my $blob_current = git_get_hash_by_path($hash_base, $file_name);
+ my $blob_current = $file_hash;
my $blob_parent = git_get_hash_by_path($commit, $file_name);
if (defined $blob_current && defined $blob_parent &&
$blob_current ne $blob_parent) {
}
git_header_html();
- if (-f $home_text) {
+ if (defined $home_text && -f $home_text) {
print "<div class=\"index_include\">\n";
insert_file($home_text);
print "</div>\n";
my $formats_nav =
$cgi->a({-href => href(action=>"blob", -replay=>1)},
"blob") .
+ " | ";
+ if ($format eq 'incremental') {
+ $formats_nav .=
+ $cgi->a({-href => href(action=>"blame", javascript=>0, -replay=>1)},
+ "blame") . " (non-incremental)";
+ } else {
+ $formats_nav .=
+ $cgi->a({-href => href(action=>"blame_incremental", -replay=>1)},
+ "blame") . " (incremental)";
+ }
+ $formats_nav .=
" | " .
$cgi->a({-href => href(action=>"history", -replay=>1)},
"history") .
}
sub git_log_generic {
- my ($fmt_name, $body_subr) = @_;
+ my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
my $head = git_get_head_hash($project);
- if (!defined $hash) {
- $hash = $head;
+ if (!defined $base) {
+ $base = $head;
}
if (!defined $page) {
$page = 0;
}
my $refs = git_get_references();
- my $commit_hash = $hash;
- if (defined $hash_parent) {
- $commit_hash = "$hash_parent..$hash";
+ my $commit_hash = $base;
+ if (defined $parent) {
+ $commit_hash = "$parent..$base";
+ }
+ my @commitlist =
+ parse_commits($commit_hash, 101, (100 * $page),
+ defined $file_name ? ($file_name, "--full-history") : ());
+
+ my $ftype;
+ if (!defined $file_hash && defined $file_name) {
+ # some commits could have deleted file in question,
+ # and not have it in tree, but one of them has to have it
+ for (my $i = 0; $i < @commitlist; $i++) {
+ $file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+ last if defined $file_hash;
+ }
+ }
+ if (defined $file_hash) {
+ $ftype = git_get_type($file_hash);
+ }
+ if (defined $file_name && !defined $ftype) {
+ die_error(500, "Unknown type of object");
+ }
+ my %co;
+ if (defined $file_name) {
+ %co = parse_commit($base)
+ or die_error(404, "Unknown commit object");
}
- my @commitlist = parse_commits($commit_hash, 101, (100 * $page));
- my $paging_nav = format_paging_nav($fmt_name, $hash, $head,
- $page, $#commitlist >= 100);
+
+ my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100);
my $next_link = '';
if ($#commitlist >= 100) {
$next_link =
-accesskey => "n", -title => "Alt-n"}, "next");
}
my $patch_max = gitweb_get_feature('patches');
- if ($patch_max) {
+ if ($patch_max && !defined $file_name) {
if ($patch_max < 0 || @commitlist <= $patch_max) {
$paging_nav .= " ⋅ " .
$cgi->a({-href => href(action=>"patches", -replay=>1)},
git_header_html();
git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav);
- git_print_header_div('summary', $project);
+ if (defined $file_name) {
+ git_print_header_div('commit', esc_html($co{'title'}), $base);
+ } else {
+ git_print_header_div('summary', $project)
+ }
+ git_print_page_path($file_name, $ftype, $hash_base)
+ if (defined $file_name);
- $body_subr->(\@commitlist, 0, 99, $refs, $next_link);
+ $body_subr->(\@commitlist, 0, 99, $refs, $next_link,
+ $file_name, $file_hash, $ftype);
git_footer_html();
}
sub git_log {
- git_log_generic('log', \&git_log_body);
+ git_log_generic('log', \&git_log_body,
+ $hash, $hash_parent);
}
sub git_commit {
}
sub git_history {
- if (!defined $hash_base) {
- $hash_base = git_get_head_hash($project);
- }
- if (!defined $page) {
- $page = 0;
- }
- my $ftype;
- my %co = parse_commit($hash_base)
- or die_error(404, "Unknown commit object");
-
- my $refs = git_get_references();
- my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-
- my @commitlist = parse_commits($hash_base, 101, (100 * $page),
- $file_name, "--full-history")
- or die_error(404, "No such file or directory on given branch");
-
- if (!defined $hash && defined $file_name) {
- # some commits could have deleted file in question,
- # and not have it in tree, but one of them has to have it
- for (my $i = 0; $i <= @commitlist; $i++) {
- $hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
- last if defined $hash;
- }
- }
- if (defined $hash) {
- $ftype = git_get_type($hash);
- }
- if (!defined $ftype) {
- die_error(500, "Unknown type of object");
- }
-
- my $paging_nav = '';
- if ($page > 0) {
- $paging_nav .=
- $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
- file_name=>$file_name)},
- "first");
- $paging_nav .= " ⋅ " .
- $cgi->a({-href => href(-replay=>1, page=>$page-1),
- -accesskey => "p", -title => "Alt-p"}, "prev");
- } else {
- $paging_nav .= "first";
- $paging_nav .= " ⋅ prev";
- }
- my $next_link = '';
- if ($#commitlist >= 100) {
- $next_link =
- $cgi->a({-href => href(-replay=>1, page=>$page+1),
- -accesskey => "n", -title => "Alt-n"}, "next");
- $paging_nav .= " ⋅ $next_link";
- } else {
- $paging_nav .= " ⋅ next";
- }
-
- git_header_html();
- git_print_page_nav('history','', $hash_base,$co{'tree'},$hash_base, $paging_nav);
- git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
- git_print_page_path($file_name, $ftype, $hash_base);
-
- git_history_body(\@commitlist, 0, 99,
- $refs, $hash_base, $ftype, $next_link);
-
- git_footer_html();
+ git_log_generic('history', \&git_history_body,
+ $hash_base, $hash_parent_base,
+ $file_name, $hash);
}
sub git_search {
}
sub git_shortlog {
- git_log_generic('shortlog', \&git_shortlog_body);
+ git_log_generic('shortlog', \&git_shortlog_body,
+ $hash, $hash_parent);
}
## ......................................................................