{"thread":{"id":"43199","subject":"Re: [PATCH/RFC] gitweb: New improved patchset view","startedAt":"2006-10-29T10:22:30Z","lastAt":"2006-10-30T01:21:58Z","messageCount":8,"participants":["Luben Tuikov","Jakub Narebski","Junio C Hamano","Anand Kumria"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"297870","messageId":"200610291122.30852.jnareb@gmail.com","threadId":"43199","inReplyTo":null,"subject":"[PATCH/RFC] gitweb: New improved patchset view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-29T10:22:30Z","receivedAt":"2006-10-29T10:22:30Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"You should be able to check new commitdiff view at\n  http://roke_DOT_dyndns_DOT_info/cgi-bin/gitweb/gitweb.cgi\n(URL mangling courtesy vger banned words list, aaaarrghhh)\nif I didn't screwed something again with firewall, and when my machine\nis up; should be for at least an hour. Check for example:\n  ?p=git.git;a=commitdiff;h=origin\n  ?p=git.git;a=commitdiff;h=161332a521fe10c41979bcd493d95e2ac562b7f\n  ?p=git.git;a=commitdiff;h=e12c095aa69d8aca0326eb11960427d9bf9e2db7\n  ?p=git.git;a=commitdiff;h=82560983997c961d9deafe0074b787c8484c2e1d\nand compare to (for example)\n  http://repo.or.cz/w/git.git?a=commitdiff;h=82560983997c961d9deafe0074b787c8484c2e1d\nor (even older gitweb)\n  http://www.kernel.org/git/?p=git/git.git;a=commitdiff;h=887a612fef942dd3e7dae452e2dc582738b0fb41\nBTW. this gitweb has also my previous \"Slight improvement\" patch applied.\n\nDo you like it? What should be changed (code, output, style)?\n-- >8 --\nReplace \"gitweb diff header\" with its full sha1 of blobs with\n\"git diff\" header and extended diff header. Change also\nhighlighting of diffs.\n\nChanges:\n* \"gitweb diff header\" which looked for example like below:\n    file:_<sha1 before>_ -> file:_<sha1 after>_\n  where 'file' is file type and '<sha1>' is full sha1 of blob, is link\n  and uses default link style is changed to\n    diff --git a/<file before> b/<file after>\n  where <file> is hidden link (i.e. underline on hover, only)\n  to appropriate version of file. If file is added, a/<file> is not\n  hyperlinked, if file is deleted, b/<file> is not hyperlinked.\n* there is added \"extended diff header\", with <path> and <hash>\n  hyperlinked (and <hash> shortened to 7 characters), and <mode>\n  explained: '<mode>' is extnded to '<mode>/<symbolic mode> (<file type>)'.\n* <file> hyperlinking should work also when <file> is originally\n  quoted. For now we present filename quoted. This needed changes to\n  parse_difftree_raw_line subroutine.\n* from-file/to-file two-line header lines have slightly darker color\n  than removed/added lines.\n* chunk header has now delicate line above for easier finding chunk\n  boundary, and top margin of 1px.\n\nControversial ideas:\n* All links in patch header are hidden\n* Hashes are shortened to 7 characters\n* Filenames are presented quoted\n* Marking of chunk beginning\n* No hyperlink for renamed from/to header (bug)\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/gitweb.css  |   46 ++++++++++++---\n gitweb/gitweb.perl |  159 ++++++++++++++++++++++++++++++---------------------\n 2 files changed, 131 insertions(+), 74 deletions(-)\n\ndiff --git a/gitweb/gitweb.css b/gitweb/gitweb.css\nindex 83d900d..3aeceed 100644\n--- a/gitweb/gitweb.css\n+++ b/gitweb/gitweb.css\n@@ -303,6 +303,33 @@ td.mode {\n \tfont-family: monospace;\n }\n \n+\n+div.diff.header,\n+div.diff.extended_header {\n+\twhite-space: normal;\n+}\n+\n+div.diff.header {\n+\tfont-weight: bold;\n+\n+\tbackground-color: #edece6;\n+\n+\tmargin-top: 4px;\n+\tpadding: 4px 0px 2px 0px;\n+\tborder: solid #d9d8d1;\n+\tborder-width: 1px 0px 1px 0px;\n+}\n+\n+div.diff.extended_header,\n+div.diff.extended_header a.list {\n+\tcolor: #777777;\n+}\n+\n+div.diff.extended_header {\n+\tbackground-color: #f6f5ee;\n+\tpadding: 2px 0px 2px 0px;\n+}\n+\n div.diff a.list {\n \ttext-decoration: none;\n }\n@@ -312,31 +339,34 @@ div.diff a.list:hover {\n }\n \n div.diff.to_file a.list,\n-div.diff.to_file,\n+div.diff.to_file {\n+\tcolor: #007000;\n+}\n+\n div.diff.add {\n \tcolor: #008800;\n }\n \n div.diff.from_file a.list,\n-div.diff.from_file,\n+div.diff.from_file {\n+\tcolor: #aa0000;\n+}\n+\n div.diff.rem {\n \tcolor: #cc0000;\n }\n \n div.diff.chunk_header {\n \tcolor: #990099;\n+\tborder: dotted #ffbbff;\n+\tborder-width: 1px 0px 0px 0px;\n+\tmargin-top: 1px;\n }\n \n div.diff.incomplete {\n \tcolor: #cccccc;\n }\n \n-div.diff_info {\n-\tfont-family: monospace;\n-\tcolor: #000099;\n-\tbackground-color: #edece6;\n-\tfont-style: italic;\n-}\n \n div.index_include {\n \tborder: solid #d9d8d1;\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex cbab3c9..2d971ac 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1255,9 +1255,12 @@ sub parse_difftree_raw_line {\n \t\t$res{'status'} = $5;\n \t\t$res{'similarity'} = $6;\n \t\tif ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied\n-\t\t\t($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split(\"\\t\", $7);\n+\t\t\t($res{'from_file_raw'}, $res{'to_file_raw'}) = split(\"\\t\", $7);\n+\t\t\t$res{'from_file'} = unquote($res{'from_file_raw'});\n+\t\t\t$res{'to_file'}   = unquote($res{'to_file_raw'});\n \t\t} else {\n-\t\t\t$res{'file'} = unquote($7);\n+\t\t\t$res{'file_raw'} = $7;\n+\t\t\t$res{'file'} = unquote($res{'file_raw'});\n \t\t}\n \t}\n \t# 'c512b523472485aef4fff9e57b229d9d243c967f'\n@@ -2024,6 +2027,7 @@ sub git_patchset_body {\n \tmy $in_header = 0;\n \tmy $patch_found = 0;\n \tmy $diffinfo;\n+\tmy (@from_subst, @to_subst);\n \n \tprint \"<div class=\\\"patchset\\\">\\n\";\n \n@@ -2033,6 +2037,7 @@ sub git_patchset_body {\n \n \t\tif ($patch_line =~ m/^diff /) { # \"git diff\" header\n \t\t\t# beginning of patch (in patchset)\n+\n \t\t\tif ($patch_found) {\n \t\t\t\t# close previous patch\n \t\t\t\tprint \"</div>\\n\"; # class=\"patch\"\n@@ -2042,11 +2047,59 @@ sub git_patchset_body {\n \t\t\t}\n \t\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\";\n \n+\t\t\t# read and prepare patch information\n \t\t\tif (ref($difftree->[$patch_idx]) eq \"HASH\") {\n+\t\t\t\t# pre-parsed (or generated by hand)\n \t\t\t\t$diffinfo = $difftree->[$patch_idx];\n \t\t\t} else {\n \t\t\t\t$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);\n \t\t\t}\n+\t\t\tif ($diffinfo->{'status'} ne \"A\") { # not new (added) file\n+\t\t\t\tmy $quot = '';\n+\t\t\t\tmy $from_text;\n+\t\t\t\tmy $file_raw = $diffinfo->{'from_file_raw'} || $diffinfo->{'file_raw'};\n+\t\t\t\tif ($file_raw =~ s/^\"(.*)\"$/\\1/) {\n+\t\t\t\t\t$from_text = qq(\"a/$file_raw\");\n+\t\t\t\t\t$quot = '\"';\n+\t\t\t\t} else {\n+\t\t\t\t\t$from_text =  qq(a/$file_raw);\n+\t\t\t\t}\n+\t\t\t\tmy $file = $diffinfo->{'from_file'} || $diffinfo->{'file'};\n+\t\t\t\tmy $from_link =\n+\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n+\t\t\t\t\t                      hash=>$diffinfo->{'from_id'}, file_name=>$file),\n+\t\t\t\t\t        -class => \"list\"}, esc_html($file_raw));\n+\t\t\t\tmy $hash_link =\n+\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n+\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n+\t\t\t\t\t        -class => \"list\"}, substr($diffinfo->{'from_id'},0,7));\n+\t\t\t\t@from_subst = \n+\t\t\t\t\t($from_text, \"${quot}a/$from_link${quot}\",\n+\t\t\t\t\t$diffinfo->{'from_id'} . '\\.\\.', \"$hash_link..\");\n+\t\t\t}\n+\t\t\tif ($diffinfo->{'status'} ne \"D\") { # not deleted file\n+\t\t\t\tmy $quot = '';\n+\t\t\t\tmy $to_text;\n+\t\t\t\tmy $file_raw = $diffinfo->{'to_file_raw'} || $diffinfo->{'file_raw'};\n+\t\t\t\tif ($file_raw =~ s/^\"(.*)\"$/\\1/) {\n+\t\t\t\t\t$to_text = qq(\"b/$file_raw\");\n+\t\t\t\t\t$quot = '\"';\n+\t\t\t\t} else {\n+\t\t\t\t\t$to_text =  qq(b/$file_raw);\n+\t\t\t\t}\n+\t\t\t\tmy $file = $diffinfo->{'to_file'} || $diffinfo->{'file'};\n+\t\t\t\tmy $to_link =\n+\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n+\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n+\t\t\t\t\t        -class => \"list\"}, esc_html($file_raw));\n+\t\t\t\tmy $hash_link =\n+\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n+\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n+\t\t\t\t\t        -class => \"list\"}, substr($diffinfo->{'to_id'},0,7));\n+\t\t\t\t@to_subst =\n+\t\t\t\t\t($to_text, \"${quot}b/$to_link${quot}\",\n+\t\t\t\t\t '\\.\\.' . $diffinfo->{'to_id'}, \"..$hash_link\");\n+\t\t\t}\n \t\t\t$patch_idx++;\n \n \t\t\t# for now we skip empty patches\n@@ -2056,82 +2109,56 @@ sub git_patchset_body {\n \t\t\t\tnext LINE;\n \t\t\t}\n \n-\t\t\tif ($diffinfo->{'status'} eq \"A\") { # added\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'to_id'}) . \" (new)\" .\n-\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} elsif ($diffinfo->{'status'} eq \"D\") { # deleted\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) . \" (deleted)\" .\n-\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} elsif ($diffinfo->{'status'} eq \"R\" || # renamed\n-\t\t\t         $diffinfo->{'status'} eq \"C\" || # copied\n-\t\t\t         $diffinfo->{'status'} eq \"2\") { # with two filenames (from git_blobdiff)\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n-\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'from_file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) .\n-\t\t\t\t      \" -> \" .\n-\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'})},\n-\t\t\t\t              $diffinfo->{'to_id'});\n-\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} else { # modified, mode changed, ...\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n-\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) .\n-\t\t\t\t      \" -> \" .\n-\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'to_id'});\n-\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n-\t\t\t}\n+\t\t\t# print \"git diff\" header\n+\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n+\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n+\t\t\tprint \"<div class=\\\"diff header\\\">$patch_line</div>\\n\";\n \n-\t\t\t#print \"<div class=\\\"diff extended_header\\\">\\n\";\n+\t\t\tprint \"<div class=\\\"diff extended_header\\\">\\n\";\n \t\t\t$in_header = 1;\n \t\t\tnext LINE;\n \t\t} # start of patch in patchset\n \n+\t\tif ($in_header) {\n+\t\t\tif ($patch_line !~ m/^---/) {\n+\t\t\t\t# match <path>\n+\t\t\t\tif ($patch_line =~ m|a/|) {\n+\t\t\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n+\t\t\t\t}\n+\t\t\t\tif ($patch_line =~ m|b/|) {\n+\t\t\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n+\t\t\t\t}\n+\t\t\t\t# match <mode>\n+\t\t\t\tif ($patch_line =~ m/\\s(\\d{6})$/) {\n+\t\t\t\t\t$patch_line .= '/' . mode_str($1) . ' (' . file_type($1) . ')';\n+\t\t\t\t}\n+\t\t\t\t# match <hash>\n+\t\t\t\tif ($patch_line =~ m/^index/) {\n+\t\t\t\t\t$patch_line =~ s/0{40}/'0' x 7/e;\n+\t\t\t\t\t$patch_line =~ s/$from_subst[2]/$from_subst[3]/ if @from_subst;\n+\t\t\t\t\t$patch_line =~ s/$to_subst[2]/$to_subst[3]/ if @to_subst;\n+\t\t\t\t}\n+\t\t\t\tprint $patch_line . \"<br/>\\n\";\n \n-\t\tif ($in_header && $patch_line =~ m/^---/) {\n-\t\t\t#print \"</div>\\n\"; # class=\"diff extended_header\"\n-\t\t\t$in_header = 0;\n+\t\t\t} else {\n+\t\t\t\t#$patch_line =~ m/^---/;\n+\t\t\t\tprint \"</div>\\n\"; # class=\"diff extended_header\"\n+\t\t\t\t$in_header = 0;\n+\n+\t\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n+\t\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n \n-\t\t\tmy $file = $diffinfo->{'from_file'};\n-\t\t\t$file  ||= $diffinfo->{'file'};\n-\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t                               hash=>$diffinfo->{'from_id'}, file_name=>$file),\n-\t\t\t                -class => \"list\"}, esc_html($file));\n-\t\t\t$patch_line =~ s|a/.*$|a/$file|g;\n-\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n+\t\t\t\t$patch_line = <$fd>;\n+\t\t\t\tchomp $patch_line;\n \n-\t\t\t$patch_line = <$fd>;\n-\t\t\tchomp $patch_line;\n+\t\t\t\t#$patch_line =~ m/^+++/;\n+\t\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n+\t\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n \n-\t\t\t#$patch_line =~ m/^+++/;\n-\t\t\t$file    = $diffinfo->{'to_file'};\n-\t\t\t$file  ||= $diffinfo->{'file'};\n-\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t                               hash=>$diffinfo->{'to_id'}, file_name=>$file),\n-\t\t\t                -class => \"list\"}, esc_html($file));\n-\t\t\t$patch_line =~ s|b/.*|b/$file|g;\n-\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n+\t\t\t}\n \n \t\t\tnext LINE;\n \t\t}\n-\t\tnext LINE if $in_header;\n \n \t\tprint format_diff_line($patch_line);\n \t}\n-- \n1.4.3.3\n\n\n\n-------------------------------------------------------\n\n-- \nJakub Narebski\n"},{"id":"296670","messageId":"ei27mu$q9s$1@sea.gmane.org","threadId":"43199","inReplyTo":"200610291122.30852.jnareb@gmail.com","subject":"Re: [PATCH/RFC] gitweb: New improved patchset view","fromName":"Anand Kumria","fromEmail":"wildfire@progsoc.org","sentAt":"2006-10-29T12:44:15Z","receivedAt":"2006-10-29T12:44:15Z","isPatch":true,"sender":{"key":"wildfire@progsoc.org","avatar":null},"body":"On Sun, 29 Oct 2006 11:22:30 +0100, Jakub Narebski wrote:\n\n> You should be able to check new commitdiff view at\n>   http://roke_DOT_dyndns_DOT_info/cgi-bin/gitweb/gitweb.cgi\n> (URL mangling courtesy vger banned words list, aaaarrghhh)\n> if I didn't screwed something again with firewall, and when my machine\n> is up; should be for at least an hour. Check for example:\n>   ?p=git.git;a=commitdiff;h=origin\n>   ?p=git.git;a=commitdiff;h=161332a521fe10c41979bcd493d95e2ac562b7f\n>   ?p=git.git;a=commitdiff;h=e12c095aa69d8aca0326eb11960427d9bf9e2db7\n>   ?p=git.git;a=commitdiff;h=82560983997c961d9deafe0074b787c8484c2e1d\n> and compare to (for example)\n>   http://repo.or.cz/w/git.git?a=commitdiff;h=82560983997c961d9deafe0074b787c8484c2e1d\n> or (even older gitweb)\n>   http://www.kernel.org/git/?p=git/git.git;a=commitdiff;h=887a612fef942dd3e7dae452e2dc582738b0fb41\n> BTW. this gitweb has also my previous \"Slight improvement\" patch applied.\n> \n> Do you like it? What should be changed (code, output, style)?\n\nI think your version looks a lot nicer but I believe that the commit\nmessage area should be in a fixed-point font. In general it won't be a\nproblem but if anyone should choose to insert a diagram or something else\nthat assume a fixed-width display - it won't look good.\n\nAnand\n"},{"id":"294019","messageId":"534877.22218.qm@web31805.mail.mud.yahoo.com","threadId":"43199","inReplyTo":"200610291122.30852.jnareb@gmail.com","subject":"Re: [PATCH/RFC] gitweb: New improved patchset view","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-29T19:31:20Z","receivedAt":"2006-10-29T19:31:20Z","isPatch":true,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Jakub Narebski <jnareb@gmail.com> wrote:\n> Changes:\n> * \"gitweb diff header\" which looked for example like below:\n>     file:_<sha1 before>_ -> file:_<sha1 after>_\n>   where 'file' is file type and '<sha1>' is full sha1 of blob, is link\n>   and uses default link style is changed to\n>     diff --git a/<file before> b/<file after>\n>   where <file> is hidden link (i.e. underline on hover, only)\n>   to appropriate version of file. If file is added, a/<file> is not\n>   hyperlinked, if file is deleted, b/<file> is not hyperlinked.\n\nI like it and I like the \"hidden\" links.\n\n> * there is added \"extended diff header\", with <path> and <hash>\n>   hyperlinked (and <hash> shortened to 7 characters), and <mode>\n>   explained: '<mode>' is extnded to '<mode>/<symbolic mode> (<file type>)'.\n\nI like that too, but would leave \"100644/-rw-r--r-- (file)\" out.\n\nThere is a bug in the code: the first index link is the same\nas the second, while the first should upload the sha-7 it points to.\nFor example:\nindex 743f02b..c821e22\nBoth point to c821e22.  Please fix that.\n\n> * <file> hyperlinking should work also when <file> is originally\n>   quoted. For now we present filename quoted. This needed changes to\n>   parse_difftree_raw_line subroutine.\n\nI didn't see where you've quoted file names, but I'm a bit hesitant\nto quote anything unnecessarily in the visual output.\n\n> * from-file/to-file two-line header lines have slightly darker color\n>   than removed/added lines.\n\nGood.\n\n> * chunk header has now delicate line above for easier finding chunk\n>   boundary, and top margin of 1px.\n\nGood.\n\n> Controversial ideas:\n> * All links in patch header are hidden\n\nLove those hidden gems.\n\n> * Hashes are shortened to 7 characters\n\nThat's ok as long as the output is consisent with real git diff.\nThat is, a user using git can recognize this commitdiff output and a user\nusing this gitweb commitdiff output can recognize a git diff output.\n\n> * Filenames are presented quoted\n\nI wouldn't do that.\n\n> * Marking of chunk beginning\n\nI like the fine lines and their color as it is shown in your server.\n\n> * No hyperlink for renamed from/to header (bug)\n\nI'm sure you'll fix that.\n\nOverall I like this patch.  Why?  Because it makes gitweb commitdiff\noutput as similar as possible to git-diff output. This is always a good\nthing, since both beginners and advanced git users can recognize one\nor the other depending where they are coming from (gitweb or git).\n\n   Luben\n\n\n> \n> Signed-off-by: Jakub Narebski <jnareb@gmail.com>\n> ---\n>  gitweb/gitweb.css  |   46 ++++++++++++---\n>  gitweb/gitweb.perl |  159 ++++++++++++++++++++++++++++++---------------------\n>  2 files changed, 131 insertions(+), 74 deletions(-)\n> \n> diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css\n> index 83d900d..3aeceed 100644\n> --- a/gitweb/gitweb.css\n> +++ b/gitweb/gitweb.css\n> @@ -303,6 +303,33 @@ td.mode {\n>  \tfont-family: monospace;\n>  }\n>  \n> +\n> +div.diff.header,\n> +div.diff.extended_header {\n> +\twhite-space: normal;\n> +}\n> +\n> +div.diff.header {\n> +\tfont-weight: bold;\n> +\n> +\tbackground-color: #edece6;\n> +\n> +\tmargin-top: 4px;\n> +\tpadding: 4px 0px 2px 0px;\n> +\tborder: solid #d9d8d1;\n> +\tborder-width: 1px 0px 1px 0px;\n> +}\n> +\n> +div.diff.extended_header,\n> +div.diff.extended_header a.list {\n> +\tcolor: #777777;\n> +}\n> +\n> +div.diff.extended_header {\n> +\tbackground-color: #f6f5ee;\n> +\tpadding: 2px 0px 2px 0px;\n> +}\n> +\n>  div.diff a.list {\n>  \ttext-decoration: none;\n>  }\n> @@ -312,31 +339,34 @@ div.diff a.list:hover {\n>  }\n>  \n>  div.diff.to_file a.list,\n> -div.diff.to_file,\n> +div.diff.to_file {\n> +\tcolor: #007000;\n> +}\n> +\n>  div.diff.add {\n>  \tcolor: #008800;\n>  }\n>  \n>  div.diff.from_file a.list,\n> -div.diff.from_file,\n> +div.diff.from_file {\n> +\tcolor: #aa0000;\n> +}\n> +\n>  div.diff.rem {\n>  \tcolor: #cc0000;\n>  }\n>  \n>  div.diff.chunk_header {\n>  \tcolor: #990099;\n> +\tborder: dotted #ffbbff;\n> +\tborder-width: 1px 0px 0px 0px;\n> +\tmargin-top: 1px;\n>  }\n>  \n>  div.diff.incomplete {\n>  \tcolor: #cccccc;\n>  }\n>  \n> -div.diff_info {\n> -\tfont-family: monospace;\n> -\tcolor: #000099;\n> -\tbackground-color: #edece6;\n> -\tfont-style: italic;\n> -}\n>  \n>  div.index_include {\n>  \tborder: solid #d9d8d1;\n> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\n> index cbab3c9..2d971ac 100755\n> --- a/gitweb/gitweb.perl\n> +++ b/gitweb/gitweb.perl\n> @@ -1255,9 +1255,12 @@ sub parse_difftree_raw_line {\n>  \t\t$res{'status'} = $5;\n>  \t\t$res{'similarity'} = $6;\n>  \t\tif ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied\n> -\t\t\t($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split(\"\\t\", $7);\n> +\t\t\t($res{'from_file_raw'}, $res{'to_file_raw'}) = split(\"\\t\", $7);\n> +\t\t\t$res{'from_file'} = unquote($res{'from_file_raw'});\n> +\t\t\t$res{'to_file'}   = unquote($res{'to_file_raw'});\n>  \t\t} else {\n> -\t\t\t$res{'file'} = unquote($7);\n> +\t\t\t$res{'file_raw'} = $7;\n> +\t\t\t$res{'file'} = unquote($res{'file_raw'});\n>  \t\t}\n>  \t}\n>  \t# 'c512b523472485aef4fff9e57b229d9d243c967f'\n> @@ -2024,6 +2027,7 @@ sub git_patchset_body {\n>  \tmy $in_header = 0;\n>  \tmy $patch_found = 0;\n>  \tmy $diffinfo;\n> +\tmy (@from_subst, @to_subst);\n>  \n>  \tprint \"<div class=\\\"patchset\\\">\\n\";\n>  \n> @@ -2033,6 +2037,7 @@ sub git_patchset_body {\n>  \n>  \t\tif ($patch_line =~ m/^diff /) { # \"git diff\" header\n>  \t\t\t# beginning of patch (in patchset)\n> +\n>  \t\t\tif ($patch_found) {\n>  \t\t\t\t# close previous patch\n>  \t\t\t\tprint \"</div>\\n\"; # class=\"patch\"\n> @@ -2042,11 +2047,59 @@ sub git_patchset_body {\n>  \t\t\t}\n>  \t\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\";\n>  \n> +\t\t\t# read and prepare patch information\n>  \t\t\tif (ref($difftree->[$patch_idx]) eq \"HASH\") {\n> +\t\t\t\t# pre-parsed (or generated by hand)\n>  \t\t\t\t$diffinfo = $difftree->[$patch_idx];\n>  \t\t\t} else {\n>  \t\t\t\t$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);\n>  \t\t\t}\n> +\t\t\tif ($diffinfo->{'status'} ne \"A\") { # not new (added) file\n> +\t\t\t\tmy $quot = '';\n> +\t\t\t\tmy $from_text;\n> +\t\t\t\tmy $file_raw = $diffinfo->{'from_file_raw'} || $diffinfo->{'file_raw'};\n> +\t\t\t\tif ($file_raw =~ s/^\"(.*)\"$/\\1/) {\n> +\t\t\t\t\t$from_text = qq(\"a/$file_raw\");\n> +\t\t\t\t\t$quot = '\"';\n> +\t\t\t\t} else {\n> +\t\t\t\t\t$from_text =  qq(a/$file_raw);\n> +\t\t\t\t}\n> +\t\t\t\tmy $file = $diffinfo->{'from_file'} || $diffinfo->{'file'};\n> +\t\t\t\tmy $from_link =\n> +\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n> +\t\t\t\t\t                      hash=>$diffinfo->{'from_id'}, file_name=>$file),\n> +\t\t\t\t\t        -class => \"list\"}, esc_html($file_raw));\n> +\t\t\t\tmy $hash_link =\n> +\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n> +\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n> +\t\t\t\t\t        -class => \"list\"}, substr($diffinfo->{'from_id'},0,7));\n> +\t\t\t\t@from_subst = \n> +\t\t\t\t\t($from_text, \"${quot}a/$from_link${quot}\",\n> +\t\t\t\t\t$diffinfo->{'from_id'} . '\\.\\.', \"$hash_link..\");\n> +\t\t\t}\n> +\t\t\tif ($diffinfo->{'status'} ne \"D\") { # not deleted file\n> +\t\t\t\tmy $quot = '';\n> +\t\t\t\tmy $to_text;\n> +\t\t\t\tmy $file_raw = $diffinfo->{'to_file_raw'} || $diffinfo->{'file_raw'};\n> +\t\t\t\tif ($file_raw =~ s/^\"(.*)\"$/\\1/) {\n> +\t\t\t\t\t$to_text = qq(\"b/$file_raw\");\n> +\t\t\t\t\t$quot = '\"';\n> +\t\t\t\t} else {\n> +\t\t\t\t\t$to_text =  qq(b/$file_raw);\n> +\t\t\t\t}\n> +\t\t\t\tmy $file = $diffinfo->{'to_file'} || $diffinfo->{'file'};\n> +\t\t\t\tmy $to_link =\n> +\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n> +\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n> +\t\t\t\t\t        -class => \"list\"}, esc_html($file_raw));\n> +\t\t\t\tmy $hash_link =\n> +\t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_base,\n> +\t\t\t\t\t                      hash=>$diffinfo->{'to_id'}, file_name=>$file),\n> +\t\t\t\t\t        -class => \"list\"}, substr($diffinfo->{'to_id'},0,7));\n> +\t\t\t\t@to_subst =\n> +\t\t\t\t\t($to_text, \"${quot}b/$to_link${quot}\",\n> +\t\t\t\t\t '\\.\\.' . $diffinfo->{'to_id'}, \"..$hash_link\");\n> +\t\t\t}\n>  \t\t\t$patch_idx++;\n>  \n>  \t\t\t# for now we skip empty patches\n> @@ -2056,82 +2109,56 @@ sub git_patchset_body {\n>  \t\t\t\tnext LINE;\n>  \t\t\t}\n>  \n> -\t\t\tif ($diffinfo->{'status'} eq \"A\") { # added\n> -\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'to_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n> -\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n> -\t\t\t\t              $diffinfo->{'to_id'}) . \" (new)\" .\n> -\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n> -\n> -\t\t\t} elsif ($diffinfo->{'status'} eq \"D\") { # deleted\n> -\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'from_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n> -\t\t\t\t                             hash=>$diffinfo->{'from_id'},\n> file_name=>$diffinfo->{'file'})},\n> -\t\t\t\t              $diffinfo->{'from_id'}) . \" (deleted)\" .\n> -\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n> -\n> -\t\t\t} elsif ($diffinfo->{'status'} eq \"R\" || # renamed\n> -\t\t\t         $diffinfo->{'status'} eq \"C\" || # copied\n> -\t\t\t         $diffinfo->{'status'} eq \"2\") { # with two filenames (from git_blobdiff)\n> -\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n> -\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n> -\t\t\t\t                             hash=>$diffinfo->{'from_id'},\n> file_name=>$diffinfo->{'from_file'})},\n> -\t\t\t\t              $diffinfo->{'from_id'}) .\n> -\t\t\t\t      \" -> \" .\n> -\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n> -\t\t\t\t                             hash=>$diffinfo->{'to_id'},\n> file_name=>$diffinfo->{'to_file'})},\n> -\t\t\t\t              $diffinfo->{'to_id'});\n> -\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n> -\n> -\t\t\t} else { # modified, mode changed, ...\n> -\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n> -\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n> -\t\t\t\t                             hash=>$diffinfo->{'from_id'},\n> file_name=>$diffinfo->{'file'})},\n> -\t\t\t\t              $diffinfo->{'from_id'}) .\n> -\t\t\t\t      \" -> \" .\n> -\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n> -\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n> -\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n> -\t\t\t\t              $diffinfo->{'to_id'});\n> -\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n> -\t\t\t}\n> +\t\t\t# print \"git diff\" header\n> +\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n> +\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n> +\t\t\tprint \"<div class=\\\"diff header\\\">$patch_line</div>\\n\";\n>  \n> -\t\t\t#print \"<div class=\\\"diff extended_header\\\">\\n\";\n> +\t\t\tprint \"<div class=\\\"diff extended_header\\\">\\n\";\n>  \t\t\t$in_header = 1;\n>  \t\t\tnext LINE;\n>  \t\t} # start of patch in patchset\n>  \n> +\t\tif ($in_header) {\n> +\t\t\tif ($patch_line !~ m/^---/) {\n> +\t\t\t\t# match <path>\n> +\t\t\t\tif ($patch_line =~ m|a/|) {\n> +\t\t\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n> +\t\t\t\t}\n> +\t\t\t\tif ($patch_line =~ m|b/|) {\n> +\t\t\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n> +\t\t\t\t}\n> +\t\t\t\t# match <mode>\n> +\t\t\t\tif ($patch_line =~ m/\\s(\\d{6})$/) {\n> +\t\t\t\t\t$patch_line .= '/' . mode_str($1) . ' (' . file_type($1) . ')';\n> +\t\t\t\t}\n> +\t\t\t\t# match <hash>\n> +\t\t\t\tif ($patch_line =~ m/^index/) {\n> +\t\t\t\t\t$patch_line =~ s/0{40}/'0' x 7/e;\n> +\t\t\t\t\t$patch_line =~ s/$from_subst[2]/$from_subst[3]/ if @from_subst;\n> +\t\t\t\t\t$patch_line =~ s/$to_subst[2]/$to_subst[3]/ if @to_subst;\n> +\t\t\t\t}\n> +\t\t\t\tprint $patch_line . \"<br/>\\n\";\n>  \n> -\t\tif ($in_header && $patch_line =~ m/^---/) {\n> -\t\t\t#print \"</div>\\n\"; # class=\"diff extended_header\"\n> -\t\t\t$in_header = 0;\n> +\t\t\t} else {\n> +\t\t\t\t#$patch_line =~ m/^---/;\n> +\t\t\t\tprint \"</div>\\n\"; # class=\"diff extended_header\"\n> +\t\t\t\t$in_header = 0;\n> +\n> +\t\t\t\t$patch_line =~ s/$from_subst[0]/$from_subst[1]/ if @from_subst;\n> +\t\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n>  \n> -\t\t\tmy $file = $diffinfo->{'from_file'};\n> -\t\t\t$file  ||= $diffinfo->{'file'};\n> -\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n> -\t\t\t                               hash=>$diffinfo->{'from_id'}, file_name=>$file),\n> -\t\t\t                -class => \"list\"}, esc_html($file));\n> -\t\t\t$patch_line =~ s|a/.*$|a/$file|g;\n> -\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n> +\t\t\t\t$patch_line = <$fd>;\n> +\t\t\t\tchomp $patch_line;\n>  \n> -\t\t\t$patch_line = <$fd>;\n> -\t\t\tchomp $patch_line;\n> +\t\t\t\t#$patch_line =~ m/^+++/;\n> +\t\t\t\t$patch_line =~ s/$to_subst[0]/$to_subst[1]/ if @to_subst;\n> +\t\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n>  \n> -\t\t\t#$patch_line =~ m/^+++/;\n> -\t\t\t$file    = $diffinfo->{'to_file'};\n> -\t\t\t$file  ||= $diffinfo->{'file'};\n> -\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n> -\t\t\t                               hash=>$diffinfo->{'to_id'}, file_name=>$file),\n> -\t\t\t                -class => \"list\"}, esc_html($file));\n> -\t\t\t$patch_line =~ s|b/.*|b/$file|g;\n> -\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n> +\t\t\t}\n>  \n>  \t\t\tnext LINE;\n>  \t\t}\n> -\t\tnext LINE if $in_header;\n>  \n>  \t\tprint format_diff_line($patch_line);\n>  \t}\n> -- \n> 1.4.3.3\n> \n> \n> \n> -------------------------------------------------------\n> \n> -- \n> Jakub Narebski\n> Poland\n> -\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n> \n"},{"id":"298712","messageId":"20061029193839.99122.qmail@web31802.mail.mud.yahoo.com","threadId":"43199","inReplyTo":"ei27mu$q9s$1@sea.gmane.org","subject":"Re: [PATCH/RFC] gitweb: New improved patchset view","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-29T19:38:38Z","receivedAt":"2006-10-29T19:38:38Z","isPatch":true,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Anand Kumria <wildfire@progsoc.org> wrote:\n> I think your version looks a lot nicer but I believe that the commit\n> message area should be in a fixed-point font. In general it won't be a\n> problem but if anyone should choose to insert a diagram or something else\n> that assume a fixed-width display - it won't look good.\n\nThanks for catching this!\n\nJakub, I believe I submitted a patch to change that to fixed point\nand you can see the reasons in the commit message.  See commit\n4b7ce6e2d6ba088da50de1df38b040ea2c0b8f18.\n\nPlease change it to fixed point.\n\n    Luben\n"},{"id":"294849","messageId":"200610300051.37896.jnareb@gmail.com","threadId":"43199","inReplyTo":"200610291122.30852.jnareb@gmail.com","subject":"[PATCH/RFC (take 2)] gitweb: New improved patchset view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-29T23:51:37Z","receivedAt":"2006-10-29T23:51:37Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Replace \"gitweb diff header\" with its full sha1 of blobs and replace\nit by \"git diff\" header and extended diff header. Change also somewhat\nhighlighting of diffs.\n\nChanges:\n* \"gitweb diff header\" which looked for example like below:\n    file:_<sha1 before>_ -> file:_<sha1 after>_\n  where 'file' is file type and '<sha1>' is full sha1 of blob is\n  changed to\n    diff --git _a/<file before>_ _b/<file after>_\n  In both cases links are visible and use default link style. If file\n  is added, a/<file> is not hyperlinked, if file is deleted, b/<file>\n  is not hyperlinked.\n* there is added \"extended diff header\", with <path> and <hash>\n  hyperlinked (and <hash> shortened to 7 characters), and <mode>\n  explained: '<mode>' is extended to '<mode> (<file type>)'.\n* <file> hyperlinking should work also when <file> is originally\n  quoted. For now we present filename quoted. This needed changes to\n  parse_difftree_raw_line subroutine. And doesn't work: perhaps\n  unquote is broken.\n* from-file/to-file two-line header lines have slightly darker color\n  than removed/added lines.\n* chunk header has now delicate line above for easier finding chunk\n  boundary, and top margin of 1px.\n\nWORK IN PROGRESS: might not work (and actually doesn't work correctly)\nfor strange filenames, i.e. filenames contaning either metacharacters\nor having TAB, LF, backslash or doublequote in them.\n\nCode should be much more clean, by the way.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/gitweb.css  |   46 +++++++++++---\n gitweb/gitweb.perl |  178 +++++++++++++++++++++++++++++++++-------------------\n 2 files changed, 151 insertions(+), 73 deletions(-)\n\ndiff --git a/gitweb/gitweb.css b/gitweb/gitweb.css\nindex 83d900d..3aeceed 100644\n--- a/gitweb/gitweb.css\n+++ b/gitweb/gitweb.css\n@@ -303,6 +303,33 @@ td.mode {\n \tfont-family: monospace;\n }\n \n+\n+div.diff.header,\n+div.diff.extended_header {\n+\twhite-space: normal;\n+}\n+\n+div.diff.header {\n+\tfont-weight: bold;\n+\n+\tbackground-color: #edece6;\n+\n+\tmargin-top: 4px;\n+\tpadding: 4px 0px 2px 0px;\n+\tborder: solid #d9d8d1;\n+\tborder-width: 1px 0px 1px 0px;\n+}\n+\n+div.diff.extended_header,\n+div.diff.extended_header a.list {\n+\tcolor: #777777;\n+}\n+\n+div.diff.extended_header {\n+\tbackground-color: #f6f5ee;\n+\tpadding: 2px 0px 2px 0px;\n+}\n+\n div.diff a.list {\n \ttext-decoration: none;\n }\n@@ -312,31 +339,34 @@ div.diff a.list:hover {\n }\n \n div.diff.to_file a.list,\n-div.diff.to_file,\n+div.diff.to_file {\n+\tcolor: #007000;\n+}\n+\n div.diff.add {\n \tcolor: #008800;\n }\n \n div.diff.from_file a.list,\n-div.diff.from_file,\n+div.diff.from_file {\n+\tcolor: #aa0000;\n+}\n+\n div.diff.rem {\n \tcolor: #cc0000;\n }\n \n div.diff.chunk_header {\n \tcolor: #990099;\n+\tborder: dotted #ffbbff;\n+\tborder-width: 1px 0px 0px 0px;\n+\tmargin-top: 1px;\n }\n \n div.diff.incomplete {\n \tcolor: #cccccc;\n }\n \n-div.diff_info {\n-\tfont-family: monospace;\n-\tcolor: #000099;\n-\tbackground-color: #edece6;\n-\tfont-style: italic;\n-}\n \n div.index_include {\n \tborder: solid #d9d8d1;\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex cbab3c9..a5a140c 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1255,9 +1255,12 @@ sub parse_difftree_raw_line {\n \t\t$res{'status'} = $5;\n \t\t$res{'similarity'} = $6;\n \t\tif ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied\n-\t\t\t($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split(\"\\t\", $7);\n+\t\t\t($res{'from_file_raw'}, $res{'to_file_raw'}) = split(\"\\t\", $7);\n+\t\t\t$res{'from_file'} = unquote($res{'from_file_raw'});\n+\t\t\t$res{'to_file'}   = unquote($res{'to_file_raw'});\n \t\t} else {\n-\t\t\t$res{'file'} = unquote($7);\n+\t\t\t$res{'file_raw'} = $7;\n+\t\t\t$res{'file'} = unquote($res{'file_raw'});\n \t\t}\n \t}\n \t# 'c512b523472485aef4fff9e57b229d9d243c967f'\n@@ -2023,7 +2026,9 @@ sub git_patchset_body {\n \tmy $patch_idx = 0;\n \tmy $in_header = 0;\n \tmy $patch_found = 0;\n+\tmy $skip_patch = 0;\n \tmy $diffinfo;\n+\tmy (%from, %to);\n \n \tprint \"<div class=\\\"patchset\\\">\\n\";\n \n@@ -2033,6 +2038,8 @@ sub git_patchset_body {\n \n \t\tif ($patch_line =~ m/^diff /) { # \"git diff\" header\n \t\t\t# beginning of patch (in patchset)\n+\t\t\t$skip_patch = 0;\n+\n \t\t\tif ($patch_found) {\n \t\t\t\t# close previous patch\n \t\t\t\tprint \"</div>\\n\"; # class=\"patch\"\n@@ -2042,96 +2049,137 @@ sub git_patchset_body {\n \t\t\t}\n \t\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\";\n \n+\t\t\t# read and prepare patch information\n \t\t\tif (ref($difftree->[$patch_idx]) eq \"HASH\") {\n+\t\t\t\t# pre-parsed (or generated by hand)\n \t\t\t\t$diffinfo = $difftree->[$patch_idx];\n \t\t\t} else {\n \t\t\t\t$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);\n \t\t\t}\n+\t\t\tif ($diffinfo->{'status'} ne \"A\") { # not new (added) file\n+\t\t\t\t$from{'name'} = $diffinfo->{'from_file_raw'} || $diffinfo->{'file_raw'};\n+\t\t\t\t # because of \"a/file\" not a/\"file\"\n+\t\t\t\t$from{'quoted'} = ($from{'name'} =~ s/^\"(.*)\"$/$1/);\n+\n+\t\t\t\tmy $file = $diffinfo->{'from_file'} || $diffinfo->{'file'};\n+\t\t\t\t$from{'href'} = href(action=>\"blob\", hash_base=>$hash_parent,\n+\t\t\t\t                     hash=>$diffinfo->{'from_id'}, file_name=>$file);\n+\t\t\t}\n+\t\t\tif ($diffinfo->{'status'} ne \"D\") { # not deleted file\n+\t\t\t\t$to{'name'} = $diffinfo->{'to_file_raw'} || $diffinfo->{'file_raw'};\n+\t\t\t\t# because of \"b/file\" not b/\"file\"\n+\t\t\t\t$to{'quoted'} = ($to{'name'} =~ s/^\"(.*)\"$/$1/);\n+\n+\t\t\t\tmy $file = $diffinfo->{'to_file'} || $diffinfo->{'file'};\n+\t\t\t\t$to{'href'} = href(action=>\"blob\", hash_base=>$hash,\n+\t\t\t\t                   hash=>$diffinfo->{'to_id'}, file_name=>$file);\n+\t\t\t}\n \t\t\t$patch_idx++;\n \n \t\t\t# for now we skip empty patches\n \t\t\tif ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) {\n \t\t\t\t# no change, empty patch\n \t\t\t\t$in_header = 1;\n+\t\t\t\t$skip_patch = 0;\n \t\t\t\tnext LINE;\n \t\t\t}\n \n-\t\t\tif ($diffinfo->{'status'} eq \"A\") { # added\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'to_id'}) . \" (new)\" .\n-\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} elsif ($diffinfo->{'status'} eq \"D\") { # deleted\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" . file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) . \" (deleted)\" .\n-\t\t\t\t      \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} elsif ($diffinfo->{'status'} eq \"R\" || # renamed\n-\t\t\t         $diffinfo->{'status'} eq \"C\" || # copied\n-\t\t\t         $diffinfo->{'status'} eq \"2\") { # with two filenames (from git_blobdiff)\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n-\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'from_file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) .\n-\t\t\t\t      \" -> \" .\n-\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'})},\n-\t\t\t\t              $diffinfo->{'to_id'});\n-\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n-\n-\t\t\t} else { # modified, mode changed, ...\n-\t\t\t\tprint \"<div class=\\\"diff_info\\\">\" .\n-\t\t\t\t      file_type($diffinfo->{'from_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t\t                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'from_id'}) .\n-\t\t\t\t      \" -> \" .\n-\t\t\t\t      file_type($diffinfo->{'to_mode'}) . \":\" .\n-\t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t\t                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},\n-\t\t\t\t              $diffinfo->{'to_id'});\n-\t\t\t\tprint \"</div>\\n\"; # class=\"diff_info\"\n+\t\t\t# print \"git diff\" header\n+\t\t\tif ($from{'name'}) {\n+\t\t\t\tmy $from_link = $cgi->a({-href => $from{'href'}, -class => \"path\"},\n+\t\t\t\t                        'a/' . esc_html($from{'name'}));\n+\t\t\t\tmy ($q, $qq) = $from{'quoted'} ? ('\"', '&quot;') : ('', '');\n+\t\t\t\t$patch_line =~ s|${q}a/\\Q$from{'name'}\\E${q}|${qq}$from_link${qq}|;\n+\t\t\t} else {\n+\t\t\t\t# at least one of %from and %to must be set\n+\t\t\t\t$patch_line =~ s|([\"]?a/\\Q$to{'name'}\\E[\"]?)|esc_html($1)|e;\n+\t\t\t}\n+\t\t\tif ($to{'name'}) {\n+\t\t\t\tmy $to_link = $cgi->a({-href => $to{'href'}, -class => \"path\"},\n+\t\t\t\t                      'b/' . esc_html($to{'name'}));\n+\t\t\t\tmy ($q, $qq) = $to{'quoted'} ? ('\"', '&quot;') : ('', '');\n+\t\t\t\t$patch_line =~ s|${q}b/\\Q$to{'name'}\\E${q}$|${qq}$to_link${qq}|;\n+\t\t\t} else {\n+\t\t\t\t# at least one of %from and %to must be set\n+\t\t\t\t$patch_line =~ s|([\"]?b/\\Q$from{'name'}\\E[\"]?)$|esc_html($1)|e;\n \t\t\t}\n \n-\t\t\t#print \"<div class=\\\"diff extended_header\\\">\\n\";\n+\t\t\tprint \"<div class=\\\"diff header\\\">$patch_line</div>\\n\";\n+\t\t\tprint \"<div class=\\\"diff extended_header\\\">\\n\";\n \t\t\t$in_header = 1;\n \t\t\tnext LINE;\n+\t\t} else {\n+\t\t\tnext LINE if $skip_patch;\n \t\t} # start of patch in patchset\n \n+\t\tif ($in_header) {\n+\t\t\tif ($patch_line !~ m/^---/) {\n+\t\t\t\t# match <path>\n+\t\t\t\tif ($patch_line =~ m!^(copy|rename) from ! && $from{'name'}) {\n+\t\t\t\t\tmy $qq = $from{'quoted'} ? '&quot;' : '';\n+\t\t\t\t\tmy $from_link = $cgi->a({-href=>$from{'href'},-class=>\"list\"},\n+\t\t\t\t\t                        esc_html($from{'name'}));\n+\t\t\t\t\t$patch_line =~ s!from .*$!from $qq$from_link$qq!;\n+\t\t\t\t}\n+\t\t\t\tif ($patch_line =~ m!^(copy|rename) to ! && $to{'name'}) {\n+\t\t\t\t\tmy $qq = $to{'quoted'} ? '&quot;' : '';\n+\t\t\t\t\tmy $to_link = $cgi->a({-href=>$to{'href'},-class=>\"list\"},\n+\t\t\t\t\t                      esc_html($to{'name'}));\n+\t\t\t\t\t$patch_line =~ s!to .*$!to $qq$to_link$qq!;\n+\t\t\t\t}\n+\t\t\t\t# match <mode>\n+\t\t\t\tif ($patch_line =~ m/\\s(\\d{6})$/) {\n+\t\t\t\t\t$patch_line .= '<span class=\"info\"> (' . file_type($1) . ')</span>';\n+\t\t\t\t}\n+\t\t\t\t# match <hash>\n+\t\t\t\tif ($patch_line =~ m/^index/) {\n+\t\t\t\t\tmy ($from_link, $to_link);\n+\t\t\t\t\tif ($from{'href'}) {\n+\t\t\t\t\t\t$from_link = $cgi->a({-href=>$from{'href'},-class=>\"list\"},\n+\t\t\t\t\t\t                     substr($diffinfo->{'from_id'},0,7));\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\t$from_link = '0' x 7;\n+\t\t\t\t\t}\n+\t\t\t\t\tif ($to{'href'}) {\n+\t\t\t\t\t\t$to_link = $cgi->a({-href=>$to{'href'},-class=>\"list\"},\n+\t\t\t\t\t\t                   substr($diffinfo->{'to_id'},0,7));\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\t$to_link = '0' x 7;\n+\t\t\t\t\t}\n+\t\t\t\t\tmy ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});\n+\t\t\t\t\t$patch_line =~ s!$from_id\\.\\.$to_id!$from_link..$to_link!;\n+\t\t\t\t}\n+\t\t\t\tprint $patch_line . \"<br/>\\n\";\n \n-\t\tif ($in_header && $patch_line =~ m/^---/) {\n-\t\t\t#print \"</div>\\n\"; # class=\"diff extended_header\"\n-\t\t\t$in_header = 0;\n+\t\t\t} else {\n+\t\t\t\t#$patch_line =~ m/^---/;\n+\t\t\t\tprint \"</div>\\n\"; # class=\"diff extended_header\"\n+\t\t\t\t$in_header = 0;\n+\n+\t\t\t\tif ($from{'name'}) {\n+\t\t\t\t\tmy $qq = $from{'quoted'} ? '&quot;' : '';\n+\t\t\t\t\tmy $from_link = $cgi->a({-href=>$from{'href'},-class=>\"list\"},\n+\t\t\t\t\t                        esc_html($from{'name'}));\n+\t\t\t\t\t$patch_line =~ s![\"]?a/.*$!${qq}a/$from_link${qq}!;\n+\t\t\t\t}\n+\t\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n \n-\t\t\tmy $file = $diffinfo->{'from_file'};\n-\t\t\t$file  ||= $diffinfo->{'file'};\n-\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash_parent,\n-\t\t\t                               hash=>$diffinfo->{'from_id'}, file_name=>$file),\n-\t\t\t                -class => \"list\"}, esc_html($file));\n-\t\t\t$patch_line =~ s|a/.*$|a/$file|g;\n-\t\t\tprint \"<div class=\\\"diff from_file\\\">$patch_line</div>\\n\";\n+\t\t\t\t$patch_line = <$fd>;\n+\t\t\t\tchomp $patch_line;\n \n-\t\t\t$patch_line = <$fd>;\n-\t\t\tchomp $patch_line;\n+\t\t\t\t#$patch_line =~ m/^+++/;\n+\t\t\t\tif ($to{'name'}) {\n+\t\t\t\t\tmy $qq = $to{'quoted'} ? '&quot;' : '';\n+\t\t\t\t\tmy $from_link = $cgi->a({-href=>$to{'href'},-class=>\"list\"},\n+\t\t\t\t\t                        esc_html($to{'name'}));\n+\t\t\t\t\t$patch_line =~ s![\"]?b/.*$!${qq}b/$from_link${qq}!;\n+\t\t\t\t}\n+\t\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n \n-\t\t\t#$patch_line =~ m/^+++/;\n-\t\t\t$file    = $diffinfo->{'to_file'};\n-\t\t\t$file  ||= $diffinfo->{'file'};\n-\t\t\t$file = $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n-\t\t\t                               hash=>$diffinfo->{'to_id'}, file_name=>$file),\n-\t\t\t                -class => \"list\"}, esc_html($file));\n-\t\t\t$patch_line =~ s|b/.*|b/$file|g;\n-\t\t\tprint \"<div class=\\\"diff to_file\\\">$patch_line</div>\\n\";\n+\t\t\t}\n \n \t\t\tnext LINE;\n \t\t}\n-\t\tnext LINE if $in_header;\n \n \t\tprint format_diff_line($patch_line);\n \t}\n-- \n1.4.3.3\n"},{"id":"294575","messageId":"200610300134.53668.jnareb@gmail.com","threadId":"43199","inReplyTo":"200610300051.37896.jnareb@gmail.com","subject":"Re: [PATCH/RFC (take 2)] gitweb: New improved patchset view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-30T00:34:53Z","receivedAt":"2006-10-30T00:34:53Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Few other questions, probably to be adressed in the future patches, and \nnot added to this one.\n\n0. git-ls-tree and git-diff-tree without -z does quote not only !isprint \ncharacters like LF '\\n' and non-space whitespace characters like TAB \n'\\t' (and of course quoting characters '\\' and '\"'), but for example \nalso UTF-8 characters. See for example git-ls-tree output for \ngitweb/test directory. This screws somewhat idea how we should treat \nfilenames which are quoted.\n\n[BTW. How git should deal with being deployed in the environment where \nfilesystem pathnames coding might not be UTF-8, and console (terminal) \ncoding might be not UTF-8?]\n\n\n1. Current version doesn't display empty patches (i.e. pure rename and \nmode change combinations) and doesn't provide links to them from \ndifftree. This is legacy of old /usr/bin/diff using code, which did not \ngenerated extended diff header, which is only output for \"empty \npatches\". Should we change this, or leave as is?\n\n2. Schould we change syntax highlighting of chunk header line, namely \nchanging slightly syntax coloring of \"in which function are we\" part of \nchunk header?\n\n3. Should we make from-range/to-range in chunk header hyperlink to the \nstart of given bunch of lines in appropriate file? Or perhaps to the \nmiddle of the bunch of lines? Or to first changed line (omitting \ncontext)?\n\n-- \nJakub Narebski\n"},{"id":"296035","messageId":"7vr6wqy5cb.fsf@assigned-by-dhcp.cox.net","threadId":"43199","inReplyTo":"200610300134.53668.jnareb@gmail.com","subject":"Re: [PATCH/RFC (take 2)] gitweb: New improved patchset view","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-30T01:12:20Z","receivedAt":"2006-10-30T01:12:20Z","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> Few other questions, probably to be adressed in the future patches, and \n> not added to this one.\n>\n> 0. git-ls-tree and git-diff-tree without -z does quote not only ...\n> also UTF-8 characters.\n\nI've already mentioned this in an earlier message to you:\n\n\tMessage-ID: <7v3b972bzq.fsf@assigned-by-dhcp.cox.net>\n\nLet's illustrate what I mean by an untested patch; this does:\n\n 0. Use explicitly \"unsigned char\" so that (ch < ' ') does not\n    catch bytes in 0x80- range.  The original meant to catch the\n    control characters only so this is a bugfix;\n\n 1. We still worry about control characters in 0x80-0x9f range;\n    if there are some, that is not a valid UTF-8 string (or\n    other encodings that is compatible with ASCII), and quoting\n    only these bytes and not quoting 0xa0- range can result\n    in letters chopped in the middle, so we would quote all\n    bytes in 0xa0- range when we have them;\n\n 2. Otherwise we do not quote bytes in 0xa0- range.\n\n-- >8 --\ndiff --git a/quote.c b/quote.c\nindex ee7d62c..4f086fb 100644\n--- a/quote.c\n+++ b/quote.c\n@@ -199,18 +199,32 @@ static int quote_c_style_counted(const c\n \n #define EMITQ() EMIT('\\\\')\n \n-\tconst char *sp;\n-\tint ch, count = 0, needquote = 0;\n+\tconst unsigned char *name_u = (const unsigned char *)name;\n+\tconst unsigned char *sp;\n+\tint ch, count = 0, needquote = 0, has_high_ctrl = 0;\n+\n+\t/* Check if we have control character in 0x80-0x9f range */\n+\tfor (sp = name_u; sp < name_u + namelen; sp++) {\n+\t\tch = *sp;\n+\t\tif (!ch)\n+\t\t\tbreak;\n+\t\tif ((ch < ' ') || (ch == '\"') || (ch == '\\\\') ||\n+\t\t    (ch == 0177) || (ch == 0377))\n+\t\t\tneedquote = 1;\n+\t\telse if (0x80 <= ch && ch <= 0x9f)\n+\t\t\tneedquote = has_high_ctrl = 1;\n+\t}\n \n \tif (!no_dq)\n \t\tEMIT('\"');\n-\tfor (sp = name; sp < name + namelen; sp++) {\n+\n+\tfor (sp = name_u; sp < name_u + namelen; sp++) {\n \t\tch = *sp;\n \t\tif (!ch)\n \t\t\tbreak;\n \t\tif ((ch < ' ') || (ch == '\"') || (ch == '\\\\') ||\n-\t\t    (ch == 0177) || (ch == 0377)) {\n-\t\t\tneedquote = 1;\n+\t\t    (ch == 0177) ||\n+\t\t    (has_high_ctrl && 0x80 <= ch)) {\n \t\t\tswitch (ch) {\n \t\t\tcase '\\a': EMITQ(); ch = 'a'; break;\n \t\t\tcase '\\b': EMITQ(); ch = 'b'; break;\n-- 8< --\n\n> 1. Current version doesn't display empty patches (i.e. pure rename and \n> mode change combinations) and doesn't provide links to them from \n> difftree. This is legacy of old /usr/bin/diff using code, which did not \n> generated extended diff header, which is only output for \"empty \n> patches\". Should we change this, or leave as is?\n\nI think this needs to be fixed.\n\n> 2. Schould we change syntax highlighting of chunk header line, namely \n> changing slightly syntax coloring of \"in which function are we\" part of \n> chunk header?\n\nProbably matching \"git diff --color\" would be sensible; by\nfollowing what has already been done, you do not have to think\nabout what to color and how yourself.\n\n> 3. Should we make from-range/to-range in chunk header hyperlink to the \n> start of given bunch of lines in appropriate file? Or perhaps to the \n> middle of the bunch of lines? Or to first changed line (omitting \n> context)?\n\nI do not see what usage pattern this link would help.  Care to\nexplain a bit better?\n"},{"id":"297798","messageId":"200610300221.58782.jnareb@gmail.com","threadId":"43199","inReplyTo":"7vr6wqy5cb.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH/RFC (take 2)] gitweb: New improved patchset view","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-30T01:21:58Z","receivedAt":"2006-10-30T01:21:58Z","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>> 3. Should we make from-range/to-range in chunk header hyperlink to the \n>> start of given bunch of lines in appropriate file? Or perhaps to the \n>> middle of the bunch of lines? Or to first changed line (omitting \n>> context)?\n> \n> I do not see what usage pattern this link would help.  Care to\n> explain a bit better?\n\nFor example for the following header\n\ndiff --git _a/gitweb/gitweb.perl_ _b/gitweb/gitweb.perl_\nindex _cbab3c9_.._a5a140c_ 100755\n--- a/_gitweb/gitweb.perl_\n+++ b/_gitweb/gitweb.perl_\n@@ _-1255,9_ _+1255,12_ @@ sub parse_difftree_raw_line {\n\nThe '-1255,9' would be link to older version of gitweb/gitweb.perl file,\ndirectly to the line #1255 (or to first changed line, or to the middle\nof the chunk i.e. 1255+9/2 line).\n\nThe '+1255,12' would be link to newer version of file, to line 1255.\n-- \nJakub Narebski\n"}]}