X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/0afd1e32598f813f593cd79a67cfdaa35688d2fe3892fb17c3107ec10124d2ea..704f5e109dd0609873779ed12b868a9b3479d52fbf8539e58277d579cabe5c84:/gitweb.perl diff --git a/gitweb.perl b/gitweb.perl index 21e19f0..315eb11 100755 --- a/gitweb.perl +++ b/gitweb.perl @@ -39,7 +39,8 @@ 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++"; @@ -50,17 +51,19 @@ 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 a single stylesheet, which can be overridden in GITWEB_CONFIG. +our $stylesheet = undef; -# URI of GIT logo +# URI of GIT logo (72x27 size) our $logo = "++GITWEB_LOGO++"; # URI of GIT favicon, assumed to be image/png type our $favicon = "++GITWEB_FAVICON++"; -our $githelp_url = "http://git.or.cz/"; -our $githelp_label = "git homepage"; +# URI and label (title) of GIT logo link +#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/"; +#our $logo_label = "git documentation"; +our $logo_url = "http://git.or.cz/"; +our $logo_label = "git homepage"; # source of projects list our $projects_list = "++GITWEB_LIST++"; @@ -74,7 +77,7 @@ our $strict_export = "++GITWEB_STRICT_EXPORT++"; # list of git base URLs used for URL to where fetch project from, # i.e. full URL is "$git_base_url/$project" -our @git_base_url_list = ("++GITWEB_BASE_URL++"); +our @git_base_url_list = grep { $_ ne '' } ("++GITWEB_BASE_URL++"); # default blob_plain mimetype and default charset for text/plain blob our $default_blob_plain_mimetype = 'text/plain'; @@ -98,25 +101,81 @@ our %feature = ( # # use gitweb_check_feature() to check if 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]}, + + # Make gitweb consider projects in project root subdirectories + # to be forks of existing projects. Given project $projname.git, + # projects matching $projname/*.git will not be shown in the main + # projects list, instead a '+' mark will be added to $projname + # there and a 'forks' view will be enabled for the project, listing + # all the forks. This feature is supported only if project list + # is taken from a directory, not file. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'forks'}{'default'} = [1]; + # Project specific override is not supported. + 'forks' => { + 'override' => 0, + 'default' => [0]}, ); sub gitweb_check_feature { @@ -134,12 +193,6 @@ sub gitweb_check_feature { 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'); @@ -152,12 +205,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) = @_; @@ -181,12 +228,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'); @@ -314,6 +355,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; @@ -373,11 +421,13 @@ my %actions = ( "commitdiff" => \&git_commitdiff, "commitdiff_plain" => \&git_commitdiff_plain, "commit" => \&git_commit, + "forks" => \&git_forks, "heads" => \&git_heads, "history" => \&git_history, "log" => \&git_log, "rss" => \&git_rss, "search" => \&git_search, + "search_help" => \&git_search_help, "shortlog" => \&git_shortlog, "summary" => \&git_summary, "tag" => \&git_tag, @@ -412,6 +462,9 @@ 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", action => "a", @@ -424,6 +477,7 @@ sub href(%) { page => "pg", order => "o", searchtext => "s", + searchtype => "st", ); my %mapping = @mapping; @@ -517,12 +571,17 @@ sub esc_url { } # replace invalid utf8 character with SUBSTITUTION sequence -sub esc_html { +sub esc_html ($;%) { my $str = shift; + my %opts = @_; + $str = to_utf8($str); $str = escapeHTML($str); $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) $str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1) + if ($opts{'-nbsp'}) { + $str =~ s/ / /g; + } return $str; } @@ -747,7 +806,7 @@ sub format_diff_line { $diff_class = " incomplete"; } $line = untabify($line); - return "
" . esc_html($line) . "
\n"; + return "
" . esc_html($line, -nbsp=>1) . "
\n"; } ## ---------------------------------------------------------------------- @@ -823,7 +882,7 @@ sub git_get_hash_by_path { close $fd or return undef; #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/; if (defined $type && $type ne $2) { # type doesn't match return undef; @@ -855,13 +914,21 @@ sub git_get_project_url_list { } sub git_get_projects_list { + my ($filter) = @_; my @list; + $filter ||= ''; + $filter =~ s/\.git$//; + if (-d $projects_list) { # search in directory - my $dir = $projects_list; + my $dir = $projects_list . ($filter ? "/$filter" : ''); + # remove the trailing "/" + $dir =~ s!/+$!!; my $pfxlen = length("$dir"); + my ($check_forks) = gitweb_check_feature('forks'); + File::Find::find({ follow_fast => 1, # follow symbolic links dangling_symlinks => 0, # ignore dangling symlinks, silently @@ -873,8 +940,10 @@ sub git_get_projects_list { my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - if (check_export_ok("$projectroot/$subdir")) { - push @list, { path => $subdir }; + if ($check_forks and $subdir =~ m#/.#) { + $File::Find::prune = 1; + } elsif (check_export_ok("$projectroot/$filter/$subdir")) { + push @list, { path => ($filter ? "$filter/" : '') . $subdir }; $File::Find::prune = 1; } }, @@ -894,6 +963,17 @@ 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; + } + my $sfx = substr($path, length($filter)); + if ($sfx !~ /^\/.*\.git$/) { + next; + } + } if (check_export_ok("$projectroot/$path")) { my $pr = { path => $path, @@ -939,6 +1019,24 @@ sub git_get_project_owner { return $owner; } +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 git_get_references { my $type = shift || ""; my %refs; @@ -1055,12 +1153,13 @@ sub parse_commit { if (defined $commit_text) { @commit_lines = @$commit_text; } else { - $/ = "\0"; - open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id + local $/ = "\0"; + open my $fd, "-|", git_cmd(), "rev-list", + "--header", "--parents", "--max-count=1", + $commit_id, "--" or return; @commit_lines = split '\n', <$fd>; close $fd or return; - $/ = "\n"; pop @commit_lines; } my $header = shift @commit_lines; @@ -1221,7 +1320,7 @@ sub parse_ls_tree_line ($;%) { my %res; #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s; $res{'mode'} = $1; $res{'type'} = $2; @@ -1238,47 +1337,88 @@ sub parse_ls_tree_line ($;%) { ## ...................................................................... ## parse to array of hashes functions -sub git_get_refs_list { - my $type = shift || ""; - my %refs; - my @reflist; +sub git_get_heads_list { + my $limit = shift; + my @headslist; - my @refs; - open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/" + open my $fd, '-|', git_cmd(), 'for-each-ref', + ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate', + '--format=%(objectname) %(refname) %(subject)%00%(committer)', + 'refs/heads' or return; while (my $line = <$fd>) { - chomp $line; - if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) { - if (defined $refs{$1}) { - push @{$refs{$1}}, $2; - } else { - $refs{$1} = [ $2 ]; - } + my %ref_item; - if (! $4) { # unpeeled, direct reference - push @refs, { hash => $1, name => $3 }; # without type - } elsif ($3 eq $refs[-1]{'name'}) { - # most likely a tag is followed by its peeled - # (deref) one, and when that happens we know the - # previous one was of type 'tag'. - $refs[-1]{'type'} = "tag"; - } + chomp $line; + my ($refinfo, $committerinfo) = split(/\0/, $line); + my ($hash, $name, $title) = split(' ', $refinfo, 3); + my ($committer, $epoch, $tz) = + ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); + $name =~ s!^refs/heads/!!; + + $ref_item{'name'} = $name; + $ref_item{'id'} = $hash; + $ref_item{'title'} = $title || '(no commit message)'; + $ref_item{'epoch'} = $epoch; + if ($epoch) { + $ref_item{'age'} = age_string(time - $ref_item{'epoch'}); + } else { + $ref_item{'age'} = "unknown"; } + + push @headslist, \%ref_item; } close $fd; - foreach my $ref (@refs) { - my $ref_file = $ref->{'name'}; - my $ref_id = $ref->{'hash'}; + return wantarray ? @headslist : \@headslist; +} - my $type = $ref->{'type'} || git_get_type($ref_id) || next; - my %ref_item = parse_ref($ref_file, $ref_id, $type); +sub git_get_tags_list { + my $limit = shift; + my @tagslist; - push @reflist, \%ref_item; + open my $fd, '-|', git_cmd(), 'for-each-ref', + ($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate', + '--format=%(objectname) %(objecttype) %(refname) '. + '%(*objectname) %(*objecttype) %(subject)%00%(creator)', + 'refs/tags' + or return; + while (my $line = <$fd>) { + my %ref_item; + + chomp $line; + my ($refinfo, $creatorinfo) = split(/\0/, $line); + my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6); + my ($creator, $epoch, $tz) = + ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/); + $name =~ s!^refs/tags/!!; + + $ref_item{'type'} = $type; + $ref_item{'id'} = $id; + $ref_item{'name'} = $name; + if ($type eq "tag") { + $ref_item{'subject'} = $title; + $ref_item{'reftype'} = $reftype; + $ref_item{'refid'} = $refid; + } else { + $ref_item{'reftype'} = $type; + $ref_item{'refid'} = $id; + } + + if ($type eq "tag" || $type eq "commit") { + $ref_item{'epoch'} = $epoch; + if ($epoch) { + $ref_item{'age'} = age_string(time - $ref_item{'epoch'}); + } else { + $ref_item{'age'} = "unknown"; + } + } + + push @tagslist, \%ref_item; } - # sort refs by age - @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; - return (\@reflist, \%refs); + close $fd; + + return wantarray ? @tagslist : \@tagslist; } ## ---------------------------------------------------------------------- @@ -1375,7 +1515,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) { @@ -1450,11 +1590,9 @@ EOF } print "
\n" . - "" . - "\"git\"" . - "\n"; + $cgi->a({-href => esc_url($logo_url), + -title => $logo_label}, + 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)); @@ -1475,11 +1613,16 @@ EOF } $cgi->param("a", "search"); $cgi->param("h", $search_hash); + $cgi->param("p", $project); print $cgi->startform(-method => "get", -action => $my_uri) . "
\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" . "
" . $cgi->end_form() . "\n"; @@ -1635,17 +1778,16 @@ sub git_print_page_path { my $type = shift; my $hb = shift; - if (!defined $name) { - print "
/
\n"; - } else { + + print "
"; + 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 "
"; - 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, @@ -1661,11 +1803,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 "
\n"; } + print "
\n"; } # sub git_print_log (\@;%) { @@ -1718,15 +1861,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 element sub git_print_tree_entry { my ($t, $basedir, $hash_base, $have_blame) = @_; @@ -1742,26 +1876,28 @@ sub git_print_tree_entry { if ($t->{'type'} eq "blob") { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key), - -class => "list"}, esc_html($t->{'name'})) . "\n"; + file_name=>"$basedir$t->{'name'}", %base_key), + -class => "list"}, esc_html($t->{'name'})) . "\n"; print ""; + 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'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "blame"); + 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"); } print " | " . $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base, - file_name=>"$basedir$t->{'name'}")}, - "raw"); + file_name=>"$basedir$t->{'name'}")}, + "raw"); print "\n"; } elsif ($t->{'type'} eq "tree") { @@ -1771,8 +1907,12 @@ sub git_print_tree_entry { esc_html($t->{'name'})); print "\n"; print ""; + 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"); } @@ -1829,7 +1969,7 @@ sub git_difftree_body { print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chng\n"; print ""; @@ -1855,12 +1995,15 @@ 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") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed @@ -1881,31 +2024,34 @@ sub git_difftree_body { } print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, - hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chnge\n"; print ""; - if ($diff{'to_id'} ne $diff{'from_id'}) { # modified - if ($action eq 'commitdiff') { - # link to patch - $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch"); - } else { - print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'file'})}, - "diff"); - } - print " | "; + if ($action eq 'commitdiff') { + # link to patch + $patchno++; + print $cgi->a({-href => "#patch$patchno"}, "patch") . + " | "; + } elsif ($diff{'to_id'} ne $diff{'from_id'}) { + # "commit" view and modified file (not onlu mode changed) + print $cgi->a({-href => href(action=>"blobdiff", + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'file'})}, + "diff") . + " | "; } + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'})}, + "blob") . " | "; print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, - file_name=>$diff{'file'})}, - "blame") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$hash, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied @@ -1926,26 +2072,29 @@ sub git_difftree_body { -class => "list"}, esc_html($diff{'from_file'})) . " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]\n" . ""; - if ($diff{'to_id'} ne $diff{'from_id'}) { - if ($action eq 'commitdiff') { - # link to patch - $patchno++; - print $cgi->a({-href => "#patch$patchno"}, "patch"); - } else { - print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, - "diff"); - } - print " | "; + if ($action eq 'commitdiff') { + # link to patch + $patchno++; + print $cgi->a({-href => "#patch$patchno"}, "patch") . + " | "; + } elsif ($diff{'to_id'} ne $diff{'from_id'}) { + # "commit" view and modified file (not only pure rename or copy) + print $cgi->a({-href => href(action=>"blobdiff", + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, + "diff") . + " | "; } + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'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") . " | "; + file_name=>$diff{'from_file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'from_file'})}, - "history"); + file_name=>$diff{'from_file'})}, + "history"); print "\n"; } # we should not encounter Unmerged (U) or Unknown (X) status @@ -1986,13 +2135,6 @@ sub git_patchset_body { } $patch_idx++; - # for now, no extended header, hence we skip empty patches - # companion to next LINE if $in_header; - if ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) { # no change - $in_header = 1; - next LINE; - } - if ($diffinfo->{'status'} eq "A") { # added print "
" . file_type($diffinfo->{'to_mode'}) . ":" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash, @@ -2079,6 +2221,131 @@ sub git_patchset_body { # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +sub git_project_list_body { + my ($projlist, $order, $from, $to, $extra, $no_header) = @_; + + my ($check_forks) = gitweb_check_feature('forks'); + + my @projects; + foreach my $pr (@$projlist) { + my (@aa) = git_get_last_activity($pr->{'path'}); + unless (@aa) { + next; + } + ($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); + } + if (!defined $pr->{'owner'}) { + $pr->{'owner'} = get_file_owner("$projectroot/$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; + } + } + push @projects, $pr; + } + + $order ||= "project"; + $from = 0 unless defined $from; + $to = $#projects if (!defined $to || $#projects < $to); + + print "\n"; + unless ($no_header) { + print "\n"; + if ($check_forks) { + print "\n"; + } + if ($order eq "project") { + @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "descr") { + @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "owner") { + @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "age") { + @projects = sort {$a->{'age'} <=> $b->{'age'}} @projects; + print "\n"; + } else { + print "\n"; + } + print "\n" . + "\n"; + } + my $alternate = 1; + for (my $i = $from; $i <= $to; $i++) { + my $pr = $projects[$i]; + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + if ($check_forks) { + print "\n"; + } + print "\n" . + "\n" . + "\n"; + print "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n"; + if ($check_forks) { + print "\n"; + } + print "\n" . + "\n"; + } + print "
Project" . + $cgi->a({-href => href(project=>undef, order=>'project'), + -class => "header"}, "Project") . + "Description" . + $cgi->a({-href => href(project=>undef, order=>'descr'), + -class => "header"}, "Description") . + "Owner" . + $cgi->a({-href => href(project=>undef, order=>'owner'), + -class => "header"}, "Owner") . + "Last Change" . + $cgi->a({-href => href(project=>undef, order=>'age'), + -class => "header"}, "Last Change") . + "
"; + if ($pr->{'forks'}) { + print "\n"; + print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+"); + } + print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), + -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'age'}) . "\">" . + $pr->{'age_string'} . "" . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . + $cgi->a({-href => '/git-browser/by-commit.html?r='.$pr->{'path'}}, "graphiclog") . " | " . + $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") : '') . + "
$extra
\n"; +} + sub git_shortlog_body { # uses global variable $project my ($revlist, $from, $to, $refs, $extra) = @_; @@ -2107,6 +2374,7 @@ sub git_shortlog_body { href(action=>"commit", hash=>$commit), $ref); print "\n" . "" . + $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()) { @@ -2198,8 +2466,7 @@ sub git_tags_body { for (my $i = $from; $i <= $to; $i++) { my $entry = $taglist->[$i]; my %tag = %$entry; - my $comment_lines = $tag{'comment'}; - my $comment = shift @$comment_lines; + my $comment = $tag{'subject'}; my $comment_short; if (defined $comment) { $comment_short = chop_str($comment, 30, 5); @@ -2232,7 +2499,7 @@ sub git_tags_body { $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . - " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'refid'})}, "log"); + " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log"); } elsif ($tag{'reftype'} eq "blob") { print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw"); } @@ -2257,23 +2524,23 @@ sub git_heads_body { my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my $entry = $headlist->[$i]; - my %tag = %$entry; - my $curr = $tag{'id'} eq $head; + my %ref = %$entry; + my $curr = $ref{'id'} eq $head; if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; - print "$tag{'age'}\n" . - ($tag{'id'} eq $head ? "" : "") . - $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'}), - -class => "list name"},esc_html($tag{'name'})) . + print "$ref{'age'}\n" . + ($curr ? "" : "") . + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}), + -class => "list name"},esc_html($ref{'name'})) . "\n" . "" . - $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " . - $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, "tree") . + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " . + $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " . + $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") . "\n" . ""; } @@ -2296,30 +2563,9 @@ sub git_project_list { } my @list = git_get_projects_list(); - my @projects; if (!@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) { - next; - } - $pr->{'commit'} = \%co; - if (!defined $pr->{'descr'}) { - my $descr = git_get_project_description($pr->{'path'}) || ""; - $pr->{'descr'} = chop_str($descr, 25, 5); - } - if (!defined $pr->{'owner'}) { - $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || ""; - } - push @projects, $pr; - } git_header_html(); if (-f $home_text) { @@ -2329,75 +2575,30 @@ sub git_project_list { close $fd; print "
\n"; } - print "\n" . - "\n"; - $order ||= "project"; - if ($order eq "project") { - @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "descr") { - @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "owner") { - @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "age") { - @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; - print "\n"; - } else { - print "\n"; + git_project_list_body(\@list, $order); + git_footer_html(); +} + +sub git_forks { + my $order = $cgi->param('o'); + if (defined $order && $order !~ m/project|descr|owner|age/) { + die_error(undef, "Unknown order parameter"); } - print "\n" . - "\n"; - my $alternate = 1; - foreach my $pr (@projects) { - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n"; - print "\n" . - "\n" . - "\n"; + + my @list = git_get_projects_list($project); + if (!@list) { + die_error(undef, "No forks found"); } - print "
Project" . - $cgi->a({-href => href(project=>undef, order=>'project'), - -class => "header"}, "Project") . - "Description" . - $cgi->a({-href => href(project=>undef, order=>'descr'), - -class => "header"}, "Description") . - "Owner" . - $cgi->a({-href => href(project=>undef, order=>'owner'), - -class => "header"}, "Owner") . - "Last Change" . - $cgi->a({-href => href(project=>undef, order=>'age'), - -class => "header"}, "Last Change") . - "
" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), - -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . - $pr->{'commit'}{'age_string'} . "" . - $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") . - "
\n"; + + git_header_html(); + git_print_page_nav('',''); + git_print_header_div('summary', "$project forks"); + git_project_list_body(\@list, $order); git_footer_html(); } sub git_project_index { - my @projects = git_get_projects_list(); + my @projects = git_get_projects_list($project); print $cgi->header( -type => 'text/plain', @@ -2428,17 +2629,14 @@ sub git_summary { my $owner = git_get_project_owner($project); - my ($reflist, $refs) = git_get_refs_list(); + my $refs = git_get_references(); + my @taglist = git_get_tags_list(15); + my @headlist = git_get_heads_list(15); + my @forklist; + my ($check_forks) = gitweb_check_feature('forks'); - my @taglist; - my @headlist; - foreach my $ref (@$reflist) { - if ($ref->{'name'} =~ s!^heads/!!) { - push @headlist, $ref; - } else { - $ref->{'name'} =~ s!^tags/!!; - push @taglist, $ref; - } + if ($check_forks) { + @forklist = git_get_projects_list($project); } git_header_html(); @@ -2461,8 +2659,16 @@ sub git_summary { } print "\n"; + if (-s "$projectroot/$project/README.html") { + if (open my $fd, "$projectroot/$project/README.html") { + print "
readme
\n"; + print $_ while (<$fd>); + close $fd; + } + } + open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17", - git_get_head_hash($project) + git_get_head_hash($project), "--" or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; @@ -2482,6 +2688,13 @@ sub git_summary { $cgi->a({-href => href(action=>"heads")}, "...")); } + if (@forklist) { + git_print_header_div('forks'); + git_project_list_body(\@forklist, undef, 0, 15, + $cgi->a({-href => href(action=>"forks")}, "..."), + 'noheader'); + } + git_footer_html(); } @@ -2539,7 +2752,7 @@ sub git_blame2 { if ($ftype !~ "blob") { die_error("400 Bad Request", "Object is not a blob"); } - open ($fd, "-|", git_cmd(), "blame", '--porcelain', '--', + open ($fd, "-|", git_cmd(), "blame", '-p', '--', $file_name, $hash_base) or die_error(undef, "Open git-blame failed"); git_header_html(); @@ -2568,7 +2781,7 @@ HTML while (1) { $_ = <$fd>; last unless defined $_; - my ($full_rev, $lineno, $orig_lineno, $group_size) = + my ($full_rev, $orig_lineno, $lineno, $group_size) = /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/; if (!exists $metainfo{$full_rev}) { $metainfo{$full_rev} = {}; @@ -2592,7 +2805,7 @@ HTML print "\n"; if ($group_size) { print " 1); print ">"; print $cgi->a({-href => href(action=>"commit", @@ -2723,9 +2936,9 @@ sub git_tags { git_print_page_nav('','', $head,undef,$head); git_print_header_div('summary', $project); - my ($taglist) = git_get_refs_list("tags"); - if (@$taglist) { - git_tags_body($taglist); + my @tagslist = git_get_tags_list(); + if (@tagslist) { + git_tags_body(\@tagslist); } git_footer_html(); } @@ -2736,9 +2949,9 @@ sub git_heads { git_print_page_nav('','', $head,undef,$head); git_print_header_div('summary', $project); - my ($headlist) = git_get_refs_list("heads"); - if (@$headlist) { - git_heads_body($headlist, $head); + my @headslist = git_get_heads_list(); + if (@headslist) { + git_heads_body(\@headslist, $head); } git_footer_html(); } @@ -2851,7 +3064,7 @@ sub git_blob { $nr++; $line = untabify($line); printf "
%4i %s
\n", - $nr, $nr, $nr, esc_html($line); + $nr, $nr, $nr, esc_html($line, -nbsp=>1); } close $fd or print "Reading blob failed.\n"; @@ -2882,7 +3095,7 @@ sub git_tree { my $refs = git_get_references(); my $ref = format_ref_marker($refs, $hash_base); git_header_html(); - my $base = ""; + my $basedir = ''; my ($have_blame) = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { my @views_nav = (); @@ -2899,7 +3112,7 @@ sub git_tree { # FIXME: Should be available when we have no hash base as well. push @views_nav, $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, - "snapshot"); + "snapshot"); } git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav)); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); @@ -2910,12 +3123,39 @@ sub git_tree { print "
$hash
\n"; } if (defined $file_name) { - $base = esc_html("$file_name/"); + $basedir = $file_name; + if ($basedir ne '' && substr($basedir, -1) ne '/') { + $basedir .= '/'; + } } git_print_page_path($file_name, 'tree', $hash_base); print "
\n"; print "\n"; my $alternate = 1; + # '..' (top directory) link if possible + if (defined $hash_base && + defined $file_name && $file_name =~ m![^/]+$!) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + my $up = $file_name; + $up =~ s!/?[^/]+$!!; + undef $up unless $up; + # based on git_print_tree_entry + print '\n"; + print '\n"; + print "\n"; + + print "\n"; + } foreach my $line (@entries) { my %t = parse_ls_tree_line($line, -z => 1); @@ -2926,7 +3166,7 @@ sub git_tree { } $alternate ^= 1; - git_print_tree_entry(\%t, $base, $hash_base, $have_blame); + git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame); print "\n"; } @@ -2978,7 +3218,7 @@ sub git_log { my $refs = git_get_references(); my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash + open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--" or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; @@ -3017,7 +3257,7 @@ sub git_log { "\n"; print "
\n"; - git_print_simplified_log($co{'comment'}); + git_print_log($co{'comment'}, -final_empty_line=> 1); print "
\n"; } git_footer_html(); @@ -3035,7 +3275,8 @@ sub git_commit { if (!defined $parent) { $parent = "--root"; } - open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $parent, $hash + open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id", + @diff_opts, $parent, $hash, "--" or die_error(undef, "Open git-diff-tree failed"); my @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-diff-tree failed"); @@ -3057,7 +3298,7 @@ sub git_commit { "blame"); } git_header_html(undef, $expires); - git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', + git_print_page_nav('commit', '', $hash, $co{'tree'}, $hash, join (' | ', @views_nav)); @@ -3140,7 +3381,8 @@ sub git_blobdiff { if (defined $hash_base && defined $hash_parent_base) { if (defined $file_name) { # read raw output - open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base, + open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, + $hash_parent_base, $hash_base, "--", $file_name or die_error(undef, "Open git-diff-tree failed"); @difftree = map { chomp; $_ } <$fd>; @@ -3154,7 +3396,8 @@ sub git_blobdiff { # try to find filename from $hash # read filtered raw output - open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base + open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, + $hash_parent_base, $hash_base, "--" or die_error(undef, "Open git-diff-tree failed"); @difftree = # ':100644 100644 03b21826... 3b93d5e7... M ls-files.c' @@ -3224,7 +3467,8 @@ sub git_blobdiff { } # open patch output - open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts, $hash_parent, $hash + open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts, + $hash_parent, $hash, "--" or die_error(undef, "Open git-diff failed"); } else { die_error('404 Not Found', "Missing one of the blob diff parameters") @@ -3301,6 +3545,51 @@ sub git_commitdiff { if (!%co) { die_error(undef, "Unknown commit object"); } + + # we need to prepare $formats_nav before any parameter munging + my $formats_nav; + if ($format eq 'html') { + $formats_nav = + $cgi->a({-href => href(action=>"commitdiff_plain", + hash=>$hash, hash_parent=>$hash_parent)}, + "raw"); + + if (defined $hash_parent) { + # commitdiff with two commits given + my $hash_parent_short = $hash_parent; + if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) { + $hash_parent_short = substr($hash_parent, 0, 7); + } + $formats_nav .= + ' (from: ' . + $cgi->a({-href => href(action=>"commitdiff", + hash=>$hash_parent)}, + esc_html($hash_parent_short)) . + ')'; + } elsif (!$co{'parent'}) { + # --root commitdiff + $formats_nav .= ' (initial)'; + } elsif (scalar @{$co{'parents'}} == 1) { + # single parent commit + $formats_nav .= + ' (parent: ' . + $cgi->a({-href => href(action=>"commitdiff", + hash=>$co{'parent'})}, + esc_html(substr($co{'parent'}, 0, 7))) . + ')'; + } else { + # merge commit + $formats_nav .= + ' (merge: ' . + join(' ', map { + $cgi->a({-href => href(action=>"commitdiff", + hash=>$_)}, + esc_html(substr($_, 0, 7))); + } @{$co{'parents'}} ) . + ')'; + } + } + if (!defined $hash_parent) { $hash_parent = $co{'parent'} || '--root'; } @@ -3310,7 +3599,8 @@ sub git_commitdiff { my @difftree; if ($format eq 'html') { open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, - "--patch-with-raw", "--full-index", $hash_parent, $hash + "--no-commit-id", "--patch-with-raw", "--full-index", + $hash_parent, $hash, "--" or die_error(undef, "Open git-diff-tree failed"); while (chomp(my $line = <$fd>)) { @@ -3321,7 +3611,7 @@ sub git_commitdiff { } elsif ($format eq 'plain') { open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, - '-p', $hash_parent, $hash + '-p', $hash_parent, $hash, "--" or die_error(undef, "Open git-diff-tree failed"); } else { @@ -3338,19 +3628,17 @@ sub git_commitdiff { if ($format eq 'html') { my $refs = git_get_references(); my $ref = format_ref_marker($refs, $co{'id'}); - my $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", - hash=>$hash, hash_parent=>$hash_parent)}, - "raw"); git_header_html(undef, $expires); git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); git_print_authorship(\%co); print "
\n"; - print "
\n"; - git_print_simplified_log($co{'comment'}, 1); # skip title - print "
\n"; # class="log" + if (@{$co{'comment'}} > 1) { + print "
\n"; + git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1); + print "
\n"; # class="log" + } } elsif ($format eq 'plain') { my $refs = git_get_references("tags"); @@ -3482,18 +3770,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'); @@ -3501,23 +3779,26 @@ 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 "
' . mode_str('040000') . "'; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, + file_name=>$up)}, + ".."); + print "
\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; + 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; @@ -3559,7 +3840,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 | " . @@ -3619,6 +3900,31 @@ sub git_search { git_footer_html(); } +sub git_search_help { + git_header_html(); + git_print_page_nav('','', $hash,$hash,$hash); + print < +
commit
+
The commit messages and authorship information will be scanned for the given string.
+
author
+
Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.
+
committer
+
Name and e-mail of the committer and date of commit will be scanned for the given string.
+EOT + my ($have_pickaxe) = gitweb_check_feature('pickaxe'); + if ($have_pickaxe) { + print <pickaxe +
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.
+EOT + } + print "\n"; + git_footer_html(); +} + sub git_shortlog { my $head = git_get_head_hash($project); if (!defined $hash) { @@ -3630,7 +3936,7 @@ sub git_shortlog { my $refs = git_get_references(); my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash + open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--" or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; @@ -3658,7 +3964,8 @@ sub git_shortlog { sub git_rss { # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ - open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150", git_get_head_hash($project) + open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150", + git_get_head_hash($project), "--" or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-rev-list failed"); @@ -3682,7 +3989,7 @@ XML } my %cd = parse_date($co{'committer_epoch'}); open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, - $co{'parent'}, $co{'id'} + $co{'parent'}, $co{'id'}, "--" or next; my @difftree = map { chomp; $_ } <$fd>; close $fd @@ -3727,7 +4034,7 @@ sub git_opml { - $site_name Git OPML Export + $site_name OPML Export