Git development
 help / color / mirror / Atom feed
* [PATCH 1/9] gitweb: Load checking
From: John 'Warthog9' Hawley @ 2010-01-14  1:22 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-1-git-send-email-warthog9@eaglescrag.net>

From: John 'Warthog9' Hawley <warthog9@kernel.org>

This changes slightly the behavior of gitweb, so that it verifies
that the box isn't inundated with before attempting to serve gitweb.
If the box is overloaded, it basically returns a 503 Server Unavailable
until the load falls below the defined threshold.  This helps dramatically
if you have a box that's I/O bound, reaches a certain load and you
don't want gitweb, the I/O hog that it is, increasing the pain the
server is already undergoing.

This behavior is controlled by $maxload configuration variable.
Default is a load of 300, which for most cases should never be hit.
Unset it (set it to undefined value, i.e. undef) to turn off checking.

Currently it requires that '/proc/loadavg' file exists, otherwise the
load check is bypassed (load is taken to be 0).  So platforms that do
not implement '/proc/loadavg' currently cannot use this feature.
(provisions are included for additional checks to be added by others)

Signed-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
 gitweb/README      |    7 ++++++-
 gitweb/gitweb.perl |   45 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/gitweb/README b/gitweb/README
index e34ee79..6c2c8e1 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -174,7 +174,7 @@ not include variables usually directly set during build):
    Base URL for relative URLs in pages generated by gitweb,
    (e.g. $logo, $favicon, @stylesheets if they are relative URLs),
    needed and used only for URLs with nonempty PATH_INFO via
-   <base href="$base_url>.  Usually gitweb sets its value correctly,
+   <base href="$base_url">.  Usually gitweb sets its value correctly,
    and there is no need to set this variable, e.g. to $my_uri or "/".
  * $home_link
    Target of the home link on top of all pages (the first part of view
@@ -228,6 +228,11 @@ not include variables usually directly set during build):
    repositories from launching cross-site scripting (XSS) attacks.  Set this
    to true if you don't trust the content of your repositories. The default
    is false.
+ * $maxload
+   Used to set the maximum load that we will still respond to gitweb queries.
+   If server load exceed this value then return "503 Service Unavaliable" error.
+   Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
+   undefined value to turn it off.  The default is 300.
 
 
 Projects list file format
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 7e477af..0a07d3a 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -221,6 +221,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 = (
@@ -551,6 +557,32 @@ 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;
+}
+
+if (defined $maxload && get_loadavg() > $maxload) {
+	die_error(503, "The load average on the server is too high");
+}
+
 # version of the core git binary
 our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 $number_of_git_cmds++;
@@ -3354,14 +3386,19 @@ sub git_footer_html {
 # 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">
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 6/9] gitweb: add a get function to compliment print_sort_th
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-6-git-send-email-warthog9@eaglescrag.net>

This adds a get function for print_sort_th so that the basic
function can be used outside of their straight printing operation.
---
 gitweb/gitweb.perl |   11 +++++++++--
 1 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d38aad6..07fdeb5 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4375,17 +4375,24 @@ sub fill_project_list_info {
 # print 'sort by' <th> element, generating 'sort by $name' replay link
 # if that order is not selected
 sub print_sort_th {
+	print get_sort_th(@_);
+}
+
+sub get_sort_th {
 	my ($name, $order, $header) = @_;
+	my $sortth = "";
 	$header ||= ucfirst($name);
 
 	if ($order eq $name) {
-		print "<th>$header</th>\n";
+		$sortth .= "<th>$header</th>\n";
 	} else {
-		print "<th>" .
+		$sortth .= "<th>" .
 		      $cgi->a({-href => href(-replay=>1, order=>$name),
 		               -class => "header"}, $header) .
 		      "</th>\n";
 	}
+
+	return $sortth;
 }
 
 sub git_project_list_body {
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 3/9] gitweb: Add option to force version match
From: John 'Warthog9' Hawley @ 2010-01-14  1:22 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-3-git-send-email-warthog9@eaglescrag.net>

This adds $git_versions_must_match variable, which is set to true,
checks that we are running on the same version of git that we
shipped with, and if not throw '500 Internal Server Error' error.
What is checked is the version of gitweb (embedded in building
gitweb.cgi), against version of runtime git binary used.

Gitweb can usually run with a mismatched git install.  This is more
here to give an obvious warning as to whats going on vs. silently
failing.

By default this feature is turned on.

Signed-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
 gitweb/README      |    3 +++
 gitweb/gitweb.perl |   23 +++++++++++++++++++++++
 2 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/gitweb/README b/gitweb/README
index 6c2c8e1..03151d2 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -233,6 +233,9 @@ not include variables usually directly set during build):
    If server load exceed this value then return "503 Service Unavaliable" error.
    Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
    undefined value to turn it off.  The default is 300.
+ * $git_versions_must_match
+   If set, gitweb fails with 500 Internal Server Error if the version of gitweb
+   doesn't match version of git binary.  The default is true.
 
 
 Projects list file format
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 8298de5..b41bc33 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -221,6 +221,9 @@ our %avatar_size = (
 	'double'  => 32
 );
 
+# If it is true, exit if gitweb version and git binary version don't match
+our $git_versions_must_match = 1;
+
 # 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.
@@ -587,6 +590,26 @@ if (defined $maxload && get_loadavg() > $maxload) {
 our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 $number_of_git_cmds++;
 
+# Throw an error if git versions does not match, if $git_versions_must_match is true.
+if ($git_versions_must_match &&
+    $git_version ne $version) {
+	my $admin_contact =
+		defined $ENV{'SERVER_ADMIN'} ? ", $ENV{'SERVER_ADMIN'}," : '';
+	my $err_msg = <<EOT;
+<h1 align="center">*** Warning ***</h1>
+<p>
+This version of gitweb was compiled for <b>@{[esc_html($version)]}</b>,
+however git version <b>@{[esc_html($git_version)]}</b> was found on server,
+and administrator requested strict version checking.
+</p>
+<p>
+Please contact the server administrator${admin_contact} to either configure
+gitweb to allow mismatched versions, or update git or gitweb installation.
+</p>
+EOT
+	die_error(500, 'Internal server error', $err_msg);
+}
+
 $projects_list ||= $projectroot;
 
 # ======================================================================
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 0/9] Gitweb caching v5
From: John 'Warthog9' Hawley @ 2010-01-14  1:22 UTC (permalink / raw)
  To: git

Afternoon everyone,
 
This is the latest incarnation of gitweb w/ caching.  This is
finally at the point where it should probably start either being
considered for inclusion or mainline, or I need to accept that this
will never get in and more perminantely fork (as is the case with
Fedora where this is going in as gitweb-caching as a parrallel rpm
package).

That said this brings the base up to mainline (again), it updates a
number of elements in the caching engine, and this is a much cleaner
break-out of the tree vs. what I am currently developing against.

v5:
	- Missed a couple of things that were in my local tree, and
	  added them back in.
	- Split up the die_error and the version matching patch
	- Set version matching to be on by default - otherwise this
	  really is code that will never get checked, or at best
	  enabled by default by distributions
	- Added a minor code cleanup with respect to $site_header
	  that was already in my tree
	- Applied against a more recent git tree vs. 1.6.6-rc2
	- Removed breakout patch for now (did that in v4 actually)
	  and will deal with that separately 

	http://git.kernel.org/?p=git/warthog9/gitweb.git;a=shortlog;h=refs/heads/gitweb-ml-v5

v4:
	- major re-working of the caching layer to use file handle
	  redirection instead of buffering output
	- other minor improvements

	http://git.kernel.org/?p=git/warthog9/gitweb.git;a=shortlog;h=refs/heads/gitweb-ml-v4
v3:
	- various minor re-works based on mailing list feedback,
	  this series was not sent to the mailing list.
v2:
	- Better breakout
	- You can actually disable the cache now

- John 'Warthog9' Hawley 



John 'Warthog9' Hawley (9):
  gitweb: Load checking
  gitweb: change die_error to take "extra" argument for extended die
    information
  gitweb: Add option to force version match
  gitweb: Makefile improvements
  gitweb: add a get function to compliment print_local_time
  gitweb: add a get function to compliment print_sort_th
  gitweb: cleanup error message produced by undefined $site_header
  gitweb: Convert output to using indirect file handle
  gitweb: File based caching layer (from git.kernel.org)

 Makefile           |   65 +---
 gitweb/Makefile    |  129 +++++++
 gitweb/README      |   10 +-
 gitweb/cache.pm    |  283 ++++++++++++++
 gitweb/gitweb.css  |    6 +
 gitweb/gitweb.perl | 1034 ++++++++++++++++++++++++++++++----------------------
 6 files changed, 1030 insertions(+), 497 deletions(-)
 create mode 100644 gitweb/Makefile
 create mode 100644 gitweb/cache.pm

^ permalink raw reply

* [PATCH 2/9] gitweb: change die_error to take "extra" argument for extended die information
From: John 'Warthog9' Hawley @ 2010-01-14  1:22 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-2-git-send-email-warthog9@eaglescrag.net>

This is a small change that just adds a 3rd, optional, parameter to die_error
that allows for extended error information to be output along with what the
error was.
---
 gitweb/gitweb.perl |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 0a07d3a..8298de5 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3391,6 +3391,7 @@ sub git_footer_html {
 sub die_error {
 	my $status = shift || 500;
 	my $error = shift || "Internal server error";
+	my $extra = shift;
 
 	my %http_responses = (
 		400 => '400 Bad Request',
@@ -3405,8 +3406,13 @@ sub die_error {
 <br /><br />
 $status - $error
 <br />
-</div>
 EOF
+	if (defined $extra) {
+		print "<hr />\n" .
+			"$extra\n";
+	}
+	print "</div>\n";
+
 	git_footer_html();
 	exit;
 }
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 5/9] gitweb: add a get function to compliment print_local_time
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-5-git-send-email-warthog9@eaglescrag.net>

This adds a get function for print_local_time so that the basic
function can be used outside of their straight printing operation.
---
 gitweb/gitweb.perl |   11 +++++++++--
 1 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b41bc33..d38aad6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3537,14 +3537,21 @@ sub git_print_header_div {
 }
 
 sub print_local_time {
+	print get_local_time(@_);
+}
+
+sub get_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
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 8/9] gitweb: Convert output to using indirect file handle
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-8-git-send-email-warthog9@eaglescrag.net>

This converts the output handling of gitweb to using an indirect
file handle.  This is in preparation to add the caching layer.  This
is a slight modification to the way I was originally doing it by
passing the output around.  This should be a nop and this shouldn't
change the behavior of gitweb.  This does leave error reporting
functions (die_error specifically) continuing to output directly
as I want to garauntee those will report their errors regardless of
what may be going on with respect to the rest of the output.
---
 gitweb/gitweb.perl |  880 ++++++++++++++++++++++++++--------------------------
 1 files changed, 448 insertions(+), 432 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c4a177d..8bb323c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -450,6 +450,13 @@ our %feature = (
 		'default' => [0]},
 );
 
