From e6e592db4c0099a6412aed6e868769535900f112 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 14 Feb 2010 22:46:28 +0100 Subject: gitweb: Die if there are parsing errors in config file Otherwise the errors can propagate, and show in damnest places, and you would spend your time chasing ghosts instead of debugging real problem (yes, it is from personal experience). This follows (parts of) advice in `perldoc -f do` documentation. This required restructoring code a bit, so we die only if we are reading (executing) config file. As a side effect $GITWEB_CONFIG_SYSTEM is always available, even when we use $GITWEB_CONFIG. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1f6978ac1f..20106a4f42 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -550,11 +550,14 @@ sub filter_snapshot_fmts { } our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; +our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; +# die if there are errors parsing config file if (-e $GITWEB_CONFIG) { do $GITWEB_CONFIG; -} else { - our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; - do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM; + die $@ if $@; +} elsif (-e $GITWEB_CONFIG_SYSTEM) { + do $GITWEB_CONFIG_SYSTEM; + die $@ if $@; } # Get loadavg of system, to compare against $maxload. -- cgit v1.3 From 453541fcfcbc54aa3b0035667e5d5885d407d0a5 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 7 Feb 2010 21:51:18 +0100 Subject: gitweb: esc_html (short) error message in die_error The error message (second argument to die_error) is meant to be short, one-line text description of given error. A few callers call die_error with error message containing unescaped user supplied data ($hash, $file_name). Instead of forcing callers to escape data, simply call esc_html on the parameter. Note that optional third parameter, which contains detailed error description, is meant to be HTML formatted, and therefore should be not escaped. While at it update esc_html synopsis/usage, and bring default error description to read 'Internal Server Error' (titlecased). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1f6978ac1f..2ccbb6aa34 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3372,7 +3372,7 @@ sub git_footer_html { ""; } -# die_error(, ) +# die_error(, [, ]) # Example: die_error(404, 'Hash not found') # By convention, use the following status codes (as defined in RFC 2616): # 400: Invalid or missing CGI parameters, or @@ -3387,7 +3387,7 @@ sub git_footer_html { # or down for maintenance). Generally, this is a temporary state. sub die_error { my $status = shift || 500; - my $error = shift || "Internal server error"; + my $error = esc_html(shift || "Internal Server Error"); my $extra = shift; my %http_responses = ( -- cgit v1.3 From 1df48766137f2040e2e992d7d278d5aca26406cf Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 7 Feb 2010 21:52:25 +0100 Subject: gitweb: Protect escaping functions against calling on undef This is a bit of future-proofing esc_html and friends: when called with undefined value they would now would return undef... which would probably mean that error would still occur, but closer to the source of problem. This means that we can safely use esc_html(shift) || "Internal Server Error" in die_error() instead of esc_html(shift || "Internal Server Error") Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2ccbb6aa34..3c879b88fe 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1143,6 +1143,7 @@ sub validate_refname { # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning sub to_utf8 { my $str = shift; + return undef unless defined $str; if (utf8::valid($str)) { utf8::decode($str); return $str; @@ -1155,6 +1156,7 @@ sub to_utf8 { # correct, but quoted slashes look too horrible in bookmarks sub esc_param { my $str = shift; + return undef unless defined $str; $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg; $str =~ s/ /\+/g; return $str; @@ -1163,6 +1165,7 @@ sub esc_param { # quote unsafe chars in whole URL, so some charactrs cannot be quoted sub esc_url { my $str = shift; + return undef unless defined $str; $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg; $str =~ s/\+/%2B/g; $str =~ s/ /\+/g; @@ -1174,6 +1177,8 @@ sub esc_html { my $str = shift; my %opts = @_; + return undef unless defined $str; + $str = to_utf8($str); $str = $cgi->escapeHTML($str); if ($opts{'-nbsp'}) { @@ -1188,6 +1193,8 @@ sub esc_path { my $str = shift; my %opts = @_; + return undef unless defined $str; + $str = to_utf8($str); $str = $cgi->escapeHTML($str); if ($opts{'-nbsp'}) { @@ -3387,7 +3394,7 @@ sub git_footer_html { # or down for maintenance). Generally, this is a temporary state. sub die_error { my $status = shift || 500; - my $error = esc_html(shift || "Internal Server Error"); + my $error = esc_html(shift) || "Internal Server Error"; my $extra = shift; my %http_responses = ( -- cgit v1.3 From 9be3614eff36271d5f1cd460a568a219902cb044 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 1 Mar 2010 22:51:34 +0100 Subject: gitweb: Fix project-specific feature override behavior This commit fixes a bug in processing project-specific override in a situation when there is no project, e.g. for the projects list page. When 'snapshot' feature had project specific config override enabled by putting $feature{'snapshot'}{'override'} = 1; (or equivalent) in $GITWEB_CONFIG, and when viewing toplevel gitweb page, which means the projects list page (to be more exact this happens for any project-less action), gitweb would put the following Perl warnings in error log: gitweb.cgi: Use of uninitialized value $git_dir in concatenation (.) or string at gitweb.cgi line 2065. fatal: error processing config file(s) gitweb.cgi: Use of uninitialized value $git_dir in concatenation (.) or string at gitweb.cgi line 2221. gitweb.cgi: Use of uninitialized value $git_dir in concatenation (.) or string at gitweb.cgi line 2218. The problem is in the following fragment of code: # path to the current git repository our $git_dir; $git_dir = "$projectroot/$project" if $project; # list of supported snapshot formats our @snapshot_fmts = gitweb_get_feature('snapshot'); @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts); For the toplevel gitweb page, which is the list of projects, $project is not defined, therefore neither is $git_dir. gitweb_get_feature() subroutine calls git_get_project_config() if project specific override is turned on... but we don't have project here. Those errors mentioned above occur in the following fragment of code in git_get_project_config(): # get config if (!defined $config_file || $config_file ne "$git_dir/config") { %config = git_parse_project_config('gitweb'); $config_file = "$git_dir/config"; } git_parse_project_config() calls git_cmd() which has '--git-dir='.$git_dir There are (at least) three possible solutions: 1. Harden gitweb_get_feature() so that it doesn't call git_get_project_config() if $project (and therefore $git_dir) is not defined; there is no project for project specific config. 2. Harden git_get_project_config() like you did in your fix, returning early if $git_dir is not defined. 3. Harden git_cmd() so that it doesn't add "--git-dir=$git_dir" if $git_dir is not defined, and change git_get_project_config() so that it doesn't even try to access $git_dir if it is not defined. This commit implements both 1.) and 2.), i.e. gitweb_get_feature() doesn't call project-specific override if $git_dir is not defined (if there is no project), and git_get_project_config() returns early if $git_dir is not defined. Add a test for this bug to t/t9500-gitweb-standalone-no-errors.sh test. Reported-by: Eli Barzilay Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 ++++++++- t/t9500-gitweb-standalone-no-errors.sh | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1f6978ac1f..0b1e357ce9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -454,7 +454,11 @@ sub gitweb_get_feature { $feature{$name}{'sub'}, $feature{$name}{'override'}, @{$feature{$name}{'default'}}); - if (!$override) { return @defaults; } + # project specific override is possible only if we have project + our $git_dir; # global variable, declared later + if (!$override || !defined $git_dir) { + return @defaults; + } if (!defined $sub) { warn "feature $name is not overridable"; return @defaults; @@ -2202,6 +2206,9 @@ sub config_to_multi { sub git_get_project_config { my ($key, $type) = @_; + # do we have project + return unless (defined $project && defined $git_dir); + # key sanity check return unless ($key); $key =~ s/^gitweb\.//; diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 2fc7fdb124..63b6b060e6 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -591,13 +591,21 @@ test_debug 'cat gitweb.log' # ---------------------------------------------------------------------- # gitweb config and repo config -cat >>gitweb_config.perl <>gitweb_config.perl <<\EOF + +# turn on override for each overridable feature +foreach my $key (keys %feature) { + if ($feature{$key}{'sub'}) { + $feature{$key}{'override'} = 1; + } +} EOF +test_expect_success \ + 'config override: projects list (implicit)' \ + 'gitweb_run' +test_debug 'cat gitweb.log' + test_expect_success \ 'config override: tree view, features not overridden in repo config' \ 'gitweb_run "p=.git;a=tree"' -- cgit v1.3 From 7a49c254cdaec6b15a6e2818e29fdb34fc6f7717 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 27 Mar 2010 20:26:59 +0100 Subject: gitweb: git_get_project_config requires only $git_dir, not also $project Fix overeager early return in git_get_project_config, introduced in 9be3614 (gitweb: Fix project-specific feature override behavior, 2010-03-01). When git_get_project_config is called from projects list page via git_get_project_owner($path) etc., it is called with $git_dir defined (in git_get_project_owner($path) etc.), but $project variable is not defined. git_get_project_config doesn't use $project variable anyway. Reported-by: Tobias Heinlein Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3d80deba01..9d4c58238e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2209,8 +2209,7 @@ sub config_to_multi { sub git_get_project_config { my ($key, $type) = @_; - # do we have project - return unless (defined $project && defined $git_dir); + return unless defined $git_dir; # key sanity check return unless ($key); -- cgit v1.3 From 377bee3424ba871278d230ed296f74ccac8ad607 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 24 Apr 2010 15:53:19 +0200 Subject: gitweb: href(..., -path_info => 0|1) If named boolean option -path_info is passed to href() subroutine, it would use its value to decide whether to generate path_info URL form. If this option is not passed, href() queries 'pathinfo' feature to check whether to generate path_info URL (if generating path_info link is possible at all). href(-replay=>1, -path_info=>0) is meant to be used to generate a key for caching gitweb output; alternate solution would be to use freeze() from Storable (core module) on %input_params hash (or its reference), e.g.: $key = freeze \%input_params; or other serialization of %input_params. While at it document extra options/flags to href(). Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c356e95f18..6cefb09b45 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -977,6 +977,10 @@ exit; ## ====================================================================== ## action links +# possible values of extra options +# -full => 0|1 - use absolute/full URL ($my_uri/$my_url as base) +# -replay => 1 - start from a current view (replay with modifications) +# -path_info => 0|1 - don't use/use path_info URL (if possible) sub href { my %params = @_; # default is to use -absolute url() i.e. $my_uri @@ -993,7 +997,8 @@ sub href { } my $use_pathinfo = gitweb_check_feature('pathinfo'); - if ($use_pathinfo and defined $params{'project'}) { + if (defined $params{'project'} && + (exists $params{-path_info} ? $params{-path_info} : $use_pathinfo)) { # try to put as many parameters as possible in PATH_INFO: # - project name # - action -- cgit v1.3 From c42b00c8f2b8c04ff9829e88dcde92d7294cd460 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 24 Apr 2010 15:56:13 +0200 Subject: gitweb: Use nonlocal jump instead of 'exit' in die_error Use 'goto DONE' in place of 'exit' to end request processing in die_error() subroutine. While at it, do not end gitweb with 'exit'. This would make it easier in the future to add support or improve support for persistent environments such as FastCGI and mod_perl. It would also make it easier to make use of die_error() as an error handler (for fatalsToBrowser). Perl 5 allows non-local jumps; the restriction is that you cannot jump into a scope. Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6cefb09b45..ed92dcac08 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -972,7 +972,8 @@ if ($action !~ m/^(?:opml|project_list|project_index)$/ && die_error(400, "Project needed"); } $actions{$action}->(); -exit; +DONE_GITWEB: +1; ## ====================================================================== ## action links @@ -3432,7 +3433,7 @@ EOF print "\n"; git_footer_html(); - exit; + goto DONE_GITWEB; } ## ---------------------------------------------------------------------- -- cgit v1.3 From 7a59745710e964f9498f37a3184bf95b9b4a772b Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 24 Apr 2010 16:00:04 +0200 Subject: gitweb: Add custom error handler using die_error Change the default message for errors (for fatalsToBrowser) to use die_error() subroutine. This way errors (and explicitely calling 'die MESSAGE') would generate 'Internal Server Error' error message. Note that call to set_message is intentionally not put in BEGIN block; we set error handler to use die_error() only after we are sure that we can use it, after all needed variables are set. Due to the fact that error handler set via set_message() subroutine from CGI::Carp (in the fatalsToBrowser case) is called after HTTP headers were already printed (with exception of MOD_PERL), gitweb cannot return 'Status: 500 Internal Server Error'. Thanks to the fact that die_error() no longer uses 'exit', errors would be logged by CGI::Carp, independent on whether default error handler is used, or handle_errors_html which uses die_error is used. Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ed92dcac08..e579c14b44 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -11,7 +11,7 @@ use strict; use warnings; use CGI qw(:standard :escapeHTML -nosticky); use CGI::Util qw(unescape); -use CGI::Carp qw(fatalsToBrowser); +use CGI::Carp qw(fatalsToBrowser set_message); use Encode; use Fcntl ':mode'; use File::Find qw(); @@ -952,6 +952,21 @@ if ($git_avatar eq 'gravatar') { $git_avatar = ''; } +# custom error handler: 'die ' is Internal Server Error +sub handle_errors_html { + my $msg = shift; # it is already HTML escaped + + # to avoid infinite loop where error occurs in die_error, + # change handler to default handler, disabling handle_errors_html + set_message("Error occured when inside die_error:\n$msg"); + + # you cannot jump out of die_error when called as error handler; + # the subroutine set via CGI::Carp::set_message is called _after_ + # HTTP headers are already written, so it cannot write them itself + die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1); +} +set_message(\&handle_errors_html); + # dispatch if (!defined $action) { if (defined $hash) { @@ -3167,6 +3182,7 @@ sub blob_contenttype { sub git_header_html { my $status = shift || "200 OK"; my $expires = shift; + my %opts = @_; my $title = "$site_name"; if (defined $project) { @@ -3194,7 +3210,8 @@ sub git_header_html { $content_type = 'text/html'; } print $cgi->header(-type=>$content_type, -charset => 'utf-8', - -status=> $status, -expires => $expires); + -status=> $status, -expires => $expires) + unless ($opts{'-no_http_headers'}); my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : ''; print < @@ -3411,6 +3428,7 @@ sub die_error { my $status = shift || 500; my $error = esc_html(shift) || "Internal Server Error"; my $extra = shift; + my %opts = @_; my %http_responses = ( 400 => '400 Bad Request', @@ -3419,7 +3437,7 @@ sub die_error { 500 => '500 Internal Server Error', 503 => '503 Service Unavailable', ); - git_header_html($http_responses{$status}); + git_header_html($http_responses{$status}, undef, %opts); print <

