From 744d0ac33ab579845808b8b01e526adc4678a226 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Wed, 8 Nov 2006 17:59:41 +0100 Subject: [PATCH] gitweb: New improved patchset view Replace "gitweb diff header" with its full sha1 of blobs and replace it by "git diff" header and extended diff header. Change also somewhat highlighting of diffs. Added `file_type_long' subroutine to convert file mode in octal to file type description (only for file modes which used by git). Changes: * "gitweb diff header" which looked for example like below: file:__ -> file:__ where 'file' is file type and '' is full sha1 of blob is changed to diff --git _a/_ _b/_ In both cases links are visible and use default link style. If file is added, a/ is not hyperlinked. If file is deleted, b/ is not hyperlinked. * there is added "extended diff header", with and hyperlinked (and shortened to 7 characters), and explained: '' is extended to ' ()', where added text is slightly lighter to easy distinguish that it was added (and it is difference from git-diff output). * from-file/to-file two-line header lines have slightly darker color than removed/added lines. * chunk header has now delicate line above for easier finding chunk boundary, and top margin of 2px, both barely visible. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 68 +++++++++++++---- gitweb/gitweb.perl | 183 +++++++++++++++++++++++++++++---------------- 2 files changed, 174 insertions(+), 77 deletions(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index e19e6bc2ef..974b47f19c 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -298,40 +298,82 @@ td.mode { font-family: monospace; } -div.diff a.list { - text-decoration: none; +/* styling of diffs (patchsets): commitdiff and blobdiff views */ +div.diff.header, +div.diff.extended_header { + white-space: normal; } -div.diff a.list:hover { +div.diff.header { + font-weight: bold; + + background-color: #edece6; + + margin-top: 4px; + padding: 4px 0px 2px 0px; + border: solid #d9d8d1; + border-width: 1px 0px 1px 0px; +} + +div.diff.header a.path { text-decoration: underline; } -div.diff.to_file a.list, -div.diff.to_file, +div.diff.extended_header, +div.diff.extended_header a.path, +div.diff.extended_header a.hash { + color: #777777; +} + +div.diff.extended_header .info { + color: #b0b0b0; +} + +div.diff.extended_header { + background-color: #f6f5ee; + padding: 2px 0px 2px 0px; +} + +div.diff a.path, +div.diff a.hash { + text-decoration: none; +} + +div.diff a.path:hover, +div.diff a.hash:hover { + text-decoration: underline; +} + +div.diff.to_file a.path, +div.diff.to_file { + color: #007000; +} + div.diff.add { color: #008800; } -div.diff.from_file a.list, -div.diff.from_file, +div.diff.from_file a.path, +div.diff.from_file { + color: #aa0000; +} + div.diff.rem { color: #cc0000; } div.diff.chunk_header { color: #990099; + + border: dotted #ffe0ff; + border-width: 1px 0px 0px 0px; + margin-top: 2px; } div.diff.incomplete { color: #cccccc; } -div.diff_info { - font-family: monospace; - color: #000099; - background-color: #edece6; - font-style: italic; -} div.index_include { border: solid #d9d8d1; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b80fc6070b..f937ee1aec 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -785,6 +785,32 @@ sub file_type { } } +# convert file mode in octal to file type description string +sub file_type_long { + my $mode = shift; + + if ($mode !~ m/^[0-7]+$/) { + return $mode; + } else { + $mode = oct $mode; + } + + if (S_ISDIR($mode & S_IFMT)) { + return "directory"; + } elsif (S_ISLNK($mode)) { + return "symlink"; + } elsif (S_ISREG($mode)) { + if ($mode & S_IXUSR) { + return "executable"; + } else { + return "file"; + }; + } else { + return "unknown"; + } +} + + ## ---------------------------------------------------------------------- ## functions returning short HTML fragments, or transforming HTML fragments ## which don't beling to other sections @@ -2171,6 +2197,7 @@ sub git_patchset_body { my $in_header = 0; my $patch_found = 0; my $diffinfo; + my (%from, %to); print "
\n"; @@ -2181,6 +2208,10 @@ sub git_patchset_body { if ($patch_line =~ m/^diff /) { # "git diff" header # beginning of patch (in patchset) if ($patch_found) { + # close extended header for previous empty patch + if ($in_header) { + print "
\n" # class="diff extended_header" + } # close previous patch print "\n"; # class="patch" } else { @@ -2189,89 +2220,113 @@ sub git_patchset_body { } print "
\n"; + # read and prepare patch information if (ref($difftree->[$patch_idx]) eq "HASH") { + # pre-parsed (or generated by hand) $diffinfo = $difftree->[$patch_idx]; } else { $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]); } + $from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; + $to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; + if ($diffinfo->{'status'} ne "A") { # not new (added) file + $from{'href'} = href(action=>"blob", hash_base=>$hash_parent, + hash=>$diffinfo->{'from_id'}, + file_name=>$from{'file'}); + } + if ($diffinfo->{'status'} ne "D") { # not deleted file + $to{'href'} = href(action=>"blob", hash_base=>$hash, + hash=>$diffinfo->{'to_id'}, + file_name=>$to{'file'}); + } $patch_idx++; - if ($diffinfo->{'status'} eq "A") { # added - print "
" . file_type($diffinfo->{'to_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash, - hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'to_id'}) . " (new)" . - "
\n"; # class="diff_info" - - } elsif ($diffinfo->{'status'} eq "D") { # deleted - print "
" . file_type($diffinfo->{'from_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, - hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'from_id'}) . " (deleted)" . - "
\n"; # class="diff_info" - - } elsif ($diffinfo->{'status'} eq "R" || # renamed - $diffinfo->{'status'} eq "C" || # copied - $diffinfo->{'status'} eq "2") { # with two filenames (from git_blobdiff) - print "
" . - file_type($diffinfo->{'from_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, - hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'from_file'})}, - $diffinfo->{'from_id'}) . - " -> " . - file_type($diffinfo->{'to_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash, - hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'})}, - $diffinfo->{'to_id'}); - print "
\n"; # class="diff_info" - - } else { # modified, mode changed, ... - print "
" . - file_type($diffinfo->{'from_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, - hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'from_id'}) . - " -> " . - file_type($diffinfo->{'to_mode'}) . ":" . - $cgi->a({-href => href(action=>"blob", hash_base=>$hash, - hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'to_id'}); - print "
\n"; # class="diff_info" + # print "git diff" header + $patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!; + if ($from{'href'}) { + $patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"}, + 'a/' . esc_path($from{'file'})); + } else { # file was added + $patch_line .= 'a/' . esc_path($from{'file'}); + } + $patch_line .= ' '; + if ($to{'href'}) { + $patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"}, + 'b/' . esc_path($to{'file'})); + } else { # file was deleted + $patch_line .= 'b/' . esc_path($to{'file'}); } - #print "
\n"; + print "
$patch_line
\n"; + print "
\n"; $in_header = 1; next LINE; - } # start of patch in patchset + } + if ($in_header) { + if ($patch_line !~ m/^---/) { + # match + if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) { + $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"}, + esc_path($from{'file'})); + } + if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) { + $patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"}, + esc_path($to{'file'})); + } + # match + if ($patch_line =~ m/\s(\d{6})$/) { + $patch_line .= ' (' . + file_type_long($1) . + ')'; + } + # match + if ($patch_line =~ m/^index/) { + my ($from_link, $to_link); + if ($from{'href'}) { + $from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"}, + substr($diffinfo->{'from_id'},0,7)); + } else { + $from_link = '0' x 7; + } + if ($to{'href'}) { + $to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"}, + substr($diffinfo->{'to_id'},0,7)); + } else { + $to_link = '0' x 7; + } + my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'}); + $patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!; + } + print $patch_line . "
\n"; - if ($in_header && $patch_line =~ m/^---/) { - #print "
\n"; # class="diff extended_header" - $in_header = 0; + } else { + #$in_header && $patch_line =~ m/^---/; + print "
\n"; # class="diff extended_header" + $in_header = 0; - my $file = $diffinfo->{'from_file'}; - $file ||= $diffinfo->{'file'}; - $file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, - hash=>$diffinfo->{'from_id'}, file_name=>$file), - -class => "list"}, esc_path($file)); - $patch_line =~ s|a/.*$|a/$file|g; - print "
$patch_line
\n"; + if ($from{'href'}) { + $patch_line = '--- a/' . + $cgi->a({-href=>$from{'href'}, -class=>"path"}, + esc_path($from{'file'})); + } + print "
$patch_line
\n"; - $patch_line = <$fd>; - chomp $patch_line; + $patch_line = <$fd>; + chomp $patch_line; - #$patch_line =~ m/^+++/; - $file = $diffinfo->{'to_file'}; - $file ||= $diffinfo->{'file'}; - $file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash, - hash=>$diffinfo->{'to_id'}, file_name=>$file), - -class => "list"}, esc_path($file)); - $patch_line =~ s|b/.*|b/$file|g; - print "
$patch_line
\n"; + #$patch_line =~ m/^+++/; + if ($to{'href'}) { + $patch_line = '+++ b/' . + $cgi->a({-href=>$to{'href'}, -class=>"path"}, + esc_path($to{'file'})); + } + print "
$patch_line
\n"; + + } next LINE; } - next LINE if $in_header; print format_diff_line($patch_line); }