From e4b48eaab724d9fd5941c6a683a34ee38a864897 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 7 Sep 2009 14:40:00 +0200 Subject: gitweb: Add 'show-sizes' feature to show blob sizes in tree view Add support for 'show-sizes' feature to show (in separate column, between mode and filename) the size of blobs (files) in the 'tree' view. It passes '-l' option to "git ls-tree" invocation. For the 'tree' and 'commit' (submodule) entries, '-' is shown in place of size; for generated '..' "up directory" entry nothing is shown. The 'show-sizes' feature is enabled by default. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 69 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 15 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 24b219310a..7b1c60e2c1 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -297,6 +297,19 @@ our %feature = ( 'override' => 0, 'default' => [1]}, + # Enable showing size of blobs in a 'tree' view, in a separate + # column, similar to what 'ls -l' does. This cost a bit of IO. + + # To disable system wide have in $GITWEB_CONFIG + # $feature{'show-sizes'}{'default'} = [0]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'show-sizes'}{'override'} = 1; + # and in project config gitweb.showsizes = 0|1; + 'show-sizes' => { + 'sub' => sub { feature_bool('showsizes', @_) }, + '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 @@ -2764,16 +2777,31 @@ sub parse_ls_tree_line { my %opts = @_; my %res; - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s; + if ($opts{'-l'}) { + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa 16717 panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s; - $res{'mode'} = $1; - $res{'type'} = $2; - $res{'hash'} = $3; - if ($opts{'-z'}) { - $res{'name'} = $4; + $res{'mode'} = $1; + $res{'type'} = $2; + $res{'hash'} = $3; + $res{'size'} = $4; + if ($opts{'-z'}) { + $res{'name'} = $5; + } else { + $res{'name'} = unquote($5); + } } else { - $res{'name'} = unquote($4); + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s; + + $res{'mode'} = $1; + $res{'type'} = $2; + $res{'hash'} = $3; + if ($opts{'-z'}) { + $res{'name'} = $4; + } else { + $res{'name'} = unquote($4); + } } return wantarray ? %res : \%res; @@ -3564,6 +3592,9 @@ sub git_print_tree_entry { # and link is the action links of the entry. print "" . mode_str($t->{'mode'}) . "\n"; + if (exists $t->{'size'}) { + print "$t->{'size'}\n"; + } if ($t->{'type'} eq "blob") { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, @@ -3609,12 +3640,14 @@ sub git_print_tree_entry { } elsif ($t->{'type'} eq "tree") { print ""; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, + file_name=>"$basedir$t->{'name'}", + %base_key)}, esc_path($t->{'name'})); print "\n"; print ""; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, + file_name=>"$basedir$t->{'name'}", + %base_key)}, "tree"); if (defined $hash_base) { print " | " . @@ -5088,10 +5121,14 @@ sub git_tree { } die_error(404, "No such tree") unless defined($hash); + my $show_sizes = gitweb_check_feature('show-sizes'); + my $have_blame = gitweb_check_feature('blame'); + my @entries = (); { local $/ = "\0"; - open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash + open my $fd, "-|", git_cmd(), "ls-tree", '-z', + ($show_sizes ? '-l' : ()), @extra_options, $hash or die_error(500, "Open git-ls-tree failed"); @entries = map { chomp; $_ } <$fd>; close $fd @@ -5102,7 +5139,6 @@ sub git_tree { my $ref = format_ref_marker($refs, $hash_base); git_header_html(); my $basedir = ''; - my $have_blame = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { my @views_nav = (); if (defined $file_name) { @@ -5118,7 +5154,8 @@ sub git_tree { # FIXME: Should be available when we have no hash base as well. push @views_nav, $snapshot_links; } - git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav)); + git_print_page_nav('tree','', $hash_base, undef, undef, + join(' | ', @views_nav)); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); } else { undef $hash_base; @@ -5151,8 +5188,10 @@ sub git_tree { undef $up unless $up; # based on git_print_tree_entry print '' . mode_str('040000') . "\n"; + print ' '."\n" if $show_sizes; print ''; - print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, + print $cgi->a({-href => href(action=>"tree", + hash_base=>$hash_base, file_name=>$up)}, ".."); print "\n"; @@ -5161,7 +5200,7 @@ sub git_tree { print "\n"; } foreach my $line (@entries) { - my %t = parse_ls_tree_line($line, -z => 1); + my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes); if ($alternate) { print "\n"; -- cgit v1.3 From fdb0c36e903d13c184f9a465035c75565c5c072a Mon Sep 17 00:00:00 2001 From: Mark Rada Date: Sat, 26 Sep 2009 13:46:08 -0400 Subject: gitweb: check given hash before trying to create snapshot Makes things nicer in cases when you hand craft the snapshot URL but make a typo in defining the hash variable (e.g. netx instead of next); you will now get an error message instead of a broken tarball. Tests for t9501 are included to demonstrate added functionality. Signed-off-by: Mark Rada Signed-off-by: Shawn O. Pearce --- gitweb/gitweb.perl | 7 ++++-- t/t9501-gitweb-standalone-http-status.sh | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 24b219310a..8d4a2ae600 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5196,8 +5196,11 @@ sub git_snapshot { die_error(403, "Unsupported snapshot format"); } - if (!defined $hash) { - $hash = git_get_head_hash($project); + my $type = git_get_type("$hash^{}"); + if (!$type) { + die_error(404, 'Object does not exist'); + } elsif ($type eq 'blob') { + die_error(400, 'Object is not a tree-ish'); } my $name = $project; diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh index d0ff21d426..0688a57e1d 100644 --- a/t/t9501-gitweb-standalone-http-status.sh +++ b/t/t9501-gitweb-standalone-http-status.sh @@ -75,4 +75,43 @@ test_expect_success \ test_debug 'cat gitweb.output' +# ---------------------------------------------------------------------- +# snapshot hash ids + +test_expect_success 'snapshots: good tree-ish id' ' + gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" && + grep "Status: 200 OK" gitweb.output +' +test_debug 'cat gitweb.output' + +test_expect_success 'snapshots: bad tree-ish id' ' + gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" && + grep "404 - Object does not exist" gitweb.output +' +test_debug 'cat gitweb.output' + +test_expect_success 'snapshots: bad tree-ish id (tagged object)' ' + echo object > tag-object && + git add tag-object && + git commit -m "Object to be tagged" && + git tag tagged-object `git hash-object tag-object` && + gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" && + grep "400 - Object is not a tree-ish" gitweb.output +' +test_debug 'cat gitweb.output' + +test_expect_success 'snapshots: good object id' ' + ID=`git rev-parse --verify HEAD` && + gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" && + grep "Status: 200 OK" gitweb.output +' +test_debug 'cat gitweb.output' + +test_expect_success 'snapshots: bad object id' ' + gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" && + grep "404 - Object does not exist" gitweb.output +' +test_debug 'cat gitweb.output' + + test_done -- cgit v1.3 From 1655c98790682aed8892eb8a9eb6d44e00d5f69f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 9 Oct 2009 14:26:44 +0200 Subject: gitweb: Do not show 'patch' link for merge commits The 'patch' view is about generating text/plain patch that can be given to "git am", and "git am" doesn't understand merges anyway. Therefore link to 'patch' view should not be shown for merge commits. Also call to git-format-patch inside the 'patch' action would fail when 'patch' action is called for a merge commit, with "Reading git-format-patch failed" text as 'patch' view body. Signed-off-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 24b219310a..c939e2434d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5328,7 +5328,7 @@ sub git_commit { } @$parents ) . ')'; } - if (gitweb_check_feature('patches')) { + if (gitweb_check_feature('patches') && @$parents <= 1) { $formats_nav .= " | " . $cgi->a({-href => href(action=>"patch", -replay=>1)}, "patch"); @@ -5616,7 +5616,7 @@ sub git_commitdiff { $formats_nav = $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); - if ($patch_max) { + if ($patch_max && @{$co{'parents'}} <= 1) { $formats_nav .= " | " . $cgi->a({-href => href(action=>"patch", -replay=>1)}, "patch"); @@ -5824,7 +5824,7 @@ sub git_commitdiff_plain { # format-patch-style patches sub git_patch { - git_commitdiff(-format => 'patch', -single=> 1); + git_commitdiff(-format => 'patch', -single => 1); } sub git_patches { -- cgit v1.3 From 452e2256d2d7cb5494ca10fcbbb6bdf29570f2c0 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 13 Oct 2009 21:51:36 +0200 Subject: gitweb: fix esc_param The custom CGI escaping done in esc_param failed to escape UTF-8 properly. Fix by using CGI::escape on each sequence of matched characters instead of sprintf()ing a custom escaping for each byte. Additionally, the space -> + escape was being escaped due to greedy matching on the first substitution. Fix by adding space to the list of characters not handled on the first substitution. Finally, remove an unnecessary escaping of the + sign. Signed-off-by: Giuseppe Bilotta 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 24b219310a..4b21ad25df 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1083,8 +1083,7 @@ sub to_utf8 { # correct, but quoted slashes look too horrible in bookmarks sub esc_param { my $str = shift; - $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 e133d65c3e3d7f5f869edb2d6205af68af0fe38f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 15 Oct 2009 21:14:59 -0700 Subject: gitweb: linkify author/committer names with search It's nice to search for an author by merely clicking on their name in gitweb. This is usually faster than selecting the name, copying the selection, pasting it into the search box, selecting between author/committer and then hitting enter. Linkify the avatar icon in log/shortlog view because the icon is directly adjacent to the name and thus more related. The same is not true when in commit/tag view where the icon is farther away. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 4 ++++ gitweb/gitweb.perl | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 8f68fe3091..27405e647d 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -32,6 +32,10 @@ img.avatar { vertical-align: middle; } +a.list img.avatar { + border-style: none; +} + div.page_header { height: 25px; padding: 8px; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 4b21ad25df..c160a56c6c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1594,6 +1594,29 @@ sub git_get_avatar { } } +sub format_search_author { + my ($author, $searchtype, $displaytext) = @_; + my $have_search = gitweb_check_feature('search'); + + if ($have_search) { + my $performed = ""; + if ($searchtype eq 'author') { + $performed = "authored"; + } elsif ($searchtype eq 'committer') { + $performed = "committed"; + } + + return $cgi->a({-href => href(action=>"search", hash=>$hash, + searchtext=>$author, + searchtype=>$searchtype), class=>"list", + title=>"Search for commits $performed by $author"}, + $displaytext); + + } else { + return $displaytext; + } +} + # format the author name of the given commit with the given tag # the author name is chopped and escaped according to the other # optional parameters (see chop_str). @@ -1602,8 +1625,10 @@ sub format_author_html { my $co = shift; my $author = chop_and_escape_str($co->{'author_name'}, @_); return "<$tag class=\"author\">" . - git_get_avatar($co->{'author_email'}, -pad_after => 1) . - $author . ""; + format_search_author($co->{'author_name'}, "author", + git_get_avatar($co->{'author_email'}, -pad_after => 1) . + $author) . + ""; } # format git diff header line, i.e. "diff --(git|combined|cc) ..." @@ -3372,10 +3397,11 @@ sub git_print_authorship { my $co = shift; my %opts = @_; my $tag = $opts{-tag} || 'div'; + my $author = $co->{'author_name'}; my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'}); print "<$tag class=\"author_date\">" . - esc_html($co->{'author_name'}) . + 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) @@ -3394,8 +3420,12 @@ sub git_print_authorship_rows { @people = ('author', 'committer') unless @people; foreach my $who (@people) { my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"}); - print "$who" . esc_html($co->{$who}) . "" . - "" . + print "$who" . + format_search_author($co->{"${who}_name"}, $who, + esc_html($co->{"${who}_name"})) . " " . + format_search_author($co->{"${who}_email"}, $who, + esc_html("<" . $co->{"${who}_email"} . ">")) . + "" . git_get_avatar($co->{"${who}_email"}, -size => 'double') . "\n" . "" . -- cgit v1.3 From b9759f0762c17a5b7bc36af98613c67249931330 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Fri, 6 Nov 2009 16:08:41 +0100 Subject: gitweb: Fix blob linenr links in pathinfo mode In pathinfo mode, we use that refers to the base location of gitweb in order for various external media links to work well. However, this means that for the page to refer to itself, it must regenerate full link, and this is exactly what the blob view page did not do for line numbers. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 24b219310a..184b683aa5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5065,7 +5065,8 @@ sub git_blob { chomp $line; $nr++; $line = untabify($line); - printf "
%4i %s
\n", + printf "\n", $nr, $nr, $nr, esc_html($line, -nbsp=>1); } } -- cgit v1.3 From b629275fd02aa07c2630d1a8c8a14011ff164043 Mon Sep 17 00:00:00 2001 From: Mark Rada Date: Sat, 7 Nov 2009 16:13:29 +0100 Subject: gitweb: Smarter snapshot names Teach gitweb how to produce nicer snapshot names by only using the short hash id. If clients make requests using a tree-ish that is not a partial or full SHA-1 hash, then the short hash will also be appended to whatever they asked for. If clients request snapshot of a tag (which means that $hash ('h') parameter has 'refs/tags/' prefix), use only tag name. Update tests cases in t9502-gitweb-standalone-parse-output. Gitweb uses the following format for snapshot filenames: -. where is project name with '.git' or '/.git' suffix stripped, unless '.git' is the whole project name. For snapshot prefix it uses: -/ as compared to / before (without version info). Current rules for : * if 'h' / $hash parameter is SHA-1 or shortened SHA-1, use SHA-1 shortened to to 7 characters * otherwise if 'h' / $hash parameter is tag name (it begins with 'refs/tags/' prefix, use tag name (with 'refs/tags/' stripped * otherwise if 'h' / $hash parameter starts with 'refs/heads/' prefix, strip this prefix, convert '/' into '.', and append shortened SHA-1 after '-', i.e. use - Signed-off-by: Mark Rada Signed-off-by: Shawn O. Pearce Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 76 ++++++++++++++++++++++++------- t/t9502-gitweb-standalone-parse-output.sh | 38 ++++++++++++++-- 2 files changed, 93 insertions(+), 21 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8d4a2ae600..d8dfd950a4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1983,16 +1983,27 @@ sub quote_command { # get HEAD ref of given project as hash sub git_get_head_hash { - my $project = shift; + return git_get_full_hash(shift, 'HEAD'); +} + +sub git_get_full_hash { + return git_get_hash(@_); +} + +sub git_get_short_hash { + return git_get_hash(@_, '--short=7'); +} + +sub git_get_hash { + my ($project, $hash, @options) = @_; my $o_git_dir = $git_dir; my $retval = undef; $git_dir = "$projectroot/$project"; - if (open my $fd, "-|", git_cmd(), "rev-parse", "--verify", "HEAD") { - my $head = <$fd>; + if (open my $fd, '-|', git_cmd(), 'rev-parse', + '--verify', '-q', @options, $hash) { + $retval = <$fd>; + chomp $retval if defined $retval; close $fd; - if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) { - $retval = $1; - } } if (defined $o_git_dir) { $git_dir = $o_git_dir; @@ -5179,6 +5190,43 @@ sub git_tree { git_footer_html(); } +sub snapshot_name { + my ($project, $hash) = @_; + + # path/to/project.git -> project + # path/to/project/.git -> project + my $name = to_utf8($project); + $name =~ s,([^/])/*\.git$,$1,; + $name = basename($name); + # sanitize name + $name =~ s/[[:cntrl:]]/?/g; + + my $ver = $hash; + if ($hash =~ /^[0-9a-fA-F]+$/) { + # shorten SHA-1 hash + my $full_hash = git_get_full_hash($project, $hash); + if ($full_hash =~ /^$hash/ && length($hash) > 7) { + $ver = git_get_short_hash($project, $hash); + } + } elsif ($hash =~ m!^refs/tags/(.*)$!) { + # tags don't need shortened SHA-1 hash + $ver = $1; + } else { + # branches and other need shortened SHA-1 hash + if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) { + $ver = $1; + } + $ver .= '-' . git_get_short_hash($project, $hash); + } + # in case of hierarchical branch names + $ver =~ s!/!.!g; + + # name = project-version_string + $name = "$name-$ver"; + + return wantarray ? ($name, $name) : $name; +} + sub git_snapshot { my $format = $input_params{'snapshot_format'}; if (!@snapshot_fmts) { @@ -5203,24 +5251,20 @@ sub git_snapshot { die_error(400, 'Object is not a tree-ish'); } - my $name = $project; - $name =~ s,([^/])/*\.git$,$1,; - $name = basename($name); - my $filename = to_utf8($name); - $name =~ s/\047/\047\\\047\047/g; - my $cmd; - $filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}"; - $cmd = quote_command( + my ($name, $prefix) = snapshot_name($project, $hash); + my $filename = "$name$known_snapshot_formats{$format}{'suffix'}"; + my $cmd = quote_command( git_cmd(), 'archive', "--format=$known_snapshot_formats{$format}{'format'}", - "--prefix=$name/", $hash); + "--prefix=$prefix/", $hash); if (exists $known_snapshot_formats{$format}{'compressor'}) { $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}}); } + $filename =~ s/(["\\])/\\$1/g; print $cgi->header( -type => $known_snapshot_formats{$format}{'type'}, - -content_disposition => 'inline; filename="' . "$filename" . '"', + -content_disposition => 'inline; filename="' . $filename . '"', -status => '200 OK'); open my $fd, "-|", $cmd diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index 741187b9e4..dd83890001 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh @@ -56,29 +56,57 @@ test_debug ' test_expect_success 'snapshot: full sha1' ' gitweb_run "p=.git;a=snapshot;h=$FULL_ID;sf=tar" && - check_snapshot ".git-$FULL_ID" ".git" + check_snapshot ".git-$SHORT_ID" ' test_debug 'cat gitweb.headers && cat file_list' test_expect_success 'snapshot: shortened sha1' ' gitweb_run "p=.git;a=snapshot;h=$SHORT_ID;sf=tar" && - check_snapshot ".git-$SHORT_ID" ".git" + check_snapshot ".git-$SHORT_ID" +' +test_debug 'cat gitweb.headers && cat file_list' + +test_expect_success 'snapshot: almost full sha1' ' + ID=$(git rev-parse --short=30 HEAD) && + gitweb_run "p=.git;a=snapshot;h=$ID;sf=tar" && + check_snapshot ".git-$SHORT_ID" ' test_debug 'cat gitweb.headers && cat file_list' test_expect_success 'snapshot: HEAD' ' gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tar" && - check_snapshot ".git-HEAD" ".git" + check_snapshot ".git-HEAD-$SHORT_ID" ' test_debug 'cat gitweb.headers && cat file_list' test_expect_success 'snapshot: short branch name (master)' ' gitweb_run "p=.git;a=snapshot;h=master;sf=tar" && - check_snapshot ".git-master" ".git" + ID=$(git rev-parse --verify --short=7 master) && + check_snapshot ".git-master-$ID" +' +test_debug 'cat gitweb.headers && cat file_list' + +test_expect_success 'snapshot: short tag name (first)' ' + gitweb_run "p=.git;a=snapshot;h=first;sf=tar" && + ID=$(git rev-parse --verify --short=7 first) && + check_snapshot ".git-first-$ID" +' +test_debug 'cat gitweb.headers && cat file_list' + +test_expect_success 'snapshot: full branch name (refs/heads/master)' ' + gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" && + ID=$(git rev-parse --verify --short=7 master) && + check_snapshot ".git-master-$ID" +' +test_debug 'cat gitweb.headers && cat file_list' + +test_expect_success 'snapshot: full tag name (refs/tags/first)' ' + gitweb_run "p=.git;a=snapshot;h=refs/tags/first;sf=tar" && + check_snapshot ".git-first" ' test_debug 'cat gitweb.headers && cat file_list' -test_expect_failure 'snapshot: hierarchical branch name (xx/test)' ' +test_expect_success 'snapshot: hierarchical branch name (xx/test)' ' gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" && ! grep "filename=.*/" gitweb.headers ' -- cgit v1.3 From 42671caa7d619da9d490e77c438845c1e638c54c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 13 Nov 2009 02:02:12 +0100 Subject: gitweb: Refactor 'log' action generation, adding git_log_body() Put the main part of 'log' view generation into git_log_body, similarly how it is done for 'shortlog' and 'history' views (and also for 'tags' and 'heads' views). This is preparation for extracting common code between 'log', 'shortlog' and 'history' actions. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 81 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 33 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 62325ea877..2e92fde294 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4361,6 +4361,46 @@ sub git_project_list_body { print "\n"; } +sub git_log_body { + # uses global variable $project + my ($commitlist, $from, $to, $refs, $extra) = @_; + + $from = 0 unless defined $from; + $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); + + for (my $i = 0; $i <= $to; $i++) { + my %co = %{$commitlist->[$i]}; + 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, + $commit); + print "
\n" . + "
\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") . + "
\n" . + "
\n"; + git_print_authorship(\%co, -tag => 'span'); + print "
\n
\n"; + + print "
\n"; + git_print_log($co{'comment'}, -final_empty_line=> 1); + print "
\n"; + } + if ($extra) { + print "
\n"; + print "$extra\n"; + print "
\n"; + } +} + sub git_shortlog_body { # uses global variable $project my ($commitlist, $from, $to, $refs, $extra) = @_; @@ -5310,7 +5350,12 @@ sub git_log { my @commitlist = parse_commits($hash, 101, (100 * $page)); my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100); - + my $next_link; + if ($#commitlist >= 100) { + $next_link = + $cgi->a({-href => href(-replay=>1, page=>$page+1), + -accesskey => "n", -title => "Alt-n"}, "next"); + } my ($patch_max) = gitweb_get_feature('patches'); if ($patch_max) { if ($patch_max < 0 || @commitlist <= $patch_max) { @@ -5329,39 +5374,9 @@ sub git_log { git_print_header_div('summary', $project); print "
Last change $co{'age_string'}.

\n"; } - my $to = ($#commitlist >= 99) ? (99) : ($#commitlist); - for (my $i = 0; $i <= $to; $i++) { - my %co = %{$commitlist[$i]}; - 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, - $commit); - print "
\n" . - "
\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") . - "
\n" . - "
\n"; - git_print_authorship(\%co, -tag => 'span'); - print "
\n
\n"; - print "
\n"; - git_print_log($co{'comment'}, -final_empty_line=> 1); - print "
\n"; - } - if ($#commitlist >= 100) { - print "
\n"; - print $cgi->a({-href => href(-replay=>1, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - print "
\n"; - } + git_log_body(\@commitlist, 0, 99, $refs, $next_link); + git_footer_html(); } -- cgit v1.3 From 15f0b112d86001b0ae4be2555513c45b3cf65844 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 13 Nov 2009 02:02:13 +0100 Subject: gitweb: Refactor common parts of 'log' and 'shortlog' views Put the common parts of git_log and git_shortlog into git_log_generic subroutine: git_log and git_shortlog are now thin wrappers calling git_log_generic with appropriate arguments. The unification of code responsible for 'log' and 'shorlog' actions lead to the following changes in gitweb output * 'tree' link in page_nav now uses $hash parameter, as was the case for 'shortlog' but not for 'log' * 'log' view now respect $hash_parent limiting, like 'shortlog' did * 'log' view doesn't have special case for empty list anymore, and it always uses page_header linking to summary view, like 'shortlog' did. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 72 +++++++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 52 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e92fde294..3ddd147257 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5337,7 +5337,9 @@ sub git_snapshot { close $fd; } -sub git_log { +sub git_log_generic { + my ($fmt_name, $body_subr) = @_; + my $head = git_get_head_hash($project); if (!defined $hash) { $hash = $head; @@ -5347,16 +5349,21 @@ sub git_log { } my $refs = git_get_references(); - my @commitlist = parse_commits($hash, 101, (100 * $page)); + my $commit_hash = $hash; + if (defined $hash_parent) { + $commit_hash = "$hash_parent..$hash"; + } + my @commitlist = parse_commits($commit_hash, 101, (100 * $page)); - my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100); - my $next_link; + my $paging_nav = format_paging_nav($fmt_name, $hash, $head, + $page, $#commitlist >= 100); + my $next_link = ''; if ($#commitlist >= 100) { $next_link = $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } - my ($patch_max) = gitweb_get_feature('patches'); + my $patch_max = gitweb_get_feature('patches'); if ($patch_max) { if ($patch_max < 0 || @commitlist <= $patch_max) { $paging_nav .= " ⋅ " . @@ -5366,20 +5373,18 @@ sub git_log { } git_header_html(); - git_print_page_nav('log','', $hash,undef,undef, $paging_nav); - - if (!@commitlist) { - my %co = parse_commit($hash); - - git_print_header_div('summary', $project); - print "
Last change $co{'age_string'}.

\n"; - } + git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav); + git_print_header_div('summary', $project); - git_log_body(\@commitlist, 0, 99, $refs, $next_link); + $body_subr->(\@commitlist, 0, 99, $refs, $next_link); git_footer_html(); } +sub git_log { + git_log_generic('log', \&git_log_body); +} + sub git_commit { $hash ||= $hash_base || "HEAD"; my %co = parse_commit($hash) @@ -6243,44 +6248,7 @@ EOT } sub git_shortlog { - my $head = git_get_head_hash($project); - if (!defined $hash) { - $hash = $head; - } - if (!defined $page) { - $page = 0; - } - my $refs = git_get_references(); - - my $commit_hash = $hash; - if (defined $hash_parent) { - $commit_hash = "$hash_parent..$hash"; - } - my @commitlist = parse_commits($commit_hash, 101, (100 * $page)); - - my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100); - my $next_link = ''; - if ($#commitlist >= 100) { - $next_link = - $cgi->a({-href => href(-replay=>1, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } - my $patch_max = gitweb_check_feature('patches'); - if ($patch_max) { - if ($patch_max < 0 || @commitlist <= $patch_max) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"patches", -replay=>1)}, - "patches"); - } - } - - git_header_html(); - git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); - git_print_header_div('summary', $project); - - git_shortlog_body(\@commitlist, 0, 99, $refs, $next_link); - - git_footer_html(); + git_log_generic('shortlog', \&git_shortlog_body); } ## ...................................................................... -- cgit v1.3 From 69ca37d2abe2ae92e3456341aeab2ad6a7380bf0 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 13 Nov 2009 02:02:14 +0100 Subject: gitweb: Make 'history' view (re)use git_log_generic() Make git_history use git_log_generic, passing git_history_body as one of its paramaters. This required changes to git_log_generic, in particular passing more things as parameters. While refactoring common code of 'log', 'shortlog' and 'history' view, we did unify pagination, using always the form used by 'history' view, namely first * prev * next in place of HEAD * prev * next used by 'log' and 'shortlog' views. The 'history' view now supports commit limiting via 'hpb' parameter, similarly to 'shortlog' (and 'log') view. Performance of 'history' view got improved a bit, as it doesn't run git_get_hash_by_path for "current" version in a loop. Error detection and reporting for 'history' view changed a bit. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 146 +++++++++++++++++++++-------------------------------- 1 file changed, 57 insertions(+), 89 deletions(-) (limited to 'gitweb/gitweb.perl') diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3ddd147257..681e635090 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3363,22 +3363,18 @@ sub git_print_page_nav { } sub format_paging_nav { - my ($action, $hash, $head, $page, $has_next_link) = @_; + my ($action, $page, $has_next_link) = @_; my $paging_nav; - if ($hash ne $head || $page) { - $paging_nav .= $cgi->a({-href => href(action=>$action)}, "HEAD"); - } else { - $paging_nav .= "HEAD"; - } - if ($page > 0) { - $paging_nav .= " ⋅ " . + $paging_nav .= + $cgi->a({-href => href(-replay=>1, page=>undef)}, "first") . + " ⋅ " . $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { - $paging_nav .= " ⋅ prev"; + $paging_nav .= "first ⋅ prev"; } if ($has_next_link) { @@ -4447,7 +4443,8 @@ sub git_shortlog_body { sub git_history_body { # Warning: assumes constant type (blob or tree) during history - my ($commitlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_; + my ($commitlist, $from, $to, $refs, $extra, + $file_name, $file_hash, $ftype) = @_; $from = 0 unless defined $from; $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist}); @@ -4481,7 +4478,7 @@ sub git_history_body { $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff"); if ($ftype eq 'blob') { - my $blob_current = git_get_hash_by_path($hash_base, $file_name); + my $blob_current = $file_hash; my $blob_parent = git_get_hash_by_path($commit, $file_name); if (defined $blob_current && defined $blob_parent && $blob_current ne $blob_parent) { @@ -5338,25 +5335,48 @@ sub git_snapshot { } sub git_log_generic { - my ($fmt_name, $body_subr) = @_; + my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_; my $head = git_get_head_hash($project); - if (!defined $hash) { - $hash = $head; + if (!defined $base) { + $base = $head; } if (!defined $page) { $page = 0; } my $refs = git_get_references(); - my $commit_hash = $hash; - if (defined $hash_parent) { - $commit_hash = "$hash_parent..$hash"; + my $commit_hash = $base; + if (defined $parent) { + $commit_hash = "$parent..$base"; + } + my @commitlist = + parse_commits($commit_hash, 101, (100 * $page), + defined $file_name ? ($file_name, "--full-history") : ()); + + my $ftype; + if (!defined $file_hash && defined $file_name) { + # some commits could have deleted file in question, + # and not have it in tree, but one of them has to have it + for (my $i = 0; $i < @commitlist; $i++) { + $file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name); + last if defined $file_hash; + } + } + if (defined $file_hash) { + $ftype = git_get_type($file_hash); + } + if (defined $file_name && !defined $ftype) { + die_error(500, "Unknown type of object"); + } + my %co; + if (defined $file_name) { + %co = parse_commit($base) + or die_error(404, "Unknown commit object"); } - my @commitlist = parse_commits($commit_hash, 101, (100 * $page)); - my $paging_nav = format_paging_nav($fmt_name, $hash, $head, - $page, $#commitlist >= 100); + + my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100); my $next_link = ''; if ($#commitlist >= 100) { $next_link = @@ -5364,7 +5384,7 @@ sub git_log_generic { -accesskey => "n", -title => "Alt-n"}, "next"); } my $patch_max = gitweb_get_feature('patches'); - if ($patch_max) { + if ($patch_max && !defined $file_name) { if ($patch_max < 0 || @commitlist <= $patch_max) { $paging_nav .= " ⋅ " . $cgi->a({-href => href(action=>"patches", -replay=>1)}, @@ -5374,15 +5394,23 @@ sub git_log_generic { git_header_html(); git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav); - git_print_header_div('summary', $project); + if (defined $file_name) { + git_print_header_div('commit', esc_html($co{'title'}), $base); + } else { + git_print_header_div('summary', $project) + } + git_print_page_path($file_name, $ftype, $hash_base) + if (defined $file_name); - $body_subr->(\@commitlist, 0, 99, $refs, $next_link); + $body_subr->(\@commitlist, 0, 99, $refs, $next_link, + $file_name, $file_hash, $ftype); git_footer_html(); } sub git_log { - git_log_generic('log', \&git_log_body); + git_log_generic('log', \&git_log_body, + $hash, $hash_parent); } sub git_commit { @@ -5921,70 +5949,9 @@ sub git_patches { } sub git_history { - if (!defined $hash_base) { - $hash_base = git_get_head_hash($project); - } - if (!defined $page) { - $page = 0; - } - my $ftype; - my %co = parse_commit($hash_base) - or die_error(404, "Unknown commit object"); - - my $refs = git_get_references(); - my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - - my @commitlist = parse_commits($hash_base, 101, (100 * $page), - $file_name, "--full-history") - or die_error(404, "No such file or directory on given branch"); - - if (!defined $hash && defined $file_name) { - # some commits could have deleted file in question, - # and not have it in tree, but one of them has to have it - for (my $i = 0; $i <= @commitlist; $i++) { - $hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name); - last if defined $hash; - } - } - if (defined $hash) { - $ftype = git_get_type($hash); - } - if (!defined $ftype) { - die_error(500, "Unknown type of object"); - } - - my $paging_nav = ''; - if ($page > 0) { - $paging_nav .= - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name)}, - "first"); - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(-replay=>1, page=>$page-1), - -accesskey => "p", -title => "Alt-p"}, "prev"); - } else { - $paging_nav .= "first"; - $paging_nav .= " ⋅ prev"; - } - my $next_link = ''; - if ($#commitlist >= 100) { - $next_link = - $cgi->a({-href => href(-replay=>1, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - $paging_nav .= " ⋅ $next_link"; - } else { - $paging_nav .= " ⋅ next"; - } - - git_header_html(); - git_print_page_nav('history','', $hash_base,$co{'tree'},$hash_base, $paging_nav); - git_print_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, $ftype, $hash_base); - - git_history_body(\@commitlist, 0, 99, - $refs, $hash_base, $ftype, $next_link); - - git_footer_html(); + git_log_generic('history', \&git_history_body, + $hash_base, $hash_parent_base, + $file_name, $hash); } sub git_search { @@ -6248,7 +6215,8 @@ EOT } sub git_shortlog { - git_log_generic('shortlog', \&git_shortlog_body); + git_log_generic('shortlog', \&git_shortlog_body, + $hash, $hash_parent); } ## ...................................................................... -- cgit v1.3