@@ -3433,7 +3451,8 @@ EOF print "\n"; git_footer_html(); - goto DONE_GITWEB; + goto DONE_GITWEB + unless ($opts{'-error_handler'}); } ## ---------------------------------------------------------------------- -- cgit v1.3 From efb2d0c5dcea1069bae2d26c9534e2025ee63e66 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 24 Apr 2010 16:01:10 +0200 Subject: gitweb: Move generating page title to separate subroutine get_page_title subroutine is currently used only in git_header_html. Nevertheless refactoring title generation allowed to reduce indent level. It would be used in more than one callsite in the patch adding caching activity indicator to gitweb. Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e579c14b44..7d75dc4c8c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3179,24 +3179,30 @@ sub blob_contenttype { ## ====================================================================== ## functions printing HTML: header, footer, error page +sub get_page_title { + my $title = to_utf8($site_name); + + return $title unless (defined $project); + $title .= " - " . to_utf8($project); + + return $title unless (defined $action); + $title .= "/$action"; # $action is US-ASCII (7bit ASCII) + + return $title unless (defined $file_name); + $title .= " - " . esc_path($file_name); + if ($action eq "tree" && $file_name !~ m|/$|) { + $title .= "/"; + } + + return $title; +} + sub git_header_html { my $status = shift || "200 OK"; my $expires = shift; my %opts = @_; - my $title = "$site_name"; - if (defined $project) { - $title .= " - " . to_utf8($project); - if (defined $action) { - $title .= "/$action"; - if (defined $file_name) { - $title .= " - " . esc_path($file_name); - if ($action eq "tree" && $file_name !~ m|/$|) { - $title .= "/"; - } - } - } - } + 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'. -- cgit v1.3 From ee1d8ee0f0f3faaa72f5c4886e61befaf7915718 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 30 Apr 2010 18:30:31 +0200 Subject: gitweb: Silence 'Variable VAR may be unavailable' warnings When $projects_list points to a directory, and git_get_projects_list scans this directory for repositories, there can be generated the following warnings (for persistent services like mod_perl or plackup): Variable "$project_maxdepth" may be unavailable at gitweb.cgi line 2443. Variable "$projectroot" may be unavailable at gitweb.cgi line 2451. Those are false positives; silence those warnings by explicitely declaring $project_maxdepth and $projectroot with 'our', as global variables, in anonymous subrotine passed to File::Find::find. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 48e21dad6c..64eaa75996 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1520,6 +1520,9 @@ sub git_get_projects_list { follow_skip => 2, # ignore duplicates dangling_symlinks => 0, # ignore dangling symlinks, silently wanted => sub { + # global variables + our $project_maxdepth; + our $projectroot; # skip project-list toplevel, if we get it. return if (m!^[/.]$!); # only directories can be git repositories -- cgit v1.3 From b331fe54762770c647b8f95c8f5b16004c6120da Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 27 Apr 2010 21:34:44 +0200 Subject: gitweb: Syntax highlighting support It requires the 'highlight' program to do all the heavy-lifting. This is loosely based on Daniel Svensson's and Sham Chukoury's work in gitweb-xmms2.git (it cannot be cherry-picked, as gitweb-xmms2 first forked wildly, then not contributed back, and then went stale). [jn: cherry picked from bc1ed6aafd9ee4937559535c66c8bddf1864bec6 in http://repo.or.cz/w/git/dscho.git, with a few changes] Signed-off-by: Johannes Schindelin Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 18 +++++++++++++++ gitweb/gitweb.perl | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 50067f2e0d..4132aabcdb 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -572,3 +572,21 @@ span.match { div.binary { font-style: italic; } + +/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */ + +/* Highlighting theme definition: */ + +.num { color:#2928ff; } +.esc { color:#ff00ff; } +.str { color:#ff0000; } +.dstr { color:#818100; } +.slc { color:#838183; font-style:italic; } +.com { color:#838183; font-style:italic; } +.dir { color:#008200; } +.sym { color:#000000; } +.line { color:#555555; } +.kwa { color:#000000; font-weight:bold; } +.kwb { color:#830000; } +.kwc { color:#000000; font-weight:bold; } +.kwd { color:#010181; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c356e95f18..de18ebf301 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -227,6 +227,36 @@ our %avatar_size = ( # Leave it undefined (or set to 'undef') to turn off load checking. our $maxload = 300; +# syntax highlighting +our %highlight_type = ( + # match by basename + 'SConstruct' => 'py', + 'Program' => 'py', + 'Library' => 'py', + 'Makefile' => 'make', + # match by extension + '\.py$' => 'py', # Python + '\.c$' => 'c', + '\.h$' => 'c', + '\.cpp$' => 'cpp', + '\.cxx$' => 'cpp', + '\.rb$' => 'ruby', + '\.java$' => 'java', + '\.css$' => 'css', + '\.php3?$' => 'php', + '\.sh$' => 'sh', # Bash / shell script + '\.pl$' => 'pl', # Perl + '\.js$' => 'js', # JavaScript + '\.tex$' => 'tex', # TeX and LaTeX + '\.bib$' => 'bib', # BibTeX + '\.x?html$' => 'xml', + '\.xml$' => 'xml', + '\.awk$' => 'awk', + '\.bat$' => 'bat', # DOS Batch script + '\.ini$' => 'ini', + '\.spec$' => 'spec', # RPM Spec +); + # You define site-wide feature defaults here; override them with # $GITWEB_CONFIG as necessary. our %feature = ( @@ -445,6 +475,19 @@ our %feature = ( 'javascript-actions' => { 'override' => 0, 'default' => [0]}, + + # Syntax highlighting support. This is based on Daniel Svensson's + # and Sham Chukoury's work in gitweb-xmms2.git. + # It requires the 'highlight' program, and therefore is disabled + # by default. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'highlight'}{'default'} = [1]; + + 'highlight' => { + 'sub' => sub { feature_bool('highlight', @_) }, + 'override' => 0, + 'default' => [0]}, ); sub gitweb_get_feature { @@ -5346,6 +5389,7 @@ sub git_blob { open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(500, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); + # use 'blob_plain' (aka 'raw') view for files that cannot be displayed if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) { close $fd; return git_blob_plain($mimetype); @@ -5353,6 +5397,25 @@ sub git_blob { # we can have blame only for text/* mimetype $have_blame &&= ($mimetype =~ m!^text/!); + my $have_highlight = gitweb_check_feature('highlight'); + my $syntax; + if ($have_highlight && defined($file_name)) { + my $basename = basename($file_name, '.in'); + foreach my $regexp (keys %highlight_type) { + if ($basename =~ /$regexp/) { + $syntax = $highlight_type{$regexp}; + last; + } + } + + if ($syntax) { + close $fd; + open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". + "highlight --xhtml --fragment -t 8 --syntax $syntax |" + or die_error(500, "Couldn't open file or run syntax highlighter"); + } + } + git_header_html(undef, $expires); my $formats_nav = ''; if (defined $hash_base && (my %co = parse_commit($hash_base))) { @@ -5404,7 +5467,7 @@ sub git_blob { $line = untabify($line); printf "\n", - $nr, $nr, $nr, esc_html($line, -nbsp=>1); + $nr, $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1); } } close $fd -- cgit v1.3 From 592ea4173af6b445042d74b989762fd41aecdf4c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 27 Apr 2010 21:34:45 +0200 Subject: gitweb: Refactor syntax highlighting support This refactoring (adding guess_file_syntax and run_highlighter subroutines) is meant to make it easier in the future to add support for other syntax highlighing solutions, or make it smarter by not re-running `git cat-file` second time. Instead of looping over list of regexps (keys of %highlight_type hash), make use of the fact that choosing syntax is based either on full basename (%highlight_basename), or on file extension (%highlight_ext). Add some basic test of syntax highlighting (with 'highlight' as prerequisite) to t/t9500-gitweb-standalone-no-errors.sh test. While at it make git_blob Perl style prettier. Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 116 ++++++++++++++++++--------------- t/t9500-gitweb-standalone-no-errors.sh | 29 +++++++++ 2 files changed, 92 insertions(+), 53 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index de18ebf301..7d9b660463 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -227,36 +227,6 @@ our %avatar_size = ( # Leave it undefined (or set to 'undef') to turn off load checking. our $maxload = 300; -# syntax highlighting -our %highlight_type = ( - # match by basename - 'SConstruct' => 'py', - 'Program' => 'py', - 'Library' => 'py', - 'Makefile' => 'make', - # match by extension - '\.py$' => 'py', # Python - '\.c$' => 'c', - '\.h$' => 'c', - '\.cpp$' => 'cpp', - '\.cxx$' => 'cpp', - '\.rb$' => 'ruby', - '\.java$' => 'java', - '\.css$' => 'css', - '\.php3?$' => 'php', - '\.sh$' => 'sh', # Bash / shell script - '\.pl$' => 'pl', # Perl - '\.js$' => 'js', # JavaScript - '\.tex$' => 'tex', # TeX and LaTeX - '\.bib$' => 'bib', # BibTeX - '\.x?html$' => 'xml', - '\.xml$' => 'xml', - '\.awk$' => 'awk', - '\.bat$' => 'bat', # DOS Batch script - '\.ini$' => 'ini', - '\.spec$' => 'spec', # RPM Spec -); - # You define site-wide feature defaults here; override them with # $GITWEB_CONFIG as necessary. our %feature = ( @@ -478,8 +448,8 @@ our %feature = ( # Syntax highlighting support. This is based on Daniel Svensson's # and Sham Chukoury's work in gitweb-xmms2.git. - # It requires the 'highlight' program, and therefore is disabled - # by default. + # It requires the 'highlight' program present in $PATH, + # and therefore is disabled by default. # To enable system wide have in $GITWEB_CONFIG # $feature{'highlight'}{'default'} = [1]; @@ -3198,6 +3168,61 @@ sub blob_contenttype { return $type; } +# guess file syntax for syntax highlighting; return undef if no highlighting +# the name of syntax can (in the future) depend on syntax highlighter used +sub guess_file_syntax { + my ($highlight, $mimetype, $file_name) = @_; + return undef unless ($highlight && defined $file_name); + + # configuration for 'highlight' (http://www.andre-simon.de/) + # match by basename + my %highlight_basename = ( + #'Program' => 'py', + #'Library' => 'py', + 'SConstruct' => 'py', # SCons equivalent of Makefile + 'Makefile' => 'make', + ); + # match by extension + my %highlight_ext = ( + # main extensions, defining name of syntax; + # see files in /usr/share/highlight/langDefs/ directory + map { $_ => $_ } + qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), + # alternate extensions, see /etc/highlight/filetypes.conf + 'h' => 'c', + map { $_ => 'cpp' } qw(cxx c++ cc), + map { $_ => 'php' } qw(php3 php4), + map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' + 'mak' => 'make', + map { $_ => 'xml' } qw(xhtml html htm), + ); + + my $basename = basename($file_name, '.in'); + return $highlight_basename{$basename} + if exists $highlight_basename{$basename}; + + $basename =~ /\.([^.]*)$/; + my $ext = $1 or return undef; + return $highlight_ext{$ext} + if exists $highlight_ext{$ext}; + + return undef; +} + +# run highlighter and return FD of its output, +# or return original FD if no highlighting +sub run_highlighter { + my ($fd, $highlight, $syntax) = @_; + return $fd unless ($highlight && defined $syntax); + + close $fd + or die_error(404, "Reading blob failed"); + open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". + "highlight --xhtml --fragment --syntax $syntax |" + or die_error(500, "Couldn't open file or run syntax highlighter"); + return $fd; +} + ## ====================================================================== ## functions printing HTML: header, footer, error page @@ -5397,24 +5422,10 @@ sub git_blob { # we can have blame only for text/* mimetype $have_blame &&= ($mimetype =~ m!^text/!); - my $have_highlight = gitweb_check_feature('highlight'); - my $syntax; - if ($have_highlight && defined($file_name)) { - my $basename = basename($file_name, '.in'); - foreach my $regexp (keys %highlight_type) { - if ($basename =~ /$regexp/) { - $syntax = $highlight_type{$regexp}; - last; - } - } - - if ($syntax) { - close $fd; - open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". - "highlight --xhtml --fragment -t 8 --syntax $syntax |" - or die_error(500, "Couldn't open file or run syntax highlighter"); - } - } + my $highlight = gitweb_check_feature('highlight'); + my $syntax = guess_file_syntax($highlight, $mimetype, $file_name); + $fd = run_highlighter($fd, $highlight, $syntax) + if $syntax; git_header_html(undef, $expires); my $formats_nav = ''; @@ -5465,9 +5476,8 @@ sub git_blob { chomp $line; $nr++; $line = untabify($line); - printf "\n", - $nr, $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1); + printf qq!
%4i %s
\n!, + $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1); } } close $fd diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 63b6b060e6..4f2b9b062b 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -647,4 +647,33 @@ test_expect_success \ gitweb_run "p=.git;a=summary"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# syntax highlighting + +cat >>gitweb_config.perl <<\EOF +$feature{'highlight'}{'override'} = 1; +EOF + +highlight --version >/dev/null 2>&1 +if [ $? -eq 127 ]; then + say "Skipping syntax highlighting test, because 'highlight' was not found" +else + test_set_prereq HIGHLIGHT +fi + +test_expect_success HIGHLIGHT \ + 'syntax highlighting (no highlight)' \ + 'git config gitweb.highlight yes && + gitweb_run "p=.git;a=blob;f=file"' +test_debug 'cat gitweb.log' + +test_expect_success HIGHLIGHT \ + 'syntax highlighting (highlighted)' \ + 'git config gitweb.highlight yes && + echo "#!/usr/bin/sh" > test.sh && + git add test.sh && + git commit -m "Add test.sh" && + gitweb_run "p=.git;a=blob;f=test.sh"' +test_debug 'cat gitweb.log' + test_done -- cgit v1.3 From c2394fe93441618056363a14c8b63f526afa9615 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 7 May 2010 14:54:04 +0200 Subject: gitweb: Put all per-connection code in run() subroutine All code that is run per-connection (as opposed to those parts of gitweb code that can be run once) is put into appropriate subroutines: - evaluate_uri - evaluate_gitweb_config - evaluate_git_version (here only because $GIT can be set in config) - check_loadavg (as soon as possible; $git_version must be defined) - evaluate_query_params (counterpart to evaluate_path_info) - evaluate_and_validate_params - evaluate_git_dir (requires $project) - configure_gitweb_features (@snapshot_fmts, $git_avatar) - dispatch (includes setting default $action) The difference is best viewed with '-w', '--ignore-all-space' option, because of reindent caused by putting code in subroutines. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 357 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 204 insertions(+), 153 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ed92dcac08..4a3632ab50 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -28,34 +28,42 @@ BEGIN { CGI->compile() if $ENV{'MOD_PERL'}; } -our $cgi = new CGI; our $version = "++GIT_VERSION++"; -our $my_url = $cgi->url(); -our $my_uri = $cgi->url(-absolute => 1); -# Base URL for relative URLs in gitweb ($logo, $favicon, ...), -# needed and used only for URLs with nonempty PATH_INFO -our $base_url = $my_url; +our ($my_url, $my_uri, $base_url, $path_info, $home_link); +sub evaluate_uri { + our $cgi; -# When the script is used as DirectoryIndex, the URL does not contain the name -# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we -# have to do it ourselves. We make $path_info global because it's also used -# later on. -# -# Another issue with the script being the DirectoryIndex is that the resulting -# $my_url data is not the full script URL: this is good, because we want -# generated links to keep implying the script name if it wasn't explicitly -# indicated in the URL we're handling, but it means that $my_url cannot be used -# as base URL. -# Therefore, if we needed to strip PATH_INFO, then we know that we have -# to build the base URL ourselves: -our $path_info = $ENV{"PATH_INFO"}; -if ($path_info) { - if ($my_url =~ s,\Q$path_info\E$,, && - $my_uri =~ s,\Q$path_info\E$,, && - defined $ENV{'SCRIPT_NAME'}) { - $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'}; + our $my_url = $cgi->url(); + our $my_uri = $cgi->url(-absolute => 1); + + # Base URL for relative URLs in gitweb ($logo, $favicon, ...), + # needed and used only for URLs with nonempty PATH_INFO + our $base_url = $my_url; + + # When the script is used as DirectoryIndex, the URL does not contain the name + # of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we + # have to do it ourselves. We make $path_info global because it's also used + # later on. + # + # Another issue with the script being the DirectoryIndex is that the resulting + # $my_url data is not the full script URL: this is good, because we want + # generated links to keep implying the script name if it wasn't explicitly + # indicated in the URL we're handling, but it means that $my_url cannot be used + # as base URL. + # Therefore, if we needed to strip PATH_INFO, then we know that we have + # to build the base URL ourselves: + our $path_info = $ENV{"PATH_INFO"}; + if ($path_info) { + if ($my_url =~ s,\Q$path_info\E$,, && + $my_uri =~ s,\Q$path_info\E$,, && + defined $ENV{'SCRIPT_NAME'}) { + $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'}; + } } + + # target of the home link on top of all pages + our $home_link = $my_uri || "/"; } # core git executable to use @@ -70,9 +78,6 @@ our $projectroot = "++GITWEB_PROJECTROOT++"; # the number is relative to the projectroot our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++"; -# target of the home link on top of all pages -our $home_link = $my_uri || "/"; - # string of the home link on top of all pages our $home_link_str = "++GITWEB_HOME_LINK_STR++"; @@ -553,15 +558,18 @@ sub filter_snapshot_fmts { !$known_snapshot_formats{$_}{'disabled'}} @fmts; } -our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; -our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; -# die if there are errors parsing config file -if (-e $GITWEB_CONFIG) { - do $GITWEB_CONFIG; - die $@ if $@; -} elsif (-e $GITWEB_CONFIG_SYSTEM) { - do $GITWEB_CONFIG_SYSTEM; - die $@ if $@; +our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM); +sub evaluate_gitweb_config { + our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; + our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; + # die if there are errors parsing config file + if (-e $GITWEB_CONFIG) { + do $GITWEB_CONFIG; + die $@ if $@; + } elsif (-e $GITWEB_CONFIG_SYSTEM) { + do $GITWEB_CONFIG_SYSTEM; + die $@ if $@; + } } # Get loadavg of system, to compare against $maxload. @@ -587,13 +595,16 @@ sub get_loadavg { } # version of the core git binary -our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown"; -$number_of_git_cmds++; - -$projects_list ||= $projectroot; +our $git_version; +sub evaluate_git_version { + our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown"; + $number_of_git_cmds++; +} -if (defined $maxload && get_loadavg() > $maxload) { - die_error(503, "The load average on the server is too high"); +sub check_loadavg { + if (defined $maxload && get_loadavg() > $maxload) { + die_error(503, "The load average on the server is too high"); + } } # ====================================================================== @@ -680,11 +691,15 @@ our %allowed_options = ( # should be single values, but opt can be an array. We should probably # build an array of parameters that can be multi-valued, but since for the time # being it's only this one, we just single it out -while (my ($name, $symbol) = each %cgi_param_mapping) { - if ($symbol eq 'opt') { - $input_params{$name} = [ $cgi->param($symbol) ]; - } else { - $input_params{$name} = $cgi->param($symbol); +sub evaluate_query_params { + our $cgi; + + while (my ($name, $symbol) = each %cgi_param_mapping) { + if ($symbol eq 'opt') { + $input_params{$name} = [ $cgi->param($symbol) ]; + } else { + $input_params{$name} = $cgi->param($symbol); + } } } @@ -831,149 +846,185 @@ sub evaluate_path_info { } } } -evaluate_path_info(); -our $action = $input_params{'action'}; -if (defined $action) { - if (!validate_action($action)) { - die_error(400, "Invalid action parameter"); +our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base, + $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp, + $searchtext, $search_regexp); +sub evaluate_and_validate_params { + our $action = $input_params{'action'}; + if (defined $action) { + if (!validate_action($action)) { + die_error(400, "Invalid action parameter"); + } } -} -# parameters which are pathnames -our $project = $input_params{'project'}; -if (defined $project) { - if (!validate_project($project)) { - undef $project; - die_error(404, "No such project"); + # parameters which are pathnames + our $project = $input_params{'project'}; + if (defined $project) { + if (!validate_project($project)) { + undef $project; + die_error(404, "No such project"); + } } -} -our $file_name = $input_params{'file_name'}; -if (defined $file_name) { - if (!validate_pathname($file_name)) { - die_error(400, "Invalid file parameter"); + our $file_name = $input_params{'file_name'}; + if (defined $file_name) { + if (!validate_pathname($file_name)) { + die_error(400, "Invalid file parameter"); + } } -} -our $file_parent = $input_params{'file_parent'}; -if (defined $file_parent) { - if (!validate_pathname($file_parent)) { - die_error(400, "Invalid file parent parameter"); + our $file_parent = $input_params{'file_parent'}; + if (defined $file_parent) { + if (!validate_pathname($file_parent)) { + die_error(400, "Invalid file parent parameter"); + } } -} -# parameters which are refnames -our $hash = $input_params{'hash'}; -if (defined $hash) { - if (!validate_refname($hash)) { - die_error(400, "Invalid hash parameter"); + # parameters which are refnames + our $hash = $input_params{'hash'}; + if (defined $hash) { + if (!validate_refname($hash)) { + die_error(400, "Invalid hash parameter"); + } } -} -our $hash_parent = $input_params{'hash_parent'}; -if (defined $hash_parent) { - if (!validate_refname($hash_parent)) { - die_error(400, "Invalid hash parent parameter"); + our $hash_parent = $input_params{'hash_parent'}; + if (defined $hash_parent) { + if (!validate_refname($hash_parent)) { + die_error(400, "Invalid hash parent parameter"); + } } -} -our $hash_base = $input_params{'hash_base'}; -if (defined $hash_base) { - if (!validate_refname($hash_base)) { - die_error(400, "Invalid hash base parameter"); + our $hash_base = $input_params{'hash_base'}; + if (defined $hash_base) { + if (!validate_refname($hash_base)) { + die_error(400, "Invalid hash base parameter"); + } } -} -our @extra_options = @{$input_params{'extra_options'}}; -# @extra_options is always defined, since it can only be (currently) set from -# CGI, and $cgi->param() returns the empty array in array context if the param -# is not set -foreach my $opt (@extra_options) { - if (not exists $allowed_options{$opt}) { - die_error(400, "Invalid option parameter"); - } - if (not grep(/^$action$/, @{$allowed_options{$opt}})) { - die_error(400, "Invalid option parameter for this action"); + our @extra_options = @{$input_params{'extra_options'}}; + # @extra_options is always defined, since it can only be (currently) set from + # CGI, and $cgi->param() returns the empty array in array context if the param + # is not set + foreach my $opt (@extra_options) { + if (not exists $allowed_options{$opt}) { + die_error(400, "Invalid option parameter"); + } + if (not grep(/^$action$/, @{$allowed_options{$opt}})) { + die_error(400, "Invalid option parameter for this action"); + } } -} -our $hash_parent_base = $input_params{'hash_parent_base'}; -if (defined $hash_parent_base) { - if (!validate_refname($hash_parent_base)) { - die_error(400, "Invalid hash parent base parameter"); + our $hash_parent_base = $input_params{'hash_parent_base'}; + if (defined $hash_parent_base) { + if (!validate_refname($hash_parent_base)) { + die_error(400, "Invalid hash parent base parameter"); + } } -} -# other parameters -our $page = $input_params{'page'}; -if (defined $page) { - if ($page =~ m/[^0-9]/) { - die_error(400, "Invalid page parameter"); + # other parameters + our $page = $input_params{'page'}; + if (defined $page) { + if ($page =~ m/[^0-9]/) { + die_error(400, "Invalid page parameter"); + } } -} -our $searchtype = $input_params{'searchtype'}; -if (defined $searchtype) { - if ($searchtype =~ m/[^a-z]/) { - die_error(400, "Invalid searchtype parameter"); + our $searchtype = $input_params{'searchtype'}; + if (defined $searchtype) { + if ($searchtype =~ m/[^a-z]/) { + die_error(400, "Invalid searchtype parameter"); + } } -} -our $search_use_regexp = $input_params{'search_use_regexp'}; + our $search_use_regexp = $input_params{'search_use_regexp'}; -our $searchtext = $input_params{'searchtext'}; -our $search_regexp; -if (defined $searchtext) { - if (length($searchtext) < 2) { - die_error(403, "At least two characters are required for search parameter"); + our $searchtext = $input_params{'searchtext'}; + our $search_regexp; + if (defined $searchtext) { + if (length($searchtext) < 2) { + die_error(403, "At least two characters are required for search parameter"); + } + $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext; } - $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext; } # path to the current git repository our $git_dir; -$git_dir = "$projectroot/$project" if $project; +sub evaluate_git_dir { + our $git_dir = "$projectroot/$project" if $project; +} -# list of supported snapshot formats -our @snapshot_fmts = gitweb_get_feature('snapshot'); -@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts); +our (@snapshot_fmts, $git_avatar); +sub configure_gitweb_features { + # list of supported snapshot formats + our @snapshot_fmts = gitweb_get_feature('snapshot'); + @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts); -# check that the avatar feature is set to a known provider name, -# and for each provider check if the dependencies are satisfied. -# if the provider name is invalid or the dependencies are not met, -# reset $git_avatar to the empty string. -our ($git_avatar) = gitweb_get_feature('avatar'); -if ($git_avatar eq 'gravatar') { - $git_avatar = '' unless (eval { require Digest::MD5; 1; }); -} elsif ($git_avatar eq 'picon') { - # no dependencies -} else { - $git_avatar = ''; + # check that the avatar feature is set to a known provider name, + # and for each provider check if the dependencies are satisfied. + # if the provider name is invalid or the dependencies are not met, + # reset $git_avatar to the empty string. + our ($git_avatar) = gitweb_get_feature('avatar'); + if ($git_avatar eq 'gravatar') { + $git_avatar = '' unless (eval { require Digest::MD5; 1; }); + } elsif ($git_avatar eq 'picon') { + # no dependencies + } else { + $git_avatar = ''; + } } # dispatch -if (!defined $action) { - if (defined $hash) { - $action = git_get_type($hash); - } elsif (defined $hash_base && defined $file_name) { - $action = git_get_type("$hash_base:$file_name"); - } elsif (defined $project) { - $action = 'summary'; - } else { - $action = 'project_list'; +sub dispatch { + if (!defined $action) { + if (defined $hash) { + $action = git_get_type($hash); + } elsif (defined $hash_base && defined $file_name) { + $action = git_get_type("$hash_base:$file_name"); + } elsif (defined $project) { + $action = 'summary'; + } else { + $action = 'project_list'; + } } + if (!defined($actions{$action})) { + die_error(400, "Unknown action"); + } + if ($action !~ m/^(?:opml|project_list|project_index)$/ && + !$project) { + die_error(400, "Project needed"); + } + $actions{$action}->(); } -if (!defined($actions{$action})) { - die_error(400, "Unknown action"); -} -if ($action !~ m/^(?:opml|project_list|project_index)$/ && - !$project) { - die_error(400, "Project needed"); + +sub run { + our $t0 = [Time::HiRes::gettimeofday()] + if defined $t0; + + evaluate_uri(); + evaluate_gitweb_config(); + evaluate_git_version(); + check_loadavg(); + + # $projectroot and $projects_list might be set in gitweb config file + $projects_list ||= $projectroot; + + evaluate_query_params(); + evaluate_path_info(); + evaluate_and_validate_params(); + evaluate_git_dir(); + + configure_gitweb_features(); + + dispatch(); + + DONE_GITWEB: + 1; } -$actions{$action}->(); -DONE_GITWEB: -1; +our $cgi = CGI->new(); +run(); ## ====================================================================== ## action links -- cgit v1.3 From a0446e7ba8dc05c67052b67c2faacdf5f7ce625a Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 7 May 2010 14:54:05 +0200 Subject: gitweb: Add support for FastCGI, using CGI::Fast Former run() subroutine got renamed to run_request(). The new run() subroutine can run multiple requests at once if run as FastCGI script. To run gitweb as FastCGI script you must specify '--fastcgi' / '-f' command line option to gitweb, otherwise it runs as an ordinary CGI script. [jn: cherry picked from 56d7d436644ab296155a697552ea1345f2701620 in http://utsl.gen.nz/gitweb/?p=gitweb which was originally based on v264 (2326acfa95ac86a53804ca8eeeb482c2f9265e34) by Kay Sievers; updated to reflect current gitweb code] TODO: update 'gitweb/README' and/or 'gitweb/INSTALL' files. Signed-off-by: Sam Vilain Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 4a3632ab50..b044d18aca 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -999,7 +999,7 @@ sub dispatch { $actions{$action}->(); } -sub run { +sub run_request { our $t0 = [Time::HiRes::gettimeofday()] if defined $t0; @@ -1019,11 +1019,61 @@ sub run { configure_gitweb_features(); dispatch(); +} + +our $is_last_request = sub { 1 }; +our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook); +our $CGI = 'CGI'; +our $cgi; +sub evaluate_argv { + return unless (@ARGV); + + require Getopt::Long; + Getopt::Long::GetOptions( + 'fastcgi|fcgi|f' => sub { + require CGI::Fast; + our $CGI = 'CGI::Fast'; + + my $request_number = 0; + # let each child service 100 requests + our $is_last_request = sub { ++$request_number > 100 }; + }, + 'nproc|n=i' => sub { + my ($arg, $val) = @_; + return unless eval { require FCGI::ProcManager; 1; }; + my $proc_manager = FCGI::ProcManager->new({ + n_processes => $val, + }); + our $pre_listen_hook = sub { $proc_manager->pm_manage() }; + our $pre_dispatch_hook = sub { $proc_manager->pm_pre_dispatch() }; + our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() }; + }, + ); +} + +sub run { + evaluate_argv(); + + $pre_listen_hook->() + if $pre_listen_hook; + + REQUEST: + while ($cgi = $CGI->new()) { + $pre_dispatch_hook->() + if $pre_dispatch_hook; + + run_request(); + + $pre_dispatch_hook->() + if $post_dispatch_hook; + + last REQUEST if ($is_last_request->()); + } DONE_GITWEB: 1; } -our $cgi = CGI->new(); + run(); ## ====================================================================== -- cgit v1.3 From 04794fdc2701f15d79d92805cfe94c9e754e2e05 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Mon, 10 May 2010 18:41:35 +0200 Subject: gitweb: Use @diff_opts while using format-patch Make git-format-patch (used by 'patch' and 'patches' views) use the same rename detection options that git-diff and git-diff-tree (used by 'commitdiff', 'blobdiff', etc.) use. Signed-off-by: Pavan Kumar Sunkara Acked-by: Jakub Narebski Acked-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c356e95f18..77e5f795a1 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -6117,8 +6117,8 @@ sub git_commitdiff { } push @commit_spec, '--root', $hash; } - open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8', - '--stdout', @commit_spec + open $fd, "-|", git_cmd(), "format-patch", @diff_opts, + '--encoding=utf8', '--stdout', @commit_spec or die_error(500, "Open git-format-patch failed"); } else { die_error(400, "Unknown commitdiff format"); -- cgit v1.3 From 45aa9895c5bef3914b6f3707b983f7a53900ebb7 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Jun 2010 23:11:18 +0200 Subject: gitweb: Run in FastCGI mode if gitweb script has .fcgi extension If the name of the script ($SCRIPT_NAME or $SCRIPT_FILENAME CGI environment variable, or __FILE__ literal) ends with '.fcgi' extension, run gitweb in FastCGI mode, as if it was run with '--fastcgi' / '--fcgi' option. This is intended for easy deploying gitweb using FastCGI interface. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b044d18aca..e39ef866f0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1025,19 +1025,24 @@ our $is_last_request = sub { 1 }; our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook); our $CGI = 'CGI'; our $cgi; +sub configure_as_fcgi { + require CGI::Fast; + our $CGI = 'CGI::Fast'; + + my $request_number = 0; + # let each child service 100 requests + our $is_last_request = sub { ++$request_number > 100 }; +} sub evaluate_argv { + my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__; + configure_as_fcgi() + if $script_name =~ /\.fcgi$/; + return unless (@ARGV); require Getopt::Long; Getopt::Long::GetOptions( - 'fastcgi|fcgi|f' => sub { - require CGI::Fast; - our $CGI = 'CGI::Fast'; - - my $request_number = 0; - # let each child service 100 requests - our $is_last_request = sub { ++$request_number > 100 }; - }, + 'fastcgi|fcgi|f' => \&configure_as_fcgi, 'nproc|n=i' => sub { my ($arg, $val) = @_; return unless eval { require FCGI::ProcManager; 1; }; -- cgit v1.3 From ad709ea985b527adb546f4c841fbd480f2c6bdfd Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 13 Jun 2010 00:35:59 +0200 Subject: gitweb: Fix typo in hash key name in %opts in git_header_html The name of the key has to be the same in call site handle_errors_html and in called subroutine that uses it, i.e. git_header_html. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 934aacb614..e108bbc60a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3220,7 +3220,7 @@ sub git_header_html { } print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires) - unless ($opts{'-no_http_headers'}); + unless ($opts{'-no_http_header'}); my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : ''; print < -- cgit v1.3 From 5ed2ec1041b6aeec81b0f0a9775355a9b2c755a6 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 13 Jun 2010 12:09:32 +0200 Subject: gitweb: Return or exit after done serving request Check if there is a caller in top frame of gitweb, and either 'return' if gitweb code is wrapped in subroutine, or 'exit' if it is not. This should avoid gitweb.cgi: Subroutine git_SOMETHING redefined at gitweb.cgi line NNN warnings in error_log when running gitweb with mod_perl (using ModPerl::Registry handler) Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2365311d94..9d2b8c3b64 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1000,8 +1000,16 @@ if ($action !~ m/^(?:opml|project_list|project_index)$/ && die_error(400, "Project needed"); } $actions{$action}->(); + DONE_GITWEB: -1; +if (defined caller) { + # wrapped in a subroutine processing requests, + # e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI + return; +} else { + # pure CGI script, serving single request + exit; +} ## ====================================================================== ## action links -- cgit v1.3 From 869d58813b24c74e84c9388041eafcef40cb51e4 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 5 Jul 2010 20:52:43 +0200 Subject: gitweb: Move evaluate_gitweb_config out of run_request Move evaluate_gitweb_config() and evaluate_git_version() out of run_request() to run(), making them not run one for each request. This changes how git behaves in FastCGI case. This change makes it impossible to have config which changes with request, but I don't think anyone relied on such (hidden action) behavior. While at it, reset timer and number of git commands at beginning of run_request() in new reset_timer() subroutine. This fixes case when gitweb was run using FastCGI interface: time is reported for request, and not for single run of gitweb script. This changes slightly behavior in non-FastCGI case: the number of git commands reported is 1 less (running `git --version` one per gitweb is not counted now). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 9446376535..1f611d22d4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1027,18 +1027,18 @@ sub dispatch { $actions{$action}->(); } -sub run_request { +sub reset_timer { our $t0 = [Time::HiRes::gettimeofday()] if defined $t0; + our $number_of_git_cmds = 0; +} + +sub run_request { + reset_timer(); evaluate_uri(); - evaluate_gitweb_config(); - evaluate_git_version(); check_loadavg(); - # $projectroot and $projects_list might be set in gitweb config file - $projects_list ||= $projectroot; - evaluate_query_params(); evaluate_path_info(); evaluate_and_validate_params(); @@ -1086,6 +1086,11 @@ sub evaluate_argv { sub run { evaluate_argv(); + evaluate_gitweb_config(); + evaluate_git_version(); + + # $projectroot and $projects_list might be set in gitweb config file + $projects_list ||= $projectroot; $pre_listen_hook->() if $pre_listen_hook; -- cgit v1.3 From 109988f2cb00f78b746d05c40b8a8960bbb12ff8 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Thu, 15 Jul 2010 12:59:01 +0530 Subject: gitweb: fix esc_url Earlier, 452e225 (gitweb: fix esc_param, 2009-10-13) fixed CGI escaping rules used in esc_url. A very similar logic exists in esc_param and needs to be fixed the same way. Signed-off-by: Pavan Kumar Sunkara Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c356e95f18..a97ce03444 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1173,8 +1173,7 @@ sub esc_param { sub esc_url { my $str = shift; return undef unless defined $str; - $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg; - $str =~ s/\+/%2B/g; + $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg; $str =~ s/ /\+/g; return $str; } -- cgit v1.3 From 61bf126ecb24977b883079942b71ff96174c19fb Mon Sep 17 00:00:00 2001 From: "Alejandro R. Sedeño" Date: Wed, 28 Jul 2010 14:40:53 -0400 Subject: gitweb: move highlight config out of guess_file_syntax() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move highlight config out of guess_file_syntax() so that it can be extended/overridden by system/user configuration. Signed-off-by: Alejandro R. Sedeño Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index cedc357313..e0e9532648 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -232,6 +232,29 @@ our %avatar_size = ( # Leave it undefined (or set to 'undef') to turn off load checking. our $maxload = 300; +# configuration for 'highlight' (http://www.andre-simon.de/) +# match by basename +our %highlight_basename = ( + #'Program' => 'py', + #'Library' => 'py', + 'SConstruct' => 'py', # SCons equivalent of Makefile + 'Makefile' => 'make', +); +# match by extension +our %highlight_ext = ( + # main extensions, defining name of syntax; + # see files in /usr/share/highlight/langDefs/ directory + map { $_ => $_ } + qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), + # alternate extensions, see /etc/highlight/filetypes.conf + 'h' => 'c', + map { $_ => 'cpp' } qw(cxx c++ cc), + map { $_ => 'php' } qw(php3 php4), + map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' + 'mak' => 'make', + map { $_ => 'xml' } qw(xhtml html htm), +); + # You define site-wide feature defaults here; override them with # $GITWEB_CONFIG as necessary. our %feature = ( @@ -3316,30 +3339,6 @@ sub blob_contenttype { sub guess_file_syntax { my ($highlight, $mimetype, $file_name) = @_; return undef unless ($highlight && defined $file_name); - - # configuration for 'highlight' (http://www.andre-simon.de/) - # match by basename - my %highlight_basename = ( - #'Program' => 'py', - #'Library' => 'py', - 'SConstruct' => 'py', # SCons equivalent of Makefile - 'Makefile' => 'make', - ); - # match by extension - my %highlight_ext = ( - # main extensions, defining name of syntax; - # see files in /usr/share/highlight/langDefs/ directory - map { $_ => $_ } - qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), - # alternate extensions, see /etc/highlight/filetypes.conf - 'h' => 'c', - map { $_ => 'cpp' } qw(cxx c++ cc), - map { $_ => 'php' } qw(php3 php4), - map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' - 'mak' => 'make', - map { $_ => 'xml' } qw(xhtml html htm), - ); - my $basename = basename($file_name, '.in'); return $highlight_basename{$basename} if exists $highlight_basename{$basename}; -- cgit v1.3 From 7f425db9146b7ec8c67edfd8b49218cc110151ed Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 30 Jul 2010 22:01:59 -0500 Subject: gitweb: allow configurations that change with each request gitolite's contrib/gitweb/gitweb.conf includes: $ENV{GL_USER} = $cgi->remote_user || "gitweb"; which is useful for setups where a user has to be authenticated to access certain repos. Perhaps other typical configurations change per session in other ways, too. v1.7.2-rc2~6 (gitweb: Move evaluate_gitweb_config out of run_request, 2010-07-05) broke such configurations for a speedup, by loading the configuration once per FastCGI process. Probably in the end there should be a way to specify in the configuration whether a particular installation wants the speedup or the flexibility. But for now it is easier to just undo the relevant change. This partially reverts commit 869d58813b24c74e84c9388041eafcef40cb51e4. Reported-by: Julio Lajara Analysis-by: Jakub Narebski Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index cedc357313..c1e910af6d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1037,8 +1037,12 @@ sub run_request { reset_timer(); evaluate_uri(); + evaluate_gitweb_config(); check_loadavg(); + # $projectroot and $projects_list might be set in gitweb config file + $projects_list ||= $projectroot; + evaluate_query_params(); evaluate_path_info(); evaluate_and_validate_params(); @@ -1086,12 +1090,8 @@ sub evaluate_argv { sub run { evaluate_argv(); - evaluate_gitweb_config(); evaluate_git_version(); - # $projectroot and $projects_list might be set in gitweb config file - $projects_list ||= $projectroot; - $pre_listen_hook->() if $pre_listen_hook; -- cgit v1.3 From 0b45010e76ef2114a530d81da626871de23f467f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 2 Aug 2010 22:21:47 +0200 Subject: gitweb: Fix typo in run() subroutine Run $post_dispatch_hook->() not $pre_dispatch_hook->() after each request. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e0e9532648..8b02767271 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1125,7 +1125,7 @@ sub run { run_request(); - $pre_dispatch_hook->() + $post_dispatch_hook->() if $post_dispatch_hook; last REQUEST if ($is_last_request->()); -- cgit v1.3 From 497d9c343902591e98a6612385d529816420ea94 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sat, 7 Aug 2010 16:56:47 -0500 Subject: gitweb: clarify search results page when no matching commit found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When searching commits for a string that never occurs, the results page looks something like this: projects / foo.git / search \o/ summary | ... | tree [commit] search: [ kfjdkas ] [ ]re first ⋅ prev ⋅ next Merge branch 'maint' Foo: a demonstration project Without a list of hits to compare it to, the header describing the commit named by the hash parameter (usually HEAD) may itself look like a hit. Add some text (“No match.”) to replace the empty list of hits and avoid this confusion. While at it, remove some nearby dead code, left behind from a simplification a few years ago (v1.5.4-rc0~276^2~4, 2007-11-01). Noticed-by: Erick Mattos Signed-off-by: Jonathan Nieder Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index cedc357313..6deec87e5d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -6522,12 +6522,13 @@ sub git_search { $paging_nav .= " ⋅ next"; } - if ($#commitlist >= 100) { - } - git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash); - git_search_grep_body(\@commitlist, 0, 99, $next_link); + if ($page == 0 && !@commitlist) { + print "

