diff options
Diffstat (limited to 'gitweb')
-rw-r--r-- | gitweb/README | 16 | ||||
-rw-r--r-- | gitweb/gitweb.css | 2 | ||||
-rwxr-xr-x | gitweb/gitweb.perl | 426 |
3 files changed, 339 insertions, 105 deletions
diff --git a/gitweb/README b/gitweb/README index 78e6fc05f3..e02e90f042 100644 --- a/gitweb/README +++ b/gitweb/README @@ -26,12 +26,26 @@ You can specify the following configuration variables when building GIT: * GITWEB_LOGO Points to the location where you put git-logo.png on your web server. * GITWEB_CONFIG - This file will be loaded using 'require'. If the environment + This file will be loaded using 'require' and can be used to override any + of the options above as well as some other options - see the top of + 'gitweb.cgi' for their full list and description. If the environment $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the environment variable will be loaded instead of the file specified when gitweb.cgi was created. +Runtime gitweb configuration +---------------------------- + +You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG` +(defaults to 'gitweb_config.perl' in the same directory as the CGI). +See the top of 'gitweb.cgi' for the list of variables and some description. +The most notable thing that is not configurable at compile time are the +optional features, stored in the '%features' variable. You can find further +description on how to reconfigure the default features setting in your +`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'. + + Webserver configuration ----------------------- diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 3f62b6d752..0eda98237e 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -333,6 +333,8 @@ div.index_include { } div.search { + font-size: 12px; + font-weight: normal; margin: 4px 8px; position: absolute; top: 56px; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2390603e97..ba7a42a983 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -39,10 +39,21 @@ our $home_link_str = "++GITWEB_HOME_LINK_STR++"; # name of your site or organization to appear in page titles # replace this with something more descriptive for clearer bookmarks -our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled"; +our $site_name = "++GITWEB_SITENAME++" + || ($ENV{'SERVER_NAME'} || "Untitled") . " Git"; +# filename of html text to include at top of each page +our $site_header = "++GITWEB_SITE_HEADER++"; # html text to include at home page our $home_text = "++GITWEB_HOMETEXT++"; +# filename of html text to include at bottom of each page +our $site_footer = "++GITWEB_SITE_FOOTER++"; + +# URI of stylesheets +our @stylesheets = ("++GITWEB_CSS++"); +our $stylesheet; +# default is not to define style sheet, but it can be overwritten later +undef $stylesheet; # URI of default stylesheet our $stylesheet = "++GITWEB_CSS++"; @@ -93,21 +104,66 @@ our %feature = ( # # use gitweb_check_feature(<feature>) to check if <feature> is enabled + # Enable the 'blame' blob view, showing the last commit that modified + # each line in the file. This can be very CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'blame'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.blame = 0|1; 'blame' => { 'sub' => \&feature_blame, 'override' => 0, 'default' => [0]}, + # Enable the 'snapshot' link, providing a compressed tarball of any + # tree. This can potentially generate high traffic if you have large + # project. + + # To disable system wide have in $GITWEB_CONFIG + # $feature{'snapshot'}{'default'} = [undef]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.snapshot = none|gzip|bzip2; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, # => [content-encoding, suffix, program] 'default' => ['x-gzip', 'gz', 'gzip']}, + # Enable the pickaxe search, which will list the commits that modified + # a given string in a file. This can be practical and quite faster + # alternative to 'blame', but still potentially CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pickaxe'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'pickaxe'}{'override'} = 1; + # and in project config gitweb.pickaxe = 0|1; 'pickaxe' => { 'sub' => \&feature_pickaxe, 'override' => 0, 'default' => [1]}, + + # Make gitweb use an alternative format of the URLs which can be + # more readable and natural-looking: project name is embedded + # directly in the path and the query string contains other + # auxiliary information. All gitweb installations recognize + # URL in either format; this configures in which formats gitweb + # generates links. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pathinfo'}{'default'} = [1]; + # Project specific override is not supported. + + # Note that you will need to change the default location of CSS, + # favicon, logo and possibly other files to an absolute URL. Also, + # if gitweb.cgi serves as your indexfile, you will need to force + # $my_uri to contain the script name in your $GITWEB_CONFIG. + 'pathinfo' => { + 'override' => 0, + 'default' => [0]}, ); sub gitweb_check_feature { @@ -118,15 +174,13 @@ sub gitweb_check_feature { $feature{$name}{'override'}, @{$feature{$name}{'default'}}); if (!$override) { return @defaults; } + if (!defined $sub) { + warn "feature $name is not overrideable"; + return @defaults; + } return $sub->(@defaults); } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'blame'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.blame = 0|1; - sub feature_blame { my ($val) = git_get_project_config('blame', '--bool'); @@ -139,12 +193,6 @@ sub feature_blame { return $_[0]; } -# To disable system wide have in $GITWEB_CONFIG -# $feature{'snapshot'}{'default'} = [undef]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.snapshot = none|gzip|bzip2 - sub feature_snapshot { my ($ctype, $suffix, $command) = @_; @@ -168,12 +216,6 @@ sub gitweb_have_snapshot { return $have_snapshot; } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'pickaxe'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'pickaxe'}{'override'} = 1; -# and in project config gitweb.pickaxe = 0|1; - sub feature_pickaxe { my ($val) = git_get_project_config('pickaxe', '--bool'); @@ -186,6 +228,22 @@ sub feature_pickaxe { return ($_[0]); } +# checking HEAD file with -e is fragile if the repository was +# initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed +# and then pruned. +sub check_head_link { + my ($dir) = @_; + my $headfile = "$dir/HEAD"; + return ((-e $headfile) || + (-l $headfile && readlink($headfile) =~ /^refs\/heads\//)); +} + +sub check_export_ok { + my ($dir) = @_; + return (check_head_link($dir) && + (!$export_ok || -e "$dir/$export_ok")); +} + # rename detection options for git-diff and git-diff-tree # - default is '-M', with the cost proportional to # (number of removed files) * (number of new files). @@ -218,7 +276,7 @@ our $project = $cgi->param('p'); if (defined $project) { if (!validate_pathname($project) || !(-d "$projectroot/$project") || - !(-e "$projectroot/$project/HEAD") || + !check_head_link("$projectroot/$project") || ($export_ok && !(-e "$projectroot/$project/$export_ok")) || ($strict_export && !project_in_list($project))) { undef $project; @@ -285,6 +343,13 @@ if (defined $searchtext) { $searchtext = quotemeta $searchtext; } +our $searchtype = $cgi->param('st'); +if (defined $searchtype) { + if ($searchtype =~ m/[^a-z]/) { + die_error(undef, "Invalid searchtype parameter"); + } +} + # now read PATH_INFO and use it as alternative to parameters sub evaluate_path_info { return if defined $project; @@ -295,7 +360,7 @@ sub evaluate_path_info { # find which part of PATH_INFO is project $project = $path_info; $project =~ s,/+$,,; - while ($project && !-e "$projectroot/$project/HEAD") { + while ($project && !check_head_link("$projectroot/$project")) { $project =~ s,/*[^/]*$,,; } # validate project @@ -349,6 +414,7 @@ my %actions = ( "log" => \&git_log, "rss" => \&git_rss, "search" => \&git_search, + "search_help" => \&git_search_help, "shortlog" => \&git_shortlog, "summary" => \&git_summary, "tag" => \&git_tag, @@ -381,6 +447,10 @@ exit; sub href(%) { my %params = @_; + my $href = $my_uri; + + # XXX: Warning: If you touch this, check the search form for updating, + # too. my @mapping = ( project => "p", @@ -394,11 +464,25 @@ sub href(%) { page => "pg", order => "o", searchtext => "s", + searchtype => "st", ); my %mapping = @mapping; $params{'project'} = $project unless exists $params{'project'}; + my ($use_pathinfo) = gitweb_check_feature('pathinfo'); + if ($use_pathinfo) { + # use PATH_INFO for project name + $href .= "/$params{'project'}" if defined $params{'project'}; + delete $params{'project'}; + + # Summary just uses the project path URL + if (defined $params{'action'} && $params{'action'} eq 'summary') { + delete $params{'action'}; + } + } + + # now encode the parameters explicitly my @result = (); for (my $i = 0; $i < @mapping; $i += 2) { my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]); @@ -406,7 +490,9 @@ sub href(%) { push @result, $symbol . "=" . esc_param($params{$name}); } } - return "$my_uri?" . join(';', @result); + $href .= "?" . join(';', @result) if scalar @result; + + return $href; } @@ -828,8 +914,7 @@ sub git_get_projects_list { my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - if (-e "$projectroot/$subdir/HEAD" && (!$export_ok || - -e "$projectroot/$subdir/$export_ok")) { + if (check_export_ok("$projectroot/$subdir")) { push @list, { path => $subdir }; $File::Find::prune = 1; } @@ -850,8 +935,7 @@ sub git_get_projects_list { if (!defined $path) { next; } - if (-e "$projectroot/$path/HEAD" && (!$export_ok || - -e "$projectroot/$path/$export_ok")) { + if (check_export_ok("$projectroot/$path")) { my $pr = { path => $path, owner => to_utf8($owner), @@ -961,6 +1045,9 @@ sub parse_date { $date{'hour_local'} = $hour; $date{'minute_local'} = $min; $date{'tz_local'} = $tz; + $date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s", + 1900+$year, $mon+1, $mday, + $hour, $min, $sec, $tz); return %date; } @@ -999,6 +1086,24 @@ sub parse_tag { return %tag } +sub git_get_last_activity { + my ($path) = @_; + my $fd; + + $git_dir = "$projectroot/$path"; + open($fd, "-|", git_cmd(), 'for-each-ref', + '--format=%(refname) %(committer)', + '--sort=-committerdate', + 'refs/heads') or return; + my $most_recent = <$fd>; + close $fd or return; + if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) { + my $timestamp = $1; + my $age = time - $timestamp; + return ($age, age_string($age)); + } +} + sub parse_commit { my $commit_id = shift; my $commit_text = shift; @@ -1328,7 +1433,7 @@ sub git_header_html { my $status = shift || "200 OK"; my $expires = shift; - my $title = "$site_name git"; + my $title = "$site_name"; if (defined $project) { $title .= " - $project"; if (defined $action) { @@ -1366,8 +1471,17 @@ sub git_header_html { <meta name="generator" content="gitweb/$version git/$git_version"/> <meta name="robots" content="index, nofollow"/> <title>$title</title> -<link rel="stylesheet" type="text/css" href="$stylesheet"/> EOF +# print out each stylesheet that exist + if (defined $stylesheet) { +#provides backwards capability for those people who define style sheet in a config file + print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n"; + } else { + foreach my $stylesheet (@stylesheets) { + next unless $stylesheet; + print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n"; + } + } if (defined $project) { printf('<link rel="alternate" title="%s log" '. 'href="%s" type="application/rss+xml"/>'."\n", @@ -1385,8 +1499,15 @@ EOF } print "</head>\n" . - "<body>\n" . - "<div class=\"page_header\">\n" . + "<body>\n"; + + if (-f $site_header) { + open (my $fd, $site_header); + print <$fd>; + close $fd; + } + + print "<div class=\"page_header\">\n" . $cgi->a({-href => esc_url($logo_url), -title => $logo_label}, qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>)); @@ -1410,11 +1531,16 @@ EOF } $cgi->param("a", "search"); $cgi->param("h", $search_hash); + $cgi->param("p", $project); print $cgi->startform(-method => "get", -action => $my_uri) . "<div class=\"search\">\n" . $cgi->hidden(-name => "p") . "\n" . $cgi->hidden(-name => "a") . "\n" . $cgi->hidden(-name => "h") . "\n" . + $cgi->popup_menu(-name => 'st', -default => 'commit', + -values => ['commit', 'author', 'committer', 'pickaxe']) . + $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) . + " search:\n", $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . "</div>" . $cgi->end_form() . "\n"; @@ -1437,8 +1563,15 @@ sub git_footer_html { print $cgi->a({-href => href(project=>undef, action=>"project_index"), -class => "rss_logo"}, "TXT") . "\n"; } - print "</div>\n" . - "</body>\n" . + print "</div>\n" ; + + if (-f $site_footer) { + open (my $fd, $site_footer); + print <$fd>; + close $fd; + } + + print "</body>\n" . "</html>"; } @@ -1563,17 +1696,16 @@ sub git_print_page_path { my $type = shift; my $hb = shift; - if (!defined $name) { - print "<div class=\"page_path\">/</div>\n"; - } else { + + print "<div class=\"page_path\">"; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), + -title => 'tree root'}, "[$project]"); + print " / "; + if (defined $name) { my @dirname = split '/', $name; my $basename = pop @dirname; my $fullname = ''; - print "<div class=\"page_path\">"; - print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), - -title => 'tree root'}, "[$project]"); - print " / "; foreach my $dir (@dirname) { $fullname .= ($fullname ? '/' : '') . $dir; print $cgi->a({-href => href(action=>"tree", file_name=>$fullname, @@ -1589,11 +1721,12 @@ sub git_print_page_path { print $cgi->a({-href => href(action=>"tree", file_name=>$file_name, hash_base=>$hb), -title => $name}, esc_html($basename)); + print " / "; } else { print esc_html($basename); } - print "<br/></div>\n"; } + print "<br/></div>\n"; } # sub git_print_log (\@;%) { @@ -1646,15 +1779,6 @@ sub git_print_log ($;%) { } } -sub git_print_simplified_log { - my $log = shift; - my $remove_title = shift; - - git_print_log($log, - -final_empty_line=> 1, - -remove_title => $remove_title); -} - # print tree entry (row of git_tree), but without encompassing <tr> element sub git_print_tree_entry { my ($t, $basedir, $hash_base, $have_blame) = @_; @@ -1673,16 +1797,18 @@ sub git_print_tree_entry { file_name=>"$basedir$t->{'name'}", %base_key), -class => "list"}, esc_html($t->{'name'})) . "</td>\n"; print "<td class=\"link\">"; + print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, + file_name=>"$basedir$t->{'name'}", %base_key)}, + "blob"); if ($have_blame) { - print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, + print " | " . + $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key)}, "blame"); } if (defined $hash_base) { - if ($have_blame) { - print " | "; - } - print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, + print " | " . + $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")}, "history"); } @@ -1699,8 +1825,12 @@ sub git_print_tree_entry { esc_html($t->{'name'})); print "</td>\n"; print "<td class=\"link\">"; + print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, + file_name=>"$basedir$t->{'name'}", %base_key)}, + "tree"); if (defined $hash_base) { - print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, + print " | " . + $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$basedir$t->{'name'}")}, "history"); } @@ -1783,6 +1913,9 @@ sub git_difftree_body { print $cgi->a({-href => "#patch$patchno"}, "patch"); print " | "; } + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, + hash_base=>$parent, file_name=>$diff{'file'})}, + "blob") . " | "; print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, file_name=>$diff{'file'})}, "blame") . " | "; @@ -1828,6 +1961,9 @@ sub git_difftree_body { } print " | "; } + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'})}, + "blob") . " | "; print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, file_name=>$diff{'file'})}, "blame") . " | "; @@ -1868,6 +2004,9 @@ sub git_difftree_body { } print " | "; } + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, + hash_base=>$parent, file_name=>$diff{'from_file'})}, + "blob") . " | "; print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, file_name=>$diff{'from_file'})}, "blame") . " | "; @@ -2035,6 +2174,7 @@ sub git_shortlog_body { href(action=>"commit", hash=>$commit), $ref); print "</td>\n" . "<td class=\"link\">" . + $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree"); if (gitweb_have_snapshot()) { @@ -2229,16 +2369,11 @@ sub git_project_list { die_error(undef, "No projects found"); } foreach my $pr (@list) { - my $head = git_get_head_hash($pr->{'path'}); - if (!defined $head) { - next; - } - $git_dir = "$projectroot/$pr->{'path'}"; - my %co = parse_commit($head); - if (!%co) { + my (@aa) = git_get_last_activity($pr->{'path'}); + unless (@aa) { next; } - $pr->{'commit'} = \%co; + ($pr->{'age'}, $pr->{'age_string'}) = @aa; if (!defined $pr->{'descr'}) { my $descr = git_get_project_description($pr->{'path'}) || ""; $pr->{'descr'} = chop_str($descr, 25, 5); @@ -2288,7 +2423,7 @@ sub git_project_list { "</th>\n"; } if ($order eq "age") { - @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; + @projects = sort {$a->{'age'} <=> $b->{'age'}} @projects; print "<th>Last Change</th>\n"; } else { print "<th>" . @@ -2310,8 +2445,8 @@ sub git_project_list { -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" . "<td>" . esc_html($pr->{'descr'}) . "</td>\n" . "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n"; - print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" . - $pr->{'commit'}{'age_string'} . "</td>\n" . + print "<td class=\"". age_class($pr->{'age'}) . "\">" . + $pr->{'age_string'} . "</td>\n" . "<td class=\"link\">" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . @@ -2389,6 +2524,14 @@ sub git_summary { } print "</table>\n"; + if (-s "$projectroot/$project/README.html") { + if (open my $fd, "$projectroot/$project/README.html") { + print "<div class=\"title\">readme</div>\n"; + print $_ while (<$fd>); + close $fd; + } + } + open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17", git_get_head_hash($project) or die_error(undef, "Open git-rev-list failed"); @@ -2467,7 +2610,8 @@ sub git_blame2 { if ($ftype !~ "blob") { die_error("400 Bad Request", "Object is not a blob"); } - open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base) + open ($fd, "-|", git_cmd(), "blame", '-p', '--', + $file_name, $hash_base) or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = @@ -2491,25 +2635,52 @@ sub git_blame2 { <table class="blame"> <tr><th>Commit</th><th>Line</th><th>Data</th></tr> HTML - while (<$fd>) { - /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/; - my $full_rev = $1; + my %metainfo = (); + while (1) { + $_ = <$fd>; + last unless defined $_; + my ($full_rev, $orig_lineno, $lineno, $group_size) = + /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/; + if (!exists $metainfo{$full_rev}) { + $metainfo{$full_rev} = {}; + } + my $meta = $metainfo{$full_rev}; + while (<$fd>) { + last if (s/^\t//); + if (/^(\S+) (.*)$/) { + $meta->{$1} = $2; + } + } + my $data = $_; my $rev = substr($full_rev, 0, 8); - my $lineno = $2; - my $data = $3; - - if (!defined $last_rev) { - $last_rev = $full_rev; - } elsif ($last_rev ne $full_rev) { - $last_rev = $full_rev; + my $author = $meta->{'author'}; + my %date = parse_date($meta->{'author-time'}, + $meta->{'author-tz'}); + my $date = $date{'iso-tz'}; + if ($group_size) { $current_color = ++$current_color % $num_colors; } print "<tr class=\"$rev_color[$current_color]\">\n"; - print "<td class=\"sha1\">" . - $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, - esc_html($rev)) . "</td>\n"; - print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" . - esc_html($lineno) . "</a></td>\n"; + if ($group_size) { + print "<td class=\"sha1\""; + print " title=\"$author, $date\""; + print " rowspan=\"$group_size\"" if ($group_size > 1); + print ">"; + print $cgi->a({-href => href(action=>"commit", + hash=>$full_rev, + file_name=>$file_name)}, + esc_html($rev)); + print "</td>\n"; + } + my $blamed = href(action => 'blame', + file_name => $meta->{'filename'}, + hash_base => $full_rev); + print "<td class=\"linenr\">"; + print $cgi->a({ -href => "$blamed#l$orig_lineno", + -id => "l$lineno", + -class => "linenr" }, + esc_html($lineno)); + print "</td>"; print "<td class=\"pre\">" . esc_html($data) . "</td>\n"; print "</tr>\n"; } @@ -2819,6 +2990,30 @@ sub git_tree { print "<div class=\"page_body\">\n"; print "<table cellspacing=\"0\">\n"; my $alternate = 1; + # '..' (top directory) link if possible + if (defined $hash_base && + defined $file_name && $file_name =~ m![^/]+$!) { + if ($alternate) { + print "<tr class=\"dark\">\n"; + } else { + print "<tr class=\"light\">\n"; + } + $alternate ^= 1; + + my $up = $file_name; + $up =~ s!/?[^/]+$!!; + undef $up unless $up; + # based on git_print_tree_entry + print '<td class="mode">' . mode_str('040000') . "</td>\n"; + print '<td class="list">'; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, + file_name=>$up)}, + ".."); + print "</td>\n"; + print "<td class=\"link\"></td>\n"; + + print "</tr>\n"; + } foreach my $line (@entries) { my %t = parse_ls_tree_line($line, -z => 1); @@ -2920,7 +3115,7 @@ sub git_log { "</div>\n"; print "<div class=\"log_body\">\n"; - git_print_simplified_log($co{'comment'}); + git_print_log($co{'comment'}, -final_empty_line=> 1); print "</div>\n"; } git_footer_html(); @@ -2943,6 +3138,9 @@ sub git_commit { my @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-diff-tree failed"); + # filter out commit ID output + @difftree = grep(!/^[0-9a-fA-F]{40}$/, @difftree); + # non-textual hash id's can be cached my $expires; if ($hash =~ m/^[0-9a-fA-F]{40}$/) { @@ -3219,7 +3417,9 @@ sub git_commitdiff { while (chomp(my $line = <$fd>)) { # empty line ends raw part of diff-tree output last unless $line; - push @difftree, $line; + # filter out commit ID output + push @difftree, $line + unless $line =~ m/^[0-9a-fA-F]{40}$/; } } elsif ($format eq 'plain') { @@ -3251,9 +3451,11 @@ sub git_commitdiff { git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); git_print_authorship(\%co); print "<div class=\"page_body\">\n"; - print "<div class=\"log\">\n"; - git_print_simplified_log($co{'comment'}, 1); # skip title - print "</div>\n"; # class="log" + if (@{$co{'comment'}} > 1) { + print "<div class=\"log\">\n"; + git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1); + print "</div>\n"; # class="log" + } } elsif ($format eq 'plain') { my $refs = git_get_references("tags"); @@ -3385,18 +3587,8 @@ sub git_search { die_error(undef, "Unknown commit object"); } - my $commit_search = 1; - my $author_search = 0; - my $committer_search = 0; - my $pickaxe_search = 0; - if ($searchtext =~ s/^author\\://i) { - $author_search = 1; - } elsif ($searchtext =~ s/^committer\\://i) { - $committer_search = 1; - } elsif ($searchtext =~ s/^pickaxe\\://i) { - $commit_search = 0; - $pickaxe_search = 1; - + $searchtype ||= 'commit'; + if ($searchtype eq 'pickaxe') { # pickaxe may take all resources of your box and run for several minutes # with every query - so decide by yourself how public you make this feature my ($have_pickaxe) = gitweb_check_feature('pickaxe'); @@ -3404,23 +3596,24 @@ sub git_search { die_error('403 Permission denied', "Permission denied"); } } + git_header_html(); git_print_page_nav('','', $hash,$co{'tree'},$hash); git_print_header_div('commit', esc_html($co{'title'}), $hash); print "<table cellspacing=\"0\">\n"; my $alternate = 1; - if ($commit_search) { + if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') { $/ = "\0"; open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next; while (my $commit_text = <$fd>) { if (!grep m/$searchtext/i, $commit_text) { next; } - if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) { + if ($searchtype eq 'author' && !grep m/\nauthor .*$searchtext/i, $commit_text) { next; } - if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) { + if ($searchtype eq 'committer' && !grep m/\ncommitter .*$searchtext/i, $commit_text) { next; } my @commit_lines = split "\n", $commit_text; @@ -3462,7 +3655,7 @@ sub git_search { close $fd; } - if ($pickaxe_search) { + if ($searchtype eq 'pickaxe') { $/ = "\n"; my $git_command = git_cmd_str(); open my $fd, "-|", "$git_command rev-list $hash | " . @@ -3522,6 +3715,31 @@ sub git_search { git_footer_html(); } +sub git_search_help { + git_header_html(); + git_print_page_nav('','', $hash,$hash,$hash); + print <<EOT; +<dl> +<dt><b>commit</b></dt> +<dd>The commit messages and authorship information will be scanned for the given string.</dd> +<dt><b>author</b></dt> +<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd> +<dt><b>committer</b></dt> +<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd> +EOT + my ($have_pickaxe) = gitweb_check_feature('pickaxe'); + if ($have_pickaxe) { + print <<EOT; +<dt><b>pickaxe</b></dt> +<dd>All commits that caused the string to appear or disappear from any file (changes that +added, removed or "modified" the string) will be listed. This search can take a while and +takes a lot of strain on the server, so please use it wisely.</dd> +EOT + } + print "</dl>\n"; + git_footer_html(); +} + sub git_shortlog { my $head = git_get_head_hash($project); if (!defined $hash) { @@ -3630,7 +3848,7 @@ sub git_opml { <?xml version="1.0" encoding="utf-8"?> <opml version="1.0"> <head> - <title>$site_name Git OPML Export</title> + <title>$site_name OPML Export</title> </head> <body> <outline text="git RSS feeds"> |