From 9aa1757382002655cb72bad6a163ff430e08c962 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 11 Dec 2006 18:09:58 +0100 Subject: [PATCH 1/8] gitweb: Don't use Content-Encoding: header in git_snapshot Do not use Content-Encoding: HTTP header in git_snapshot, using instead type according to the snapshot type (compression type). Some of web browser take Content-Encoding: to be _transparent_ also for downloading, and store decompressed file (with incorrect compression suffix) on download. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5ea3fda540..145d5b53b8 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3414,8 +3414,7 @@ sub git_snapshot { my $filename = basename($project) . "-$hash.tar.$suffix"; print $cgi->header( - -type => 'application/x-tar', - -content_encoding => $ctype, + -type => "application/$ctype", -content_disposition => 'inline; filename="' . "$filename" . '"', -status => '200 OK'); From e33fba4c5a33bce748d2c8a1c7a38b78a1fd2cda Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 10 Dec 2006 13:25:46 +0100 Subject: [PATCH 2/8] gitweb: Show target of symbolic link in "tree" view In "tree" view (git_print_tree_entry subroutine), for entries which are symbolic links, add " -> link_target" after file name (a la "ls -l"). Link target is _not_ hyperlinked. While at it, correct whitespaces (tabs are for aling, spaces are for indent) in modified git_print_tree_entry subroutine. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 145d5b53b8..b706f7435c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1989,12 +1989,31 @@ sub git_print_log ($;%) { } } +# return link target (what link points to) +sub git_get_link_target { + my $hash = shift; + my $link_target; + + # read link + open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash + or return; + { + local $/; + $link_target = <$fd>; + } + close $fd + or return; + + return $link_target; +} + + # print tree entry (row of git_tree), but without encompassing element sub git_print_tree_entry { my ($t, $basedir, $hash_base, $have_blame) = @_; my %base_key = (); - $base_key{hash_base} = $hash_base if defined $hash_base; + $base_key{'hash_base'} = $hash_base if defined $hash_base; # The format of a table row is: mode list link. Where mode is # the mode of the entry, list is the name of the entry, an href, @@ -2005,16 +2024,23 @@ sub git_print_tree_entry { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key), - -class => "list"}, esc_path($t->{'name'})) . "\n"; + -class => "list"}, esc_path($t->{'name'})); + if (S_ISLNK(oct $t->{'mode'})) { + my $link_target = git_get_link_target($t->{'hash'}); + if ($link_target) { + print " -> " . esc_path($link_target); + } + } + print "\n"; print ""; print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "blob"); + 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"); + file_name=>"$basedir$t->{'name'}", %base_key)}, + "blame"); } if (defined $hash_base) { print " | " . @@ -2036,8 +2062,8 @@ sub git_print_tree_entry { print "\n"; print ""; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "tree"); + file_name=>"$basedir$t->{'name'}", %base_key)}, + "tree"); if (defined $hash_base) { print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, From ca94601c8fcc4ddda4b48f05c748b3d52c54809c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 10 Dec 2006 13:25:47 +0100 Subject: [PATCH 3/8] gitweb: Add generic git_object subroutine to display object of any type Add generic "object" view implemented in git_object subroutine, which is used to display object of any type; to be more exact it redirects to the view of correct type: "blob", "tree", "commit" or "tag". To identify object you have to provide either hash (identifier of an object), or (in the case of tree and blob objects) hash of commit object (hash_base) and path (file_name). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b706f7435c..d3816b8841 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -434,6 +434,7 @@ my %actions = ( "tags" => \&git_tags, "tree" => \&git_tree, "snapshot" => \&git_snapshot, + "object" => \&git_object, # those below don't need $project "opml" => \&git_opml, "project_list" => \&git_project_list, @@ -3619,6 +3620,53 @@ sub git_commit { git_footer_html(); } +sub git_object { + # object is defined by: + # - hash or hash_base alone + # - hash_base and file_name + my $type; + + # - hash or hash_base alone + if ($hash || ($hash_base && !defined $file_name)) { + my $object_id = $hash || $hash_base; + + my $git_command = git_cmd_str(); + open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null" + or die_error('404 Not Found', "Object does not exist"); + $type = <$fd>; + chomp $type; + close $fd + or die_error('404 Not Found', "Object does not exist"); + + # - hash_base and file_name + } elsif ($hash_base && defined $file_name) { + $file_name =~ s,/+$,,; + + system(git_cmd(), "cat-file", '-e', $hash_base) == 0 + or die_error('404 Not Found', "Base object does not exist"); + + # here errors should not hapen + open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name + or die_error(undef, "Open git-ls-tree failed"); + my $line = <$fd>; + close $fd; + + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) { + die_error('404 Not Found', "File or directory for given base does not exist"); + } + $type = $2; + $hash = $3; + } else { + die_error('404 Not Found', "Not enough information to find object"); + } + + print $cgi->redirect(-uri => href(action=>$type, -full=>1, + hash=>$hash, hash_base=>$hash_base, + file_name=>$file_name), + -status => '302 Found'); +} + sub git_blobdiff { my $format = shift || 'html'; From 3bf9d57051845cee996f73b5402a07cbeda84c88 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 10 Dec 2006 13:25:48 +0100 Subject: [PATCH 4/8] gitweb: Hyperlink target of symbolic link in "tree" view (if possible) Make symbolic link target in "tree" view into hyperlink to generic "object" view (as we don't know if the link target is file (blob) or directory (tree), and if it exist at all). Target of link is made into hyperlink when: * hash_base is provided (otherwise we cannot find hash of link target) * link is relative * in no place link goes out of root tree (top dir) Full path of symlink target from the root dir is provided in the title attribute of hyperlink. Currently symbolic link name uses ordinary file style (hidden hyperlink), while the hyperlink to symlink target uses default hyperlink style, so it is underlined while link target which is not made into hyperlink is not underlined. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 52 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d3816b8841..d902913966 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2008,6 +2008,48 @@ sub git_get_link_target { return $link_target; } +# given link target, and the directory (basedir) the link is in, +# return target of link relative to top directory (top tree); +# return undef if it is not possible (including absolute links). +sub normalize_link_target { + my ($link_target, $basedir, $hash_base) = @_; + + # we can normalize symlink target only if $hash_base is provided + return unless $hash_base; + + # absolute symlinks (beginning with '/') cannot be normalized + return if (substr($link_target, 0, 1) eq '/'); + + # normalize link target to path from top (root) tree (dir) + my $path; + if ($basedir) { + $path = $basedir . '/' . $link_target; + } else { + # we are in top (root) tree (dir) + $path = $link_target; + } + + # remove //, /./, and /../ + my @path_parts; + foreach my $part (split('/', $path)) { + # discard '.' and '' + next if (!$part || $part eq '.'); + # handle '..' + if ($part eq '..') { + if (@path_parts) { + pop @path_parts; + } else { + # link leads outside repository (outside top dir) + return; + } + } else { + push @path_parts, $part; + } + } + $path = join('/', @path_parts); + + return $path; +} # print tree entry (row of git_tree), but without encompassing element sub git_print_tree_entry { @@ -2029,7 +2071,15 @@ sub git_print_tree_entry { if (S_ISLNK(oct $t->{'mode'})) { my $link_target = git_get_link_target($t->{'hash'}); if ($link_target) { - print " -> " . esc_path($link_target); + my $norm_target = normalize_link_target($link_target, $basedir, $hash_base); + if (defined $norm_target) { + print " -> " . + $cgi->a({-href => href(action=>"object", hash_base=>$hash_base, + file_name=>$norm_target), + -title => $norm_target}, esc_path($link_target)); + } else { + print " -> " . esc_path($link_target); + } } } print "\n"; From bfe2191f79aa4e74eafc709ea41cc0999c9f5be5 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 10 Dec 2006 13:25:49 +0100 Subject: [PATCH 5/8] gitweb: SHA-1 in commit log message links to "object" view Instead of checking if explicit SHA-1 in commit log message is sha1 of commit and making link to "commit" view, make [fragment of] explicit SHA-1 in commit log message link to "object" view. While at it allow to hyperlink also shortened SHA-1, from 8 characters up to full SHA-1, instead of requiring full 40 characters of SHA-1. This makes the following changes: * SHA-1 of objects which no longer exists, for example in commit cherry-picked from no longer existing temporary branch, or revert of commit in rebased branch, are no longer marked as such by not being made into hyperlink (and not having default hyperlink view: being underlined among others). On the other hand it makes gitweb to not write error messages when object is not found to web serwer log; it also moves cost of getting type and SHA-1 validation to when link is clicked, and not only viewed. * SHA-1 of other objects: blobs, trees, tags are also hyperlinked and lead to appropriate view (although in the case of tags it is more natural to just use tag name). * You can put shortened SHA-1 of commit in the commit message, and it would be hyperlinked; it would be checked on clicking if abbrev is unique. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d902913966..040ee719f0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -828,14 +828,12 @@ sub format_log_line_html { my $line = shift; $line = esc_html($line, -nbsp=>1); - if ($line =~ m/([0-9a-fA-F]{40})/) { + if ($line =~ m/([0-9a-fA-F]{8,40})/) { my $hash_text = $1; - if (git_get_type($hash_text) eq "commit") { - my $link = - $cgi->a({-href => href(action=>"commit", hash=>$hash_text), - -class => "text"}, $hash_text); - $line =~ s/$hash_text/$link/; - } + my $link = + $cgi->a({-href => href(action=>"object", hash=>$hash_text), + -class => "text"}, $hash_text); + $line =~ s/$hash_text/$link/; } return $line; } From 549ab4a30703012ff3a12b5455d319216805a8db Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 15 Dec 2006 17:53:45 +0100 Subject: [PATCH 6/8] gitweb: Do not show difftree for merges in "commit" view Do not show difftree against first parent for merges (commits with more than one parent) in "commit" view, because it usually is misleading. git-show and git-whatchanged doesn't show diff for merges either. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 040ee719f0..ebf35a1e2b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3572,14 +3572,19 @@ sub git_commit { my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'}); my $parent = $co{'parent'}; + my $parents = $co{'parents'}; if (!defined $parent) { $parent = "--root"; } - 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"); + my @difftree; + if (@$parents <= 1) { + # difftree output is not printed for merges + open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id", + @diff_opts, $parent, $hash, "--" + or die_error(undef, "Open git-diff-tree failed"); + @difftree = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading git-diff-tree failed"); + } # non-textual hash id's can be cached my $expires; @@ -3641,7 +3646,7 @@ sub git_commit { } print "" . "\n"; - my $parents = $co{'parents'}; + foreach my $par (@$parents) { print "" . "parent" . @@ -3663,7 +3668,10 @@ sub git_commit { git_print_log($co{'comment'}); print "\n"; - git_difftree_body(\@difftree, $hash, $parent); + if (@$parents <= 1) { + # do not output difftree/whatchanged for merges + git_difftree_body(\@difftree, $hash, $parent); + } git_footer_html(); } From 5fce278ef313d3b43a5df5bd0494ad23dc313d22 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 15 Dec 2006 23:49:12 +0100 Subject: [PATCH 7/8] gitweb: Add title attribute to ref marker with full ref name Add title attribute, which will be shown as popup on mouseover in graphical web browsers, with full name of ref, including part (type) removed from the name of ref itself. This is useful to see that this strange ref is StGIT ref, or it is remote branch, or it is lightweigh tag (with branch-like name). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ebf35a1e2b..79e518a912 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -855,7 +855,8 @@ sub format_ref_marker { $name = $ref; } - $markers .= " " . esc_html($name) . ""; + $markers .= " " . + esc_html($name) . ""; } } From c9d193dffbeaf0f106e712879c3ce3d2a4846157 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 15 Dec 2006 21:57:16 +0100 Subject: [PATCH 8/8] gitweb: Add "next" link to commit view Add a kind of "next" view in the bottom part of navigation bar for "commit" view, similar to what was added for "commitdiff" view in commit 151602df00b8e5c5b4a8193f59a94b85f9b5aebc 'gitweb: Add "next" link to commitdiff view' For "commit" view for single parent commit: (parent: _commit_) For "commit" view for merge (multi-parent) commit: (merge: _commit_ _commit_ ...) For "commit" view for root (parentless) commit (initial) where _link_ denotes hyperlink. SHA1 of commit is shortened to 7 characters on display. While at it, remove leftovers from commit cae1862a by Petr Baudis: 'gitweb: More per-view navigation bar links' namely the "blame" link if there exist $file_name and commit has a parent; it was added in git_commit probably by mistake. The rest of what mentioned commit added for git_commit was removed in commit 6e0e92fda893311ff5af91836e5007bf6bbd4a21 by Luben Tuikov: 'gitweb: Do not print "log" and "shortlog" redundantly in commit view' (which should have probably removed also this "blame" link removed now). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 79e518a912..4059894e0b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3572,8 +3572,34 @@ sub git_commit { my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'}); - my $parent = $co{'parent'}; - my $parents = $co{'parents'}; + my $parent = $co{'parent'}; + my $parents = $co{'parents'}; # listref + + # we need to prepare $formats_nav before any parameter munging + my $formats_nav; + if (!defined $parent) { + # --root commitdiff + $formats_nav .= '(initial)'; + } elsif (@$parents == 1) { + # single parent commit + $formats_nav .= + '(parent: ' . + $cgi->a({-href => href(action=>"commit", + hash=>$parent)}, + esc_html(substr($parent, 0, 7))) . + ')'; + } else { + # merge commit + $formats_nav .= + '(merge: ' . + join(' ', map { + $cgi->a({-href => href(action=>"commitdiff", + hash=>$_)}, + esc_html(substr($_, 0, 7))); + } @$parents ) . + ')'; + } + if (!defined $parent) { $parent = "--root"; } @@ -3597,16 +3623,10 @@ sub git_commit { my $have_snapshot = gitweb_have_snapshot(); - my @views_nav = (); - if (defined $file_name && defined $co{'parent'}) { - push @views_nav, - $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, - "blame"); - } git_header_html(undef, $expires); git_print_page_nav('commit', '', $hash, $co{'tree'}, $hash, - join (' | ', @views_nav)); + $formats_nav); if (defined $co{'parent'}) { git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);