No match.

\n"; + } else { + git_search_grep_body(\@commitlist, 0, 99, $next_link); + } } if ($searchtype eq 'pickaxe') { -- cgit v1.3 From 22e5e58a3c75b73764b860907e4d871195f276ac Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Sun, 22 Aug 2010 13:12:12 +0200 Subject: Typos in code comments, an error message, documentation Signed-off-by: Ralf Wildenhues Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.6.3.txt | 2 +- Documentation/RelNotes-1.6.0.2.txt | 2 +- Documentation/RelNotes-1.6.4.3.txt | 2 +- Documentation/RelNotes-1.6.5.4.txt | 2 +- Documentation/RelNotes-1.6.5.7.txt | 2 +- Documentation/RelNotes-1.6.6.txt | 4 ++-- Documentation/RelNotes-1.7.0.txt | 2 +- Documentation/howto/revert-a-faulty-merge.txt | 2 +- builtin/blame.c | 4 ++-- compat/mingw.c | 2 +- compat/nedmalloc/malloc.c.h | 2 +- compat/regex/regex.c | 2 +- contrib/workdir/git-new-workdir | 4 ++-- gitweb/README | 4 ++-- gitweb/gitweb.perl | 12 ++++++------ graph.c | 2 +- pack-check.c | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/Documentation/RelNotes-1.5.6.3.txt b/Documentation/RelNotes-1.5.6.3.txt index 942611299d..f61dd3504a 100644 --- a/Documentation/RelNotes-1.5.6.3.txt +++ b/Documentation/RelNotes-1.5.6.3.txt @@ -4,7 +4,7 @@ GIT v1.5.6.3 Release Notes Fixes since v1.5.6.2 -------------------- -* Setting core.sharerepository to traditional "true" value was supposed to make +* Setting core.sharedrepository to traditional "true" value was supposed to make the repository group writable but should not affect permission for others. However, since 1.5.6, it was broken to drop permission for others when umask is 022, making the repository unreadable by others. diff --git a/Documentation/RelNotes-1.6.0.2.txt b/Documentation/RelNotes-1.6.0.2.txt index 51b32f5d94..e1e24b3295 100644 --- a/Documentation/RelNotes-1.6.0.2.txt +++ b/Documentation/RelNotes-1.6.0.2.txt @@ -17,7 +17,7 @@ Fixes since v1.6.0.1 * Many commands did not use the correct working tree location when used with GIT_WORK_TREE environment settings. -* Some systems needs to use compatibility fnmach and regex libraries +* Some systems need to use compatibility fnmatch and regex libraries independent from each other; the compat/ area has been reorganized to allow this. diff --git a/Documentation/RelNotes-1.6.4.3.txt b/Documentation/RelNotes-1.6.4.3.txt index 4f29babdeb..5643e6537d 100644 --- a/Documentation/RelNotes-1.6.4.3.txt +++ b/Documentation/RelNotes-1.6.4.3.txt @@ -11,7 +11,7 @@ Fixes since v1.6.4.2 been deprecated. * "git fetch" and "git clone" had an extra sanity check to verify the - presense of the corresponding *.pack file before downloading *.idx + presence of the corresponding *.pack file before downloading *.idx file by issuing a HEAD request. Github server however sometimes gave 500 (Internal server error) response to HEAD even if a GET request for *.pack file to the same URL would have succeeded, and broke diff --git a/Documentation/RelNotes-1.6.5.4.txt b/Documentation/RelNotes-1.6.5.4.txt index e42f8b2397..d3a2a3e712 100644 --- a/Documentation/RelNotes-1.6.5.4.txt +++ b/Documentation/RelNotes-1.6.5.4.txt @@ -26,7 +26,7 @@ Fixes since v1.6.5.3 future versions, but not in this release, * "git merge -m ..." added the standard merge message - on its own after user-supplied message, which should have overrided the + on its own after user-supplied message, which should have overridden the standard one. Other minor documentation updates are included. diff --git a/Documentation/RelNotes-1.6.5.7.txt b/Documentation/RelNotes-1.6.5.7.txt index 5b49ea53be..dc5302c21c 100644 --- a/Documentation/RelNotes-1.6.5.7.txt +++ b/Documentation/RelNotes-1.6.5.7.txt @@ -10,7 +10,7 @@ Fixes since v1.6.5.6 an older version of git should just ignore them. Instead we diagnosed it as an error. -* With help.autocorrect set to non-zero value, the logic to guess typoes +* With help.autocorrect set to non-zero value, the logic to guess typos in the subcommand name misfired and ran a random nonsense command. * If a command is run with an absolute path as a pathspec inside a bare diff --git a/Documentation/RelNotes-1.6.6.txt b/Documentation/RelNotes-1.6.6.txt index 04e205c457..c50b59c495 100644 --- a/Documentation/RelNotes-1.6.6.txt +++ b/Documentation/RelNotes-1.6.6.txt @@ -29,7 +29,7 @@ or adjust to the new behaviour, on the day their sysadmin decides to install the new version of git. When we switched from "git-foo" to "git foo" in 1.6.0, even though the change had been advertised and the transition guide had been provided for a very long time, the users procrastinated -during the entire transtion period, and ended up panicking on the day +during the entire transition period, and ended up panicking on the day their sysadmins updated their git installation. We are trying to avoid repeating that unpleasantness in the 1.7.0 release. @@ -94,7 +94,7 @@ users will fare this time. * "git diff" traditionally treated various "ignore whitespace" options only as a way to filter the patch output. "git diff --exit-code -b" exited with non-zero status even if all changes were about changing the - ammount of whitespace and nothing else. and "git diff -b" showed the + amount of whitespace and nothing else. and "git diff -b" showed the "diff --git" header line for such a change without patch text. In 1.7.0, the "ignore whitespaces" will affect the semantics of the diff --git a/Documentation/RelNotes-1.7.0.txt b/Documentation/RelNotes-1.7.0.txt index 43e3f33615..0bb8c0b2a2 100644 --- a/Documentation/RelNotes-1.7.0.txt +++ b/Documentation/RelNotes-1.7.0.txt @@ -202,7 +202,7 @@ release, unless otherwise noted. the branch is fully merged to its upstream branch if it is not merged to the current branch. It now deletes it in such a case. - * "fiter-branch" command incorrectly said --prune-empty and --filter-commit + * "filter-branch" command incorrectly said --prune-empty and --filter-commit were incompatible; the latter should be read as --commit-filter. * When using "git status" or asking "git diff" to compare the work tree diff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt index ff5c0bc27a..6fd711996a 100644 --- a/Documentation/howto/revert-a-faulty-merge.txt +++ b/Documentation/howto/revert-a-faulty-merge.txt @@ -229,7 +229,7 @@ reverting W. Mainline's history would look like this: A---B---C But if you don't actually need to change commit A, then you need some way to -recreate it as a new commit with the same changes in it. The rebase commmand's +recreate it as a new commit with the same changes in it. The rebase command's --no-ff option provides a way to do this: $ git rebase [-i] --no-ff P diff --git a/builtin/blame.c b/builtin/blame.c index 01e62fdeb0..28e3be2ead 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2376,11 +2376,11 @@ parse_done: * * The remaining are: * - * (1) if dashdash_pos != 0, its either + * (1) if dashdash_pos != 0, it is either * "blame [revisions] -- " or * "blame -- " * - * (2) otherwise, its one of the two: + * (2) otherwise, it is one of the two: * "blame [revisions] " * "blame " * diff --git a/compat/mingw.c b/compat/mingw.c index 9a8e336582..96be8a02cf 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -641,7 +641,7 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on } /* - * Determines the absolute path of cmd using the the split path in path. + * Determines the absolute path of cmd using the split path in path. * If cmd contains a slash or backslash, no lookup is performed. */ static char *path_lookup(const char *cmd, char **path, int exe_only) diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h index 74c42e3162..87260d2642 100644 --- a/compat/nedmalloc/malloc.c.h +++ b/compat/nedmalloc/malloc.c.h @@ -2069,7 +2069,7 @@ static void init_malloc_global_mutex() { Each freshly allocated chunk must have both cinuse and pinuse set. That is, each allocated chunk borders either a previously allocated and still in-use chunk, or the base of its memory arena. This is - ensured by making all allocations from the the `lowest' part of any + ensured by making all allocations from the `lowest' part of any found chunk. Further, no free chunk physically borders another one, so each free chunk is known to be preceded and followed by either inuse chunks or the ends of memory. diff --git a/compat/regex/regex.c b/compat/regex/regex.c index 556d8ab11f..be851fc502 100644 --- a/compat/regex/regex.c +++ b/compat/regex/regex.c @@ -3122,7 +3122,7 @@ re_match (bufp, string, size, pos, regs) /* re_match_2 matches the compiled pattern in BUFP against the - the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 and SIZE2, respectively). We start matching at POS, and stop matching at STOP. diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 993cacf324..3ad2c0cea5 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -54,13 +54,13 @@ then die "destination directory '$new_workdir' already exists." fi -# make sure the the links use full paths +# make sure the links use full paths git_dir=$(cd "$git_dir"; pwd) # create the workdir mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" -# create the links to the original repo. explictly exclude index, HEAD and +# create the links to the original repo. explicitly exclude index, HEAD and # logs/HEAD from the list since they are purely related to the current working # directory, and should not be shared. for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn diff --git a/gitweb/README b/gitweb/README index 0e19be8d21..d481198796 100644 --- a/gitweb/README +++ b/gitweb/README @@ -95,7 +95,7 @@ You can specify the following configuration variables when building GIT: 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 localtion where you put gitweb.js on your web server + 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 @@ -233,7 +233,7 @@ not include variables usually directly set during build): 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. + If server load exceed this value then return "503 Service Unavailable" 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. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6deec87e5d..84261bba34 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -245,7 +245,7 @@ our %feature = ( # return value of feature-sub indicates if to enable specified feature # # if there is no 'sub' key (no feature-sub), then feature cannot be - # overriden + # overridden # # use gitweb_get_feature() to retrieve the value # (an array) or gitweb_check_feature() to check if @@ -1323,7 +1323,7 @@ sub esc_param { return $str; } -# quote unsafe chars in whole URL, so some charactrs cannot be quoted +# quote unsafe chars in whole URL, so some characters cannot be quoted sub esc_url { my $str = shift; return undef unless defined $str; @@ -3782,9 +3782,9 @@ sub git_print_authorship { } # Outputs table rows containing the full author or committer information, -# in the format expected for 'commit' view (& similia). +# in the format expected for 'commit' view (& similar). # Parameters are a commit hash reference, followed by the list of people -# to output information for. If the list is empty it defalts to both +# to output information for. If the list is empty it defaults to both # author and committer. sub git_print_authorship_rows { my $co = shift; @@ -4513,8 +4513,8 @@ sub git_patchset_body { print "\n"; # class="patch" } - # for compact combined (--cc) format, with chunk and patch simpliciaction - # patchset might be empty, but there might be unprocessed raw lines + # for compact combined (--cc) format, with chunk and patch simplification + # the patchset might be empty, but there might be unprocessed raw lines for (++$patch_idx if $patch_number > 0; $patch_idx < @$difftree; ++$patch_idx) { diff --git a/graph.c b/graph.c index ac7c605406..85ab150787 100644 --- a/graph.c +++ b/graph.c @@ -439,7 +439,7 @@ static void graph_update_width(struct git_graph *graph, max_cols++; /* - * We added a column for the the current commit as part of + * We added a column for the current commit as part of * graph->num_parents. If the current commit was already in * graph->columns, then we have double counted it. */ diff --git a/pack-check.c b/pack-check.c index 395fb9527a..9d0cb9a114 100644 --- a/pack-check.c +++ b/pack-check.c @@ -77,7 +77,7 @@ static int verify_packfile(struct packed_git *p, err = error("%s SHA1 checksum mismatch", p->pack_name); if (hashcmp(index_base + index_size - 40, pack_sig)) - err = error("%s SHA1 does not match its inddex", + err = error("%s SHA1 does not match its index", p->pack_name); unuse_pack(w_curs); -- cgit v1.3 From d8a94803842989582989fd5f5c3062c49134ad5b Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Fri, 27 Aug 2010 13:38:16 -0400 Subject: gitweb: Don't die_error in git_tag after already printing headers This fixes an XML error when visiting a nonexistent tag (i.e. "../gitweb.cgi?p=git.git;a=tag;h=refs/tags/BADNAME"). Signed-off-by: Anders Kaseorg Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d0687f4581..a85e2f6319 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5191,15 +5191,15 @@ sub git_summary { } sub git_tag { - my $head = git_get_head_hash($project); - git_header_html(); - git_print_page_nav('','', $head,undef,$head); my %tag = parse_tag($hash); if (! %tag) { die_error(404, "Unknown tag object"); } + my $head = git_get_head_hash($project); + git_header_html(); + git_print_page_nav('','', $head,undef,$head); git_print_header_div('commit', esc_html($tag{'name'}), $hash); print "
\n" . "\n" . -- cgit v1.3 From 7ce896b3000a7bd2ea24f02ad3051f43ad351e1f Mon Sep 17 00:00:00 2001 From: Christopher Wilson Date: Tue, 21 Sep 2010 00:25:19 -0700 Subject: Enable highlight executable path as a configuration option Allow build-time/run-time configuration of the highlight executable (must be the one from http://www.andre-simon.de due to assumptions about parameters and output). Defaults to previous behavior which assumes that highlight is available on the server PATH. However, if this is not the case, the path to the highlight executable can be configured at build time as a configuration variable HIGHLIGHT_BIN = /path/to/highlight or at runtime by configuring GITWEB_CONFIG $highlight_bin = /path/to/highlight Signed-off-by: Christopher Wilson Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/Makefile | 4 +++- gitweb/README | 11 ++++++++++- gitweb/gitweb.perl | 9 ++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) mode change 100644 => 100755 gitweb/README (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/Makefile b/gitweb/Makefile index 2fb7c2d77b..e32ee76309 100644 --- a/gitweb/Makefile +++ b/gitweb/Makefile @@ -35,6 +35,7 @@ GITWEB_FAVICON = static/git-favicon.png GITWEB_JS = static/gitweb.js GITWEB_SITE_HEADER = GITWEB_SITE_FOOTER = +HIGHLIGHT_BIN = highlight # include user config -include ../config.mak.autogen @@ -129,7 +130,8 @@ GITWEB_REPLACE = \ -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' + -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \ + -e 's|++HIGHLIGHT_BIN++|$(HIGHLIGHT_BIN)|g' GITWEB-BUILD-OPTIONS: FORCE @rm -f $@+ diff --git a/gitweb/README b/gitweb/README old mode 100644 new mode 100755 index d481198796..bf3664f2b7 --- a/gitweb/README +++ b/gitweb/README @@ -114,6 +114,11 @@ You can specify the following configuration variables when building GIT: 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 @@ -236,7 +241,11 @@ not include variables usually directly set during build): If server load exceed this value then return "503 Service Unavailable" 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. - + * $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] Projects list file format ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index a85e2f6319..e5910ce8f9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -165,6 +165,12 @@ our @diff_opts = ('-M'); # taken from git_commit # the gitweb domain. our $prevent_xss = 0; +# 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] +our $highlight_bin = "++HIGHLIGHT_BIN++"; + # information about snapshot formats that gitweb is capable of serving our %known_snapshot_formats = ( # name => { @@ -3360,7 +3366,8 @@ sub run_highlighter { close $fd or die_error(404, "Reading blob failed"); open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". - "highlight --xhtml --fragment --syntax $syntax |" + quote_command($highlight_bin). + " --xhtml --fragment --syntax $syntax |" or die_error(500, "Couldn't open file or run syntax highlighter"); return $fd; } -- cgit v1.3 From d48b28418355ef41a9501eb28a82ec0b69e62a17 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Fri, 24 Sep 2010 20:00:52 +0000 Subject: perl: bump the required Perl version to 5.8 from 5.6.[21] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Formalize our dependency on perl 5.8, bumped from 5.6.[12]. We already used the three-arg form of open() which was introduced in 5.6.1, but t/t9700/test.pl explicitly depended on 5.6.2. However git-add--interactive.pl has been failing on the 5.6 line since it was introduced in v1.5.0-rc0~12^2~2 back in 2006 due to this open syntax: sub run_cmd_pipe { my $fh = undef; open($fh, '-|', @_) or die; return <$fh>; } Which when executed dies on "Can't use an undefined value as filehandle reference". Several of our tests also fail on 5.6 (even more when compiled with NO_PERL_MAKEMAKER=1): t2016-checkout-patch.sh t3904-stash-patch.sh t3701-add-interactive.sh t7105-reset-patch.sh t7501-commit.sh t9700-perl-git.sh Our code is bitrotting on 5.6 with no-one interested in fixing it, and pinning us to such an ancient release of Perl is keeping us from using useful features introduced in the 5.8 release. The 5.6 series is now over 10 years old, and the 5.6.2 maintenance release almost 7. 5.8 on the other hand is more than 8 years old. All the modern Unix-like operating systems have now upgraded to it or a later version, and 5.8 packages are available for old IRIX, AIX Solaris and Tru64 systems. Signed-off-by: Ævar Arnfjörð Bjarmason Acked-by: Tor Arntsen Acked-by: Randal L. Schwartz Signed-off-by: Junio C Hamano --- INSTALL | 8 ++++---- git-add--interactive.perl | 1 + git-archimport.perl | 1 + git-cvsexportcommit.perl | 1 + git-cvsimport.perl | 1 + git-cvsserver.perl | 1 + git-difftool.perl | 1 + git-relink.perl | 2 +- git-send-email.perl | 1 + git-svn.perl | 1 + gitweb/gitweb.perl | 1 + perl/Git.pm | 1 + t/t7006/test-terminal.perl | 1 + t/t9700/test.pl | 2 +- 14 files changed, 17 insertions(+), 6 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/INSTALL b/INSTALL index 59200b730e..10a1cba643 100644 --- a/INSTALL +++ b/INSTALL @@ -67,10 +67,10 @@ Issues of note: - A POSIX-compliant shell is required to run many scripts needed for everyday use (e.g. "bisect", "pull"). - - "Perl" is needed to use some of the features (e.g. preparing a - partial commit using "git add -i/-p", interacting with svn - repositories with "git svn"). If you can live without these, use - NO_PERL. + - "Perl" version 5.8 or later is needed to use some of the + features (e.g. preparing a partial commit using "git add -i/-p", + interacting with svn repositories with "git svn"). If you can + live without these, use NO_PERL. - "openssl" library is used by git-imap-send to use IMAP over SSL. If you don't need it, use NO_OPENSSL. diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 27fc79347a..a96fb53156 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1,5 +1,6 @@ #!/usr/bin/perl -w +use 5.008; use strict; use Git; diff --git a/git-archimport.perl b/git-archimport.perl index 98f3ede566..947638c38d 100755 --- a/git-archimport.perl +++ b/git-archimport.perl @@ -54,6 +54,7 @@ and can contain multiple, unrelated branches. =cut +use 5.008; use strict; use warnings; use Getopt::Std; diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 59b672213b..9a8188bba7 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,5 +1,6 @@ #!/usr/bin/perl -w +use 5.008; use strict; use Getopt::Std; use File::Temp qw(tempdir); diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 9e03eee458..53869fb644 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -13,6 +13,7 @@ # The head revision is on branch "origin" by default. # You can change that with the '-o' option. +use 5.008; use strict; use warnings; use Getopt::Long; diff --git a/git-cvsserver.perl b/git-cvsserver.perl index e9f3037df3..2822bed1fd 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -15,6 +15,7 @@ #### #### +use 5.008; use strict; use warnings; use bytes; diff --git a/git-difftool.perl b/git-difftool.perl index adc42de875..e95e4ad973 100755 --- a/git-difftool.perl +++ b/git-difftool.perl @@ -10,6 +10,7 @@ # # Any arguments that are unknown to this script are forwarded to 'git diff'. +use 5.008; use strict; use warnings; use Cwd qw(abs_path); diff --git a/git-relink.perl b/git-relink.perl index 937c69a748..af2e305fa3 100755 --- a/git-relink.perl +++ b/git-relink.perl @@ -6,7 +6,7 @@ # # Scan two git object-trees, and hardlink any common objects between them. -use 5.006; +use 5.008; use strict; use warnings; use Getopt::Long; diff --git a/git-send-email.perl b/git-send-email.perl index 6dab3bf6a7..314e59e7a9 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -16,6 +16,7 @@ # and second line is the subject of the message. # +use 5.008; use strict; use warnings; use Term::ReadLine; diff --git a/git-svn.perl b/git-svn.perl index 9b046b693f..d2922245aa 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # Copyright (C) 2006, Eric Wong # License: GPL v2 or later +use 5.008; use warnings; use strict; use vars qw/ $AUTHOR $VERSION diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index a85e2f6319..e645d4a821 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -7,6 +7,7 @@ # # This program is licensed under the GPLv2 +use 5.008; use strict; use warnings; use CGI qw(:standard :escapeHTML -nosticky); diff --git a/perl/Git.pm b/perl/Git.pm index 6cb0dd1934..205e48aa3a 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -7,6 +7,7 @@ Git - Perl interface to the Git version control system package Git; +use 5.008; use strict; diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl index 73ff809371..6b5f22ae4a 100755 --- a/t/t7006/test-terminal.perl +++ b/t/t7006/test-terminal.perl @@ -1,4 +1,5 @@ #!/usr/bin/perl +use 5.008; use strict; use warnings; use IO::Pty; diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 671f38db2b..c15ca2d647 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl use lib (split(/:/, $ENV{GITPERLLIB})); -use 5.006002; +use 5.008; use warnings; use strict; -- cgit v1.3 From 8ff76f4c3b4e72ac8259465bdf6e33f4da19170d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 26 Sep 2010 13:34:56 +0200 Subject: gitweb: Move call to evaluate_git_version after evaluate_gitweb_config Now evaluate_git_version() is inside run_request() to be called for each request, instead of once per starting gitweb; this currently matters only when using FastCGI interface (gitweb.fcgi). This change was done because evaluate_git_version() uses $GIT variable, which can be set / modified by gitweb config file, but the variable is modified this way by gitweb config file used in gitweb tests. Without this change there is spurious extra output from t9500 test when tests are run with '--debug' option. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index a85e2f6319..d521b4cf88 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1061,6 +1061,7 @@ sub run_request { evaluate_uri(); evaluate_gitweb_config(); + evaluate_git_version(); check_loadavg(); # $projectroot and $projects_list might be set in gitweb config file @@ -1113,7 +1114,6 @@ sub evaluate_argv { sub run { evaluate_argv(); - evaluate_git_version(); $pre_listen_hook->() if $pre_listen_hook; -- cgit v1.3 From 7e00dc58d14a5f8ea229e5a0acf5f019028e3a40 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 13 Oct 2010 13:33:48 +0200 Subject: gitweb: Fix bug in evaluate_path_info There was bug in parsing "project/:/file" and "project/:/" path_info URLs, with implicit HEAD as 'hash_base'. For such URLs the refname is empty, and before this fix regexp for parsing path_info fragment assumed that it is always non-empty. Refname cannot contain ':', as per 'git check-ref-format'. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e5910ce8f9..c4d3e0846a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -780,10 +780,10 @@ sub evaluate_path_info { 'history', ); - # we want to catch + # we want to catch, among others # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name] my ($parentrefname, $parentpathname, $refname, $pathname) = - ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/); + ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/); # first, analyze the 'current' part if (defined $pathname) { -- cgit v1.3 From d0af3734f14410e9c297aa08bf46fdb153a25ccb Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 13 Oct 2010 13:35:20 +0200 Subject: gitweb: Improve behavior for actionless path_info gitweb URLs Eli Barzilay noticed that http://server/gitweb/project/ link goes to 'shortlog' view, while 'commit' view would be more useful, but that 'shortlog' action is more apropriate for http://server/gitweb/project/.. links. Therefore for the case when we don't have either action, or filename, or parent hash [base] in path_info-based URL, i.e. for http://server/gitweb/project/ link, instead of using 'shortlog' view we allow dispatch() subroutine to detect type of object and use appropriate action (in most case it would be either 'commit' action, or 'tag', or 'tree' for top directory). Requested-by: Eli Barzilay Signed-off-by: Jakub Narebski Tested-by: Eli Barzilay Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c4d3e0846a..8d7e4c5e49 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -819,8 +819,15 @@ sub evaluate_path_info { # hash_base instead. It should also be noted that hand-crafted # links having 'history' as an action and no pathname or hash # set will fail, but that happens regardless of PATH_INFO. - $input_params{'action'} ||= "shortlog"; - if (grep { $_ eq $input_params{'action'} } @wants_base) { + if (defined $parentrefname) { + # if there is parent let the default be 'shortlog' action + # (for http://git.example.com/repo.git/A..B links); if there + # is no parent, dispatch will detect type of object and set + # action appropriately if required (if action is not set) + $input_params{'action'} ||= "shortlog"; + } + if ($input_params{'action'} && + grep { $_ eq $input_params{'action'} } @wants_base) { $input_params{'hash_base'} ||= $refname; } else { $input_params{'hash'} ||= $refname; -- cgit v1.3 From 3962f1d756ab41c1d180e35483d1c8dffe51e0d1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 9 Nov 2010 19:27:54 +0100 Subject: gitweb: Time::HiRes is in core for Perl 5.8 We say 'use 5.008' at the beginning of the script, therefore there is no need to check if Time::HiRes module is available. We can also import gettimeofday and tv_interval. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 253f41adc9..23f9e79932 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -17,12 +17,10 @@ use Encode; use Fcntl ':mode'; use File::Find qw(); use File::Basename qw(basename); +use Time::HiRes qw(gettimeofday tv_interval); binmode STDOUT, ':utf8'; -our $t0; -if (eval { require Time::HiRes; 1; }) { - $t0 = [Time::HiRes::gettimeofday()]; -} +our $t0 = [ gettimeofday() ]; our $number_of_git_cmds = 0; BEGIN { @@ -1065,7 +1063,7 @@ sub dispatch { } sub reset_timer { - our $t0 = [Time::HiRes::gettimeofday()] + our $t0 = [ gettimeofday() ] if defined $t0; our $number_of_git_cmds = 0; } @@ -3590,7 +3588,7 @@ sub git_footer_html { print "
\n"; print 'This page took '. ''. - Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]). + tv_interval($t0, [ gettimeofday() ]). ' seconds '. ' and '. ''. @@ -5298,7 +5296,7 @@ sub git_blame_common { print 'END'; if (defined $t0 && gitweb_check_feature('timed')) { print ' '. - Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]). + tv_interval($t0, [ gettimeofday() ]). ' '.$number_of_git_cmds; } print "\n"; -- cgit v1.3 From 9e70e15814d645b48e2ed892520b88122c992b30 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:08 +0100 Subject: gitweb: use fullname as hash_base in heads link Otherwise, if names are manipulated for display, the link will point to the wrong head. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 253f41adc9..77693abb2a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4960,7 +4960,7 @@ sub git_heads_body { "
\n" . ""; } -- cgit v1.3 From 60efa2451e46810bdf7da6a31758c779de8ff4f7 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:09 +0100 Subject: gitweb: introduce remote_heads feature With this feature enabled, remote heads are retrieved (and displayed) when getting (and displaying) the heads list. Typical usage would be for local repository browsing, e.g. by using git-instaweb (or even a more permanent gitweb setup), to check the repository status and the relation between tracking branches and the originating remotes. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 77693abb2a..e1787c2e0e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -493,6 +493,18 @@ our %feature = ( 'sub' => sub { feature_bool('highlight', @_) }, 'override' => 0, 'default' => [0]}, + + # Enable displaying of remote heads in the heads list + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'remote_heads'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'remote_heads'}{'override'} = 1; + # and in project config gitweb.remote_heads = 0|1; + 'remote_heads' => { + 'sub' => sub { feature_bool('remote_heads', @_) }, + 'override' => 0, + 'default' => [0]}, ); sub gitweb_get_feature { @@ -3160,10 +3172,12 @@ sub git_get_heads_list { my $limit = shift; my @headslist; + my $remote_heads = gitweb_check_feature('remote_heads'); + open my $fd, '-|', git_cmd(), 'for-each-ref', ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate', '--format=%(objectname) %(refname) %(subject)%00%(committer)', - 'refs/heads' + 'refs/heads', ($remote_heads ? 'refs/remotes' : ()) or return; while (my $line = <$fd>) { my %ref_item; @@ -3174,7 +3188,7 @@ sub git_get_heads_list { my ($committer, $epoch, $tz) = ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); $ref_item{'fullname'} = $name; - $name =~ s!^refs/heads/!!; + $name =~ s!^refs/(?:head|remote)s/!!; $ref_item{'name'} = $name; $ref_item{'id'} = $hash; -- cgit v1.3 From 9b3f3de16c4a3b8414e5d49f79dda343a4ec1511 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:10 +0100 Subject: gitweb: git_get_heads_list accepts an optional list of refs git_get_heads_list(limit, class1, class2, ...) can now be used to retrieve refs/class1, refs/class2 etc. Defaults to ('heads', 'remotes') or ('heads') depending on whether the 'remote_heads' feature is enabled or not. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e1787c2e0e..951bb0d6f0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3169,15 +3169,18 @@ sub parse_from_to_diffinfo { ## parse to array of hashes functions sub git_get_heads_list { - my $limit = shift; + my ($limit, @classes) = @_; + unless (@classes) { + my $remote_heads = gitweb_check_feature('remote_heads'); + @classes = ('heads', $remote_heads ? 'remotes' : ()); + } + my @patterns = map { "refs/$_" } @classes; my @headslist; - my $remote_heads = gitweb_check_feature('remote_heads'); - open my $fd, '-|', git_cmd(), 'for-each-ref', ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate', '--format=%(objectname) %(refname) %(subject)%00%(committer)', - 'refs/heads', ($remote_heads ? 'refs/remotes' : ()) + @patterns or return; while (my $line = <$fd>) { my %ref_item; -- cgit v1.3 From 00fa6fef631edeb5e0e34b7748c07882ec0e51d7 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:11 +0100 Subject: gitweb: separate heads and remotes lists We specialize the 'heads' action to only display local branches, and introduce a 'remotes' action to display the remote branches (only available when the remotes_head feature is enabled). Mirroring this, we also split the heads list in summary view into local and remote lists, each linking to the appropriate action. The git_get_heads_list now defaults to 'heads' only, regardless of whether the remote heads feature is active or not. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 951bb0d6f0..9fcbbb2d6c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -719,6 +719,7 @@ our %actions = ( "log" => \&git_log, "patch" => \&git_patch, "patches" => \&git_patches, + "remotes" => \&git_remotes, "rss" => \&git_rss, "atom" => \&git_atom, "search" => \&git_search, @@ -3170,10 +3171,7 @@ sub parse_from_to_diffinfo { sub git_get_heads_list { my ($limit, @classes) = @_; - unless (@classes) { - my $remote_heads = gitweb_check_feature('remote_heads'); - @classes = ('heads', $remote_heads ? 'remotes' : ()); - } + @classes = ('heads') unless @classes; my @patterns = map { "refs/$_" } @classes; my @headslist; @@ -5126,6 +5124,7 @@ sub git_summary { my %co = parse_commit("HEAD"); my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : (); my $head = $co{'id'}; + my $remote_heads = gitweb_check_feature('remote_heads'); my $owner = git_get_project_owner($project); @@ -5134,6 +5133,7 @@ sub git_summary { # there are more ... my @taglist = git_get_tags_list(16); my @headlist = git_get_heads_list(16); + my @remotelist = $remote_heads ? git_get_heads_list(16, 'remotes') : (); my @forklist; my $check_forks = gitweb_check_feature('forks'); @@ -5211,6 +5211,13 @@ sub git_summary { $cgi->a({-href => href(action=>"heads")}, "...")); } + if (@remotelist) { + git_print_header_div('remotes'); + git_heads_body(\@remotelist, $head, 0, 15, + $#remotelist <= 15 ? undef : + $cgi->a({-href => href(action=>"remotes")}, "...")); + } + if (@forklist) { git_print_header_div('forks'); git_project_list_body(\@forklist, 'age', 0, 15, @@ -5525,6 +5532,22 @@ sub git_heads { git_footer_html(); } +sub git_remotes { + gitweb_check_feature('remote_heads') + or die_error(403, "Remote heads view is disabled"); + + my $head = git_get_head_hash($project); + git_header_html(); + git_print_page_nav('','', $head,undef,$head); + git_print_header_div('summary', $project); + + my @remotelist = git_get_heads_list(undef, 'remotes'); + if (@remotelist) { + git_heads_body(\@remotelist, $head); + } + git_footer_html(); +} + sub git_blob_plain { my $type = shift; my $expires; -- cgit v1.3 From 11e7bece1559695faf72803ef8aa52c1d65350f9 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:12 +0100 Subject: gitweb: nagivation menu for tags, heads and remotes tags, heads and remotes are all views that inspect a (particular class of) refs, so allow the user to easily switch between them by adding the appropriate navigation submenu to each view. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 9fcbbb2d6c..7cd50d4b82 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3733,6 +3733,19 @@ sub git_print_page_nav { "\n"; } +# returns a submenu for the nagivation of the refs views (tags, heads, +# remotes) with the current view disabled and the remotes view only +# available if the feature is enabled +sub format_ref_views { + my ($current) = @_; + my @ref_views = qw{tags heads}; + push @ref_views, 'remotes' if gitweb_check_feature('remote_heads'); + return join " | ", map { + $_ eq $current ? $_ : + $cgi->a({-href => href(action=>$_)}, $_) + } @ref_views +} + sub format_paging_nav { my ($action, $page, $has_next_link) = @_; my $paging_nav; @@ -5509,7 +5522,7 @@ sub git_blame_data { sub git_tags { my $head = git_get_head_hash($project); git_header_html(); - git_print_page_nav('','', $head,undef,$head); + git_print_page_nav('','', $head,undef,$head,format_ref_views('tags')); git_print_header_div('summary', $project); my @tagslist = git_get_tags_list(); @@ -5522,7 +5535,7 @@ sub git_tags { sub git_heads { my $head = git_get_head_hash($project); git_header_html(); - git_print_page_nav('','', $head,undef,$head); + git_print_page_nav('','', $head,undef,$head,format_ref_views('heads')); git_print_header_div('summary', $project); my @headslist = git_get_heads_list(); @@ -5538,7 +5551,7 @@ sub git_remotes { my $head = git_get_head_hash($project); git_header_html(); - git_print_page_nav('','', $head,undef,$head); + git_print_page_nav('','', $head,undef,$head,format_ref_views('remotes')); git_print_header_div('summary', $project); my @remotelist = git_get_heads_list(undef, 'remotes'); -- cgit v1.3 From c7d94cdbe7ed29a0e26645fa4bb30d87755d86aa Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:13 +0100 Subject: gitweb: allow action specialization in page header An optional -action_extra parameter is given to git_header_html() to identify a variant of the action that is being displayed. For example, this can be used to specify that the remotes view is being used for a specific remote and not to display all remotes. When -action_extra is provided, the action name in the header will be turned into a link to the action without any arguments or parameters, to provide a quick link to the non-specific variant of the action. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7cd50d4b82..c3b89666c5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3526,7 +3526,15 @@ EOF if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); if (defined $action) { - print " / $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"; } -- cgit v1.3 From bb60776063f0575c1a880cd606b7470e36a7cd86 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:14 +0100 Subject: gitweb: remotes view for a single remote When 'remotes' view is passed the 'hash' parameter, interpret it as the name of a remote and limit the view the the heads of that remote. In single-remote view we let the user switch easily to the default remotes view by specifying an -action_extra for the page header and by enabling the 'remotes' link in the reference navigation submenu. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c3b89666c5..bf387572bf 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5558,14 +5558,36 @@ sub git_remotes { or die_error(403, "Remote heads view is disabled"); my $head = git_get_head_hash($project); - git_header_html(); - git_print_page_nav('','', $head,undef,$head,format_ref_views('remotes')); - git_print_header_div('summary', $project); + my $remote = $input_params{'hash'}; + + my @remotelist; + + if (defined $remote) { + # only display the heads in a given remote, stripping the + # remote name which is already visible elsewhere + @remotelist = map { + my $ref = $_ ; + $ref->{'name'} =~ s!^$remote/!!; + $ref + } git_get_heads_list(undef, "remotes/$remote"); + } else { + @remotelist = git_get_heads_list(undef, 'remotes'); + } + + git_header_html(undef, undef, -action_extra => $remote); + git_print_page_nav('', '', $head, undef, $head, + format_ref_views($remote ? '' : 'remotes')); + + if (defined $remote) { + git_print_header_div('remotes', "$remote remote for $project"); + } else { + git_print_header_div('summary', "$project remotes"); + } - my @remotelist = git_get_heads_list(undef, 'remotes'); if (@remotelist) { git_heads_body(\@remotelist, $head); } + git_footer_html(); } -- cgit v1.3 From 0e65699992ddaf68d9d1c30ad8cd1e418cf1bc52 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:15 +0100 Subject: gitweb: refactor repository URL printing Factor out the code to display the repository URL(s) from summary view into a format_rep_url() routine. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index bf387572bf..6e7a66368d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3797,6 +3797,11 @@ sub git_print_header_div { "\n\n"; } +sub format_repo_url { + my ($name, $url) = @_; + return "\n"; +} + sub print_local_time { print format_local_time(@_); } @@ -5180,7 +5185,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 "\n"; + print format_repo_url($url_tag, $git_url); $url_tag = ""; } -- cgit v1.3 From b891d52a644e58a830091463911cb859499ae504 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:16 +0100 Subject: gitweb: provide a routine to display (sub)sections The routine puts the given contento into a DIV element, automatically adding a header div. The content can be provided as a standard scalar value (which is used as-is), as a scalar ref (which is HTML-escaped), as a function reference to be executed, or as a file handle to be dumped. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6e7a66368d..64da0cc730 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3802,6 +3802,44 @@ sub format_repo_url { return "\n"; } +# Group output by placing it in a DIV element and adding a header. +# Options for start_div() can be provided by passing a hash reference as the +# first parameter to the function. +# Options to git_print_header_div() can be provided by passing an array +# reference. This must follow the options to start_div if they are present. +# The content can be a scalar, which is output as-is, a scalar reference, which +# is output after html escaping, an IO handle passed either as *handle or +# *handle{IO}, or a function reference. In the latter case all following +# parameters will be taken as argument to the content function call. +sub git_print_section { + my ($div_args, $header_args, $content); + my $arg = shift; + if (ref($arg) eq 'HASH') { + $div_args = $arg; + $arg = shift; + } + if (ref($arg) eq 'ARRAY') { + $header_args = $arg; + $arg = shift; + } + $content = $arg; + + print $cgi->start_div($div_args); + git_print_header_div(@$header_args); + + if (ref($content) eq 'CODE') { + $content->(@_); + } elsif (ref($content) eq 'SCALAR') { + print esc_html($$content); + } elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') { + print <$content>; + } elsif (!ref($content) && defined($content)) { + print $content; + } + + print $cgi->end_div; +} + sub print_local_time { print format_local_time(@_); } -- cgit v1.3 From 9d0d42f3450fe34737f92b9bde31ac1cf6deeadb Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Nov 2010 13:26:17 +0100 Subject: gitweb: group remote heads by remote In remote and summary view, display a block for each remote, with the fetch and push URL(s) as well as the list of the remote heads. In summary view, if the number of remotes is higher than a prescribed limit, only display the first remotes and their fetch and push urls, without any heads information and without grouping. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 166 +++++++++++++++++++++++++++++++++++++++++------ gitweb/static/gitweb.css | 6 ++ 2 files changed, 152 insertions(+), 20 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 64da0cc730..f4715531f7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2772,6 +2772,44 @@ sub git_get_last_activity { return (undef, undef); } +# Implementation note: when a single remote is wanted, we cannot use 'git +# remote show -n' because that command always work (assuming it's a remote URL +# if it's not defined), and we cannot use 'git remote show' because that would +# try to make a network roundtrip. So the only way to find if that particular +# remote is defined is to walk the list provided by 'git remote -v' and stop if +# and when we find what we want. +sub git_get_remotes_list { + my $wanted = shift; + my %remotes = (); + + open my $fd, '-|' , git_cmd(), 'remote', '-v'; + return unless $fd; + while (my $remote = <$fd>) { + chomp $remote; + $remote =~ s!\t(.*?)\s+\((\w+)\)$!!; + next if $wanted and not $remote eq $wanted; + my ($url, $key) = ($1, $2); + + $remotes{$remote} ||= { 'heads' => () }; + $remotes{$remote}{$key} = $url; + } + close $fd or return; + return wantarray ? %remotes : \%remotes; +} + +# Takes a hash of remotes as first parameter and fills it by adding the +# available remote heads for each of the indicated remotes. +sub fill_remote_heads { + my $remotes = shift; + my @heads = map { "remotes/$_" } keys %$remotes; + my @remoteheads = git_get_heads_list(undef, @heads); + foreach my $remote (keys %$remotes) { + $remotes->{$remote}{'heads'} = [ grep { + $_->{'name'} =~ s!^$remote/!! + } @remoteheads ]; + } +} + sub git_get_references { my $type = shift || ""; my %refs; @@ -5051,6 +5089,101 @@ sub git_heads_body { print "
" . $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " . $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") . + $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, "tree") . "
\n"; } +# Display a single remote block +sub git_remote_block { + my ($remote, $rdata, $limit, $head) = @_; + + my $heads = $rdata->{'heads'}; + my $fetch = $rdata->{'fetch'}; + my $push = $rdata->{'push'}; + + my $urls_table = "\n" ; + + if (defined $fetch) { + if ($fetch eq $push) { + $urls_table .= format_repo_url("URL", $fetch); + } else { + $urls_table .= format_repo_url("Fetch URL", $fetch); + $urls_table .= format_repo_url("Push URL", $push) if defined $push; + } + } elsif (defined $push) { + $urls_table .= format_repo_url("Push URL", $push); + } else { + $urls_table .= format_repo_url("", "No remote URL"); + } + + $urls_table .= "
\n"; + + my $dots; + if (defined $limit && $limit < @$heads) { + $dots = $cgi->a({-href => href(action=>"remotes", hash=>$remote)}, "..."); + } + + print $urls_table; + git_heads_body($heads, $head, 0, $limit, $dots); +} + +# Display a list of remote names with the respective fetch and push URLs +sub git_remotes_list { + my ($remotedata, $limit) = @_; + print "\n"; + my $alternate = 1; + my @remotes = sort keys %$remotedata; + + my $limited = $limit && $limit < @remotes; + + $#remotes = $limit - 1 if $limited; + + while (my $remote = shift @remotes) { + my $rdata = $remotedata->{$remote}; + my $fetch = $rdata->{'fetch'}; + my $push = $rdata->{'push'}; + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print ""; + print ""; + + print "\n"; + } + + if ($limited) { + print "\n" . + "\n" . "\n"; + } + + print "
" . + $cgi->a({-href=> href(action=>'remotes', hash=>$remote), + -class=> "list name"},esc_html($remote)) . + "" . + (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") . + " | " . + (defined $push ? $cgi->a({-href=> $push}, "push") : "push") . + "
" . + $cgi->a({-href => href(action=>"remotes")}, "...") . + "
"; +} + +# Display remote heads grouped by remote, unless there are too many +# remotes, in which case we only display the remote names +sub git_remotes_body { + my ($remotedata, $limit, $head) = @_; + if ($limit and $limit < keys %$remotedata) { + git_remotes_list($remotedata, $limit); + } else { + fill_remote_heads($remotedata); + while (my ($remote, $rdata) = each %$remotedata) { + git_print_section({-class=>"remote", -id=>$remote}, + ["remotes", $remote, $remote], sub { + git_remote_block($remote, $rdata, $limit, $head); + }); + } + } +} + sub git_search_grep_body { my ($commitlist, $from, $to, $extra) = @_; $from = 0 unless defined $from; @@ -5197,7 +5330,7 @@ sub git_summary { # there are more ... my @taglist = git_get_tags_list(16); my @headlist = git_get_heads_list(16); - my @remotelist = $remote_heads ? git_get_heads_list(16, 'remotes') : (); + my %remotedata = $remote_heads ? git_get_remotes_list() : (); my @forklist; my $check_forks = gitweb_check_feature('forks'); @@ -5275,11 +5408,9 @@ sub git_summary { $cgi->a({-href => href(action=>"heads")}, "...")); } - if (@remotelist) { + if (%remotedata) { git_print_header_div('remotes'); - git_heads_body(\@remotelist, $head, 0, 15, - $#remotelist <= 15 ? undef : - $cgi->a({-href => href(action=>"remotes")}, "...")); + git_remotes_body(\%remotedata, 15, $head); } if (@forklist) { @@ -5596,6 +5727,7 @@ sub git_heads { git_footer_html(); } +# used both for single remote view and for list of all the remotes sub git_remotes { gitweb_check_feature('remote_heads') or die_error(403, "Remote heads view is disabled"); @@ -5603,32 +5735,26 @@ sub git_remotes { my $head = git_get_head_hash($project); my $remote = $input_params{'hash'}; - my @remotelist; + my $remotedata = git_get_remotes_list($remote); + die_error(500, "Unable to get remote information") unless defined $remotedata; - if (defined $remote) { - # only display the heads in a given remote, stripping the - # remote name which is already visible elsewhere - @remotelist = map { - my $ref = $_ ; - $ref->{'name'} =~ s!^$remote/!!; - $ref - } git_get_heads_list(undef, "remotes/$remote"); - } else { - @remotelist = git_get_heads_list(undef, 'remotes'); + unless (%$remotedata) { + die_error(404, defined $remote ? + "Remote $remote not found" : + "No remotes found"); } git_header_html(undef, undef, -action_extra => $remote); git_print_page_nav('', '', $head, undef, $head, format_ref_views($remote ? '' : 'remotes')); + fill_remote_heads($remotedata); if (defined $remote) { git_print_header_div('remotes', "$remote remote for $project"); + git_remote_block($remote, $remotedata->{$remote}, undef, $head); } else { git_print_header_div('summary', "$project remotes"); - } - - if (@remotelist) { - git_heads_body(\@remotelist, $head); + git_remotes_body($remotedata, undef, $head); } git_footer_html(); diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css index 4132aabcdb..79d7eebba7 100644 --- a/gitweb/static/gitweb.css +++ b/gitweb/static/gitweb.css @@ -573,6 +573,12 @@ div.binary { font-style: italic; } +div.remote { + margin: .5em; + border: 1px solid #d9d8d1; + display: inline-block; +} + /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */ /* Highlighting theme definition: */ -- cgit v1.3 From da4b2432cc6fe514dd72431de7be3ff2b4c86d7e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 25 Nov 2010 19:43:59 +0100 Subject: gitweb: selectable configurations that change with each request Allow selecting whether configuration file should be (re)parsed on each request (the default, for backward compatibility with configurations that change per session, see commit 7f425db (gitweb: allow configurations that change with each request, 2010-07-30)), or whether should it be parsed only once (for performance speedup for persistent environments, though currently only FastCGI is able to make use of it, when flexibility is not important). You can also have configuration file parsed only once, but have parts of configuration (re)evaluated once per each request. This is done by introducing $per_request_config variable: if set to code reference, this code would be run once per request, while config file would be parsed only once. For example gitolite's contrib/gitweb/gitweb.conf fragment mentioned in 7f425db could be rewritten as our $per_request_config = sub { $ENV{GL_USER} = ($cgi && $cgi->remote_user) || "gitweb"; }; to make use of this feature. If $per_request_config is not a code reference, it is taken to be boolean variable, to choose between running config file for each request (flexibility), and running config file only once (performance in persistent environments). The default value for $per_request_config is 1 (true), which means that old configuration that require to change per session (like gitolite's) will keep working. While at it, make it so evaluate_git_version() is run only once. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/README | 7 +++++++ gitweb/gitweb.perl | 24 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/README b/gitweb/README index bf3664f2b7..6646fdaed0 100644 --- a/gitweb/README +++ b/gitweb/README @@ -246,6 +246,13 @@ not include variables usually directly set during build): 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] + * $per_request_config + If set to code reference, it would be run once per each request. You can + set parts of configuration that change per session, e.g. by setting it to + sub { $ENV{GL_USER} = $cgi->remote_user || "gitweb"; } + Otherwise it is treated as boolean value: if true gitweb would process + config file once per request, if false it would process config file only + once. The default is true. Projects list file format ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 679f2da3ee..d8e4263ba9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -601,6 +601,14 @@ sub filter_snapshot_fmts { !$known_snapshot_formats{$_}{'disabled'}} @fmts; } +# If it is set to code reference, it is code that it is to be run once per +# request, allowing updating configurations that change with each request, +# while running other code in config file only once. +# +# Otherwise, if it is false then gitweb would process config file only once; +# if it is true then gitweb config would be run for each request. +our $per_request_config = 1; + our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM); sub evaluate_gitweb_config { our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; @@ -1070,12 +1078,22 @@ sub reset_timer { our $number_of_git_cmds = 0; } +our $first_request = 1; sub run_request { reset_timer(); evaluate_uri(); - evaluate_gitweb_config(); - evaluate_git_version(); + if ($first_request) { + evaluate_gitweb_config(); + evaluate_git_version(); + } + if ($per_request_config) { + if (ref($per_request_config) eq 'CODE') { + $per_request_config->(); + } elsif (!$first_request) { + evaluate_gitweb_config(); + } + } check_loadavg(); # $projectroot and $projects_list might be set in gitweb config file @@ -1129,6 +1147,7 @@ sub evaluate_argv { sub run { evaluate_argv(); + $first_request = 1; $pre_listen_hook->() if $pre_listen_hook; @@ -1141,6 +1160,7 @@ sub run { $post_dispatch_hook->() if $post_dispatch_hook; + $first_request = 0; last REQUEST if ($is_last_request->()); } -- cgit v1.3 From 67976c65e0e931ad73ec21e5effe3dc204003df4 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 14 Dec 2010 16:54:31 +0100 Subject: gitweb: Fix handling of whitespace in generated links When creating path_info part of link, don't encode space as '+', because while $cgi->param('foo') translates '+' in query param to ' ', neither $ENV{'PATH_INFO'} nor $cgi->path_info() do. This fixes the issue with pathnames with embedded whitespace and $feature{'pathinfo'} / path_info links. It is done by using newly introduced esc_path_info() instead of esc_url() in href() subroutine. Also while links are more clear not escaping space (' ') characters in generated links, the trailing space must be URI-encoded, otherwise would get discarded. Issue noticed thanks to John 'Warthog9' Hawley. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e645d4a821..75cef245a2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1186,7 +1186,7 @@ sub href { $href =~ s,/$,,; # Then add the project name, if present - $href .= "/".esc_url($params{'project'}); + $href .= "/".esc_path_info($params{'project'}); delete $params{'project'}; # since we destructively absorb parameters, we keep this @@ -1196,7 +1196,8 @@ sub href { # Summary just uses the project path URL, any other action is # added to the URL if (defined $params{'action'}) { - $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary'; + $href .= "/".esc_path_info($params{'action'}) + unless $params{'action'} eq 'summary'; delete $params{'action'}; } @@ -1206,13 +1207,13 @@ sub href { || $params{'hash_parent'} || $params{'hash'}); if (defined $params{'hash_base'}) { if (defined $params{'hash_parent_base'}) { - $href .= esc_url($params{'hash_parent_base'}); + $href .= esc_path_info($params{'hash_parent_base'}); # skip the file_parent if it's the same as the file_name if (defined $params{'file_parent'}) { if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) { delete $params{'file_parent'}; } elsif ($params{'file_parent'} !~ /\.\./) { - $href .= ":/".esc_url($params{'file_parent'}); + $href .= ":/".esc_path_info($params{'file_parent'}); delete $params{'file_parent'}; } } @@ -1220,19 +1221,19 @@ sub href { delete $params{'hash_parent'}; delete $params{'hash_parent_base'}; } elsif (defined $params{'hash_parent'}) { - $href .= esc_url($params{'hash_parent'}). ".."; + $href .= esc_path_info($params{'hash_parent'}). ".."; delete $params{'hash_parent'}; } - $href .= esc_url($params{'hash_base'}); + $href .= esc_path_info($params{'hash_base'}); if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) { - $href .= ":/".esc_url($params{'file_name'}); + $href .= ":/".esc_path_info($params{'file_name'}); delete $params{'file_name'}; } delete $params{'hash'}; delete $params{'hash_base'}; } elsif (defined $params{'hash'}) { - $href .= esc_url($params{'hash'}); + $href .= esc_path_info($params{'hash'}); delete $params{'hash'}; } @@ -1265,6 +1266,9 @@ sub href { } $href .= "?" . join(';', @result) if scalar @result; + # final transformation: trailing spaces must be escaped (URI-encoded) + $href =~ s/(\s+)$/CGI::escape($1)/e; + return $href; } @@ -1347,6 +1351,17 @@ sub esc_param { return $str; } +# the quoting rules for path_info fragment are slightly different +sub esc_path_info { + my $str = shift; + return undef unless defined $str; + + # path_info doesn't treat '+' as space (specially), but '?' must be escaped + $str =~ s/([^A-Za-z0-9\-_.~();\/;:@&= +]+)/CGI::escape($1)/eg; + + return $str; +} + # quote unsafe chars in whole URL, so some characters cannot be quoted sub esc_url { my $str = shift; -- cgit v1.3 From 3017ed62f47ce14a959e2d315c434d4980cf4243 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 15 Dec 2010 00:34:01 +0100 Subject: gitweb: Introduce esc_attr to escape attributes of HTML elements It is needed only to escape attributes of handcrafted HTML elements, and not those generated using CGI.pm subroutines / methods for HTML generation. While at it, add esc_url and esc_html where needed, and prefer to use CGI.pm HTML generating methods than handcrafted HTML code. Most of those are probably unnecessary (could be exploited only by person with write access to gitweb config, or at least access to the repository). This fixes CVE-2010-3906 Reported-by: Emanuele Gentili Helped-by: John 'Warthog9' Hawley Helped-by: Jonathan Nieder Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2cb832753a..c3a04b1223 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1084,6 +1084,13 @@ sub esc_url { return $str; } +# quote unsafe characters in HTML attributes +sub esc_attr { + + # for XHTML conformance escaping '"' to '"' is not enough + return esc_html(@_); +} + # replace invalid utf8 character with SUBSTITUTION sequence sub esc_html { my $str = shift; @@ -1489,7 +1496,7 @@ sub format_ref_marker { hash=>$dest )}, $name); - $markers .= " " . + $markers .= " " . $link . ""; } } @@ -1573,7 +1580,7 @@ sub git_get_avatar { return $pre_white . "" . $post_white; } else { @@ -2245,7 +2252,7 @@ sub git_show_project_tagcloud { } else { my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud; return '

