{"thread":{"id":"43198","subject":"[PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to \"object\" view","startedAt":"2006-12-04T18:26:20Z","lastAt":"2006-12-10T21:29:34Z","messageCount":12,"participants":["Jakub Narebski","Junio C Hamano","Jakub Narebsmi"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"295848","messageId":"1165256780505-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":null,"subject":"[PATCH] gitweb: Better symbolic link support in \"tree\" view","fromName":"Jakub Narebsmi","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-04T18:26:20Z","receivedAt":"2006-12-04T18:26:20Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: Jakub Narebski <jnareb@gmail.com>\n\nIn \"tree\" view (git_print_tree_entry subroutine), add for symbolic\nlinks after file name \" -> link_target\", a la \"ls -l\".  Use\ngit_get_link_target_html to escape target name and make it into\nhyperlink if possible.\n\nTarget of link is made into hyperlink when:\n * hash_base is provided (otherwise we cannot find hash of link\n   target)\n * link is relative\n * in no place link goes out of root tree (top dir)\n * target of link exists for hash_base\n\nFull path of symlink target from the root dir is provided in the title\nattribute of hyperlink. If symlink target is a directory, '/' is added\nto end of path in title attribute.\n\nCurrently symbolic link name uses ordinary file style (hidden\nhyperlink), while the hyperlink to symlink target uses default\nhyperlink style.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/gitweb.perl |   91 +++++++++++++++++++++++++++++++++++++++++++++++----\n 1 files changed, 83 insertions(+), 8 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex ffe8ce1..3c1b75d 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1989,12 +1989,83 @@ sub git_print_log ($;%) {\n \t}\n }\n \n+# print link tree entry (name of what link points to, possibly hyperlink)\n+sub git_get_link_target_html {\n+\tmy ($t, $basedir, $hash_base) = @_;\n+\n+\tmy $fd;\n+\tmy %target = ();\n+\n+\t# read link\n+\topen $fd, \"-|\", git_cmd(), \"cat-file\", \"blob\", $t->{'hash'};\n+\t{\n+\t\tlocal $/;\n+\t\t$target{'orig'} = <$fd>;\n+\t}\n+\tclose $fd;\n+\n+\t# we can make hyperlink out of link target only if $hash_base is provided\n+\treturn esc_path($target{'orig'})\n+\t\tunless $hash_base;\n+\n+\t# absolute links are returned as is, no hyperlink\n+\tif (substr($target{'orig'}, 0, 1) eq '/') {\n+\t\treturn esc_path($target{'orig'});\n+\t}\n+\n+\t# normalize link target to path from top (root) tree (dir)\n+\tif ($basedir) {\n+\t\t$target{'path'} = $basedir . '/' . $target{'orig'};\n+\t} else {\n+\t\t# we are in top (root) tree (dir)\n+\t\t$target{'path'} = $target{'orig'};\n+\t}\n+\t$target{'parts'} = [ ];\n+\tforeach my $part (split('/', $target{'path'})) {\n+\t\t# discard '.' and ''\n+\t\tnext if (!$part || $part eq '.');\n+\t\t# handle '..'\n+\t\tif ($part eq '..') {\n+\t\t\tif (@{$target{'parts'}}) {\n+\t\t\t\tpop @{$target{'parts'}};\n+\t\t\t} else {\n+\t\t\t\t# link leads outside repository\n+\t\t\t\treturn esc_path($target{'orig'});\n+\t\t\t}\n+\t\t} else {\n+\t\t\tpush @{$target{'parts'}}, $part;\n+\t\t}\n+\t}\n+\t$target{'path'} = join('/', @{$target{'parts'}});\n+\n+\t# check if path exists\n+\topen $fd, \"-|\", git_cmd(), \"ls-tree\", $hash_base, \"--\", $target{'path'}\n+\t\tor return esc_path($target{'orig'});\n+\tmy $line = <$fd>;\n+\tclose $fd\n+\t\tor return esc_path($target{'orig'});\n+\treturn esc_path($target{'orig'}) unless $line;\n+\n+\t# parse ls-tree line to get type and hash of target of link\n+\t(undef, $target{'type'}, $target{'hash'}) =\n+\t\t($line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\\t/);\n+\n+\t# make a link if path exists\n+\treturn $cgi->a({-href => href(action    => $target{'type'},\n+\t                              hash      => $target{'hash'},\n+\t                              file_name => $target{'path'},\n+\t                              hash_base => $hash_base),\n+\t                -title => $target{'path'} .\n+\t                          ($target{'type'} eq \"tree\" ? '/' : '')},\n+\t               esc_path($target{'orig'}));\n+}\n+\n # print tree entry (row of git_tree), but without encompassing <tr> element\n sub git_print_tree_entry {\n \tmy ($t, $basedir, $hash_base, $have_blame) = @_;\n \n \tmy %base_key = ();\n-\t$base_key{hash_base} = $hash_base if defined $hash_base;\n+\t$base_key{'hash_base'} = $hash_base if defined $hash_base;\n \n \t# The format of a table row is: mode list link.  Where mode is\n \t# the mode of the entry, list is the name of the entry, an href,\n@@ -2005,16 +2076,20 @@ sub git_print_tree_entry {\n \t\tprint \"<td class=\\\"list\\\">\" .\n \t\t\t$cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n \t\t\t                       file_name=>\"$basedir$t->{'name'}\", %base_key),\n-\t\t\t        -class => \"list\"}, esc_path($t->{'name'})) . \"</td>\\n\";\n+\t\t\t        -class => \"list\"}, esc_path($t->{'name'}));\n+\t\tif (S_ISLNK(oct $t->{'mode'})) {\n+\t\t\tprint \" -> \" . git_get_link_target_html($t, $basedir, $hash_base);\n+\t\t}\n+\t\tprint \"</td>\\n\";\n \t\tprint \"<td class=\\\"link\\\">\";\n \t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n-\t\t\t\t\t     file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t      \"blob\");\n+\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t              \"blob\");\n \t\tif ($have_blame) {\n \t\t\tprint \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"blame\", hash=>$t->{'hash'},\n-\t\t\t\t                           file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t\t            \"blame\");\n+\t\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t\t              \"blame\");\n \t\t}\n \t\tif (defined $hash_base) {\n \t\t\tprint \" | \" .\n@@ -2036,8 +2111,8 @@ sub git_print_tree_entry {\n \t\tprint \"</td>\\n\";\n \t\tprint \"<td class=\\\"link\\\">\";\n \t\tprint $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n-\t\t\t\t\t     file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t      \"tree\");\n+\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t              \"tree\");\n \t\tif (defined $hash_base) {\n \t\t\tprint \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"history\", hash_base=>$hash_base,\n-- \n1.4.4.1\n"},{"id":"295629","messageId":"7vr6vfkv3i.fsf@assigned-by-dhcp.cox.net","threadId":"43198","inReplyTo":"1165256780505-git-send-email-jnareb@gmail.com","subject":"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-05T01:08:33Z","receivedAt":"2006-12-05T01:08:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebsmi <jnareb@gmail.com> writes:\n\n> In \"tree\" view (git_print_tree_entry subroutine), add for symbolic\n> links after file name \" -> link_target\", a la \"ls -l\".  Use\n> git_get_link_target_html to escape target name and make it into\n> hyperlink if possible.\n\nI think \" -> link_target\" is fine, but I do not know if it is\nuseful (while I do not think it is wrong) to make the value that\nwould have been returned from readlink() into an href, even when\nit points at something inside the same revision.\n\n"},{"id":"296564","messageId":"200612052227.56770.jnareb@gmail.com","threadId":"43198","inReplyTo":"7vr6vfkv3i.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-05T21:27:56Z","receivedAt":"2006-12-05T21:27:56Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n>> In \"tree\" view (git_print_tree_entry subroutine), add for symbolic\n>> links after file name \" -> link_target\", a la \"ls -l\".  Use\n>> git_get_link_target_html to escape target name and make it into\n>> hyperlink if possible.\n> \n> I think \" -> link_target\" is fine, but I do not know if it is\n> useful (while I do not think it is wrong) to make the value that\n> would have been returned from readlink() into an href, even when\n> it points at something inside the same revision.\n\nI have added this bit (making symbolic link target symlink) because \notherwise there is no way, besides hand-munging the URL, to go to the \nlink target.\n\nFrom the command line, tools usually follow symlinks. In gitweb, \"blob\" \nview (and \"blob_plain\" view) show symbolic link contents... not that \nI'm for changing this, mind you.\n-- \nJakub Narebski\n"},{"id":"298075","messageId":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","threadId":"43198","inReplyTo":"200612052227.56770.jnareb@gmail.com","subject":"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-05T22:34:58Z","receivedAt":"2006-12-05T22:34:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Junio C Hamano wrote:\n>> ...\n>> I think \" -> link_target\" is fine, but I do not know if it is\n>> useful (while I do not think it is wrong) to make the value that\n>> would have been returned from readlink() into an href, even when\n>> it points at something inside the same revision.\n>\n> I have added this bit (making symbolic link target symlink) because \n> otherwise there is no way, besides hand-munging the URL, to go to the \n> link target.\n\nI can read what you wrote it does.\n\nFor one thing, the user is tracking the symbolic link itself,\nnot the contents of the file or directory the link points at.\nFor that \"tracked symlink\", where it points at is the important\ncontent, not what the file that is pointed at happens to contain\nin the same revision.\n\nIf you have to open an extra object while drawing the list, I do\nnot think it is worth doing it.\n\nIn order to show \" -> link_target\", you have to read the\ncontents of the blob.  I think that overhead to read one extra\nblob is probably an acceptable tradeoff for convenience.\n\nBut if you want to make it a link into the same tree, you would\nneed to check if link_target path exists and if it is a blob or\ntree to produce an appropriate tree_view/blob_view link (I\nhaven't read your code but that is the natural thing to do).\n\nThat would involve in reading a few more tree objects (depending\non how deep the target is in the tree), and I do not think it is\nworth doing it while drawing a list.  After you prepared dozens\nof such links, the user would click at most one of them and \nleaves the page; your cycles to draw those unclicked links were\nwasted.\n\nIf you wanted to do this, a better way would be to have a new\nview that takes a commit/tree object and a path from the top of\nthe repository, and shows either \"no such path in that tree\" or\n\"here is the view for that object, by the way it was a blob.\"\npage.  Then your list drawing would still need to open each\nsymlink blob to show \" -> link_target\", and need to check if it\ngoes outside the repository (I would assume you are handling\nrelative links as well), but you do not need to do expensive\nls-tree step one per symlink on the page.  The href attr of the\nA element \" -> link_target\" would point at that \"universal\nobject view\" with the link_target pathname (that is, the blob\ncontents) and the commit/tree object name (h or hb I do not know\nwhich) and you will spend cycles to run ls-tree only when the\nuser actually asks to follow that link.\n\nIn other words, I think trying to be lazy is extremely important\nwhile drawing a big list.\n"},{"id":"294072","messageId":"200612060006.44155.jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-05T23:06:43Z","receivedAt":"2006-12-05T23:06:43Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n>> Junio C Hamano wrote:\n>>> ...\n>>> I think \" -> link_target\" is fine, but I do not know if it is\n>>> useful (while I do not think it is wrong) to make the value that\n>>> would have been returned from readlink() into an href, even when\n>>> it points at something inside the same revision.\n>>\n>> I have added this bit (making symbolic link target symlink) because \n>> otherwise there is no way, besides hand-munging the URL, to go to the \n>> link target.\n> \n> I can read what you wrote it does.\n> \n> For one thing, the user is tracking the symbolic link itself,\n> not the contents of the file or directory the link points at.\n> For that \"tracked symlink\", where it points at is the important\n> content, not what the file that is pointed at happens to contain\n> in the same revision.\n> \n> If you have to open an extra object while drawing the list, I do\n> not think it is worth doing it.\n> \n> In order to show \" -> link_target\", you have to read the\n> contents of the blob.  I think that overhead to read one extra\n> blob is probably an acceptable tradeoff for convenience.\n\nEspecially that it is done _only_ if there exist symbolic link\nentry in a tree.\n \n> But if you want to make it a link into the same tree, you would\n> need to check if link_target path exists and if it is a blob or\n> tree to produce an appropriate tree_view/blob_view link (I\n> haven't read your code but that is the natural thing to do).\n> \n> That would involve in reading a few more tree objects (depending\n> on how deep the target is in the tree), and I do not think it is\n> worth doing it while drawing a list.  After you prepared dozens\n> of such links, the user would click at most one of them and \n> leaves the page; your cycles to draw those unclicked links were\n> wasted.\n\nActually this requires only one call to git-ls-tree\n\n  $ git ls-tree $hash_base -- $target_path\n\nInternally this mean reading a few more tree objects, but in gitweb\nthe cost of fork is what (I think) dominates.\n\n> If you wanted to do this, a better way would be to have a new\n> view that takes a commit/tree object and a path from the top of\n> the repository, and shows either \"no such path in that tree\" or\n> \"here is the view for that object, by the way it was a blob.\"\n> page.  Then your list drawing would still need to open each\n> symlink blob to show \" -> link_target\", and need to check if it\n> goes outside the repository (I would assume you are handling\n> relative links as well),\n\nI handle _only_ relative links. There is no way to treat absolute links \nleading within repository (well, there is, but absoulte links depends \non position of repository in the filesystem, and that is usually bad \nidea... unless absolute link is not to file within repository). The \nlink is \"normalized\" to path from the top of the tree/top of repository \ntree (dealing with /./, /../, and // in the way).\n\n>                          but you do not need to do expensive \n> ls-tree step one per symlink on the page.  The href attr of the\n> A element \" -> link_target\" would point at that \"universal\n> object view\" with the link_target pathname (that is, the blob\n> contents) and the commit/tree object name (h or hb I do not know\n> which) and you will spend cycles to run ls-tree only when the\n> user actually asks to follow that link.\n> \n> In other words, I think trying to be lazy is extremely important\n> while drawing a big list.\n\nWell, that is certainly another solution. I'm not sure if distinction \nbetween checking if link target exists (and getting target type while \nat it) and providing link to perhaps \"no such patch in that tree\" page \nis worth %feature... well, I guess it is not.\n\nI'll split the patch into two: first to read link target and show it in \n\"tree\" view _without_ hyperlink, and later perhaps either your or mine \nsolution (most probably yours), depending on feedback.\n\n-- \nJakub Narebski\n"},{"id":"297761","messageId":"11657535491406-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 0/3] gitweb: Better symbolic link support in \"tree\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:44Z","receivedAt":"2006-12-10T12:25:44Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote in\n\"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view\":\n\n> If you wanted to do this, a better way would be to have a new\n> view that takes a commit/tree object and a path from the top of\n> the repository, and shows either \"no such path in that tree\" or\n> \"here is the view for that object, by the way it was a blob.\"\n> page.  Then your list drawing would still need to open each\n> symlink blob to show \" -> link_target\", and need to check if it\n> goes outside the repository (I would assume you are handling\n> relative links as well), but you do not need to do expensive\n> ls-tree step one per symlink on the page.  The href attr of the\n> A element \" -> link_target\" would point at that \"universal\n> object view\" with the link_target pathname (that is, the blob\n> contents) and the commit/tree object name (h or hb I do not know\n> which) and you will spend cycles to run ls-tree only when the\n> user actually asks to follow that link.\n> \n> In other words, I think trying to be lazy is extremely important\n> while drawing a big list.\n\nI not necessarily agree; I think that symbolic links are sufficnetly\nrare that a bit more time spent to make the view better for end user\n(link only if target exists) is worth it. But...\n\nHere follows the implementation of this idea: first to read link\ntarget and show it in \"tree\" view _without_ hyperlink, then\nintroduction of generic \"object\" view which does the verification and\nredirect to correct view accorting to the type of object, and last\nshow link target hyperlinked in \"tree\" view using \"object\" view/action\nlink.\n\nWhile at it implement the same \"lazy\" solution in commitsha1 commitag\nin format_log_line_html subroutine.\n\nTable of contents:\n [PATCH 1/3] gitweb: Show target of symbolic link in \"tree\" view\n [PATCH 2/3] gitweb: Add generic git_object subroutine to display object of any type\n [PATCH 3/3] gitweb: Hyperlink target of symbolic link in \"tree\" view (if possible)\n [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to \"object\" view\n\nDiffstat:\n gitweb/gitweb.perl |  152 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 files changed, 137 insertions(+), 15 deletions(-)\n\n-- \nJakub Narebski\nShadeHawk on #git\n"},{"id":"296744","messageId":"1165753577728-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 0/3] gitweb: Better symbolic link support in \"tree\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:45Z","receivedAt":"2006-12-10T12:25:45Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote in\n\"Re: [PATCH] gitweb: Better symbolic link support in \"tree\" view\":\n\n> If you wanted to do this, a better way would be to have a new\n> view that takes a commit/tree object and a path from the top of\n> the repository, and shows either \"no such path in that tree\" or\n> \"here is the view for that object, by the way it was a blob.\"\n> page.  Then your list drawing would still need to open each\n> symlink blob to show \" -> link_target\", and need to check if it\n> goes outside the repository (I would assume you are handling\n> relative links as well), but you do not need to do expensive\n> ls-tree step one per symlink on the page.  The href attr of the\n> A element \" -> link_target\" would point at that \"universal\n> object view\" with the link_target pathname (that is, the blob\n> contents) and the commit/tree object name (h or hb I do not know\n> which) and you will spend cycles to run ls-tree only when the\n> user actually asks to follow that link.\n> \n> In other words, I think trying to be lazy is extremely important\n> while drawing a big list.\n\nI not necessarily agree; I think that symbolic links are sufficnetly\nrare that a bit more time spent to make the view better for end user\n(link only if target exists) is worth it. But...\n\nHere follows the implementation of this idea: first to read link\ntarget and show it in \"tree\" view _without_ hyperlink, then\nintroduction of generic \"object\" view which does the verification and\nredirect to correct view accorting to the type of object, and last\nshow link target hyperlinked in \"tree\" view using \"object\" view/action\nlink.\n\nWhile at it implement the same \"lazy\" solution in commitsha1 commitag\nin format_log_line_html subroutine.\n\nTable of contents:\n [PATCH 1/3] gitweb: Show target of symbolic link in \"tree\" view\n [PATCH 2/3] gitweb: Add generic git_object subroutine to display object of any type\n [PATCH 3/3] gitweb: Hyperlink target of symbolic link in \"tree\" view (if possible)\n [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to \"object\" view\n\nDiffstat:\n gitweb/gitweb.perl |  152 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 files changed, 137 insertions(+), 15 deletions(-)\n\n-- \nJakub Narebski\nShadeHawk on #git\n"},{"id":"295400","messageId":"11657535944050-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/3] gitweb: Show target of symbolic link in \"tree\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:46Z","receivedAt":"2006-12-10T12:25:46Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"In \"tree\" view (git_print_tree_entry subroutine), for entries which are\nsymbolic links, add \" -> link_target\" after file name (a la \"ls -l\").\n\nLink target is _not_ hyperlinked.\n\nWhile at it, correct whitespaces (tabs are for aling, spaces are for indent)\nin modified git_print_tree_entry subroutine.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nJakub Narebski wrote:\n> I'll split the patch into two: first to read link target and show it in \n> \"tree\" view _without_ hyperlink, and later perhaps either your or mine \n> solution (most probably yours), depending on feedback.\n\nThat implements the first part.\n\n gitweb/gitweb.perl |   42 ++++++++++++++++++++++++++++++++++--------\n 1 files changed, 34 insertions(+), 8 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 5ea3fda..0c2cfc7 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1989,12 +1989,31 @@ sub git_print_log ($;%) {\n \t}\n }\n \n+# return link target (what link points to)\n+sub git_get_link_target {\n+\tmy $hash = shift;\n+\tmy $link_target;\n+\n+\t# read link\n+\topen my $fd, \"-|\", git_cmd(), \"cat-file\", \"blob\", $hash\n+\t\tor return;\n+\t{\n+\t\tlocal $/;\n+\t\t$link_target = <$fd>;\n+\t}\n+\tclose $fd\n+\t\tor return;\n+\n+\treturn $link_target;\n+}\n+\n+\n # print tree entry (row of git_tree), but without encompassing <tr> element\n sub git_print_tree_entry {\n \tmy ($t, $basedir, $hash_base, $have_blame) = @_;\n \n \tmy %base_key = ();\n-\t$base_key{hash_base} = $hash_base if defined $hash_base;\n+\t$base_key{'hash_base'} = $hash_base if defined $hash_base;\n \n \t# The format of a table row is: mode list link.  Where mode is\n \t# the mode of the entry, list is the name of the entry, an href,\n@@ -2005,16 +2024,23 @@ sub git_print_tree_entry {\n \t\tprint \"<td class=\\\"list\\\">\" .\n \t\t\t$cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n \t\t\t                       file_name=>\"$basedir$t->{'name'}\", %base_key),\n-\t\t\t        -class => \"list\"}, esc_path($t->{'name'})) . \"</td>\\n\";\n+\t\t\t        -class => \"list\"}, esc_path($t->{'name'}));\n+\t\tif (S_ISLNK(oct $t->{'mode'})) {\n+\t\t\tmy $link_target = git_get_link_target($t->{'hash'});\n+\t\t\tif ($link_target) {\n+\t\t\t\tprint \" -> \" . esc_path($link_target);\n+\t\t\t}\n+\t\t}\n+\t\tprint \"</td>\\n\";\n \t\tprint \"<td class=\\\"link\\\">\";\n \t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n-\t\t\t\t\t     file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t      \"blob\");\n+\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t              \"blob\");\n \t\tif ($have_blame) {\n \t\t\tprint \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"blame\", hash=>$t->{'hash'},\n-\t\t\t\t                           file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t\t            \"blame\");\n+\t\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t\t              \"blame\");\n \t\t}\n \t\tif (defined $hash_base) {\n \t\t\tprint \" | \" .\n@@ -2036,8 +2062,8 @@ sub git_print_tree_entry {\n \t\tprint \"</td>\\n\";\n \t\tprint \"<td class=\\\"link\\\">\";\n \t\tprint $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n-\t\t\t\t\t     file_name=>\"$basedir$t->{'name'}\", %base_key)},\n-\t\t\t      \"tree\");\n+\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n+\t\t              \"tree\");\n \t\tif (defined $hash_base) {\n \t\t\tprint \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"history\", hash_base=>$hash_base,\n-- \n1.4.4.1\n"},{"id":"294669","messageId":"11657536121090-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 2/3] gitweb: Add generic git_object subroutine to display object of any type","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:47Z","receivedAt":"2006-12-10T12:25:47Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Add generic \"object\" view implemented in git_object subroutine, which is\nused to display object of any type; to be more exact it redirects to the\nview of correct type: \"blob\", \"tree\", \"commit\" or \"tag\".  To identify object\nyou have to provide either hash (identifier of an object), or (in the case of\ntree and blob objects) hash of commit object (hash_base) and path (file_name).\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nFirst version had checking if $hash and $hash_base are explicit SHA-1\nif are defined.  This version doesn't have this check, which is\nimportant for next patch.\n\n gitweb/gitweb.perl |   48 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 48 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 0c2cfc7..a988f85 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -434,6 +434,7 @@ my %actions = (\n \t\"tags\" => \\&git_tags,\n \t\"tree\" => \\&git_tree,\n \t\"snapshot\" => \\&git_snapshot,\n+\t\"object\" => \\&git_object,\n \t# those below don't need $project\n \t\"opml\" => \\&git_opml,\n \t\"project_list\" => \\&git_project_list,\n@@ -3620,6 +3621,53 @@ sub git_commit {\n \tgit_footer_html();\n }\n \n+sub git_object {\n+\t# object is defined by:\n+\t# - hash or hash_base alone\n+\t# - hash_base and file_name\n+\tmy $type;\n+\n+\t# - hash or hash_base alone\n+\tif ($hash || ($hash_base && !defined $file_name)) {\n+\t\tmy $object_id = $hash || $hash_base;\n+\n+\t\tmy $git_command = git_cmd_str();\n+\t\topen my $fd, \"-|\", \"$git_command cat-file -t $object_id 2>/dev/null\"\n+\t\t\tor die_error('404 Not Found', \"Object does not exist\");\n+\t\t$type = <$fd>;\n+\t\tchomp $type;\n+\t\tclose $fd\n+\t\t\tor die_error('404 Not Found', \"Object does not exist\");\n+\n+\t# - hash_base and file_name\n+\t} elsif ($hash_base && defined $file_name) {\n+\t\t$file_name =~ s,/+$,,;\n+\n+\t\tsystem(git_cmd(), \"cat-file\", '-e', $hash_base) == 0\n+\t\t\tor die_error('404 Not Found', \"Base object does not exist\");\n+\n+\t\t# here errors should not hapen\n+\t\topen my $fd, \"-|\", git_cmd(), \"ls-tree\", $hash_base, \"--\", $file_name\n+\t\t\tor die_error(undef, \"Open git-ls-tree failed\");\n+\t\tmy $line = <$fd>;\n+\t\tclose $fd;\n+\n+\t\t#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa\tpanic.c'\n+\t\tunless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\\t/) {\n+\t\t\tdie_error('404 Not Found', \"File or directory for given base does not exist\");\n+\t\t}\n+\t\t$type = $2;\n+\t\t$hash = $3;\n+\t} else {\n+\t\tdie_error('404 Not Found', \"Not enough information to find object\");\n+\t}\n+\n+\tprint $cgi->redirect(-uri => href(action=>$type, -full=>1,\n+\t                                  hash=>$hash, hash_base=>$hash_base,\n+\t                                  file_name=>$file_name),\n+\t                     -status => '302 Found');\n+}\n+\n sub git_blobdiff {\n \tmy $format = shift || 'html';\n \n-- \n1.4.4.1\n"},{"id":"297079","messageId":"11657536303381-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 3/3] gitweb: Hyperlink target of symbolic link in \"tree\" view (if possible)","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:48Z","receivedAt":"2006-12-10T12:25:48Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Make symbolic link target in \"tree\" view into hyperlink to generic\n\"object\" view (as we don't know if the link target is file (blob) or\ndirectory (tree), and if it exist at all).\n\nTarget of link is made into hyperlink when:\n * hash_base is provided (otherwise we cannot find hash\n   of link target)\n * link is relative\n * in no place link goes out of root tree (top dir)\n\nFull path of symlink target from the root dir is provided in the title\nattribute of hyperlink.\n\nCurrently symbolic link name uses ordinary file style (hidden\nhyperlink), while the hyperlink to symlink target uses default\nhyperlink style, so it is underlined while link target which is not\nmade into hyperlink is not underlined.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nJunio C Hamano wrote:\n> In other words, I think trying to be lazy is extremely important\n> while drawing a big list.\n\nThis implements \"lazy\" hyperlinking of symbolic link target in \"tree\"\nview.\n\n gitweb/gitweb.perl |   52 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 51 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex a988f85..6493311 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2008,6 +2008,48 @@ sub git_get_link_target {\n \treturn $link_target;\n }\n \n+# given link target, and the directory (basedir) the link is in,\n+# return target of link relative to top directory (top tree);\n+# return undef if it is not possible (including absolute links).\n+sub normalize_link_target {\n+\tmy ($link_target, $basedir, $hash_base) = @_;\n+\n+\t# we can normalize symlink target only if $hash_base is provided\n+\treturn unless $hash_base;\n+\n+\t# absolute symlinks (beginning with '/') cannot be normalized\n+\treturn if (substr($link_target, 0, 1) eq '/');\n+\n+\t# normalize link target to path from top (root) tree (dir)\n+\tmy $path;\n+\tif ($basedir) {\n+\t\t$path = $basedir . '/' . $link_target;\n+\t} else {\n+\t\t# we are in top (root) tree (dir)\n+\t\t$path = $link_target;\n+\t}\n+\n+\t# remove //, /./, and /../\n+\tmy @path_parts;\n+\tforeach my $part (split('/', $path)) {\n+\t\t# discard '.' and ''\n+\t\tnext if (!$part || $part eq '.');\n+\t\t# handle '..'\n+\t\tif ($part eq '..') {\n+\t\t\tif (@path_parts) {\n+\t\t\t\tpop @path_parts;\n+\t\t\t} else {\n+\t\t\t\t# link leads outside repository (outside top dir)\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tpush @path_parts, $part;\n+\t\t}\n+\t}\n+\t$path = join('/', @path_parts);\n+\n+\treturn $path;\n+}\n \n # print tree entry (row of git_tree), but without encompassing <tr> element\n sub git_print_tree_entry {\n@@ -2029,7 +2071,15 @@ sub git_print_tree_entry {\n \t\tif (S_ISLNK(oct $t->{'mode'})) {\n \t\t\tmy $link_target = git_get_link_target($t->{'hash'});\n \t\t\tif ($link_target) {\n-\t\t\t\tprint \" -> \" . esc_path($link_target);\n+\t\t\t\tmy $norm_target = normalize_link_target($link_target, $basedir, $hash_base);\n+\t\t\t\tif (defined $norm_target) {\n+\t\t\t\t\tprint \" -> \" .\n+\t\t\t\t\t      $cgi->a({-href => href(action=>\"object\", hash_base=>$hash_base,\n+\t\t\t\t\t                             file_name=>$norm_target),\n+\t\t\t\t\t               -title => $norm_target}, esc_path($link_target));\n+\t\t\t\t} else {\n+\t\t\t\t\tprint \" -> \" . esc_path($link_target);\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n \t\tprint \"</td>\\n\";\n-- \n1.4.4.1\n"},{"id":"294018","messageId":"11657536473295-git-send-email-jnareb@gmail.com","threadId":"43198","inReplyTo":"7vk616ezu5.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to \"object\" view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-10T12:25:49Z","receivedAt":"2006-12-10T12:25:49Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Instead of checking if explicit SHA-1 in commit log message is sha1 of\ncommit and making link to \"commit\" view, make [fragment of] explicit\nSHA-1 in commit log message link to \"object\" view.  While at it allow\nto hyperlink also shortened SHA-1, from 8 characters up to full SHA-1,\ninstead of requiring full 40 characters of SHA-1.\n\nThis makes the following changes:\n\n * SHA-1 of objects which no longer exists, for example in commit\n   cherry-picked from no longer existing temporary branch, or revert\n   of commit in rebased branch, are no longer marked as such by not\n   being made into hyperlink (and not having default hyperlink view:\n   being underlined among others).  On the other hand it makes gitweb\n   to not write error messages when object is not found to web serwer\n   log; it also moves cost of getting type and SHA-1 validation to\n   when link is clicked, and not only viewed.\n\n * SHA-1 of other objects: blobs, trees, tags are also hyperlinked\n   and lead to appropriate view (although in the case of tags it is\n   more natural to just use tag name).\n\n * You can put shortened SHA-1 of commit in the commit message, and it\n   would be hyperlinked; it would be checked on clicking if abbrev is\n   unique.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis follows the \"lazy hyperlink\" idea of symbolic link support in the\n\"tree\" view.\n\nIt is an RFC (Requests For Comments) because I'm not sure if it\nwouldn't be better to make dead SHA-1 marked in commit log message,\ninstead of finfing it out after clicking...\n\n gitweb/gitweb.perl |   12 +++++-------\n 1 files changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 6493311..7d24c10 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -828,14 +828,12 @@ sub format_log_line_html {\n \tmy $line = shift;\n \n \t$line = esc_html($line, -nbsp=>1);\n-\tif ($line =~ m/([0-9a-fA-F]{40})/) {\n+\tif ($line =~ m/([0-9a-fA-F]{8,40})/) {\n \t\tmy $hash_text = $1;\n-\t\tif (git_get_type($hash_text) eq \"commit\") {\n-\t\t\tmy $link =\n-\t\t\t\t$cgi->a({-href => href(action=>\"commit\", hash=>$hash_text),\n-\t\t\t\t        -class => \"text\"}, $hash_text);\n-\t\t\t$line =~ s/$hash_text/$link/;\n-\t\t}\n+\t\tmy $link =\n+\t\t\t$cgi->a({-href => href(action=>\"object\", hash=>$hash_text),\n+\t\t\t        -class => \"text\"}, $hash_text);\n+\t\t$line =~ s/$hash_text/$link/;\n \t}\n \treturn $line;\n }\n-- \n1.4.4.1\n"},{"id":"298688","messageId":"7vhcw34eyp.fsf@assigned-by-dhcp.cox.net","threadId":"43198","inReplyTo":"11657536473295-git-send-email-jnareb@gmail.com","subject":"Re: [PATCH/RFC 4/3] gitweb: SHA-1 in commit log message links to \"object\" view","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-10T21:29:34Z","receivedAt":"2006-12-10T21:29:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Instead of checking if explicit SHA-1 in commit log message is sha1 of\n> commit and making link to \"commit\" view, make [fragment of] explicit\n> SHA-1 in commit log message link to \"object\" view.  While at it allow\n> to hyperlink also shortened SHA-1, from 8 characters up to full SHA-1,\n> instead of requiring full 40 characters of SHA-1.\n>\n> This makes the following changes:\n>\n>  * SHA-1 of objects which no longer exists, for example in commit\n>    cherry-picked from no longer existing temporary branch, or revert\n>    of commit in rebased branch, are no longer marked as such by not\n>    being made into hyperlink (and not having default hyperlink view:\n>    being underlined among others).  On the other hand it makes gitweb\n>    to not write error messages when object is not found to web serwer\n>    log; it also moves cost of getting type and SHA-1 validation to\n>    when link is clicked, and not only viewed.\n>\n>  * SHA-1 of other objects: blobs, trees, tags are also hyperlinked\n>    and lead to appropriate view (although in the case of tags it is\n>    more natural to just use tag name).\n>\n>  * You can put shortened SHA-1 of commit in the commit message, and it\n>    would be hyperlinked; it would be checked on clicking if abbrev is\n>    unique.\n>\n> Signed-off-by: Jakub Narebski <jnareb@gmail.com>\n> ---\n> This follows the \"lazy hyperlink\" idea of symbolic link support in the\n> \"tree\" view.\n>\n> It is an RFC (Requests For Comments) because I'm not sure if it\n> wouldn't be better to make dead SHA-1 marked in commit log message,\n> instead of finfing it out after clicking...\n\nI am certainly in favor of the approach.  Will look at the code\nlater.  Thanks.\n"}]}