X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/ffde06d9503b7012498d4597f108b2c4c626f3602e216999c1b6b386f810ad65..34ad37e62846aad75906720a7c8c2aec8cf4f105ed784fea75bc5a0a6845b798:/gitweb.perl
diff --git a/gitweb.perl b/gitweb.perl
index 5211306..2298acf 100755
--- a/gitweb.perl
+++ b/gitweb.perl
@@ -18,6 +18,10 @@ use File::Find qw();
use File::Basename qw(basename);
binmode STDOUT, ':utf8';
+BEGIN {
+ CGI->compile() if $ENV{'MOD_PERL'};
+}
+
our $cgi = new CGI;
our $version = "++GIT_VERSION++";
our $my_url = $cgi->url();
@@ -68,6 +72,10 @@ our $logo_label = "git homepage";
# source of projects list
our $projects_list = "++GITWEB_LIST++";
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
# show repository only if this file exists
# (only effective if this variable evaluates to true)
our $export_ok = "++GITWEB_EXPORT_OK++";
@@ -173,8 +181,8 @@ our %feature = (
# projects matching $projname/*.git will not be shown in the main
# projects list, instead a '+' mark will be added to $projname
# there and a 'forks' view will be enabled for the project, listing
- # all the forks. This feature is supported only if project list
- # is taken from a directory, not file.
+ # all the forks. If project list is taken from a file, forks have
+ # to be listed after the main project.
# To enable system wide have in $GITWEB_CONFIG
# $feature{'forks'}{'default'} = [1];
@@ -451,10 +459,16 @@ my %actions = (
"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");
@@ -557,12 +571,6 @@ sub validate_refname {
return $input;
}
-# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT);
-sub to_utf8 {
- my $str = shift;
- return decode("utf8", $str, Encode::FB_DEFAULT);
-}
-
# quote unsafe chars, but keep the slash, even when it's not
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
@@ -587,8 +595,8 @@ sub esc_html ($;%) {
my $str = shift;
my %opts = @_;
- $str = to_utf8($str);
- $str = escapeHTML($str);
+ $str = decode_utf8($str);
+ $str = $cgi->escapeHTML($str);
if ($opts{'-nbsp'}) {
$str =~ s/ / /g;
}
@@ -601,8 +609,8 @@ sub esc_path {
my $str = shift;
my %opts = @_;
- $str = to_utf8($str);
- $str = escapeHTML($str);
+ $str = decode_utf8($str);
+ $str = $cgi->escapeHTML($str);
if ($opts{'-nbsp'}) {
$str =~ s/ / /g;
}
@@ -831,7 +839,7 @@ sub file_type_long {
## ----------------------------------------------------------------------
## functions returning short HTML fragments, or transforming HTML fragments
-## which don't beling to other sections
+## which don't belong to other sections
# format line of commit message.
sub format_log_line_html {
@@ -884,7 +892,7 @@ sub format_subject_html {
if (length($short) < length($long)) {
return $cgi->a({-href => $href, -class => "list subject",
- -title => to_utf8($long)},
+ -title => decode_utf8($long)},
esc_html($short) . $extra);
} else {
return $cgi->a({-href => $href, -class => "list subject"},
@@ -896,19 +904,34 @@ sub format_subject_html {
sub format_diff_line {
my $line = shift;
my ($from, $to) = @_;
- my $char = substr($line, 0, 1);
my $diff_class = "";
chomp $line;
- if ($char eq '+') {
- $diff_class = " add";
- } elsif ($char eq "-") {
- $diff_class = " rem";
- } elsif ($char eq "@") {
- $diff_class = " chunk_header";
- } elsif ($char eq "\\") {
- $diff_class = " incomplete";
+ if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
+ # combined diff
+ my $prefix = substr($line, 0, scalar @{$from->{'href'}});
+ if ($line =~ m/^\@{3}/) {
+ $diff_class = " chunk_header";
+ } elsif ($line =~ m/^\\/) {
+ $diff_class = " incomplete";
+ } elsif ($prefix =~ tr/+/+/) {
+ $diff_class = " add";
+ } elsif ($prefix =~ tr/-/-/) {
+ $diff_class = " rem";
+ }
+ } else {
+ # assume ordinary diff
+ my $char = substr($line, 0, 1);
+ if ($char eq '+') {
+ $diff_class = " add";
+ } elsif ($char eq '-') {
+ $diff_class = " rem";
+ } elsif ($char eq '@') {
+ $diff_class = " chunk_header";
+ } elsif ($char eq "\\") {
+ $diff_class = " incomplete";
+ }
}
$line = untabify($line);
if ($from && $to && $line =~ m/^\@{2} /) {
@@ -929,6 +952,39 @@ sub format_diff_line {
$line = "@@ $from_text $to_text @@" .
"" . esc_html($section, -nbsp=>1) . "";
return "
$line
\n";
+ } elsif ($from && $to && $line =~ m/^\@{3}/) {
+ my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
+ my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+
+ @from_text = split(' ', $ranges);
+ for (my $i = 0; $i < @from_text; ++$i) {
+ ($from_start[$i], $from_nlines[$i]) =
+ (split(',', substr($from_text[$i], 1)), 0);
+ }
+
+ $to_text = pop @from_text;
+ $to_start = pop @from_start;
+ $to_nlines = pop @from_nlines;
+
+ $line = "$prefix ";
+ for (my $i = 0; $i < @from_text; ++$i) {
+ if ($from->{'href'}[$i]) {
+ $line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
+ -class=>"list"}, $from_text[$i]);
+ } else {
+ $line .= $from_text[$i];
+ }
+ $line .= " ";
+ }
+ if ($to->{'href'}) {
+ $line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
+ -class=>"list"}, $to_text);
+ } else {
+ $line .= $to_text;
+ }
+ $line .= " $prefix" .
+ "" . esc_html($section, -nbsp=>1) . "";
+ return "$line
\n";
}
return "" . esc_html($line, -nbsp=>1) . "
\n";
}
@@ -983,7 +1039,7 @@ sub git_get_project_config {
$key =~ s/^gitweb\.//;
return if ($key =~ m/\W/);
- my @x = (git_cmd(), 'repo-config');
+ my @x = (git_cmd(), 'config');
if (defined $type) { push @x, $type; }
push @x, "--get";
push @x, "gitweb.$key";
@@ -1014,6 +1070,30 @@ sub git_get_hash_by_path {
return $3;
}
+# get path of entry with given hash at given tree-ish (ref)
+# used to get 'from' filename for combined diff (merge commit) for renames
+sub git_get_path_by_hash {
+ my $base = shift || return;
+ my $hash = shift || return;
+
+ local $/ = "\0";
+
+ open my $fd, "-|", git_cmd(), "ls-tree", '-r', '-t', '-z', $base
+ or return undef;
+ while (my $line = <$fd>) {
+ chomp $line;
+
+ #'040000 tree 595596a6a9117ddba9fe379b6b012b558bac8423 gitweb'
+ #'100644 blob e02e90f0429be0d2a69b76571101f20b8f75530f gitweb/README'
+ if ($line =~ m/(?:[0-9]+) (?:.+) $hash\t(.+)$/) {
+ close $fd;
+ return $1;
+ }
+ }
+ close $fd;
+ return undef;
+}
+
## ......................................................................
## git utility functions, directly accessing git repository
@@ -1044,6 +1124,8 @@ sub git_get_projects_list {
$filter ||= '';
$filter =~ s/\.git$//;
+ my ($check_forks) = gitweb_check_feature('forks');
+
if (-d $projects_list) {
# search in directory
my $dir = $projects_list . ($filter ? "/$filter" : '');
@@ -1051,8 +1133,6 @@ sub git_get_projects_list {
$dir =~ s!/+$!!;
my $pfxlen = length("$dir");
- my ($check_forks) = gitweb_check_feature('forks');
-
File::Find::find({
follow_fast => 1, # follow symbolic links
dangling_symlinks => 0, # ignore dangling symlinks, silently
@@ -1078,7 +1158,9 @@ sub git_get_projects_list {
# 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+ my %paths;
open my ($fd), $projects_list or return;
+ PROJECT:
while (my $line = <$fd>) {
chomp $line;
my ($path, $owner) = split ' ', $line;
@@ -1091,24 +1173,41 @@ sub git_get_projects_list {
# looking for forks;
my $pfx = substr($path, 0, length($filter));
if ($pfx ne $filter) {
- next;
+ next PROJECT;
}
my $sfx = substr($path, length($filter));
if ($sfx !~ /^\/.*\.git$/) {
- next;
+ next PROJECT;
+ }
+ } elsif ($check_forks) {
+ PATH:
+ foreach my $filter (keys %paths) {
+ # looking for forks;
+ my $pfx = substr($path, 0, length($filter));
+ if ($pfx ne $filter) {
+ next PATH;
+ }
+ my $sfx = substr($path, length($filter));
+ if ($sfx !~ /^\/.*\.git$/) {
+ next PATH;
+ }
+ # is a fork, don't include it in
+ # the list
+ next PROJECT;
}
}
if (check_export_ok("$projectroot/$path")) {
my $pr = {
path => $path,
- owner => to_utf8($owner),
+ owner => decode_utf8($owner),
};
- push @list, $pr
+ push @list, $pr;
+ (my $forks_path = $path) =~ s/\.git$//;
+ $paths{$forks_path}++;
}
}
close $fd;
}
- @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
return @list;
}
@@ -1130,7 +1229,7 @@ sub git_get_project_owner {
$pr = unescape($pr);
$ow = unescape($ow);
if ($pr eq $project) {
- $owner = to_utf8($ow);
+ $owner = decode_utf8($ow);
last;
}
}
@@ -1272,7 +1371,7 @@ sub parse_tag {
}
sub parse_commit_text {
- my ($commit_text) = @_;
+ my ($commit_text, $withparents) = @_;
my @commit_lines = split '\n', $commit_text;
my %co;
@@ -1282,13 +1381,12 @@ sub parse_commit_text {
if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
return;
}
- $co{'id'} = $header;
- my @parents;
+ ($co{'id'}, my @parents) = split ' ', $header;
while (my $line = shift @commit_lines) {
last if $line eq "\n";
if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
$co{'tree'} = $1;
- } elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
+ } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
push @parents, $1;
} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
$co{'author'} = $1;
@@ -1374,12 +1472,13 @@ sub parse_commit {
local $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list",
+ "--parents",
"--header",
"--max-count=1",
$commit_id,
"--",
or die_error(undef, "Open git-rev-list failed");
- %co = parse_commit_text(<$fd>);
+ %co = parse_commit_text(<$fd>, 1);
close $fd;
return %co;
@@ -1392,35 +1491,13 @@ sub parse_commits {
$maxcount ||= 1;
$skip ||= 0;
- # Delete once rev-list supports the --skip option
- if ($skip > 0) {
- open my $fd, "-|", git_cmd(), "rev-list",
- ($arg ? ($arg) : ()),
- ("--max-count=" . ($maxcount + $skip)),
- $commit_id,
- "--",
- ($filename ? ($filename) : ())
- or die_error(undef, "Open git-rev-list failed");
- while (my $line = <$fd>) {
- if ($skip-- <= 0) {
- chomp $line;
- my %co = parse_commit($line);
- push @cos, \%co;
- }
- }
- close $fd;
-
- return wantarray ? @cos : \@cos;
- }
-
local $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list",
"--header",
($arg ? ($arg) : ()),
("--max-count=" . $maxcount),
- # Add once rev-list supports the --skip option
- # ("--skip=" . $skip),
+ ("--skip=" . $skip),
$commit_id,
"--",
($filename ? ($filename) : ())
@@ -1497,6 +1574,17 @@ sub parse_difftree_raw_line {
$res{'file'} = unquote($7);
}
}
+ # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh'
+ # combined diff (for merge commit)
+ elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) {
+ $res{'nparents'} = length($1);
+ $res{'from_mode'} = [ split(' ', $2) ];
+ $res{'to_mode'} = pop @{$res{'from_mode'}};
+ $res{'from_id'} = [ split(' ', $3) ];
+ $res{'to_id'} = pop @{$res{'from_id'}};
+ $res{'status'} = [ split('', $4) ];
+ $res{'to_file'} = unquote($5);
+ }
# 'c512b523472485aef4fff9e57b229d9d243c967f'
elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
$res{'commit'} = $1;
@@ -1626,7 +1714,7 @@ sub get_file_owner {
}
my $owner = $gcos;
$owner =~ s/[,;].*$//;
- return to_utf8($owner);
+ return decode_utf8($owner);
}
## ......................................................................
@@ -1709,7 +1797,7 @@ sub git_header_html {
my $title = "$site_name";
if (defined $project) {
- $title .= " - $project";
+ $title .= " - " . decode_utf8($project);
if (defined $action) {
$title .= "/$action";
if (defined $file_name) {
@@ -1734,6 +1822,7 @@ sub git_header_html {
}
print $cgi->header(-type=>$content_type, -charset => 'utf-8',
-status=> $status, -expires => $expires);
+ my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
print <
@@ -1742,7 +1831,7 @@ sub git_header_html {
-
+
$title
EOF
@@ -1818,7 +1907,7 @@ EOF
$cgi->hidden(-name => "a") . "\n" .
$cgi->hidden(-name => "h") . "\n" .
$cgi->popup_menu(-name => 'st', -default => 'commit',
- -values => ['commit', 'author', 'committer', 'pickaxe']) .
+ -values => ['commit', 'author', 'committer', 'pickaxe']) .
$cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
" search:\n",
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
@@ -1888,16 +1977,16 @@ sub git_print_page_nav {
my %arg = map { $_ => {action=>$_} } @navs;
if (defined $head) {
for (qw(commit commitdiff)) {
- $arg{$_}{hash} = $head;
+ $arg{$_}{'hash'} = $head;
}
if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) {
for (qw(shortlog log)) {
- $arg{$_}{hash} = $head;
+ $arg{$_}{'hash'} = $head;
}
}
}
- $arg{tree}{hash} = $treehead if defined $treehead;
- $arg{tree}{hash_base} = $treebase if defined $treebase;
+ $arg{'tree'}{'hash'} = $treehead if defined $treehead;
+ $arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
print "\n" .
(join " | ",
@@ -1945,9 +2034,9 @@ sub git_print_header_div {
my ($action, $title, $hash, $hash_base) = @_;
my %args = ();
- $args{action} = $action;
- $args{hash} = $hash if $hash;
- $args{hash_base} = $hash_base if $hash_base;
+ $args{'action'} = $action;
+ $args{'hash'} = $hash if $hash;
+ $args{'hash_base'} = $hash_base if $hash_base;
print "