threads / rfc / 43198

RFC patch, 3 partsgitweb: SHA-1 in commit log message links to "object" view

Subject: [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to "object" view

## tl;dr

12 messages between Dec 4, 2006 and Dec 10, 2006. Diffs are folded; open one to read it.

replies: 11people: 2as markdown or json

Jakub Narebsmi· Dec 4, 2006, 18:26 UTC · lore

[PATCH] gitweb: Better symbolic link support in "tree" view

From: Jakub Narebski <jnareb@gmail.com>

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@gmail.com>
---
 gitweb/gitweb.perl |   91 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 83 insertions(+), 8 deletions(-)
Show changes to gitweb/gitweb.perl +83 −8
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
Junio C Hamano· Dec 5, 2006, 01:08 UTC · re: Jakub Narebsmi · lore

Re: [PATCH] gitweb: Better symbolic link support in "tree" view

Jakub Narebsmi <jnareb@gmail.com> writes:
> 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.

I think " -> link_target" is fine, but I do not know if it is useful (while I do not think it is wrong) to make the value that would have been returned from readlink() into an href, even when it points at something inside the same revision.

Jakub Narebski· Dec 5, 2006, 21:27 UTC · re: Junio C Hamano · lore

Re: [PATCH] gitweb: Better symbolic link support in "tree" view

Junio C Hamano wrote:
Show 11 quoted lines
> Jakub Narebski <jnareb@gmail.com> writes:
> 
>> 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.
> 
> I think " -> link_target" is fine, but I do not know if it is
> useful (while I do not think it is wrong) to make the value that
> would have been returned from readlink() into an href, even when
> it points at something inside the same revision.

I have added this bit (making symbolic link target symlink) because otherwise there is no way, besides hand-munging the URL, to go to the link target.

From the command line, tools usually follow symlinks. In gitweb, "blob" view (and "blob_plain" view) show symbolic link contents... not that I'm for changing this, mind you.

-- 
Jakub Narebski
Junio C Hamano· Dec 5, 2006, 22:34 UTC · re: Jakub Narebski · lore

Re: [PATCH] gitweb: Better symbolic link support in "tree" view

Jakub Narebski <jnareb@gmail.com> writes:
Show 10 quoted lines
> Junio C Hamano wrote:
>> ...
>> I think " -> link_target" is fine, but I do not know if it is
>> useful (while I do not think it is wrong) to make the value that
>> would have been returned from readlink() into an href, even when
>> it points at something inside the same revision.
>
> I have added this bit (making symbolic link target symlink) because 
> otherwise there is no way, besides hand-munging the URL, to go to the 
> link target.
I can read what you wrote it does.

For one thing, the user is tracking the symbolic link itself, not the contents of the file or directory the link points at. For that "tracked symlink", where it points at is the important content, not what the file that is pointed at happens to contain in the same revision.

If you have to open an extra object while drawing the list, I do not think it is worth doing it.

In order to show " -> link_target", you have to read the contents of the blob. I think that overhead to read one extra blob is probably an acceptable tradeoff for convenience.

But if you want to make it a link into the same tree, you would need to check if link_target path exists and if it is a blob or tree to produce an appropriate tree_view/blob_view link (I haven't read your code but that is the natural thing to do).

That would involve in reading a few more tree objects (depending on how deep the target is in the tree), and I do not think it is worth doing it while drawing a list. After you prepared dozens of such links, the user would click at most one of them and leaves the page; your cycles to draw those unclicked links were wasted.

If you wanted to do this, a better way would be to have a new view that takes a commit/tree object and a path from the top of the repository, and shows either "no such path in that tree" or "here is the view for that object, by the way it was a blob." page. Then your list drawing would still need to open each symlink blob to show " -> link_target", and need to check if it goes outside the repository (I would assume you are handling relative links as well), but you do not need to do expensive ls-tree step one per symlink on the page. The href attr of the A element " -> link_target" would point at that "universal object view" with the link_target pathname (that is, the blob contents) and the commit/tree object name (h or hb I do not know which) and you will spend cycles to run ls-tree only when the user actually asks to follow that link.

In other words, I think trying to be lazy is extremely important while drawing a big list.

Jakub Narebski· Dec 5, 2006, 23:06 UTC · re: Junio C Hamano · lore

Re: [PATCH] gitweb: Better symbolic link support in "tree" view

Junio C Hamano wrote:
Show 27 quoted lines
> Jakub Narebski <jnareb@gmail.com> writes:
> 
>> Junio C Hamano wrote:
>>> ...
>>> I think " -> link_target" is fine, but I do not know if it is
>>> useful (while I do not think it is wrong) to make the value that
>>> would have been returned from readlink() into an href, even when
>>> it points at something inside the same revision.
>>
>> I have added this bit (making symbolic link target symlink) because 
>> otherwise there is no way, besides hand-munging the URL, to go to the 
>> link target.
> 
> I can read what you wrote it does.
> 
> For one thing, the user is tracking the symbolic link itself,
> not the contents of the file or directory the link points at.
> For that "tracked symlink", where it points at is the important
> content, not what the file that is pointed at happens to contain
> in the same revision.
> 
> If you have to open an extra object while drawing the list, I do
> not think it is worth doing it.
> 
> In order to show " -> link_target", you have to read the
> contents of the blob.  I think that overhead to read one extra
> blob is probably an acceptable tradeoff for convenience.
Especially that it is done _only_ if there exist symbolic link
entry in a tree.
 
Show 11 quoted lines
> But if you want to make it a link into the same tree, you would
> need to check if link_target path exists and if it is a blob or
> tree to produce an appropriate tree_view/blob_view link (I
> haven't read your code but that is the natural thing to do).
> 
> That would involve in reading a few more tree objects (depending
> on how deep the target is in the tree), and I do not think it is
> worth doing it while drawing a list.  After you prepared dozens
> of such links, the user would click at most one of them and 
> leaves the page; your cycles to draw those unclicked links were
> wasted.
Actually this requires only one call to git-ls-tree
  $ git ls-tree $hash_base -- $target_path

Internally this mean reading a few more tree objects, but in gitweb the cost of fork is what (I think) dominates.

Show 8 quoted lines
> If you wanted to do this, a better way would be to have a new
> view that takes a commit/tree object and a path from the top of
> the repository, and shows either "no such path in that tree" or
> "here is the view for that object, by the way it was a blob."
> page.  Then your list drawing would still need to open each
> symlink blob to show " -> link_target", and need to check if it
> goes outside the repository (I would assume you are handling
> relative links as well),

I handle _only_ relative links. There is no way to treat absolute links leading within repository (well, there is, but absoulte links depends on position of repository in the filesystem, and that is usually bad idea... unless absolute link is not to file within repository). The link is "normalized" to path from the top of the tree/top of repository tree (dealing with /./, /../, and // in the way).

Show 10 quoted lines
>                          but you do not need to do expensive 
> ls-tree step one per symlink on the page.  The href attr of the
> A element " -> link_target" would point at that "universal
> object view" with the link_target pathname (that is, the blob
> contents) and the commit/tree object name (h or hb I do not know
> which) and you will spend cycles to run ls-tree only when the
> user actually asks to follow that link.
> 
> In other words, I think trying to be lazy is extremely important
> while drawing a big list.

Well, that is certainly another solution. I'm not sure if distinction between checking if link target exists (and getting target type while at it) and providing link to perhaps "no such patch in that tree" page is worth %feature... well, I guess it is not.

I'll split the patch into two: first to read link target and show it in "tree" view _without_ hyperlink, and later perhaps either your or mine solution (most probably yours), depending on feedback.

-- 
Jakub Narebski
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

[PATCH 0/3] gitweb: Better symbolic link support in "tree" view

Junio C Hamano wrote in "Re: [PATCH] gitweb: Better symbolic link support in "tree" view":

Show 17 quoted lines
> If you wanted to do this, a better way would be to have a new
> view that takes a commit/tree object and a path from the top of
> the repository, and shows either "no such path in that tree" or
> "here is the view for that object, by the way it was a blob."
> page.  Then your list drawing would still need to open each
> symlink blob to show " -> link_target", and need to check if it
> goes outside the repository (I would assume you are handling
> relative links as well), but you do not need to do expensive
> ls-tree step one per symlink on the page.  The href attr of the
> A element " -> link_target" would point at that "universal
> object view" with the link_target pathname (that is, the blob
> contents) and the commit/tree object name (h or hb I do not know
> which) and you will spend cycles to run ls-tree only when the
> user actually asks to follow that link.
> 
> In other words, I think trying to be lazy is extremely important
> while drawing a big list.

I not necessarily agree; I think that symbolic links are sufficnetly rare that a bit more time spent to make the view better for end user (link only if target exists) is worth it. But...

Here follows the implementation of this idea: first to read link target and show it in "tree" view _without_ hyperlink, then introduction of generic "object" view which does the verification and redirect to correct view accorting to the type of object, and last show link target hyperlinked in "tree" view using "object" view/action link.

While at it implement the same "lazy" solution in commitsha1 commitag in format_log_line_html subroutine.

Table of contents:
 [PATCH 1/3] gitweb: Show target of symbolic link in "tree" view
 [PATCH 2/3] gitweb: Add generic git_object subroutine to display object of any type
 [PATCH 3/3] gitweb: Hyperlink target of symbolic link in "tree" view (if possible)
 [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to "object" view
Diffstat:
 gitweb/gitweb.perl |  152 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 137 insertions(+), 15 deletions(-)
-- 
Jakub Narebski
ShadeHawk on #git
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

[PATCH 0/3] gitweb: Better symbolic link support in "tree" view

Junio C Hamano wrote in "Re: [PATCH] gitweb: Better symbolic link support in "tree" view":

Show 17 quoted lines
> If you wanted to do this, a better way would be to have a new
> view that takes a commit/tree object and a path from the top of
> the repository, and shows either "no such path in that tree" or
> "here is the view for that object, by the way it was a blob."
> page.  Then your list drawing would still need to open each
> symlink blob to show " -> link_target", and need to check if it
> goes outside the repository (I would assume you are handling
> relative links as well), but you do not need to do expensive
> ls-tree step one per symlink on the page.  The href attr of the
> A element " -> link_target" would point at that "universal
> object view" with the link_target pathname (that is, the blob
> contents) and the commit/tree object name (h or hb I do not know
> which) and you will spend cycles to run ls-tree only when the
> user actually asks to follow that link.
> 
> In other words, I think trying to be lazy is extremely important
> while drawing a big list.

I not necessarily agree; I think that symbolic links are sufficnetly rare that a bit more time spent to make the view better for end user (link only if target exists) is worth it. But...

Here follows the implementation of this idea: first to read link target and show it in "tree" view _without_ hyperlink, then introduction of generic "object" view which does the verification and redirect to correct view accorting to the type of object, and last show link target hyperlinked in "tree" view using "object" view/action link.

While at it implement the same "lazy" solution in commitsha1 commitag in format_log_line_html subroutine.

Table of contents:
 [PATCH 1/3] gitweb: Show target of symbolic link in "tree" view
 [PATCH 2/3] gitweb: Add generic git_object subroutine to display object of any type
 [PATCH 3/3] gitweb: Hyperlink target of symbolic link in "tree" view (if possible)
 [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to "object" view
Diffstat:
 gitweb/gitweb.perl |  152 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 137 insertions(+), 15 deletions(-)
-- 
Jakub Narebski
ShadeHawk on #git
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

[PATCH 1/3] 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 <jnareb@gmail.com>
---
Jakub Narebski wrote:
> I'll split the patch into two: first to read link target and show it in 
> "tree" view _without_ hyperlink, and later perhaps either your or mine 
> solution (most probably yours), depending on feedback.
That implements the first part.
 gitweb/gitweb.perl |   42 ++++++++++++++++++++++++++++++++++--------
 1 files changed, 34 insertions(+), 8 deletions(-)
Show changes to gitweb/gitweb.perl +34 −8
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 5ea3fda..0c2cfc7 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 <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 +2024,23 @@ 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'})) {
+			my $link_target = git_get_link_target($t->{'hash'});
+			if ($link_target) {
+				print " -> " . esc_path($link_target);
+			}
+		}
+		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 +2062,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
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

[PATCH 2/3] 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 <jnareb@gmail.com>
---
First version had checking if $hash and $hash_base are explicit SHA-1
if are defined.  This version doesn't have this check, which is
important for next patch.
 gitweb/gitweb.perl |   48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)
Show changes to gitweb/gitweb.perl +48 −0
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 0c2cfc7..a988f85 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,
@@ -3620,6 +3621,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';
 
-- 
1.4.4.1
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

[PATCH 3/3] 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 <jnareb@gmail.com>
---
Junio C Hamano wrote:
> In other words, I think trying to be lazy is extremely important
> while drawing a big list.

This implements "lazy" hyperlinking of symbolic link target in "tree" view.

 gitweb/gitweb.perl |   52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 51 insertions(+), 1 deletions(-)
Show changes to gitweb/gitweb.perl +51 −1
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index a988f85..6493311 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 <tr> 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 "</td>\n";
-- 
1.4.4.1
Jakub Narebski· Dec 10, 2006, 12:25 UTC · re: Junio C Hamano · lore

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 <jnareb@gmail.com>
---
This follows the "lazy hyperlink" idea of symbolic link support in the
"tree" view.

It is an RFC (Requests For Comments) because I'm not sure if it wouldn't be better to make dead SHA-1 marked in commit log message, instead of finfing it out after clicking...

 gitweb/gitweb.perl |   12 +++++-------
 1 files changed, 5 insertions(+), 7 deletions(-)
Show changes to gitweb/gitweb.perl +5 −7
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6493311..7d24c10 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;
 }
-- 
1.4.4.1
Junio C Hamano· Dec 10, 2006, 21:29 UTC · re: Jakub Narebski · lore

Re: [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to "object" view

Jakub Narebski <jnareb@gmail.com> writes:
Show 33 quoted lines
> 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 <jnareb@gmail.com>
> ---
> This follows the "lazy hyperlink" idea of symbolic link support in the
> "tree" view.
>
> It is an RFC (Requests For Comments) because I'm not sure if it
> wouldn't be better to make dead SHA-1 marked in commit log message,
> instead of finfing it out after clicking...

I am certainly in favor of the approach. Will look at the code later. Thanks.

← back to recent threads