From: Jakub Narebski <jnareb@xxxxxxxxx> In "tree" view (git_print_tree_entry subroutine), add for symbolic links after file name " -> link_target", a la "ls -l". Use git_get_link_target_html to escape target name and make it into hyperlink if possible. 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) * target of link exists for hash_base Full path of symlink target from the root dir is provided in the title attribute of hyperlink. If symlink target is a directory, '/' is added to end of path in title attribute. Currently symbolic link name uses ordinary file style (hidden hyperlink), while the hyperlink to symlink target uses default hyperlink style. Signed-off-by: Jakub Narebski <jnareb@xxxxxxxxx> --- gitweb/gitweb.perl | 91 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 83 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ffe8ce1..3c1b75d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1989,12 +1989,83 @@ sub git_print_log ($;%) { } } +# print link tree entry (name of what link points to, possibly hyperlink) +sub git_get_link_target_html { + my ($t, $basedir, $hash_base) = @_; + + my $fd; + my %target = (); + + # read link + open $fd, "-|", git_cmd(), "cat-file", "blob", $t->{'hash'}; + { + local $/; + $target{'orig'} = <$fd>; + } + close $fd; + + # we can make hyperlink out of link target only if $hash_base is provided + return esc_path($target{'orig'}) + unless $hash_base; + + # absolute links are returned as is, no hyperlink + if (substr($target{'orig'}, 0, 1) eq '/') { + return esc_path($target{'orig'}); + } + + # normalize link target to path from top (root) tree (dir) + if ($basedir) { + $target{'path'} = $basedir . '/' . $target{'orig'}; + } else { + # we are in top (root) tree (dir) + $target{'path'} = $target{'orig'}; + } + $target{'parts'} = [ ]; + foreach my $part (split('/', $target{'path'})) { + # discard '.' and '' + next if (!$part || $part eq '.'); + # handle '..' + if ($part eq '..') { + if (@{$target{'parts'}}) { + pop @{$target{'parts'}}; + } else { + # link leads outside repository + return esc_path($target{'orig'}); + } + } else { + push @{$target{'parts'}}, $part; + } + } + $target{'path'} = join('/', @{$target{'parts'}}); + + # check if path exists + open $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $target{'path'} + or return esc_path($target{'orig'}); + my $line = <$fd>; + close $fd + or return esc_path($target{'orig'}); + return esc_path($target{'orig'}) unless $line; + + # parse ls-tree line to get type and hash of target of link + (undef, $target{'type'}, $target{'hash'}) = + ($line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/); + + # make a link if path exists + return $cgi->a({-href => href(action => $target{'type'}, + hash => $target{'hash'}, + file_name => $target{'path'}, + hash_base => $hash_base), + -title => $target{'path'} . + ($target{'type'} eq "tree" ? '/' : '')}, + esc_path($target{'orig'})); +} + # print tree entry (row of git_tree), but without encompassing <tr> 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 +2076,20 @@ sub git_print_tree_entry { print "<td class=\"list\">" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}", %base_key), - -class => "list"}, esc_path($t->{'name'})) . "</td>\n"; + -class => "list"}, esc_path($t->{'name'})); + if (S_ISLNK(oct $t->{'mode'})) { + print " -> " . git_get_link_target_html($t, $basedir, $hash_base); + } + print "</td>\n"; print "<td class=\"link\">"; 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 +2111,8 @@ sub git_print_tree_entry { print "</td>\n"; print "<td class=\"link\">"; print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "tree"); + file_name=>"$basedir$t->{'name'}", %base_key)}, + "tree"); if (defined $hash_base) { print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, -- 1.4.4.1 - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html