+# Basic file handler for all of gitweb, there are two of them.  The first
+# is the basic text/html file handler which is used for everything other
+# then the binary files, that uses a separate file handler though
+# these are both set to STDOUT for the time being.
+our $output_handler = *STDOUT;
+our $output_handler_bin = *STDOUT;
+
 sub gitweb_get_feature {
 	my ($name) = @_;
 	return unless exists $feature{$name};
@@ -3081,7 +3088,7 @@ sub insert_file {
 	my $filename = shift;
 
 	open my $fd, '<', $filename;
-	print map { to_utf8($_) } <$fd>;
+	print {$output_handler} map { to_utf8($_) } <$fd>;
 	close $fd;
 }
 
@@ -3198,10 +3205,10 @@ sub git_header_html {
 	} else {
 		$content_type = 'text/html';
 	}
-	print $cgi->header(-type=>$content_type, -charset => 'utf-8',
+	print {$output_handler} $cgi->header(-type=>$content_type, -charset => 'utf-8',
 	                   -status=> $status, -expires => $expires);
 	my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
-	print <<EOF;
+	print {$output_handler} <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
@@ -3216,16 +3223,16 @@ EOF
 	# the stylesheet, favicon etc urls won't work correctly with path_info
 	# unless we set the appropriate base URL
 	if ($ENV{'PATH_INFO'}) {
-		print "<base href=\"".esc_url($base_url)."\" />\n";
+		print {$output_handler} "<base href=\"".esc_url($base_url)."\" />\n";
 	}
 	# print out each stylesheet that exist, providing backwards capability
 	# for those people who defined $stylesheet in a config file
 	if (defined $stylesheet) {
-		print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+		print {$output_handler} '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
 	} else {
 		foreach my $stylesheet (@stylesheets) {
 			next unless $stylesheet;
-			print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+			print {$output_handler} '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
 		}
 	}
 	if (defined $project) {
@@ -3244,7 +3251,7 @@ EOF
 
 			$href_params{'action'} = $type;
 			$link_attr{'-href'} = href(%href_params);
-			print "<link ".
+			print {$output_handler} "<link ".
 			      "rel=\"$link_attr{'-rel'}\" ".
 			      "title=\"$link_attr{'-title'}\" ".
 			      "href=\"$link_attr{'-href'}\" ".
@@ -3254,7 +3261,7 @@ EOF
 			$href_params{'extra_options'} = '--no-merges';
 			$link_attr{'-href'} = href(%href_params);
 			$link_attr{'-title'} .= ' (no merges)';
-			print "<link ".
+			print {$output_handler} "<link ".
 			      "rel=\"$link_attr{'-rel'}\" ".
 			      "title=\"$link_attr{'-title'}\" ".
 			      "href=\"$link_attr{'-href'}\" ".
@@ -3263,37 +3270,37 @@ EOF
 		}
 
 	} else {
-		printf('<link rel="alternate" title="%s projects list" '.
+		printf( {$output_handler} '<link rel="alternate" title="%s projects list" '.
 		       'href="%s" type="text/plain; charset=utf-8" />'."\n",
 		       $site_name, href(project=>undef, action=>"project_index"));
-		printf('<link rel="alternate" title="%s projects feeds" '.
+		printf( {$output_handler} '<link rel="alternate" title="%s projects feeds" '.
 		       'href="%s" type="text/x-opml" />'."\n",
 		       $site_name, href(project=>undef, action=>"opml"));
 	}
 	if (defined $favicon) {
-		print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
+		print {$output_handler} qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
 	}
 
-	print "</head>\n" .
+	print {$output_handler} "</head>\n" .
 	      "<body>\n";
 
 	if ($site_header && -f $site_header) {
 		insert_file($site_header);
 	}
 
-	print "<div class=\"page_header\">\n" .
+	print {$output_handler} "<div class=\"page_header\">\n" .
 	      $cgi->a({-href => esc_url($logo_url),
 	               -title => $logo_label},
 	              qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
-	print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+	print {$output_handler} $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
 	if (defined $project) {
-		print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
+		print {$output_handler} $cgi->a({-href => href(action=>"summary")}, esc_html($project));
 		if (defined $action) {
-			print " / $action";
+			print {$output_handler} " / $action";
 		}
-		print "\n";
+		print {$output_handler} "\n";
 	}
-	print "</div>\n";
+	print {$output_handler} "</div>\n";
 
 	my $have_search = gitweb_check_feature('search');
 	if (defined $project && $have_search) {
@@ -3313,7 +3320,7 @@ EOF
 		if ($use_pathinfo) {
 			$action .= "/".esc_url($project);
 		}
-		print $cgi->startform(-method => "get", -action => $action) .
+		print {$output_handler} $cgi->startform(-method => "get", -action => $action) .
 		      "<div class=\"search\">\n" .
 		      (!$use_pathinfo &&
 		      $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
@@ -3336,11 +3343,11 @@ EOF
 sub git_footer_html {
 	my $feed_class = 'rss_logo';
 
-	print "<div class=\"page_footer\">\n";
+	print {$output_handler} "<div class=\"page_footer\">\n";
 	if (defined $project) {
 		my $descr = git_get_project_description($project);
 		if (defined $descr) {
-			print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
+			print {$output_handler} "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
 		}
 
 		my %href_params = get_feed_info();
@@ -3351,22 +3358,22 @@ sub git_footer_html {
 
 		foreach my $format qw(RSS Atom) {
 			$href_params{'action'} = lc($format);
-			print $cgi->a({-href => href(%href_params),
+			print {$output_handler} $cgi->a({-href => href(%href_params),
 			              -title => "$href_params{'-title'} $format feed",
 			              -class => $feed_class}, $format)."\n";
 		}
 
 	} else {
-		print $cgi->a({-href => href(project=>undef, action=>"opml"),
+		print {$output_handler} $cgi->a({-href => href(project=>undef, action=>"opml"),
 		              -class => $feed_class}, "OPML") . " ";
-		print $cgi->a({-href => href(project=>undef, action=>"project_index"),
+		print {$output_handler} $cgi->a({-href => href(project=>undef, action=>"project_index"),
 		              -class => $feed_class}, "TXT") . "\n";
 	}
-	print "</div>\n"; # class="page_footer"
+	print {$output_handler} "</div>\n"; # class="page_footer"
 
 	if (defined $t0 && gitweb_check_feature('timed')) {
-		print "<div id=\"generating_info\">\n";
-		print 'This page took '.
+		print {$output_handler} "<div id=\"generating_info\">\n";
+		print {$output_handler} 'This page took '.
 		      '<span id="generating_time" class="time_span">'.
 		      Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
 		      ' seconds </span>'.
@@ -3375,26 +3382,26 @@ sub git_footer_html {
 		      $number_of_git_cmds.
 		      '</span> git commands '.
 		      " to generate.\n";
-		print "</div>\n"; # class="page_footer"
+		print {$output_handler} "</div>\n"; # class="page_footer"
 	}
 
 	if (-f $site_footer) {
 		insert_file($site_footer);
 	}
 
-	print qq!<script type="text/javascript" src="$javascript"></script>\n!;
+	print {$output_handler} qq!<script type="text/javascript" src="$javascript"></script>\n!;
 	if ($action eq 'blame_incremental') {
-		print qq!<script type="text/javascript">\n!.
+		print {$output_handler} qq!<script type="text/javascript">\n!.
 		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
 		      qq!           "!. href() .qq!");\n!.
 		      qq!</script>\n!;
 	} elsif (gitweb_check_feature('javascript-actions')) {
-		print qq!<script type="text/javascript">\n!.
+		print {$output_handler} qq!<script type="text/javascript">\n!.
 		      qq!window.onload = fixLinks;\n!.
 		      qq!</script>\n!;
 	}
 
-	print "</body>\n" .
+	print {$output_handler} "</body>\n" .
 	      "</html>";
 }
 
@@ -3416,6 +3423,14 @@ sub die_error {
 	my $error = shift || "Internal server error";
 	my $extra = shift;
 
+	# The output handlers for die_error need to be reset to STDOUT
+	# so that half the message isn't being output to random and 
+	# half to STDOUT as expected.  This is mainly for the benefit
+	# of using git_header_html() and git_footer_html() since those
+	# internaly use the indirect print handler.
+	$output_handler = *STDOUT;
+	$output_handler_bin = *STDOUT;
+
 	my %http_responses = (
 		400 => '400 Bad Request',
 		403 => '403 Forbidden',
@@ -3484,12 +3499,12 @@ sub git_print_page_nav {
 		$arg{$label}{'_href'} = $link;
 	}
 
-	print "<div class=\"page_nav\">\n" .
+	print {$output_handler} "<div class=\"page_nav\">\n" .
 		(join " | ",
 		 map { $_ eq $current ?
 		       $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
 		 } @navs);
-	print "<br/>\n$extra<br/>\n" .
+	print {$output_handler} "<br/>\n$extra<br/>\n" .
 	      "</div>\n";
 }
 
@@ -3530,14 +3545,14 @@ sub git_print_header_div {
 	$args{'hash'} = $hash if $hash;
 	$args{'hash_base'} = $hash_base if $hash_base;
 
-	print "<div class=\"header\">\n" .
+	print {$output_handler} "<div class=\"header\">\n" .
 	      $cgi->a({-href => href(%args), -class => "title"},
 	      $title ? $title : $action) .
 	      "\n</div>\n";
 }
 
 sub print_local_time {
-	print get_local_time(@_);
+	print {$output_handler} get_local_time(@_);
 }
 
 sub get_local_time {
@@ -3562,11 +3577,11 @@ sub git_print_authorship {
 	my $author = $co->{'author_name'};
 
 	my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
-	print "<$tag class=\"author_date\">" .
+	print {$output_handler} "<$tag class=\"author_date\">" .
 	      format_search_author($author, "author", esc_html($author)) .
 	      " [$ad{'rfc2822'}";
-	print_local_time(%ad) if ($opts{-localtime});
-	print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
+	print {$output_handler} get_local_time(%ad) if ($opts{-localtime});
+	print {$output_handler} "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
 		  . "</$tag>\n";
 }
 
@@ -3582,7 +3597,7 @@ sub git_print_authorship_rows {
 	@people = ('author', 'committer') unless @people;
 	foreach my $who (@people) {
 		my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
-		print "<tr><td>$who</td><td>" .
+		print {$output_handler} "<tr><td>$who</td><td>" .
 		      format_search_author($co->{"${who}_name"}, $who,
 			       esc_html($co->{"${who}_name"})) . " " .
 		      format_search_author($co->{"${who}_email"}, $who,
@@ -3592,8 +3607,8 @@ sub git_print_authorship_rows {
 		      "</td></tr>\n" .
 		      "<tr>" .
 		      "<td></td><td> $wd{'rfc2822'}";
-		print_local_time(%wd);
-		print "</td>" .
+		print {$output_handler} get_local_time(%wd);
+		print {$output_handler} "</td>" .
 		      "</tr>\n";
 	}
 }
@@ -3603,11 +3618,10 @@ sub git_print_page_path {
 	my $type = shift;
 	my $hb = shift;
 
-
-	print "<div class=\"page_path\">";
-	print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
+	print {$output_handler} "<div class=\"page_path\">";
+	print {$output_handler} $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
 	              -title => 'tree root'}, to_utf8("[$project]"));
-	print " / ";
+	print {$output_handler} " / ";
 	if (defined $name) {
 		my @dirname = split '/', $name;
 		my $basename = pop @dirname;
@@ -3615,25 +3629,25 @@ sub git_print_page_path {
 
 		foreach my $dir (@dirname) {
 			$fullname .= ($fullname ? '/' : '') . $dir;
-			print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
+			print {$output_handler} $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
 			                             hash_base=>$hb),
 			              -title => $fullname}, esc_path($dir));
-			print " / ";
+			print {$output_handler} " / ";
 		}
 		if (defined $type && $type eq 'blob') {
-			print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
+			print {$output_handler} $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
 			                             hash_base=>$hb),
 			              -title => $name}, esc_path($basename));
 		} elsif (defined $type && $type eq 'tree') {
-			print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
+			print {$output_handler} $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
 			                             hash_base=>$hb),
 			              -title => $name}, esc_path($basename));
-			print " / ";
+			print {$output_handler} " / ";
 		} else {
-			print esc_path($basename);
+			print {$output_handler} esc_path($basename);
 		}
 	}
-	print "<br/></div>\n";
+	print {$output_handler} "<br/></div>\n";
 }
 
 sub git_print_log {
@@ -3657,7 +3671,7 @@ sub git_print_log {
 			$signoff = 1;
 			$empty = 0;
 			if (! $opts{'-remove_signoff'}) {
-				print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
+				print {$output_handler} "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
 				next;
 			} else {
 				# remove signoff lines
@@ -3676,12 +3690,12 @@ sub git_print_log {
 			$empty = 0;
 		}
 
-		print format_log_line_html($line) . "<br/>\n";
+		print {$output_handler} format_log_line_html($line) . "<br/>\n";
 	}
 
 	if ($opts{'-final_empty_line'}) {
 		# end with single empty line
-		print "<br/>\n" unless $empty;
+		print {$output_handler} "<br/>\n" unless $empty;
 	}
 }
 
@@ -3754,12 +3768,12 @@ sub git_print_tree_entry {
 	# the mode of the entry, list is the name of the entry, an href,
 	# and link is the action links of the entry.
 
-	print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+	print {$output_handler} "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
 	if (exists $t->{'size'}) {
-		print "<td class=\"size\">$t->{'size'}</td>\n";
+		print {$output_handler} "<td class=\"size\">$t->{'size'}</td>\n";
 	}
 	if ($t->{'type'} eq "blob") {
-		print "<td class=\"list\">" .
+		print {$output_handler} "<td class=\"list\">" .
 			$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
 			                       file_name=>"$basedir$t->{'name'}", %base_key),
 			        -class => "list"}, esc_path($t->{'name'}));
@@ -3768,71 +3782,71 @@ sub git_print_tree_entry {
 			if ($link_target) {
 				my $norm_target = normalize_link_target($link_target, $basedir);
 				if (defined $norm_target) {
-					print " -> " .
+					print {$output_handler} " -> " .
 					      $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
 					                             file_name=>$norm_target),
 					               -title => $norm_target}, esc_path($link_target));
 				} else {
-					print " -> " . esc_path($link_target);
+					print {$output_handler} " -> " . esc_path($link_target);
 				}
 			}
 		}
-		print "</td>\n";
-		print "<td class=\"link\">";
-		print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+		print {$output_handler} "</td>\n";
+		print {$output_handler} "<td class=\"link\">";
+		print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
 		                             file_name=>"$basedir$t->{'name'}", %base_key)},
 		              "blob");
 		if ($have_blame) {
-			print " | " .
+			print {$output_handler} " | " .
 			      $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
 			                             file_name=>"$basedir$t->{'name'}", %base_key)},
 			              "blame");
 		}
 		if (defined $hash_base) {
-			print " | " .
+			print {$output_handler} " | " .
 			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
 			                             hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
 			              "history");
 		}
-		print " | " .
+		print {$output_handler} " | " .
 			$cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
 			                       file_name=>"$basedir$t->{'name'}")},
 			        "raw");
-		print "</td>\n";
+		print {$output_handler} "</td>\n";
 
 	} elsif ($t->{'type'} eq "tree") {
-		print "<td class=\"list\">";
-		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+		print {$output_handler} "<td class=\"list\">";
+		print {$output_handler} $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
 		                             file_name=>"$basedir$t->{'name'}",
 		                             %base_key)},
 		              esc_path($t->{'name'}));
-		print "</td>\n";
-		print "<td class=\"link\">";
-		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+		print {$output_handler} "</td>\n";
+		print {$output_handler} "<td class=\"link\">";
+		print {$output_handler} $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
 		                             file_name=>"$basedir$t->{'name'}",
 		                             %base_key)},
 		              "tree");
 		if (defined $hash_base) {
-			print " | " .
+			print {$output_handler} " | " .
 			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
 			                             file_name=>"$basedir$t->{'name'}")},
 			              "history");
 		}
-		print "</td>\n";
+		print {$output_handler} "</td>\n";
 	} else {
 		# unknown object: we can only present history for it
 		# (this includes 'commit' object, i.e. submodule support)
-		print "<td class=\"list\">" .
+		print {$output_handler} "<td class=\"list\">" .
 		      esc_path($t->{'name'}) .
 		      "</td>\n";
-		print "<td class=\"link\">";
+		print {$output_handler} "<td class=\"link\">";
 		if (defined $hash_base) {
-			print $cgi->a({-href => href(action=>"history",
+			print {$output_handler} $cgi->a({-href => href(action=>"history",
 			                             hash_base=>$hash_base,
 			                             file_name=>"$basedir$t->{'name'}")},
 			              "history");
 		}
-		print "</td>\n";
+		print {$output_handler} "</td>\n";
 	}
 }
 
@@ -3879,13 +3893,13 @@ sub git_difftree_body {
 	my ($difftree, $hash, @parents) = @_;
 	my ($parent) = $parents[0];
 	my $have_blame = gitweb_check_feature('blame');
-	print "<div class=\"list_head\">\n";
+	print {$output_handler} "<div class=\"list_head\">\n";
 	if ($#{$difftree} > 10) {
-		print(($#{$difftree} + 1) . " files changed:\n");
+		print {$output_handler} (($#{$difftree} + 1) . " files changed:\n");
 	}
-	print "</div>\n";
+	print {$output_handler} "</div>\n";
 
-	print "<table class=\"" .
+	print {$output_handler} "<table class=\"" .
 	      (@parents > 1 ? "combined " : "") .
 	      "diff_tree\">\n";
 
@@ -3893,11 +3907,11 @@ sub git_difftree_body {
 	my $has_header = @$difftree && @parents > 1 && $action eq 'commitdiff';
 	if ($has_header) {
 		# table header
-		print "<thead><tr>\n" .
+		print {$output_handler} "<thead><tr>\n" .
 		       "<th></th><th></th>\n"; # filename, patchN link
 		for (my $i = 0; $i < @parents; $i++) {
 			my $par = $parents[$i];
-			print "<th>" .
+			print {$output_handler} "<th>" .
 			      $cgi->a({-href => href(action=>"commitdiff",
 			                             hash=>$hash, hash_parent=>$par),
 			               -title => 'commitdiff to parent number ' .
@@ -3905,7 +3919,7 @@ sub git_difftree_body {
 			              $i+1) .
 			      "&nbsp;</th>\n";
 		}
-		print "</tr></thead>\n<tbody>\n";
+		print {$output_handler} "</tr></thead>\n<tbody>\n";
 	}
 
 	my $alternate = 1;
@@ -3914,9 +3928,9 @@ sub git_difftree_body {
 		my $diff = parsed_difftree_line($line);
 
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 
@@ -3927,14 +3941,14 @@ sub git_difftree_body {
 
 			if (!is_deleted($diff)) {
 				# file exists in the result (child) commit
-				print "<td>" .
+				print {$output_handler} "<td>" .
 				      $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 				                             file_name=>$diff->{'to_file'},
 				                             hash_base=>$hash),
 				              -class => "list"}, esc_path($diff->{'to_file'})) .
 				      "</td>\n";
 			} else {
-				print "<td>" .
+				print {$output_handler} "<td>" .
 				      esc_path($diff->{'to_file'}) .
 				      "</td>\n";
 			}
@@ -3942,7 +3956,7 @@ sub git_difftree_body {
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print "<td class=\"link\">" .
+				print {$output_handler} "<td class=\"link\">" .
 				      $cgi->a({-href => "#patch$patchno"}, "patch") .
 				      " | " .
 				      "</td>\n";
@@ -3960,9 +3974,9 @@ sub git_difftree_body {
 				$not_deleted ||= ($status ne 'D');
 
 				if ($status eq 'A') {
-					print "<td  class=\"link\" align=\"right\"> | </td>\n";
+					print {$output_handler} "<td  class=\"link\" align=\"right\"> | </td>\n";
 				} elsif ($status eq 'D') {
-					print "<td class=\"link\">" .
+					print {$output_handler} "<td class=\"link\">" .
 					      $cgi->a({-href => href(action=>"blob",
 					                             hash_base=>$hash,
 					                             hash=>$from_hash,
@@ -3971,11 +3985,11 @@ sub git_difftree_body {
 					      " | </td>\n";
 				} else {
 					if ($diff->{'to_id'} eq $from_hash) {
-						print "<td class=\"link nochange\">";
+						print {$output_handler} "<td class=\"link nochange\">";
 					} else {
-						print "<td class=\"link\">";
+						print {$output_handler} "<td class=\"link\">";
 					}
-					print $cgi->a({-href => href(action=>"blobdiff",
+					print {$output_handler} $cgi->a({-href => href(action=>"blobdiff",
 					                             hash=>$diff->{'to_id'},
 					                             hash_parent=>$from_hash,
 					                             hash_base=>$hash,
@@ -3987,24 +4001,24 @@ sub git_difftree_body {
 				}
 			}
 
-			print "<td class=\"link\">";
+			print {$output_handler} "<td class=\"link\">";
 			if ($not_deleted) {
-				print $cgi->a({-href => href(action=>"blob",
+				print {$output_handler} $cgi->a({-href => href(action=>"blob",
 				                             hash=>$diff->{'to_id'},
 				                             file_name=>$diff->{'to_file'},
 				                             hash_base=>$hash)},
 				              "blob");
-				print " | " if ($has_history);
+				print {$output_handler} " | " if ($has_history);
 			}
 			if ($has_history) {
-				print $cgi->a({-href => href(action=>"history",
+				print {$output_handler} $cgi->a({-href => href(action=>"history",
 				                             file_name=>$diff->{'to_file'},
 				                             hash_base=>$hash)},
 				              "history");
 			}
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 
-			print "</tr>\n";
+			print {$output_handler} "</tr>\n";
 			next; # instead of 'else' clause, to avoid extra indent
 		}
 		# else ordinary diff
@@ -4030,51 +4044,51 @@ sub git_difftree_body {
 			my $mode_chng = "<span class=\"file_status new\">[new $to_file_type";
 			$mode_chng   .= " with mode: $to_mode_str" if $to_mode_str;
 			$mode_chng   .= "]</span>";
-			print "<td>";
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+			print {$output_handler} "<td>";
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff->{'file'}),
 			              -class => "list"}, esc_path($diff->{'file'}));
-			print "</td>\n";
-			print "<td>$mode_chng</td>\n";
-			print "<td class=\"link\">";
+			print {$output_handler} "</td>\n";
+			print {$output_handler} "<td>$mode_chng</td>\n";
+			print {$output_handler} "<td class=\"link\">";
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch");
-				print " | ";
+				print {$output_handler} $cgi->a({-href => "#patch$patchno"}, "patch");
+				print {$output_handler} " | ";
 			}
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff->{'file'})},
 			              "blob");
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 
 		} elsif ($diff->{'status'} eq "D") { # deleted
 			my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>";
-			print "<td>";
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+			print {$output_handler} "<td>";
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
 			                             hash_base=>$parent, file_name=>$diff->{'file'}),
 			               -class => "list"}, esc_path($diff->{'file'}));
-			print "</td>\n";
-			print "<td>$mode_chng</td>\n";
-			print "<td class=\"link\">";
+			print {$output_handler} "</td>\n";
+			print {$output_handler} "<td>$mode_chng</td>\n";
+			print {$output_handler} "<td class=\"link\">";
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch");
-				print " | ";
+				print {$output_handler} $cgi->a({-href => "#patch$patchno"}, "patch");
+				print {$output_handler} " | ";
 			}
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
 			                             hash_base=>$parent, file_name=>$diff->{'file'})},
 			              "blob") . " | ";
 			if ($have_blame) {
-				print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
+				print {$output_handler} $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
 				                             file_name=>$diff->{'file'})},
 				              "blame") . " | ";
 			}
-			print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
+			print {$output_handler} $cgi->a({-href => href(action=>"history", hash_base=>$parent,
 			                             file_name=>$diff->{'file'})},
 			              "history");
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 
 		} elsif ($diff->{'status'} eq "M" || $diff->{'status'} eq "T") { # modified, or type changed
 			my $mode_chnge = "";
@@ -4092,39 +4106,39 @@ sub git_difftree_body {
 				}
 				$mode_chnge .= "]</span>\n";
 			}
-			print "<td>";
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+			print {$output_handler} "<td>";
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff->{'file'}),
 			              -class => "list"}, esc_path($diff->{'file'}));
-			print "</td>\n";
-			print "<td>$mode_chnge</td>\n";
-			print "<td class=\"link\">";
+			print {$output_handler} "</td>\n";
+			print {$output_handler} "<td>$mode_chnge</td>\n";
+			print {$output_handler} "<td class=\"link\">";
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				print {$output_handler} $cgi->a({-href => "#patch$patchno"}, "patch") .
 				      " | ";
 			} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
 				# "commit" view and modified file (not onlu mode changed)
-				print $cgi->a({-href => href(action=>"blobdiff",
+				print {$output_handler} $cgi->a({-href => href(action=>"blobdiff",
 				                             hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
 				                             hash_base=>$hash, hash_parent_base=>$parent,
 				                             file_name=>$diff->{'file'})},
 				              "diff") .
 				      " | ";
 			}
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+			print {$output_handler} $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff->{'file'})},
 			               "blob") . " | ";
 			if ($have_blame) {
-				print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+				print {$output_handler} $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
 				                             file_name=>$diff->{'file'})},
 				              "blame") . " | ";
 			}
-			print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+			print {$output_handler} $cgi->a({-href => href(action=>"history", hash_base=>$hash,
 			                             file_name=>$diff->{'file'})},
 			              "history");
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 
 		} elsif ($diff->{'status'} eq "R" || $diff->{'status'} eq "C") { # renamed or copied
 			my %status_name = ('R' => 'moved', 'C' => 'copied');
@@ -4134,7 +4148,7 @@ sub git_difftree_body {
 				# mode also for directories, so we cannot use $to_mode_str
 				$mode_chng = sprintf(", mode: %04o", $to_mode_oct & 0777);
 			}
-			print "<td>" .
+			print {$output_handler} "<td>" .
 			      $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
 			                             hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}),
 			              -class => "list"}, esc_path($diff->{'to_file'})) . "</td>\n" .
@@ -4147,35 +4161,35 @@ sub git_difftree_body {
 			if ($action eq 'commitdiff') {
 				# link to patch
 				$patchno++;
-				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				print {$output_handler} $cgi->a({-href => "#patch$patchno"}, "patch") .
 				      " | ";
 			} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
 				# "commit" view and modified file (not only pure rename or copy)
-				print $cgi->a({-href => href(action=>"blobdiff",
+				print {$output_handler} $cgi->a({-href => href(action=>"blobdiff",
 				                             hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
 				                             hash_base=>$hash, hash_parent_base=>$parent,
 				                             file_name=>$diff->{'to_file'}, file_parent=>$diff->{'from_file'})},
 				              "diff") .
 				      " | ";
 			}
-			print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+			print {$output_handler} $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,
+				print {$output_handler} $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
 				                             file_name=>$diff->{'to_file'})},
 				              "blame") . " | ";
 			}
-			print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+			print {$output_handler} $cgi->a({-href => href(action=>"history", hash_base=>$hash,
 			                            file_name=>$diff->{'to_file'})},
 			              "history");
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 
 		} # we should not encounter Unmerged (U) or Unknown (X) status
-		print "</tr>\n";
+		print {$output_handler} "</tr>\n";
 	}
-	print "</tbody>" if $has_header;
-	print "</table>\n";
+	print {$output_handler} "</tbody>" if $has_header;
+	print {$output_handler} "</table>\n";
 }
 
 sub git_patchset_body {
@@ -4190,7 +4204,7 @@ sub git_patchset_body {
 	my $to_name;
 	my (%from, %to);
 
-	print "<div class=\"patchset\">\n";
+	print {$output_handler} "<div class=\"patchset\">\n";
 
 	# skip to first patch
 	while ($patch_line = <$fd>) {
@@ -4218,7 +4232,7 @@ sub git_patchset_body {
 		# and parse raw git-diff line if needed
 		if (is_patch_split($diffinfo, { 'to_file' => $to_name })) {
 			# this is continuation of a split patch
-			print "<div class=\"patch cont\">\n";
+			print {$output_handler} "<div class=\"patch cont\">\n";
 		} else {
 			# advance raw git-diff output if needed
 			$patch_idx++ if defined $diffinfo;
@@ -4230,7 +4244,7 @@ sub git_patchset_body {
 			# find which patch (using pathname of result) we are at now;
 			if ($is_combined) {
 				while ($to_name ne $diffinfo->{'to_file'}) {
-					print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+					print {$output_handler} "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
 					      format_diff_cc_simplified($diffinfo, @hash_parents) .
 					      "</div>\n";  # class="patch"
 
@@ -4247,7 +4261,7 @@ sub git_patchset_body {
 
 			# this is first patch for raw difftree line with $patch_idx index
 			# we index @$difftree array from 0, but number patches from 1
-			print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
+			print {$output_handler} "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
 		}
 
 		# git diff header
@@ -4255,25 +4269,25 @@ sub git_patchset_body {
 		#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
 		$patch_number++;
 		# print "git diff" header
-		print format_git_diff_header_line($patch_line, $diffinfo,
+		print {$output_handler} format_git_diff_header_line($patch_line, $diffinfo,
 		                                  \%from, \%to);
 
 		# print extended diff header
-		print "<div class=\"diff extended_header\">\n";
+		print {$output_handler} "<div class=\"diff extended_header\">\n";
 	EXTENDED_HEADER:
 		while ($patch_line = <$fd>) {
 			chomp $patch_line;
 
 			last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
 
-			print format_extended_diff_header_line($patch_line, $diffinfo,
+			print {$output_handler} format_extended_diff_header_line($patch_line, $diffinfo,
 			                                       \%from, \%to);
 		}
-		print "</div>\n"; # class="diff extended_header"
+		print {$output_handler} "</div>\n"; # class="diff extended_header"
 
 		# from-file/to-file diff header
 		if (! $patch_line) {
-			print "</div>\n"; # class="patch"
+			print {$output_handler} "</div>\n"; # class="patch"
 			last PATCH;
 		}
 		next PATCH if ($patch_line =~ m/^diff /);
@@ -4284,7 +4298,7 @@ sub git_patchset_body {
 		chomp $patch_line;
 		#assert($patch_line =~ m/^\+\+\+/) if DEBUG;
 
-		print format_diff_from_to_header($last_patch_line, $patch_line,
+		print {$output_handler} format_diff_from_to_header($last_patch_line, $patch_line,
 		                                 $diffinfo, \%from, \%to,
 		                                 @hash_parents);
 
@@ -4295,11 +4309,11 @@ sub git_patchset_body {
 
 			next PATCH if ($patch_line =~ m/^diff /);
 
-			print format_diff_line($patch_line, \%from, \%to);
+			print {$output_handler} format_diff_line($patch_line, \%from, \%to);
 		}
 
 	} continue {
-		print "</div>\n"; # class="patch"
+		print {$output_handler} "</div>\n"; # class="patch"
 	}
 
 	# for compact combined (--cc) format, with chunk and patch simpliciaction
@@ -4311,7 +4325,7 @@ sub git_patchset_body {
 		$diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
 
 		# generate anchor for "patch" links in difftree / whatchanged part
-		print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+		print {$output_handler} "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
 		      format_diff_cc_simplified($diffinfo, @hash_parents) .
 		      "</div>\n";  # class="patch"
 
@@ -4320,13 +4334,13 @@ sub git_patchset_body {
 
 	if ($patch_number == 0) {
 		if (@hash_parents > 1) {
-			print "<div class=\"diff nodifferences\">Trivial merge</div>\n";
+			print {$output_handler} "<div class=\"diff nodifferences\">Trivial merge</div>\n";
 		} else {
-			print "<div class=\"diff nodifferences\">No differences found</div>\n";
+			print {$output_handler} "<div class=\"diff nodifferences\">No differences found</div>\n";
 		}
 	}
 
-	print "</div>\n"; # class="patchset"
+	print {$output_handler} "</div>\n"; # class="patchset"
 }
 
 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -4375,7 +4389,7 @@ sub fill_project_list_info {
 # print 'sort by' <th> element, generating 'sort by $name' replay link
 # if that order is not selected
 sub print_sort_th {
-	print get_sort_th(@_);
+	print {$output_handler} get_sort_th(@_);
 }
 
 sub get_sort_th {
@@ -4428,20 +4442,20 @@ sub git_project_list_body {
 			}
 		}
 		my $cloud = git_populate_project_tagcloud(\%ctags);
-		print git_show_project_tagcloud($cloud, 64);
+		print {$output_handler} git_show_project_tagcloud($cloud, 64);
 	}
 
-	print "<table class=\"project_list\">\n";
+	print {$output_handler} "<table class=\"project_list\">\n";
 	unless ($no_header) {
-		print "<tr>\n";
+		print {$output_handler} "<tr>\n";
 		if ($check_forks) {
-			print "<th></th>\n";
+			print {$output_handler} "<th></th>\n";
 		}
-		print_sort_th('project', $order, 'Project');
-		print_sort_th('descr', $order, 'Description');
-		print_sort_th('owner', $order, 'Owner');
-		print_sort_th('age', $order, 'Last Change');
-		print "<th></th>\n" . # for links
+		print {$output_handler} get_sort_th('project', $order, 'Project');
+		print {$output_handler} get_sort_th('descr', $order, 'Description');
+		print {$output_handler} get_sort_th('owner', $order, 'Owner');
+		print {$output_handler} get_sort_th('age', $order, 'Last Change');
+		print {$output_handler} "<th></th>\n" . # for links
 		      "</tr>\n";
 	}
 	my $alternate = 1;
@@ -4461,26 +4475,26 @@ sub git_project_list_body {
 		}
 
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 		if ($check_forks) {
-			print "<td>";
+			print {$output_handler} "<td>";
 			if ($pr->{'forks'}) {
-				print "<!-- $pr->{'forks'} -->\n";
-				print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+");
+				print {$output_handler} "<!-- $pr->{'forks'} -->\n";
+				print {$output_handler} $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+");
 			}
-			print "</td>\n";
+			print {$output_handler} "</td>\n";
 		}
-		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-		                        -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
+		print {$output_handler} "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+		                        -class => "list"}, esc_html($pr->{'path'})) ."</td>\n".
 		      "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
 		                        -class => "list", -title => $pr->{'descr_long'}},
 		                        esc_html($pr->{'descr'})) . "</td>\n" .
 		      "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
-		print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+		print {$output_handler} "<td class=\"". age_class($pr->{'age'}) . "\">" .
 		      (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
@@ -4492,14 +4506,14 @@ sub git_project_list_body {
 		      "</tr>\n";
 	}
 	if (defined $extra) {
-		print "<tr>\n";
+		print {$output_handler} "<tr>\n";
 		if ($check_forks) {
-			print "<td></td>\n";
+			print {$output_handler} "<td></td>\n";
 		}
-		print "<td colspan=\"5\">$extra</td>\n" .
+		print {$output_handler} "<td colspan=\"5\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 sub git_log_body {
@@ -4519,7 +4533,7 @@ sub git_log_body {
 		               "<span class=\"age\">$co{'age_string'}</span>" .
 		               esc_html($co{'title'}) . $ref,
 		               $commit);
-		print "<div class=\"title_text\">\n" .
+		print {$output_handler} "<div class=\"title_text\">\n" .
 		      "<div class=\"log_link\">\n" .
 		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
 		      " | " .
@@ -4529,16 +4543,16 @@ sub git_log_body {
 		      "<br/>\n" .
 		      "</div>\n";
 		      git_print_authorship(\%co, -tag => 'span');
-		      print "<br/>\n</div>\n";
+		      print {$output_handler} "<br/>\n</div>\n";
 
-		print "<div class=\"log_body\">\n";
+		print {$output_handler} "<div class=\"log_body\">\n";
 		git_print_log($co{'comment'}, -final_empty_line=> 1);
-		print "</div>\n";
+		print {$output_handler} "</div>\n";
 	}
 	if ($extra) {
-		print "<div class=\"page_nav\">\n";
-		print "$extra\n";
-		print "</div>\n";
+		print {$output_handler} "<div class=\"page_nav\">\n";
+		print {$output_handler} "$extra\n";
+		print {$output_handler} "</div>\n";
 	}
 }
 
@@ -4549,41 +4563,41 @@ sub git_shortlog_body {
 	$from = 0 unless defined $from;
 	$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
-	print "<table class=\"shortlog\">\n";
+	print {$output_handler} "<table class=\"shortlog\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my %co = %{$commitlist->[$i]};
 		my $commit = $co{'id'};
 		my $ref = format_ref_marker($refs, $commit);
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 		# git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
-		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+		print {$output_handler} "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
 		      format_author_html('td', \%co, 10) . "<td>";
-		print format_subject_html($co{'title'}, $co{'title_short'},
+		print {$output_handler} format_subject_html($co{'title'}, $co{'title_short'},
 		                          href(action=>"commit", hash=>$commit), $ref);
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
 		      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
 		my $snapshot_links = format_snapshot_links($commit);
 		if (defined $snapshot_links) {
-			print " | " . $snapshot_links;
+			print {$output_handler} " | " . $snapshot_links;
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "</tr>\n";
 	}
 	if (defined $extra) {
-		print "<tr>\n" .
+		print {$output_handler} "<tr>\n" .
 		      "<td colspan=\"4\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 sub git_history_body {
@@ -4594,7 +4608,7 @@ sub git_history_body {
 	$from = 0 unless defined $from;
 	$to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
 
-	print "<table class=\"history\">\n";
+	print {$output_handler} "<table class=\"history\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my %co = %{$commitlist->[$i]};
@@ -4606,18 +4620,18 @@ sub git_history_body {
 		my $ref = format_ref_marker($refs, $commit);
 
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+		print {$output_handler} "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
 	# shortlog:   format_author_html('td', \%co, 10)
 		      format_author_html('td', \%co, 15, 3) . "<td>";
 		# originally git_history used chop_str($co{'title'}, 50)
-		print format_subject_html($co{'title'}, $co{'title_short'},
+		print {$output_handler} format_subject_html($co{'title'}, $co{'title_short'},
 		                          href(action=>"commit", hash=>$commit), $ref);
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . " | " .
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
@@ -4627,7 +4641,7 @@ sub git_history_body {
 			my $blob_parent  = git_get_hash_by_path($commit, $file_name);
 			if (defined $blob_current && defined $blob_parent &&
 					$blob_current ne $blob_parent) {
-				print " | " .
+				print {$output_handler} " | " .
 					$cgi->a({-href => href(action=>"blobdiff",
 					                       hash=>$blob_current, hash_parent=>$blob_parent,
 					                       hash_base=>$hash_base, hash_parent_base=>$commit,
@@ -4635,15 +4649,15 @@ sub git_history_body {
 					        "diff to current");
 			}
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "</tr>\n";
 	}
 	if (defined $extra) {
-		print "<tr>\n" .
+		print {$output_handler} "<tr>\n" .
 		      "<td colspan=\"4\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 sub git_tags_body {
@@ -4652,7 +4666,7 @@ sub git_tags_body {
 	$from = 0 unless defined $from;
 	$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
 
-	print "<table class=\"tags\">\n";
+	print {$output_handler} "<table class=\"tags\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $taglist->[$i];
@@ -4663,50 +4677,50 @@ sub git_tags_body {
 			$comment_short = chop_str($comment, 30, 5);
 		}
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 		if (defined $tag{'age'}) {
-			print "<td><i>$tag{'age'}</i></td>\n";
+			print {$output_handler} "<td><i>$tag{'age'}</i></td>\n";
 		} else {
-			print "<td></td>\n";
+			print {$output_handler} "<td></td>\n";
 		}
-		print "<td>" .
+		print {$output_handler} "<td>" .
 		      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),
 		               -class => "list name"}, esc_html($tag{'name'})) .
 		      "</td>\n" .
 		      "<td>";
 		if (defined $comment) {
-			print format_subject_html($comment, $comment_short,
+			print {$output_handler} format_subject_html($comment, $comment_short,
 			                          href(action=>"tag", hash=>$tag{'id'}));
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "<td class=\"selflink\">";
 		if ($tag{'type'} eq "tag") {
-			print $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag");
+			print {$output_handler} $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag");
 		} else {
-			print "&nbsp;";
+			print {$output_handler} "&nbsp;";
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "<td class=\"link\">" . " | " .
 		      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
 		if ($tag{'reftype'} eq "commit") {
-			print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") .
+			print {$output_handler} " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") .
 			      " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log");
 		} elsif ($tag{'reftype'} eq "blob") {
-			print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
+			print {$output_handler} " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "</tr>";
 	}
 	if (defined $extra) {
-		print "<tr>\n" .
+		print {$output_handler} "<tr>\n" .
 		      "<td colspan=\"5\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 sub git_heads_body {
@@ -4715,19 +4729,19 @@ sub git_heads_body {
 	$from = 0 unless defined $from;
 	$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
-	print "<table class=\"heads\">\n";
+	print {$output_handler} "<table class=\"heads\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $headlist->[$i];
 		my %ref = %$entry;
 		my $curr = $ref{'id'} eq $head;
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td><i>$ref{'age'}</i></td>\n" .
+		print {$output_handler} "<td><i>$ref{'age'}</i></td>\n" .
 		      ($curr ? "<td class=\"current_head\">" : "<td>") .
 		      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}),
 		               -class => "list name"},esc_html($ref{'name'})) .
@@ -4740,11 +4754,11 @@ sub git_heads_body {
 		      "</tr>";
 	}
 	if (defined $extra) {
-		print "<tr>\n" .
+		print {$output_handler} "<tr>\n" .
 		      "<td colspan=\"3\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 sub git_search_grep_body {
@@ -4752,7 +4766,7 @@ sub git_search_grep_body {
 	$from = 0 unless defined $from;
 	$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
-	print "<table class=\"commit_search\">\n";
+	print {$output_handler} "<table class=\"commit_search\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my %co = %{$commitlist->[$i]};
@@ -4761,12 +4775,12 @@ sub git_search_grep_body {
 		}
 		my $commit = $co{'id'};
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+		print {$output_handler} "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
 		      format_author_html('td', \%co, 15, 5) .
 		      "<td>" .
 		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -4786,25 +4800,25 @@ sub git_search_grep_body {
 				$match = esc_html($match);
 				$trail = esc_html($trail);
 
-				print "$lead<span class=\"match\">$match</span>$trail<br />";
+				print {$output_handler} "$lead<span class=\"match\">$match</span>$trail<br />";
 			}
 		}
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
 		      " | " .
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") .
 		      " | " .
 		      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-		print "</td>\n" .
+		print {$output_handler} "</td>\n" .
 		      "</tr>\n";
 	}
 	if (defined $extra) {
-		print "<tr>\n" .
+		print {$output_handler} "<tr>\n" .
 		      "<td colspan=\"3\">$extra</td>\n" .
 		      "</tr>\n";
 	}
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 }
 
 ## ======================================================================
@@ -4824,11 +4838,11 @@ sub git_project_list {
 
 	git_header_html();
 	if (-f $home_text) {
-		print "<div class=\"index_include\">\n";
+		print {$output_handler} "<div class=\"index_include\">\n";
 		insert_file($home_text);
-		print "</div>\n";
+		print {$output_handler} "</div>\n";
 	}
-	print $cgi->startform(-method => "get") .
+	print {$output_handler} $cgi->startform(-method => "get") .
 	      "<p class=\"projsearch\">Search:\n" .
 	      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
 	      "</p>" .
@@ -4858,7 +4872,7 @@ sub git_forks {
 sub git_project_index {
 	my @projects = git_get_projects_list($project);
 
-	print $cgi->header(
+	print {$output_handler} $cgi->header(
 		-type => 'text/plain',
 		-charset => 'utf-8',
 		-content_disposition => 'inline; filename="index.aux"');
@@ -4875,7 +4889,7 @@ sub git_project_index {
 		$path  =~ s/ /\+/g;
 		$owner =~ s/ /\+/g;
 
-		print "$path $owner\n";
+		print {$output_handler} "$path $owner\n";
 	}
 }
 
@@ -4902,12 +4916,12 @@ sub git_summary {
 	git_header_html();
 	git_print_page_nav('summary','', $head);
 
-	print "<div class=\"title\">&nbsp;</div>\n";
-	print "<table class=\"projects_list\">\n" .
+	print {$output_handler} "<div class=\"title\">&nbsp;</div>\n";
+	print {$output_handler} "<table class=\"projects_list\">\n" .
 	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
 	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
 	if (defined $cd{'rfc2822'}) {
-		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+		print {$output_handler} "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
 	}
 
 	# use per project git URL list in $projectroot/$project/cloneurl
@@ -4917,7 +4931,7 @@ sub git_summary {
 	@url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
 	foreach my $git_url (@url_list) {
 		next unless $git_url;
-		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
+		print {$output_handler} "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
 		$url_tag = "";
 	}
 
@@ -4926,23 +4940,23 @@ sub git_summary {
 	if ($show_ctags) {
 		my $ctags = git_get_project_ctags($project);
 		my $cloud = git_populate_project_tagcloud($ctags);
-		print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
-		print "</td>\n<td>" unless %$ctags;
-		print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
-		print "</td>\n<td>" if %$ctags;
-		print git_show_project_tagcloud($cloud, 48);
-		print "</td></tr>";
+		print {$output_handler} "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
+		print {$output_handler} "</td>\n<td>" unless %$ctags;
+		print {$output_handler} "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
+		print {$output_handler} "</td>\n<td>" if %$ctags;
+		print {$output_handler} git_show_project_tagcloud($cloud, 48);
+		print {$output_handler} "</td></tr>";
 	}
 
-	print "</table>\n";
+	print {$output_handler} "</table>\n";
 
 	# If XSS prevention is on, we don't include README.html.
 	# TODO: Allow a readme in some safe format.
 	if (!$prevent_xss && -s "$projectroot/$project/README.html") {
-		print "<div class=\"title\">readme</div>\n" .
+		print {$output_handler} "<div class=\"title\">readme</div>\n" .
 		      "<div class=\"readme\">\n";
 		insert_file("$projectroot/$project/README.html");
-		print "\n</div>\n"; # class="readme"
+		print {$output_handler} "\n</div>\n"; # class="readme"
 	}
 
 	# we need to request one more than 16 (0..15) to check if
@@ -4991,7 +5005,7 @@ sub git_tag {
 	}
 
 	git_print_header_div('commit', esc_html($tag{'name'}), $hash);
-	print "<div class=\"title_text\">\n" .
+	print {$output_handler} "<div class=\"title_text\">\n" .
 	      "<table class=\"object_header\">\n" .
 	      "<tr>\n" .
 	      "<td>object</td>\n" .
@@ -5003,15 +5017,15 @@ sub git_tag {
 	if (defined($tag{'author'})) {
 		git_print_authorship_rows(\%tag, 'author');
 	}
-	print "</table>\n\n" .
+	print {$output_handler} "</table>\n\n" .
 	      "</div>\n";
-	print "<div class=\"page_body\">";
+	print {$output_handler} "<div class=\"page_body\">";
 	my $comment = $tag{'comment'};
 	foreach my $line (@$comment) {
 		chomp $line;
-		print esc_html($line, -nbsp=>1) . "<br/>\n";
+		print {$output_handler} esc_html($line, -nbsp=>1) . "<br/>\n";
 	}
-	print "</div>\n";
+	print {$output_handler} "</div>\n";
 	git_footer_html();
 }
 
@@ -5062,21 +5076,23 @@ sub git_blame_common {
 
 	# incremental blame data returns early
 	if ($format eq 'data') {
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type=>"text/plain", -charset => "utf-8",
 			-status=> "200 OK");
 		local $| = 1; # output autoflush
-		print while <$fd>;
+		while (<$fd>) {
+			 print {$output_handler} $_;
+		}
 		close $fd
-			or print "ERROR $!\n";
+			or die_error(500, "ERROR $!\n");
 
-		print 'END';
+		print {$output_handler} 'END';
 		if (defined $t0 && gitweb_check_feature('timed')) {
-			print ' '.
+			print {$output_handler} ' '.
 			      Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
 			      ' '.$number_of_git_cmds;
 		}
-		print "\n";
+		print {$output_handler} "\n";
 
 		return;
 	}
@@ -5109,20 +5125,20 @@ sub git_blame_common {
 
 	# page body
 	if ($format eq 'incremental') {
-		print "<noscript>\n<div class=\"error\"><center><b>\n".
+		print {$output_handler} "<noscript>\n<div class=\"error\"><center><b>\n".
 		      "This page requires JavaScript to run.\n Use ".
 		      $cgi->a({-href => href(action=>'blame',javascript=>0,-replay=>1)},
 		              'this page').
 		      " instead.\n".
 		      "</b></center></div>\n</noscript>\n";
 
-		print qq!<div id="progress_bar" style="width: 100%; background-color: yellow"></div>\n!;
+		print {$output_handler} qq!<div id="progress_bar" style="width: 100%; background-color: yellow"></div>\n!;
 	}
 
-	print qq!<div class="page_body">\n!;
-	print qq!<div id="progress_info">... / ...</div>\n!
+	print {$output_handler} qq!<div class="page_body">\n!;
+	print {$output_handler} qq!<div id="progress_info">... / ...</div>\n!
 		if ($format eq 'incremental');
-	print qq!<table id="blame_table" class="blame" width="100%">\n!.
+	print {$output_handler} qq!<table id="blame_table" class="blame" width="100%">\n!.
 	      #qq!<col width="5.5em" /><col width="2.5em" /><col width="*" />\n!.
 	      qq!<thead>\n!.
 	      qq!<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n!.
@@ -5143,12 +5159,12 @@ sub git_blame_common {
 			chomp $line;
 			$linenr++;
 
-			print qq!<tr id="l$linenr" class="$color_class">!.
+			print {$output_handler} qq!<tr id="l$linenr" class="$color_class">!.
 			      qq!<td class="sha1"><a href=""> </a></td>!.
 			      qq!<td class="linenr">!.
 			      qq!<a class="linenr" href="">$linenr</a></td>!;
-			print qq!<td class="pre">! . esc_html($line) . "</td>\n";
-			print qq!</tr>\n!;
+			print {$output_handler} qq!<td class="pre">! . esc_html($line) . "</td>\n";
+			print {$output_handler} qq!</tr>\n!;
 		}
 
 	} else { # porcelain, i.e. ordinary blame
@@ -5189,25 +5205,25 @@ sub git_blame_common {
 			$tr_class .= ' boundary' if (exists $meta->{'boundary'});
 			$tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
 			$tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
-			print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
+			print {$output_handler} "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
 			if ($group_size) {
-				print "<td class=\"sha1\"";
-				print " title=\"". esc_html($author) . ", $date\"";
-				print " rowspan=\"$group_size\"" if ($group_size > 1);
-				print ">";
-				print $cgi->a({-href => href(action=>"commit",
+				print {$output_handler} "<td class=\"sha1\"";
+				print {$output_handler} " title=\"". esc_html($author) . ", $date\"";
+				print {$output_handler} " rowspan=\"$group_size\"" if ($group_size > 1);
+				print {$output_handler} ">";
+				print {$output_handler} $cgi->a({-href => href(action=>"commit",
 				                             hash=>$full_rev,
 				                             file_name=>$file_name)},
 				              esc_html($short_rev));
 				if ($group_size >= 2) {
 					my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
 					if (@author_initials) {
-						print "<br />" .
+						print {$output_handler} "<br />" .
 						      esc_html(join('', @author_initials));
 						#           or join('.', ...)
 					}
 				}
-				print "</td>\n";
+				print {$output_handler} "</td>\n";
 			}
 			# 'previous' <sha1 of parent commit> <filename at commit>
 			if (exists $meta->{'previous'} &&
@@ -5224,23 +5240,23 @@ sub git_blame_common {
 			my $blamed = href(action => 'blame',
 			                  file_name => $linenr_filename,
 			                  hash_base => $linenr_commit);
-			print "<td class=\"linenr\">";
-			print $cgi->a({ -href => "$blamed#l$orig_lineno",
+			print {$output_handler} "<td class=\"linenr\">";
+			print {$output_handler} $cgi->a({ -href => "$blamed#l$orig_lineno",
 			                -class => "linenr" },
 			              esc_html($lineno));
-			print "</td>";
-			print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
-			print "</tr>\n";
+			print {$output_handler} "</td>";
+			print {$output_handler} "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+			print {$output_handler} "</tr>\n";
 		} # end while
 
 	}
 
 	# footer
-	print "</tbody>\n".
+	print {$output_handler} "</tbody>\n".
 	      "</table>\n"; # class="blame"
-	print "</div>\n";   # class="blame_body"
+	print {$output_handler} "</div>\n";   # class="blame_body"
 	close $fd
-		or print "Reading blob failed\n";
+		or print {$output_handler} "Reading blob failed\n";
 
 	git_footer_html();
 }
@@ -5323,16 +5339,17 @@ sub git_blob_plain {
 	my $sandbox = $prevent_xss &&
 		$type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
 
-	print $cgi->header(
+	print {$output_handler} $cgi->header(
 		-type => $type,
 		-expires => $expires,
 		-content_disposition =>
 			($sandbox ? 'attachment' : 'inline')
 			. '; filename="' . $save_as . '"');
 	local $/ = undef;
-	binmode STDOUT, ':raw';
-	print <$fd>;
-	binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+
+	binmode $output_handler_bin, ':raw';
+	print {$output_handler_bin} <$fd>;
+	binmode $output_handler_bin, ':utf8'; # as set at the beginning of gitweb.cgi
 	close $fd;
 }
 
@@ -5391,18 +5408,18 @@ sub git_blob {
 		git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 		git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 	} else {
-		print "<div class=\"page_nav\">\n" .
+		print {$output_handler} "<div class=\"page_nav\">\n" .
 		      "<br/><br/></div>\n" .
 		      "<div class=\"title\">$hash</div>\n";
 	}
 	git_print_page_path($file_name, "blob", $hash_base);
-	print "<div class=\"page_body\">\n";
+	print {$output_handler} "<div class=\"page_body\">\n";
 	if ($mimetype =~ m!^image/!) {
-		print qq!<img type="$mimetype"!;
+		print {$output_handler} qq!<img type="$mimetype"!;
 		if ($file_name) {
-			print qq! alt="$file_name" title="$file_name"!;
+			print {$output_handler} qq! alt="$file_name" title="$file_name"!;
 		}
-		print qq! src="! .
+		print {$output_handler} qq! src="! .
 		      href(action=>"blob_plain", hash=>$hash,
 		           hash_base=>$hash_base, file_name=>$file_name) .
 		      qq!" />\n!;
@@ -5412,14 +5429,14 @@ sub git_blob {
 			chomp $line;
 			$nr++;
 			$line = untabify($line);
-			printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
+			printf {$output_handler} "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
 				. "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
 			       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
 		}
 	}
 	close $fd
-		or print "Reading blob failed.\n";
-	print "</div>";
+		or print {$output_handler} "Reading blob failed.\n";
+	print {$output_handler} "</div>";
 	git_footer_html();
 }
 
@@ -5474,9 +5491,9 @@ sub git_tree {
 		git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
 	} else {
 		undef $hash_base;
-		print "<div class=\"page_nav\">\n";
-		print "<br/><br/></div>\n";
-		print "<div class=\"title\">$hash</div>\n";
+		print {$output_handler} "<div class=\"page_nav\">\n";
+		print {$output_handler} "<br/><br/></div>\n";
+		print {$output_handler} "<div class=\"title\">$hash</div>\n";
 	}
 	if (defined $file_name) {
 		$basedir = $file_name;
@@ -5485,16 +5502,16 @@ sub git_tree {
 		}
 		git_print_page_path($file_name, 'tree', $hash_base);
 	}
-	print "<div class=\"page_body\">\n";
-	print "<table class=\"tree\">\n";
+	print {$output_handler} "<div class=\"page_body\">\n";
+	print {$output_handler} "<table class=\"tree\">\n";
 	my $alternate = 1;
 	# '..' (top directory) link if possible
 	if (defined $hash_base &&
 	    defined $file_name && $file_name =~ m![^/]+$!) {
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 
@@ -5502,33 +5519,33 @@ sub git_tree {
 		$up =~ s!/?[^/]+$!!;
 		undef $up unless $up;
 		# based on git_print_tree_entry
-		print '<td class="mode">' . mode_str('040000') . "</td>\n";
-		print '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
-		print '<td class="list">';
-		print $cgi->a({-href => href(action=>"tree",
+		print {$output_handler} '<td class="mode">' . mode_str('040000') . "</td>\n";
+		print {$output_handler} '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
+		print {$output_handler} '<td class="list">';
+		print {$output_handler} $cgi->a({-href => href(action=>"tree",
 		                             hash_base=>$hash_base,
 		                             file_name=>$up)},
 		              "..");
-		print "</td>\n";
-		print "<td class=\"link\"></td>\n";
+		print {$output_handler} "</td>\n";
+		print {$output_handler} "<td class=\"link\"></td>\n";
 
-		print "</tr>\n";
+		print {$output_handler} "</tr>\n";
 	}
 	foreach my $line (@entries) {
 		my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
 
 		if ($alternate) {
-			print "<tr class=\"dark\">\n";
+			print {$output_handler} "<tr class=\"dark\">\n";
 		} else {
-			print "<tr class=\"light\">\n";
+			print {$output_handler} "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
 
 		git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame);
 
-		print "</tr>\n";
+		print {$output_handler} "</tr>\n";
 	}
-	print "</table>\n" .
+	print {$output_handler} "</table>\n" .
 	      "</div>";
 	git_footer_html();
 }
@@ -5605,16 +5622,16 @@ sub git_snapshot {
 	}
 
 	$filename =~ s/(["\\])/\\$1/g;
-	print $cgi->header(
+	print {$output_handler} $cgi->header(
 		-type => $known_snapshot_formats{$format}{'type'},
 		-content_disposition => 'inline; filename="' . $filename . '"',
 		-status => '200 OK');
 
 	open my $fd, "-|", $cmd
 		or die_error(500, "Execute git-archive failed");
-	binmode STDOUT, ':raw';
-	print <$fd>;
-	binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+	binmode $output_handler_bin, ':raw';
+	print {$output_handler_bin} <$fd>;
+	binmode $output_handler_bin, ':utf8'; # as set at the beginning of gitweb.cgi
 	close $fd;
 }
 
@@ -5765,11 +5782,11 @@ sub git_commit {
 	} else {
 		git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash);
 	}
-	print "<div class=\"title_text\">\n" .
+	print {$output_handler} "<div class=\"title_text\">\n" .
 	      "<table class=\"object_header\">\n";
 	git_print_authorship_rows(\%co);
-	print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
-	print "<tr>" .
+	print {$output_handler} "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
+	print {$output_handler} "<tr>" .
 	      "<td>tree</td>" .
 	      "<td class=\"sha1\">" .
 	      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash),
@@ -5780,13 +5797,13 @@ sub git_commit {
 	              "tree");
 	my $snapshot_links = format_snapshot_links($hash);
 	if (defined $snapshot_links) {
-		print " | " . $snapshot_links;
+		print {$output_handler} " | " . $snapshot_links;
 	}
-	print "</td>" .
+	print {$output_handler} "</td>" .
 	      "</tr>\n";
 
 	foreach my $par (@$parents) {
-		print "<tr>" .
+		print {$output_handler} "<tr>" .
 		      "<td>parent</td>" .
 		      "<td class=\"sha1\">" .
 		      $cgi->a({-href => href(action=>"commit", hash=>$par),
@@ -5799,12 +5816,12 @@ sub git_commit {
 		      "</td>" .
 		      "</tr>\n";
 	}
-	print "</table>".
+	print {$output_handler} "</table>".
 	      "</div>\n";
 
-	print "<div class=\"page_body\">\n";
+	print {$output_handler} "<div class=\"page_body\">\n";
 	git_print_log($co{'comment'});
-	print "</div>\n";
+	print {$output_handler} "</div>\n";
 
 	git_difftree_body(\@difftree, $hash, @$parents);
 
@@ -5852,7 +5869,7 @@ sub git_object {
 		die_error(400, "Not enough information to find object");
 	}
 
-	print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+	print {$output_handler} $cgi->redirect(-uri => href(action=>$type, -full=>1,
 	                                  hash=>$hash, hash_base=>$hash_base,
 	                                  file_name=>$file_name),
 	                     -status => '302 Found');
@@ -5943,23 +5960,23 @@ sub git_blobdiff {
 			git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 			git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 		} else {
-			print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
-			print "<div class=\"title\">$hash vs $hash_parent</div>\n";
+			print {$output_handler} "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
+			print {$output_handler} "<div class=\"title\">$hash vs $hash_parent</div>\n";
 		}
 		if (defined $file_name) {
 			git_print_page_path($file_name, "blob", $hash_base);
 		} else {
-			print "<div class=\"page_path\"></div>\n";
+			print {$output_handler} "<div class=\"page_path\"></div>\n";
 		}
 
 	} elsif ($format eq 'plain') {
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type => 'text/plain',
 			-charset => 'utf-8',
 			-expires => $expires,
 			-content_disposition => 'inline; filename="' . "$file_name" . '.patch"');
 
-		print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+		print {$output_handler} "X-Git-Url: " . $cgi->self_url() . "\n\n";
 
 	} else {
 		die_error(400, "Unknown blobdiff format");
@@ -5967,12 +5984,12 @@ sub git_blobdiff {
 
 	# patch
 	if ($format eq 'html') {
-		print "<div class=\"page_body\">\n";
+		print {$output_handler} "<div class=\"page_body\">\n";
 
 		git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
 		close $fd;
 
-		print "</div>\n"; # class="page_body"
+		print {$output_handler} "</div>\n"; # class="page_body"
 		git_footer_html();
 
 	} else {
@@ -5980,12 +5997,12 @@ sub git_blobdiff {
 			$line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg;
 			$line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg;
 
-			print $line;
+			print {$output_handler} $line;
 
 			last if $line =~ m!^\+\+\+!;
 		}
 		local $/ = undef;
-		print <$fd>;
+		print {$output_handler} <$fd>;
 		close $fd;
 	}
 }
@@ -6148,16 +6165,16 @@ sub git_commitdiff {
 		git_header_html(undef, $expires);
 		git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
 		git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
-		print "<div class=\"title_text\">\n" .
+		print {$output_handler} "<div class=\"title_text\">\n" .
 		      "<table class=\"object_header\">\n";
 		git_print_authorship_rows(\%co);
-		print "</table>".
+		print {$output_handler} "</table>".
 		      "</div>\n";
-		print "<div class=\"page_body\">\n";
+		print {$output_handler} "<div class=\"page_body\">\n";
 		if (@{$co{'comment'}} > 1) {
-			print "<div class=\"log\">\n";
+			print {$output_handler} "<div class=\"log\">\n";
 			git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
-			print "</div>\n"; # class="log"
+			print {$output_handler} "</div>\n"; # class="log"
 		}
 
 	} elsif ($format eq 'plain') {
@@ -6165,27 +6182,27 @@ sub git_commitdiff {
 		my $tagname = git_get_rev_name_tags($hash);
 		my $filename = basename($project) . "-$hash.patch";
 
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type => 'text/plain',
 			-charset => 'utf-8',
 			-expires => $expires,
 			-content_disposition => 'inline; filename="' . "$filename" . '"');
 		my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
-		print "From: " . to_utf8($co{'author'}) . "\n";
-		print "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n";
-		print "Subject: " . to_utf8($co{'title'}) . "\n";
+		print {$output_handler} "From: " . to_utf8($co{'author'}) . "\n";
+		print {$output_handler} "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n";
+		print {$output_handler} "Subject: " . to_utf8($co{'title'}) . "\n";
 
-		print "X-Git-Tag: $tagname\n" if $tagname;
-		print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+		print {$output_handler} "X-Git-Tag: $tagname\n" if $tagname;
+		print {$output_handler} "X-Git-Url: " . $cgi->self_url() . "\n\n";
 
 		foreach my $line (@{$co{'comment'}}) {
-			print to_utf8($line) . "\n";
+			print {$output_handler} to_utf8($line) . "\n";
 		}
-		print "---\n\n";
+		print {$output_handler} "---\n\n";
 	} elsif ($format eq 'patch') {
 		my $filename = basename($project) . "-$hash.patch";
 
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type => 'text/plain',
 			-charset => 'utf-8',
 			-expires => $expires,
@@ -6198,24 +6215,24 @@ sub git_commitdiff {
 			$hash_parent eq '-c' || $hash_parent eq '--cc';
 		git_difftree_body(\@difftree, $hash,
 		                  $use_parents ? @{$co{'parents'}} : $hash_parent);
-		print "<br/>\n";
+		print {$output_handler} "<br/>\n";
 
 		git_patchset_body($fd, \@difftree, $hash,
 		                  $use_parents ? @{$co{'parents'}} : $hash_parent);
 		close $fd;
-		print "</div>\n"; # class="page_body"
+		print {$output_handler} "</div>\n"; # class="page_body"
 		git_footer_html();
 
 	} elsif ($format eq 'plain') {
 		local $/ = undef;
-		print <$fd>;
+		print {$output_handler} <$fd>;
 		close $fd
-			or print "Reading git-diff-tree failed\n";
+			or print {$output_handler} "Reading git-diff-tree failed\n";
 	} elsif ($format eq 'patch') {
 		local $/ = undef;
-		print <$fd>;
+		print {$output_handler} <$fd>;
 		close $fd
-			or print "Reading git-format-patch failed\n";
+			or print {$output_handler} "Reading git-format-patch failed\n";
 	}
 }
 
@@ -6318,7 +6335,7 @@ sub git_search {
 		git_print_page_nav('','', $hash,$co{'tree'},$hash);
 		git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
-		print "<table class=\"pickaxe search\">\n";
+		print {$output_handler} "<table class=\"pickaxe search\">\n";
 		my $alternate = 1;
 		local $/ = "\n";
 		open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
@@ -6334,24 +6351,24 @@ sub git_search {
 			if (defined $set{'commit'}) {
 				# finish previous commit
 				if (%co) {
-					print "</td>\n" .
+					print {$output_handler} "</td>\n" .
 					      "<td class=\"link\">" .
 					      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
 					      " | " .
 					      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-					print "</td>\n" .
+					print {$output_handler} "</td>\n" .
 					      "</tr>\n";
 				}
 
 				if ($alternate) {
-					print "<tr class=\"dark\">\n";
+					print {$output_handler} "<tr class=\"dark\">\n";
 				} else {
-					print "<tr class=\"light\">\n";
+					print {$output_handler} "<tr class=\"light\">\n";
 				}
 				$alternate ^= 1;
 				%co = parse_commit($set{'commit'});
 				my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-				print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+				print {$output_handler} "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
 				      "<td><i>$author</i></td>\n" .
 				      "<td>" .
 				      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -6360,7 +6377,7 @@ sub git_search {
 			} elsif (defined $set{'to_id'}) {
 				next if ($set{'to_id'} =~ m/^0{40}$/);
 
-				print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+				print {$output_handler} $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
 				                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
 				              -class => "list"},
 				              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
@@ -6371,23 +6388,23 @@ sub git_search {
 
 		# finish last commit (warning: repetition!)
 		if (%co) {
-			print "</td>\n" .
+			print {$output_handler} "</td>\n" .
 			      "<td class=\"link\">" .
 			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
 			      " | " .
 			      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-			print "</td>\n" .
+			print {$output_handler} "</td>\n" .
 			      "</tr>\n";
 		}
 
-		print "</table>\n";
+		print {$output_handler} "</table>\n";
 	}
 
 	if ($searchtype eq 'grep') {
 		git_print_page_nav('','', $hash,$co{'tree'},$hash);
 		git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
-		print "<table class=\"grep_search\">\n";
+		print {$output_handler} "<table class=\"grep_search\">\n";
 		my $alternate = 1;
 		my $matches = 0;
 		local $/ = "\n";
@@ -6406,21 +6423,21 @@ sub git_search {
 				(undef, $file, $lno, $ltext) = split(/:/, $line, 4);
 			}
 			if ($file ne $lastfile) {
-				$lastfile and print "</td></tr>\n";
+				$lastfile and print {$output_handler} "</td></tr>\n";
 				if ($alternate++) {
-					print "<tr class=\"dark\">\n";
+					print {$output_handler} "<tr class=\"dark\">\n";
 				} else {
-					print "<tr class=\"light\">\n";
+					print {$output_handler} "<tr class=\"light\">\n";
 				}
-				print "<td class=\"list\">".
+				print {$output_handler} "<td class=\"list\">".
 					$cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
 							       file_name=>"$file"),
 						-class => "list"}, esc_path($file));
-				print "</td><td>\n";
+				print {$output_handler} "</td><td>\n";
 				$lastfile = $file;
 			}
 			if ($binary) {
-				print "<div class=\"binary\">Binary file</div>\n";
+				print {$output_handler} "<div class=\"binary\">Binary file</div>\n";
 			} else {
 				$ltext = untabify($ltext);
 				if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
@@ -6432,7 +6449,7 @@ sub git_search {
 				} else {
 					$ltext = esc_html($ltext, -nbsp=>1);
 				}
-				print "<div class=\"pre\">" .
+				print {$output_handler} "<div class=\"pre\">" .
 					$cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
 							       file_name=>"$file").'#l'.$lno,
 						-class => "linenr"}, sprintf('%4i', $lno))
@@ -6440,16 +6457,16 @@ sub git_search {
 			}
 		}
 		if ($lastfile) {
-			print "</td></tr>\n";
+			print {$output_handler} "</td></tr>\n";
 			if ($matches > 1000) {
-				print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
+				print {$output_handler} "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
 			}
 		} else {
-			print "<div class=\"diff nodifferences\">No matches found</div>\n";
+			print {$output_handler} "<div class=\"diff nodifferences\">No matches found</div>\n";
 		}
 		close $fd;
 
-		print "</table>\n";
+		print {$output_handler} "</table>\n";
 	}
 	git_footer_html();
 }
@@ -6457,7 +6474,7 @@ sub git_search {
 sub git_search_help {
 	git_header_html();
 	git_print_page_nav('','', $hash,$hash,$hash);
-	print <<EOT;
+	print {$output_handler} <<EOT;
 <p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
 regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
 the pattern entered is recognized as the POSIX extended
@@ -6469,7 +6486,7 @@ insensitive).</p>
 EOT
 	my $have_grep = gitweb_check_feature('grep');
 	if ($have_grep) {
-		print <<EOT;
+		print {$output_handler} <<EOT;
 <dt><b>grep</b></dt>
 <dd>All files in the currently selected tree (HEAD unless you are explicitly browsing
     a different one) are searched for the given pattern. On large trees, this search can take
@@ -6478,7 +6495,7 @@ due to git-grep peculiarity, currently if regexp mode is turned off, the matches
 case-sensitive.</dd>
 EOT
 	}
-	print <<EOT;
+	print {$output_handler} <<EOT;
 <dt><b>author</b></dt>
 <dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.</dd>
 <dt><b>committer</b></dt>
@@ -6486,7 +6503,7 @@ EOT
 EOT
 	my $have_pickaxe = gitweb_check_feature('pickaxe');
 	if ($have_pickaxe) {
-		print <<EOT;
+		print {$output_handler} <<EOT;
 <dt><b>pickaxe</b></dt>
 <dd>All commits that caused the string to appear or disappear from any file (changes that
 added, removed or "modified" the string) will be listed. This search can take a while and
@@ -6494,7 +6511,7 @@ takes a lot of strain on the server, so please use it wisely. Note that since yo
 interested even in changes just changing the case as well, this search is case sensitive.</dd>
 EOT
 	}
-	print "</dl>\n";
+	print {$output_handler} "</dl>\n";
 	git_footer_html();
 }
 
@@ -6541,7 +6558,7 @@ sub git_feed {
 				$since = Time::ParseDate::parsedate($if_modified, GMT => 1);
 			}
 			if (defined $since && $latest_epoch <= $since) {
-				print $cgi->header(
+				print {$output_handler} $cgi->header(
 					-type => $content_type,
 					-charset => 'utf-8',
 					-last_modified => $latest_date{'rfc2822'},
@@ -6549,12 +6566,12 @@ sub git_feed {
 				return;
 			}
 		}
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type => $content_type,
 			-charset => 'utf-8',
 			-last_modified => $latest_date{'rfc2822'});
 	} else {
-		print $cgi->header(
+		print {$output_handler} $cgi->header(
 			-type => $content_type,
 			-charset => 'utf-8');
 	}
@@ -6598,13 +6615,13 @@ sub git_feed {
 	} else {
 		$alt_url = href(-full=>1, action=>"summary");
 	}
-	print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
+	print {$output_handler} qq!<?xml version="1.0" encoding="utf-8"?>\n!;
 	if ($format eq 'rss') {
-		print <<XML;
+		print {$output_handler} <<XML;
 <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
 <channel>
 XML
-		print "<title>$title</title>\n" .
+		print {$output_handler} "<title>$title</title>\n" .
 		      "<link>$alt_url</link>\n" .
 		      "<description>$descr</description>\n" .
 		      "<language>en</language>\n" .
@@ -6614,22 +6631,22 @@ XML
 			# prefer the logo to the favicon, since RSS
 			# doesn't allow both
 			my $img = esc_url($logo || $favicon);
-			print "<image>\n" .
+			print {$output_handler} "<image>\n" .
 			      "<url>$img</url>\n" .
 			      "<title>$title</title>\n" .
 			      "<link>$alt_url</link>\n" .
 			      "</image>\n";
 		}
 		if (%latest_date) {
-			print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
-			print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
+			print {$output_handler} "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
+			print {$output_handler} "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
 		}
-		print "<generator>gitweb v.$version/$git_version</generator>\n";
+		print {$output_handler} "<generator>gitweb v.$version/$git_version</generator>\n";
 	} elsif ($format eq 'atom') {
-		print <<XML;
+		print {$output_handler} <<XML;
 <feed xmlns="http://www.w3.org/2005/Atom">
 XML
-		print "<title>$title</title>\n" .
+		print {$output_handler} "<title>$title</title>\n" .
 		      "<subtitle>$descr</subtitle>\n" .
 		      '<link rel="alternate" type="text/html" href="' .
 		      $alt_url . '" />' . "\n" .
@@ -6639,19 +6656,19 @@ XML
 		      # use project owner for feed author
 		      "<author><name>$owner</name></author>\n";
 		if (defined $favicon) {
-			print "<icon>" . esc_url($favicon) . "</icon>\n";
+			print {$output_handler} "<icon>" . esc_url($favicon) . "</icon>\n";
 		}
 		if (defined $logo_url) {
 			# not twice as wide as tall: 72 x 27 pixels
-			print "<logo>" . esc_url($logo) . "</logo>\n";
+			print {$output_handler} "<logo>" . esc_url($logo) . "</logo>\n";
 		}
 		if (! %latest_date) {
 			# dummy date to keep the feed valid until commits trickle in:
-			print "<updated>1970-01-01T00:00:00Z</updated>\n";
+			print {$output_handler} "<updated>1970-01-01T00:00:00Z</updated>\n";
 		} else {
-			print "<updated>$latest_date{'iso-8601'}</updated>\n";
+			print {$output_handler} "<updated>$latest_date{'iso-8601'}</updated>\n";
 		}
-		print "<generator version='$version/$git_version'>gitweb</generator>\n";
+		print {$output_handler} "<generator version='$version/$git_version'>gitweb</generator>\n";
 	}
 
 	# contents
@@ -6676,7 +6693,7 @@ XML
 		# print element (entry, item)
 		my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit);
 		if ($format eq 'rss') {
-			print "<item>\n" .
+			print {$output_handler} "<item>\n" .
 			      "<title>" . esc_html($co{'title'}) . "</title>\n" .
 			      "<author>" . esc_html($co{'author'}) . "</author>\n" .
 			      "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
@@ -6686,22 +6703,22 @@ XML
 			      "<content:encoded>" .
 			      "<![CDATA[\n";
 		} elsif ($format eq 'atom') {
-			print "<entry>\n" .
+			print {$output_handler} "<entry>\n" .
 			      "<title type=\"html\">" . esc_html($co{'title'}) . "</title>\n" .
 			      "<updated>$cd{'iso-8601'}</updated>\n" .
 			      "<author>\n" .
 			      "  <name>" . esc_html($co{'author_name'}) . "</name>\n";
 			if ($co{'author_email'}) {
-				print "  <email>" . esc_html($co{'author_email'}) . "</email>\n";
+				print {$output_handler} "  <email>" . esc_html($co{'author_email'}) . "</email>\n";
 			}
-			print "</author>\n" .
+			print {$output_handler} "</author>\n" .
 			      # use committer for contributor
 			      "<contributor>\n" .
 			      "  <name>" . esc_html($co{'committer_name'}) . "</name>\n";
 			if ($co{'committer_email'}) {
-				print "  <email>" . esc_html($co{'committer_email'}) . "</email>\n";
+				print {$output_handler} "  <email>" . esc_html($co{'committer_email'}) . "</email>\n";
 			}
-			print "</contributor>\n" .
+			print {$output_handler} "</contributor>\n" .
 			      "<published>$cd{'iso-8601'}</published>\n" .
 			      "<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
 			      "<id>$co_url</id>\n" .
@@ -6709,19 +6726,19 @@ XML
 			      "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
 		}
 		my $comment = $co{'comment'};
-		print "<pre>\n";
+		print {$output_handler} "<pre>\n";
 		foreach my $line (@$comment) {
 			$line = esc_html($line);
-			print "$line\n";
+			print {$output_handler} "$line\n";
 		}
-		print "</pre><ul>\n";
+		print {$output_handler} "</pre><ul>\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 "<li>" .
+			print {$output_handler} "<li>" .
 			      "[" .
 			      $cgi->a({-href => href(-full=>1, action=>"blobdiff",
 			                             hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'},
@@ -6729,26 +6746,26 @@ XML
 			                             file_name=>$file, file_parent=>$difftree{'from_file'}),
 			              -title => "diff"}, 'D');
 			if ($have_blame) {
-				print $cgi->a({-href => href(-full=>1, action=>"blame",
+				print {$output_handler} $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",
+				print {$output_handler} $cgi->a({-href => href(-full=>1, action=>"history",
 				                             file_name=>$file, hash=>$commit),
 				              -title => "history"}, 'H');
 			}
 			$file = esc_path($file);
-			print "] ".
+			print {$output_handler} "] ".
 			      "$file</li>\n";
 		}
 		if ($format eq 'rss') {
-			print "</ul>]]>\n" .
+			print {$output_handler} "</ul>]]>\n" .
 			      "</content:encoded>\n" .
 			      "</item>\n";
 		} elsif ($format eq 'atom') {
-			print "</ul>\n</div>\n" .
+			print {$output_handler} "</ul>\n</div>\n" .
 			      "</content>\n" .
 			      "</entry>\n";
 		}
@@ -6756,9 +6773,9 @@ XML
 
 	# end of feed
 	if ($format eq 'rss') {
-		print "</channel>\n</rss>\n";
+		print {$output_handler} "</channel>\n</rss>\n";
 	} elsif ($format eq 'atom') {
-		print "</feed>\n";
+		print {$output_handler} "</feed>\n";
 	}
 }
 
@@ -6772,13 +6789,12 @@ sub git_atom {
 
 sub git_opml {
 	my @list = git_get_projects_list();
-
-	print $cgi->header(
+	print {$output_handler} $cgi->header(
 		-type => 'text/xml',
 		-charset => 'utf-8',
 		-content_disposition => 'inline; filename="opml.xml"');
 
-	print <<XML;
+	print {$output_handler} <<XML;
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
 <head>
@@ -6803,9 +6819,9 @@ XML
 		my $path = esc_html(chop_str($proj{'path'}, 25, 5));
 		my $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
 		my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
-		print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
+		print {$output_handler} "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
 	}
-	print <<XML;
+	print {$output_handler} <<XML;
 </outline>
 </body>
 </opml>
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 9/9] gitweb: File based caching layer (from git.kernel.org)
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-9-git-send-email-warthog9@eaglescrag.net>

This is a very large patch that implements the file based
caching layer that is used on such large sites as kernel.org and
soon git.fedoraproject.org.  This provides a simple, and straight
forward caching mechanism that scales dramatically better than
Gitweb by itself.

The caching layer basically buffers the output that Gitweb would
normally return, and saves that output to a cache file on the local
disk.  When the file is requested it attempts to gain a shared lock
on the cache file and cat it out to the client.  Should an exclusive
lock be on a file (it's being updated) the code has a choice to either
update in the background and go ahead and show the stale page while
update is being performed, or stall the client(s) until the page
is generated.

There are two forms of stalling involved here, background building
and non-background building, both of which are discussed in the
configuration page.

There are still a few known "issues" with respect to this:
- Code needs to be added to be "browser" aware so
  that clients like wget that are trying to get a
  binary blob don't obtain a "Generating..." page
- There is an intermittent flushing issue that has yet
  to be tracked down

Caching is disabled by default with the $cache_enable variable,
setting this to 1 will enable file based caching.  It is expected
that this will be extended to include additional types of caching
(like memcached) in the future and should not be exclusively
considered a binary value.
---
 gitweb/cache.pm    |  283 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 gitweb/gitweb.css  |    6 +
 gitweb/gitweb.perl |   58 ++++++++++-
 3 files changed, 344 insertions(+), 3 deletions(-)
 create mode 100644 gitweb/cache.pm

diff --git a/gitweb/cache.pm b/gitweb/cache.pm
new file mode 100644
index 0000000..d08bcec
--- /dev/null
+++ b/gitweb/cache.pm
@@ -0,0 +1,283 @@
+# gitweb - simple web interface to track changes in git repositories
+#
+# (C) 2006, John 'Warthog9' Hawley <warthog19@eaglescrag.net>
+#
+# This program is licensed under the GPLv2
+
+#
+# Gitweb caching engine
+#
+
+use File::Path qw(make_path remove_tree);
+use Digest::MD5 qw(md5 md5_hex md5_base64);
+use Fcntl ':flock';
+
+sub cache_fetch {
+	my ($action) = @_;
+	my $cacheTime = 0;
+
+	# Deal with cache being disabled
+	if( $cache_enable == 0 ){
+		$output_handler = *STDOUT;
+		$output_handler_bin = *STDOUT;
+		$actions{$action}->();
+		return;
+	}elsif( $cache_enable == 1 ){
+		#obviously we are using file based caching
+
+		if(! -d $cachedir){
+			print "*** Warning ***: Caching enabled but cache directory does not exsist.  ($cachedir)\n";
+			mkdir ("cache", 0665) || die "Cannot create cache dir - you will need to manually create";
+			print "Cache directory created successfully\n";
+		}
+
+		our $full_url = "$my_url?". $ENV{'QUERY_STRING'};
+		our $urlhash = md5_hex($full_url);
+		our $fullhashdir = "$cachedir/". substr( $urlhash, 0, 2) ."/";
+
+		my $numdirs = make_path( $fullhashdir, { mode => 0777, error => \my $mkdirerr, } );
+		if( @$mkdirerr ){
+			my $mkdirerrmsg = "";
+			for my $diag (@$mkdirerr) {
+				my ($file, $message) = %$diag;
+				if($file eq '' ){
+					$mkdirerrmsg .= "general error: $message\n";
+				}else{
+					$mkdirerrmsg .= "problem unlinking $file: $message\n";
+				}
+			}
+			die_error(500, "Could not create cache directory | $mkdirerrmsg");
+		}
+		$fullhashpath = "$fullhashdir/". substr( $urlhash, 2 );
+		$fullhashbinpath = "$fullhashpath.bin";
+	} # done dealing with cache enabled / disabled
+
+	if(! -e "$fullhashpath" ){
+		if(! defined(my $childPid = fork()) ){
+			cacheUpdate($action,0);
+			cacheDisplay($action);
+		} elsif ( $childPid == 0 ){
+			#run the updater
+			cacheUpdate($action,1);
+		}else{
+			cacheWaitForUpdate($action);
+		}
+	}else{
+		#if cache is out dated, update
+		#else displayCache();
+		open(cacheFile, '<', "$fullhashpath");
+		stat(cacheFile);
+		close(cacheFile);
+		$cacheTime = get_loadavg() * 60;
+		if( $cacheTime > $maxCacheTime ){
+			$cacheTime = $maxCacheTime;
+		}
+		if( $cacheTime < $minCacheTime ){
+			$cacheTime = $minCacheTime;
+		}
+		if( (stat(_))[9] < (time - $cacheTime) ){
+			if( ! defined(my $childPid = fork()) ){
+				cacheUpdate($action,0);
+				cacheDisplay($action);
+			} elsif ( $childPid == 0 ){
+				#run the updater
+				#print "Running updater\n";
+				cacheUpdate($action,1);
+			}else{
+				#print "Waiting for update\n";
+				cacheWaitForUpdate($action);
+			}
+		} else {
+			cacheDisplay($action);
+		}
+
+
+	}
+
+	#
+	# If all of the caching failes - lets go ahead and press on without it and fall back to 'default'
+	# non-caching behavior.  This is the softest of the failure conditions.
+	#
+	#$actions{$action}->();
+}
+
+sub cacheUpdate {
+	my ($action,$areForked) = @_;
+	my $lockingStatus;
+	my $fileData = "";
+
+	if($backgroundCache){
+		open(cacheFileBG, '>:utf8', "$fullhashpath.bg");
+		my $lockStatBG = flock(cacheFileBG,LOCK_EX|LOCK_NB);
+
+		$lockStatus = $lockStatBG;
+	}else{
+		open(cacheFile, '>:utf8', "$fullhashpath");
+		my $lockStat = flock(cacheFile,LOCK_EX|LOCK_NB);
+
+		$lockStatus = $lockStat;
+	}
+	#print "lock status: $lockStat\n";
+
+
+	if (! $lockStatus ){
+		if ( $areForked ){
+			exit(0);
+		}else{
+			return;
+		}
+	}
+
+	if(
+		$action eq "snapshot"
+		||
+		$action eq "blob_plain"
+	){
+		open cacheFileBin, '>', $fullhashbinpath or die_error(500, "Could not open bin dump file");
+		$output_handler_bin = *cacheFileBin;
+	}
+
+	$output_handler = *cacheFile;
+
+	if($backgroundCache){
+		open(cacheFile, '>:utf8', "$fullhashpath");
+		$lockStat = flock(cacheFile,LOCK_EX);
+
+		if (! $lockStat ){
+			if ( $areForked ){
+				exit(0);
+			}else{
+				return;
+			}
+		}
+	}
+
+	$actions{$action}->();
+
+	if(
+		$action eq "snapshot"
+		||
+		$action eq "blob_plain"
+	){
+		close(cacheFileBin);
+	}
+
+	flock(cacheFile,LOCK_UN);
+	close(cacheFile);
+
+	if($backgroundCache){
+		flock(cacheFileBG,LOCK_UN);
+		close(cacheFileBG);
+	}
+
+	if ( $areForked ){
+		exit(0);
+	} else {
+		return;
+	}
+}
+
+
+sub cacheWaitForUpdate {
+	my ($action) = @_;
+	my $x = 0;
+	my $max = 10;
+	my $lockStat = 0;
+
+	if( $backgroundCache ){
+		if( -e "$fullhashpath" ){
+			open(cacheFile, '<:utf8', "$fullhashpath");
+			$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);
+			stat(cacheFile);
+			close(cacheFile);
+
+			if( $lockStat && ( (stat(_))[9] > (time - $maxCacheLife) ) ){
+				cacheDisplay($action);
+				return;
+			}
+		}
+	}
+
+	if(
+		$action eq "atom"
+		||
+		$action eq "rss"
+		||
+		$action eq "opml"
+	){
+		do {
+			sleep 2 if $x > 0;
+			open(cacheFile, '<:utf8', "$fullhashpath");
+			$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);
+			close(cacheFile);
+			$x++;
+			$combinedLockStat = $lockStat;
+		} while ((! $combinedLockStat) && ($x < $max));
+
+		if( $x != $max ){
+			cacheDisplay($action);
+		}
+		return;
+	}
+
+	$| = 1;
+
+	print $::cgi->header(-type=>'text/html', -charset => 'utf-8',
+	                   -status=> 200, -expires => 'never');
+
+	print <<EOF;
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www/w3.porg/TR/html4/strict.dtd">
+<!-- git web w/caching interface version $version, (C) 2006-2010, John 'Warthog9' Hawley <warthog9\@kernel.org> -->
+<!-- git core binaries version $git_version -->
+<head>
+<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
+<meta name="generator" content="gitweb/$version git/$git_version"/>
+<meta name="robots" content="index, nofollow"/>
+<meta http-equiv="refresh" content="0"/>
+<title>$title</title>
+</head>
+<body>
+EOF
+
+	print "Generating..";
+	do {
+		print ".";
+		sleep 2 if $x > 0;
+		open(cacheFile, '<:utf8', "$fullhashpath");
+		$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);
+		close(cacheFile);
+		$x++;
+		$combinedLockStat = $lockStat;
+	} while ((! $combinedLockStat) && ($x < $max));
+	print <<EOF;
+</body>
+</html>
+EOF
+	return;
+}
+
+sub cacheDisplay {
+	my ($action) = @_;
+	open(cacheFile, '<:utf8', "$fullhashpath");
+	$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);
+	if (! $lockStat ){
+		close(cacheFile);
+		cacheWaitForUpdate($action);
+	}
+
+	while( <cacheFile> ){
+		print $_;
+	}
+	if(
+		$action eq "snapshot"
+		||
+		$action eq "blob_plain"
+	){
+		open(cacheFileBin, '<', "$fullhashbinpath");
+		binmode STDOUT, ':raw';
+		print <cacheFileBin>;
+		binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+		close(cacheFileBin);
+	}
+	close(cacheFile);
+}
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 50067f2..f809f16 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -67,6 +67,12 @@ div.page_path {
 	border-width: 0px 0px 1px;
 }
 
+div.cachetime {
+	float: left;
+	margin-right: 10px;
+	color: #555555;
+}
+
 div.page_footer {
 	height: 17px;
 	padding: 4px 8px;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 8bb323c..ec95bb9 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -230,6 +230,50 @@ our $git_versions_must_match = 1;
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
+# This enables/disables the caching layer in gitweb.  This currently only supports the
+# 'dumb' file based caching layer, primarily used on git.kernel.org.  this is reasonably
+# effective but it has the downside of requiring a huge amount of disk space if there
+# are a number of repositories involved.  It is not uncommon for git.kernel.org to have
+# on the order of 80G - 120G accumulate over the course of a few months.  It is recommended
+# that the cache directory be periodically completely deleted, and this is safe to perform.
+# Suggested mechanism
+# mv $cacheidr $cachedir.flush;mkdir $cachedir;rm -rf $cachedir.flush
+# Value is binary. 0 = disabled (default), 1 = enabled.
+#
+# Values of caching:
+# 	1 = 'dumb' file based caching used on git.kernel.org
+our $cache_enable = 0;
+
+# Used to set the minimum cache timeout for the dynamic caching algorithm.  Basically
+# if we calculate the cache to be under this number of seconds we set the cache timeout
+# to this minimum.
+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour
+our $minCacheTime = 20;
+
+# Used to set the maximum cache timeout for the dynamic caching algorithm.  Basically
+# if we calculate the cache to exceed this number of seconds we set the cache timeout
+# to this maximum.
+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour
+our $maxCacheTime = 1200;
+
+# If you need to change the location of the caching directory, override this
+# otherwise this will probably do fine for you
+our $cachedir = 'cache';
+
+# If this is set (to 1) cache will do it's best to always display something instead
+# of making someone wait for the cache to update.  This will launch the cacheUpdate
+# into the background and it will lock a <file>.bg file and will only lock the
+# actual cache file when it needs to write into it.  In theory this will make
+# gitweb seem more responsive at the price of possibly stale data.
+our $backgroundCache = 1;
+
+# Used to set the maximum cache file life.  If a cache files last modify time exceeds
+# this value, it will assume that the data is just too old, and HAS to be regenerated
+# instead of trying to display the existing cache data.
+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour
+# 18000 = 5 hours
+our $maxCacheLife = 18000;
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -593,6 +637,11 @@ if (defined $maxload && get_loadavg() > $maxload) {
 	die_error(503, "The load average on the server is too high");
 }
 
+#
+# Includes
+#
+do 'cache.pm';
+
 # version of the core git binary
 our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 $number_of_git_cmds++;
@@ -994,7 +1043,7 @@ if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
     !$project) {
 	die_error(400, "Project needed");
 }
-$actions{$action}->();
+cache_fetch($action);
 exit;
 
 ## ======================================================================
@@ -3200,7 +3249,9 @@ sub git_header_html {
 	# support xhtml+xml but choking when it gets what it asked for.
 	if (defined $cgi->http('HTTP_ACCEPT') &&
 	    $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
-	    $cgi->Accept('application/xhtml+xml') != 0) {
+	    $cgi->Accept('application/xhtml+xml') != 0
+	    &&
+	    $cache_enable == 0) {
 		$content_type = 'application/xhtml+xml';
 	} else {
 		$content_type = 'text/html';
@@ -3344,6 +3395,7 @@ sub git_footer_html {
 	my $feed_class = 'rss_logo';
 
 	print {$output_handler} "<div class=\"page_footer\">\n";
+	print {$output_handler} "<div class=\"cachetime\">Cache Last Updated: ". gmtime( time ) ." GMT</div>\n";
 	if (defined $project) {
 		my $descr = git_get_project_description($project);
 		if (defined $descr) {
@@ -3424,7 +3476,7 @@ sub die_error {
 	my $extra = shift;
 
 	# The output handlers for die_error need to be reset to STDOUT
-	# so that half the message isn't being output to random and 
+	# so that half the message isn't being output to random and
 	# half to STDOUT as expected.  This is mainly for the benefit
 	# of using git_header_html() and git_footer_html() since those
 	# internaly use the indirect print handler.
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 7/9] gitweb: cleanup error message produced by undefined $site_header
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-7-git-send-email-warthog9@eaglescrag.net>

If $site_header is not defined you get extraneous errors in the web
logs:

[Wed Jan 13 16:55:42 2010] [error] [client ::1] [Wed Jan 13 16:55:42 2010] gitweb.cgi: Use of uninitialized value $site_header in -f at /var/www/gitweb/gitweb.cgi line 3287., referer: http://git/gitweb.cgi

for example.  This ensures that the variable is defined before trying to use it.
---
 gitweb/gitweb.perl |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 07fdeb5..c4a177d 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3277,7 +3277,7 @@ EOF
 	print "</head>\n" .
 	      "<body>\n";
 
-	if (-f $site_header) {
+	if ($site_header && -f $site_header) {
 		insert_file($site_header);
 	}
 
-- 
1.6.5.2

^ permalink raw reply related

* [PATCH 4/9] gitweb: Makefile improvements
From: John 'Warthog9' Hawley @ 2010-01-14  1:23 UTC (permalink / raw)
  To: git
In-Reply-To: <1263432185-21334-4-git-send-email-warthog9@eaglescrag.net>

From: John 'Warthog9' Hawley <warthog9@kernel.org>

This commit adjust the main Makefile so you can simply run

     make gitweb

which in turn calls gitweb/Makefile.  This means that in order to
generate gitweb, you can simply run 'make' from gitweb subdirectory:

     cd gitweb
     make

Targets gitweb/gitweb.cgi and (dependent on JSMIN being defined)
gitweb/gitweb.min.js in main Makefile are preserved for backward
compatibility.

Signed-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
 Makefile        |   65 +++++----------------------
 gitweb/Makefile |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+), 53 deletions(-)
 create mode 100644 gitweb/Makefile

diff --git a/Makefile b/Makefile
index be18389..9f069a8 100644
--- a/Makefile
+++ b/Makefile
@@ -282,29 +282,6 @@ pathsep = :
 # JavaScript minifier invocation that can function as filter
 JSMIN =
 
-# default configuration for gitweb
-GITWEB_CONFIG = gitweb_config.perl
-GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
-GITWEB_HOME_LINK_STR = projects
-GITWEB_SITENAME =
-GITWEB_PROJECTROOT = /pub/git
-GITWEB_PROJECT_MAXDEPTH = 2007
-GITWEB_EXPORT_OK =
-GITWEB_STRICT_EXPORT =
-GITWEB_BASE_URL =
-GITWEB_LIST =
-GITWEB_HOMETEXT = indextext.html
-GITWEB_CSS = gitweb.css
-GITWEB_LOGO = git-logo.png
-GITWEB_FAVICON = git-favicon.png
-ifdef JSMIN
-GITWEB_JS = gitweb.min.js
-else
-GITWEB_JS = gitweb.js
-endif
-GITWEB_SITE_HEADER =
-GITWEB_SITE_FOOTER =
-
 export prefix bindir sharedir sysconfdir
 
 CC = gcc
@@ -1535,6 +1512,11 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
 	chmod +x $@+ && \
 	mv $@+ $@
 
+
+.PHONY: gitweb
+gitweb:
+	$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
+
 ifdef JSMIN
 OTHER_PROGRAMS += gitweb/gitweb.cgi   gitweb/gitweb.min.js
 gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js
@@ -1542,30 +1524,13 @@ else
 OTHER_PROGRAMS += gitweb/gitweb.cgi
 gitweb/gitweb.cgi: gitweb/gitweb.perl
 endif
-	$(QUIET_GEN)$(RM) $@ $@+ && \
-	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-	    -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-	    -e 's|++GIT_BINDIR++|$(bindir)|g' \
-	    -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
-	    -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
-	    -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-	    -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-	    -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
-	    -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
-	    -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
-	    -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
-	    -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
-	    -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
-	    -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
-	    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
-	    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
-	    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
-	    -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
-	    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
-	    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
-	    $< >$@+ && \
-	chmod +x $@+ && \
-	mv $@+ $@
+	$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
+
+ifdef JSMIN
+gitweb/gitweb.min.js: gitweb/gitweb.js
+	$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
+endif # JSMIN
+
 
 git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
 	$(QUIET_GEN)$(RM) $@ $@+ && \
@@ -1592,12 +1557,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
 	mv $@+ $@
 endif # NO_PERL
 
-
-ifdef JSMIN
-gitweb/gitweb.min.js: gitweb/gitweb.js
-	$(QUIET_GEN)$(JSMIN) <$< >$@
-endif # JSMIN
-
 ifndef NO_PYTHON
 $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS
 $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
diff --git a/gitweb/Makefile b/gitweb/Makefile
new file mode 100644
index 0000000..c9eb1ee
--- /dev/null
+++ b/gitweb/Makefile
@@ -0,0 +1,129 @@
+# The default target of this Makefile is...
+all::
+
+# Define V=1 to have a more verbose compile.
+#
+# Define JSMIN to point to JavaScript minifier that functions as
+# a filter to have gitweb.js minified.
+#
+
+prefix ?= $(HOME)
+bindir ?= $(prefix)/bin
+RM ?= rm -f
+
+# JavaScript minifier invocation that can function as filter
+JSMIN ?=
+
+# default configuration for gitweb
+GITWEB_CONFIG = gitweb_config.perl
+GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
+GITWEB_HOME_LINK_STR = projects
+GITWEB_SITENAME =
+GITWEB_PROJECTROOT = /pub/git
+GITWEB_PROJECT_MAXDEPTH = 2007
+GITWEB_EXPORT_OK =
+GITWEB_STRICT_EXPORT =
+GITWEB_BASE_URL =
+GITWEB_LIST =
+GITWEB_HOMETEXT = indextext.html
+GITWEB_CSS = gitweb.css
+GITWEB_LOGO = git-logo.png
+GITWEB_FAVICON = git-favicon.png
+ifdef JSMIN
+GITWEB_JS = gitweb.min.js
+else
+GITWEB_JS = gitweb.js
+endif
+GITWEB_SITE_HEADER =
+GITWEB_SITE_FOOTER =
+
+# include user config
+-include ../config.mak.autogen
+-include ../config.mak
+
+# determine version
+../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+	$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
+
+-include ../GIT-VERSION-FILE
+
+### Build rules
+
+SHELL_PATH ?= $(SHELL)
+PERL_PATH  ?= /usr/bin/perl
+
+# Shell quote;
+bindir_SQ = $(subst ','\'',$(bindir))         #'
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) #'
+PERL_PATH_SQ  = $(subst ','\'',$(PERL_PATH))  #'
+
+# Quiet generation (unless V=1)
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+	QUIET          = @
+	QUIET_GEN      = $(QUIET)echo '   ' GEN $@;
+	QUIET_SUBDIR0  = +@subdir=
+	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+	                 $(MAKE) $(PRINT_DIR) -C $$subdir
+	export V
+	export QUIET
+	export QUIET_GEN
+	export QUIET_SUBDIR0
+	export QUIET_SUBDIR1
+endif
+endif
+
+all:: gitweb.cgi
+
+ifdef JSMIN
+FILES=gitweb.cgi gitweb.min.js
+gitweb.cgi: gitweb.perl gitweb.min.js
+else # !JSMIN
+FILES=gitweb.cgi
+gitweb.cgi: gitweb.perl
+endif # JSMIN
+
+gitweb.cgi:
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+	    -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
+	    -e 's|++GIT_BINDIR++|$(bindir)|g' \
+	    -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+	    -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
+	    -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
+	    -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
+	    -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+	    -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
+	    -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+	    -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
+	    -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
+	    -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
+	    -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
+	    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
+	    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
+	    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+	    -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
+	    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
+	    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
+	    $< >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
+
+ifdef JSMIN
+gitweb.min.js: gitweb.js
+	$(QUIET_GEN)$(JSMIN) <$< >$@
+endif # JSMIN
+
+clean:
+	$(RM) $(FILES)
+
+.PHONY: all clean .FORCE-GIT-VERSION-FILE
-- 
1.6.5.2

^ permalink raw reply related

* [RFC] Git Wiki Move
From: Petr Baudis @ 2010-01-14  1:24 UTC (permalink / raw)
  To: git
In-Reply-To: <20100113232908.GA3299@machine.or.cz>

  Hi!

On Thu, Jan 14, 2010 at 12:29:08AM +0100, Petr Baudis wrote:
> I would like to notify you that unfortunately, Czech UPC terminated the
> sponsorship of the hardware and connectivity hosting the Git Wiki and
> repo.or.cz (after generously donating it for several years).

  ...please scratch the Git Wiki part, you would be supporting just
repo.or.cz - we are considering to move the Git wiki to wiki.kernel.org
MediaWiki installation and I would like to ask if anyone disagrees with
this. The motivation is that:

	(i) wiki.kernel.org is actually maintained! Thus, there should
	be less spam or upgrade issues and better support in case of
	problems.

	(ii) Also, I personally think MediaWiki is so much nicer than
	ikiwiki...

	(iii) ...and OBTW, no CamelCase!

  Of course, there will be compatibility redirects.


  P.S.: Sorry for such hasty mail, I just want to avoid extracting money
from someone on false pretenses. ;-) Let me know if you already sent
some money and wished to support just the wiki.

				Petr "Pasky" Baudis

^ permalink raw reply

* Re: [PATCH] git push --track
From: Tay Ray Chuan @ 2010-01-14  1:27 UTC (permalink / raw)
  To: Rudolf Polzer; +Cc: git
In-Reply-To: <op.u6g8jnixg402ra@nb-04>

Hi,

On Wed, Jan 13, 2010 at 11:12 PM, Rudolf Polzer <divVerent@alientrap.org> wrote:
> Hi,
>
> I'd like a feature to automatically "transform" a non-tracking local branch
> into a tracking branch on push. A patch to do that is attached.
>
> Usage:
>
> git branch mybranch
> git checkout mybranch
> ...
> git push --track origin mybranch:mybranch
>
> will not just perform the push, but also write a block
>
> [branch "mybranch"]
>        remote = origin
>        merge = refs/heads/mybranch
>
> to the git configuration so the branch becomes tracking.
>
> This should be a simpler alternative to the otherwise usual procedure
>
> git push origin mybranch:mybranch
> git config branch.mybranch.remote origin
> git config branch.mybranch.merge refs/heads/mybranch
>
> Are there any chances for this getting added to official git - or an
> alternate convenient way convert a local to a tracking branch?

before I put up my comments on the patch, I wonder if git-push is the
best place to add this feature, as git-push usually deals with
"pushing" data to another repo.

I think git-branch would be a better place to do this.

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: [PATCH] git push --track
From: Miles Bader @ 2010-01-14  1:35 UTC (permalink / raw)
  To: Tay Ray Chuan; +Cc: Rudolf Polzer, git
In-Reply-To: <be6fef0d1001131727r128c7727td2b948018d308719@mail.gmail.com>

Tay Ray Chuan <rctay89@gmail.com> writes:
> before I put up my comments on the patch, I wonder if git-push is the
> best place to add this feature, as git-push usually deals with
> "pushing" data to another repo.
>
> I think git-branch would be a better place to do this.

No.  I think push is absolutely the right place to do it.

I very often create local branches and only later decide that I'd also
like to push them to a remote -- but just as often, or more often, I
never do so.  It would be extremely annoying if I had to make that
decision at branch-time.

-Miles

-- 
Pray, v. To ask that the laws of the universe be annulled in behalf of a
single petitioner confessedly unworthy.

^ permalink raw reply

* Re: [PATCH] git push --track
From: Tay Ray Chuan @ 2010-01-14  1:37 UTC (permalink / raw)
  To: Miles Bader; +Cc: Rudolf Polzer, git
In-Reply-To: <87eilt5uzx.fsf@catnip.gol.com>

Hi,

On Thu, Jan 14, 2010 at 9:35 AM, Miles Bader <miles@gnu.org> wrote:
> Tay Ray Chuan <rctay89@gmail.com> writes:
>> before I put up my comments on the patch, I wonder if git-push is the
>> best place to add this feature, as git-push usually deals with
>> "pushing" data to another repo.
>>
>> I think git-branch would be a better place to do this.
>
> No.  I think push is absolutely the right place to do it.
>
> I very often create local branches and only later decide that I'd also
> like to push them to a remote -- but just as often, or more often, I
> never do so.  It would be extremely annoying if I had to make that
> decision at branch-time.

I'm not saying that the feature should belong to git-branch and so you
have to make this decision at branch time - I'm saying that since this
has got to do with modifying branches (sort of), it should be in
there.

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: [PATCH] git push --track
From: Miles Bader @ 2010-01-14  1:49 UTC (permalink / raw)
  To: Tay Ray Chuan; +Cc: Rudolf Polzer, git
In-Reply-To: <be6fef0d1001131737i59b2e843ib032c30027520b54@mail.gmail.com>

On Thu, Jan 14, 2010 at 10:37 AM, Tay Ray Chuan <rctay89@gmail.com> wrote:
>> I very often create local branches and only later decide that I'd also
>> like to push them to a remote -- but just as often, or more often, I
>> never do so.  It would be extremely annoying if I had to make that
>> decision at branch-time.
>
> I'm not saying that the feature should belong to git-branch and so you
> have to make this decision at branch time - I'm saying that since this
> has got to do with modifying branches (sort of), it should be in
> there.

I, and it appears other people, want to make this association at
push-time -- that's almost always when I make the decision "I'd like
to track this" -- so the most convenient and intuitive thing would be
to have a --track option to push.

Of course there could _also_ be some sort of branch sub-command (or
another command) to setup or change tracking state without pushing.
But "push --track" is more important for normal usage I think.

-miles

-- 
Do not taunt Happy Fun Ball.

^ permalink raw reply

* Re: [PATCH] git push --track
From: Tay Ray Chuan @ 2010-01-14  1:58 UTC (permalink / raw)
  To: Miles Bader; +Cc: Rudolf Polzer, git
In-Reply-To: <fc339e4a1001131749s4da9526bs13406571773c38fd@mail.gmail.com>

On Thu, Jan 14, 2010 at 9:49 AM, Miles Bader <miles@gnu.org> wrote:
> I, and it appears other people, want to make this association at
> push-time -- that's almost always when I make the decision "I'd like
> to track this" -- so the most convenient and intuitive thing would be
> to have a --track option to push.
>
> Of course there could _also_ be some sort of branch sub-command (or
> another command) to setup or change tracking state without pushing.
> But "push --track" is more important for normal usage I think.

Ok, I see your point.

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: touching a file causes it to be listed using git diff-files
From: Jeff King @ 2010-01-14  3:02 UTC (permalink / raw)
  To: Stephen Bannasch; +Cc: git
In-Reply-To: <p0624080ec7740ddc4caf@[63.138.152.134]>

On Wed, Jan 13, 2010 at 06:57:28PM -0500, Stephen Bannasch wrote:

> If I touch a file in the working directory (only changing it's last-modified) attribute it shows up when running git diff-files.
> 
> If I then run git status followed by git diff-files again it doesn't show up either time.
> 
> Is this an error?

No. For performance reasons[1], plumbing commands like diff-files do not
update the index. You must run "git update-index --refresh" manually.

User-facing porcelain commands like "git diff" and "git status" will
refresh the index automatically. So "git status" will, as a side effect,
refresh the index and impact further calls to diff-files.

-Peff

[1] Refreshing the index needs to stat all of the files. If you are
writing a script using plumbing commands, you probably want to do the
possibly-expensive refresh once at the start of your script, and then
issue many diff commands. This makes a lot of sense for "diff-index",
which otherwise does not need to touch the working tree at all. I'm not
sure how much it helps with diff-files, though, which clearly ends up
stat'ing the working tree file anyway.

^ permalink raw reply

* [PATCH v3] fast-import: tag may point to any object type
From: Dmitry Potapov @ 2010-01-14  4:44 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Junio C Hamano
In-Reply-To: <20100113172456.GB18625@spearce.org>

If you tried to export the official git repository, and then to import it
back then git-fast-import would die complaining that "Mark :1 not a commit".

Accordingly to a generated crash file, Mark 1 is not a commit but a blob,
which is pointed by junio-gpg-pub tag. Because git-tag allows to create such
tags, git-fast-import should import them.

Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
Acked-by: Shawn O. Pearce <spearce@spearce.org>
---

On Wed, Jan 13, 2010 at 09:24:56AM -0800, Shawn O. Pearce wrote:
> Dmitry Potapov <dpotapov@gmail.com> wrote:
> >  
> > +		type = sha1_object_info(sha1, NULL);
> >  		buf = read_object_with_reference(sha1,
> > -			commit_type, &size, sha1);
> > +			typename(type), &size, sha1);
> 
> This is better spelled as:
> 
>   buf = read_sha1_file(sha1, &type, &size);

Thank you for correction.

> 
> But otherwise the patch looks correct now, thanks.
> 
> If you could make this one change, add my
> 
>   Acked-by: Shawn O. Pearce <spearce@spearce.org>
> 
> and resend so Junio can apply, thanks.

 fast-import.c |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/fast-import.c b/fast-import.c
index cd87049..60d0aa2 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2305,6 +2305,7 @@ static void parse_new_tag(void)
 	struct tag *t;
 	uintmax_t from_mark = 0;
 	unsigned char sha1[20];
+	enum object_type type;
 
 	/* Obtain the new tag name from the rest of our command */
 	sp = strchr(command_buf.buf, ' ') + 1;
@@ -2325,19 +2326,18 @@ static void parse_new_tag(void)
 	s = lookup_branch(from);
 	if (s) {
 		hashcpy(sha1, s->sha1);
+		type = OBJ_COMMIT;
 	} else if (*from == ':') {
 		struct object_entry *oe;
 		from_mark = strtoumax(from + 1, NULL, 10);
 		oe = find_mark(from_mark);
-		if (oe->type != OBJ_COMMIT)
-			die("Mark :%" PRIuMAX " not a commit", from_mark);
+		type = oe->type;
 		hashcpy(sha1, oe->sha1);
 	} else if (!get_sha1(from, sha1)) {
 		unsigned long size;
 		char *buf;
 
-		buf = read_object_with_reference(sha1,
-			commit_type, &size, sha1);
+		buf = read_sha1_file(sha1, &type, &size);
 		if (!buf || size < 46)
 			die("Not a valid commit: %s", from);
 		free(buf);
@@ -2362,7 +2362,7 @@ static void parse_new_tag(void)
 		    "object %s\n"
 		    "type %s\n"
 		    "tag %s\n",
-		    sha1_to_hex(sha1), commit_type, t->name);
+		    sha1_to_hex(sha1), typename(type), t->name);
 	if (tagger)
 		strbuf_addf(&new_data,
 			    "tagger %s\n", tagger);
-- 
1.6.6.137.g1acb

^ permalink raw reply related

* Re: touching a file causes it to be listed using git diff-files
From: Stephen Bannasch @ 2010-01-14  5:01 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20100114030204.GB1878@coredump.intra.peff.net>

At 10:02 PM -0500 1/13/10, Jeff King wrote:
>On Wed, Jan 13, 2010 at 06:57:28PM -0500, Stephen Bannasch wrote:
>
>> If I touch a file in the working directory (only changing it's last-modified) attribute it shows up when running git diff-files.
>>
>> If I then run git status followed by git diff-files again it doesn't show up either time.
>>
>> Is this an error?
>
>No. For performance reasons[1], plumbing commands like diff-files do not
>update the index. You must run "git update-index --refresh" manually.
>
>User-facing porcelain commands like "git diff" and "git status" will
>refresh the index automatically. So "git status" will, as a side effect,
>refresh the index and impact further calls to diff-files.
>
>-Peff
>
>[1] Refreshing the index needs to stat all of the files. If you are
>writing a script using plumbing commands, you probably want to do the
>possibly-expensive refresh once at the start of your script, and then
>issue many diff commands. This makes a lot of sense for "diff-index",
>which otherwise does not need to touch the working tree at all. I'm not
>sure how much it helps with diff-files, though, which clearly ends up
>stat'ing the working tree file anyway.

Jeff,

Thanks for that explanation.

Do you know if there is a plumbing command that will stat all of the files without listing the files that actually do need updating?

In the case where file1 has been only touched but file2 has been changed (and both are tracked) I'd like to stat both filesand then run diff-files as plumbing to see that only file2 has been changed in the working directory.

'git diff' also stats the files but strangely 'git diff --quiet' doesn't ???

In this example .snarfit.yml has real changes.

  $ touch nowebdb.cs

  $ git diff-files
  :100644 100644 22dab3b1c864d808da4d2be40196250ba879f68f 0000000000000000000000000000000000000000 M      .snarfit.yml
  :100644 100644 57982f13c69a91e3341d5b06021d31944633b5a3 0000000000000000000000000000000000000000 M      nowebdb.css

  $ git diff --quiet

  $ git diff-files
  :100644 100644 22dab3b1c864d808da4d2be40196250ba879f68f 0000000000000000000000000000000000000000 M      .snarfit.yml
  :100644 100644 57982f13c69a91e3341d5b06021d31944633b5a3 0000000000000000000000000000000000000000 M      nowebdb.css

  $ git diff --shortstat
   1 files changed, 2 insertions(+), 2 deletions(-)

  $ git diff-files
  :100644 100644 22dab3b1c864d808da4d2be40196250ba879f68f 0000000000000000000000000000000000000000 M      .snarfit.yml

I thought adding '-q' to update-index would cause it to run quietly but that is not the case:

  $ git diff-files
  :100644 100644 22dab3b1c864d808da4d2be40196250ba879f68f 0000000000000000000000000000000000000000 M      .snarfit.yml
  :100644 100644 57982f13c69a91e3341d5b06021d31944633b5a3 0000000000000000000000000000000000000000 M      nowebdb.css

  $ git update-index --refresh -q
  .snarfit.yml: needs update

  $ git diff-files
  :100644 100644 22dab3b1c864d808da4d2be40196250ba879f68f 0000000000000000000000000000000000000000 M      .snarfit.yml

^ permalink raw reply

* Re: [PATCH 1/5] MSVC: Windows-native implementation for subset of Pthreads API
From: Dmitry Potapov @ 2010-01-14  5:12 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: kusmabite, msysgit, git, Andrzej K. Haczewski
In-Reply-To: <201001131940.43868.j6t@kdbg.org>

On Wed, Jan 13, 2010 at 07:40:43PM +0100, Johannes Sixt wrote:
> On Mittwoch, 13. Januar 2010, Dmitry Potapov wrote:
> > On Tue, Jan 12, 2010 at 10:13:38PM +0100, Johannes Sixt wrote:
> > > In particular, it doesn't say that it is atomic WRT reads such as we have
> > >
> > > here:
> > > > >> +     /* we're done waiting, so make sure we decrease waiters count
> > > > >> */ +     EnterCriticalSection(&cond->waiters_lock);
> > > > >> +     --cond->waiters;
> > > > >> +     LeaveCriticalSection(&cond->waiters_lock);
> >
> > and these lines should be replaced with
> >
> >   InterlockedDecrement(&cond->waiters)
> 
> Ah, yes, of course. I quoted the wrong section, sorry. By "atomic WRT reads" I 
> meant this snippet:
> 
> >> +     EnterCriticalSection(&cond->waiters_lock);
> >> +     have_waiters = cond->waiters > 0;
> >> +     LeaveCriticalSection(&cond->waiters_lock);
> 
> Is there "InterlockedRead()"? I suppose no, but I would get confirmation that 
> a simple memory mov instruction is atomic WRT Interlocked* functions.

If I were writing Interlocked API, I would certainly add InterlockedRead()
and InterlockedWrite() functions, but somehow Microsoft decided that these
functions are redundant. Instead, they provided the following comment:
"Simple reads and writes to properly-aligned 32-bit variables are atomic
operations."
http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx

If an operation is atomic, it means that no matter what else is happening
on the system, this operation will performed atomically WRT with any other.
So, yes, the 'mov' instruction is atomic WRT Interlocked functions, no
matter how Interlocked functions are implemented.

As to your concern about gcc doing something different. Let's take a look
how atomic_read is implemented in the Linux kernel:

arch/alpha/include/asm/atomic.h:
#define atomic_read(v)		((v)->counter + 0)

arch/arm/include/asm/atomic.h
#define atomic_read(v)	((v)->counter)

arch/avr32/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/blackfin/include/asm/atomic.h
#define atomic_read(v)	__raw_uncached_fetch_asm(&(v)->counter)

arch/cris/include/asm/atomic.h
#define atomic_read(v) ((v)->counter)

arch/frv/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/h8300/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/ia64/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/m32r/include/asm/atomic.h
#define atomic_read(v)	((v)->counter)

arch/m68k/include/asm/atomic_mm.h
#define atomic_read(v)		((v)->counter)

arch/m68k/include/asm/atomic_no.h
#define atomic_read(v)		((v)->counter)

arch/mips/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/mn10300/include/asm/atomic.h
#define atomic_read(v)	((v)->counter)

arch/parisc/include/asm/atomic.h
static __inline__ int atomic_read(const atomic_t *v)
{
	return v->counter;
}

arch/powerpc/include/asm/atomic.h
static __inline__ int atomic_read(const atomic_t *v)
{
	int t;

	__asm__ __volatile__("lwz%U1%X1 %0,%1" : "=r"(t) : "m"(v->counter));

	return t;
}

arch/s390/include/asm/atomic.h
static inline int atomic_read(const atomic_t *v)
{
	barrier();
	return v->counter;
}

arch/sh/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

arch/sparc/include/asm/atomic_32.h
#define atomic_read(v)          ((v)->counter)

arch/sparc/include/asm/atomic_64.h
#define atomic_read(v)		((v)->counter)

arch/x86/include/asm/atomic_32.h
static inline int atomic_read(const atomic_t *v)
{
	return v->counter;
}

arch/x86/include/asm/atomic_64.h
static inline int atomic_read(const atomic_t *v)
{
	return v->counter;
}

arch/xtensa/include/asm/atomic.h
#define atomic_read(v)		((v)->counter)

===

You see, except PowerPC and s390, it is just 'mov' written in C.
Moreover, if you look at git log, you will see that using asm
for PowerPC is just a matter of optimization:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=9f0cbea0d8cc47801b853d3c61d0e17475b0cc89

As to s390, I have no idea why it uses barrier(), but I do not think
that Windows will ever run on this obsolete architecture, so I don't
even care to look at it closer...

In fact, ability to read and write some integer type is a requirement of
the C standard (at least, for C99):

Section 7.14, paragraph 2:
"The type defined is
       sig_atomic_t
which is the (possibly volatile-qualified) integer type of an object
that can be accessed as an atomic entity, even in the presence of
asynchronous interrupts."

Section 7.18.3, paragraph 3:
"If sig_atomic_t (see 7.14) is defined as a signed integer type, the
value of SIG_ATOMIC_MIN shall be no greater than −127 and the value of
SIG_ATOMIC_MAX shall be no less than 127; otherwise, sig_atomic_t is
defined as an unsigned integer type, and the value of SIG_ATOMIC_MIN
shall be 0 and the value of SIG_ATOMIC_MAX shall be no less than 255."

So, for any architecture where you can use C, it should exist some
integer type that can be read and written atomically (though, it can
be as small as one byte).

Finally, there is a paranoiac implementation of InterlockedRead(&foo):

   result = InterlockedAdd(&foo, 0)

but, IMHO, it is pathetic...


I hope I have explained well enough why I can vouch that the above
assignment works atomically WRT any Interlock function.


Dmitry

^ permalink raw reply

* Re: [PATCH] git push --track
From: Tay Ray Chuan @ 2010-01-14  5:21 UTC (permalink / raw)
  To: Rudolf Polzer
  Cc: Ilari Liusvaara, Matthieu Moy, Miles Bader, Johannes Schindelin,
	git
In-Reply-To: <op.u6haiiiog402ra@nb-04>

Hi,

generally, it would be better if you could add some tests for this.

If I'm not wrong, the place to put it would be t5516-fetch-push.sh.

On Wed, Jan 13, 2010 at 11:55 PM, Rudolf Polzer <divVerent@alientrap.org> wrote:
> On Wed, 13 Jan 2010 16:43:10 +0100, Ilari Liusvaara
> <ilari.liusvaara@elisanet.fi> wrote:

please don't drop people from the Cc list - especially when you're
replying to somebody!

> From 123598516c7d4e1f83591e8dae64e2c76dc87c90 Mon Sep 17 00:00:00 2001
> From: Rudolf Polzer <divVerent@alientrap.org>
> Date: Wed, 13 Jan 2010 16:42:04 +0100
> Subject: [PATCH 1/2] Add a feature "git push --track" to automatically make
> the pushed branches tracking

Each patch should be sent out in its own mail. (As Matthieu has
recommended, you should check out Documentation/SubmittingPatches.)

>  static const char * const push_usage[] = {
> @@ -115,6 +116,36 @@ static int push_with_options(struct transport
> *transport, int flags)
>                fprintf(stderr, "Pushing to %s\n", transport->url);
>        err = transport_push(transport, refspec_nr, refspec, flags,
>                             &nonfastforward);
> +       if (err == 0 && flags & TRANSPORT_PUSH_TRACK) {
> +               struct ref *remote_refs =
> +                       transport->get_refs_list(transport, 1);
> +               struct ref *local_refs = get_local_heads();
> +               int match_flags = 0;
> +               if (flags & TRANSPORT_PUSH_ALL)
> +                       match_flags |= MATCH_REFS_ALL;
> +               if (flags & TRANSPORT_PUSH_MIRROR)
> +                       match_flags |= MATCH_REFS_MIRROR;
> +               if(!(flags & TRANSPORT_PUSH_DRY_RUN))
> +               if(!match_refs(local_refs, &remote_refs, refspec_nr,
> refspec,
> +                                       match_flags)) {

It would be better if you can move this to
transport.c::transport_push(). It repeats what's already there, so you
don't have to configure match_flags, nor call match_refs, etc.

> +                       struct ref *next = remote_refs;
> +                       while(next) {
> [snip]
> +                               next = next->next;

In most places, this is done like this:

  struct ref* ref;
  for (ref = remote_refs; ref; ref = ref->next) {
    ...
  }

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: touching a file causes it to be listed using git diff-files
From: Jeff King @ 2010-01-14  5:26 UTC (permalink / raw)
  To: Stephen Bannasch; +Cc: git
In-Reply-To: <p06240810c774498d4a2e@[63.138.152.134]>

On Thu, Jan 14, 2010 at 12:01:46AM -0500, Stephen Bannasch wrote:

> Do you know if there is a plumbing command that will stat all of the
> files without listing the files that actually do need updating?
> 
> In the case where file1 has been only touched but file2 has been
> changed (and both are tracked) I'd like to stat both filesand then run
> diff-files as plumbing to see that only file2 has been changed in the
> working directory.

Judging from the scripts in git itself, I think the recommended practice
is to simply "git update-index --refresh -q >/dev/null".

> 'git diff' also stats the files but strangely 'git diff --quiet' doesn't ???

I can't reproduce that behavior here on the current "master". Old
versions of git used to not do the index refresh for "diff". What
version of git are you using?

-Peff

^ permalink raw reply

* Re: [PATCH v4 3/3] Smart-http tests: Test http-backend without curl or a webserver
From: Michael Haggerty @ 2010-01-14  5:27 UTC (permalink / raw)
  To: Tarmigan Casebolt; +Cc: Junio C Hamano, Shawn O. Pearce, git
In-Reply-To: <1262468639-8847-1-git-send-email-tarmigan+git@gmail.com>

Tarmigan Casebolt wrote:
> This reuses many of the tests from the old t5560 but runs those tests
> without curl or a webserver.  This will hopefully increase the testing
> coverage for http-backend because it does not require users to set
> GIT_TEST_HTTPD.
> 
> Signed-off-by: Tarmigan Casebolt <tarmigan+git@gmail.com>

I'm not sure which version of this patch is currently committed, but in
any case test t/t5560-http-backend-noserver.sh is broken for me in
"next".  According to git-bisect, the guilty commit is

fd0a8c2e6428acb883bf4707de54b3e026c57455 'Smart-http tests: Test
http-backend without curl or a webserver'

I'm not familiar with this part of the code and I haven't done any
special setup (e.g., I have not set GIT_TEST_HTTPD, whatever that is).
Simply checking out, "make"ing, then running
t5560-http-backend-noserver.sh results in 13/14 failures starting with
test 2:

> 
> * expecting success: 
> 	log_div "refs/heads/master"
> 	GET refs/heads/master "404 Not Found"
> 
> --- exp	2010-01-14 05:21:34.000000000 +0000
> +++ act	2010-01-14 05:21:34.000000000 +0000
> @@ -1 +1 @@
> -Status: 404 Not Found
> +Status: 500 Internal Server Error
> * FAIL 2: direct refs/heads/master not found

This failure is on Ubuntu Linux 8.04 "hardy".

Please let me know if I am doing something wrong or if you would like
any more information.

Michael

^ permalink raw reply

* Re: Problem: fatal oops during git fetch
From: Dmitry Potapov @ 2010-01-14  5:28 UTC (permalink / raw)
  To: Tilo Schwarz; +Cc: git
In-Reply-To: <op.u6hrkho1a8ed4e@dellschleppa>

On Wed, Jan 13, 2010 at 11:03:43PM +0100, Tilo Schwarz wrote:
> 
> Yesterday I tried to fetch bug fixes from one remote machine to the main  
> development machine being on branch 'test' using
> 
> git fetch remote/test
> 
> I got something like
> 
> fatal: oops 'SHA1'
> Fetch failed
> 
> Any ideas what happened here?

My guess is that the remote repository is corrupted, and the specified SHA1
object was not found or it was corrupted. Try to run:

  git fsck --full

in the remote repository.


Dmitry

^ permalink raw reply

* Re: Syncing a git working tree with Dropbox?
From: Tay Ray Chuan @ 2010-01-14  5:39 UTC (permalink / raw)
  To: chombee; +Cc: git
In-Reply-To: <20100113235718.GA7033@dulip>

Hi,

On Thu, Jan 14, 2010 at 7:57 AM, chombee <chombee@lavabit.com> wrote:
> My idea is that I keep my .git folder safely outside of my Dropbox
> folder, but my git repository has a detached working tree that is
> located in the Dropbox folder. On machine B it would be the same setup.
> So the two machines each have their own clone of the git repo and these
> are synchronised by git push and git pull with a 'central' remote repo.
> But the two clones share the same working tree, or more accurately their
> working trees are synced by Dropbox.
>
> The working tree is just files, I don't see how it's different from
> Dropbox syncing any other files. Dropbox and git ought not to collide in
> any way. So this should work fine shouldn't it?

Your changes in git (like new commits) won't be synced.

-- 
Cheers,
Ray Chuan

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox