зеркало из https://github.com/microsoft/git.git
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:_<sha1 before>_ -> file:_<sha1 after>_ where 'file' is file type and '<sha1>' is full sha1 of blob is changed to diff --git _a/<file before>_ _b/<file after>_ In both cases links are visible and use default link style. If file is added, a/<file> is not hyperlinked. If file is deleted, b/<file> is not hyperlinked. * there is added "extended diff header", with <path> and <hash> hyperlinked (and <hash> shortened to 7 characters), and <mode> explained: '<mode>' is extended to '<mode> (<file type description>)', 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 <jnareb@gmail.com> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Родитель
1d3bc0cc0a
Коммит
744d0ac33a
|
@ -298,40 +298,82 @@ td.mode {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff a.list {
|
/* styling of diffs (patchsets): commitdiff and blobdiff views */
|
||||||
text-decoration: none;
|
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;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff.to_file a.list,
|
div.diff.extended_header,
|
||||||
div.diff.to_file,
|
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 {
|
div.diff.add {
|
||||||
color: #008800;
|
color: #008800;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff.from_file a.list,
|
div.diff.from_file a.path,
|
||||||
div.diff.from_file,
|
div.diff.from_file {
|
||||||
|
color: #aa0000;
|
||||||
|
}
|
||||||
|
|
||||||
div.diff.rem {
|
div.diff.rem {
|
||||||
color: #cc0000;
|
color: #cc0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff.chunk_header {
|
div.diff.chunk_header {
|
||||||
color: #990099;
|
color: #990099;
|
||||||
|
|
||||||
|
border: dotted #ffe0ff;
|
||||||
|
border-width: 1px 0px 0px 0px;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff.incomplete {
|
div.diff.incomplete {
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.diff_info {
|
|
||||||
font-family: monospace;
|
|
||||||
color: #000099;
|
|
||||||
background-color: #edece6;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.index_include {
|
div.index_include {
|
||||||
border: solid #d9d8d1;
|
border: solid #d9d8d1;
|
||||||
|
|
|
@ -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
|
## functions returning short HTML fragments, or transforming HTML fragments
|
||||||
## which don't beling to other sections
|
## which don't beling to other sections
|
||||||
|
@ -2171,6 +2197,7 @@ sub git_patchset_body {
|
||||||
my $in_header = 0;
|
my $in_header = 0;
|
||||||
my $patch_found = 0;
|
my $patch_found = 0;
|
||||||
my $diffinfo;
|
my $diffinfo;
|
||||||
|
my (%from, %to);
|
||||||
|
|
||||||
print "<div class=\"patchset\">\n";
|
print "<div class=\"patchset\">\n";
|
||||||
|
|
||||||
|
@ -2181,6 +2208,10 @@ sub git_patchset_body {
|
||||||
if ($patch_line =~ m/^diff /) { # "git diff" header
|
if ($patch_line =~ m/^diff /) { # "git diff" header
|
||||||
# beginning of patch (in patchset)
|
# beginning of patch (in patchset)
|
||||||
if ($patch_found) {
|
if ($patch_found) {
|
||||||
|
# close extended header for previous empty patch
|
||||||
|
if ($in_header) {
|
||||||
|
print "</div>\n" # class="diff extended_header"
|
||||||
|
}
|
||||||
# close previous patch
|
# close previous patch
|
||||||
print "</div>\n"; # class="patch"
|
print "</div>\n"; # class="patch"
|
||||||
} else {
|
} else {
|
||||||
|
@ -2189,89 +2220,113 @@ sub git_patchset_body {
|
||||||
}
|
}
|
||||||
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
|
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
|
||||||
|
|
||||||
|
# read and prepare patch information
|
||||||
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
||||||
|
# pre-parsed (or generated by hand)
|
||||||
$diffinfo = $difftree->[$patch_idx];
|
$diffinfo = $difftree->[$patch_idx];
|
||||||
} else {
|
} else {
|
||||||
$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
|
$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++;
|
$patch_idx++;
|
||||||
|
|
||||||
if ($diffinfo->{'status'} eq "A") { # added
|
# print "git diff" header
|
||||||
print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
|
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
|
||||||
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
|
if ($from{'href'}) {
|
||||||
hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
|
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
|
||||||
$diffinfo->{'to_id'}) . " (new)" .
|
'a/' . esc_path($from{'file'}));
|
||||||
"</div>\n"; # class="diff_info"
|
} else { # file was added
|
||||||
|
$patch_line .= 'a/' . esc_path($from{'file'});
|
||||||
} elsif ($diffinfo->{'status'} eq "D") { # deleted
|
}
|
||||||
print "<div class=\"diff_info\">" . file_type($diffinfo->{'from_mode'}) . ":" .
|
$patch_line .= ' ';
|
||||||
$cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
|
if ($to{'href'}) {
|
||||||
hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
|
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
|
||||||
$diffinfo->{'from_id'}) . " (deleted)" .
|
'b/' . esc_path($to{'file'}));
|
||||||
"</div>\n"; # class="diff_info"
|
} else { # file was deleted
|
||||||
|
$patch_line .= 'b/' . esc_path($to{'file'});
|
||||||
} elsif ($diffinfo->{'status'} eq "R" || # renamed
|
|
||||||
$diffinfo->{'status'} eq "C" || # copied
|
|
||||||
$diffinfo->{'status'} eq "2") { # with two filenames (from git_blobdiff)
|
|
||||||
print "<div class=\"diff_info\">" .
|
|
||||||
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 "</div>\n"; # class="diff_info"
|
|
||||||
|
|
||||||
} else { # modified, mode changed, ...
|
|
||||||
print "<div class=\"diff_info\">" .
|
|
||||||
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 "</div>\n"; # class="diff_info"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#print "<div class=\"diff extended_header\">\n";
|
print "<div class=\"diff header\">$patch_line</div>\n";
|
||||||
|
print "<div class=\"diff extended_header\">\n";
|
||||||
$in_header = 1;
|
$in_header = 1;
|
||||||
next LINE;
|
next LINE;
|
||||||
} # start of patch in patchset
|
}
|
||||||
|
|
||||||
|
if ($in_header) {
|
||||||
|
if ($patch_line !~ m/^---/) {
|
||||||
|
# match <path>
|
||||||
|
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 <mode>
|
||||||
|
if ($patch_line =~ m/\s(\d{6})$/) {
|
||||||
|
$patch_line .= '<span class="info"> (' .
|
||||||
|
file_type_long($1) .
|
||||||
|
')</span>';
|
||||||
|
}
|
||||||
|
# match <hash>
|
||||||
|
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 . "<br/>\n";
|
||||||
|
|
||||||
if ($in_header && $patch_line =~ m/^---/) {
|
} else {
|
||||||
#print "</div>\n"; # class="diff extended_header"
|
#$in_header && $patch_line =~ m/^---/;
|
||||||
|
print "</div>\n"; # class="diff extended_header"
|
||||||
$in_header = 0;
|
$in_header = 0;
|
||||||
|
|
||||||
my $file = $diffinfo->{'from_file'};
|
if ($from{'href'}) {
|
||||||
$file ||= $diffinfo->{'file'};
|
$patch_line = '--- a/' .
|
||||||
$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
|
$cgi->a({-href=>$from{'href'}, -class=>"path"},
|
||||||
hash=>$diffinfo->{'from_id'}, file_name=>$file),
|
esc_path($from{'file'}));
|
||||||
-class => "list"}, esc_path($file));
|
}
|
||||||
$patch_line =~ s|a/.*$|a/$file|g;
|
|
||||||
print "<div class=\"diff from_file\">$patch_line</div>\n";
|
print "<div class=\"diff from_file\">$patch_line</div>\n";
|
||||||
|
|
||||||
$patch_line = <$fd>;
|
$patch_line = <$fd>;
|
||||||
chomp $patch_line;
|
chomp $patch_line;
|
||||||
|
|
||||||
#$patch_line =~ m/^+++/;
|
#$patch_line =~ m/^+++/;
|
||||||
$file = $diffinfo->{'to_file'};
|
if ($to{'href'}) {
|
||||||
$file ||= $diffinfo->{'file'};
|
$patch_line = '+++ b/' .
|
||||||
$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
|
$cgi->a({-href=>$to{'href'}, -class=>"path"},
|
||||||
hash=>$diffinfo->{'to_id'}, file_name=>$file),
|
esc_path($to{'file'}));
|
||||||
-class => "list"}, esc_path($file));
|
}
|
||||||
$patch_line =~ s|b/.*|b/$file|g;
|
|
||||||
print "<div class=\"diff to_file\">$patch_line</div>\n";
|
print "<div class=\"diff to_file\">$patch_line</div>\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
next LINE;
|
next LINE;
|
||||||
}
|
}
|
||||||
next LINE if $in_header;
|
|
||||||
|
|
||||||
print format_diff_line($patch_line);
|
print format_diff_line($patch_line);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче