"project_index" => \&git_project_index,
);
-if (defined $project) {
- $action ||= 'summary';
-} else {
- $action ||= 'project_list';
+if (!defined $action) {
+ if (defined $hash) {
+ $action = git_get_type($hash);
+ } elsif (defined $hash_base && defined $file_name) {
+ $action = git_get_type("$hash_base:$file_name");
+ } elsif (defined $project) {
+ $action = 'summary';
+ } else {
+ $action = 'project_list';
+ }
}
if (!defined($actions{$action})) {
die_error(undef, "Unknown action");
sub age_class {
my $age = shift;
- if ($age < 60*60*2) {
+ if (!defined $age) {
+ return "noage";
+ } elsif ($age < 60*60*2) {
return "age0";
} elsif ($age < 60*60*24*2) {
return "age1";
my $line = <$fd>;
close $fd or return undef;
+ if (!defined $line) {
+ # there is no tree or hash given by $path at $base
+ return undef;
+ }
+
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
if (defined $type && $type ne $2) {
'refs/heads') or return;
my $most_recent = <$fd>;
close $fd or return;
- if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
+ if (defined $most_recent &&
+ $most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
my $timestamp = $1;
my $age = time - $timestamp;
return ($age, age_string($age));
pop @commit_lines; # Remove '\0'
+ if (! @commit_lines) {
+ return;
+ }
+
my $header = shift @commit_lines;
- if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
+ if ($header !~ m/^[0-9a-fA-F]{40}/) {
return;
}
($co{'id'}, my @parents) = split ' ', $header;
my $alternate = 1;
my $patchno = 0;
foreach my $line (@{$difftree}) {
- my %diff = parse_difftree_raw_line($line);
+ my $diff;
+ if (ref($line) eq "HASH") {
+ # pre-parsed (or generated by hand)
+ $diff = $line;
+ } else {
+ $diff = parse_difftree_raw_line($line);
+ }
if ($alternate) {
print "<tr class=\"dark\">\n";
}
$alternate ^= 1;
- if (exists $diff{'nparents'}) { # combined diff
+ if (exists $diff->{'nparents'}) { # combined diff
- fill_from_file_info(\%diff, @parents);
+ fill_from_file_info($diff, @parents)
+ unless exists $diff->{'from_file'};
- if ($diff{'to_id'} ne ('0' x 40)) {
+ if ($diff->{'to_id'} ne ('0' x 40)) {
# file exists in the result (child) commit
print "<td>" .
- $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- file_name=>$diff{'to_file'},
+ $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ file_name=>$diff->{'to_file'},
hash_base=>$hash),
- -class => "list"}, esc_path($diff{'to_file'})) .
+ -class => "list"}, esc_path($diff->{'to_file'})) .
"</td>\n";
} else {
print "<td>" .
- esc_path($diff{'to_file'}) .
+ esc_path($diff->{'to_file'}) .
"</td>\n";
}
my $has_history = 0;
my $not_deleted = 0;
- for (my $i = 0; $i < $diff{'nparents'}; $i++) {
+ for (my $i = 0; $i < $diff->{'nparents'}; $i++) {
my $hash_parent = $parents[$i];
- my $from_hash = $diff{'from_id'}[$i];
- my $from_path = $diff{'from_file'}[$i];
- my $status = $diff{'status'}[$i];
+ my $from_hash = $diff->{'from_id'}[$i];
+ my $from_path = $diff->{'from_file'}[$i];
+ my $status = $diff->{'status'}[$i];
$has_history ||= ($status ne 'A');
$not_deleted ||= ($status ne 'D');
"blob" . ($i+1)) .
" | </td>\n";
} else {
- if ($diff{'to_id'} eq $from_hash) {
+ if ($diff->{'to_id'} eq $from_hash) {
print "<td class=\"link nochange\">";
} else {
print "<td class=\"link\">";
}
print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'},
+ hash=>$diff->{'to_id'},
hash_parent=>$from_hash,
hash_base=>$hash,
hash_parent_base=>$hash_parent,
- file_name=>$diff{'to_file'},
+ file_name=>$diff->{'to_file'},
file_parent=>$from_path)},
"diff" . ($i+1)) .
" | </td>\n";
print "<td class=\"link\">";
if ($not_deleted) {
print $cgi->a({-href => href(action=>"blob",
- hash=>$diff{'to_id'},
- file_name=>$diff{'to_file'},
+ hash=>$diff->{'to_id'},
+ file_name=>$diff->{'to_file'},
hash_base=>$hash)},
"blob");
print " | " if ($has_history);
}
if ($has_history) {
print $cgi->a({-href => href(action=>"history",
- file_name=>$diff{'to_file'},
+ file_name=>$diff->{'to_file'},
hash_base=>$hash)},
"history");
}
my ($to_mode_oct, $to_mode_str, $to_file_type);
my ($from_mode_oct, $from_mode_str, $from_file_type);
- if ($diff{'to_mode'} ne ('0' x 6)) {
- $to_mode_oct = oct $diff{'to_mode'};
+ if ($diff->{'to_mode'} ne ('0' x 6)) {
+ $to_mode_oct = oct $diff->{'to_mode'};
if (S_ISREG($to_mode_oct)) { # only for regular file
$to_mode_str = sprintf("%04o", $to_mode_oct & 0777); # permission bits
}
- $to_file_type = file_type($diff{'to_mode'});
+ $to_file_type = file_type($diff->{'to_mode'});
}
- if ($diff{'from_mode'} ne ('0' x 6)) {
- $from_mode_oct = oct $diff{'from_mode'};
+ if ($diff->{'from_mode'} ne ('0' x 6)) {
+ $from_mode_oct = oct $diff->{'from_mode'};
if (S_ISREG($to_mode_oct)) { # only for regular file
$from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits
}
- $from_file_type = file_type($diff{'from_mode'});
+ $from_file_type = file_type($diff->{'from_mode'});
}
- if ($diff{'status'} eq "A") { # created
+ if ($diff->{'status'} eq "A") { # created
my $mode_chng = "<span class=\"file_status new\">[new $to_file_type";
$mode_chng .= " with mode: $to_mode_str" if $to_mode_str;
$mode_chng .= "]</span>";
print "<td>";
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_path($diff{'file'}));
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ hash_base=>$hash, file_name=>$diff->{'file'}),
+ -class => "list"}, esc_path($diff->{'file'}));
print "</td>\n";
print "<td>$mode_chng</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => "#patch$patchno"}, "patch");
print " | ";
}
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'})},
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ hash_base=>$hash, file_name=>$diff->{'file'})},
"blob");
print "</td>\n";
- } elsif ($diff{'status'} eq "D") { # deleted
+ } elsif ($diff->{'status'} eq "D") { # deleted
my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>";
print "<td>";
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
- hash_base=>$parent, file_name=>$diff{'file'}),
- -class => "list"}, esc_path($diff{'file'}));
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+ hash_base=>$parent, file_name=>$diff->{'file'}),
+ -class => "list"}, esc_path($diff->{'file'}));
print "</td>\n";
print "<td>$mode_chng</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => "#patch$patchno"}, "patch");
print " | ";
}
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
- hash_base=>$parent, file_name=>$diff{'file'})},
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+ hash_base=>$parent, file_name=>$diff->{'file'})},
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
- file_name=>$diff{'file'})},
+ file_name=>$diff->{'file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
- file_name=>$diff{'file'})},
+ file_name=>$diff->{'file'})},
"history");
print "</td>\n";
- } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed
+ } elsif ($diff->{'status'} eq "M" || $diff->{'status'} eq "T") { # modified, or type changed
my $mode_chnge = "";
- if ($diff{'from_mode'} != $diff{'to_mode'}) {
+ if ($diff->{'from_mode'} != $diff->{'to_mode'}) {
$mode_chnge = "<span class=\"file_status mode_chnge\">[changed";
if ($from_file_type ne $to_file_type) {
$mode_chnge .= " from $from_file_type to $to_file_type";
$mode_chnge .= "]</span>\n";
}
print "<td>";
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_path($diff{'file'}));
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ hash_base=>$hash, file_name=>$diff->{'file'}),
+ -class => "list"}, esc_path($diff->{'file'}));
print "</td>\n";
print "<td>$mode_chnge</td>\n";
print "<td class=\"link\">";
$patchno++;
print $cgi->a({-href => "#patch$patchno"}, "patch") .
" | ";
- } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+ } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
# "commit" view and modified file (not onlu mode changed)
print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'file'})},
+ file_name=>$diff->{'file'})},
"diff") .
" | ";
}
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'})},
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ hash_base=>$hash, file_name=>$diff->{'file'})},
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
- file_name=>$diff{'file'})},
+ file_name=>$diff->{'file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
- file_name=>$diff{'file'})},
+ file_name=>$diff->{'file'})},
"history");
print "</td>\n";
- } elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied
+ } elsif ($diff->{'status'} eq "R" || $diff->{'status'} eq "C") { # renamed or copied
my %status_name = ('R' => 'moved', 'C' => 'copied');
- my $nstatus = $status_name{$diff{'status'}};
+ my $nstatus = $status_name{$diff->{'status'}};
my $mode_chng = "";
- if ($diff{'from_mode'} != $diff{'to_mode'}) {
+ if ($diff->{'from_mode'} != $diff->{'to_mode'}) {
# mode also for directories, so we cannot use $to_mode_str
$mode_chng = sprintf(", mode: %04o", $to_mode_oct & 0777);
}
print "<td>" .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
- hash=>$diff{'to_id'}, file_name=>$diff{'to_file'}),
- -class => "list"}, esc_path($diff{'to_file'})) . "</td>\n" .
+ hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}),
+ -class => "list"}, esc_path($diff->{'to_file'})) . "</td>\n" .
"<td><span class=\"file_status $nstatus\">[$nstatus from " .
$cgi->a({-href => href(action=>"blob", hash_base=>$parent,
- hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}),
- -class => "list"}, esc_path($diff{'from_file'})) .
- " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
+ hash=>$diff->{'from_id'}, file_name=>$diff->{'from_file'}),
+ -class => "list"}, esc_path($diff->{'from_file'})) .
+ " with " . (int $diff->{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
"<td class=\"link\">";
if ($action eq 'commitdiff') {
# link to patch
$patchno++;
print $cgi->a({-href => "#patch$patchno"}, "patch") .
" | ";
- } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+ } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
# "commit" view and modified file (not only pure rename or copy)
print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
+ file_name=>$diff->{'to_file'}, file_parent=>$diff->{'from_file'})},
"diff") .
" | ";
}
- print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$parent, file_name=>$diff{'to_file'})},
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+ hash_base=>$parent, file_name=>$diff->{'to_file'})},
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
- file_name=>$diff{'to_file'})},
+ file_name=>$diff->{'to_file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
- file_name=>$diff{'to_file'})},
+ file_name=>$diff->{'to_file'})},
"history");
print "</td>\n";
# check if current patch belong to current raw line
# and parse raw git-diff line if needed
if (defined $diffinfo &&
+ defined $from_id && defined $to_id &&
from_ids_eq($diffinfo->{'from_id'}, $from_id) &&
$diffinfo->{'to_id'} eq $to_id) {
- # this is split patch
+ # this is continuation of a split patch
print "<div class=\"patch cont\">\n";
} else {
# advance raw git-diff output if needed
esc_html($pr->{'descr'})) . "</td>\n" .
"<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
print "<td class=\"". age_class($pr->{'age'}) . "\">" .
- $pr->{'age_string'} . "</td>\n" .
+ (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
sub git_summary {
my $descr = git_get_project_description($project) || "none";
my %co = parse_commit("HEAD");
- my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+ my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();
my $head = $co{'id'};
my $owner = git_get_project_owner($project);
print "<div class=\"title\"> </div>\n";
print "<table cellspacing=\"0\">\n" .
"<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
- "<tr><td>owner</td><td>$owner</td></tr>\n" .
- "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+ "<tr><td>owner</td><td>$owner</td></tr>\n";
+ if (defined $cd{'rfc2822'}) {
+ print "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+ }
+
# use per project git URL list in $projectroot/$project/cloneurl
# or make project git URL from git base URL and project name
my $url_tag = "URL";
# we need to request one more than 16 (0..15) to check if
# those 16 are all
- my @commitlist = parse_commits($head, 17);
- git_print_header_div('shortlog');
- git_shortlog_body(\@commitlist, 0, 15, $refs,
- $#commitlist <= 15 ? undef :
- $cgi->a({-href => href(action=>"shortlog")}, "..."));
+ my @commitlist = $head ? parse_commits($head, 17) : ();
+ if (@commitlist) {
+ git_print_header_div('shortlog');
+ git_shortlog_body(\@commitlist, 0, 15, $refs,
+ $#commitlist <= 15 ? undef :
+ $cgi->a({-href => href(action=>"shortlog")}, "..."));
+ }
if (@taglist) {
git_print_header_div('tags');
git_header_html();
git_print_page_nav('','', $head,undef,$head);
my %tag = parse_tag($hash);
+
+ if (! %tag) {
+ die_error(undef, "Unknown tag object");
+ }
+
git_print_header_div('commit', esc_html($tag{'name'}), $hash);
print "<div class=\"title_text\">\n" .
"<table cellspacing=\"0\">\n" .
$parent = "--root";
}
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");
- }
+ open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+ @diff_opts,
+ (@$parents <= 1 ? $parent : '-c'),
+ $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;
git_print_log($co{'comment'});
print "</div>\n";
- if (@$parents <= 1) {
- # do not output difftree/whatchanged for merges
- git_difftree_body(\@difftree, $hash, $parent);
- }
+ git_difftree_body(\@difftree, $hash, @$parents);
git_footer_html();
}
}
}
+ my $hash_parent_param = $hash_parent;
if (!defined $hash_parent) {
- $hash_parent = $co{'parent'} || '--root';
+ $hash_parent_param =
+ @{$co{'parents'}} > 1 ? '-c' : $co{'parent'} || '--root';
}
# read commitdiff
if ($format eq 'html') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
"--no-commit-id", "--patch-with-raw", "--full-index",
- $hash_parent, $hash, "--"
+ $hash_parent_param, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
while (my $line = <$fd>) {
chomp $line;
# empty line ends raw part of diff-tree output
last unless $line;
- push @difftree, $line;
+ push @difftree, scalar parse_difftree_raw_line($line);
}
} elsif ($format eq 'plain') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
- '-p', $hash_parent, $hash, "--"
+ '-p', $hash_parent_param, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
} else {
# write patch
if ($format eq 'html') {
- git_difftree_body(\@difftree, $hash, $hash_parent);
+ git_difftree_body(\@difftree, $hash, $hash_parent || @{$co{'parents'}});
print "<br/>\n";
- git_patchset_body($fd, \@difftree, $hash, $hash_parent);
+ git_patchset_body($fd, \@difftree, $hash, $hash_parent || @{$co{'parents'}});
close $fd;
print "</div>\n"; # class="page_body"
git_footer_html();