' . join (', ', map { - "$cloud->{$_}->{topname}" + $cgi->a({-href=>"$home_link?by_tag=$_"}, $cloud->{$_}->{topname}) } splice(@tags, 0, $count)) . '

'; } } @@ -3061,11 +3068,11 @@ EOF # print out each stylesheet that exist, providing backwards capability # for those people who defined $stylesheet in a config file if (defined $stylesheet) { - print ''."\n"; + print ''."\n"; } else { foreach my $stylesheet (@stylesheets) { next unless $stylesheet; - print ''."\n"; + print ''."\n"; } } if (defined $project) { @@ -3078,7 +3085,7 @@ EOF my $type = lc($format); my %link_attr = ( '-rel' => 'alternate', - '-title' => "$project - $href_params{'-title'} - $format feed", + '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"), '-type' => "application/$type+xml" ); @@ -3105,13 +3112,13 @@ EOF } else { printf(''."\n", - $site_name, href(project=>undef, action=>"project_index")); + esc_attr($site_name), href(project=>undef, action=>"project_index")); printf(''."\n", - $site_name, href(project=>undef, action=>"opml")); + esc_attr($site_name), href(project=>undef, action=>"opml")); } if (defined $favicon) { - print qq(\n); + print qq(\n); } print "\n" . @@ -3124,7 +3131,7 @@ EOF print "
\n" . $cgi->a({-href => esc_url($logo_url), -title => $logo_label}, - qq()); + qq()); print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); @@ -5016,14 +5023,14 @@ sub git_blob { } else { print "
\n" . "

