{"thread":{"id":"28698","subject":"[PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","startedAt":"2011-10-16T23:57:31Z","lastAt":"2011-10-17T03:57:52Z","messageCount":6,"participants":["Kato Kazuyoshi","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"177811","messageId":"CAFo4x0Kb651CyxoP8wxR36aDe5=Md2UV3qjh+HPo4ad6NB=Emg@mail.gmail.com","threadId":"28698","inReplyTo":null,"subject":"[PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Kato Kazuyoshi","fromEmail":"kato.kazuyoshi@gmail.com","sentAt":"2011-10-16T23:57:31Z","receivedAt":"2011-10-16T23:57:31Z","isPatch":true,"sender":{"key":"kato.kazuyoshi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/19111?v=4"},"body":"---\nHello,\n\nI want to add some features that provided by Trac to Gitweb.\nFirst, I've implemented \"side-by-side\" style diff.\n\n gitweb/gitweb.perl       |   97 ++++++++++++++++++++++++++++++++++++++--------\n gitweb/static/gitweb.css |   16 ++++++++\n 2 files changed, 96 insertions(+), 17 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 85d64b2..9d7609f 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -757,6 +757,7 @@ our @cgi_param_mapping = (\n \textra_options => \"opt\",\n \tsearch_use_regexp => \"sr\",\n \tctag => \"by_tag\",\n+\tdiff_style => \"ds\",\n \t# this must be last entry (for manipulation from JavaScript)\n \tjavascript => \"js\"\n );\n@@ -1072,6 +1073,8 @@ sub evaluate_and_validate_params {\n \t\t}\n \t\t$search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;\n \t}\n+\n+\t$input_params{diff_style} ||= 'inline';\n }\n\n # path to the current git repository\n@@ -2235,25 +2238,25 @@ sub format_diff_line {\n \t\t# combined diff\n \t\tmy $prefix = substr($line, 0, scalar @{$from->{'href'}});\n \t\tif ($line =~ m/^\\@{3}/) {\n-\t\t\t$diff_class = \" chunk_header\";\n+\t\t\t$diff_class = \"chunk_header\";\n \t\t} elsif ($line =~ m/^\\\\/) {\n-\t\t\t$diff_class = \" incomplete\";\n+\t\t\t$diff_class = \"incomplete\";\n \t\t} elsif ($prefix =~ tr/+/+/) {\n-\t\t\t$diff_class = \" add\";\n+\t\t\t$diff_class = \"add\";\n \t\t} elsif ($prefix =~ tr/-/-/) {\n-\t\t\t$diff_class = \" rem\";\n+\t\t\t$diff_class = \"rem\";\n \t\t}\n \t} else {\n \t\t# assume ordinary diff\n \t\tmy $char = substr($line, 0, 1);\n \t\tif ($char eq '+') {\n-\t\t\t$diff_class = \" add\";\n+\t\t\t$diff_class = \"add\";\n \t\t} elsif ($char eq '-') {\n-\t\t\t$diff_class = \" rem\";\n+\t\t\t$diff_class = \"rem\";\n \t\t} elsif ($char eq '@') {\n-\t\t\t$diff_class = \" chunk_header\";\n+\t\t\t$diff_class = \"chunk_header\";\n \t\t} elsif ($char eq \"\\\\\") {\n-\t\t\t$diff_class = \" incomplete\";\n+\t\t\t$diff_class = \"incomplete\";\n \t\t}\n \t}\n \t$line = untabify($line);\n@@ -2274,7 +2277,7 @@ sub format_diff_line {\n \t\t}\n \t\t$line = \"<span class=\\\"chunk_info\\\">@@ $from_text $to_text @@</span>\" .\n \t\t        \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1) .\n\"</span>\";\n-\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n+\t\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">$line</div>\\n\";\n \t} elsif ($from && $to && $line =~ m/^\\@{3}/) {\n \t\tmy ($prefix, $ranges, $section) = $line =~ m/^(\\@+) (.*?) \\@+(.*)$/;\n \t\tmy (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);\n@@ -2307,9 +2310,9 @@ sub format_diff_line {\n \t\t}\n \t\t$line .= \" $prefix</span>\" .\n \t\t         \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1)\n. \"</span>\";\n-\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n+\t\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">$line</div>\\n\";\n \t}\n-\treturn \"<div class=\\\"diff$diff_class\\\">\" . esc_html($line, -nbsp=>1)\n. \"</div>\\n\";\n+\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">\" .\nesc_html($line, -nbsp=>1) . \"</div>\\n\";\n }\n\n # Generates undef or something like \"_snapshot_\" or \"snapshot (_tbz2_ _zip_)\",\n@@ -4826,8 +4829,32 @@ sub git_difftree_body {\n \tprint \"</table>\\n\";\n }\n\n+sub format_diff_chunk {\n+\tmy @chunk = @_;\n+\n+\tmy $first_class = $chunk[0]->[0];\n+\tmy @partial = map { $_->[1] } grep { $_->[0] eq $first_class } @chunk;\n+\n+\tif (scalar @partial < scalar @chunk) {\n+\t\treturn join '', (\"<div class='chunk'><div class='old'>\",\n+\t\t                 @partial,\n+\t\t                 \"</div>\",\n+\t\t                 \"<div class='new'>\",\n+\t\t                 (map {\n+\t\t                     $_->[1];\n+\t\t                 } @chunk[scalar @partial..scalar @chunk-1]),\n+\t\t                 \"</div></div>\");\n+\t} else {\n+\t\treturn join '', (\"<div class='chunk'><div class='\",\n+\t\t                 ($first_class eq 'add' ? 'new' : 'old'),\n+\t\t                 \"'>\",\n+\t\t                 @partial,\n+\t\t                 \"</div></div>\");\n+\t}\n+}\n+\n sub git_patchset_body {\n-\tmy ($fd, $difftree, $hash, @hash_parents) = @_;\n+\tmy ($fd, $is_inline, $difftree, $hash, @hash_parents) = @_;\n \tmy ($hash_parent) = $hash_parents[0];\n\n \tmy $is_combined = (@hash_parents > 1);\n@@ -4938,12 +4965,31 @@ sub git_patchset_body {\n\n \t\t# the patch itself\n \tLINE:\n+\t\tmy @chunk;\n \t\twhile ($patch_line = <$fd>) {\n \t\t\tchomp $patch_line;\n\n \t\t\tnext PATCH if ($patch_line =~ m/^diff /);\n\n-\t\t\tprint format_diff_line($patch_line, \\%from, \\%to);\n+\t\t\tmy ($class, $line) = format_diff_line($patch_line, \\%from, \\%to);\n+\t\t\tif ($is_inline) {\n+\t\t\t\tprint $line;\n+\t\t\t} elsif ($class eq 'add' || $class eq 'rem') {\n+\t\t\t\tpush @chunk, [ $class, $line ];\n+\t\t\t} else {\n+\t\t\t\tif (@chunk) {\n+\t\t\t\t\tprint format_diff_chunk(@chunk);\n+\t\t\t\t\t@chunk = ();\n+\t\t\t\t} elsif ($class eq 'chunk_header') {\n+\t\t\t\t\tprint $line;\n+\t\t\t\t} else {\n+\t\t\t\t\tprint q{<div class='chunk'><div class='old'>},\n+\t\t\t\t\t      $line,\n+\t\t\t\t\t      q{</div><div class='new'>},\n+\t\t\t\t\t      $line,\n+\t\t\t\t\t      q{</div></div>};\n+\t\t\t\t}\n+\t\t\t}\n \t\t}\n\n \t} continue {\n@@ -7022,7 +7068,7 @@ sub git_blobdiff {\n \t\t\t        \"raw\");\n \t\tgit_header_html(undef, $expires);\n \t\tif (defined $hash_base && (my %co = parse_commit($hash_base))) {\n-\t\t\tgit_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);\n+\t\t\tgit_print_page_nav('','', $hash_base,$co{'tree'},$hash_base,\n$formats_nav . diff_nav($input_params{diff_style}));\n \t\t\tgit_print_header_div('commit', esc_html($co{'title'}), $hash_base);\n \t\t} else {\n \t\t\tprint \"<div class=\\\"page_nav\\\"><br/>$formats_nav<br/></div>\\n\";\n@@ -7051,7 +7097,7 @@ sub git_blobdiff {\n \tif ($format eq 'html') {\n \t\tprint \"<div class=\\\"page_body\\\">\\n\";\n\n-\t\tgit_patchset_body($fd, [ \\%diffinfo ], $hash_base, $hash_parent_base);\n+\t\tgit_patchset_body($fd, $input_params{diff_style} eq 'inline', [\n\\%diffinfo ], $hash_base, $hash_parent_base);\n \t\tclose $fd;\n\n \t\tprint \"</div>\\n\"; # class=\"page_body\"\n@@ -7076,6 +7122,22 @@ sub git_blobdiff_plain {\n \tgit_blobdiff('plain');\n }\n\n+sub diff_nav {\n+\tmy ($style) = @_;\n+\n+\tmy %pairs = (inline => 'inline', 'sidebyside' => 'side by side');\n+\tjoin '', ($cgi->start_form({ method => 'get' }),\n+\t          $cgi->hidden('p'),\n+\t          $cgi->hidden('a'),\n+\t          $cgi->hidden('h'),\n+\t          $cgi->hidden('hp'),\n+\t          $cgi->hidden('hb'),\n+\t          $cgi->hidden('hpb'),\n+\t          $cgi->popup_menu('ds', [keys %pairs], $style, \\%pairs),\n+\t          $cgi->submit('change'),\n+\t          $cgi->end_form);\n+}\n+\n sub git_commitdiff {\n \tmy %params = @_;\n \tmy $format = $params{-format} || 'html';\n@@ -7228,7 +7290,7 @@ sub git_commitdiff {\n \t\tmy $ref = format_ref_marker($refs, $co{'id'});\n\n \t\tgit_header_html(undef, $expires);\n-\t\tgit_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);\n+\t\tgit_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash,\n$formats_nav . diff_nav($input_params{diff_style}));\n \t\tgit_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);\n \t\tprint \"<div class=\\\"title_text\\\">\\n\" .\n \t\t      \"<table class=\\\"object_header\\\">\\n\";\n@@ -7282,7 +7344,8 @@ sub git_commitdiff {\n \t\t                  $use_parents ? @{$co{'parents'}} : $hash_parent);\n \t\tprint \"<br/>\\n\";\n\n-\t\tgit_patchset_body($fd, \\@difftree, $hash,\n+\t\tgit_patchset_body($fd, $input_params{diff_style} eq 'inline',\n+                          \\@difftree, $hash,\n \t\t                  $use_parents ? @{$co{'parents'}} : $hash_parent);\n \t\tclose $fd;\n \t\tprint \"</div>\\n\"; # class=\"page_body\"\ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 7d88509..a6872f9 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -618,6 +618,22 @@ div.remote {\n \tcursor: pointer;\n }\n\n+/* side-by-side diff */\n+div.chunk {\n+    overflow: hidden;\n+}\n+\n+div.chunk div.old {\n+    float: left;\n+    width: 50%;\n+    overflow: hidden;\n+}\n+\n+div.chunk div.new {\n+    margin-left: 50%;\n+    width: 50%;\n+}\n+\n\n /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */\n\n-- \n1.7.7.213.g8b0e1.dirty\n"},{"id":"177812","messageId":"7vobxgpixo.fsf@alter.siamese.dyndns.org","threadId":"28698","inReplyTo":"CAFo4x0Kb651CyxoP8wxR36aDe5=Md2UV3qjh+HPo4ad6NB=Emg@mail.gmail.com","subject":"Re: [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-17T00:20:51Z","receivedAt":"2011-10-17T00:20:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kato Kazuyoshi <kato.kazuyoshi@gmail.com> writes:\n\n> @@ -2235,25 +2238,25 @@ sub format_diff_line {\n>  \t\t# combined diff\n>  \t\tmy $prefix = substr($line, 0, scalar @{$from->{'href'}});\n>  \t\tif ($line =~ m/^\\@{3}/) {\n> -\t\t\t$diff_class = \" chunk_header\";\n> +\t\t\t$diff_class = \"chunk_header\";\n>  \t\t} elsif ($line =~ m/^\\\\/) {\n> -\t\t\t$diff_class = \" incomplete\";\n> +\t\t\t$diff_class = \"incomplete\";\n>  \t\t} elsif ($prefix =~ tr/+/+/) {\n> -\t\t\t$diff_class = \" add\";\n> +\t\t\t$diff_class = \"add\";\n>  \t\t} elsif ($prefix =~ tr/-/-/) {\n> -\t\t\t$diff_class = \" rem\";\n> +\t\t\t$diff_class = \"rem\";\n>  \t\t}\n>  \t} else {\n>  \t\t# assume ordinary diff\n>  \t\tmy $char = substr($line, 0, 1);\n>  \t\tif ($char eq '+') {\n> -\t\t\t$diff_class = \" add\";\n> +\t\t\t$diff_class = \"add\";\n>  \t\t} elsif ($char eq '-') {\n> -\t\t\t$diff_class = \" rem\";\n> +\t\t\t$diff_class = \"rem\";\n>  \t\t} elsif ($char eq '@') {\n> -\t\t\t$diff_class = \" chunk_header\";\n> +\t\t\t$diff_class = \"chunk_header\";\n>  \t\t} elsif ($char eq \"\\\\\") {\n> -\t\t\t$diff_class = \" incomplete\";\n> +\t\t\t$diff_class = \"incomplete\";\n>  \t\t}\n>  \t}\n>  \t$line = untabify($line);\n> @@ -2274,7 +2277,7 @@ sub format_diff_line {\n>  \t\t}\n>  \t\t$line = \"<span class=\\\"chunk_info\\\">@@ $from_text $to_text @@</span>\" .\n>  \t\t        \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1) .\n> \"</span>\";\n> -\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n> +\t\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">$line</div>\\n\";\n\nWhat are these changes for, except perhaps to make the patch larger than\nnecesssary to make it harder to review?\n\nIt would leave an unnecessary SP like 'class=\"diff \"' when all the arms of\nif/elsif cascade fall off and $diff_class is left empty. It isn't a major\nissue (I suspect that such a case might even be an error), and I even\nthink the code after the above patch would be easier to read and more\nsensible, but shouldn't that kind of \"style fix\" be in a separate clean-up\npatch that does not introduce any new feature?\n\n> @@ -2307,9 +2310,9 @@ sub format_diff_line {\n>  \t\t}\n>  \t\t$line .= \" $prefix</span>\" .\n>  \t\t         \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1)\n> . \"</span>\";\n> -\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n> +\t\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">$line</div>\\n\";\n>  \t}\n> -\treturn \"<div class=\\\"diff$diff_class\\\">\" . esc_html($line, -nbsp=>1)\n> . \"</div>\\n\";\n> +\treturn $diff_class, \"<div class=\\\"diff $diff_class\\\">\" .\n> esc_html($line, -nbsp=>1) . \"</div>\\n\";\n>  }\n\nAnd everything else, including this hunk to change what is returned from\nthe subroutine belongs to a separate patch that implements the side-by-side\nfeature.\n"},{"id":"177814","messageId":"CAFo4x0+_Uy=yjbO61qj8krS0-iovzC5WnBE8-1n5OzxgGeg6JA@mail.gmail.com","threadId":"28698","inReplyTo":"7vobxgpixo.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Kato Kazuyoshi","fromEmail":"kato.kazuyoshi@gmail.com","sentAt":"2011-10-17T01:15:07Z","receivedAt":"2011-10-17T01:15:07Z","isPatch":true,"sender":{"key":"kato.kazuyoshi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/19111?v=4"},"body":"On Mon, Oct 17, 2011 at 9:20 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> What are these changes for, except perhaps to make the patch larger than\n> necesssary to make it harder to review?\n>\n> It would leave an unnecessary SP like 'class=\"diff \"' when all the arms of\n> if/elsif cascade fall off and $diff_class is left empty. It isn't a major\n> issue (I suspect that such a case might even be an error), and I even\n> think the code after the above patch would be easier to read and more\n> sensible, but shouldn't that kind of \"style fix\" be in a separate clean-up\n> patch that does not introduce any new feature?\n>\n\nSorry. I will separate my patch into two or three commits. Probably like:\n\n- format_diff_line returns a class with an line.\n- remove trailing space from the class.\n- add side-by-side feature and CSS.\n- add form.\n\nThank you for your correction!\n\n-- \nKato Kazuyoshi\n"},{"id":"177816","messageId":"7vbotgper8.fsf@alter.siamese.dyndns.org","threadId":"28698","inReplyTo":"CAFo4x0+_Uy=yjbO61qj8krS0-iovzC5WnBE8-1n5OzxgGeg6JA@mail.gmail.com","subject":"Re: [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-17T01:51:07Z","receivedAt":"2011-10-17T01:51:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kato Kazuyoshi <kato.kazuyoshi@gmail.com> writes:\n\n> Probably like:\n>\n> - format_diff_line returns a class with an line.\n> - remove trailing space from the class.\n> - add side-by-side feature and CSS.\n> - add form.\n>\n> Thank you for your correction!\n\nMy wording came out a bit too strong; I didn't mean to \"correct\" anything.\n\nI think a better organization would be\n\n [1/2] change code so that $diff_class does not have leading SP\n       optionally catch a case where $diff_class stays empty as an error?\n\n [2/2] add side-by-side feature, which would involve:\n  - making format_diff_line() to return $diff_class separately;\n  - necessary addition of CSS;\n  - addition of form to trigger the feature.\n\nI do not think splitting the second patch into pieces smaller than that\nmakes sense.\n\nThanks.\n \n \n"},{"id":"177819","messageId":"CAFo4x0KPP1yZec+98_rn9tG_fhtB-DYAcdi90TwiwUPq+DP90g@mail.gmail.com","threadId":"28698","inReplyTo":"7vbotgper8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Kato Kazuyoshi","fromEmail":"kato.kazuyoshi@gmail.com","sentAt":"2011-10-17T02:33:49Z","receivedAt":"2011-10-17T02:33:49Z","isPatch":true,"sender":{"key":"kato.kazuyoshi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/19111?v=4"},"body":"On Mon, Oct 17, 2011 at 10:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> I think a better organization would be\n>\n>  [1/2] change code so that $diff_class does not have leading SP\n>       optionally catch a case where $diff_class stays empty as an error?\n>\n\nI think we don't have to treat empty $diff_class as an error because\n$diff_class will be an empty when $line is around modification\n(ex. foo or quux).\n\n  foo\n- bar\n+ baz\n  quuz\n\nAnd class attributes are CDATA. \"diff[SP]\" and \"diff\" have same\nmeanings.\nhttp://www.w3.org/TR/html401/struct/global.html#h-7.5.2\nhttp://www.w3.org/TR/html401/types.html#type-cdata\n\n-- \nKato Kazuyoshi\n"},{"id":"177831","messageId":"7v7h44p8vz.fsf@alter.siamese.dyndns.org","threadId":"28698","inReplyTo":"CAFo4x0KPP1yZec+98_rn9tG_fhtB-DYAcdi90TwiwUPq+DP90g@mail.gmail.com","subject":"Re: [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-17T03:57:52Z","receivedAt":"2011-10-17T03:57:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kato Kazuyoshi <kato.kazuyoshi@gmail.com> writes:\n\n> On Mon, Oct 17, 2011 at 10:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> I think a better organization would be\n>>\n>>  [1/2] change code so that $diff_class does not have leading SP\n>>       optionally catch a case where $diff_class stays empty as an error?\n>>\n>\n> I think we don't have to treat empty $diff_class as an error because\n> $diff_class will be an empty when $line is around modification\n> (ex. foo or quux).\n>\n>   foo\n> - bar\n> + baz\n>   quuz\n\nOk. then even though class=\"diff \" is not any different from class=\"diff\",\nI have to say generated html output becomes aesthetically less nice with\nthis change; we may want to do something about it _iff_ it does not take\ntoo much change, but as I said I do not care too much about it.\n"}]}