{"thread":{"id":"28802","subject":"[PATCHv3 0/8] gitweb: side-by-side diff","startedAt":"2011-10-30T23:36:19Z","lastAt":"2011-10-30T23:36:27Z","messageCount":9,"participants":["Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"178536","messageId":"1320017787-18048-1-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":null,"subject":"[PATCHv3 0/8] gitweb: side-by-side diff","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:19Z","receivedAt":"2011-10-30T23:36:19Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"NOTE: As it is feature-freeze period, this patch series is for review.\n\nThis is refinement and extension of Kato Kazuyoshi patch series, sent\noriginally as\n\n  [PATCH/RFC] gitweb: add the ability to show side-by-side diff on commitdiff.\n  http://thread.gmane.org/gmane.comp.version-control.git/183744\n\nand then refined and split into two-patch series\n\n  [PATCH/RFC 1/2] gitweb: change format_diff_line() to remove leading SP from $diff_class\n  http://thread.gmane.org/gmane.comp.version-control.git/183770\n\n  [PATCH 2/2] gitweb: add a feature to show side-by-side diff\n  http://thread.gmane.org/gmane.comp.version-control.git/183769\n\nThis patch series originally started as rebasing second patch in above\ntwo part series on top of diff line classification refactoring\nsuggested by me and proposed by Junio.  Then I thought about putting\nall code printing side-by-side diff in print_sidebyside_diff_chunk()\nsubroutine, then...\n\n\nMain changes from v2 version from Kato Kazuyoshi:\n\n* Built on top of refactoring of code related to diff\n  output formatting (patches 1 and 2)\n\n* Code reworked so that is in my opinion easier to follow; gitweb now\n  handles merges and diffs with incomplete lines correctly (patch 3)\n  - well, it handles merges by turning off side-by-side diff for them.\n\n* Adding background color to distinguish empty context lines from\n  vertical align, similarly to e.g.\n    http://community.activestate.com/files/images/sbsdiffs.png\n  but without refinement (word diff of changes).\n\n* Adds some very basic test for side-by-side diff (patch 6 (and 5))\n\n* Split adding navigation into a separate commit, and uses [nav] links\n  rather than HTML form for selecting between inline and side-by-side\n  diff style (diff 8).  Thanks to more thorough use of href(-replay=>1,..)\n  (patch 7) style of diff should be preserved with this series.\n\n\nPlease excuse me for essentially hijacking this patch series.\n\n\nP.S. I really, really need to finish work on splitting gitweb into\nsmaller pieces.  With around 8,000 lines it becomes quite unwieldy.\nBut this would probably need total rework of error handling (the\ndie_error subroutine), and that would need another changes, etc....\n\n\nPull request:\n~~~~~~~~~~~~~\nThese changes are available in the git repository(-y/+ies) at:\n  git://repo.or.cz/git/jnareb-git.git gitweb/side-by-side-diff-v4\n  git://github.com/jnareb/git         gitweb/side-by-side-diff-v4\n\nTable of contents:\n~~~~~~~~~~~~~~~~~~\n  [PATCHv3 1/8] gitweb: Refactor diff body line classification\n  [PATCHv3 2/8] gitweb: Extract formatting of diff chunk header\n  [PATCHv3 3/8] gitweb: Add a feature to show side-by-side diff\n  [PATCHv3 4/8] gitweb: Give side-by-side diff extra CSS styling\n  [PATCHv3 5/8] t9500: Add test for handling incomplete lines in diff\n   by gitweb\n  [PATCHv3 6/8] t9500: Add basic sanity tests for side-by-side diff in\n   gitweb\n  [PATCHv3 7/8] gitweb: Use href(-replay=>1,...) for formats links in\n   \"commitdiff\"\n  [PATCHv3 8/8] gitweb: Add navigation to select side-by-side diff\n\nShortlog:\n~~~~~~~~~\nJakub Narebski (6):\n  gitweb: Refactor diff body line classification\n  gitweb: Extract formatting of diff chunk header\n  gitweb: Give side-by-side diff extra CSS styling\n  t9500: Add test for handling incomplete lines in diff by gitweb\n  t9500: Add basic sanity tests for side-by-side diff in gitweb\n  gitweb: Use href(-replay=>1,...) for formats links in \"commitdiff\"\n\nKato Kazuyoshi (2):\n  gitweb: Add a feature to show side-by-side diff\n  gitweb: Add navigation to select side-by-side diff\n\nDiffstat:\n~~~~~~~~~\n gitweb/gitweb.perl                     |  339 +++++++++++++++++++++++--------\n gitweb/static/gitweb.css               |   30 +++\n t/t9500-gitweb-standalone-no-errors.sh |   73 +++++++-\n 3 files changed, 353 insertions(+), 89 deletions(-)\n\n-- \n1.7.6\n"},{"id":"178537","messageId":"1320017787-18048-2-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 1/8] gitweb: Refactor diff body line classification","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:20Z","receivedAt":"2011-10-30T23:36:20Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Simplify classification of diff line body in format_diff_line(),\nreplacing two long if-elsif chains (one for ordinary diff and one for\ncombined diff of a merge commit) with a single regexp match.  Refactor\nthis code into diff_line_class() function.\n\nWhile at it:\n\n* Fix an artifact in that $diff_class included leading space to be\n  able to compose classes like this \"class=\\\"diff$diff_class\\\"', even\n  when $diff_class was an empty string.  This made code unnecessary\n  ugly: $diff_class is now just class name or an empty string.\n\n* Introduce \"ctx\" class for context lines ($diff_class was set to \"\"\n  in this case before this commit).\n\nIdea and initial code by Junio C Hamano, polish and testing by Jakub\nNarebski.  Inspired by patch adding side-by-side diff by Kato Kazuyoshi,\nwhich required $diff_class to be name of class without extra space.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is new in this version of side-by-side diff series.\n\nJunio, as your proposal was of \"what if\" variety, and not as a proper\npatch, I have taken authorship (after reqorking and testing it).\nShould I revert authorship to you?\n\n gitweb/gitweb.perl |   67 ++++++++++++++++++++++++++++-----------------------\n 1 files changed, 37 insertions(+), 30 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 4f0c3bd..914fd4c 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2225,40 +2225,47 @@ sub format_diff_cc_simplified {\n \treturn $result;\n }\n \n+sub diff_line_class {\n+\tmy ($line, $from, $to) = @_;\n+\n+\t# ordinary diff\n+\tmy $num_sign = 1;\n+\t# combined diff\n+\tif ($from && $to && ref($from->{'href'}) eq \"ARRAY\") {\n+\t\t$num_sign = scalar @{$from->{'href'}};\n+\t}\n+\n+\tmy @diff_line_classifier = (\n+\t\t{ regexp => qr/^\\@\\@{$num_sign} /, class => \"chunk_header\"},\n+\t\t{ regexp => qr/^\\\\/,               class => \"incomplete\"  },\n+\t\t{ regexp => qr/^ {$num_sign}/,     class => \"ctx\" },\n+\t\t# classifier for context must come before classifier add/rem,\n+\t\t# or we would have to use more complicated regexp, for example\n+\t\t# qr/(?= {0,$m}\\+)[+ ]{$num_sign}/, where $m = $num_sign - 1;\n+\t\t{ regexp => qr/^[+ ]{$num_sign}/,   class => \"add\" },\n+\t\t{ regexp => qr/^[- ]{$num_sign}/,   class => \"rem\" },\n+\t);\n+\tfor my $clsfy (@diff_line_classifier) {\n+\t\treturn $clsfy->{'class'}\n+\t\t\tif ($line =~ $clsfy->{'regexp'});\n+\t}\n+\n+\t# fallback\n+\treturn \"\";\n+}\n+\n # format patch (diff) line (not to be used for diff headers)\n sub format_diff_line {\n \tmy $line = shift;\n \tmy ($from, $to) = @_;\n-\tmy $diff_class = \"\";\n+\n+\tmy $diff_class = diff_line_class($line, $from, $to);\n+\tmy $diff_classes = \"diff\";\n+\t$diff_classes .= \" $diff_class\" if ($diff_class);\n \n \tchomp $line;\n-\n-\tif ($from && $to && ref($from->{'href'}) eq \"ARRAY\") {\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} elsif ($line =~ m/^\\\\/) {\n-\t\t\t$diff_class = \" incomplete\";\n-\t\t} elsif ($prefix =~ tr/+/+/) {\n-\t\t\t$diff_class = \" add\";\n-\t\t} elsif ($prefix =~ tr/-/-/) {\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} elsif ($char eq '-') {\n-\t\t\t$diff_class = \" rem\";\n-\t\t} elsif ($char eq '@') {\n-\t\t\t$diff_class = \" chunk_header\";\n-\t\t} elsif ($char eq \"\\\\\") {\n-\t\t\t$diff_class = \" incomplete\";\n-\t\t}\n-\t}\n \t$line = untabify($line);\n+\n \tif ($from && $to && $line =~ m/^\\@{2} /) {\n \t\tmy ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =\n \t\t\t$line =~ m/^\\@{2} (-(\\d+)(?:,(\\d+))?) (\\+(\\d+)(?:,(\\d+))?) \\@{2}(.*)$/;\n@@ -2276,7 +2283,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) . \"</span>\";\n-\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n+\t\treturn \"<div class=\\\"$diff_classes\\\">$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@@ -2309,9 +2316,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) . \"</span>\";\n-\t\treturn \"<div class=\\\"diff$diff_class\\\">$line</div>\\n\";\n+\t\treturn \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\n \t}\n-\treturn \"<div class=\\\"diff$diff_class\\\">\" . esc_html($line, -nbsp=>1) . \"</div>\\n\";\n+\treturn \"<div class=\\\"$diff_classes\\\">\" . esc_html($line, -nbsp=>1) . \"</div>\\n\";\n }\n \n # Generates undef or something like \"_snapshot_\" or \"snapshot (_tbz2_ _zip_)\",\n-- \n1.7.6\n"},{"id":"178538","messageId":"1320017787-18048-3-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 2/8] gitweb: Extract formatting of diff chunk header","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:21Z","receivedAt":"2011-10-30T23:36:21Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Refactor main parts of HTML-formatting for diff chunk headers\n(formatting means here adding links and syntax hightlighting) into\nseparate subroutines:\n\n * format_unidiff_chunk_header for ordinary diff,\n * format_cc_diff_chunk_header for combined diff\n   (more than one parent)\n\nThis makes format_diff_line() subroutine easier to follow.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is new in this version of side-by-side diff series.\n\nAfter those changes format_diff_line() fits in one page (has less than\n25 lines).  Nice, isn't it?\n\n gitweb/gitweb.perl |  114 ++++++++++++++++++++++++++++++---------------------\n 1 files changed, 67 insertions(+), 47 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 914fd4c..95d278a 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2254,6 +2254,69 @@ sub diff_line_class {\n \treturn \"\";\n }\n \n+# assumes that $from and $to are defined and correctly filled,\n+# and that $line holds a line of chunk header for unified diff\n+sub format_unidiff_chunk_header {\n+\tmy ($line, $from, $to) = @_;\n+\n+\tmy ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =\n+\t\t$line =~ m/^\\@{2} (-(\\d+)(?:,(\\d+))?) (\\+(\\d+)(?:,(\\d+))?) \\@{2}(.*)$/;\n+\n+\t$from_lines = 0 unless defined $from_lines;\n+\t$to_lines   = 0 unless defined $to_lines;\n+\n+\tif ($from->{'href'}) {\n+\t\t$from_text = $cgi->a({-href=>\"$from->{'href'}#l$from_start\",\n+\t\t                     -class=>\"list\"}, $from_text);\n+\t}\n+\tif ($to->{'href'}) {\n+\t\t$to_text   = $cgi->a({-href=>\"$to->{'href'}#l$to_start\",\n+\t\t                     -class=>\"list\"}, $to_text);\n+\t}\n+\t$line = \"<span class=\\\"chunk_info\\\">@@ $from_text $to_text @@</span>\" .\n+\t        \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1) . \"</span>\";\n+\treturn $line;\n+}\n+\n+# assumes that $from and $to are defined and correctly filled,\n+# and that $line holds a line of chunk header for combined diff\n+sub format_cc_diff_chunk_header {\n+\tmy ($line, $from, $to) = @_;\n+\n+\tmy ($prefix, $ranges, $section) = $line =~ m/^(\\@+) (.*?) \\@+(.*)$/;\n+\tmy (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);\n+\n+\t@from_text = split(' ', $ranges);\n+\tfor (my $i = 0; $i < @from_text; ++$i) {\n+\t\t($from_start[$i], $from_nlines[$i]) =\n+\t\t\t(split(',', substr($from_text[$i], 1)), 0);\n+\t}\n+\n+\t$to_text   = pop @from_text;\n+\t$to_start  = pop @from_start;\n+\t$to_nlines = pop @from_nlines;\n+\n+\t$line = \"<span class=\\\"chunk_info\\\">$prefix \";\n+\tfor (my $i = 0; $i < @from_text; ++$i) {\n+\t\tif ($from->{'href'}[$i]) {\n+\t\t\t$line .= $cgi->a({-href=>\"$from->{'href'}[$i]#l$from_start[$i]\",\n+\t\t\t                  -class=>\"list\"}, $from_text[$i]);\n+\t\t} else {\n+\t\t\t$line .= $from_text[$i];\n+\t\t}\n+\t\t$line .= \" \";\n+\t}\n+\tif ($to->{'href'}) {\n+\t\t$line .= $cgi->a({-href=>\"$to->{'href'}#l$to_start\",\n+\t\t                  -class=>\"list\"}, $to_text);\n+\t} else {\n+\t\t$line .= $to_text;\n+\t}\n+\t$line .= \" $prefix</span>\" .\n+\t         \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1) . \"</span>\";\n+\treturn $line;\n+}\n+\n # format patch (diff) line (not to be used for diff headers)\n sub format_diff_line {\n \tmy $line = shift;\n@@ -2267,56 +2330,13 @@ sub format_diff_line {\n \t$line = untabify($line);\n \n \tif ($from && $to && $line =~ m/^\\@{2} /) {\n-\t\tmy ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =\n-\t\t\t$line =~ m/^\\@{2} (-(\\d+)(?:,(\\d+))?) (\\+(\\d+)(?:,(\\d+))?) \\@{2}(.*)$/;\n-\n-\t\t$from_lines = 0 unless defined $from_lines;\n-\t\t$to_lines   = 0 unless defined $to_lines;\n-\n-\t\tif ($from->{'href'}) {\n-\t\t\t$from_text = $cgi->a({-href=>\"$from->{'href'}#l$from_start\",\n-\t\t\t                     -class=>\"list\"}, $from_text);\n-\t\t}\n-\t\tif ($to->{'href'}) {\n-\t\t\t$to_text   = $cgi->a({-href=>\"$to->{'href'}#l$to_start\",\n-\t\t\t                     -class=>\"list\"}, $to_text);\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) . \"</span>\";\n+\t\t$line = format_unidiff_chunk_header($line, $from, $to);\n \t\treturn \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\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-\n-\t\t@from_text = split(' ', $ranges);\n-\t\tfor (my $i = 0; $i < @from_text; ++$i) {\n-\t\t\t($from_start[$i], $from_nlines[$i]) =\n-\t\t\t\t(split(',', substr($from_text[$i], 1)), 0);\n-\t\t}\n-\n-\t\t$to_text   = pop @from_text;\n-\t\t$to_start  = pop @from_start;\n-\t\t$to_nlines = pop @from_nlines;\n-\n-\t\t$line = \"<span class=\\\"chunk_info\\\">$prefix \";\n-\t\tfor (my $i = 0; $i < @from_text; ++$i) {\n-\t\t\tif ($from->{'href'}[$i]) {\n-\t\t\t\t$line .= $cgi->a({-href=>\"$from->{'href'}[$i]#l$from_start[$i]\",\n-\t\t\t\t                  -class=>\"list\"}, $from_text[$i]);\n-\t\t\t} else {\n-\t\t\t\t$line .= $from_text[$i];\n-\t\t\t}\n-\t\t\t$line .= \" \";\n-\t\t}\n-\t\tif ($to->{'href'}) {\n-\t\t\t$line .= $cgi->a({-href=>\"$to->{'href'}#l$to_start\",\n-\t\t\t                  -class=>\"list\"}, $to_text);\n-\t\t} else {\n-\t\t\t$line .= $to_text;\n-\t\t}\n-\t\t$line .= \" $prefix</span>\" .\n-\t\t         \"<span class=\\\"section\\\">\" . esc_html($section, -nbsp=>1) . \"</span>\";\n+\t\t$line = format_cc_diff_chunk_header($line, $from, $to);\n \t\treturn \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\n+\n \t}\n \treturn \"<div class=\\\"$diff_classes\\\">\" . esc_html($line, -nbsp=>1) . \"</div>\\n\";\n }\n-- \n1.7.6\n"},{"id":"178539","messageId":"1320017787-18048-4-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 3/8] gitweb: Add a feature to show side-by-side diff","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:22Z","receivedAt":"2011-10-30T23:36:22Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: Kato Kazuyoshi <kato.kazuyoshi@gmail.com>\n\nThis commits adds to support for showing \"side-by-side\" style diff.\nCurrently you have to hand-craft the URL; navigation for selecting\ndiff style is to be added in the next commit.\n\n\nThe diff output in unified format from \"git diff-tree\" is reorganized to\nside-by-side style chunk by chunk with format_sidebyside_diff_chunk().\nThis reorganization requires knowledge about diff line classification,\nso format_diff_line() was renamed to process_diff_line(), and changed to\nreturn tuple (list) consisting of class of diff line and of\nHTML-formatted (but not wrapped in <div class=\"diff ...\">...</div>) diff\nline.  Wrapping is now done by caller, i.e. git_patchset_body().\n\nGitweb uses float+margin CSS-based layout for \"side by side\" diff.\n\n\nYou can specify style of diff with \"ds\" ('diff_style') query\nparameter.  Currently supported values are 'inline' and 'sidebyside';\nthe default is 'inline'.\n\nAnother solution would be to use \"opt\" ('extra_options') for that...\nthough current use of it in gitweb seems to suggest that \"opt\" is more\nabout passing extra options to underlying git commands, and \"git diff\"\ndoesn't support '--side-by-side' like GNU diff does, (yet?).\n\nSigned-off-by: Kato Kazuyoshi <kato.kazuyoshi@gmail.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nHeavily changed from original submission by Kato Kazuyoshi, but the\nmain idea, code structure (somewhat) and CSS-base layout remains.\nThat's why authorship remains with him; Kato, please speak up if you\nwould like to change this.\n\nThe decision to move some of formatting outside process_diff_line()\n(formerly format_diff_line()) was not really necessary, in hindsight...\n\n gitweb/gitweb.perl       |  116 +++++++++++++++++++++++++++++++++++++++++----\n gitweb/static/gitweb.css |   17 +++++++\n 2 files changed, 122 insertions(+), 11 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 95d278a..68629f6 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -759,6 +759,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@@ -2317,28 +2318,27 @@ sub format_cc_diff_chunk_header {\n \treturn $line;\n }\n \n-# format patch (diff) line (not to be used for diff headers)\n-sub format_diff_line {\n+# process patch (diff) line (not to be used for diff headers),\n+# returning class and HTML-formatted (but not wrapped) line\n+sub process_diff_line {\n \tmy $line = shift;\n \tmy ($from, $to) = @_;\n \n \tmy $diff_class = diff_line_class($line, $from, $to);\n-\tmy $diff_classes = \"diff\";\n-\t$diff_classes .= \" $diff_class\" if ($diff_class);\n \n \tchomp $line;\n \t$line = untabify($line);\n \n \tif ($from && $to && $line =~ m/^\\@{2} /) {\n \t\t$line = format_unidiff_chunk_header($line, $from, $to);\n-\t\treturn \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\n+\t\treturn $diff_class, $line;\n \n \t} elsif ($from && $to && $line =~ m/^\\@{3}/) {\n \t\t$line = format_cc_diff_chunk_header($line, $from, $to);\n-\t\treturn \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\n+\t\treturn $diff_class, $line;\n \n \t}\n-\treturn \"<div class=\\\"$diff_classes\\\">\" . esc_html($line, -nbsp=>1) . \"</div>\\n\";\n+\treturn $diff_class, esc_html($line, -nbsp=>1);\n }\n \n # Generates undef or something like \"_snapshot_\" or \"snapshot (_tbz2_ _zip_)\",\n@@ -4860,8 +4860,78 @@ sub git_difftree_body {\n \tprint \"</table>\\n\";\n }\n \n+sub print_sidebyside_diff_chunk {\n+\tmy @chunk = @_;\n+\tmy (@ctx, @rem, @add);\n+\n+\treturn unless @chunk;\n+\n+\t# incomplete last line might be among removed or added lines,\n+\t# or both, or among context lines: find which\n+\tfor (my $i = 1; $i < @chunk; $i++) {\n+\t\tif ($chunk[$i][0] eq 'incomplete') {\n+\t\t\t$chunk[$i][0] = $chunk[$i-1][0];\n+\t\t}\n+\t}\n+\n+\t# guardian\n+\tpush @chunk, [\"\", \"\"];\n+\n+\tforeach my $line_info (@chunk) {\n+\t\tmy ($class, $line) = @$line_info;\n+\n+\t\t# print chunk headers\n+\t\tif ($class && $class eq 'chunk_header') {\n+\t\t\tprint $line;\n+\t\t\tnext;\n+\t\t}\n+\n+\t\t## print from accumulator when type of class of lines change\n+\t\t# empty contents block on start rem/add block, or end of chunk\n+\t\tif (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {\n+\t\t\tprint join '',\n+\t\t\t\t'<div class=\"chunk_block\">',\n+\t\t\t\t\t'<div class=\"old\">',\n+\t\t\t\t\t@ctx,\n+\t\t\t\t\t'</div>',\n+\t\t\t\t\t'<div class=\"new\">',\n+\t\t\t\t\t@ctx,\n+\t\t\t\t\t'</div>',\n+\t\t\t\t'</div>';\n+\t\t\t@ctx = ();\n+\t\t}\n+\t\t# empty add/rem block on start context block, or end of chunk\n+\t\tif ((@rem || @add) && (!$class || $class eq 'ctx')) {\n+\t\t\tprint join '',\n+\t\t\t\t'<div class=\"chunk_block\">',\n+\t\t\t\t\t'<div class=\"old\">',\n+\t\t\t\t\t@rem,\n+\t\t\t\t\t'</div>',\n+\t\t\t\t\t'<div class=\"new\">',\n+\t\t\t\t\t@add,\n+\t\t\t\t\t'</div>',\n+\t\t\t\t'</div>';\n+\t\t\t@rem = @add = ();\n+\t\t}\n+\n+\t\t## adding lines to accumulator\n+\t\t# guardian value\n+\t\tlast unless $line;\n+\t\t# rem, add or change\n+\t\tif ($class eq 'rem') {\n+\t\t\tpush @rem, $line;\n+\t\t} elsif ($class eq 'add') {\n+\t\t\tpush @add, $line;\n+\t\t}\n+\t\t# context line\n+\t\tif ($class eq 'ctx') {\n+\t\t\tpush @ctx, $line;\n+\t\t}\n+\t}\n+}\n+\n sub git_patchset_body {\n-\tmy ($fd, $difftree, $hash, @hash_parents) = @_;\n+\tmy ($fd, $diff_style, $difftree, $hash, @hash_parents) = @_;\n \tmy ($hash_parent) = $hash_parents[0];\n \n \tmy $is_combined = (@hash_parents > 1);\n@@ -4871,6 +4941,7 @@ sub git_patchset_body {\n \tmy $diffinfo;\n \tmy $to_name;\n \tmy (%from, %to);\n+\tmy @chunk; # for side-by-side diff\n \n \tprint \"<div class=\\\"patchset\\\">\\n\";\n \n@@ -4977,10 +5048,29 @@ sub git_patchset_body {\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) = process_diff_line($patch_line, \\%from, \\%to);\n+\t\t\tmy $diff_classes = \"diff\";\n+\t\t\t$diff_classes .= \" $class\" if ($class);\n+\t\t\t$line = \"<div class=\\\"$diff_classes\\\">$line</div>\\n\";\n+\n+\t\t\tif ($diff_style eq 'sidebyside' && !$is_combined) {\n+\t\t\t\tif ($class eq 'chunk_header') {\n+\t\t\t\t\tprint_sidebyside_diff_chunk(@chunk);\n+\t\t\t\t\t@chunk = ( [ $class, $line ] );\n+\t\t\t\t} else {\n+\t\t\t\t\tpush @chunk, [ $class, $line ];\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\t# default 'inline' style and unknown styles\n+\t\t\t\tprint $line;\n+\t\t\t}\n \t\t}\n \n \t} continue {\n+\t\tif (@chunk) {\n+\t\t\tprint_sidebyside_diff_chunk(@chunk);\n+\t\t\t@chunk = ();\n+\t\t}\n \t\tprint \"</div>\\n\"; # class=\"patch\"\n \t}\n \n@@ -6976,6 +7066,7 @@ sub git_object {\n \n sub git_blobdiff {\n \tmy $format = shift || 'html';\n+\tmy $diff_style = $input_params{'diff_style'} || 'inline';\n \n \tmy $fd;\n \tmy @difftree;\n@@ -7085,7 +7176,8 @@ 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, $diff_style,\n+\t\t                  [ \\%diffinfo ], $hash_base, $hash_parent_base);\n \t\tclose $fd;\n \n \t\tprint \"</div>\\n\"; # class=\"page_body\"\n@@ -7113,6 +7205,7 @@ sub git_blobdiff_plain {\n sub git_commitdiff {\n \tmy %params = @_;\n \tmy $format = $params{-format} || 'html';\n+\tmy $diff_style = $input_params{'diff_style'} || 'inline';\n \n \tmy ($patch_max) = gitweb_get_feature('patches');\n \tif ($format eq 'patch') {\n@@ -7316,7 +7409,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, $diff_style,\n+\t\t                  \\@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..21842a6 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -475,6 +475,23 @@ div.diff.nodifferences {\n \tcolor: #600000;\n }\n \n+/* side-by-side diff */\n+div.chunk_block {\n+\toverflow: hidden;\n+}\n+\n+div.chunk_block div.old {\n+\tfloat: left;\n+\twidth: 50%;\n+\toverflow: hidden;\n+}\n+\n+div.chunk_block div.new {\n+\tmargin-left: 50%;\n+\twidth: 50%;\n+}\n+\n+\n div.index_include {\n \tborder: solid #d9d8d1;\n \tborder-width: 0px 0px 1px;\n-- \n1.7.6\n"},{"id":"178540","messageId":"1320017787-18048-5-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 4/8] gitweb: Give side-by-side diff extra CSS styling","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:23Z","receivedAt":"2011-10-30T23:36:23Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Use separate background colors for pure removal, pure addition and\nchange for side-by-side diff.  This makes reading such diff easier,\nallowing to easily distinguish empty lines in diff from vertical\nwhitespace used to align chunk blocks.\n\nNote that if lines in diff were numbered, the absence of line numbers\n[for one side] would help in distinguishing empty lines from vertical\nalign.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is new in this version of series.\n\nNote that the code could probably be written with less duplication, at\nthe cost of being more complicated.  I think it is worth tradeoff as\nwritten here.\n\nFor how output looks like, compare for example:\n\n  http://confluence.atlassian.com/display/FISHEYE/Using+Side+by+Side+Diff+View\n\n gitweb/gitweb.perl       |   39 +++++++++++++++++++++++++++++----------\n gitweb/static/gitweb.css |   13 +++++++++++++\n 2 files changed, 42 insertions(+), 10 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 68629f6..f69ed08 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -4890,7 +4890,7 @@ sub print_sidebyside_diff_chunk {\n \t\t# empty contents block on start rem/add block, or end of chunk\n \t\tif (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {\n \t\t\tprint join '',\n-\t\t\t\t'<div class=\"chunk_block\">',\n+\t\t\t\t'<div class=\"chunk_block ctx\">',\n \t\t\t\t\t'<div class=\"old\">',\n \t\t\t\t\t@ctx,\n \t\t\t\t\t'</div>',\n@@ -4902,15 +4902,34 @@ sub print_sidebyside_diff_chunk {\n \t\t}\n \t\t# empty add/rem block on start context block, or end of chunk\n \t\tif ((@rem || @add) && (!$class || $class eq 'ctx')) {\n-\t\t\tprint join '',\n-\t\t\t\t'<div class=\"chunk_block\">',\n-\t\t\t\t\t'<div class=\"old\">',\n-\t\t\t\t\t@rem,\n-\t\t\t\t\t'</div>',\n-\t\t\t\t\t'<div class=\"new\">',\n-\t\t\t\t\t@add,\n-\t\t\t\t\t'</div>',\n-\t\t\t\t'</div>';\n+\t\t\tif (!@add) {\n+\t\t\t\t# pure removal\n+\t\t\t\tprint join '',\n+\t\t\t\t\t'<div class=\"chunk_block rem\">',\n+\t\t\t\t\t\t'<div class=\"old\">',\n+\t\t\t\t\t\t@rem,\n+\t\t\t\t\t\t'</div>',\n+\t\t\t\t\t'</div>';\n+\t\t\t} elsif (!@rem) {\n+\t\t\t\t# pure addition\n+\t\t\t\tprint join '',\n+\t\t\t\t\t'<div class=\"chunk_block add\">',\n+\t\t\t\t\t\t'<div class=\"new\">',\n+\t\t\t\t\t\t@add,\n+\t\t\t\t\t\t'</div>',\n+\t\t\t\t\t'</div>';\n+\t\t\t} else {\n+\t\t\t\t# assume that it is change\n+\t\t\t\tprint join '',\n+\t\t\t\t\t'<div class=\"chunk_block chg\">',\n+\t\t\t\t\t\t'<div class=\"old\">',\n+\t\t\t\t\t\t@rem,\n+\t\t\t\t\t\t'</div>',\n+\t\t\t\t\t\t'<div class=\"new\">',\n+\t\t\t\t\t\t@add,\n+\t\t\t\t\t\t'</div>',\n+\t\t\t\t\t'</div>';\n+\t\t\t}\n \t\t\t@rem = @add = ();\n \t\t}\n \ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 21842a6..c7827e8 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -491,6 +491,19 @@ div.chunk_block div.new {\n \twidth: 50%;\n }\n \n+div.chunk_block.rem div.old div.diff.rem {\n+\tbackground-color: #fff5f5;\n+}\n+div.chunk_block.add div.new div.diff.add {\n+\tbackground-color: #f8fff8;\n+}\n+div.chunk_block.chg div     div.diff {\n+\tbackground-color: #fffff0;\n+}\n+div.chunk_block.ctx div     div.diff.ctx {\n+\tcolor: #404040;\n+}\n+\n \n div.index_include {\n \tborder: solid #d9d8d1;\n-- \n1.7.6\n"},{"id":"178541","messageId":"1320017787-18048-6-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 5/8] t9500: Add test for handling incomplete lines in diff by gitweb","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:24Z","receivedAt":"2011-10-30T23:36:24Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Check that \"commitdiff\" action in gitweb can handle (without errors)\nincomplete lines as added and removed lines, and as context lines.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nNew in this series, and independent on side-by-side stuff.\n\n t/t9500-gitweb-standalone-no-errors.sh |   47 ++++++++++++++++++++++++++++++++\n 1 files changed, 47 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh\nindex 5329715..c731507 100755\n--- a/t/t9500-gitweb-standalone-no-errors.sh\n+++ b/t/t9500-gitweb-standalone-no-errors.sh\n@@ -274,6 +274,53 @@ test_expect_success \\\n \t'gitweb_run \"p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar\"'\n \n # ----------------------------------------------------------------------\n+# commitdiff testing (incomplete lines)\n+\n+test_expect_success 'setup incomplete lines' '\n+\tcat >file<<-\\EOF &&\n+\tDominus regit me,\n+\tet nihil mihi deerit.\n+\tIn loco pascuae ibi me collocavit,\n+\tsuper aquam refectionis educavit me;\n+\tanimam meam convertit,\n+\tdeduxit me super semitas jusitiae,\n+\tpropter nomen suum.\n+\tCHANGE_ME\n+\tEOF\n+\tgit commit -a -m \"Preparing for incomplete lines\" &&\n+\techo \"incomplete\" | tr -d \"\\\\012\" >>file &&\n+\tgit commit -a -m \"Add incomplete line\" &&\n+\tgit tag incomplete_lines_add &&\n+\tsed -e s/CHANGE_ME/change_me/ <file >file+ &&\n+\tmv -f file+ file &&\n+\tgit commit -a -m \"Incomplete context line\" &&\n+\tgit tag incomplete_lines_ctx &&\n+\techo \"Dominus regit me,\" >file &&\n+\techo \"incomplete line\" | tr -d \"\\\\012\" >>file &&\n+\tgit commit -a -m \"Change incomplete line\" &&\n+\tgit tag incomplete_lines_chg\n+\techo \"Dominus regit me,\" >file &&\n+\tgit commit -a -m \"Remove incomplete line\" &&\n+\tgit tag incomplete_lines_rem\n+'\n+\n+test_expect_success 'commitdiff(1): addition of incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_add\"\n+'\n+\n+test_expect_success 'commitdiff(1): incomplete line as context line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_ctx\"\n+'\n+\n+test_expect_success 'commitdiff(1): change incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_chg\"\n+'\n+\n+test_expect_success 'commitdiff(1): removal of incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_rem\"\n+'\n+\n+# ----------------------------------------------------------------------\n # commit, commitdiff: merge, large\n test_expect_success \\\n \t'Create a merge' \\\n-- \n1.7.6\n"},{"id":"178542","messageId":"1320017787-18048-7-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 6/8] t9500: Add basic sanity tests for side-by-side diff in gitweb","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:25Z","receivedAt":"2011-10-30T23:36:25Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Test that side-by-side diff can deal with incomplete lines (and while\nat it with pure addition, pure removal, and change), and with merge\ncommits, producing no errors or warnings.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is new in this version.\n\nNote this while it does not check if the output is sane, it can help\nwith manual check; just run test with `--debug' option, and point\ngitweb to \"t/trash\\ directory.t9500-gitweb-standalone-no-errors/.git/\"\nrepository.\n\n t/t9500-gitweb-standalone-no-errors.sh |   26 +++++++++++++++++++++++++-\n 1 files changed, 25 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh\nindex c731507..ab24917 100755\n--- a/t/t9500-gitweb-standalone-no-errors.sh\n+++ b/t/t9500-gitweb-standalone-no-errors.sh\n@@ -329,7 +329,8 @@ test_expect_success \\\n \t git add b &&\n \t git commit -a -m \"On branch\" &&\n \t git checkout master &&\n-\t git pull . b'\n+\t git pull . b &&\n+\t git tag merge_commit'\n \n test_expect_success \\\n \t'commit(0): merge commit' \\\n@@ -379,6 +380,29 @@ test_expect_success \\\n \t'gitweb_run \"p=.git;a=commitdiff;h=b\"'\n \n # ----------------------------------------------------------------------\n+# side-by-side diff\n+\n+test_expect_success 'side-by-side: addition of incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_add;ds=sidebyside\"\n+'\n+\n+test_expect_success 'side-by-side: incomplete line as context line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_ctx;ds=sidebyside\"\n+'\n+\n+test_expect_success 'side-by-side: changed incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_chg;ds=sidebyside\"\n+'\n+\n+test_expect_success 'side-by-side: removal of incomplete line' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=incomplete_lines_rem;ds=sidebyside\"\n+'\n+\n+test_expect_success 'side-by-side: merge commit' '\n+\tgitweb_run \"p=.git;a=commitdiff;h=merge_commit;ds=sidebyside\"\n+'\n+\n+# ----------------------------------------------------------------------\n # tags testing\n \n test_expect_success \\\n-- \n1.7.6\n"},{"id":"178543","messageId":"1320017787-18048-8-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 7/8] gitweb: Use href(-replay=>1,...) for formats links in \"commitdiff\"","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:26Z","receivedAt":"2011-10-30T23:36:26Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Use href(-replay->1,...) in (sub)navigation links (like changing style\nof view, or going to parent commit) so that extra options are\npreserved.\n\nThis is needed so clicking on such (sub)navigation link would preserve\nstyle of diff; for example when using \"side-by-side\" diff style then\ngoing to parent commit would now also use this style.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is new in this series, and independent on side-by-side stuff.\nIt can (and perhaps should) be moved earlier in this series.\n\n gitweb/gitweb.perl |   16 ++++++++--------\n 1 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f69ed08..ffaea45 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -7267,8 +7267,8 @@ sub git_commitdiff {\n \t\t\t\t}\n \t\t\t}\n \t\t\t$formats_nav .= ': ' .\n-\t\t\t\t$cgi->a({-href => href(action=>\"commitdiff\",\n-\t\t\t\t                       hash=>$hash_parent)},\n+\t\t\t\t$cgi->a({-href => href(-replay=>1,\n+\t\t\t\t                       hash=>$hash_parent, hash_base=>undef)},\n \t\t\t\t        esc_html($hash_parent_short)) .\n \t\t\t\t')';\n \t\t} elsif (!$co{'parent'}) {\n@@ -7278,28 +7278,28 @@ sub git_commitdiff {\n \t\t\t# single parent commit\n \t\t\t$formats_nav .=\n \t\t\t\t' (parent: ' .\n-\t\t\t\t$cgi->a({-href => href(action=>\"commitdiff\",\n-\t\t\t\t                       hash=>$co{'parent'})},\n+\t\t\t\t$cgi->a({-href => href(-replay=>1,\n+\t\t\t\t                       hash=>$co{'parent'}, hash_base=>undef)},\n \t\t\t\t        esc_html(substr($co{'parent'}, 0, 7))) .\n \t\t\t\t')';\n \t\t} else {\n \t\t\t# merge commit\n \t\t\tif ($hash_parent eq '--cc') {\n \t\t\t\t$formats_nav .= ' | ' .\n-\t\t\t\t\t$cgi->a({-href => href(action=>\"commitdiff\",\n+\t\t\t\t\t$cgi->a({-href => href(-replay=>1,\n \t\t\t\t\t                       hash=>$hash, hash_parent=>'-c')},\n \t\t\t\t\t        'combined');\n \t\t\t} else { # $hash_parent eq '-c'\n \t\t\t\t$formats_nav .= ' | ' .\n-\t\t\t\t\t$cgi->a({-href => href(action=>\"commitdiff\",\n+\t\t\t\t\t$cgi->a({-href => href(-replay=>1,\n \t\t\t\t\t                       hash=>$hash, hash_parent=>'--cc')},\n \t\t\t\t\t        'compact');\n \t\t\t}\n \t\t\t$formats_nav .=\n \t\t\t\t' (merge: ' .\n \t\t\t\tjoin(' ', map {\n-\t\t\t\t\t$cgi->a({-href => href(action=>\"commitdiff\",\n-\t\t\t\t\t                       hash=>$_)},\n+\t\t\t\t\t$cgi->a({-href => href(-replay=>1,\n+\t\t\t\t\t                       hash=>$_, hash_base=>undef)},\n \t\t\t\t\t        esc_html(substr($_, 0, 7)));\n \t\t\t\t} @{$co{'parents'}} ) .\n \t\t\t\t')';\n-- \n1.7.6\n"},{"id":"178544","messageId":"1320017787-18048-9-git-send-email-jnareb@gmail.com","threadId":"28802","inReplyTo":"1320017787-18048-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv3 8/8] gitweb: Add navigation to select side-by-side diff","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-10-30T23:36:27Z","receivedAt":"2011-10-30T23:36:27Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: Kato Kazuyoshi <kato.kazuyoshi@gmail.com>\n\nAdd to the lower part of navigation bar (the action specific part)\nlinks allowing to switch between 'inline' (ordinary) diff and\n'side by side' style diff.\n\nIt is not shown for combined / compact combined diff.\n\nSigned-off-by: Kato Kazuyoshi <kato.kazuyoshi@gmail.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch was originally part of other commit, but was split into a\nseparate patch.\n\nNote that \"blobdiff\" should probably also get this navigation.\n\n gitweb/gitweb.perl |   23 +++++++++++++++++++++++\n 1 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex ffaea45..f80f259 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -7164,6 +7164,7 @@ sub git_blobdiff {\n \t\tmy $formats_nav =\n \t\t\t$cgi->a({-href => href(action=>\"blobdiff_plain\", -replay=>1)},\n \t\t\t        \"raw\");\n+\t\t$formats_nav .= diff_style_nav($diff_style);\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@@ -7221,6 +7222,27 @@ sub git_blobdiff_plain {\n \tgit_blobdiff('plain');\n }\n \n+# assumes that it is added as later part of already existing navigation,\n+# so it returns \"| foo | bar\" rather than just \"foo | bar\"\n+sub diff_style_nav {\n+\tmy ($diff_style, $is_combined) = @_;\n+\t$diff_style ||= 'inline';\n+\n+\treturn \"\" if ($is_combined);\n+\n+\tmy @styles = (inline => 'inline', 'sidebyside' => 'side by side');\n+\tmy %styles = @styles;\n+\t@styles =\n+\t\t@styles[ map { $_ * 2 } 0..$#styles/2 ];\n+\n+\treturn join '',\n+\t\tmap { \" | \".$_ }\n+\t\tmap {\n+\t\t\t$_ eq $diff_style ? $styles{$_} :\n+\t\t\t$cgi->a({-href => href(-replay=>1, diff_style => $_)}, $styles{$_})\n+\t\t} @styles;\n+}\n+\n sub git_commitdiff {\n \tmy %params = @_;\n \tmy $format = $params{-format} || 'html';\n@@ -7250,6 +7272,7 @@ sub git_commitdiff {\n \t\t\t\t$cgi->a({-href => href(action=>\"patch\", -replay=>1)},\n \t\t\t\t\t\"patch\");\n \t\t}\n+\t\t$formats_nav .= diff_style_nav($diff_style, @{$co{'parents'}} > 1);\n \n \t\tif (defined $hash_parent &&\n \t\t    $hash_parent ne '-c' && $hash_parent ne '--cc') {\n-- \n1.7.6\n"}]}