# name of your site or organization to appear in page titles
# replace this with something more descriptive for clearer bookmarks
-our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled";
+our $site_name = "++GITWEB_SITENAME++"
+ || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
# filename of html text to include at top of each page
our $site_header = "++GITWEB_SITE_HEADER++";
# URI of stylesheets
our @stylesheets = ("++GITWEB_CSS++");
-our $stylesheet;
-# default is not to define style sheet, but it can be overwritten later
-undef $stylesheet;
-
-# URI of default stylesheet
-our $stylesheet = "++GITWEB_CSS++";
+# URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
+our $stylesheet = undef;
# URI of GIT logo (72x27 size)
our $logo = "++GITWEB_LOGO++";
# URI of GIT favicon, assumed to be image/png type
# list of git base URLs used for URL to where fetch project from,
# i.e. full URL is "$git_base_url/$project"
-our @git_base_url_list = ("++GITWEB_BASE_URL++");
+our @git_base_url_list = grep { $_ ne '' } ("++GITWEB_BASE_URL++");
# default blob_plain mimetype and default charset for text/plain blob
our $default_blob_plain_mimetype = 'text/plain';
$searchtext = quotemeta $searchtext;
}
+our $searchtype = $cgi->param('st');
+if (defined $searchtype) {
+ if ($searchtype =~ m/[^a-z]/) {
+ die_error(undef, "Invalid searchtype parameter");
+ }
+}
+
# now read PATH_INFO and use it as alternative to parameters
sub evaluate_path_info {
return if defined $project;
"log" => \&git_log,
"rss" => \&git_rss,
"search" => \&git_search,
+ "search_help" => \&git_search_help,
"shortlog" => \&git_shortlog,
"summary" => \&git_summary,
"tag" => \&git_tag,
page => "pg",
order => "o",
searchtext => "s",
+ searchtype => "st",
);
my %mapping = @mapping;
return $owner;
}
+sub git_get_last_activity {
+ my ($path) = @_;
+ my $fd;
+
+ $git_dir = "$projectroot/$path";
+ open($fd, "-|", git_cmd(), 'for-each-ref',
+ '--format=%(refname) %(committer)',
+ '--sort=-committerdate',
+ 'refs/heads') or return;
+ my $most_recent = <$fd>;
+ close $fd or return;
+ if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
+ my $timestamp = $1;
+ my $age = time - $timestamp;
+ return ($age, age_string($age));
+ }
+}
+
sub git_get_references {
my $type = shift || "";
my %refs;
$date{'hour_local'} = $hour;
$date{'minute_local'} = $min;
$date{'tz_local'} = $tz;
+ $date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s",
+ 1900+$year, $mon+1, $mday,
+ $hour, $min, $sec, $tz);
return %date;
}
return %tag
}
-sub git_get_last_activity {
- my ($path) = @_;
- my $fd;
-
- $git_dir = "$projectroot/$path";
- open($fd, "-|", git_cmd(), 'for-each-ref',
- '--format=%(refname) %(committer)',
- '--sort=-committerdate',
- 'refs/heads') or return;
- my $most_recent = <$fd>;
- close $fd or return;
- if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
- my $timestamp = $1;
- my $age = time - $timestamp;
- return ($age, age_string($age));
- }
-}
-
sub parse_commit {
my $commit_id = shift;
my $commit_text = shift;
my $status = shift || "200 OK";
my $expires = shift;
- my $title = "$site_name git";
+ my $title = "$site_name";
if (defined $project) {
$title .= " - $project";
if (defined $action) {
$cgi->hidden(-name => "p") . "\n" .
$cgi->hidden(-name => "a") . "\n" .
$cgi->hidden(-name => "h") . "\n" .
+ $cgi->popup_menu(-name => 'st', -default => 'commit',
+ -values => ['commit', 'author', 'committer', 'pickaxe']) .
+ $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+ " search:\n",
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
"</div>" .
$cgi->end_form() . "\n";
}
}
-sub git_print_simplified_log {
- my $log = shift;
- my $remove_title = shift;
-
- git_print_log($log,
- -final_empty_line=> 1,
- -remove_title => $remove_title);
-}
-
# print tree entry (row of git_tree), but without encompassing <tr> element
sub git_print_tree_entry {
my ($t, $basedir, $hash_base, $have_blame) = @_;
}
print "</table>\n";
+ if (-s "$projectroot/$project/README.html") {
+ if (open my $fd, "$projectroot/$project/README.html") {
+ print "<div class=\"title\">readme</div>\n";
+ print $_ while (<$fd>);
+ close $fd;
+ }
+ }
+
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
git_get_head_hash($project)
or die_error(undef, "Open git-rev-list failed");
if ($ftype !~ "blob") {
die_error("400 Bad Request", "Object is not a blob");
}
- open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
+ open ($fd, "-|", git_cmd(), "blame", '-p', '--',
+ $file_name, $hash_base)
or die_error(undef, "Open git-blame failed");
git_header_html();
my $formats_nav =
<table class="blame">
<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
HTML
- while (<$fd>) {
- my ($full_rev, $author, $date, $lineno, $data) =
- /^([0-9a-f]{40}).*?\s\((.*?)\s+([-\d]+ [:\d]+ [-+\d]+)\s+(\d+)\)\s(.*)/;
+ my %metainfo = ();
+ while (1) {
+ $_ = <$fd>;
+ last unless defined $_;
+ my ($full_rev, $orig_lineno, $lineno, $group_size) =
+ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+ if (!exists $metainfo{$full_rev}) {
+ $metainfo{$full_rev} = {};
+ }
+ my $meta = $metainfo{$full_rev};
+ while (<$fd>) {
+ last if (s/^\t//);
+ if (/^(\S+) (.*)$/) {
+ $meta->{$1} = $2;
+ }
+ }
+ my $data = $_;
my $rev = substr($full_rev, 0, 8);
- my $print_c8 = 0;
-
- if (!defined $last_rev) {
- $last_rev = $full_rev;
- $print_c8 = 1;
- } elsif ($last_rev ne $full_rev) {
- $last_rev = $full_rev;
+ my $author = $meta->{'author'};
+ my %date = parse_date($meta->{'author-time'},
+ $meta->{'author-tz'});
+ my $date = $date{'iso-tz'};
+ if ($group_size) {
$current_color = ++$current_color % $num_colors;
- $print_c8 = 1;
}
print "<tr class=\"$rev_color[$current_color]\">\n";
- print "<td class=\"sha1\"";
- if ($print_c8 == 1) {
+ if ($group_size) {
+ print "<td class=\"sha1\"";
print " title=\"$author, $date\"";
- }
- print ">";
- if ($print_c8 == 1) {
- print $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)},
+ print " rowspan=\"$group_size\"" if ($group_size > 1);
+ print ">";
+ print $cgi->a({-href => href(action=>"commit",
+ hash=>$full_rev,
+ file_name=>$file_name)},
esc_html($rev));
+ print "</td>\n";
}
- print "</td>\n";
- print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" .
- esc_html($lineno) . "</a></td>\n";
+ my $blamed = href(action => 'blame',
+ file_name => $meta->{'filename'},
+ hash_base => $full_rev);
+ print "<td class=\"linenr\">";
+ print $cgi->a({ -href => "$blamed#l$orig_lineno",
+ -id => "l$lineno",
+ -class => "linenr" },
+ esc_html($lineno));
+ print "</td>";
print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
print "</tr>\n";
}
"</div>\n";
print "<div class=\"log_body\">\n";
- git_print_simplified_log($co{'comment'});
+ git_print_log($co{'comment'}, -final_empty_line=> 1);
print "</div>\n";
}
git_footer_html();
if (!defined $parent) {
$parent = "--root";
}
- open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $parent, $hash
+ 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");
if (!%co) {
die_error(undef, "Unknown commit object");
}
+
+ # we need to prepare $formats_nav before any parameter munging
+ my $formats_nav;
+ if ($format eq 'html') {
+ $formats_nav =
+ $cgi->a({-href => href(action=>"commitdiff_plain",
+ hash=>$hash, hash_parent=>$hash_parent)},
+ "raw");
+
+ if (defined $hash_parent) {
+ # commitdiff with two commits given
+ my $hash_parent_short = $hash_parent;
+ if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+ $hash_parent_short = substr($hash_parent, 0, 7);
+ }
+ $formats_nav .=
+ ' (from: ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$hash_parent)},
+ esc_html($hash_parent_short)) .
+ ')';
+ } elsif (!$co{'parent'}) {
+ # --root commitdiff
+ $formats_nav .= ' (initial)';
+ } elsif (scalar @{$co{'parents'}} == 1) {
+ # single parent commit
+ $formats_nav .=
+ ' (parent: ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$co{'parent'})},
+ esc_html(substr($co{'parent'}, 0, 7))) .
+ ')';
+ } else {
+ # merge commit
+ $formats_nav .=
+ ' (merge: ' .
+ join(' ', map {
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$_)},
+ esc_html(substr($_, 0, 7)));
+ } @{$co{'parents'}} ) .
+ ')';
+ }
+ }
+
if (!defined $hash_parent) {
$hash_parent = $co{'parent'} || '--root';
}
my @difftree;
if ($format eq 'html') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+ "--no-commit-id",
"--patch-with-raw", "--full-index", $hash_parent, $hash
or die_error(undef, "Open git-diff-tree failed");
if ($format eq 'html') {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'});
- my $formats_nav =
- $cgi->a({-href => href(action=>"commitdiff_plain",
- hash=>$hash, hash_parent=>$hash_parent)},
- "raw");
git_header_html(undef, $expires);
git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
git_print_authorship(\%co);
print "<div class=\"page_body\">\n";
- print "<div class=\"log\">\n";
- git_print_simplified_log($co{'comment'}, 1); # skip title
- print "</div>\n"; # class="log"
+ if (@{$co{'comment'}} > 1) {
+ print "<div class=\"log\">\n";
+ git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
+ print "</div>\n"; # class="log"
+ }
} elsif ($format eq 'plain') {
my $refs = git_get_references("tags");
die_error(undef, "Unknown commit object");
}
- my $commit_search = 1;
- my $author_search = 0;
- my $committer_search = 0;
- my $pickaxe_search = 0;
- if ($searchtext =~ s/^author\\://i) {
- $author_search = 1;
- } elsif ($searchtext =~ s/^committer\\://i) {
- $committer_search = 1;
- } elsif ($searchtext =~ s/^pickaxe\\://i) {
- $commit_search = 0;
- $pickaxe_search = 1;
-
+ $searchtype ||= 'commit';
+ if ($searchtype eq 'pickaxe') {
# pickaxe may take all resources of your box and run for several minutes
# with every query - so decide by yourself how public you make this feature
my ($have_pickaxe) = gitweb_check_feature('pickaxe');
die_error('403 Permission denied', "Permission denied");
}
}
+
git_header_html();
git_print_page_nav('','', $hash,$co{'tree'},$hash);
git_print_header_div('commit', esc_html($co{'title'}), $hash);
print "<table cellspacing=\"0\">\n";
my $alternate = 1;
- if ($commit_search) {
+ if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
$/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next;
while (my $commit_text = <$fd>) {
if (!grep m/$searchtext/i, $commit_text) {
next;
}
- if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) {
+ if ($searchtype eq 'author' && !grep m/\nauthor .*$searchtext/i, $commit_text) {
next;
}
- if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
+ if ($searchtype eq 'committer' && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
next;
}
my @commit_lines = split "\n", $commit_text;
close $fd;
}
- if ($pickaxe_search) {
+ if ($searchtype eq 'pickaxe') {
$/ = "\n";
my $git_command = git_cmd_str();
open my $fd, "-|", "$git_command rev-list $hash | " .
git_footer_html();
}
+sub git_search_help {
+ git_header_html();
+ git_print_page_nav('','', $hash,$hash,$hash);
+ print <<EOT;
+<dl>
+<dt><b>commit</b></dt>
+<dd>The commit messages and authorship information will be scanned for the given string.</dd>
+<dt><b>author</b></dt>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd>
+<dt><b>committer</b></dt>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd>
+EOT
+ my ($have_pickaxe) = gitweb_check_feature('pickaxe');
+ if ($have_pickaxe) {
+ print <<EOT;
+<dt><b>pickaxe</b></dt>
+<dd>All commits that caused the string to appear or disappear from any file (changes that
+added, removed or "modified" the string) will be listed. This search can take a while and
+takes a lot of strain on the server, so please use it wisely.</dd>
+EOT
+ }
+ print "</dl>\n";
+ git_footer_html();
+}
+
sub git_shortlog {
my $head = git_get_head_hash($project);
if (!defined $hash) {
<?xml version="1.0" encoding="utf-8"?>
<opml version="1.0">
<head>
- <title>$site_name Git OPML Export</title>
+ <title>$site_name OPML Export</title>
</head>
<body>
<outline text="git RSS feeds">