\n" . - "
$hash
\n"; + "
".esc_html($hash)."
\n"; } git_print_page_path($file_name, "blob", $hash_base); print "
\n"; if ($mimetype =~ m!^image/!) { - print qq!$file_name$hash, @@ -5094,7 +5101,7 @@ sub git_tree { undef $hash_base; print "
\n"; print "

\n"; - print "
$hash
\n"; + print "
".esc_html($hash)."
\n"; } if (defined $file_name) { $basedir = $file_name; @@ -5511,7 +5518,7 @@ sub git_blobdiff { git_print_header_div('commit', esc_html($co{'title'}), $hash_base); } else { print "

$formats_nav
\n"; - print "
$hash vs $hash_parent
\n"; + print "
".esc_html("$hash vs $hash_parent")."
\n"; } if (defined $file_name) { git_print_page_path($file_name, "blob", $hash_base); -- cgit v1.3 From 05bb5a2584ca0e1c87a2135a83c5573b9337d06f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 18 Dec 2010 21:02:13 +0100 Subject: gitweb: Include links to feeds in HTML header only for '200 OK' response To do that, generating ""s to feeds were refactored into print_feed_meta() subroutine, to keep nesting (indent) level in git_header_html() low. This has also the advantage of making code more clear. Signed-off-by: Jakub Narebski Signed-off-by: John 'Warthog9' Hawley Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 89 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 42 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 53253c622d..42cc1da7ff 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3394,6 +3394,51 @@ sub get_page_title { return $title; } +sub print_feed_meta { + if (defined $project) { + my %href_params = get_feed_info(); + if (!exists $href_params{'-title'}) { + $href_params{'-title'} = 'log'; + } + + foreach my $format qw(RSS Atom) { + my $type = lc($format); + my %link_attr = ( + '-rel' => 'alternate', + '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"), + '-type' => "application/$type+xml" + ); + + $href_params{'action'} = $type; + $link_attr{'-href'} = href(%href_params); + print "\n"; + + $href_params{'extra_options'} = '--no-merges'; + $link_attr{'-href'} = href(%href_params); + $link_attr{'-title'} .= ' (no merges)'; + print "\n"; + } + + } else { + printf(''."\n", + esc_attr($site_name), href(project=>undef, action=>"project_index")); + printf(''."\n", + esc_attr($site_name), href(project=>undef, action=>"opml")); + } +} + sub git_header_html { my $status = shift || "200 OK"; my $expires = shift; @@ -3443,48 +3488,8 @@ EOF print ''."\n"; } } - if (defined $project) { - my %href_params = get_feed_info(); - if (!exists $href_params{'-title'}) { - $href_params{'-title'} = 'log'; - } - - foreach my $format qw(RSS Atom) { - my $type = lc($format); - my %link_attr = ( - '-rel' => 'alternate', - '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"), - '-type' => "application/$type+xml" - ); - - $href_params{'action'} = $type; - $link_attr{'-href'} = href(%href_params); - print "\n"; - - $href_params{'extra_options'} = '--no-merges'; - $link_attr{'-href'} = href(%href_params); - $link_attr{'-title'} .= ' (no merges)'; - print "\n"; - } - - } else { - printf(''."\n", - esc_attr($site_name), href(project=>undef, action=>"project_index")); - printf(''."\n", - esc_attr($site_name), href(project=>undef, action=>"opml")); - } + print_feed_meta() + if ($status eq '200 OK'); if (defined $favicon) { print qq(\n); } -- cgit v1.3 From 9d9f5e72dc5959750498acc81b3e2c8ca76ad499 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 3 Sep 2010 19:44:39 -0500 Subject: gitweb: skip logo in atom feed when there is none With v1.5.0-rc0~169 (gitweb: Fix Atom feed : it is $logo, not $logo_url, 2006-12-04), the logo URI to be written to Atom feeds was corrected but the case of no logo forgotten. Acked-by: Eric Wong Signed-off-by: Jonathan Nieder Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 42cc1da7ff..59b2e08b98 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -6870,7 +6870,7 @@ XML if (defined $favicon) { print "" . esc_url($favicon) . "\n"; } - if (defined $logo_url) { + if (defined $logo) { # not twice as wide as tall: 72 x 27 pixels print "" . esc_url($logo) . "\n"; } -- cgit v1.3 From 6822052427353525fe7158398bc6cf4bbd5d1351 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 3 Sep 2010 19:45:09 -0500 Subject: gitweb: make logo optional Some sites may not want to have a logo at all. While at it, use $cgi->img to simplify this code. (CGI.pm learned most HTML4 tags by version 2.79, so this should be portable to perl 5.8, though I haven't tested.) Signed-off-by: Jonathan Nieder Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 59b2e08b98..10cf97e66f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3501,10 +3501,15 @@ EOF insert_file($site_header); } - print "
\n" . - $cgi->a({-href => esc_url($logo_url), - -title => $logo_label}, - qq()); + print "
\n"; + if (defined $logo) { + print $cgi->a({-href => esc_url($logo_url), + -title => $logo_label}, + $cgi->img({-src => esc_url($logo), + -width => 72, -height => 27, + -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)); -- cgit v1.3 From 3ce19eb857c46727239eda3b3dd265d725ea8ddd Mon Sep 17 00:00:00 2001 From: Sylvain Rabot Date: Thu, 30 Dec 2010 22:20:28 +0100 Subject: gitweb: add extensions to highlight feature map added: sql, php5, phps, bash, zsh, ksh, mk, make Signed-off-by: Sylvain Rabot Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c65af1a00a..84f2c1df2c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -250,13 +250,14 @@ our %highlight_ext = ( # main extensions, defining name of syntax; # see files in /usr/share/highlight/langDefs/ directory map { $_ => $_ } - qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), + qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make), # alternate extensions, see /etc/highlight/filetypes.conf 'h' => 'c', + map { $_ => 'sh' } qw(bash zsh ksh), map { $_ => 'cpp' } qw(cxx c++ cc), - map { $_ => 'php' } qw(php3 php4), + map { $_ => 'php' } qw(php3 php4 php5 phps), map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' - 'mak' => 'make', + map { $_ => 'make'} qw(mak mk), map { $_ => 'xml' } qw(xhtml html htm), ); -- cgit v1.3 From 3ca7353cab4ed6c7efac0c8d7477c87112fc7350 Mon Sep 17 00:00:00 2001 From: Sylvain Rabot Date: Thu, 30 Dec 2010 22:20:29 +0100 Subject: gitweb: remove unnecessary test when closing file descriptor It happens that closing file descriptor fails whereas the blob is perfectly readable. According to perlman the reasons could be: If the file handle came from a piped open, "close" will additionally return false if one of the other system calls involved fails, or if the program exits with non-zero status. (If the only problem was that the program exited non-zero, $! will be set to 0.) Closing a pipe also waits for the process executing on the pipe to complete, in case you want to look at the output of the pipe afterwards, and implicitly puts the exit status value of that command into $?. Prematurely closing the read end of a pipe (i.e. before the process writ- ing to it at the other end has closed it) will result in a SIGPIPE being delivered to the writer. If the other end can't handle that, be sure to read all the data before closing the pipe. In this case we don't mind that close fails. Signed-off-by: Sylvain Rabot Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 84f2c1df2c..4e3bf4a06b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3465,8 +3465,7 @@ sub run_highlighter { my ($fd, $highlight, $syntax) = @_; return $fd unless ($highlight && defined $syntax); - close $fd - or die_error(404, "Reading blob failed"); + close $fd; open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". quote_command($highlight_bin). " --xhtml --fragment --syntax $syntax |" -- cgit v1.3 From d2d434beeb03e4ee648ca7ca2a1ea1ed09077306 Mon Sep 17 00:00:00 2001 From: Adam Tkac Date: Thu, 27 Jan 2011 13:51:51 +0100 Subject: Don't pass "--xhtml" to hightlight in gitweb.perl script. The "--xhtml" option is supported only in highlight < 3.0. There is no option to enforce (X)HTML output format compatible with both highlight < 3.0 and highlight >= 3.0. However default output format is HTML so we don't need to explicitly specify it. Signed-off-by: Adam Tkac Helped-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1025c2f716..0779f12d61 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3468,7 +3468,7 @@ sub run_highlighter { close $fd; open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". quote_command($highlight_bin). - " --xhtml --fragment --syntax $syntax |" + " --fragment --syntax $syntax |" or die_error(500, "Couldn't open file or run syntax highlighter"); return $fd; } -- cgit v1.3 From 0f54b7d09ac5b87c327a1e8da43bc48be98f2758 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Sat, 19 Feb 2011 15:27:41 +0000 Subject: gitweb/gitweb.perl: remove use of qw(...) as parentheses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the qw(...) construct as implicit parentheses was deprecated in perl 5.13.5. Change the relevant code in gitweb to not use the deprecated construct. The offending code was introduced in 3562198b by Jakub Narebski. The issue is that perl will now warn about this: $ perl -wE 'for my $i qw(a b) { say $i }' Use of qw(...) as parentheses is deprecated at -e line 1. a b This caused gitweb.perl to warn on perl 5.13.5 and above, and these tests to fail on those perl versions: ./t9501-gitweb-standalone-http-status.sh (Wstat: 256 Tests: 11 Failed: 10) Failed tests: 2-11 Non-zero exit status: 1 ./t9502-gitweb-standalone-parse-output.sh (Wstat: 256 Tests: 10 Failed: 9) Failed tests: 2-10 Non-zero exit status: 1 Signed-off-by: Ævar Arnfjörð Bjarmason Acked-by: Jakub Narębski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0779f12d61..b02372c3e2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3501,7 +3501,7 @@ sub print_feed_meta { $href_params{'-title'} = 'log'; } - foreach my $format qw(RSS Atom) { + foreach my $format (qw(RSS Atom)) { my $type = lc($format); my %link_attr = ( '-rel' => 'alternate', @@ -3682,7 +3682,7 @@ sub git_footer_html { } $href_params{'-title'} ||= 'log'; - foreach my $format qw(RSS Atom) { + foreach my $format (qw(RSS Atom)) { $href_params{'action'} = lc($format); print $cgi->a({-href => href(%href_params), -title => "$href_params{'-title'} $format feed", -- cgit v1.3 From 98885c2914b22d70d09302528d86f87e795f7485 Mon Sep 17 00:00:00 2001 From: Ævar Arnfjörð Bjarmason Date: Sat, 19 Feb 2011 15:27:42 +0000 Subject: gitweb/gitweb.perl: don't call S_ISREG() with undef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change S_ISREG($to_mode_oct) to S_ISREG($from_mode_oct) in the branch that handles from modes, not to modes. This logic appears to have been caused by copy/paste programming by Jakub Narebski in e8e41a93. It would be better to rewrite this code not to be duplicated, but I haven't done so. This issue caused a failing test on perl 5.13.9, which has a warning that turned this up: gitweb.perl: Use of uninitialized value in subroutine entry at /home/avar/g/git/t/../gitweb/gitweb.perl line 4415. Which caused the Git test suite to fail on this test: ./t9500-gitweb-standalone-no-errors.sh (Wstat: 256 Tests: 90 Failed: 84) Failed tests: 1-8, 10-36, 38-45, 47-48, 50-88 Non-zero exit status: 1 Reported-by: perl 5.13.9 Signed-off-by: Ævar Arnfjörð Bjarmason Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b02372c3e2..1b9369d1a7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4412,7 +4412,7 @@ sub git_difftree_body { } if ($diff->{'from_mode'} ne ('0' x 6)) { $from_mode_oct = oct $diff->{'from_mode'}; - if (S_ISREG($to_mode_oct)) { # only for regular file + if (S_ISREG($from_mode_oct)) { # only for regular file $from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits } $from_file_type = file_type($diff->{'from_mode'}); -- cgit v1.3 From 6affdbe677c4b2b8547bd9aa471464dd31a94a1c Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 16 Mar 2011 15:34:13 -0700 Subject: gitweb: highlight: replace tabs with spaces Consider the following code fragment: /* * test */ vim ":set list" mode shows that the first character on each line is a tab: ^I/*$ ^I * test$ ^I */$ By default, the "highlight" program will retain the tabs in the HTML output: $ highlight --fragment --syntax c test.c /* * test */ vim list mode: ^I/*$ ^I * test$ ^I */$ In gitweb, this winds up looking something like: 1 /* 2 * test 3 */ I tried both Firefox and Opera and saw the same behavior. The desired output is: 1 /* 2 * test 3 */ This can be accomplished by specifying "--replace-tabs=8" on the highlight command line. Signed-off-by: Kevin Cernekee Acked-by: John 'Warthog9' Hawley Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1b9369d1a7..b04ab8c9bb 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3468,7 +3468,7 @@ sub run_highlighter { close $fd; open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". quote_command($highlight_bin). - " --fragment --syntax $syntax |" + " --replace-tabs=8 --fragment --syntax $syntax |" or die_error(500, "Couldn't open file or run syntax highlighter"); return $fd; } -- cgit v1.3 From 5e96a847b391eda693b60347bb190dd45fb1721c Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Fri, 18 Mar 2011 17:00:16 +0100 Subject: gitweb: fix #patchNN anchors when path_info is enabled When $feature{'pathinfo'} is used, gitweb script sets the base URL to itself, so that relative links to static files work correctly. It does it by adding something like below to HTML head: This breaks the "patch" anchor links seen on the commitdiff pages, because these links, being relative (), are resolved (computed) relative to the base URL and not relative to current URL, i.e. as: http://HOST/gitweb.cgi#patch1 Instead, they should look like this: http://HOST/gitweb.cgi/myproject.git/commitdiff/35a9811ef9d68eae9afd76bede121da4f89b448c#patch1 Add an "-anchor" parameter to href(), and use href(-anchor=>"patch1") to generate "patch" anchor links, so that the full path is included in the patch link. While at it, convert print "foo"; print "bar"; to print "foo" . "bar"; in the neighborhood of changes. Signed-off-by: Kevin Cernekee Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b04ab8c9bb..f275adbcf1 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1199,11 +1199,15 @@ if (defined caller) { # -full => 0|1 - use absolute/full URL ($my_uri/$my_url as base) # -replay => 1 - start from a current view (replay with modifications) # -path_info => 0|1 - don't use/use path_info URL (if possible) +# -anchor => ANCHOR - add #ANCHOR to end of URL, implies -replay if used alone sub href { my %params = @_; # default is to use -absolute url() i.e. $my_uri my $href = $params{-full} ? $my_url : $my_uri; + # implicit -replay, must be first of implicit params + $params{-replay} = 1 if (keys %params == 1 && $params{-anchor}); + $params{'project'} = $project unless exists $params{'project'}; if ($params{-replay}) { @@ -1314,6 +1318,10 @@ sub href { # final transformation: trailing spaces must be escaped (URI-encoded) $href =~ s/(\s+)$/CGI::escape($1)/e; + if ($params{-anchor}) { + $href .= "#".esc_param($params{-anchor}); + } + return $href; } @@ -4335,7 +4343,8 @@ sub git_difftree_body { # link to patch $patchno++; print "" . - $cgi->a({-href => "#patch$patchno"}, "patch") . + $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . " | " . "\n"; } @@ -4432,8 +4441,9 @@ sub git_difftree_body { if ($action eq 'commitdiff') { # link to patch $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch"); - print " | "; + print $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . + " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'}, hash_base=>$hash, file_name=>$diff->{'file'})}, @@ -4452,8 +4462,9 @@ sub git_difftree_body { if ($action eq 'commitdiff') { # link to patch $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch"); - print " | "; + print $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . + " | "; } print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'}, hash_base=>$parent, file_name=>$diff->{'file'})}, @@ -4494,7 +4505,8 @@ sub git_difftree_body { if ($action eq 'commitdiff') { # link to patch $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch") . + print $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . " | "; } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) { # "commit" view and modified file (not onlu mode changed) @@ -4539,7 +4551,8 @@ sub git_difftree_body { if ($action eq 'commitdiff') { # link to patch $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch") . + print $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . " | "; } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) { # "commit" view and modified file (not only pure rename or copy) -- cgit v1.3 From 6368d9f1af7a5d4c8af2f11ffdb6af577f1e98f5 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 19 Mar 2011 23:53:55 +0100 Subject: gitweb: Always call parse_date with timezone parameter Timezone is required to correctly set local time, which would be needed for future 'localtime' feature. While at it, remove unnecessary call to the function from git_log_body, as its return value is not used anywhere. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b04ab8c9bb..9dccfb01d6 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4906,7 +4906,6 @@ sub git_log_body { next if !%co; my $commit = $co{'id'}; my $ref = format_ref_marker($refs, $commit); - my %ad = parse_date($co{'author_epoch'}); git_print_header_div('commit', "$co{'age_string'}" . esc_html($co{'title'}) . $ref, @@ -7064,7 +7063,7 @@ sub git_feed { if (defined($commitlist[0])) { %latest_commit = %{$commitlist[0]}; my $latest_epoch = $latest_commit{'committer_epoch'}; - %latest_date = parse_date($latest_epoch); + %latest_date = parse_date($latest_epoch, $latest_commit{'comitter_tz'}); my $if_modified = $cgi->http('IF_MODIFIED_SINCE'); if (defined $if_modified) { my $since; @@ -7195,7 +7194,7 @@ XML if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) { last; } - my %cd = parse_date($co{'author_epoch'}); + my %cd = parse_date($co{'author_epoch'}, $co{'author_tz'}); # get list of changed files open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, -- cgit v1.3 From 2b1e17237b0b14855ad8337ec001d6d1fd7cca61 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 25 Mar 2011 20:20:49 +0100 Subject: gitweb: Fix handling of fractional timezones in parse_date Fractional timezones, like -0330 (NST used in Canada) or +0430 (Afghanistan, Iran DST), were not handled properly in parse_date; this means values such as 'minute_local' and 'iso-tz' were not generated correctly. This was caused by two mistakes: * sign of timezone was applied only to hour part of offset, and not as it should be also to minutes part (this affected only negative fractional timezones). * 'int $h + $m/60' is 'int($h + $m/60)' and not 'int($h) + $m/60', so fractional part was discarded altogether ($h is hours, $m is minutes, which is always less than 60). Note that positive fractional timezones +0430, +0530 and +1030 can be found as authortime in git.git repository itself. For example http://repo.or.cz/w/git.git/commit/88d50e7 had authortime of "Fri, 8 Jan 2010 18:48:07 +0000 (23:48 +0530)", which is not marked with 'atnight', when "git show 88d50e7" gives correct author date of "Sat Jan 9 00:18:07 2010 +0530". Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 9dccfb01d6..46186ab909 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2913,8 +2913,10 @@ sub parse_date { $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", 1900+$year, 1+$mon, $mday, $hour ,$min, $sec; - $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; - my $local = $epoch + ((int $1 + ($2/60)) * 3600); + my ($tz_sign, $tz_hour, $tz_min) = + ($tz =~ m/^([-+])(\d\d)(\d\d)$/); + $tz_sign = ($tz_sign eq '-' ? -1 : +1); + my $local = $epoch + $tz_sign*((($tz_hour*60) + $tz_min)*60); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); $date{'hour_local'} = $hour; $date{'minute_local'} = $min; -- cgit v1.3 From 0c8c385ef188bf76b6edcda87a7cc9bc05596fa3 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Tue, 26 Apr 2011 11:32:00 +1000 Subject: gitweb: supply '-n' to gzip for identical output For projects that do not release official archives, gitweb's snapshot feature would be an excellent alternative, and but without the '-n' ('--no-name') argument, gzip includes a timestamp in output which results in different files. Because some systems hash/checksum downloaded files to ensure integrity of the tarball (e.g FreeBSD), it is desirable to produce tarballs in a reproducible way for that purpose. Whilst '--no-name' is more descriptive, the long version of the flag is not supported on all systems. In particular, OpenBSD does not appear to support it. Supply '-n' to gzip to exclude timestamp from output and produce idential output every time. Signed-off-by: Fraser Tweedale Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ee69ea683a..f8db40a1c2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -186,7 +186,7 @@ our %known_snapshot_formats = ( 'type' => 'application/x-gzip', 'suffix' => '.tar.gz', 'format' => 'tar', - 'compressor' => ['gzip']}, + 'compressor' => ['gzip', '-n']}, 'tbz2' => { 'display' => 'tar.bz2', -- cgit v1.3 From 12b1443c2cf41174311c81667022c6f3149aa542 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 29 Apr 2011 19:51:56 +0200 Subject: gitweb: Restructure projects list generation Extract filtering out forks (which is done if 'forks' feature is enabled) into filter_forks_from_projects_list subroutine, and searching projects (via projects search form, or via content tags) into search_projects_list subroutine. Both are now run _before_ displaying projects, and not while printing; this allow to know upfront if there were any found projects. Gitweb now can and do print 'No such projects found' if user searches for phrase which does not correspond to any project (any repository). This also would allow splitting projects list into pages, if we so desire. Filtering out forks and marking repository (project) as having forks is now consolidated into one subroutine (special case of handling forks in git_get_projects_list only for $projects_list being file is now removed). Forks handling is also cleaned up and simplified. $pr->{'forks'} now contains un-filled list of forks; we can now also detect situation where the way for having forks is prepared, but there are no forks yet. Sorting projects got also refactored in a very straight way (just moving code) into sort_projects_list subroutine. The interaction between forks, content tags and searching is now made more explicit: searching whether by tag, or via search form turns off fork filtering (gitweb searches also forks, and will show all results). If 'ctags' feature is disabled, then searching by tag is too. The t9500 test now includes some basic test for 'forks' and 'ctags' features; the t9502 includes test checking if gitweb correctly filters out forks. Generating list of projects by scanning given directory is now also a bit simplified wrt. handling filtering; it is byproduct of extracting filtering forks to separate subroutine. While at it we now detect that there are no projects and respond with "404 No projects found" also for 'project_index' and 'opml' actions. Helped-by: Jonathan Nieder Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 250 +++++++++++++++++++++--------- t/t9500-gitweb-standalone-no-errors.sh | 49 ++++++ t/t9502-gitweb-standalone-parse-output.sh | 74 +++++++++ 3 files changed, 296 insertions(+), 77 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ee69ea683a..014b33b50a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2651,21 +2651,23 @@ sub git_get_project_url_list { } sub git_get_projects_list { - my ($filter) = @_; + my $filter = shift || ''; my @list; - $filter ||= ''; $filter =~ s/\.git$//; - my $check_forks = gitweb_check_feature('forks'); - if (-d $projects_list) { # search in directory - my $dir = $projects_list . ($filter ? "/$filter" : ''); + my $dir = $projects_list; # remove the trailing "/" $dir =~ s!/+$!!; - my $pfxlen = length("$dir"); - my $pfxdepth = ($dir =~ tr!/!!); + my $pfxlen = length("$projects_list"); + my $pfxdepth = ($projects_list =~ tr!/!!); + # when filtering, search only given subdirectory + if ($filter) { + $dir .= "/$filter"; + $dir =~ s!/+$!!; + } File::Find::find({ follow_fast => 1, # follow symbolic links @@ -2680,14 +2682,14 @@ sub git_get_projects_list { # only directories can be git repositories return unless (-d $_); # don't traverse too deep (Find is super slow on os x) + # $project_maxdepth excludes depth of $projectroot if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) { $File::Find::prune = 1; return; } - my $subdir = substr($File::Find::name, $pfxlen + 1); + my $path = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - my $path = ($filter ? "$filter/" : '') . $subdir; if (check_export_ok("$projectroot/$path")) { push @list, { path => $path }; $File::Find::prune = 1; @@ -2700,7 +2702,6 @@ sub git_get_projects_list { # 'git%2Fgit.git Linus+Torvalds' # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' - my %paths; open my $fd, '<', $projects_list or return; PROJECT: while (my $line = <$fd>) { @@ -2711,32 +2712,9 @@ sub git_get_projects_list { if (!defined $path) { next; } - if ($filter ne '') { - # looking for forks; - my $pfx = substr($path, 0, length($filter)); - if ($pfx ne $filter) { - next PROJECT; - } - my $sfx = substr($path, length($filter)); - if ($sfx !~ /^\/.*\.git$/) { - next PROJECT; - } - } elsif ($check_forks) { - PATH: - foreach my $filter (keys %paths) { - # looking for forks; - my $pfx = substr($path, 0, length($filter)); - if ($pfx ne $filter) { - next PATH; - } - my $sfx = substr($path, length($filter)); - if ($sfx !~ /^\/.*\.git$/) { - next PATH; - } - # is a fork, don't include it in - # the list - next PROJECT; - } + # if $filter is rpovided, check if $path begins with $filter + if ($filter && $path !~ m!^\Q$filter\E/!) { + next; } if (check_export_ok("$projectroot/$path")) { my $pr = { @@ -2744,8 +2722,6 @@ sub git_get_projects_list { owner => to_utf8($owner), }; push @list, $pr; - (my $forks_path = $path) =~ s/\.git$//; - $paths{$forks_path}++; } } close $fd; @@ -2753,6 +2729,98 @@ sub git_get_projects_list { return @list; } +# written with help of Tree::Trie module (Perl Artistic License, GPL compatibile) +# as side effects it sets 'forks' field to list of forks for forked projects +sub filter_forks_from_projects_list { + my $projects = shift; + + my %trie; # prefix tree of directories (path components) + # generate trie out of those directories that might contain forks + foreach my $pr (@$projects) { + my $path = $pr->{'path'}; + $path =~ s/\.git$//; # forks of 'repo.git' are in 'repo/' directory + next if ($path =~ m!/$!); # skip non-bare repositories, e.g. 'repo/.git' + next unless ($path); # skip '.git' repository: tests, git-instaweb + next unless (-d $path); # containing directory exists + $pr->{'forks'} = []; # there can be 0 or more forks of project + + # add to trie + my @dirs = split('/', $path); + # walk the trie, until either runs out of components or out of trie + my $ref = \%trie; + while (scalar @dirs && + exists($ref->{$dirs[0]})) { + $ref = $ref->{shift @dirs}; + } + # create rest of trie structure from rest of components + foreach my $dir (@dirs) { + $ref = $ref->{$dir} = {}; + } + # create end marker, store $pr as a data + $ref->{''} = $pr if (!exists $ref->{''}); + } + + # filter out forks, by finding shortest prefix match for paths + my @filtered; + PROJECT: + foreach my $pr (@$projects) { + # trie lookup + my $ref = \%trie; + DIR: + foreach my $dir (split('/', $pr->{'path'})) { + if (exists $ref->{''}) { + # found [shortest] prefix, is a fork - skip it + push @{$ref->{''}{'forks'}}, $pr; + next PROJECT; + } + if (!exists $ref->{$dir}) { + # not in trie, cannot have prefix, not a fork + push @filtered, $pr; + next PROJECT; + } + # If the dir is there, we just walk one step down the trie. + $ref = $ref->{$dir}; + } + # we ran out of trie + # (shouldn't happen: it's either no match, or end marker) + push @filtered, $pr; + } + + return @filtered; +} + +# note: fill_project_list_info must be run first, +# for 'descr_long' and 'ctags' to be filled +sub search_projects_list { + my ($projlist, %opts) = @_; + my $tagfilter = $opts{'tagfilter'}; + my $searchtext = $opts{'searchtext'}; + + return @$projlist + unless ($tagfilter || $searchtext); + + my @projects; + PROJECT: + foreach my $pr (@$projlist) { + + if ($tagfilter) { + next unless ref($pr->{'ctags'}) eq 'HASH'; + next unless + grep { lc($_) eq lc($tagfilter) } keys %{$pr->{'ctags'}}; + } + + if ($searchtext) { + next unless + $pr->{'path'} =~ /$searchtext/ || + $pr->{'descr_long'} =~ /$searchtext/; + } + + push @projects, $pr; + } + + return @projects; +} + our $gitweb_project_owner = undef; sub git_get_project_list_from_file { @@ -4742,7 +4810,7 @@ sub git_patchset_body { # project in the list, removing invalid projects from returned list # NOTE: modifies $projlist, but does not remove entries from it sub fill_project_list_info { - my ($projlist, $check_forks) = @_; + my $projlist = shift; my @projects; my $show_ctags = gitweb_check_feature('ctags'); @@ -4762,23 +4830,36 @@ sub fill_project_list_info { if (!defined $pr->{'owner'}) { $pr->{'owner'} = git_get_project_owner("$pr->{'path'}") || ""; } - if ($check_forks) { - my $pname = $pr->{'path'}; - if (($pname =~ s/\.git$//) && - ($pname !~ /\/$/) && - (-d "$projectroot/$pname")) { - $pr->{'forks'} = "-d $projectroot/$pname"; - } else { - $pr->{'forks'} = 0; - } + if ($show_ctags) { + $pr->{'ctags'} = git_get_project_ctags($pr->{'path'}); } - $show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'}); push @projects, $pr; } return @projects; } +sub sort_projects_list { + my ($projlist, $order) = @_; + my @projects; + + my %order_info = ( + project => { key => 'path', type => 'str' }, + descr => { key => 'descr_long', type => 'str' }, + owner => { key => 'owner', type => 'str' }, + age => { key => 'age', type => 'num' } + ); + my $oi = $order_info{$order}; + return @$projlist unless defined $oi; + if ($oi->{'type'} eq 'str') { + @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist; + } else { + @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist; + } + + return @projects; +} + # print 'sort by' element, generating 'sort by $name' replay link # if that order is not selected sub print_sort_th { @@ -4805,28 +4886,39 @@ sub format_sort_th { sub git_project_list_body { # actually uses global variable $project my ($projlist, $order, $from, $to, $extra, $no_header) = @_; + my @projects = @$projlist; my $check_forks = gitweb_check_feature('forks'); - my @projects = fill_project_list_info($projlist, $check_forks); + my $show_ctags = gitweb_check_feature('ctags'); + my $tagfilter = $show_ctags ? $cgi->param('by_tag') : undef; + $check_forks = undef + if ($tagfilter || $searchtext); + + # filtering out forks before filling info allows to do less work + @projects = filter_forks_from_projects_list(\@projects) + if ($check_forks); + @projects = fill_project_list_info(\@projects); + # searching projects require filling to be run before it + @projects = search_projects_list(\@projects, + 'searchtext' => $searchtext, + 'tagfilter' => $tagfilter) + if ($tagfilter || $searchtext); $order ||= $default_projects_order; $from = 0 unless defined $from; $to = $#projects if (!defined $to || $#projects < $to); - my %order_info = ( - project => { key => 'path', type => 'str' }, - descr => { key => 'descr_long', type => 'str' }, - owner => { key => 'owner', type => 'str' }, - age => { key => 'age', type => 'num' } - ); - my $oi = $order_info{$order}; - if ($oi->{'type'} eq 'str') { - @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects; - } else { - @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects; + # short circuit + if ($from > $to) { + print "
\n". + "No such projects found
\n". + "Click ".$cgi->a({-href=>href(project=>undef)},"here")." to view all projects
\n". + "
\n
\n"; + return; } - my $show_ctags = gitweb_check_feature('ctags'); + @projects = sort_projects_list(\@projects, $order); + if ($show_ctags) { my %ctags; foreach my $p (@projects) { @@ -4852,32 +4944,26 @@ sub git_project_list_body { "\n"; } my $alternate = 1; - my $tagfilter = $cgi->param('by_tag'); for (my $i = $from; $i <= $to; $i++) { my $pr = $projects[$i]; - next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}}; - next if $searchtext and not $pr->{'path'} =~ /$searchtext/ - and not $pr->{'descr_long'} =~ /$searchtext/; - # Weed out forks or non-matching entries of search - if ($check_forks) { - my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#; - $forkbase="^$forkbase" if $forkbase; - next if not $searchtext and not $tagfilter and $show_ctags - and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe - } - if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; + if ($check_forks) { print ""; if ($pr->{'forks'}) { - print "\n"; - print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+"); + my $nforks = scalar @{$pr->{'forks'}}; + if ($nforks > 0) { + print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"), + -title => "$nforks forks"}, "+"); + } else { + print $cgi->span({-title => "$nforks forks"}, "+"); + } } print "\n"; } @@ -5357,7 +5443,10 @@ sub git_forks { } sub git_project_index { - my @projects = git_get_projects_list($project); + my @projects = git_get_projects_list(); + if (!@projects) { + die_error(404, "No projects found"); + } print $cgi->header( -type => 'text/plain', @@ -5399,7 +5488,11 @@ sub git_summary { my $check_forks = gitweb_check_feature('forks'); if ($check_forks) { + # find forks of a project @forklist = git_get_projects_list($project); + # filter out forks of forks + @forklist = filter_forks_from_projects_list(\@forklist) + if (@forklist); } git_header_html(); @@ -7319,6 +7412,9 @@ sub git_atom { sub git_opml { my @list = git_get_projects_list(); + if (!@list) { + die_error(404, "No projects found"); + } print $cgi->header( -type => 'text/xml', diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index afac5b56a8..71ef0acb1b 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -595,4 +595,53 @@ test_expect_success HIGHLIGHT \ git commit -m "Add test.sh" && gitweb_run "p=.git;a=blob;f=test.sh"' +# ---------------------------------------------------------------------- +# forks of projects + +cat >>gitweb_config.perl <<\EOF && +$feature{'forks'}{'default'} = [1]; +EOF + +test_expect_success \ + 'forks: prepare' \ + 'git init --bare foo.git && + git --git-dir=foo.git --work-tree=. add file && + git --git-dir=foo.git --work-tree=. commit -m "Initial commit" && + echo "foo" > foo.git/description && + mkdir -p foo && + (cd foo && + git clone --shared --bare ../foo.git foo-forked.git && + echo "fork of foo" > foo-forked.git/description)' + +test_expect_success \ + 'forks: projects list' \ + 'gitweb_run' + +test_expect_success \ + 'forks: forks action' \ + 'gitweb_run "p=foo.git;a=forks"' + +# ---------------------------------------------------------------------- +# content tags (tag cloud) + +cat >>gitweb_config.perl <<-\EOF && +# we don't test _setting_ content tags, so any true value is good +$feature{'ctags'}{'default'} = ['ctags_script.cgi']; +EOF + +test_expect_success \ + 'ctags: tag cloud in projects list' \ + 'mkdir .git/ctags && + echo "2" > .git/ctags/foo && + echo "1" > .git/ctags/bar && + gitweb_run' + +test_expect_success \ + 'ctags: search projects by existing tag' \ + 'gitweb_run "by_tag=foo"' + +test_expect_success \ + 'ctags: search projects by non existent tag' \ + 'gitweb_run "by_tag=non-existent"' + test_done diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index dd83890001..731e64c3ad 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh @@ -112,4 +112,78 @@ test_expect_success 'snapshot: hierarchical branch name (xx/test)' ' ' test_debug 'cat gitweb.headers' +# ---------------------------------------------------------------------- +# forks of projects + +test_expect_success 'forks: setup' ' + git init --bare foo.git && + echo file > file && + git --git-dir=foo.git --work-tree=. add file && + git --git-dir=foo.git --work-tree=. commit -m "Initial commit" && + echo "foo" > foo.git/description && + git clone --bare foo.git foo.bar.git && + echo "foo.bar" > foo.bar.git/description && + git clone --bare foo.git foo_baz.git && + echo "foo_baz" > foo_baz.git/description && + rm -fr foo && + mkdir -p foo && + ( + cd foo && + git clone --shared --bare ../foo.git foo-forked.git && + echo "fork of foo" > foo-forked.git/description + ) +' + +test_expect_success 'forks: not skipped unless "forks" feature enabled' ' + gitweb_run "a=project_list" && + grep -q ">\\.git<" gitweb.body && + grep -q ">foo\\.git<" gitweb.body && + grep -q ">foo_baz\\.git<" gitweb.body && + grep -q ">foo\\.bar\\.git<" gitweb.body && + grep -q ">foo_baz\\.git<" gitweb.body && + grep -q ">foo/foo-forked\\.git<" gitweb.body && + grep -q ">fork of .*<" gitweb.body +' + +cat >>gitweb_config.perl <<\EOF && +$feature{'forks'}{'default'} = [1]; +EOF + +test_expect_success 'forks: forks skipped if "forks" feature enabled' ' + gitweb_run "a=project_list" && + grep -q ">\\.git<" gitweb.body && + grep -q ">foo\\.git<" gitweb.body && + grep -q ">foo_baz\\.git<" gitweb.body && + grep -q ">foo\\.bar\\.git<" gitweb.body && + grep -q ">foo_baz\\.git<" gitweb.body && + grep -v ">foo/foo-forked\\.git<" gitweb.body && + grep -v ">fork of .*<" gitweb.body +' + +test_expect_success 'forks: "forks" action for forked repository' ' + gitweb_run "p=foo.git;a=forks" && + grep -q ">foo/foo-forked\\.git<" gitweb.body && + grep -q ">fork of foo<" gitweb.body +' + +test_expect_success 'forks: can access forked repository' ' + gitweb_run "p=foo/foo-forked.git;a=summary" && + grep -q "200 OK" gitweb.headers && + grep -q ">fork of foo<" gitweb.body +' + +test_expect_success 'forks: project_index lists all projects (incl. forks)' ' + cat >expected <<-\EOF + .git + foo.bar.git + foo.git + foo/foo-forked.git + foo_baz.git + EOF + gitweb_run "a=project_index" && + sed -e "s/ .*//" actual && + test_cmp expected actual +' + + test_done -- cgit v1.3 From 0fa920c35c2617d7c1756b1e2bf5c56a81db3232 Mon Sep 17 00:00:00 2001 From: Sebastien Cevey Date: Fri, 29 Apr 2011 19:51:59 +0200 Subject: gitweb: Split git_project_list_body in two functions Extract the printing of project rows (body/contents of projects list table) on the 'project_list' page into a separate git_project_list_rows function. This makes it easier to reuse the code to print different subsets of the whole project list. [jn: Updated to post restructuring projects list generation] Signed-off-by: Sebastien Cevey Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 89 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 39 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e81c27d14c..f8d57223d5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4946,6 +4946,55 @@ sub format_sort_th { return $sort_th; } +sub git_project_list_rows { + my ($projlist, $from, $to, $check_forks) = @_; + + $from = 0 unless defined $from; + $to = $#$projlist if (!defined $to || $#$projlist < $to); + + my $alternate = 1; + for (my $i = $from; $i <= $to; $i++) { + my $pr = $projlist->[$i]; + + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + if ($check_forks) { + print ""; + if ($pr->{'forks'}) { + my $nforks = scalar @{$pr->{'forks'}}; + if ($nforks > 0) { + print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"), + -title => "$nforks forks"}, "+"); + } else { + print $cgi->span({-title => "$nforks forks"}, "+"); + } + } + print "\n"; + } + print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), + -class => "list"}, esc_html($pr->{'path'})) . "\n" . + "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), + -class => "list", -title => $pr->{'descr_long'}}, + esc_html($pr->{'descr'})) . "\n" . + "" . chop_and_escape_str($pr->{'owner'}, 15) . "\n"; + print "{'age'}) . "\">" . + (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "\n" . + "" . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") . + ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') . + "\n" . + "\n"; + } +} + sub git_project_list_body { # actually uses global variable $project my ($projlist, $order, $from, $to, $extra, $no_header) = @_; @@ -5001,47 +5050,9 @@ sub git_project_list_body { print "\n" . # for links "\n"; } - my $alternate = 1; - for (my $i = $from; $i <= $to; $i++) { - my $pr = $projects[$i]; - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; + git_project_list_rows(\@projects, $from, $to, $check_forks); - if ($check_forks) { - print ""; - if ($pr->{'forks'}) { - my $nforks = scalar @{$pr->{'forks'}}; - if ($nforks > 0) { - print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"), - -title => "$nforks forks"}, "+"); - } else { - print $cgi->span({-title => "$nforks forks"}, "+"); - } - } - print "\n"; - } - print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), - -class => "list"}, esc_html($pr->{'path'})) . "\n" . - "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), - -class => "list", -title => $pr->{'descr_long'}}, - esc_html($pr->{'descr'})) . "\n" . - "" . chop_and_escape_str($pr->{'owner'}, 15) . "\n"; - print "{'age'}) . "\">" . - (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "\n" . - "" . - $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . - $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . - $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " . - $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") . - ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') . - "\n" . - "\n"; - } if (defined $extra) { print "\n"; if ($check_forks) { -- cgit v1.3 From 0368c492d664775924388b18494eeb4690692ab8 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 29 Apr 2011 19:51:57 +0200 Subject: gitweb: Change the way "content tags" ('ctags') are handled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The major change is removing the ability to edit content tags (ctags) in a web browser. The interface was created by gitweb, while actual editing of tags was to be done by external script; the API was not defined, and neither was provided example implementation. Such split is also a bit fragile - interface and implementation have to be kept in sync. Gitweb provided only ability to add tags; you could not edit tags nor delete them. Format of ctags is now described in the comment above git_get_project_ctags subroutine. Gitweb now is more robust with respect to original ctags format; it also accepts two new formats: $GIT_DIR/ctags file, with one content tag per line, and multi-value `gitweb.ctag' config variable. Gathering all ctags of all project is now put in git_gather_all_ctags subroutine, making git_project_list_body more clear. git_populate_project_tagcloud subroutine now generates data used for tag cloud, including generation of ctag link, also in the case HTML::TagCloud module is unavailable. Links are now generated using href() subroutine - this is more robust, as ctags might contain '?', ';' and '=' special characters that need to be escaped in query param. Shown tags are HTML-escaped. The generation of tag cloud in git_show_project_tagcloud in the case when HTML::TagCloud is not available is now changed slightly. The 'content tags' field on project summary page is made more in line with other fields in "projects_list" table. Because one cannot now add new tags from web interface, this field is no longer displayed when there are no content tags for given project. Ctags-issue-Reported-by: Uwe Kleine-König Ctags-issue-Reported-by: Jonathan Nieder Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 141 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 44 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 014b33b50a..60cb772c58 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -412,20 +412,23 @@ our %feature = ( 'override' => 0, 'default' => []}, - # Allow gitweb scan project content tags described in ctags/ - # of project repository, and display the popular Web 2.0-ish - # "tag cloud" near the project list. Note that this is something - # COMPLETELY different from the normal Git tags. + # Allow gitweb scan project content tags of project repository, + # and display the popular Web 2.0-ish "tag cloud" near the projects + # list. Note that this is something COMPLETELY different from the + # normal Git tags. # gitweb by itself can show existing tags, but it does not handle - # tagging itself; you need an external application for that. - # For an example script, check Girocco's cgi/tagproj.cgi. + # tagging itself; you need to do it externally, outside gitweb. + # The format is described in git_get_project_ctags() subroutine. # You may want to install the HTML::TagCloud Perl module to get # a pretty tag cloud instead of just a list of tags. # To enable system wide have in $GITWEB_CONFIG - # $feature{'ctags'}{'default'} = ['path_to_tag_script']; + # $feature{'ctags'}{'default'} = [1]; # Project specific override is not supported. + + # In the future whether ctags editing is enabled might depend + # on the value, but using 1 should always mean no editing of ctags. 'ctags' => { 'override' => 0, 'default' => [0]}, @@ -703,6 +706,7 @@ our @cgi_param_mapping = ( snapshot_format => "sf", extra_options => "opt", search_use_regexp => "sr", + ctag => "by_tag", # this must be last entry (for manipulation from JavaScript) javascript => "js" ); @@ -2572,23 +2576,66 @@ sub git_get_project_description { return $descr; } +# supported formats: +# * $GIT_DIR/ctags/ file (in 'ctags' subdirectory) +# - if its contents is a number, use it as tag weight, +# - otherwise add a tag with weight 1 +# * $GIT_DIR/ctags file, each line is a tag (with weight 1) +# the same value multiple times increases tag weight +# * `gitweb.ctag' multi-valued repo config variable sub git_get_project_ctags { - my $path = shift; + my $project = shift; my $ctags = {}; - $git_dir = "$projectroot/$path"; - opendir my $dh, "$git_dir/ctags" - or return $ctags; - foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh)) { - open my $ct, '<', $_ or next; - my $val = <$ct>; - chomp $val; - close $ct; - my $ctag = $_; $ctag =~ s#.*/##; - $ctags->{$ctag} = $val; + $git_dir = "$projectroot/$project"; + if (opendir my $dh, "$git_dir/ctags") { + my @files = grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh); + foreach my $tagfile (@files) { + open my $ct, '<', $tagfile + or next; + my $val = <$ct>; + chomp $val if $val; + close $ct; + + (my $ctag = $tagfile) =~ s#.*/##; + if ($val =~ /\d+/) { + $ctags->{$ctag} = $val; + } else { + $ctags->{$ctag} = 1; + } + } + closedir $dh; + + } elsif (open my $fh, '<', "$git_dir/ctags") { + while (my $line = <$fh>) { + chomp $line; + $ctags->{$line}++ if $line; + } + close $fh; + + } else { + my $taglist = config_to_multi(git_get_project_config('ctag')); + foreach my $tag (@$taglist) { + $ctags->{$tag}++; + } } - closedir $dh; - $ctags; + + return $ctags; +} + +# return hash, where keys are content tags ('ctags'), +# and values are sum of weights of given tag in every project +sub git_gather_all_ctags { + my $projects = shift; + my $ctags = {}; + + foreach my $p (@$projects) { + foreach my $ct (keys %{$p->{'ctags'}}) { + $ctags->{$ct} += $p->{'ctags'}->{$ct}; + } + } + + return $ctags; } sub git_populate_project_tagcloud { @@ -2608,31 +2655,41 @@ sub git_populate_project_tagcloud { my $cloud; if (eval { require HTML::TagCloud; 1; }) { $cloud = HTML::TagCloud->new; - foreach (sort keys %ctags_lc) { + foreach my $ctag (sort keys %ctags_lc) { # Pad the title with spaces so that the cloud looks # less crammed. - my $title = $ctags_lc{$_}->{topname}; + my $title = esc_html($ctags_lc{$ctag}->{topname}); $title =~ s/ / /g; $title =~ s/^/ /g; $title =~ s/$/ /g; - $cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count}); + $cloud->add($title, href(project=>undef, ctag=>$ctag), + $ctags_lc{$ctag}->{count}); } } else { - $cloud = \%ctags_lc; + $cloud = {}; + foreach my $ctag (keys %ctags_lc) { + my $title = $ctags_lc{$ctag}->{topname}; + $cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count}; + $cloud->{$ctag}{ctag} = + $cgi->a({-href=>href(project=>undef, ctag=>$ctag)}, + esc_html($title, -nbsp=>1)); + } } - $cloud; + return $cloud; } sub git_show_project_tagcloud { my ($cloud, $count) = @_; - print STDERR ref($cloud)."..\n"; if (ref $cloud eq 'HTML::TagCloud') { return $cloud->html_and_css($count); } else { - my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud; - return '

' . join (', ', map { - $cgi->a({-href=>"$home_link?by_tag=$_"}, $cloud->{$_}->{topname}) - } splice(@tags, 0, $count)) . '

'; + my @tags = sort { $cloud->{$a}->{'count'} <=> $cloud->{$b}->{'count'} } keys %$cloud; + return + '
' . + join (', ', map { + $cloud->{$_}->{'ctag'} + } splice(@tags, 0, $count)) . + '
'; } } @@ -4920,13 +4977,8 @@ sub git_project_list_body { @projects = sort_projects_list(\@projects, $order); if ($show_ctags) { - my %ctags; - foreach my $p (@projects) { - foreach my $ct (keys %{$p->{'ctags'}}) { - $ctags{$ct} += $p->{'ctags'}->{$ct}; - } - } - my $cloud = git_populate_project_tagcloud(\%ctags); + my $ctags = git_gather_all_ctags(\@projects); + my $cloud = git_populate_project_tagcloud($ctags); print git_show_project_tagcloud($cloud, 64); } @@ -5521,13 +5573,14 @@ sub git_summary { my $show_ctags = gitweb_check_feature('ctags'); if ($show_ctags) { my $ctags = git_get_project_ctags($project); - my $cloud = git_populate_project_tagcloud($ctags); - print "Content tags:
"; - print "\n" unless %$ctags; - print "
Add:
"; - print "\n" if %$ctags; - print git_show_project_tagcloud($cloud, 48); - print ""; + if (%$ctags) { + # without ability to add tags, don't show if there are none + my $cloud = git_populate_project_tagcloud($ctags); + print "" . + "content tags" . + "".git_show_project_tagcloud($cloud, 48)."" . + "\n"; + } } print "\n"; -- cgit v1.3 From e4e3b32bd2317ab73f03d692ab3f7231e2e6787d Mon Sep 17 00:00:00 2001 From: Sebastien Cevey Date: Fri, 29 Apr 2011 19:52:00 +0200 Subject: gitweb: Modularized git_get_project_description to be more generic Introduce a git_get_file_or_project_config utility function to retrieve a repository variable either from a plain text file in the $GIT_DIR or else from 'gitweb.$variable' in the repository config (e.g. 'description'). This would be used in next commit to retrieve category for a project, which is to be stored in the same way as project description. Signed-off-by: Sebastien Cevey Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index f8d57223d5..e8685acea3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2562,18 +2562,26 @@ sub git_get_path_by_hash { ## ...................................................................... ## git utility functions, directly accessing git repository -sub git_get_project_description { - my $path = shift; +# get the value of config variable either from file named as the variable +# itself in the repository ($GIT_DIR/$name file), or from gitweb.$name +# configuration variable in the repository config file. +sub git_get_file_or_project_config { + my ($path, $name) = @_; $git_dir = "$projectroot/$path"; - open my $fd, '<', "$git_dir/description" - or return git_get_project_config('description'); - my $descr = <$fd>; + open my $fd, '<', "$git_dir/$name" + or return git_get_project_config($name); + my $conf = <$fd>; close $fd; - if (defined $descr) { - chomp $descr; + if (defined $conf) { + chomp $conf; } - return $descr; + return $conf; +} + +sub git_get_project_description { + my $path = shift; + return git_get_file_or_project_config($path, 'description'); } # supported formats: -- cgit v1.3 From 4b9447f98ef8699450fa0276d212167a25667d7f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 29 Apr 2011 19:51:58 +0200 Subject: gitweb: Mark matched 'ctag' / contents tag (?by_tag=foo) It might have been hard to discover that current view is limited to projects with given content tag (ctag), as it was distinquished only in gitweb URL. Mark matched contents tag in the tag cloud using "match" class, for easier discovery. This commit introduces a bit of further code duplication in git_populate_project_tagcloud(). Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 60cb772c58..e81c27d14c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2653,6 +2653,7 @@ sub git_populate_project_tagcloud { } my $cloud; + my $matched = $cgi->param('by_tag'); if (eval { require HTML::TagCloud; 1; }) { $cloud = HTML::TagCloud->new; foreach my $ctag (sort keys %ctags_lc) { @@ -2662,17 +2663,22 @@ sub git_populate_project_tagcloud { $title =~ s/ / /g; $title =~ s/^/ /g; $title =~ s/$/ /g; + if (defined $matched && $matched eq $ctag) { + $title = qq($title); + } $cloud->add($title, href(project=>undef, ctag=>$ctag), $ctags_lc{$ctag}->{count}); } } else { $cloud = {}; foreach my $ctag (keys %ctags_lc) { - my $title = $ctags_lc{$ctag}->{topname}; + my $title = esc_html($ctags_lc{$ctag}->{topname}, -nbsp=>1); + if (defined $matched && $matched eq $ctag) { + $title = qq($title); + } $cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count}; $cloud->{$ctag}{ctag} = - $cgi->a({-href=>href(project=>undef, ctag=>$ctag)}, - esc_html($title, -nbsp=>1)); + $cgi->a({-href=>href(project=>undef, ctag=>$ctag)}, $title); } } return $cloud; -- cgit v1.3 From d940c9015d1a770b4f8ed8e9b38cf3d16f47a6cd Mon Sep 17 00:00:00 2001 From: Sebastien Cevey Date: Fri, 29 Apr 2011 19:52:01 +0200 Subject: gitweb: Optional grouping of projects by category This adds the $projects_list_group_categories option which, if enabled, will result in grouping projects by category on the project list page. The category is specified for each project by the $GIT_DIR/category file or the 'gitweb.category' variable in its configuration file. By default, projects are put in the $project_list_default_category category. Note: - Categories are always sorted alphabetically, with projects in each category sorted according to the globally selected $order. - When displaying a subset of all the projects (page limiting), the category headers are only displayed for projects present on the page. The feature is inspired from Sham Chukoury's patch for the XMMS2 gitweb, but has been rewritten for the current gitweb code. The CSS for categories is inspired from Gustavo Sverzut Barbieri's patch to group projects by path. Thanks to Florian Ragwitz for Perl tips. [jn: Updated to post restructuring projects list generation, fixed bugs, added very basic test in t9500 that there are no warnings from Perl.] Signed-off-by: Sebastien Cevey Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/README | 16 +++++++++ gitweb/gitweb.perl | 62 ++++++++++++++++++++++++++++++++-- gitweb/static/gitweb.css | 7 ++++ t/t9500-gitweb-standalone-no-errors.sh | 8 +++++ 4 files changed, 90 insertions(+), 3 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/README b/gitweb/README index a92bde7f14..a3a697bf55 100644 --- a/gitweb/README +++ b/gitweb/README @@ -207,6 +207,15 @@ not include variables usually directly set during build): full description is available as 'title' attribute (usually shown on mouseover). By default set to 25, which might be too small if you use long project descriptions. + * $projects_list_group_categories + Enables the grouping of projects by category on the project list page. + The category of a project is determined by the $GIT_DIR/category + file or the 'gitweb.category' variable in its repository configuration. + Disabled by default. + * $project_list_default_category + Default category for projects for which none is specified. If set + to the empty string, such projects will remain uncategorized and + listed at the top, above categorized projects. * @git_base_url_list List of git base URLs used for URL to where fetch project from, shown in project summary page. Full URL is "$git_base_url/$project". @@ -314,6 +323,13 @@ You can use the following files in repository: from the template during repository creation. You can use the gitweb.description repo configuration variable, but the file takes precedence. + * category (or gitweb.category) + Singe line category of a project, used to group projects if + $projects_list_group_categories is enabled. By default (file and + configuration variable absent), uncategorized projects are put in + the $project_list_default_category category. You can use the + gitweb.category repo configuration variable, but the file takes + precedence. * cloneurl (or multiple-valued gitweb.url) File with repository URL (used for clone and fetch), one per line. Displayed in the project summary page. You can use multiple-valued diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e8685acea3..f78fdd7c5f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -115,6 +115,14 @@ our $projects_list = "++GITWEB_LIST++"; # the width (in characters) of the projects list "Description" column our $projects_list_description_width = 25; +# group projects by category on the projects list +# (enabled if this variable evaluates to true) +our $projects_list_group_categories = 0; + +# default category if none specified +# (leave the empty string for no category) +our $project_list_default_category = ""; + # default order of projects list # valid values are none, project, descr, owner, and age our $default_projects_order = "project"; @@ -2584,6 +2592,12 @@ sub git_get_project_description { return git_get_file_or_project_config($path, 'description'); } +sub git_get_project_category { + my $path = shift; + return git_get_file_or_project_config($path, 'category'); +} + + # supported formats: # * $GIT_DIR/ctags/ file (in 'ctags' subdirectory) # - if its contents is a number, use it as tag weight, @@ -4877,8 +4891,9 @@ sub git_patchset_body { # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -# fills project list info (age, description, owner, forks) for each -# project in the list, removing invalid projects from returned list +# fills project list info (age, description, owner, category, forks) +# for each project in the list, removing invalid projects from +# returned list # NOTE: modifies $projlist, but does not remove entries from it sub fill_project_list_info { my $projlist = shift; @@ -4904,6 +4919,12 @@ sub fill_project_list_info { if ($show_ctags) { $pr->{'ctags'} = git_get_project_ctags($pr->{'path'}); } + if ($projects_list_group_categories && !defined $pr->{'category'}) { + my $cat = git_get_project_category($pr->{'path'}) || + $project_list_default_category; + $pr->{'category'} = to_utf8($cat); + } + push @projects, $pr; } @@ -4931,6 +4952,23 @@ sub sort_projects_list { return @projects; } +# returns a hash of categories, containing the list of project +# belonging to each category +sub build_projlist_by_category { + my ($projlist, $from, $to) = @_; + my %categories; + + $from = 0 unless defined $from; + $to = $#$projlist if (!defined $to || $#$projlist < $to); + + for (my $i = $from; $i <= $to; $i++) { + my $pr = $projlist->[$i]; + push @{$categories{ $pr->{'category'} }}, $pr; + } + + return wantarray ? %categories : \%categories; +} + # print 'sort by' element, generating 'sort by $name' replay link # if that order is not selected sub print_sort_th { @@ -5059,7 +5097,25 @@ sub git_project_list_body { "\n"; } - git_project_list_rows(\@projects, $from, $to, $check_forks); + if ($projects_list_group_categories) { + # only display categories with projects in the $from-$to window + @projects = sort {$a->{'category'} cmp $b->{'category'}} @projects[$from..$to]; + my %categories = build_projlist_by_category(\@projects, $from, $to); + foreach my $cat (sort keys %categories) { + unless ($cat eq "") { + print "\n"; + if ($check_forks) { + print "\n"; + } + print "".esc_html($cat)."\n"; + print "\n"; + } + + git_project_list_rows($categories{$cat}, undef, undef, $check_forks); + } + } else { + git_project_list_rows(\@projects, $from, $to, $check_forks); + } if (defined $extra) { print "\n"; diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css index 79d7eebba7..4df2d163c9 100644 --- a/gitweb/static/gitweb.css +++ b/gitweb/static/gitweb.css @@ -295,6 +295,13 @@ td.current_head { text-decoration: underline; } +td.category { + background-color: #d9d8d1; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + font-weight: bold; +} + table.diff_tree span.file_status.new { color: #008000; } diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 71ef0acb1b..f5648a6694 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -644,4 +644,12 @@ test_expect_success \ 'ctags: search projects by non existent tag' \ 'gitweb_run "by_tag=non-existent"' +# ---------------------------------------------------------------------- +# categories + +test_expect_success \ + 'categories: projects list, only default category' \ + 'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl && + gitweb_run' + test_done -- cgit v1.3 From 256b7b4883231ae9c7f24f2cb4170f61e00704e1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 28 Apr 2011 21:04:07 +0200 Subject: gitweb: Refactor generating of long dates into format_timestamp_html It is pure refactoring and doesn't change gitweb output, though this could potentially affect 'summary', 'log', and 'commit'-like views ('commit', 'commitdiff', 'tag'). Remove print_local_time and format_local_time, as their use is now replaced (indirectly) by using format_timestamp_html. While at it improve whitespace formatting. Inspired-by-code-by: Kevin Cernekee Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ee69ea683a..7329db289c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3938,22 +3938,21 @@ sub git_print_section { print $cgi->end_div; } -sub print_local_time { - print format_local_time(@_); -} +sub format_timestamp_html { + my ($date, %opts) = @_; + my $strtime = $date->{'rfc2822'}; -sub format_local_time { - my $localtime = ''; - my %date = @_; - if ($date{'hour_local'} < 6) { - $localtime .= sprintf(" (%02d:%02d %s)", - $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'}); - } else { - $localtime .= sprintf(" (%02d:%02d %s)", - $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'}); + return $strtime unless $opts{'-localtime'}; + + my $localtime_format = '(%02d:%02d %s)'; + if ($date->{'hour_local'} < 6) { + $localtime_format = '(%02d:%02d %s)'; } + $strtime .= ' ' . + sprintf($localtime_format, + $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'}); - return $localtime; + return $strtime; } # Outputs the author name and date in long form @@ -3966,10 +3965,9 @@ sub git_print_authorship { my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'}); print "<$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) - . "\n"; + " [".format_timestamp_html(\%ad, %opts)."]". + git_get_avatar($co->{'author_email'}, -pad_before => 1) . + "\n"; } # Outputs table rows containing the full author or committer information, @@ -3986,16 +3984,16 @@ sub git_print_authorship_rows { my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"}); print "$who" . format_search_author($co->{"${who}_name"}, $who, - esc_html($co->{"${who}_name"})) . " " . + esc_html($co->{"${who}_name"})) . " " . format_search_author($co->{"${who}_email"}, $who, - esc_html("<" . $co->{"${who}_email"} . ">")) . + esc_html("<" . $co->{"${who}_email"} . ">")) . "" . git_get_avatar($co->{"${who}_email"}, -size => 'double') . "\n" . "" . - " $wd{'rfc2822'}"; - print_local_time(%wd); - print "" . + "" . + format_timestamp_html(\%wd, -localtime=>1) . + "" . "\n"; } } @@ -5410,7 +5408,8 @@ sub git_summary { "description" . esc_html($descr) . "\n" . "owner" . esc_html($owner) . "\n"; if (defined $cd{'rfc2822'}) { - print "last change$cd{'rfc2822'}\n"; + print "last change" . + "".format_timestamp_html(\%cd)."\n"; } # use per project git URL list in $projectroot/$project/cloneurl -- cgit v1.3 From ce71b07632c71c2e6f4732abd3db6d59fb1bb924 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 28 Apr 2011 21:04:08 +0200 Subject: gitweb: Unify the way long timestamp is displayed format_timestamp_html loses its "-localtime => 1" option, and now always print the local time (in author/comitter/tagger local timezone), with "atnight" warning if needed. This means that both 'summary' and 'log' views now display localtime. In the case of 'log' view this can be thought as an improvement, as now one can easily see which commits in a series are made "atnight" and should be examined closer. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7329db289c..67bcfe894e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3939,11 +3939,9 @@ sub git_print_section { } sub format_timestamp_html { - my ($date, %opts) = @_; + my $date = shift; my $strtime = $date->{'rfc2822'}; - return $strtime unless $opts{'-localtime'}; - my $localtime_format = '(%02d:%02d %s)'; if ($date->{'hour_local'} < 6) { $localtime_format = '(%02d:%02d %s)'; @@ -3965,7 +3963,7 @@ sub git_print_authorship { my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'}); print "<$tag class=\"author_date\">" . format_search_author($author, "author", esc_html($author)) . - " [".format_timestamp_html(\%ad, %opts)."]". + " [".format_timestamp_html(\%ad)."]". git_get_avatar($co->{'author_email'}, -pad_before => 1) . "\n"; } @@ -3992,7 +3990,7 @@ sub git_print_authorship_rows { "\n" . "" . "" . - format_timestamp_html(\%wd, -localtime=>1) . + format_timestamp_html(\%wd) . "" . "\n"; } -- cgit v1.3 From 291e52bd19877dc8fdd77079ba4c4326f114c461 Mon Sep 17 00:00:00 2001 From: John 'Warthog9' Hawley Date: Thu, 28 Apr 2011 21:04:09 +0200 Subject: gitweb: JavaScript ability to adjust time based on timezone This patch is based on Kevin Cernekee's patch series entitled "gitweb: introduce localtime feature". While Kevin's patch changed the server side output so that the timezone was output from gitweb itself, this has a number of drawbacks, in particular with respect to gitweb-caching. This patch takes the same basic goal, display the appropriate times in a given common timezone, and implements it in JavaScript. This requires adding / using a new class, "datetime", to be able to find elements to be adjusted from JavaScript. Appropriate dates are wrapped in a span with this class. Timezone to be used can be retrieved from "gitweb_tz" cookie, though currently there is no way to set / manipulate this cookie from gitweb; this is left for later commit. Valid timezones, currently, are: "utc", "local" (which means that timezone is taken from browser), and "+/-ZZZZ" numeric timezone as in RFC-2822. Default timezone is "local" (currently not configurable, left for later commit). Fallback (should JavaScript not be enabled) is to treat dates as they have been and display them, only, in UTC. Pages affected: * 'summary' view, "last change" field (commit time from latest change) * 'log' view, author time * 'commit' and 'commitdiff' views, author/committer time * 'tag' view, tagger time Based-on-code-from: Kevin Cernekee Signed-off-by: John 'Warthog9' Hawley Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/Makefile | 1 + gitweb/gitweb.perl | 11 +++++-- gitweb/static/js/adjust-timezone.js | 60 +++++++++++++++++++++++++++++++++++++ gitweb/static/js/lib/datetime.js | 15 ++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 gitweb/static/js/adjust-timezone.js (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/Makefile b/gitweb/Makefile index 7dd1dee55a..5d20515fba 100644 --- a/gitweb/Makefile +++ b/gitweb/Makefile @@ -120,6 +120,7 @@ GITWEB_JSLIB_FILES += static/js/lib/common-lib.js GITWEB_JSLIB_FILES += static/js/lib/datetime.js GITWEB_JSLIB_FILES += static/js/lib/cookies.js GITWEB_JSLIB_FILES += static/js/javascript-detection.js +GITWEB_JSLIB_FILES += static/js/adjust-timezone.js GITWEB_JSLIB_FILES += static/js/blame_incremental.js diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 67bcfe894e..6651946f54 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3732,9 +3732,14 @@ sub git_footer_html { qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!. qq! "!. href() .qq!");\n!. qq!\n!; - } elsif (gitweb_check_feature('javascript-actions')) { + } else { print qq!\n!; } @@ -3940,7 +3945,7 @@ sub git_print_section { sub format_timestamp_html { my $date = shift; - my $strtime = $date->{'rfc2822'}; + my $strtime = ''.$date->{'rfc2822'}.''; my $localtime_format = '(%02d:%02d %s)'; if ($date->{'hour_local'} < 6) { diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js new file mode 100644 index 0000000000..1577d780f0 --- /dev/null +++ b/gitweb/static/js/adjust-timezone.js @@ -0,0 +1,60 @@ +// Copyright (C) 2011, John 'Warthog9' Hawley +// 2011, Jakub Narebski + +/** + * @fileOverview Manipulate dates in gitweb output, adjusting timezone + * @license GPLv2 or later + */ + +/** + * Get common timezone and adjust dates to use this common timezone. + * + * This function is called during onload event (added to window.onload). + * + * @param {String} tzDefault: default timezone, if there is no cookie + * @param {String} tzCookieName: name of cookie to store timezone + * @param {String} tzClassName: denotes elements with date to be adjusted + */ +function onloadTZSetup(tzDefault, tzCookieName, tzClassName) { + var tzCookie = getCookie(tzCookieName); + var tz = tzCookie ? tzCookie : tzDefault; + + // server-side of gitweb produces datetime in UTC, + // so if tz is 'utc' there is no need for changes + if (tz !== 'utc') { + fixDatetimeTZ(tz, tzClassName); + } +} + + +/** + * Replace RFC-2822 dates contained in SPAN elements with tzClassName + * CSS class with equivalent dates in given timezone. + * + * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local' + * @param {String} tzClassName: specifies elements to be changed + */ +function fixDatetimeTZ(tz, tzClassName) { + // sanity check, method should be ensured by common-lib.js + if (!document.getElementsByClassName) { + return; + } + + // translate to timezone in '(-|+)HHMM' format + tz = normalizeTimezoneInfo(tz); + + // NOTE: result of getElementsByClassName should probably be cached + var classesFound = document.getElementsByClassName(tzClassName, "span"); + for (var i = 0, len = classesFound.length; i < len; i++) { + var curElement = classesFound[i]; + + // we use *.firstChild.data (W3C DOM) instead of *.innerHTML + // as the latter doesn't always work everywhere in every browser + var epoch = parseRFC2822Date(curElement.firstChild.data); + var adjusted = formatDateRFC2882(epoch, tz); + + curElement.firstChild.data = adjusted; + } +} + +/* end of adjust-timezone.js */ diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js index b3dcedb141..f78c60a912 100644 --- a/gitweb/static/js/lib/datetime.js +++ b/gitweb/static/js/lib/datetime.js @@ -104,6 +104,21 @@ function formatTimezoneInfo(hours, minutes, sep) { return tzSign + padLeft(hours, 2, '0') + sep + padLeft(minutes, 2, '0'); } +/** + * translate 'utc' and 'local' to numerical timezone + * @param {String} timezoneInfo: might be 'utc' or 'local' (browser) + */ +function normalizeTimezoneInfo(timezoneInfo) { + switch (timezoneInfo) { + case 'utc': + return '+0000'; + case 'local': // 'local' is browser timezone + return localTimezoneInfo(); + } + return timezoneInfo; +} + + /** * return date in local time formatted in iso-8601 like format * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200' -- cgit v1.3 From 2ae8da2552f43802476676bb86b037e9028b7a7c Mon Sep 17 00:00:00 2001 From: John 'Warthog9' Hawley Date: Thu, 28 Apr 2011 21:04:10 +0200 Subject: gitweb.js: Add UI for selecting common timezone to display dates This will modify HTML, add CSS rules and add DOM event handlers so that clicking on any date (the common part, not the localtime part) will display a drop down menu to choose the timezone to change to. Currently menu displays only the following timezones: utc local -1200 -1100 ... +1100 +1200 +1300 +1400 In timezone selection menu each timezone is +1hr to the previous. The code is capable of handling fractional timezones, but those have not been added to the menu. All changes are saved to a cookie, so page changes and closing / reopening browser retains the last known timezone setting used. [jn: Changed from innerHTML to DOM, moved to event delegation for onclick to trigger menu, added close button and cookie refreshing] Helped-by: Kevin Cernekee Signed-off-by: John 'Warthog9' Hawley Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +- gitweb/static/gitweb.css | 33 ++++ gitweb/static/js/adjust-timezone.js | 298 ++++++++++++++++++++++++++++++++++-- gitweb/static/js/lib/common-lib.js | 27 +++- 4 files changed, 345 insertions(+), 16 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6651946f54..b1e80ef87b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3738,7 +3738,8 @@ sub git_footer_html { (gitweb_check_feature('javascript-actions') ? qq! fixLinks();\n! : ''). # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html - qq! onloadTZSetup('local', 'gitweb_tz', 'datetime');\n!. + qq! var tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\n!. # in days + qq! onloadTZSetup('local', tz_cookie, 'datetime');\n!. qq!};\n!. qq!\n!; } diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css index 79d7eebba7..8dd093563e 100644 --- a/gitweb/static/gitweb.css +++ b/gitweb/static/gitweb.css @@ -579,6 +579,39 @@ div.remote { display: inline-block; } +/* JavaScript-based timezone manipulation */ + +.popup { /* timezone selection UI */ + position: absolute; + /* "top: 0; right: 0;" would be better, if not for bugs in browsers */ + top: 0; left: 0; + border: 1px solid; + padding: 2px; + background-color: #f0f0f0; + font-style: normal; + color: #000000; + cursor: auto; +} + +.close-button { /* close timezone selection UI without selecting */ + /* float doesn't work within absolutely positioned container, + * if width of container is not set explicitly */ + /* float: right; */ + position: absolute; + top: 0px; right: 0px; + border: 1px solid green; + margin: 1px 1px 1px 1px; + padding-bottom: 2px; + width: 12px; + height: 10px; + font-size: 9px; + font-weight: bold; + text-align: center; + background-color: #fff0f0; + cursor: pointer; +} + + /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */ /* Highlighting theme definition: */ diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js index 1577d780f0..0c67779500 100644 --- a/gitweb/static/js/adjust-timezone.js +++ b/gitweb/static/js/adjust-timezone.js @@ -7,34 +7,51 @@ */ /** - * Get common timezone and adjust dates to use this common timezone. + * Get common timezone, add UI for changing timezones, and adjust + * dates to use requested common timezone. * * This function is called during onload event (added to window.onload). * * @param {String} tzDefault: default timezone, if there is no cookie - * @param {String} tzCookieName: name of cookie to store timezone + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzCookieInfo.name: name of cookie to store timezone * @param {String} tzClassName: denotes elements with date to be adjusted */ -function onloadTZSetup(tzDefault, tzCookieName, tzClassName) { - var tzCookie = getCookie(tzCookieName); - var tz = tzCookie ? tzCookie : tzDefault; +function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) { + var tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo); + var tz = tzDefault; + + if (tzCookieTZ) { + // set timezone to value saved in a cookie + tz = tzCookieTZ; + // refresh cookie, so its expiration counts from last use of gitweb + setCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo); + } + + // add UI for changing timezone + addChangeTZ(tz, tzCookieInfo, tzClassName); // server-side of gitweb produces datetime in UTC, // so if tz is 'utc' there is no need for changes - if (tz !== 'utc') { - fixDatetimeTZ(tz, tzClassName); - } + var nochange = tz === 'utc'; + + // adjust dates to use specified common timezone + fixDatetimeTZ(tz, tzClassName, nochange); } +/* ...................................................................... */ +/* Changing dates to use requested timezone */ + /** * Replace RFC-2822 dates contained in SPAN elements with tzClassName * CSS class with equivalent dates in given timezone. * * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local' * @param {String} tzClassName: specifies elements to be changed + * @param {Boolean} nochange: markup for timezone change, but don't change it */ -function fixDatetimeTZ(tz, tzClassName) { +function fixDatetimeTZ(tz, tzClassName, nochange) { // sanity check, method should be ensured by common-lib.js if (!document.getElementsByClassName) { return; @@ -48,13 +65,266 @@ function fixDatetimeTZ(tz, tzClassName) { for (var i = 0, len = classesFound.length; i < len; i++) { var curElement = classesFound[i]; - // we use *.firstChild.data (W3C DOM) instead of *.innerHTML - // as the latter doesn't always work everywhere in every browser - var epoch = parseRFC2822Date(curElement.firstChild.data); - var adjusted = formatDateRFC2882(epoch, tz); + curElement.title = 'Click to change timezone'; + if (!nochange) { + // we use *.firstChild.data (W3C DOM) instead of *.innerHTML + // as the latter doesn't always work everywhere in every browser + var epoch = parseRFC2822Date(curElement.firstChild.data); + var adjusted = formatDateRFC2882(epoch, tz); + + curElement.firstChild.data = adjusted; + } + } +} + + +/* ...................................................................... */ +/* Adding triggers, generating timezone menu, displaying and hiding */ + +/** + * Adds triggers for UI to change common timezone used for dates in + * gitweb output: it marks up and/or creates item to click to invoke + * timezone change UI, creates timezone UI fragment to be attached, + * and installs appropriate onclick trigger (via event delegation). + * + * @param {String} tzSelected: pre-selected timezone, + * 'utc' or 'local' or '(-|+)HHMM' + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzClassName: specifies elements to install trigger + */ +function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) { + // make link to timezone UI discoverable + addCssRule('.'+tzClassName + ':hover', + 'text-decoration: underline; cursor: help;'); + + // create form for selecting timezone (to be saved in a cookie) + var tzSelectFragment = document.createDocumentFragment(); + tzSelectFragment = createChangeTZForm(tzSelectFragment, + tzSelected, tzCookieInfo, tzClassName); + + // event delegation handler for timezone selection UI (clicking on entry) + // see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ + // assumes that there is no existing document.onclick handler + document.onclick = function onclickHandler(event) { + //IE doesn't pass in the event object + event = event || window.event; + + //IE uses srcElement as the target + var target = event.target || event.srcElement; + + switch (target.className) { + case tzClassName: + // don't display timezone menu if it is already displayed + if (tzSelectFragment.childNodes.length > 0) { + displayChangeTZForm(target, tzSelectFragment); + } + break; + } // end switch + }; +} + +/** + * Create DocumentFragment with UI for changing common timezone in + * which dates are shown in. + * + * @param {DocumentFragment} documentFragment: where attach UI + * @param {String} tzSelected: default (pre-selected) timezone + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @returns {DocumentFragment} + */ +function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) { + var div = document.createElement("div"); + div.className = 'popup'; + + /* '
X
' */ + var closeButton = document.createElement('div'); + closeButton.className = 'close-button'; + closeButton.title = '(click on this box to close)'; + closeButton.appendChild(document.createTextNode('X')); + closeButton.onclick = closeTZFormHandler(documentFragment, tzClassName); + div.appendChild(closeButton); + + /* 'Select timezone:
' */ + div.appendChild(document.createTextNode('Select timezone: ')); + var br = document.createElement('br'); + br.clear = 'all'; + div.appendChild(br); + + /* '' */ + var select = document.createElement("select"); + select.name = "tzoffset"; + //select.style.clear = 'all'; + select.appendChild(generateTZOptions(tzSelected)); + select.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName); + div.appendChild(select); + + documentFragment.appendChild(div); + + return documentFragment; +} + + +/** + * Hide (remove from DOM) timezone change UI, ensuring that it is not + * garbage collected and that it can be re-enabled later. + * + * @param {DocumentFragment} documentFragment: contains detached UI + * @param {HTMLSelectElement} target: select element inside of UI + * @param {String} tzClassName: specifies element where UI was installed + * @returns {DocumentFragment} documentFragment + */ +function removeChangeTZForm(documentFragment, target, tzClassName) { + // find containing element, where we appended timezone selection UI + // `target' is somewhere inside timezone menu + var container = target.parentNode, popup = target; + while (container && + container.className !== tzClassName) { + popup = container; + container = container.parentNode; + } + // safety check if we found correct container, + // and if it isn't deleted already + if (!container || !popup || + container.className !== tzClassName || + popup.className !== 'popup') { + return documentFragment; + } - curElement.firstChild.data = adjusted; + // timezone selection UI was appended as last child + // see also displayChangeTZForm function + var removed = popup.parentNode.removeChild(popup); + if (documentFragment.firstChild !== removed) { // the only child + // re-append it so it would be available for next time + documentFragment.appendChild(removed); } + // all of inline style was added by this script + // it is not really needed to remove it, but it is a good practice + container.removeAttribute('style'); + + return documentFragment; +} + + +/** + * Display UI for changing common timezone for dates in gitweb output. + * To be used from 'onclick' event handler. + * + * @param {HTMLElement} target: where to install/display UI + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + */ +function displayChangeTZForm(target, tzSelectFragment) { + // for absolute positioning to be related to target element + target.style.position = 'relative'; + target.style.display = 'inline-block'; + + // show/display UI for changing timezone + target.appendChild(tzSelectFragment); +} + + +/* ...................................................................... */ +/* List of timezones for timezone selection menu */ + +/** + * Generate list of timezones for creating timezone select UI + * + * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' } + */ +function generateTZList() { + var timezones = [ + { value: "utc", descr: "UTC/GMT"}, + { value: "local", descr: "Local (per browser)"} + ]; + + // generate all full hour timezones (no fractional timezones) + for (var x = -12, idx = timezones.length; x <= +14; x++, idx++) { + var hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2); + timezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'}; + if (x === 0) { + timezones[idx].descr = 'UTC\u00B100:00'; // 'UTC±00:00' + } + } + + return timezones; +} + +/** + * Generate elements for timezone select UI + * + * @param {String} tzSelected: default timezone + * @returns {DocumentFragment} list of options elements to appendChild + */ +function generateTZOptions(tzSelected) { + var elems = document.createDocumentFragment(); + var timezones = generateTZList(); + + for (var i = 0, len = timezones.length; i < len; i++) { + var tzone = timezones[i]; + var option = document.createElement("option"); + if (tzone.value === tzSelected) { + option.defaultSelected = true; + } + option.value = tzone.value; + option.appendChild(document.createTextNode(tzone.descr)); + + elems.appendChild(option); + } + + return elems; +} + + +/* ...................................................................... */ +/* Event handlers and/or their generators */ + +/** + * Create event handler that select timezone and closes timezone select UI. + * To be used as $('select[name="tzselect"]').onchange handler. + * + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzCookieInfo.name: name of cookie to save result of selection + * @param {String} tzClassName: specifies element where UI was installed + * @returns {Function} event handler + */ +function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) { + //return function selectTZ(event) { + return function (event) { + event = event || window.event; + var target = event.target || event.srcElement; + + var selected = target.options.item(target.selectedIndex); + removeChangeTZForm(tzSelectFragment, target, tzClassName); + + if (selected) { + selected.defaultSelected = true; + setCookie(tzCookieInfo.name, selected.value, tzCookieInfo); + fixDatetimeTZ(selected.value, tzClassName); + } + }; +} + +/** + * Create event handler that closes timezone select UI. + * To be used e.g. as $('.closebutton').onclick handler. + * + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + * @param {String} tzClassName: specifies element where UI was installed + * @returns {Function} event handler + */ +function closeTZFormHandler(tzSelectFragment, tzClassName) { + //return function closeTZForm(event) { + return function (event) { + event = event || window.event; + var target = event.target || event.srcElement; + + removeChangeTZForm(tzSelectFragment, target, tzClassName); + }; } /* end of adjust-timezone.js */ diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js index b37139152d..018bbb7d4c 100644 --- a/gitweb/static/js/lib/common-lib.js +++ b/gitweb/static/js/lib/common-lib.js @@ -64,7 +64,7 @@ function padLeft(input, width, ch) { /* ............................................................ */ -/* Ajax */ +/* Handling browser incompatibilities */ /** * Create XMLHttpRequest object in cross-browser way @@ -88,6 +88,31 @@ function createRequestObject() { } +/** + * Insert rule giving specified STYLE to given SELECTOR at the end of + * first CSS stylesheet. + * + * @param {String} selector: CSS selector, e.g. '.class' + * @param {String} style: rule contents, e.g. 'background-color: red;' + */ +function addCssRule(selector, style) { + var stylesheet = document.styleSheets[0]; + + var theRules = []; + if (stylesheet.cssRules) { // W3C way + theRules = stylesheet.cssRules; + } else if (stylesheet.rules) { // IE way + theRules = stylesheet.rules; + } + + if (stylesheet.insertRule) { // W3C way + stylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length); + } else if (stylesheet.addRule) { // IE way + stylesheet.addRule(selector, style); + } +} + + /* ............................................................ */ /* Support for legacy browsers */ -- cgit v1.3 From 2e987f92408f5ddd20246d7d9553b57d90767d54 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 28 Apr 2011 21:04:11 +0200 Subject: gitweb: Make JavaScript ability to adjust timezones configurable Configure JavaScript-based ability to select common timezone for git dates via %feature mechanism, namely 'javascript-timezone' feature. The following settings are configurable: * default timezone (defaults to 'local' i.e. browser timezone); this also can function as a way to disable this ability, by setting it to false-ish value (undef or '') * name of cookie to store user's choice of timezone * class name to mark dates NOTE: This is a bit of abuse of %feature system, which can store only sequence of values, rather than dictionary (hash); usually but not always only a single value is used. Based-on-code-by: John 'Warthog9' Hawley Helped-by: Kevin Cernekee Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b1e80ef87b..ac335b6dea 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -480,6 +480,18 @@ our %feature = ( 'override' => 0, 'default' => [0]}, + # Enable and configure ability to change common timezone for dates + # in gitweb output via JavaScript. Enabled by default. + # Project specific override is not supported. + 'javascript-timezone' => { + 'override' => 0, + 'default' => [ + 'local', # default timezone: 'utc', 'local', or '(-|+)HHMM' format, + # or undef to turn off this feature + 'gitweb_tz', # name of cookie where to store selected timezone + 'datetime', # CSS class used to mark up dates for manipulation + ]}, + # Syntax highlighting support. This is based on Daniel Svensson's # and Sham Chukoury's work in gitweb-xmms2.git. # It requires the 'highlight' program present in $PATH, @@ -3733,14 +3745,19 @@ sub git_footer_html { qq! "!. href() .qq!");\n!. qq!\n!; } else { + my ($jstimezone, $tz_cookie, $datetime_class) = + gitweb_get_feature('javascript-timezone'); + print qq!\n!; } @@ -3946,7 +3963,13 @@ sub git_print_section { sub format_timestamp_html { my $date = shift; - my $strtime = ''.$date->{'rfc2822'}.''; + my $strtime = $date->{'rfc2822'}; + + my (undef, undef, $datetime_class) = + gitweb_get_feature('javascript-timezone'); + if ($datetime_class) { + $strtime = qq!$strtime!; + } my $localtime_format = '(%02d:%02d %s)'; if ($date->{'hour_local'} < 6) { -- cgit v1.3 From f612a71cc901e34f5cf154e6605b7251744f6b95 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 25 May 2011 18:35:26 +0200 Subject: gitweb: Refactor reading and parsing config file into read_config_file Beside being obvious reduction of duplicated code, this is enables us to easily call site-wide config file in per-installation config file. The actual update to documentation is left for next commit, because of possible exclusive alternative (possible other next commit) of always reading system-wide config file and relying on per-instalation config file overriding system-wide defaults. Signed-off-by: Jakub Narebski Acked-by: John 'Warthog9' Hawley Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 66eadb4ab2..dfac1bce84 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -623,18 +623,30 @@ sub filter_snapshot_fmts { # if it is true then gitweb config would be run for each request. our $per_request_config = 1; +# read and parse gitweb config file given by its parameter. +# returns true on success, false on recoverable error, allowing +# to chain this subroutine, using first file that exists. +# dies on errors during parsing config file, as it is unrecoverable. +sub read_config_file { + my $filename = shift; + return unless defined $filename; + # die if there are errors parsing config file + if (-e $filename) { + do $filename; + die $@ if $@; + return 1; + } + return; +} + our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM); sub evaluate_gitweb_config { our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; - # die if there are errors parsing config file - if (-e $GITWEB_CONFIG) { - do $GITWEB_CONFIG; - die $@ if $@; - } elsif (-e $GITWEB_CONFIG_SYSTEM) { - do $GITWEB_CONFIG_SYSTEM; - die $@ if $@; - } + + # use first config file that exists + read_config_file($GITWEB_CONFIG) or + read_config_file($GITWEB_CONFIG_SYSTEM); } # Get loadavg of system, to compare against $maxload. -- cgit v1.3 From bee6ea17a1bab824eba6133eefc3c70b219ec98c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 4 Jun 2011 10:43:35 +0200 Subject: gitweb: Fix usability of $prevent_xss With XSS prevention on (enabled using $prevent_xss), blobs ('blob_plain') of all types except a few known safe ones are served with "Content-Disposition: attachment". However the check was too strict; it didn't take into account optional parameter attributes, media-type = type "/" subtype *( ";" parameter ) as described in RFC 2616 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 This fixes that, and it for example treats following as safe MIME media type: text/plain; charset=utf-8 Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index bdaa4e9463..c5548875ff 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4752,7 +4752,7 @@ 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/plain|image/(?:gif|png|jpeg))(?:[ ;]|$)!; print $cgi->header( -type => $type, -- cgit v1.3 From 2c162b56f370f5c33e6a945e6922d598006c5ec4 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Thu, 9 Jun 2011 02:08:57 -0500 Subject: gitweb: do not misparse nonnumeric content tag files that contain a digit v1.7.6-rc0~27^2~4 (gitweb: Change the way "content tags" ('ctags') are handled, 2011-04-29) tried to make gitweb's tag cloud feature more intuitive for webmasters by checking whether the ctags/