]> Lady’s Gitweb - Gitweb/commitdiff
gitweb: Hyperlink target of symbolic link in "tree" view (if possible)
authorJakub Narebski <redacted>
Sun, 10 Dec 2006 12:25:48 +0000 (13:25 +0100)
committerLady <redacted>
Mon, 6 Apr 2026 04:07:11 +0000 (00:07 -0400)
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 <redacted>
Signed-off-by: Junio C Hamano <redacted>
gitweb.perl

index 5bf8e4480da9a89119ab50e6d5b6548aff17b72364ae9ab9802a64411d5c88ff..29de80c541feeacabcfe1cf0370df3a8666132f493478cd620073aa36e825f8d 100755 (executable)
@@ -2009,6 +2009,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 <tr> element
 sub git_print_tree_entry {
@@ -2030,7 +2072,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 "</td>\n";
This page took 0.276493 seconds and 4 git commands to generate.