Browse Source

Merge branch 'jn/web' (early part)

* 'jn/web' (early part):
  gitweb: Add "next" link to commit view
  gitweb: Add title attribute to ref marker with full ref name
  gitweb: Do not show difftree for merges in "commit" view
  gitweb: SHA-1 in commit log message links to "object" view
  gitweb: Hyperlink target of symbolic link in "tree" view (if possible)
  gitweb: Add generic git_object subroutine to display object of any type
  gitweb: Show target of symbolic link in "tree" view
  gitweb: Don't use Content-Encoding: header in git_snapshot
maint
Junio C Hamano 18 years ago
parent
commit
9057695012
  1. 216
      gitweb/gitweb.perl

216
gitweb/gitweb.perl

@ -434,6 +434,7 @@ my %actions = ( @@ -434,6 +434,7 @@ my %actions = (
"tags" => \&git_tags,
"tree" => \&git_tree,
"snapshot" => \&git_snapshot,
"object" => \&git_object,
# those below don't need $project
"opml" => \&git_opml,
"project_list" => \&git_project_list,
@ -827,14 +828,12 @@ sub format_log_line_html { @@ -827,14 +828,12 @@ sub format_log_line_html {
my $line = shift;

$line = esc_html($line, -nbsp=>1);
if ($line =~ m/([0-9a-fA-F]{40})/) {
if ($line =~ m/([0-9a-fA-F]{8,40})/) {
my $hash_text = $1;
if (git_get_type($hash_text) eq "commit") {
my $link =
$cgi->a({-href => href(action=>"commit", hash=>$hash_text),
-class => "text"}, $hash_text);
$line =~ s/$hash_text/$link/;
}
my $link =
$cgi->a({-href => href(action=>"object", hash=>$hash_text),
-class => "text"}, $hash_text);
$line =~ s/$hash_text/$link/;
}
return $line;
}
@ -856,7 +855,8 @@ sub format_ref_marker { @@ -856,7 +855,8 @@ sub format_ref_marker {
$name = $ref;
}

$markers .= " <span class=\"$type\">" . esc_html($name) . "</span>";
$markers .= " <span class=\"$type\" title=\"$ref\">" .
esc_html($name) . "</span>";
}
}

@ -1989,12 +1989,73 @@ sub git_print_log ($;%) { @@ -1989,12 +1989,73 @@ sub git_print_log ($;%) {
}
}

# return link target (what link points to)
sub git_get_link_target {
my $hash = shift;
my $link_target;

# read link
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or return;
{
local $/;
$link_target = <$fd>;
}
close $fd
or return;

return $link_target;
}

# given link target, and the directory (basedir) the link is in,
# return target of link relative to top directory (top tree);
# return undef if it is not possible (including absolute links).
sub normalize_link_target {
my ($link_target, $basedir, $hash_base) = @_;

# we can normalize symlink target only if $hash_base is provided
return unless $hash_base;

# absolute symlinks (beginning with '/') cannot be normalized
return if (substr($link_target, 0, 1) eq '/');

# normalize link target to path from top (root) tree (dir)
my $path;
if ($basedir) {
$path = $basedir . '/' . $link_target;
} else {
# we are in top (root) tree (dir)
$path = $link_target;
}

# remove //, /./, and /../
my @path_parts;
foreach my $part (split('/', $path)) {
# discard '.' and ''
next if (!$part || $part eq '.');
# handle '..'
if ($part eq '..') {
if (@path_parts) {
pop @path_parts;
} else {
# link leads outside repository (outside top dir)
return;
}
} else {
push @path_parts, $part;
}
}
$path = join('/', @path_parts);

return $path;
}

# print tree entry (row of git_tree), but without encompassing <tr> element
sub git_print_tree_entry {
my ($t, $basedir, $hash_base, $have_blame) = @_;

my %base_key = ();
$base_key{hash_base} = $hash_base if defined $hash_base;
$base_key{'hash_base'} = $hash_base if defined $hash_base;

# The format of a table row is: mode list link. Where mode is
# the mode of the entry, list is the name of the entry, an href,
@ -2005,16 +2066,31 @@ sub git_print_tree_entry { @@ -2005,16 +2066,31 @@ sub git_print_tree_entry {
print "<td class=\"list\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key),
-class => "list"}, esc_path($t->{'name'})) . "</td>\n";
-class => "list"}, esc_path($t->{'name'}));
if (S_ISLNK(oct $t->{'mode'})) {
my $link_target = git_get_link_target($t->{'hash'});
if ($link_target) {
my $norm_target = normalize_link_target($link_target, $basedir, $hash_base);
if (defined $norm_target) {
print " -> " .
$cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
file_name=>$norm_target),
-title => $norm_target}, esc_path($link_target));
} else {
print " -> " . esc_path($link_target);
}
}
}
print "</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
"blob");
file_name=>"$basedir$t->{'name'}", %base_key)},
"blob");
if ($have_blame) {
print " | " .
$cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
"blame");
file_name=>"$basedir$t->{'name'}", %base_key)},
"blame");
}
if (defined $hash_base) {
print " | " .
@ -2036,8 +2112,8 @@ sub git_print_tree_entry { @@ -2036,8 +2112,8 @@ sub git_print_tree_entry {
print "</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
"tree");
file_name=>"$basedir$t->{'name'}", %base_key)},
"tree");
if (defined $hash_base) {
print " | " .
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
@ -3414,8 +3490,7 @@ sub git_snapshot { @@ -3414,8 +3490,7 @@ sub git_snapshot {
my $filename = basename($project) . "-$hash.tar.$suffix";

print $cgi->header(
-type => 'application/x-tar',
-content_encoding => $ctype,
-type => "application/$ctype",
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');

@ -3497,15 +3572,46 @@ sub git_commit { @@ -3497,15 +3572,46 @@ sub git_commit {
my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});

my $parent = $co{'parent'};
my $parent = $co{'parent'};
my $parents = $co{'parents'}; # listref

# we need to prepare $formats_nav before any parameter munging
my $formats_nav;
if (!defined $parent) {
# --root commitdiff
$formats_nav .= '(initial)';
} elsif (@$parents == 1) {
# single parent commit
$formats_nav .=
'(parent: ' .
$cgi->a({-href => href(action=>"commit",
hash=>$parent)},
esc_html(substr($parent, 0, 7))) .
')';
} else {
# merge commit
$formats_nav .=
'(merge: ' .
join(' ', map {
$cgi->a({-href => href(action=>"commitdiff",
hash=>$_)},
esc_html(substr($_, 0, 7)));
} @$parents ) .
')';
}

if (!defined $parent) {
$parent = "--root";
}
open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
@diff_opts, $parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
my @difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-diff-tree failed");
my @difftree;
if (@$parents <= 1) {
# difftree output is not printed for merges
open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
@diff_opts, $parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
@difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-diff-tree failed");
}

# non-textual hash id's can be cached
my $expires;
@ -3517,16 +3623,10 @@ sub git_commit { @@ -3517,16 +3623,10 @@ sub git_commit {

my $have_snapshot = gitweb_have_snapshot();

my @views_nav = ();
if (defined $file_name && defined $co{'parent'}) {
push @views_nav,
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
"blame");
}
git_header_html(undef, $expires);
git_print_page_nav('commit', '',
$hash, $co{'tree'}, $hash,
join (' | ', @views_nav));
$formats_nav);

if (defined $co{'parent'}) {
git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@ -3567,7 +3667,7 @@ sub git_commit { @@ -3567,7 +3667,7 @@ sub git_commit {
}
print "</td>" .
"</tr>\n";
my $parents = $co{'parents'};

foreach my $par (@$parents) {
print "<tr>" .
"<td>parent</td>" .
@ -3589,11 +3689,61 @@ sub git_commit { @@ -3589,11 +3689,61 @@ sub git_commit {
git_print_log($co{'comment'});
print "</div>\n";

git_difftree_body(\@difftree, $hash, $parent);
if (@$parents <= 1) {
# do not output difftree/whatchanged for merges
git_difftree_body(\@difftree, $hash, $parent);
}

git_footer_html();
}

sub git_object {
# object is defined by:
# - hash or hash_base alone
# - hash_base and file_name
my $type;

# - hash or hash_base alone
if ($hash || ($hash_base && !defined $file_name)) {
my $object_id = $hash || $hash_base;

my $git_command = git_cmd_str();
open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
or die_error('404 Not Found', "Object does not exist");
$type = <$fd>;
chomp $type;
close $fd
or die_error('404 Not Found', "Object does not exist");

# - hash_base and file_name
} elsif ($hash_base && defined $file_name) {
$file_name =~ s,/+$,,;

system(git_cmd(), "cat-file", '-e', $hash_base) == 0
or die_error('404 Not Found', "Base object does not exist");

# here errors should not hapen
open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
or die_error(undef, "Open git-ls-tree failed");
my $line = <$fd>;
close $fd;

#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
die_error('404 Not Found', "File or directory for given base does not exist");
}
$type = $2;
$hash = $3;
} else {
die_error('404 Not Found', "Not enough information to find object");
}

print $cgi->redirect(-uri => href(action=>$type, -full=>1,
hash=>$hash, hash_base=>$hash_base,
file_name=>$file_name),
-status => '302 Found');
}

sub git_blobdiff {
my $format = shift || 'html';


Loading…
Cancel
Save