summaryrefslogtreecommitdiff
path: root/gitweb
diff options
context:
space:
mode:
Diffstat (limited to 'gitweb')
-rw-r--r--gitweb/INSTALL165
-rw-r--r--gitweb/Makefile2
-rw-r--r--gitweb/README141
-rwxr-xr-xgitweb/gitweb.perl663
-rw-r--r--gitweb/static/js/blame_incremental.js79
5 files changed, 590 insertions, 460 deletions
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 4964a679b3..f5efe7454c 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -25,11 +25,25 @@ The above example assumes that your web server is configured to run
scripts).
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+The following optional Perl modules are required for extra features
+ - Digest::MD5 - for gravatar support
+ - CGI::Fast and FCGI - for running gitweb as FastCGI script
+ - HTML::TagCloud - for fancy tag cloud in project list view
+ - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
+
+
Build time configuration
------------------------
-See also "How to configure gitweb for your local system" in README
-file for gitweb (in gitweb/README).
+See also "How to configure gitweb for your local system" section below.
- There are many configuration variables which affect building of
gitweb.cgi; see "default configuration for gitweb" section in main
@@ -73,6 +87,125 @@ file for gitweb (in gitweb/README).
substitute gitweb.min.js and gitweb.min.css for all uses of gitweb.js and
gitweb.css in the help files.
+
+How to configure gitweb for your local system
+---------------------------------------------
+
+You can specify the following configuration variables when building GIT:
+
+ * GIT_BINDIR
+ Points where to find the git executable. You should set it up to
+ the place where the git binary was installed (usually /usr/bin) if you
+ don't install git from sources together with gitweb. [Default: $(bindir)]
+ * GITWEB_SITENAME
+ Shown in the title of all generated pages, defaults to the server name
+ (SERVER_NAME CGI environment variable) if not set. [No default]
+ * GITWEB_PROJECTROOT
+ The root directory for all projects shown by gitweb. Must be set
+ correctly for gitweb to find repositories to display. See also
+ "Gitweb repositories" in the INSTALL file for gitweb. [Default: /pub/git]
+ * GITWEB_PROJECT_MAXDEPTH
+ The filesystem traversing limit for getting the project list; the number
+ is taken as depth relative to the projectroot. It is used when
+ GITWEB_LIST is a directory (or is not set; then project root is used).
+ This is meant to speed up project listing on large work trees by limiting
+ search depth. [Default: 2007]
+ * GITWEB_LIST
+ Points to a directory to scan for projects (defaults to project root
+ if not set / if empty) or to a file with explicit listing of projects
+ (together with projects' ownership). See "Generating projects list
+ using gitweb" in INSTALL file for gitweb to find out how to generate
+ such file from scan of a directory. [No default, which means use root
+ directory for projects]
+ * GITWEB_EXPORT_OK
+ Show repository only if this file exists (in repository). Only
+ effective if this variable evaluates to true. [No default / Not set]
+ * GITWEB_STRICT_EXPORT
+ Only allow viewing of repositories also shown on the overview page.
+ This for example makes GITWEB_EXPORT_OK to decide if repository is
+ available and not only if it is shown. If GITWEB_LIST points to
+ file with list of project, only those repositories listed would be
+ available for gitweb. [No default]
+ * GITWEB_HOMETEXT
+ Points to an .html file which is included on the gitweb project
+ overview page ('projects_list' view), if it exists. Relative to
+ gitweb.cgi script. [Default: indextext.html]
+ * GITWEB_SITE_HEADER
+ Filename of html text to include at top of each page. Relative to
+ gitweb.cgi script. [No default]
+ * GITWEB_SITE_FOOTER
+ Filename of html text to include at bottom of each page. Relative to
+ gitweb.cgi script. [No default]
+ * GITWEB_HOME_LINK_STR
+ String of the home link on top of all pages, leading to $home_link
+ (usually main gitweb page, which means projects list). Used as first
+ part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
+ [Default: projects]
+ * GITWEB_SITENAME
+ Name of your site or organization to appear in page titles. Set it
+ to something descriptive for clearer bookmarks etc. If not set
+ (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
+ SERVER_NAME CGI environment variable is not set (e.g. if running
+ gitweb as standalone script). [No default]
+ * GITWEB_BASE_URL
+ Git base URLs used for URL to where fetch project from, i.e. full
+ URL is "$git_base_url/$project". Shown on projects summary page.
+ Repository URL for project can be also configured per repository; this
+ takes precedence over URLs composed from base URL and a project name.
+ Note that you can setup multiple base URLs (for example one for
+ git:// protocol access, another for http:// access) from the gitweb
+ config file. [No default]
+ * GITWEB_CSS
+ Points to the location where you put gitweb.css on your web server
+ (or to be more generic, the URI of gitweb stylesheet). Relative to the
+ base URI of gitweb. Note that you can setup multiple stylesheets from
+ the gitweb config file. [Default: static/gitweb.css (or
+ static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
+ is used)]
+ * GITWEB_JS
+ Points to the location where you put gitweb.js on your web server
+ (or to be more generic URI of JavaScript code used by gitweb).
+ Relative to base URI of gitweb. [Default: static/gitweb.js (or
+ static/gitweb.min.js if JSMIN build variable is defined / JavaScript
+ minifier is used)]
+ * CSSMIN, JSMIN
+ Invocation of a CSS minifier or a JavaScript minifier, respectively,
+ working as a filter (source on standard input, minified result on
+ standard output). If set, it is used to generate a minified version of
+ 'static/gitweb.css' or 'static/gitweb.js', respectively. *Note* that
+ minified files would have *.min.css and *.min.js extension, which is
+ important if you also set GITWEB_CSS and/or GITWEB_JS. [No default]
+ * GITWEB_LOGO
+ Points to the location where you put git-logo.png on your web server
+ (or to be more generic URI of logo, 72x27 size, displayed in top right
+ corner of each gitweb page, and used as logo for Atom feed). Relative
+ to base URI of gitweb. [Default: static/git-logo.png]
+ * GITWEB_FAVICON
+ Points to the location where you put git-favicon.png on your web server
+ (or to be more generic URI of favicon, assumed to be image/png type;
+ web browsers that support favicons (website icons) may display them
+ in the browser's URL bar and next to site name in bookmarks). Relative
+ to base URI of gitweb. [Default: static/git-favicon.png]
+ * GITWEB_CONFIG
+ This Perl file will be loaded using 'do' and can be used to override any
+ of the options above as well as some other options -- see the "Runtime
+ gitweb configuration" section below, and top of 'gitweb.cgi' for their
+ full list and description. If the environment variable GITWEB_CONFIG
+ is set when gitweb.cgi is executed, then the file specified in the
+ environment variable will be loaded instead of the file specified
+ when gitweb.cgi was created. [Default: gitweb_config.perl]
+ * GITWEB_CONFIG_SYSTEM
+ This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
+ does not exist. If the environment variable GITWEB_CONFIG_SYSTEM is set
+ when gitweb.cgi is executed, then the file specified in the environment
+ variable will be loaded instead of the file specified when gitweb.cgi was
+ created. [Default: /etc/gitweb.conf]
+ * HIGHLIGHT_BIN
+ Path to the highlight executable to use (must be the one from
+ http://www.andre-simon.de due to assumptions about parameters and output).
+ Useful if highlight is not installed on your webserver's PATH.
+ [Default: highlight]
+
Build example
~~~~~~~~~~~~~
@@ -98,7 +231,7 @@ Gitweb config file
See also "Runtime gitweb configuration" section in README file
for gitweb (in gitweb/README).
-- You can configure gitweb further using the gitweb configuration file;
+- You can configure gitweb further using the per-instance gitweb configuration file;
by default this is a file named gitweb_config.perl in the same place as
gitweb.cgi script. You can control the default place for the config file
using the GITWEB_CONFIG build configuration variable, and you can set it
@@ -108,6 +241,17 @@ for gitweb (in gitweb/README).
GITWEB_CONFIG_SYSTEM build configuration variable, and override it
through the GITWEB_CONFIG_SYSTEM environment variable.
+ Note that if per-instance configuration file exists, then system-wide
+ configuration is _not used at all_. This is quite untypical and suprising
+ behavior. On the other hand changing current behavior would break backwards
+ compatibility and can lead to unexpected changes in gitweb behavior.
+ Therefore gitweb also looks for common system-wide configuration file,
+ normally /etc/gitweb-common.conf (set during build time using build time
+ configuration variable GITWEB_CONFIG_COMMON, set it at runtime using
+ environment variable with the same name). Settings from per-instance or
+ system-wide configuration file override those from common system-wide
+ configuration file.
+
- The gitweb config file is a fragment of perl code. You can set variables
using "our $variable = value"; text from "#" character until the end
of a line is ignored. See perlsyn(1) for details.
@@ -229,21 +373,6 @@ $projects_list variable in gitweb config):
perl -- /var/www/cgi-bin/gitweb.cgi
-Requirements
-------------
-
- - Core git tools
- - Perl
- - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
- - web server
-
-The following optional Perl modules are required for extra features
- - Digest::MD5 - for gravatar support
- - CGI::Fast and FCGI - for running gitweb as FastCGI script
- - HTML::TagCloud - for fancy tag cloud in project list view
- - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
-
-
Example web server configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/gitweb/Makefile b/gitweb/Makefile
index 5d20515fba..1c85b5fda8 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -20,6 +20,7 @@ INSTALL ?= install
# default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl
GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
+GITWEB_CONFIG_COMMON = /etc/gitweb-common.conf
GITWEB_HOME_LINK_STR = projects
GITWEB_SITENAME =
GITWEB_PROJECTROOT = /pub/git
@@ -129,6 +130,7 @@ GITWEB_REPLACE = \
-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_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|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' \
diff --git a/gitweb/README b/gitweb/README
index a3a697bf55..a9988200d6 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -7,126 +7,33 @@ The one working on:
From the git version 1.4.0 gitweb is bundled with git.
-How to configure gitweb for your local system
----------------------------------------------
-
-See also the "Build time configuration" section in the INSTALL
-file for gitweb (in gitweb/INSTALL).
-
-You can specify the following configuration variables when building GIT:
- * GIT_BINDIR
- Points where to find the git executable. You should set it up to
- the place where the git binary was installed (usually /usr/bin) if you
- don't install git from sources together with gitweb. [Default: $(bindir)]
- * GITWEB_SITENAME
- Shown in the title of all generated pages, defaults to the server name
- (SERVER_NAME CGI environment variable) if not set. [No default]
- * GITWEB_PROJECTROOT
- The root directory for all projects shown by gitweb. Must be set
- correctly for gitweb to find repositories to display. See also
- "Gitweb repositories" in the INSTALL file for gitweb. [Default: /pub/git]
- * GITWEB_PROJECT_MAXDEPTH
- The filesystem traversing limit for getting the project list; the number
- is taken as depth relative to the projectroot. It is used when
- GITWEB_LIST is a directory (or is not set; then project root is used).
- This is meant to speed up project listing on large work trees by limiting
- search depth. [Default: 2007]
- * GITWEB_LIST
- Points to a directory to scan for projects (defaults to project root
- if not set / if empty) or to a file with explicit listing of projects
- (together with projects' ownership). See "Generating projects list
- using gitweb" in INSTALL file for gitweb to find out how to generate
- such file from scan of a directory. [No default, which means use root
- directory for projects]
- * GITWEB_EXPORT_OK
- Show repository only if this file exists (in repository). Only
- effective if this variable evaluates to true. [No default / Not set]
- * GITWEB_STRICT_EXPORT
- Only allow viewing of repositories also shown on the overview page.
- This for example makes GITWEB_EXPORT_OK to decide if repository is
- available and not only if it is shown. If GITWEB_LIST points to
- file with list of project, only those repositories listed would be
- available for gitweb. [No default]
- * GITWEB_HOMETEXT
- Points to an .html file which is included on the gitweb project
- overview page ('projects_list' view), if it exists. Relative to
- gitweb.cgi script. [Default: indextext.html]
- * GITWEB_SITE_HEADER
- Filename of html text to include at top of each page. Relative to
- gitweb.cgi script. [No default]
- * GITWEB_SITE_FOOTER
- Filename of html text to include at bottom of each page. Relative to
- gitweb.cgi script. [No default]
- * GITWEB_HOME_LINK_STR
- String of the home link on top of all pages, leading to $home_link
- (usually main gitweb page, which means projects list). Used as first
- part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
- [Default: projects]
- * GITWEB_SITENAME
- Name of your site or organization to appear in page titles. Set it
- to something descriptive for clearer bookmarks etc. If not set
- (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
- SERVER_NAME CGI environment variable is not set (e.g. if running
- gitweb as standalone script). [No default]
- * GITWEB_BASE_URL
- Git base URLs used for URL to where fetch project from, i.e. full
- URL is "$git_base_url/$project". Shown on projects summary page.
- Repository URL for project can be also configured per repository; this
- takes precedence over URLs composed from base URL and a project name.
- Note that you can setup multiple base URLs (for example one for
- git:// protocol access, another for http:// access) from the gitweb
- config file. [No default]
- * GITWEB_CSS
- Points to the location where you put gitweb.css on your web server
- (or to be more generic, the URI of gitweb stylesheet). Relative to the
- base URI of gitweb. Note that you can setup multiple stylesheets from
- the gitweb config file. [Default: static/gitweb.css (or
- static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
- is used)]
- * GITWEB_LOGO
- Points to the location where you put git-logo.png on your web server
- (or to be more generic URI of logo, 72x27 size, displayed in top right
- corner of each gitweb page, and used as logo for Atom feed). Relative
- to base URI of gitweb. [Default: static/git-logo.png]
- * GITWEB_FAVICON
- Points to the location where you put git-favicon.png on your web server
- (or to be more generic URI of favicon, assumed to be image/png type;
- web browsers that support favicons (website icons) may display them
- in the browser's URL bar and next to site name in bookmarks). Relative
- to base URI of gitweb. [Default: static/git-favicon.png]
- * GITWEB_JS
- Points to the location where you put gitweb.js on your web server
- (or to be more generic URI of JavaScript code used by gitweb).
- Relative to base URI of gitweb. [Default: static/gitweb.js (or
- static/gitweb.min.js if JSMIN build variable is defined / JavaScript
- minifier is used)]
- * GITWEB_CONFIG
- This Perl file will be loaded using 'do' and can be used to override any
- of the options above as well as some other options -- see the "Runtime
- gitweb configuration" section below, and top of 'gitweb.cgi' for their
- full list and description. If the environment variable GITWEB_CONFIG
- is set when gitweb.cgi is executed, then the file specified in the
- environment variable will be loaded instead of the file specified
- when gitweb.cgi was created. [Default: gitweb_config.perl]
- * GITWEB_CONFIG_SYSTEM
- This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
- does not exist. If the environment variable GITWEB_CONFIG_SYSTEM is set
- when gitweb.cgi is executed, then the file specified in the environment
- variable will be loaded instead of the file specified when gitweb.cgi was
- created. [Default: /etc/gitweb.conf]
- * HIGHLIGHT_BIN
- Path to the highlight executable to use (must be the one from
- http://www.andre-simon.de due to assumptions about parameters and output).
- Useful if highlight is not installed on your webserver's PATH.
- [Default: highlight]
-
-
Runtime gitweb configuration
----------------------------
-You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
-(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
-as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
+Gitweb obtains configuration data from the following sources in the
+following order:
+
+1. built-in values (some set during build stage),
+2. common system-wide configuration file (`GITWEB_CONFIG_COMMON`,
+ defaults to '/etc/gitweb-common.conf'),
+3. either per-instance configuration file (`GITWEB_CONFIG`, defaults to
+ 'gitweb_config.perl' in the same directory as the installed gitweb),
+ or if it does not exists then system-wide configuration file
+ (`GITWEB_CONFIG_SYSTEM`, defaults to '/etc/gitweb.conf').
+
+Values obtained in later configuration files override values obtained earlier
+in above sequence.
+
+You can read defaults in system-wide GITWEB_CONFIG_SYSTEM from GITWEB_CONFIG
+by adding
+
+ read_config_file($GITWEB_CONFIG_SYSTEM);
+
+at very beginning of per-instance GITWEB_CONFIG file. In this case
+settings in said per-instance file will override settings from
+system-wide configuration file. Note that read_config_file checks
+itself that the $GITWEB_CONFIG_SYSTEM file exists.
+
The most notable thing that is not configurable at compile time are the
optional features, stored in the '%features' variable.
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 240dd4701c..70a576a626 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -321,6 +321,10 @@ our %feature = (
# Enable text search, which will list the commits which match author,
# committer or commit text to a given string. Enabled by default.
# Project specific override is not supported.
+ #
+ # Note that this controls all search features, which means that if
+ # it is disabled, then 'grep' and 'pickaxe' search would also be
+ # disabled.
'search' => {
'override' => 0,
'default' => [1]},
@@ -328,6 +332,7 @@ our %feature = (
# Enable grep search, which will list the files in currently selected
# tree containing the given string. Enabled by default. This can be
# potentially CPU-intensive, of course.
+ # Note that you need to have 'search' feature enabled too.
# To enable system wide have in $GITWEB_CONFIG
# $feature{'grep'}{'default'} = [1];
@@ -342,6 +347,7 @@ our %feature = (
# Enable the pickaxe search, which will list the commits that modified
# a given string in a file. This can be practical and quite faster
# alternative to 'blame', but still potentially CPU-intensive.
+ # Note that you need to have 'search' feature enabled too.
# To enable system wide have in $GITWEB_CONFIG
# $feature{'pickaxe'}{'default'} = [1];
@@ -659,13 +665,25 @@ sub read_config_file {
return;
}
-our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
sub evaluate_gitweb_config {
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+ our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
+
+ # Protect agains duplications of file names, to not read config twice.
+ # Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
+ # there possibility of duplication of filename there doesn't matter.
+ $GITWEB_CONFIG = "" if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
+ $GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
+
+ # Common system-wide settings for convenience.
+ # Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+ read_config_file($GITWEB_CONFIG_COMMON);
- # use first config file that exists
- read_config_file($GITWEB_CONFIG) or
+ # Use first config file that exists. This means use the per-instance
+ # GITWEB_CONFIG if exists, otherwise use GITWEB_SYSTEM_CONFIG.
+ read_config_file($GITWEB_CONFIG) and return;
read_config_file($GITWEB_CONFIG_SYSTEM);
}
@@ -2508,6 +2526,13 @@ sub git_get_project_config {
# key sanity check
return unless ($key);
+ # only subsection, if exists, is case sensitive,
+ # and not lowercased by 'git config -z -l'
+ if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+ $key = join(".", lc($hi), $mi, lc($lo));
+ } else {
+ $key = lc($key);
+ }
$key =~ s/^gitweb\.//;
return if ($key =~ m/\W/);
@@ -2644,7 +2669,7 @@ sub git_get_project_ctags {
close $ct;
(my $ctag = $tagfile) =~ s#.*/##;
- if ($val =~ /\d+/) {
+ if ($val =~ /^\d+$/) {
$ctags->{$ctag} = $val;
} else {
$ctags->{$ctag} = 1;
@@ -3560,12 +3585,9 @@ sub mimetype_guess_file {
open(my $mh, '<', $mimemap) or return undef;
while (<$mh>) {
next if m/^#/; # skip comments
- my ($mimetype, $exts) = split(/\t+/);
- if (defined $exts) {
- my @exts = split(/\s+/, $exts);
- foreach my $ext (@exts) {
- $mimemap{$ext} = $mimetype;
- }
+ my ($mimetype, @exts) = split(/\s+/);
+ foreach my $ext (@exts) {
+ $mimemap{$ext} = $mimetype;
}
}
close($mh);
@@ -3681,6 +3703,20 @@ sub get_page_title {
return $title;
}
+sub get_content_type_html {
+ # require explicit support from the UA if we are to send the page as
+ # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+ # we have to do this because MSIE sometimes globs '*/*', pretending to
+ # 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) {
+ return 'application/xhtml+xml';
+ } else {
+ return 'text/html';
+ }
+}
+
sub print_feed_meta {
if (defined $project) {
my %href_params = get_feed_info();
@@ -3726,24 +3762,90 @@ sub print_feed_meta {
}
}
+sub print_header_links {
+ my $status = shift;
+
+ # 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="'.esc_url($stylesheet).'"/>'."\n";
+ } else {
+ foreach my $stylesheet (@stylesheets) {
+ next unless $stylesheet;
+ print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+ }
+ }
+ print_feed_meta()
+ if ($status eq '200 OK');
+ if (defined $favicon) {
+ print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
+ }
+}
+
+sub print_nav_breadcrumbs {
+ my %opts = @_;
+
+ print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+ if (defined $project) {
+ print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
+ if (defined $action) {
+ my $action_print = $action ;
+ if (defined $opts{-action_extra}) {
+ $action_print = $cgi->a({-href => href(action=>$action)},
+ $action);
+ }
+ print " / $action_print";
+ }
+ if (defined $opts{-action_extra}) {
+ print " / $opts{-action_extra}";
+ }
+ print "\n";
+ }
+}
+
+sub print_search_form {
+ if (!defined $searchtext) {
+ $searchtext = "";
+ }
+ my $search_hash;
+ if (defined $hash_base) {
+ $search_hash = $hash_base;
+ } elsif (defined $hash) {
+ $search_hash = $hash;
+ } else {
+ $search_hash = "HEAD";
+ }
+ my $action = $my_uri;
+ my $use_pathinfo = gitweb_check_feature('pathinfo');
+ if ($use_pathinfo) {
+ $action .= "/".esc_url($project);
+ }
+ print $cgi->startform(-method => "get", -action => $action) .
+ "<div class=\"search\">\n" .
+ (!$use_pathinfo &&
+ $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
+ $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
+ $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
+ $cgi->popup_menu(-name => 'st', -default => 'commit',
+ -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
+ $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+ " search:\n",
+ $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+ "<span title=\"Extended regular expression\">" .
+ $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+ -checked => $search_use_regexp) .
+ "</span>" .
+ "</div>" .
+ $cgi->end_form() . "\n";
+}
+
sub git_header_html {
my $status = shift || "200 OK";
my $expires = shift;
my %opts = @_;
my $title = get_page_title();
- my $content_type;
- # require explicit support from the UA if we are to send the page as
- # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
- # we have to do this because MSIE sometimes globs '*/*', pretending to
- # 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) {
- $content_type = 'application/xhtml+xml';
- } else {
- $content_type = 'text/html';
- }
+ my $content_type = get_content_type_html();
print $cgi->header(-type=>$content_type, -charset => 'utf-8',
-status=> $status, -expires => $expires)
unless ($opts{'-no_http_header'});
@@ -3765,22 +3867,7 @@ EOF
if ($ENV{'PATH_INFO'}) {
print "<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="'.esc_url($stylesheet).'"/>'."\n";
- } else {
- foreach my $stylesheet (@stylesheets) {
- next unless $stylesheet;
- print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
- }
- }
- print_feed_meta()
- if ($status eq '200 OK');
- if (defined $favicon) {
- print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
- }
-
+ print_header_links($status);
print "</head>\n" .
"<body>\n";
@@ -3797,59 +3884,12 @@ EOF
-alt => "git",
-class => "logo"}));
}
- print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
- if (defined $project) {
- print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
- if (defined $action) {
- my $action_print = $action ;
- if (defined $opts{-action_extra}) {
- $action_print = $cgi->a({-href => href(action=>$action)},
- $action);
- }
- print " / $action_print";
- }
- if (defined $opts{-action_extra}) {
- print " / $opts{-action_extra}";
- }
- print "\n";
- }
+ print_nav_breadcrumbs(%opts);
print "</div>\n";
my $have_search = gitweb_check_feature('search');
if (defined $project && $have_search) {
- if (!defined $searchtext) {
- $searchtext = "";
- }
- my $search_hash;
- if (defined $hash_base) {
- $search_hash = $hash_base;
- } elsif (defined $hash) {
- $search_hash = $hash;
- } else {
- $search_hash = "HEAD";
- }
- my $action = $my_uri;
- my $use_pathinfo = gitweb_check_feature('pathinfo');
- if ($use_pathinfo) {
- $action .= "/".esc_url($project);
- }
- print $cgi->startform(-method => "get", -action => $action) .
- "<div class=\"search\">\n" .
- (!$use_pathinfo &&
- $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
- $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
- $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
- $cgi->popup_menu(-name => 'st', -default => 'commit',
- -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
- $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
- " search:\n",
- $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
- "<span title=\"Extended regular expression\">" .
- $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
- -checked => $search_use_regexp) .
- "</span>" .
- "</div>" .
- $cgi->end_form() . "\n";
+ print_search_form();
}
}
@@ -5504,6 +5544,216 @@ sub git_remotes_body {
}
}
+sub git_search_message {
+ my %co = @_;
+
+ my $greptype;
+ if ($searchtype eq 'commit') {
+ $greptype = "--grep=";
+ } elsif ($searchtype eq 'author') {
+ $greptype = "--author=";
+ } elsif ($searchtype eq 'committer') {
+ $greptype = "--committer=";
+ }
+ $greptype .= $searchtext;
+ my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
+ $greptype, '--regexp-ignore-case',
+ $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
+
+ my $paging_nav = '';
+ if ($page > 0) {
+ $paging_nav .=
+ $cgi->a({-href => href(-replay=>1, page=>undef)},
+ "first") .
+ " &sdot; " .
+ $cgi->a({-href => href(-replay=>1, page=>$page-1),
+ -accesskey => "p", -title => "Alt-p"}, "prev");
+ } else {
+ $paging_nav .= "first &sdot; prev";
+ }
+ my $next_link = '';
+ if ($#commitlist >= 100) {
+ $next_link =
+ $cgi->a({-href => href(-replay=>1, page=>$page+1),
+ -accesskey => "n", -title => "Alt-n"}, "next");
+ $paging_nav .= " &sdot; $next_link";
+ } else {
+ $paging_nav .= " &sdot; next";
+ }
+
+ git_header_html();
+
+ git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash);
+ if ($page == 0 && !@commitlist) {
+ print "<p>No match.</p>\n";
+ } else {
+ git_search_grep_body(\@commitlist, 0, 99, $next_link);
+ }
+
+ git_footer_html();
+}
+
+sub git_search_changes {
+ my %co = @_;
+
+ local $/ = "\n";
+ open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+ '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+ ($search_use_regexp ? '--pickaxe-regex' : ())
+ or die_error(500, "Open git-log failed");
+
+ git_header_html();
+
+ git_print_page_nav('','', $hash,$co{'tree'},$hash);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+ print "<table class=\"pickaxe search\">\n";
+ my $alternate = 1;
+ undef %co;
+ my @files;
+ while (my $line = <$fd>) {
+ chomp $line;
+ next unless $line;
+
+ my %set = parse_difftree_raw_line($line);
+ if (defined $set{'commit'}) {
+ # finish previous commit
+ if (%co) {
+ print "</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") .
+ "</td>\n" .
+ "</tr>\n";
+ }
+
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<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" .
+ "<td><i>$author</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+ -class => "list subject"},
+ chop_and_escape_str($co{'title'}, 50) . "<br/>");
+ } elsif (defined $set{'to_id'}) {
+ next if ($set{'to_id'} =~ m/^0{40}$/);
+
+ print $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>") .
+ "<br/>\n";
+ }
+ }
+ close $fd;
+
+ # finish last commit (warning: repetition!)
+ if (%co) {
+ print "</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") .
+ "</td>\n" .
+ "</tr>\n";
+ }
+
+ print "</table>\n";
+
+ git_footer_html();
+}
+
+sub git_search_files {
+ my %co = @_;
+
+ local $/ = "\n";
+ open my $fd, "-|", git_cmd(), 'grep', '-n',
+ $search_use_regexp ? ('-E', '-i') : '-F',
+ $searchtext, $co{'tree'}
+ or die_error(500, "Open git-grep failed");
+
+ git_header_html();
+
+ git_print_page_nav('','', $hash,$co{'tree'},$hash);
+ git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+ print "<table class=\"grep_search\">\n";
+ my $alternate = 1;
+ my $matches = 0;
+ my $lastfile = '';
+ while (my $line = <$fd>) {
+ chomp $line;
+ my ($file, $lno, $ltext, $binary);
+ last if ($matches++ > 1000);
+ if ($line =~ /^Binary file (.+) matches$/) {
+ $file = $1;
+ $binary = 1;
+ } else {
+ (undef, $file, $lno, $ltext) = split(/:/, $line, 4);
+ }
+ if ($file ne $lastfile) {
+ $lastfile and print "</td></tr>\n";
+ if ($alternate++) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ print "<td class=\"list\">".
+ $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
+ file_name=>"$file"),
+ -class => "list"}, esc_path($file));
+ print "</td><td>\n";
+ $lastfile = $file;
+ }
+ if ($binary) {
+ print "<div class=\"binary\">Binary file</div>\n";
+ } else {
+ $ltext = untabify($ltext);
+ if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
+ $ltext = esc_html($1, -nbsp=>1);
+ $ltext .= '<span class="match">';
+ $ltext .= esc_html($2, -nbsp=>1);
+ $ltext .= '</span>';
+ $ltext .= esc_html($3, -nbsp=>1);
+ } else {
+ $ltext = esc_html($ltext, -nbsp=>1);
+ }
+ print "<div class=\"pre\">" .
+ $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
+ file_name=>"$file").'#l'.$lno,
+ -class => "linenr"}, sprintf('%4i', $lno))
+ . ' ' . $ltext . "</div>\n";
+ }
+ }
+ if ($lastfile) {
+ print "</td></tr>\n";
+ if ($matches > 1000) {
+ print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
+ }
+ } else {
+ print "<div class=\"diff nodifferences\">No matches found</div>\n";
+ }
+ close $fd;
+
+ print "</table>\n";
+
+ git_footer_html();
+}
+
sub git_search_grep_body {
my ($commitlist, $from, $to, $extra) = @_;
$from = 0 unless defined $from;
@@ -6127,7 +6377,16 @@ sub git_blob_plain {
# want to be sure not to break that by serving the image as an
# attachment (though Firefox 3 doesn't seem to care).
my $sandbox = $prevent_xss &&
- $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
+ $type !~ m!^(?:text/[a-z]+|image/(?:gif|png|jpeg))(?:[ ;]|$)!;
+
+ # serve text/* as text/plain
+ if ($prevent_xss &&
+ ($type =~ m!^text/[a-z]+\b(.*)$! ||
+ ($type =~ m!^[a-z]+/[a-z]\+xml\b(.*)$! && -T $fd))) {
+ my $rest = $1;
+ $rest = defined $rest ? $rest : '';
+ $type = "text/plain$rest";
+ }
print $cgi->header(
-type => $type,
@@ -6225,7 +6484,7 @@ sub git_blob {
$nr++;
$line = untabify($line);
printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
- $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
+ $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? to_utf8($line) : esc_html($line, -nbsp=>1);
}
}
close $fd
@@ -7050,7 +7309,23 @@ sub git_history {
}
sub git_search {
- gitweb_check_feature('search') or die_error(403, "Search is disabled");
+ $searchtype ||= 'commit';
+
+ # check if appropriate features are enabled
+ gitweb_check_feature('search')
+ or die_error(403, "Search is disabled");
+ if ($searchtype eq 'pickaxe') {
+ # pickaxe may take all resources of your box and run for several minutes
+ # with every query - so decide by yourself how public you make this feature
+ gitweb_check_feature('pickaxe')
+ or die_error(403, "Pickaxe search is disabled");
+ }
+ if ($searchtype eq 'grep') {
+ # grep search might be potentially CPU-intensive, too
+ gitweb_check_feature('grep')
+ or die_error(403, "Grep search is disabled");
+ }
+
if (!defined $searchtext) {
die_error(400, "Text field is empty");
}
@@ -7065,205 +7340,17 @@ sub git_search {
$page = 0;
}
- $searchtype ||= 'commit';
- if ($searchtype eq 'pickaxe') {
- # pickaxe may take all resources of your box and run for several minutes
- # with every query - so decide by yourself how public you make this feature
- gitweb_check_feature('pickaxe')
- or die_error(403, "Pickaxe is disabled");
- }
- if ($searchtype eq 'grep') {
- gitweb_check_feature('grep')
- or die_error(403, "Grep is disabled");
- }
-
- git_header_html();
-
- if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
- my $greptype;
- if ($searchtype eq 'commit') {
- $greptype = "--grep=";
- } elsif ($searchtype eq 'author') {
- $greptype = "--author=";
- } elsif ($searchtype eq 'committer') {
- $greptype = "--committer=";
- }
- $greptype .= $searchtext;
- my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
- $greptype, '--regexp-ignore-case',
- $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
-
- my $paging_nav = '';
- if ($page > 0) {
- $paging_nav .=
- $cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext,
- searchtype=>$searchtype)},
- "first");
- $paging_nav .= " &sdot; " .
- $cgi->a({-href => href(-replay=>1, page=>$page-1),
- -accesskey => "p", -title => "Alt-p"}, "prev");
- } else {
- $paging_nav .= "first";
- $paging_nav .= " &sdot; prev";
- }
- my $next_link = '';
- if ($#commitlist >= 100) {
- $next_link =
- $cgi->a({-href => href(-replay=>1, page=>$page+1),
- -accesskey => "n", -title => "Alt-n"}, "next");
- $paging_nav .= " &sdot; $next_link";
- } else {
- $paging_nav .= " &sdot; next";
- }
-
- git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
- git_print_header_div('commit', esc_html($co{'title'}), $hash);
- if ($page == 0 && !@commitlist) {
- print "<p>No match.</p>\n";
- } else {
- git_search_grep_body(\@commitlist, 0, 99, $next_link);
- }
- }
-
- if ($searchtype eq 'pickaxe') {
- git_print_page_nav('','', $hash,$co{'tree'},$hash);
- git_print_header_div('commit', esc_html($co{'title'}), $hash);
-
- print "<table class=\"pickaxe search\">\n";
- my $alternate = 1;
- local $/ = "\n";
- open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
- '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
- ($search_use_regexp ? '--pickaxe-regex' : ());
- undef %co;
- my @files;
- while (my $line = <$fd>) {
- chomp $line;
- next unless $line;
-
- my %set = parse_difftree_raw_line($line);
- if (defined $set{'commit'}) {
- # finish previous commit
- if (%co) {
- print "</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" .
- "</tr>\n";
- }
-
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<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" .
- "<td><i>$author</i></td>\n" .
- "<td>" .
- $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
- -class => "list subject"},
- chop_and_escape_str($co{'title'}, 50) . "<br/>");
- } elsif (defined $set{'to_id'}) {
- next if ($set{'to_id'} =~ m/^0{40}$/);
-
- print $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>") .
- "<br/>\n";
- }
- }
- close $fd;
-
- # finish last commit (warning: repetition!)
- if (%co) {
- print "</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" .
- "</tr>\n";
- }
-
- print "</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";
- my $alternate = 1;
- my $matches = 0;
- local $/ = "\n";
- open my $fd, "-|", git_cmd(), 'grep', '-n',
- $search_use_regexp ? ('-E', '-i') : '-F',
- $searchtext, $co{'tree'};
- my $lastfile = '';
- while (my $line = <$fd>) {
- chomp $line;
- my ($file, $lno, $ltext, $binary);
- last if ($matches++ > 1000);
- if ($line =~ /^Binary file (.+) matches$/) {
- $file = $1;
- $binary = 1;
- } else {
- (undef, $file, $lno, $ltext) = split(/:/, $line, 4);
- }
- if ($file ne $lastfile) {
- $lastfile and print "</td></tr>\n";
- if ($alternate++) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- print "<td class=\"list\">".
- $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
- file_name=>"$file"),
- -class => "list"}, esc_path($file));
- print "</td><td>\n";
- $lastfile = $file;
- }
- if ($binary) {
- print "<div class=\"binary\">Binary file</div>\n";
- } else {
- $ltext = untabify($ltext);
- if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
- $ltext = esc_html($1, -nbsp=>1);
- $ltext .= '<span class="match">';
- $ltext .= esc_html($2, -nbsp=>1);
- $ltext .= '</span>';
- $ltext .= esc_html($3, -nbsp=>1);
- } else {
- $ltext = esc_html($ltext, -nbsp=>1);
- }
- print "<div class=\"pre\">" .
- $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
- file_name=>"$file").'#l'.$lno,
- -class => "linenr"}, sprintf('%4i', $lno))
- . ' ' . $ltext . "</div>\n";
- }
- }
- if ($lastfile) {
- print "</td></tr>\n";
- if ($matches > 1000) {
- print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
- }
- } else {
- print "<div class=\"diff nodifferences\">No matches found</div>\n";
- }
- close $fd;
-
- print "</table>\n";
+ if ($searchtype eq 'commit' ||
+ $searchtype eq 'author' ||
+ $searchtype eq 'committer') {
+ git_search_message(%co);
+ } elsif ($searchtype eq 'pickaxe') {
+ git_search_changes(%co);
+ } elsif ($searchtype eq 'grep') {
+ git_search_files(%co);
+ } else {
+ die_error(400, "Unknown search type");
}
- git_footer_html();
}
sub git_search_help {
diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js
index 676da6b590..db6eb50584 100644
--- a/gitweb/static/js/blame_incremental.js
+++ b/gitweb/static/js/blame_incremental.js
@@ -29,7 +29,6 @@
/* ............................................................ */
/* utility/helper functions (and variables) */
-var xhr; // XMLHttpRequest object
var projectUrl; // partial query + separator ('?' or ';')
// 'commits' is an associative map. It maps SHA1s to Commit objects.
@@ -420,8 +419,6 @@ function handleLine(commit, group) {
// ----------------------------------------------------------------------
-var inProgress = false; // are we processing response
-
/**#@+
* @constant
*/
@@ -433,8 +430,6 @@ var endRe = /^END ?([^ ]*) ?(.*)/;
var curCommit = new Commit();
var curGroup = {};
-var pollTimer = null;
-
/**
* Parse output from 'git blame --incremental [...]', received via
* XMLHttpRequest from server (blamedataUrl), and call handleLine
@@ -535,43 +530,51 @@ function processData(unprocessed, nextReadPos) {
* Handle XMLHttpRequest errors
*
* @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
*
- * @globals pollTimer, commits, inProgress
+ * @globals commits
*/
function handleError(xhr) {
errorInfo('Server error: ' +
xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
- clearInterval(pollTimer);
+ if (typeof xhr.pollTimer === "number") {
+ clearTimeout(xhr.pollTimer);
+ delete xhr.pollTimer;
+ }
commits = {}; // free memory
-
- inProgress = false;
}
/**
* Called after XMLHttpRequest finishes (loads)
*
- * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused)
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} [xhr.pollTimer] ID of the timeout to clear
*
- * @globals pollTimer, commits, inProgress
+ * @globals commits
*/
function responseLoaded(xhr) {
- clearInterval(pollTimer);
+ if (typeof xhr.pollTimer === "number") {
+ clearTimeout(xhr.pollTimer);
+ delete xhr.pollTimer;
+ }
fixColorsAndGroups();
writeTimeInterval();
commits = {}; // free memory
-
- inProgress = false;
}
/**
* handler for XMLHttpRequest onreadystatechange event
* @see startBlame
*
- * @globals xhr, inProgress
+ * @param {XMLHttpRequest} xhr: XMLHttpRequest object
+ * @param {Number} xhr.prevDataLength: previous value of xhr.responseText.length
+ * @param {Number} xhr.nextReadPos: start of unread part of xhr.responseText
+ * @param {Number} [xhr.pollTimer] ID of the timeout (to reset or cancel)
+ * @param {Boolean} fromTimer: if handler was called from timer
*/
-function handleResponse() {
+function handleResponse(xhr, fromTimer) {
/*
* xhr.readyState
@@ -609,32 +612,31 @@ function handleResponse() {
return;
}
- // in case we were called before finished processing
- if (inProgress) {
- return;
- } else {
- inProgress = true;
- }
// extract new whole (complete) lines, and process them
- while (xhr.prevDataLength !== xhr.responseText.length) {
- if (xhr.readyState === 4 &&
- xhr.prevDataLength === xhr.responseText.length) {
- break;
- }
-
+ if (xhr.prevDataLength !== xhr.responseText.length) {
xhr.prevDataLength = xhr.responseText.length;
var unprocessed = xhr.responseText.substring(xhr.nextReadPos);
xhr.nextReadPos = processData(unprocessed, xhr.nextReadPos);
- } // end while
+ }
// did we finish work?
- if (xhr.readyState === 4 &&
- xhr.prevDataLength === xhr.responseText.length) {
+ if (xhr.readyState === 4) {
responseLoaded(xhr);
+ return;
}
- inProgress = false;
+ // if we get from timer, we have to restart it
+ // otherwise onreadystatechange gives us partial response, timer not needed
+ if (fromTimer) {
+ setTimeout(function () {
+ handleResponse(xhr, true);
+ }, 1000);
+
+ } else if (typeof xhr.pollTimer === "number") {
+ clearTimeout(xhr.pollTimer);
+ delete xhr.pollTimer;
+ }
}
// ============================================================
@@ -649,11 +651,11 @@ function handleResponse() {
* Called from 'blame_incremental' view after loading table with
* file contents, a base for blame view.
*
- * @globals xhr, t0, projectUrl, div_progress_bar, totalLines, pollTimer
+ * @globals t0, projectUrl, div_progress_bar, totalLines
*/
function startBlame(blamedataUrl, bUrl) {
- xhr = createRequestObject();
+ var xhr = createRequestObject();
if (!xhr) {
errorInfo('ERROR: XMLHttpRequest not supported');
return;
@@ -672,8 +674,9 @@ function startBlame(blamedataUrl, bUrl) {
xhr.prevDataLength = -1; // used to detect if we have new data
xhr.nextReadPos = 0; // where unread part of response starts
- xhr.onreadystatechange = handleResponse;
- //xhr.onreadystatechange = function () { handleResponse(xhr); };
+ xhr.onreadystatechange = function () {
+ handleResponse(xhr, false);
+ };
xhr.open('GET', blamedataUrl);
xhr.setRequestHeader('Accept', 'text/plain');
@@ -681,7 +684,9 @@ function startBlame(blamedataUrl, bUrl) {
// not all browsers call onreadystatechange event on each server flush
// poll response using timer every second to handle this issue
- pollTimer = setInterval(xhr.onreadystatechange, 1000);
+ xhr.pollTimer = setTimeout(function () {
+ handleResponse(xhr, true);
+ }, 1000);
}
/* end of blame_incremental.js */