From 5be01bc8d5cf9c63827311ad76dd8b66353253e1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 29 Jul 2006 22:43:40 +0200 Subject: gitweb: whitespace cleanup Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 58 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index e5fca63b9..323dfca3f 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -36,7 +36,7 @@ our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; # location for temporary files needed for diffs our $git_temp = "/tmp/gitweb"; if (! -d $git_temp) { - mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); + mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); } # target of the home link on top of all pages @@ -104,7 +104,7 @@ if (defined $project) { die_error(undef, "No such project."); } $rss_link = ""; + "$my_uri?" . esc_param("p=$project;a=rss") . "\" type=\"application/rss+xml\"/>"; $ENV{'GIT_DIR'} = "$projectroot/$project"; } else { git_project_list(); @@ -303,7 +303,7 @@ sub git_header_html { } else { $content_type = 'text/html'; } - print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); + print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); print < @@ -1138,17 +1138,17 @@ sub git_summary { "\n" . ""; if (defined($comment)) { - print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment)); + print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment)); } print "\n" . ""; if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; } print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); } print "\n" . ""; @@ -1457,17 +1457,17 @@ sub git_tags { "\n" . ""; if (defined($comment)) { - print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment); + print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment); } print "\n" . ""; if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; } print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); } print "\n" . ""; @@ -1613,13 +1613,14 @@ sub git_blob_plain_mimetype { sub git_blob_plain { if (!defined $hash) { - if (defined $file_name) { - my $base = $hash_base || git_read_head($project); - $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file."); - } else { - die_error(undef, "No file name defined."); - } - } + if (defined $file_name) { + my $base = $hash_base || git_read_head($project); + $hash = git_get_hash_by_path($base, $file_name, "blob") + or die_error(undef, "Error lookup file."); + } else { + die_error(undef, "No file name defined."); + } + } my $type = shift; open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash"); @@ -1644,13 +1645,14 @@ sub git_blob_plain { sub git_blob { if (!defined $hash) { - if (defined $file_name) { - my $base = $hash_base || git_read_head($project); - $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file."); - } else { - die_error(undef, "No file name defined."); - } - } + if (defined $file_name) { + my $base = $hash_base || git_read_head($project); + $hash = git_get_hash_by_path($base, $file_name, "blob") + or die_error(undef, "Error lookup file."); + } else { + die_error(undef, "No file name defined."); + } + } my $have_blame = git_get_project_config_bool ('blame'); open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed."); my $mimetype = git_blob_plain_mimetype($fd, $file_name); @@ -1677,7 +1679,7 @@ sub git_blob { print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "
\n"; } print "\n". - "
" . + "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "
\n"; } else { @@ -2305,7 +2307,7 @@ sub git_commitdiff { my $status = $5; my $file = validate_input(unquote($6)); if ($status eq "A") { - print "
" . file_type($to_mode) . ":" . + print "
" . file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" . "
\n"; git_diff_print(undef, "/dev/null", $to_id, "b/$file"); @@ -2359,7 +2361,7 @@ sub git_commitdiff_plain { "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n". "Subject: $co{'title'}\n"; if (defined $tagname) { - print "X-Git-Tag: $tagname\n"; + print "X-Git-Tag: $tagname\n"; } print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" . "\n"; -- cgit v1.2.1 From b9182987a80f7e820cbe1f8c7c4dc26f8586e8cd Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 18:28:34 -0700 Subject: gitweb: Use list for of open for running git commands, thorougly. Use list form of open for running git commands and reading their output through pipe, for example open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", $hash instead of open my $fd, "-|", "$GIT rev-list --header --parents $hash" Single letter options use ' instead of " as quotes, according to style used in list form of magic "-|" open. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 323dfca3f..8f7341f45 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -388,7 +388,7 @@ sub die_error { sub git_get_type { my $hash = shift; - open my $fd, "-|", "$GIT cat-file -t $hash" or return; + open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return; my $type = <$fd>; close $fd or return; chomp $type; @@ -440,7 +440,7 @@ sub git_read_tag { my %tag; my @comment; - open my $fd, "-|", "$GIT cat-file tag $tag_id" or return; + open my $fd, "-|", $GIT, "cat-file", "tag", $tag_id or return; $tag{'id'} = $tag_id; while (my $line = <$fd>) { chomp $line; @@ -512,7 +512,7 @@ sub git_read_commit { @commit_lines = @$commit_text; } else { $/ = "\0"; - open my $fd, "-|", "$GIT rev-list --header --parents --max-count=1 $commit_id" or return; + open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return; @commit_lines = split '\n', <$fd>; close $fd or return; $/ = "\n"; @@ -610,7 +610,7 @@ sub git_diff_print { if (defined $from) { $from_tmp = "$git_temp/gitweb_" . $$ . "_from"; open my $fd2, "> $from_tmp"; - open my $fd, "-|", "$GIT cat-file blob $from"; + open my $fd, "-|", $GIT, "cat-file", "blob", $from; my @file = <$fd>; print $fd2 @file; close $fd2; @@ -621,7 +621,7 @@ sub git_diff_print { if (defined $to) { $to_tmp = "$git_temp/gitweb_" . $$ . "_to"; open my $fd2, "> $to_tmp"; - open my $fd, "-|", "$GIT cat-file blob $to"; + open my $fd, "-|", $GIT, "cat-file", "blob", $to; my @file = <$fd>; print $fd2 @file; close $fd2; @@ -1062,7 +1062,8 @@ sub git_summary { "owner$owner\n" . "last change$cd{'rfc2822'}\n" . "\n"; - open my $fd, "-|", "$GIT rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) + or die_error(undef, "Open failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; print "
\n" . @@ -1526,7 +1527,7 @@ sub git_get_hash_by_path { my $tree = $base; my @parts = split '/', $path; while (my $part = shift @parts) { - open my $fd, "-|", "$GIT ls-tree $tree" or die_error(undef, "Open git-ls-tree failed."); + open my $fd, "-|", $GIT, "ls-tree", $tree or die_error(undef, "Open git-ls-tree failed."); my (@entries) = map { chomp; $_ } <$fd>; close $fd or return undef; foreach my $line (@entries) { @@ -1622,7 +1623,7 @@ sub git_blob_plain { } } my $type = shift; - open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash"); + open my $fd, "-|", $GIT, "cat-file", "blob", $hash or die_error("Couldn't cat $file_name, $hash"); $type ||= git_blob_plain_mimetype($fd, $file_name); @@ -1654,7 +1655,7 @@ sub git_blob { } } my $have_blame = git_get_project_config_bool ('blame'); - open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "cat-file", "blob", $hash or die_error(undef, "Open failed."); my $mimetype = git_blob_plain_mimetype($fd, $file_name); if ($mimetype !~ m/^text\//) { close $fd; @@ -1718,7 +1719,7 @@ sub git_tree { } } $/ = "\0"; - open my $fd, "-|", "$GIT ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed."); + open my $fd, "-|", $GIT, "ls-tree", '-z', $hash or die_error(undef, "Open git-ls-tree failed."); chomp (my (@entries) = <$fd>); close $fd or die_error(undef, "Reading tree failed."); $/ = "\n"; @@ -1799,7 +1800,8 @@ sub git_tree { sub git_rss { # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ - open my $fd, "-|", "$GIT rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) + or die_error(undef, "Open failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading rev-list failed."); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); @@ -1819,7 +1821,7 @@ sub git_rss { last; } my %cd = date_str($co{'committer_epoch'}); - open $fd, "-|", "$GIT diff-tree -r $co{'parent'} $co{'id'}" or next; + open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next; my @difftree = map { chomp; $_ } <$fd>; close $fd or next; print "\n" . @@ -1907,7 +1909,7 @@ sub git_log { " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; @@ -1992,15 +1994,14 @@ sub git_commit { my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); my @difftree; - my $root = ""; my $parent = $co{'parent'}; if (!defined $parent) { - $root = " --root"; - $parent = ""; + $parent = "--root"; } - open my $fd, "-|", "$GIT diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash + or die_error(undef, "Open failed."); @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed."); + close $fd or die_error(undef, "Reading git-diff-tree failed."); # non-textual hash id's can be cached my $expires; @@ -2244,7 +2245,8 @@ sub git_commitdiff { if (!defined $hash_parent) { $hash_parent = $co{'parent'}; } - open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash + or die_error(undef, "Open failed."); my (@difftree) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); @@ -2334,14 +2336,15 @@ sub git_commitdiff { sub git_commitdiff_plain { mkdir($git_temp, 0700); - open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash + or die_error(undef, "Open failed."); my (@difftree) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); # try to figure out the next tag after this commit my $tagname; my $refs = read_info_ref("tags"); - open $fd, "-|", "$GIT rev-list HEAD"; + open $fd, "-|", $GIT, "rev-list", "HEAD"; chomp (my (@commits) = <$fd>); close $fd; foreach my $commit (@commits) { @@ -2419,7 +2422,7 @@ sub git_history { git_print_page_path($file_name, $ftype); open my $fd, "-|", - "$GIT rev-list --full-history $hash_base -- \'$file_name\'"; + $GIT, "rev-list", "--full-history", $hash_base, "--", "\'$file_name\'"; print "\n"; my $alternate = 0; while (my $line = <$fd>) { @@ -2506,7 +2509,7 @@ sub git_search { my $alternate = 0; if ($commit_search) { $/ = "\0"; - open my $fd, "-|", "$GIT rev-list --header --parents $hash" or next; + open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", $hash or next; while (my $commit_text = <$fd>) { if (!grep m/$searchtext/i, $commit_text) { next; @@ -2627,7 +2630,7 @@ sub git_shortlog { " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; -- cgit v1.2.1 From dda754f7d4fa1bccd9f3349d73304856144a7cd2 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 29 Jul 2006 22:55:01 +0200 Subject: gitweb: simplify git_get_hash_by_path Simplify git_get_hash_by_path by using git-ls-tree to do path limiting, instead of finding correct ttree and parsing unconstrained git-ls-tree output. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 8f7341f45..9c214f534 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1525,29 +1525,15 @@ sub git_get_hash_by_path { my $path = shift || return undef; my $tree = $base; - my @parts = split '/', $path; - while (my $part = shift @parts) { - open my $fd, "-|", $GIT, "ls-tree", $tree or die_error(undef, "Open git-ls-tree failed."); - my (@entries) = map { chomp; $_ } <$fd>; - close $fd or return undef; - foreach my $line (@entries) { - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; - my $t_mode = $1; - my $t_type = $2; - my $t_hash = $3; - my $t_name = validate_input(unquote($4)); - if ($t_name eq $part) { - if (!(@parts)) { - return $t_hash; - } - if ($t_type eq "tree") { - $tree = $t_hash; - } - last; - } - } - } + + open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path + or die_error(undef, "Open git-ls-tree failed."); + my $line = <$fd>; + close $fd or return undef; + + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + return $3; } sub mimetype_guess_file { -- cgit v1.2.1 From 044bfdc8cbc4955f20afd7f9a245dfc20527ecbd Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 29 Jul 2006 23:01:00 +0200 Subject: gitweb: More explicit error messages for open "-|" Use more explicit error messages when failing magical "-|" open, stating at least the name of the git command that failed. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 9c214f534..bf3e2ca5d 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1063,7 +1063,7 @@ sub git_summary { "\n" . "
last change$cd{'rfc2822'}
\n"; open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-rev-list failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; print "
\n" . @@ -1271,7 +1271,7 @@ sub git_blame2 { die_error("400 Bad Request", "object is not a blob"); } open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) - or die_error(undef, "Open failed"); + or die_error(undef, "Open git-blame failed."); git_header_html(); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . @@ -1333,7 +1333,7 @@ sub git_blame { or die_error(undef, "Error lookup file."); } open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base) - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-annotate failed."); git_header_html(); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . @@ -1609,7 +1609,8 @@ sub git_blob_plain { } } my $type = shift; - open my $fd, "-|", $GIT, "cat-file", "blob", $hash or die_error("Couldn't cat $file_name, $hash"); + open my $fd, "-|", $GIT, "cat-file", "blob", $hash + or die_error("Couldn't cat $file_name, $hash"); $type ||= git_blob_plain_mimetype($fd, $file_name); @@ -1641,7 +1642,8 @@ sub git_blob { } } my $have_blame = git_get_project_config_bool ('blame'); - open my $fd, "-|", $GIT, "cat-file", "blob", $hash or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "cat-file", "blob", $hash + or die_error(undef, "Couldn't cat $file_name, $hash."); my $mimetype = git_blob_plain_mimetype($fd, $file_name); if ($mimetype !~ m/^text\//) { close $fd; @@ -1705,7 +1707,8 @@ sub git_tree { } } $/ = "\0"; - open my $fd, "-|", $GIT, "ls-tree", '-z', $hash or die_error(undef, "Open git-ls-tree failed."); + open my $fd, "-|", $GIT, "ls-tree", '-z', $hash + or die_error(undef, "Open git-ls-tree failed."); chomp (my (@entries) = <$fd>); close $fd or die_error(undef, "Reading tree failed."); $/ = "\n"; @@ -1787,7 +1790,7 @@ sub git_tree { sub git_rss { # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-rev-list failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading rev-list failed."); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); @@ -1895,7 +1898,8 @@ sub git_log { " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash + or die_error(undef, "Open git-rev-list failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; @@ -1985,7 +1989,7 @@ sub git_commit { $parent = "--root"; } open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-diff-tree failed."); @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-diff-tree failed."); @@ -2232,7 +2236,7 @@ sub git_commitdiff { $hash_parent = $co{'parent'}; } open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-diff-tree failed."); my (@difftree) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); @@ -2323,7 +2327,7 @@ sub git_commitdiff { sub git_commitdiff_plain { mkdir($git_temp, 0700); open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open failed."); + or die_error(undef, "Open git-diff-tree failed."); my (@difftree) = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); @@ -2616,7 +2620,8 @@ sub git_shortlog { " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open failed."); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash + or die_error(undef, "Open git-rev-list failed."); my (@revlist) = map { chomp; $_ } <$fd>; close $fd; -- cgit v1.2.1 From cefda27f741a948a78ce27180f5332c2d19199c4 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 04:08:17 +0200 Subject: gitweb: Cleanup - chomp $line in consistent style Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index bf3e2ca5d..e7ae65adf 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -635,7 +635,7 @@ sub git_diff_print { $/ = "\n"; } else { while (my $line = <$fd>) { - chomp($line); + chomp $line; my $char = substr($line, 0, 1); my $diff_class = ""; if ($char eq '+') { @@ -944,7 +944,7 @@ sub read_info_ref { # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} open my $fd, "$projectroot/$project/info/refs" or return; while (my $line = <$fd>) { - chomp($line); + chomp $line; if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { if (defined $refs{$1}) { $refs{$1} .= " / $2"; -- cgit v1.2.1 From 0881d2d1303a067a02e3811d43a1aed04a47945e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 14:58:11 +0200 Subject: gitweb: Cleanup - chomp @lines in consistent style Use 'my @lines = map { chomp; $_ } <$fd>;' form to read all lines of git command output into array without trailing newlines. It has advantage over 'chomp (my (@lines) = <$fd>);' in that it does not modify array. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index e7ae65adf..98505551c 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1064,7 +1064,7 @@ sub git_summary { "\n"; open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) or die_error(undef, "Open git-rev-list failed."); - my (@revlist) = map { chomp; $_ } <$fd>; + my @revlist = map { chomp; $_ } <$fd>; close $fd; print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog"), -class => "title"}, "shortlog") . @@ -1709,7 +1709,7 @@ sub git_tree { $/ = "\0"; open my $fd, "-|", $GIT, "ls-tree", '-z', $hash or die_error(undef, "Open git-ls-tree failed."); - chomp (my (@entries) = <$fd>); + my @entries = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading tree failed."); $/ = "\n"; @@ -1791,7 +1791,7 @@ sub git_rss { # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) or die_error(undef, "Open git-rev-list failed."); - my (@revlist) = map { chomp; $_ } <$fd>; + my @revlist = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading rev-list failed."); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); print "\n". @@ -1900,7 +1900,7 @@ sub git_log { my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open git-rev-list failed."); - my (@revlist) = map { chomp; $_ } <$fd>; + my @revlist = map { chomp; $_ } <$fd>; close $fd; if ($hash ne $head || $page) { @@ -1983,14 +1983,13 @@ sub git_commit { my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); - my @difftree; my $parent = $co{'parent'}; if (!defined $parent) { $parent = "--root"; } open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash or die_error(undef, "Open git-diff-tree failed."); - @difftree = map { chomp; $_ } <$fd>; + my @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-diff-tree failed."); # non-textual hash id's can be cached @@ -2237,7 +2236,7 @@ sub git_commitdiff { } open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash or die_error(undef, "Open git-diff-tree failed."); - my (@difftree) = map { chomp; $_ } <$fd>; + my @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); # non-textual hash id's can be cached @@ -2328,14 +2327,14 @@ sub git_commitdiff_plain { mkdir($git_temp, 0700); open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash or die_error(undef, "Open git-diff-tree failed."); - my (@difftree) = map { chomp; $_ } <$fd>; + my @difftree = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading diff-tree failed."); # try to figure out the next tag after this commit my $tagname; my $refs = read_info_ref("tags"); open $fd, "-|", $GIT, "rev-list", "HEAD"; - chomp (my (@commits) = <$fd>); + my @commits = map { chomp; $_ } <$fd>; close $fd; foreach my $commit (@commits) { if (defined $refs->{$commit}) { @@ -2622,7 +2621,7 @@ sub git_shortlog { my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash or die_error(undef, "Open git-rev-list failed."); - my (@revlist) = map { chomp; $_ } <$fd>; + my @revlist = map { chomp; $_ } <$fd>; close $fd; if ($hash ne $head || $page) { -- cgit v1.2.1 From b18f9bf462cd37bc19368cb7d5c539e019d8fd67 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 14:59:57 +0200 Subject: gitweb: Add git_page_nav for later use Adds git_page_nav subroutine to factor out the generation of the navigation bar. Based on Sven Verdoolaege code Message-Id: <20050618113121.GA13122@pc117b.liacs.nl> http://marc.theaimsgroup.com/?l=git&m=111909432415478&w=2 I tried for the refactored navbar generate the same result. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 98505551c..d0d3f3efb 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -385,6 +385,40 @@ sub die_error { exit; } +sub git_page_nav { + my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; + $extra = '' if !defined $extra; # pager or formats + + my @navs = qw(summary shortlog log commit commitdiff tree); + if ($suppress) { + @navs = grep { $_ ne $suppress } @navs; + } + + my %arg = map { $_, ''} @navs; + if (defined $head) { + for (qw(commit commitdiff)) { + $arg{$_} = ";h=$head"; + } + if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { + for (qw(shortlog log)) { + $arg{$_} = ";h=$head"; + } + } + } + $arg{tree} .= ";h=$treehead" if defined $treehead; + $arg{tree} .= ";hb=$treebase" if defined $treebase; + + print "
\n" . + (join " | ", + map { $_ eq $current + ? $_ + : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") + } + @navs); + print "
$extra
\n" . + "
\n"; +} + sub git_get_type { my $hash = shift; -- cgit v1.2.1 From 0d83ddc40f356aa0b5618a6571f5146818ec2b41 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 15:01:07 +0200 Subject: gitweb: Navbar refactoring - use git_page_nav to generate navigation bar Use git_page_nav subroutine to generate navigation bar. Additional navigation (either formats or pager/pagination) is put into variables. Corrects error in git_search where hash parameter was added to "summary" link instead of to "log" link. Might differ from previous version by additional "
" in navigation bar. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 235 +++++++++++++++--------------------------------------- 1 file changed, 66 insertions(+), 169 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index d0d3f3efb..2b35763ee 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1081,15 +1081,7 @@ sub git_summary { my $refs = read_info_ref(); git_header_html(); - print "
\n" . - "summary". - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree")}, "tree") . - "

\n" . - "
\n"; + git_page_nav('summary','', $head); print "
 
\n"; print "\n" . "\n" . @@ -1251,15 +1243,7 @@ sub git_print_page_path { sub git_tag { my $head = git_read_head($project); git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "
\n" . - "
\n" . - "
\n"; + git_page_nav('','', $head,undef,$head); my %tag = git_read_tag($hash); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($tag{'name'})) . "\n" . @@ -1307,19 +1291,13 @@ sub git_blame2 { open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) or die_error(undef, "Open git-blame failed."); git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "
\n"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "
\n"; - print "
\n". - "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . - "
\n"; + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . + "
\n"; git_print_page_path($file_name, $ftype); my @rev_color = (qw(light dark)); my $num_colors = scalar(@rev_color); @@ -1369,19 +1347,13 @@ sub git_blame { open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base) or die_error(undef, "Open git-annotate failed."); git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "
\n"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "
\n"; - print "
\n". - "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . - "
\n"; + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . + "
\n"; git_print_page_path($file_name); print "
\n"; print <\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "
\n" . - "
\n" . - "
\n"; + git_page_nav('','', $head,undef,$head); my $taglist = git_read_refs("refs/tags"); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . @@ -1515,15 +1479,7 @@ sub git_tags { sub git_heads { my $head = git_read_head($project); git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "
\n" . - "
\n" . - "
\n"; + git_page_nav('','', $head,undef,$head); my $taglist = git_read_refs("refs/heads"); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . @@ -1684,25 +1640,20 @@ sub git_blob { return git_blob_plain($mimetype); } git_header_html(); + my $formats_nav = ''; if (defined $hash_base && (my %co = git_read_commit($hash_base))) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "
\n"; if (defined $file_name) { if ($have_blame) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") . " | "; + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") . " | "; } - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head") . "
\n"; + $formats_nav .= + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head"); } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "
\n"; + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain"); } - print "
\n". - "
" . + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + print "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "
\n"; } else { @@ -1757,15 +1708,7 @@ sub git_tree { my $base = ""; if (defined $hash_base && (my %co = git_read_commit($hash_base))) { $base_key = ";hb=$hash_base"; - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash_base")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash_base")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | tree" . - "

\n" . - "
\n"; + git_page_nav('tree','', $hash_base); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" . "
\n"; @@ -1922,14 +1865,6 @@ sub git_log { $page = 0; } my $refs = read_info_ref(); - git_header_html(); - print "
\n"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") . - " | log" . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash @@ -1937,25 +1872,28 @@ sub git_log { my @revlist = map { chomp; $_ } <$fd>; close $fd; + my $paging_nav = ''; if ($hash ne $head || $page) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "HEAD"); + $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "HEAD"); } else { - print "HEAD"; + $paging_nav .= "HEAD"; } if ($page > 0) { - print " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { - print " ⋅ prev"; + $paging_nav .= " ⋅ prev"; } if ($#revlist >= (100 * ($page+1)-1)) { - print " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); } else { - print " ⋅ next"; + $paging_nav .= " ⋅ next"; } - print "
\n" . - "
\n"; + + git_header_html(); + git_page_nav('log','', $hash,undef,undef, $paging_nav); + if (!@revlist) { print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . @@ -2037,21 +1975,14 @@ sub git_commit { $ref = " " . esc_html($refs->{$co{'id'}}) . ""; } git_header_html(undef, $expires); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") . - " | commit"; - if (defined $co{'parent'}) { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff"); - } - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "\n" . - "
\n"; + my $formats_nav = ''; if (defined $file_name && defined $co{'parent'}) { my $parent = $co{'parent'}; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame") . "\n"; + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame"); } - print "
\n"; + git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', + $hash, $co{'tree'}, $hash, + $formats_nav); if (defined $co{'parent'}) { print "
\n" . @@ -2223,16 +2154,9 @@ sub git_blobdiff { mkdir($git_temp, 0700); git_header_html(); if (defined $hash_base && (my %co = git_read_commit($hash_base))) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . - "
\n"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain") . - "
\n"; + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" . "
\n"; @@ -2284,15 +2208,9 @@ sub git_commitdiff { $ref = " " . esc_html($refs->{$co{'id'}}) . ""; } git_header_html(undef, $expires); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") . - " | commitdiff" . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "
\n"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain") . "\n" . - "
\n"; + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); + git_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" . "
\n"; @@ -2424,15 +2342,7 @@ sub git_history { } my $refs = read_info_ref(); git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . - "

\n" . - "
\n"; + git_page_nav('','', $hash_base,$co{'tree'},$hash_base); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" . "
\n"; @@ -2515,15 +2425,7 @@ sub git_search { $pickaxe_search = 1; } git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary;h=$hash")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . - "

\n" . - "
\n"; + git_page_nav('','', $hash,$co{'tree'},$hash); print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" . @@ -2643,14 +2545,6 @@ sub git_shortlog { $page = 0; } my $refs = read_info_ref(); - git_header_html(); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") . - " | shortlog" . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "
\n"; my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash @@ -2658,25 +2552,28 @@ sub git_shortlog { my @revlist = map { chomp; $_ } <$fd>; close $fd; + my $paging_nav = ''; if ($hash ne $head || $page) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "HEAD"); + $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "HEAD"); } else { - print "HEAD"; + $paging_nav .= "HEAD"; } if ($page > 0) { - print " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { - print " ⋅ prev"; + $paging_nav .= " ⋅ prev"; } if ($#revlist >= (100 * ($page+1)-1)) { - print " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); } else { - print " ⋅ next"; + $paging_nav .= " ⋅ next"; } - print "
\n" . - "
\n"; + + git_header_html(); + git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); + print "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . "
\n"; -- cgit v1.2.1 From 3dc1383290f9db3371a13ae8009ce4fcd5ffc93a Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 15:02:27 +0200 Subject: gitweb: Replace form-feed character by ^L From 2be5cab10486cba804ccae063e93b146288054fe Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 13:11:56 +0200 Subject: [PATCH] Replace FORM FEED (FF) character (014, 12, 0xc) by it's textual representation '^L'. This character is used for example in GNU GPL 'COPYING' file. With this patch "blob" output for COPYING passes XHTML validation. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 1 + 1 file changed, 1 insertion(+) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 2b35763ee..79275f38d 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -250,6 +250,7 @@ sub esc_html { my $str = shift; $str = decode("utf8", $str, Encode::FB_DEFAULT); $str = escapeHTML($str); + $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) return $str; } -- cgit v1.2.1 From 7d91010df4d8c3d6f38734dc37cf7013a490670e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 17:47:22 +0200 Subject: gitweb: Show project descriptions with utf-8 characters in project list correctly Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 79275f38d..673db1f63 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -958,7 +958,7 @@ sub git_project_list { } $alternate ^= 1; print "
\n" . - "\n" . + "\n" . "\n"; print "\n" . " - - - - - -HTML - } # while (my $line = <$fd>) - print "
description" . esc_html($descr) . "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "$pr->{'descr'}" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "" . -- cgit v1.2.1 From 898a893fc9bd6510022e6248af39ca4f521e40d6 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 16:14:43 +0200 Subject: gitweb: Add "\n" after
in git_page_nav Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 673db1f63..03002f04f 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -416,7 +416,7 @@ sub git_page_nav { : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") } @navs); - print "
$extra
\n" . + print "
\n$extra
\n" . "\n"; } -- cgit v1.2.1 From 43ffc06d98e944670b71869ba7126003597a2fa0 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 17:49:00 +0200 Subject: gitweb: Pager refactoring - use git_get_paging_nav for pagination Add git_get_paging_nav subroutine which returns string with pager (paging nav) for shortlog and log actions. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 68 ++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 03002f04f..8e5e4c83c 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -420,6 +420,36 @@ sub git_page_nav { "\n"; } +sub git_get_paging_nav { + my ($project, $action, $hash, $head, $page, $nrevs) = @_; + my $paging_nav; + + + if ($hash ne $head || $page) { + $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action")}, "HEAD"); + } else { + $paging_nav .= "HEAD"; + } + + if ($page > 0) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)), + -accesskey => "p", -title => "Alt-p"}, "prev"); + } else { + $paging_nav .= " ⋅ prev"; + } + + if ($nrevs >= (100 * ($page+1)-1)) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)), + -accesskey => "n", -title => "Alt-n"}, "next"); + } else { + $paging_nav .= " ⋅ next"; + } + + return $paging_nav; +} + sub git_get_type { my $hash = shift; @@ -1873,24 +1903,7 @@ sub git_log { my @revlist = map { chomp; $_ } <$fd>; close $fd; - my $paging_nav = ''; - if ($hash ne $head || $page) { - $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "HEAD"); - } else { - $paging_nav .= "HEAD"; - } - if ($page > 0) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); - } else { - $paging_nav .= " ⋅ prev"; - } - if ($#revlist >= (100 * ($page+1)-1)) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } + my $paging_nav = git_get_paging_nav($project, 'log', $hash, $head, $page, $#revlist); git_header_html(); git_page_nav('log','', $hash,undef,undef, $paging_nav); @@ -2553,24 +2566,7 @@ sub git_shortlog { my @revlist = map { chomp; $_ } <$fd>; close $fd; - my $paging_nav = ''; - if ($hash ne $head || $page) { - $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "HEAD"); - } else { - $paging_nav .= "HEAD"; - } - if ($page > 0) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev"); - } else { - $paging_nav .= " ⋅ prev"; - } - if ($#revlist >= (100 * ($page+1)-1)) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } + my $paging_nav = git_get_paging_nav($project, 'shortlog', $hash, $head, $page, $#revlist); git_header_html(); git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); -- cgit v1.2.1 From 6855f42ecd8cfb77ec7494ac9eea9b1ef2d634a0 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 20:31:00 +0200 Subject: gitweb: Remove $project from git_get_paging_nav arguments Remove $project from arguments passed to git_get_paging_nav subroutine: it did not depend only on arguments, using $my_uri global variable (and now $project global variable). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 8e5e4c83c..3134b9ce5 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -421,7 +421,7 @@ sub git_page_nav { } sub git_get_paging_nav { - my ($project, $action, $hash, $head, $page, $nrevs) = @_; + my ($action, $hash, $head, $page, $nrevs) = @_; my $paging_nav; @@ -1903,7 +1903,7 @@ sub git_log { my @revlist = map { chomp; $_ } <$fd>; close $fd; - my $paging_nav = git_get_paging_nav($project, 'log', $hash, $head, $page, $#revlist); + my $paging_nav = git_get_paging_nav('log', $hash, $head, $page, $#revlist); git_header_html(); git_page_nav('log','', $hash,undef,undef, $paging_nav); @@ -2566,7 +2566,7 @@ sub git_shortlog { my @revlist = map { chomp; $_ } <$fd>; close $fd; - my $paging_nav = git_get_paging_nav($project, 'shortlog', $hash, $head, $page, $#revlist); + my $paging_nav = git_get_paging_nav('shortlog', $hash, $head, $page, $#revlist); git_header_html(); git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); -- cgit v1.2.1 From 27fb8c40c6b634c67f5b5fb3b354a207cc10b343 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 20:32:01 +0200 Subject: gitweb: Headers refactoring - use git_header_div for header divs Add git_header_div subroutine which prints "header" divs, now with class "header" (class "title" is taken, and has set CSS style, changing appereance and maing layout wrong), and use it thorough gitweb.cgi. Change header linking to project summary from empty (  as a contents of link) to having $project as contents/name of link. Sometimes a little reordering. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 100 ++++++++++++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 3134b9ce5..101286904 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -420,6 +420,19 @@ sub git_page_nav { "\n"; } +sub git_header_div { + my ($action, $title, $hash, $hash_base) = @_; + my $rest = ''; + + $rest .= ";h=$hash" if $hash; + $rest .= ";hb=$hash_base" if $hash_base; + + print "
\n" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action$rest"), + -class => "title"}, $title ? $title : $action) . "\n" . + "
\n"; +} + sub git_get_paging_nav { my ($action, $hash, $head, $page, $nrevs) = @_; my $paging_nav; @@ -1123,9 +1136,7 @@ sub git_summary { or die_error(undef, "Open git-rev-list failed."); my @revlist = map { chomp; $_ } <$fd>; close $fd; - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog"), -class => "title"}, "shortlog") . - "
\n"; + git_header_div('shortlog'); my $i = 16; print "\n"; my $alternate = 0; @@ -1169,9 +1180,7 @@ sub git_summary { my $taglist = git_read_refs("refs/tags"); if (defined @$taglist) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags"), -class => "title"}, "tags") . - "
\n"; + git_header_div('tags'); my $i = 16; print "
\n"; my $alternate = 0; @@ -1221,9 +1230,7 @@ sub git_summary { my $headlist = git_read_refs("refs/heads"); if (defined @$headlist) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads"), -class => "title"}, "heads") . - "
\n"; + git_header_div('heads'); my $i = 16; print "
\n"; my $alternate = 0; @@ -1276,9 +1283,7 @@ sub git_tag { git_header_html(); git_page_nav('','', $head,undef,$head); my %tag = git_read_tag($hash); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($tag{'name'})) . "\n" . - "
\n"; + git_header_div('commit', esc_html($tag{'name'}), $hash); print "
\n" . "
\n" . "\n" . @@ -1326,9 +1331,7 @@ sub git_blame2 { $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . - "
\n"; + git_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, $ftype); my @rev_color = (qw(light dark)); my $num_colors = scalar(@rev_color); @@ -1382,9 +1385,7 @@ sub git_blame { $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . - "
\n"; + git_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name); print "
\n"; print <\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . - "
\n"; + git_header_div('summary', $project); print "
\n"; + + my $taglist = git_read_refs("refs/tags"); my $alternate = 0; if (defined @$taglist) { foreach my $entry (@$taglist) { @@ -1511,11 +1511,10 @@ sub git_heads { my $head = git_read_head($project); git_header_html(); git_page_nav('','', $head,undef,$head); - my $taglist = git_read_refs("refs/heads"); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . - "
\n"; + hit_header_div('summary', $project); print "
\n"; + + my $taglist = git_read_refs("refs/heads"); my $alternate = 0; if (defined @$taglist) { foreach my $entry (@$taglist) { @@ -1684,9 +1683,7 @@ sub git_blob { $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain"); } git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . - "
\n"; + git_header_div('commit', esc_html($co{'title'}), $hash_base); } else { print "
\n" . "

\n" . @@ -1740,9 +1737,7 @@ sub git_tree { if (defined $hash_base && (my %co = git_read_commit($hash_base))) { $base_key = ";hb=$hash_base"; git_page_nav('tree','', $hash_base); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" . - "
\n"; + git_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); } else { print "
\n"; print "

\n"; @@ -1909,10 +1904,9 @@ sub git_log { git_page_nav('log','', $hash,undef,undef, $paging_nav); if (!@revlist) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . - "
\n"; my %co = git_read_commit($hash); + + git_header_div('summary', $project); print "
Last change $co{'age_string'}.

\n"; } for (my $i = ($page * 100); $i <= $#revlist; $i++) { @@ -1924,10 +1918,10 @@ sub git_log { my %co = git_read_commit($commit); next if !%co; my %ad = date_str($co{'author_epoch'}); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "title"}, - "$co{'age_string'}" . esc_html($co{'title'}) . $ref) . "\n"; - print "
\n"; + git_header_div('commit', + "$co{'age_string'}" . + esc_html($co{'title'}) . $ref, + $commit); print "
\n" . "
\n" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . @@ -1999,13 +1993,9 @@ sub git_commit { $formats_nav); if (defined $co{'parent'}) { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" . - "
\n"; + git_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); } else { - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" . - "
\n"; + git_header_div('tree', esc_html($co{'title'}), $co{'tree'}, $hash); } print "
\n" . "
\n"; @@ -2171,9 +2161,7 @@ sub git_blobdiff { my $formats_nav = $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" . - "
\n"; + git_header_div('commit', esc_html($co{'title'}), $hash_base); } else { print "
\n" . "

\n" . @@ -2225,9 +2213,7 @@ sub git_commitdiff { my $formats_nav = $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); git_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" . - "
\n"; + git_header_div('commit', esc_html($co{'title'}) . $ref, $hash); print "
\n"; my $comment = $co{'comment'}; my $empty = 0; @@ -2357,9 +2343,7 @@ sub git_history { my $refs = read_info_ref(); git_header_html(); git_page_nav('','', $hash_base,$co{'tree'},$hash_base); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" . - "
\n"; + git_header_div('commit', esc_html($co{'title'}), $hash_base); if (!defined $hash && defined $file_name) { $hash = git_get_hash_by_path($hash_base, $file_name); } @@ -2440,10 +2424,8 @@ sub git_search { } git_header_html(); git_page_nav('','', $hash,$co{'tree'},$hash); + git_header_div('commit', esc_html($co{'title'}), $hash); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" . - "
\n"; print "
\n"; my $alternate = 0; if ($commit_search) { @@ -2570,10 +2552,8 @@ sub git_shortlog { git_header_html(); git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); + git_header_div('summary', $project); - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, " ") . - "
\n"; print "
\n"; my $alternate = 0; for (my $i = ($page * 100); $i <= $#revlist; $i++) { -- cgit v1.2.1 From 7ca84b506281495b9ec43801d08b7209b08be92d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 30 Jul 2006 22:36:04 +0200 Subject: gitweb: Remove characters entities entirely when shortening string Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 1 + 1 file changed, 1 insertion(+) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 101286904..1ff29bc29 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -776,6 +776,7 @@ sub chop_str { my $tail = $2; if (length($tail) > 4) { $tail = " ..."; + $body =~ s/&[^;]$//; # remove chopped character entities } return "$body$tail"; } -- cgit v1.2.1 From 594e212bc849039a204deef1d16c2eddcc451532 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 31 Jul 2006 02:21:52 +0200 Subject: gitweb: Ref refactoring - use git_get_referencing for marking tagged/head commits Use git_get_referencing to get HTML code for markers showing which refs (tags and heads) point to current commit. It would be much easier to change format of markers in one or two places than thorough the gitweb.cgi file. Added comment about read_info_ref subroutine: for $type == "" (empty argument) it saves only last path part of ref name e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb'. Some reordering. Added $ref in one place. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 53 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 1ff29bc29..2f1731870 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1024,6 +1024,8 @@ sub read_info_ref { open my $fd, "$projectroot/$project/info/refs" or return; while (my $line = <$fd>) { chomp $line; + # attention: for $type == "" it saves only last path part of ref name + # e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb' if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { if (defined $refs{$1}) { $refs{$1} .= " / $2"; @@ -1036,6 +1038,16 @@ sub read_info_ref { return \%refs; } +sub git_get_referencing { + my ($refs, $id) = @_; + + if (defined $refs->{$id}) { + return ' ' . esc_html($refs->{$id}) . ''; + } else { + return ""; + } +} + sub git_read_refs { my $ref_dir = shift; my @reflist; @@ -1151,10 +1163,7 @@ sub git_summary { } $alternate ^= 1; if ($i-- > 0) { - my $ref = ""; - if (defined $refs->{$commit}) { - $ref = " " . esc_html($refs->{$commit}) . ""; - } + my $ref = git_get_referencing($refs, $commit); print "\n" . "\n" . "
$co{'age_string'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; @@ -1728,10 +1737,7 @@ sub git_tree { $/ = "\n"; my $refs = read_info_ref(); - my $ref = ""; - if (defined $refs->{$hash_base}) { - $ref = " " . esc_html($refs->{$hash_base}) . ""; - } + my $ref = git_get_referencing($refs, $hash_base); git_header_html(); my $base_key = ""; my $base = ""; @@ -1912,10 +1918,7 @@ sub git_log { } for (my $i = ($page * 100); $i <= $#revlist; $i++) { my $commit = $revlist[$i]; - my $ref = ""; - if (defined $refs->{$commit}) { - $ref = " " . esc_html($refs->{$commit}) . ""; - } + my $ref = git_get_referencing($refs, $commit); my %co = git_read_commit($commit); next if !%co; my %ad = date_str($co{'author_epoch'}); @@ -1979,16 +1982,13 @@ sub git_commit { $expires = "+1d"; } my $refs = read_info_ref(); - my $ref = ""; - if (defined $refs->{$co{'id'}}) { - $ref = " " . esc_html($refs->{$co{'id'}}) . ""; - } - git_header_html(undef, $expires); + my $ref = git_get_referencing($refs, $co{'id'}); my $formats_nav = ''; if (defined $file_name && defined $co{'parent'}) { my $parent = $co{'parent'}; $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame"); } + git_header_html(undef, $expires); git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', $hash, $co{'tree'}, $hash, $formats_nav); @@ -1996,7 +1996,7 @@ sub git_commit { if (defined $co{'parent'}) { git_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); } else { - git_header_div('tree', esc_html($co{'title'}), $co{'tree'}, $hash); + git_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash); } print "
\n" . "\n"; @@ -2206,13 +2206,10 @@ sub git_commitdiff { $expires = "+1d"; } my $refs = read_info_ref(); - my $ref = ""; - if (defined $refs->{$co{'id'}}) { - $ref = " " . esc_html($refs->{$co{'id'}}) . ""; - } - git_header_html(undef, $expires); + my $ref = git_get_referencing($refs, $co{'id'}); my $formats_nav = $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); + git_header_html(undef, $expires); git_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); git_header_div('commit', esc_html($co{'title'}) . $ref, $hash); print "
\n"; @@ -2364,10 +2361,7 @@ sub git_history { if (!%co) { next; } - my $ref = ""; - if (defined $refs->{$commit}) { - $ref = " " . esc_html($refs->{$commit}) . ""; - } + my $ref = git_get_referencing($refs, $commit); if ($alternate) { print "
\n"; } else { @@ -2559,10 +2553,7 @@ sub git_shortlog { my $alternate = 0; for (my $i = ($page * 100); $i <= $#revlist; $i++) { my $commit = $revlist[$i]; - my $ref = ""; - if (defined $refs->{$commit}) { - $ref = " " . esc_html($refs->{$commit}) . ""; - } + my $ref = git_get_referencing($refs, $commit); my %co = git_read_commit($commit); my %ad = date_str($co{'author_epoch'}); if ($alternate) { -- cgit v1.2.1 From 9f5dcb8168d5ea9f0169e11f017ecd9710d85797 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 31 Jul 2006 11:22:13 +0200 Subject: gitweb: Refactor generation of shortlog, tags and heads body Add git_shortlog_body, git_tags_body and git_heads_body to generate table with shortlog, tags and heads respectively in git_summary and git_shortlog, git_tags, git_heads respectively. Better support for lightweight tags in git_read_refs; currently only lightweight tag pointing to tag object is not resolved fully. Shortlog, tags and heads body tables have proper class now (we could use id instead of class). Add support for showing full comment on mouseover to tags list when comment is shortened, similar to how full title of commit was/is shown on mouseover when title was shortened. Changed layout of tags table to better show lightweight tags. Add showing which branch (head) is current branch (current head), using "current_head" class (we could use id instead). Corrected "" and hit_header_div instead of git_header_div. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 383 +++++++++++++++++++++++++----------------------------- gitweb/gitweb.css | 10 +- 2 files changed, 184 insertions(+), 209 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 2f1731870..c741e739d 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1102,6 +1102,10 @@ sub git_read_refs { $ref_item{'refid'} = $ref_id; $ref_item{'epoch'} = $co{'committer_epoch'}; $ref_item{'age'} = $co{'age_string'}; + } else { + $ref_item{'reftype'} = $type; + $ref_item{'name'} = $ref_file; + $ref_item{'refid'} = $ref_id; } push @reflist, \%ref_item; @@ -1111,6 +1115,156 @@ sub git_read_refs { return \@reflist; } +sub git_shortlog_body { + # uses global variable $project + my ($revlist, $from, $to, $refs, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to); + + print "
\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $commit = $revlist->[$i]; + #my $ref = defined $refs ? git_get_referencing($refs, $commit) : ''; + my $ref = git_get_referencing($refs, $commit); + my %co = git_read_commit($commit); + my %ad = date_str($co{'author_epoch'}); + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + # git_summary() used print "\n" . + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$co{'age_string'}$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; + if (length($co{'title_short'}) < length($co{'title'})) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), + -class => "list", -title => "$co{'title'}"}, + "" . esc_html($co{'title_short'}) . "$ref"); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), + -class => "list"}, + "" . esc_html($co{'title'}) . "$ref"); + } + print "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . + "
$extra
\n"; +} + +sub git_tags_body { + # uses global variable $project + my ($taglist, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $comment_lines = $tag{'comment'}; + my $comment = shift @$comment_lines; + my $comment_short; + if (defined $comment) { + $comment_short = chop_str($comment, 30, 5); + } + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + ""; + if (defined $comment) { + if (length($comment_short) < length($comment)) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list", -title => $comment}, $comment_short); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list"}, $comment); + } + } + print ""; + if ($tag{'type'} eq "tag") { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag"); + } else { + print " "; + } + print "" . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); + if ($tag{'reftype'} eq "commit") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); + } elsif ($tag{'reftype'} eq "blob") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw"); + } + print "
$extra
\n"; +} + +sub git_heads_body { + # uses global variable $project + my ($taglist, $head, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $curr = $tag{'id'} eq $head; + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + ($tag{'id'} eq $head ? "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" : "") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . + "
$extra
\n"; +} + sub git_summary { my $descr = git_read_description($project) || "none"; my $head = git_read_head($project); @@ -1139,138 +1293,36 @@ sub git_summary { my $refs = read_info_ref(); git_header_html(); git_page_nav('summary','', $head); + print "
 
\n"; print "\n" . "\n" . "\n" . "\n" . "
description" . esc_html($descr) . "
owner$owner
last change$cd{'rfc2822'}
\n"; + open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) or die_error(undef, "Open git-rev-list failed."); my @revlist = map { chomp; $_ } <$fd>; close $fd; git_header_div('shortlog'); - my $i = 16; - print "\n"; - my $alternate = 0; - foreach my $commit (@revlist) { - my %co = git_read_commit($commit); - my %ad = date_str($co{'author_epoch'}); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - if ($i-- > 0) { - my $ref = git_get_referencing($refs, $commit); - print "\n" . - "\n" . - "\n" . - "\n" . - ""; - } else { - print "\n" . - ""; - last; - } - } - print ""; + git_shortlog_body(\@revlist, 0, 15, $refs, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...")); my $taglist = git_read_refs("refs/tags"); if (defined @$taglist) { git_header_div('tags'); - my $i = 16; - print "
$co{'age_string'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; - if (length($co{'title_short'}) < length($co{'title'})) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"}, - "" . esc_html($co{'title_short'}) . "$ref"); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, - "" . esc_html($co{'title'}) . "$ref"); - } - print "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...") . "
\n"; - my $alternate = 0; - foreach my $entry (@$taglist) { - my %tag = %$entry; - my $comment_lines = $tag{'comment'}; - my $comment = shift @$comment_lines; - if (defined($comment)) { - $comment = chop_str($comment, 30, 5); - } - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - if ($i-- > 0) { - print "\n" . - "\n" . - "\n" . - "\n" . - ""; - } else { - print "\n" . - ""; - last; - } - } - print ""; + git_tags_body($taglist, 0, 15, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...")); } my $headlist = git_read_refs("refs/heads"); if (defined @$headlist) { git_header_div('heads'); - my $i = 16; - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"}, - "" . esc_html($tag{'name'}) . "") . - ""; - if (defined($comment)) { - print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment)); - } - print ""; - if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; - } - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); - if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); - } - print "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...") . "
\n"; - my $alternate = 0; - foreach my $entry (@$headlist) { - my %tag = %$entry; - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - if ($i-- > 0) { - print "\n" . - "\n" . - "\n" . - ""; - } else { - print "\n" . - ""; - last; - } - } - print ""; + git_heads_body($taglist, $head, 0, 15, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...")); } + git_footer_html(); } @@ -1472,48 +1524,11 @@ sub git_tags { git_header_html(); git_page_nav('','', $head,undef,$head); git_header_div('summary', $project); - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"}, - "" . esc_html($tag{'name'}) . "") . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . - "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...") . "
\n"; my $taglist = git_read_refs("refs/tags"); - my $alternate = 0; if (defined @$taglist) { - foreach my $entry (@$taglist) { - my %tag = %$entry; - my $comment_lines = $tag{'comment'}; - my $comment = shift @$comment_lines; - if (defined($comment)) { - $comment = chop_str($comment, 30, 5); - } - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - ""; - } + git_tags_body($taglist); } - print ""; git_footer_html(); } @@ -1521,32 +1536,13 @@ sub git_heads { my $head = git_read_head($project); git_header_html(); git_page_nav('','', $head,undef,$head); - hit_header_div('summary', $project); - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"}, - "" . esc_html($tag{'name'}) . "") . - ""; - if (defined($comment)) { - print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment); - } - print ""; - if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | "; - } - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); - if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); - } - print "
\n"; + git_header_div('summary', $project); my $taglist = git_read_refs("refs/heads"); my $alternate = 0; if (defined @$taglist) { - foreach my $entry (@$taglist) { - my %tag = %$entry; - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - ""; - } + git_heads_body($taglist, $head); } - print ""; git_footer_html(); } @@ -2544,48 +2540,19 @@ sub git_shortlog { close $fd; my $paging_nav = git_get_paging_nav('shortlog', $hash, $head, $page, $#revlist); + my $next_link = ''; + if ($#revlist >= (100 * ($page+1)-1)) { + $next_link = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), + -title => "Alt-n"}, "next"); + } + git_header_html(); git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); git_header_div('summary', $project); - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"}, "" . esc_html($tag{'name'}) . "") . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . - "
\n"; - my $alternate = 0; - for (my $i = ($page * 100); $i <= $#revlist; $i++) { - my $commit = $revlist[$i]; - my $ref = git_get_referencing($refs, $commit); - my %co = git_read_commit($commit); - my %ad = date_str($co{'author_epoch'}); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - ""; - } - if ($#revlist >= (100 * ($page+1)-1)) { - print "\n" . - "\n" . - "\n"; - } - print ""; + git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link); + git_footer_html(); } diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index fffdb13d0..460e72871 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -181,12 +181,16 @@ td { vertical-align: top; } -td.link { +td.link, td.selflink { padding: 2px 5px; font-family: sans-serif; font-size: 10px; } +td.selflink { + padding-right: 0px; +} + td.sha1 { font-family: monospace; } @@ -196,6 +200,10 @@ td.error { background-color: yellow; } +td.current_head { + text-decoration: underline; +} + table.diff_tree span.file_status.new { color: #008000; } -- cgit v1.2.1 From 822c185907f27c02ce7791985ea9241e37041d5f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 31 Jul 2006 18:33:37 +0200 Subject: gitweb: do not quote path for list version of open "-|" Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index c741e739d..73d14ffb1 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -2347,7 +2347,7 @@ sub git_history { git_print_page_path($file_name, $ftype); open my $fd, "-|", - $GIT, "rev-list", "--full-history", $hash_base, "--", "\'$file_name\'"; + $GIT, "rev-list", "--full-history", $hash_base, "--", $file_name; print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; - if (length($co{'title_short'}) < length($co{'title'})) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"}, - "" . esc_html($co{'title_short'}) . "$ref"); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, - "" . esc_html($co{'title_short'}) . "$ref"); - } - print "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -title => "Alt-n"}, "next") . - "
\n"; my $alternate = 0; while (my $line = <$fd>) { -- cgit v1.2.1 From 0349b4650969c9c59c58d99c95be3677f5bb2d15 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 31 Jul 2006 20:58:00 +0200 Subject: gitweb: Remove characters entities entirely when shortening string -- correction Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 73d14ffb1..0953b8cd5 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -776,7 +776,7 @@ sub chop_str { my $tail = $2; if (length($tail) > 4) { $tail = " ..."; - $body =~ s/&[^;]$//; # remove chopped character entities + $body =~ s/&[^;]*$//; # remove chopped character entities } return "$body$tail"; } -- cgit v1.2.1 From 717b831178a6c0e86ed5e7cd61002bd9ec0c2a95 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 31 Jul 2006 21:22:15 +0200 Subject: gitweb: Reordering code and dividing it into categories Reorder gitweb code around, divide it into sections (categories) and add some comments. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 1870 +++++++++++++++++++++++++++-------------------------- 1 file changed, 967 insertions(+), 903 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 0953b8cd5..16c368155 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -160,21 +160,7 @@ if (defined $searchtext) { $searchtext = quotemeta $searchtext; } -sub validate_input { - my $input = shift; - - if ($input =~ m/^[0-9a-fA-F]{40}$/) { - return $input; - } - if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) { - return undef; - } - if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) { - return undef; - } - return $input; -} - +# dispatch if (!defined $action || $action eq "summary") { git_summary(); exit; @@ -235,6 +221,24 @@ if (!defined $action || $action eq "summary") { exit; } +## ====================================================================== +## validation, quoting/unquoting and escaping + +sub validate_input { + my $input = shift; + + if ($input =~ m/^[0-9a-fA-F]{40}$/) { + return $input; + } + if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) { + return undef; + } + if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) { + return undef; + } + return $input; +} + # quote unsafe chars, but keep the slash, even when it's not # correct, but quoted slashes look too horrible in bookmarks sub esc_param { @@ -264,6 +268,29 @@ sub unquote { return $str; } +## ---------------------------------------------------------------------- +## HTML aware string manipulation + +sub chop_str { + my $str = shift; + my $len = shift; + my $add_len = shift || 10; + + # allow only $len chars, but don't cut a word if it would fit in $add_len + # if it doesn't fit, cut it if it's still longer than the dots we would add + $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/; + my $body = $1; + my $tail = $2; + if (length($tail) > 4) { + $tail = " ..."; + $body =~ s/&[^;]*$//; # remove chopped character entities + } + return "$body$tail"; +} + +## ---------------------------------------------------------------------- +## functions returning short strings + # CSS class for given age value (in seconds) sub age_class { my $age = shift; @@ -277,202 +304,108 @@ sub age_class { } } -sub git_header_html { - my $status = shift || "200 OK"; - my $expires = shift; +# convert age in seconds to "nn units ago" string +sub age_string { + my $age = shift; + my $age_str; - my $title = "$site_name git"; - if (defined $project) { - $title .= " - $project"; - if (defined $action) { - $title .= "/$action"; - if (defined $file_name) { - $title .= " - $file_name"; - if ($action eq "tree" && $file_name !~ m|/$|) { - $title .= "/"; - } - } - } - } - my $content_type; - # require explicit support from the UA if we are to send the page as - # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. - # we have to do this because MSIE sometimes globs '*/*', pretending to - # support xhtml+xml but choking when it gets what it asked for. - if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { - $content_type = 'application/xhtml+xml'; + if ($age > 60*60*24*365*2) { + $age_str = (int $age/60/60/24/365); + $age_str .= " years ago"; + } elsif ($age > 60*60*24*(365/12)*2) { + $age_str = int $age/60/60/24/(365/12); + $age_str .= " months ago"; + } elsif ($age > 60*60*24*7*2) { + $age_str = int $age/60/60/24/7; + $age_str .= " weeks ago"; + } elsif ($age > 60*60*24*2) { + $age_str = int $age/60/60/24; + $age_str .= " days ago"; + } elsif ($age > 60*60*2) { + $age_str = int $age/60/60; + $age_str .= " hours ago"; + } elsif ($age > 60*2) { + $age_str = int $age/60; + $age_str .= " min ago"; + } elsif ($age > 2) { + $age_str = int $age; + $age_str .= " sec ago"; } else { - $content_type = 'text/html'; - } - print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); - print < - - - - - - - -$title - -$rss_link - - -EOF - print "
\n" . - "" . - "\"git\"" . - "\n"; - print $cgi->a({-href => esc_param($home_link)}, "projects") . " / "; - if (defined $project) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project)); - if (defined $action) { - print " / $action"; - } - print "\n"; - if (!defined $searchtext) { - $searchtext = ""; - } - my $search_hash; - if (defined $hash_base) { - $search_hash = $hash_base; - } elsif (defined $hash) { - $search_hash = $hash; - } else { - $search_hash = "HEAD"; - } - $cgi->param("a", "search"); - $cgi->param("h", $search_hash); - print $cgi->startform(-method => "get", -action => $my_uri) . - "
\n" . - $cgi->hidden(-name => "p") . "\n" . - $cgi->hidden(-name => "a") . "\n" . - $cgi->hidden(-name => "h") . "\n" . - $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . - "
" . - $cgi->end_form() . "\n"; + $age_str .= " right now"; } - print "
\n"; + return $age_str; } -sub git_footer_html { - print "
\n"; - if (defined $project) { - my $descr = git_read_description($project); - if (defined $descr) { - print "\n"; - } - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n"; +# convert file mode in octal to symbolic file mode string +sub mode_str { + my $mode = oct shift; + + if (S_ISDIR($mode & S_IFMT)) { + return 'drwxr-xr-x'; + } elsif (S_ISLNK($mode)) { + return 'lrwxrwxrwx'; + } elsif (S_ISREG($mode)) { + # git cares only about the executable bit + if ($mode & S_IXUSR) { + return '-rwxr-xr-x'; + } else { + return '-rw-r--r--'; + }; } else { - print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n"; + return '----------'; } - print "
\n" . - "\n" . - ""; } -sub die_error { - my $status = shift || "403 Forbidden"; - my $error = shift || "Malformed query, file missing or permission denied"; +# convert file mode in octal to file type string +sub file_type { + my $mode = oct shift; - git_header_html($status); - print "
\n" . - "

\n" . - "$status - $error\n" . - "
\n" . - "
\n"; - git_footer_html(); - exit; + if (S_ISDIR($mode & S_IFMT)) { + return "directory"; + } elsif (S_ISLNK($mode)) { + return "symlink"; + } elsif (S_ISREG($mode)) { + return "file"; + } else { + return "unknown"; + } } -sub git_page_nav { - my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; - $extra = '' if !defined $extra; # pager or formats +## ---------------------------------------------------------------------- +## functions returning short HTML fragments, or transforming HTML fragments +## which don't beling to other sections - my @navs = qw(summary shortlog log commit commitdiff tree); - if ($suppress) { - @navs = grep { $_ ne $suppress } @navs; - } +# format line of commit message or tag comment +sub format_log_line_html { + my $line = shift; - my %arg = map { $_, ''} @navs; - if (defined $head) { - for (qw(commit commitdiff)) { - $arg{$_} = ";h=$head"; - } - if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { - for (qw(shortlog log)) { - $arg{$_} = ";h=$head"; - } + $line = esc_html($line); + $line =~ s/ / /g; + if ($line =~ m/([0-9a-fA-F]{40})/) { + my $hash_text = $1; + if (git_get_type($hash_text) eq "commit") { + my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text); + $line =~ s/$hash_text/$link/; } } - $arg{tree} .= ";h=$treehead" if defined $treehead; - $arg{tree} .= ";hb=$treebase" if defined $treebase; - - print "
\n" . - (join " | ", - map { $_ eq $current - ? $_ - : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") - } - @navs); - print "
\n$extra
\n" . - "
\n"; + return $line; } -sub git_header_div { - my ($action, $title, $hash, $hash_base) = @_; - my $rest = ''; - - $rest .= ";h=$hash" if $hash; - $rest .= ";hb=$hash_base" if $hash_base; +# format marker of refs pointing to given object +sub git_get_referencing { + my ($refs, $id) = @_; - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action$rest"), - -class => "title"}, $title ? $title : $action) . "\n" . - "
\n"; + if (defined $refs->{$id}) { + return ' ' . esc_html($refs->{$id}) . ''; + } else { + return ""; + } } -sub git_get_paging_nav { - my ($action, $hash, $head, $page, $nrevs) = @_; - my $paging_nav; - - - if ($hash ne $head || $page) { - $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action")}, "HEAD"); - } else { - $paging_nav .= "HEAD"; - } - - if ($page > 0) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)), - -accesskey => "p", -title => "Alt-p"}, "prev"); - } else { - $paging_nav .= " ⋅ prev"; - } - - if ($nrevs >= (100 * ($page+1)-1)) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } - - return $paging_nav; -} - -sub git_get_type { - my $hash = shift; - - open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return; - my $type = <$fd>; - close $fd or return; - chomp $type; - return $type; -} +## ---------------------------------------------------------------------- +## git utility subroutines, invoking git commands +# get HEAD ref of given project as hash sub git_read_head { my $project = shift; my $oENV = $ENV{'GIT_DIR'}; @@ -491,6 +424,57 @@ sub git_read_head { return $retval; } +# get type of given object +sub git_get_type { + my $hash = shift; + + open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return; + my $type = <$fd>; + close $fd or return; + chomp $type; + return $type; +} + +sub git_get_project_config { + my $key = shift; + + return unless ($key); + $key =~ s/^gitweb\.//; + return if ($key =~ m/\W/); + + my $val = qx($GIT repo-config --get gitweb.$key); + return ($val); +} + +sub git_get_project_config_bool { + my $val = git_get_project_config (@_); + if ($val and $val =~ m/true|yes|on/) { + return (1); + } + return; # implicit false +} + +# get hash of given path at given ref +sub git_get_hash_by_path { + my $base = shift; + my $path = shift || return undef; + + my $tree = $base; + + open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path + or die_error(undef, "Open git-ls-tree failed."); + my $line = <$fd>; + close $fd or return undef; + + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + return $3; +} + +## ...................................................................... +## git utility functions, directly accessing git repository + +# assumes that PATH is not symref sub git_read_hash { my $path = shift; @@ -513,6 +497,100 @@ sub git_read_description { return $descr; } +sub git_read_projects { + my @list; + + if (-d $projects_list) { + # search in directory + my $dir = $projects_list; + opendir my ($dh), $dir or return undef; + while (my $dir = readdir($dh)) { + if (-e "$projectroot/$dir/HEAD") { + my $pr = { + path => $dir, + }; + push @list, $pr + } + } + closedir($dh); + } elsif (-f $projects_list) { + # read from file(url-encoded): + # 'git%2Fgit.git Linus+Torvalds' + # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' + # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' + open my ($fd), $projects_list or return undef; + while (my $line = <$fd>) { + chomp $line; + my ($path, $owner) = split ' ', $line; + $path = unescape($path); + $owner = unescape($owner); + if (!defined $path) { + next; + } + if (-e "$projectroot/$path/HEAD") { + my $pr = { + path => $path, + owner => decode("utf8", $owner, Encode::FB_DEFAULT), + }; + push @list, $pr + } + } + close $fd; + } + @list = sort {$a->{'path'} cmp $b->{'path'}} @list; + return @list; +} + +sub read_info_ref { + my $type = shift || ""; + my %refs; + # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 + # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} + open my $fd, "$projectroot/$project/info/refs" or return; + while (my $line = <$fd>) { + chomp $line; + # attention: for $type == "" it saves only last path part of ref name + # e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb' + if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { + if (defined $refs{$1}) { + $refs{$1} .= " / $2"; + } else { + $refs{$1} = $2; + } + } + } + close $fd or return; + return \%refs; +} + +## ---------------------------------------------------------------------- +## parse to hash functions + +sub date_str { + my $epoch = shift; + my $tz = shift || "-0000"; + + my %date; + my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); + my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch); + $date{'hour'} = $hour; + $date{'minute'} = $min; + $date{'mday'} = $mday; + $date{'day'} = $days[$wday]; + $date{'month'} = $months[$mon]; + $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; + $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; + + $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; + my $local = $epoch + ((int $1 + ($2/60)) * 3600); + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); + $date{'hour_local'} = $hour; + $date{'minute_local'} = $min; + $date{'tz_local'} = $tz; + return %date; +} + sub git_read_tag { my $tag_id = shift; my %tag; @@ -548,37 +626,6 @@ sub git_read_tag { return %tag } -sub age_string { - my $age = shift; - my $age_str; - - if ($age > 60*60*24*365*2) { - $age_str = (int $age/60/60/24/365); - $age_str .= " years ago"; - } elsif ($age > 60*60*24*(365/12)*2) { - $age_str = int $age/60/60/24/(365/12); - $age_str .= " months ago"; - } elsif ($age > 60*60*24*7*2) { - $age_str = int $age/60/60/24/7; - $age_str .= " weeks ago"; - } elsif ($age > 60*60*24*2) { - $age_str = int $age/60/60/24; - $age_str .= " days ago"; - } elsif ($age > 60*60*2) { - $age_str = int $age/60/60; - $age_str .= " hours ago"; - } elsif ($age > 60*2) { - $age_str = int $age/60; - $age_str .= " min ago"; - } elsif ($age > 2) { - $age_str = int $age; - $age_str .= " sec ago"; - } else { - $age_str .= " right now"; - } - return $age_str; -} - sub git_read_commit { my $commit_id = shift; my $commit_text = shift; @@ -673,187 +720,78 @@ sub git_read_commit { return %co; } -sub git_diff_print { - my $from = shift; - my $from_name = shift; - my $to = shift; - my $to_name = shift; - my $format = shift || "html"; - - my $from_tmp = "/dev/null"; - my $to_tmp = "/dev/null"; - my $pid = $$; +## ...................................................................... +## parse to array of hashes functions - # create tmp from-file - if (defined $from) { - $from_tmp = "$git_temp/gitweb_" . $$ . "_from"; - open my $fd2, "> $from_tmp"; - open my $fd, "-|", $GIT, "cat-file", "blob", $from; - my @file = <$fd>; - print $fd2 @file; - close $fd2; - close $fd; - } +sub git_read_refs { + my $ref_dir = shift; + my @reflist; - # create tmp to-file - if (defined $to) { - $to_tmp = "$git_temp/gitweb_" . $$ . "_to"; - open my $fd2, "> $to_tmp"; - open my $fd, "-|", $GIT, "cat-file", "blob", $to; - my @file = <$fd>; - print $fd2 @file; - close $fd2; - close $fd; + my @refs; + opendir my $dh, "$projectroot/$project/$ref_dir"; + while (my $dir = readdir($dh)) { + if ($dir =~ m/^\./) { + next; + } + if (-d "$projectroot/$project/$ref_dir/$dir") { + opendir my $dh2, "$projectroot/$project/$ref_dir/$dir"; + my @subdirs = grep !m/^\./, readdir $dh2; + closedir($dh2); + foreach my $subdir (@subdirs) { + push @refs, "$dir/$subdir" + } + next; + } + push @refs, $dir; } - - open my $fd, "-|", "/usr/bin/diff -u -p -L \'$from_name\' -L \'$to_name\' $from_tmp $to_tmp"; - if ($format eq "plain") { - undef $/; - print <$fd>; - $/ = "\n"; - } else { - while (my $line = <$fd>) { - chomp $line; - my $char = substr($line, 0, 1); - my $diff_class = ""; - if ($char eq '+') { - $diff_class = " add"; - } elsif ($char eq "-") { - $diff_class = " rem"; - } elsif ($char eq "@") { - $diff_class = " chunk_header"; - } elsif ($char eq "\\") { - # skip errors - next; - } - while ((my $pos = index($line, "\t")) != -1) { - if (my $count = (8 - (($pos-1) % 8))) { - my $spaces = ' ' x $count; - $line =~ s/\t/$spaces/; - } + closedir($dh); + foreach my $ref_file (@refs) { + my $ref_id = git_read_hash("$project/$ref_dir/$ref_file"); + my $type = git_get_type($ref_id) || next; + my %ref_item; + my %co; + $ref_item{'type'} = $type; + $ref_item{'id'} = $ref_id; + $ref_item{'epoch'} = 0; + $ref_item{'age'} = "unknown"; + if ($type eq "tag") { + my %tag = git_read_tag($ref_id); + $ref_item{'comment'} = $tag{'comment'}; + if ($tag{'type'} eq "commit") { + %co = git_read_commit($tag{'object'}); + $ref_item{'epoch'} = $co{'committer_epoch'}; + $ref_item{'age'} = $co{'age_string'}; + } elsif (defined($tag{'epoch'})) { + my $age = time - $tag{'epoch'}; + $ref_item{'epoch'} = $tag{'epoch'}; + $ref_item{'age'} = age_string($age); } - print "
" . esc_html($line) . "
\n"; - } - } - close $fd; - - if (defined $from) { - unlink($from_tmp); - } - if (defined $to) { - unlink($to_tmp); - } -} - -sub mode_str { - my $mode = oct shift; - - if (S_ISDIR($mode & S_IFMT)) { - return 'drwxr-xr-x'; - } elsif (S_ISLNK($mode)) { - return 'lrwxrwxrwx'; - } elsif (S_ISREG($mode)) { - # git cares only about the executable bit - if ($mode & S_IXUSR) { - return '-rwxr-xr-x'; + $ref_item{'reftype'} = $tag{'type'}; + $ref_item{'name'} = $tag{'name'}; + $ref_item{'refid'} = $tag{'object'}; + } elsif ($type eq "commit"){ + %co = git_read_commit($ref_id); + $ref_item{'reftype'} = "commit"; + $ref_item{'name'} = $ref_file; + $ref_item{'title'} = $co{'title'}; + $ref_item{'refid'} = $ref_id; + $ref_item{'epoch'} = $co{'committer_epoch'}; + $ref_item{'age'} = $co{'age_string'}; } else { - return '-rw-r--r--'; - }; - } else { - return '----------'; - } -} - -sub chop_str { - my $str = shift; - my $len = shift; - my $add_len = shift || 10; - - # allow only $len chars, but don't cut a word if it would fit in $add_len - # if it doesn't fit, cut it if it's still longer than the dots we would add - $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/; - my $body = $1; - my $tail = $2; - if (length($tail) > 4) { - $tail = " ..."; - $body =~ s/&[^;]*$//; # remove chopped character entities - } - return "$body$tail"; -} - -sub file_type { - my $mode = oct shift; - - if (S_ISDIR($mode & S_IFMT)) { - return "directory"; - } elsif (S_ISLNK($mode)) { - return "symlink"; - } elsif (S_ISREG($mode)) { - return "file"; - } else { - return "unknown"; - } -} - -sub format_log_line_html { - my $line = shift; - - $line = esc_html($line); - $line =~ s/ / /g; - if ($line =~ m/([0-9a-fA-F]{40})/) { - my $hash_text = $1; - if (git_get_type($hash_text) eq "commit") { - my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text); - $line =~ s/$hash_text/$link/; + $ref_item{'reftype'} = $type; + $ref_item{'name'} = $ref_file; + $ref_item{'refid'} = $ref_id; } - } - return $line; -} - -sub date_str { - my $epoch = shift; - my $tz = shift || "-0000"; - - my %date; - my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); - my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); - my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch); - $date{'hour'} = $hour; - $date{'minute'} = $min; - $date{'mday'} = $mday; - $date{'day'} = $days[$wday]; - $date{'month'} = $months[$mon]; - $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; - $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; - $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; - my $local = $epoch + ((int $1 + ($2/60)) * 3600); - ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); - $date{'hour_local'} = $hour; - $date{'minute_local'} = $min; - $date{'tz_local'} = $tz; - return %date; + push @reflist, \%ref_item; + } + # sort tags by age + @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; + return \@reflist; } -# git-logo (cached in browser for one day) -sub git_logo { - binmode STDOUT, ':raw'; - print $cgi->header(-type => 'image/png', -expires => '+1d'); - # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g' - print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" . - "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" . - "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" . - "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" . - "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" . - "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" . - "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" . - "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" . - "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" . - "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" . - "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" . - "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" . - "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"; -} +## ---------------------------------------------------------------------- +## filesystem-related functions sub get_file_owner { my $path = shift; @@ -868,253 +806,283 @@ sub get_file_owner { return decode("utf8", $owner, Encode::FB_DEFAULT); } -sub git_read_projects { - my @list; +## ...................................................................... +## mimetype related functions - if (-d $projects_list) { - # search in directory - my $dir = $projects_list; - opendir my ($dh), $dir or return undef; - while (my $dir = readdir($dh)) { - if (-e "$projectroot/$dir/HEAD") { - my $pr = { - path => $dir, - }; - push @list, $pr - } - } - closedir($dh); - } elsif (-f $projects_list) { - # read from file(url-encoded): - # 'git%2Fgit.git Linus+Torvalds' - # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' - # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' - open my ($fd), $projects_list or return undef; - while (my $line = <$fd>) { - chomp $line; - my ($path, $owner) = split ' ', $line; - $path = unescape($path); - $owner = unescape($owner); - if (!defined $path) { - next; - } - if (-e "$projectroot/$path/HEAD") { - my $pr = { - path => $path, - owner => decode("utf8", $owner, Encode::FB_DEFAULT), - }; - push @list, $pr - } +sub mimetype_guess_file { + my $filename = shift; + my $mimemap = shift; + -r $mimemap or return undef; + + my %mimemap; + open(MIME, $mimemap) or return undef; + while () { + my ($mime, $exts) = split(/\t+/); + my @exts = split(/\s+/, $exts); + foreach my $ext (@exts) { + $mimemap{$ext} = $mime; } - close $fd; } - @list = sort {$a->{'path'} cmp $b->{'path'}} @list; - return @list; -} + close(MIME); -sub git_get_project_config { - my $key = shift; + $filename =~ /\.(.*?)$/; + return $mimemap{$1}; +} - return unless ($key); - $key =~ s/^gitweb\.//; - return if ($key =~ m/\W/); +sub mimetype_guess { + my $filename = shift; + my $mime; + $filename =~ /\./ or return undef; - my $val = qx($GIT repo-config --get gitweb.$key); - return ($val); + if ($mimetypes_file) { + my $file = $mimetypes_file; + #$file =~ m#^/# or $file = "$projectroot/$path/$file"; + $mime = mimetype_guess_file($filename, $file); + } + $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); + return $mime; } -sub git_get_project_config_bool { - my $val = git_get_project_config (@_); - if ($val and $val =~ m/true|yes|on/) { - return (1); - } - return; # implicit false -} +sub git_blob_plain_mimetype { + my $fd = shift; + my $filename = shift; -sub git_project_list { - my @list = git_read_projects(); - my @projects; - if (!@list) { - die_error(undef, "No project found."); - } - foreach my $pr (@list) { - my $head = git_read_head($pr->{'path'}); - if (!defined $head) { - next; - } - $ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}"; - my %co = git_read_commit($head); - if (!%co) { - next; - } - $pr->{'commit'} = \%co; - if (!defined $pr->{'descr'}) { - my $descr = git_read_description($pr->{'path'}) || ""; - $pr->{'descr'} = chop_str($descr, 25, 5); - } - if (!defined $pr->{'owner'}) { - $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || ""; - } - push @projects, $pr; - } - git_header_html(); - if (-f $home_text) { - print "
\n"; - open (my $fd, $home_text); - print <$fd>; - close $fd; - print "
\n"; - } - print "
\n" . - "\n"; - if (!defined($order) || (defined($order) && ($order eq "project"))) { - @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; - print "\n"; - } else { - print "\n"; + if ($filename) { + my $mime = mimetype_guess($filename); + $mime and return $mime; } - if (defined($order) && ($order eq "descr")) { - @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; - print "\n"; + + # just in case + return $default_blob_plain_mimetype unless $fd; + + if (-T $fd) { + return 'text/plain' . + ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); + } elsif (! $filename) { + return 'application/octet-stream'; + } elsif ($filename =~ m/\.png$/i) { + return 'image/png'; + } elsif ($filename =~ m/\.gif$/i) { + return 'image/gif'; + } elsif ($filename =~ m/\.jpe?g$/i) { + return 'image/jpeg'; } else { - print "\n"; + return 'application/octet-stream'; } - if (defined($order) && ($order eq "owner")) { - @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; - print "\n"; - } else { - print "\n"; +} + +## ====================================================================== +## functions printing HTML: header, footer, error page + +sub git_header_html { + my $status = shift || "200 OK"; + my $expires = shift; + + my $title = "$site_name git"; + if (defined $project) { + $title .= " - $project"; + if (defined $action) { + $title .= "/$action"; + if (defined $file_name) { + $title .= " - $file_name"; + if ($action eq "tree" && $file_name !~ m|/$|) { + $title .= "/"; + } + } + } } - if (defined($order) && ($order eq "age")) { - @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; - print "\n"; + my $content_type; + # require explicit support from the UA if we are to send the page as + # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. + # we have to do this because MSIE sometimes globs '*/*', pretending to + # support xhtml+xml but choking when it gets what it asked for. + if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { + $content_type = 'application/xhtml+xml'; } else { - print "\n"; + $content_type = 'text/html'; } - print "\n" . - "\n"; - my $alternate = 0; - foreach my $pr (@projects) { - if ($alternate) { - print "\n"; + print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); + print < + + + + + + + +$title + +$rss_link + + +EOF + print "
\n" . + "" . + "\"git\"" . + "\n"; + print $cgi->a({-href => esc_param($home_link)}, "projects") . " / "; + if (defined $project) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project)); + if (defined $action) { + print " / $action"; + } + print "\n"; + if (!defined $searchtext) { + $searchtext = ""; + } + my $search_hash; + if (defined $hash_base) { + $search_hash = $hash_base; + } elsif (defined $hash) { + $search_hash = $hash; } else { - print "
\n"; + $search_hash = "HEAD"; } - $alternate ^= 1; - print "\n" . - "\n" . - "\n"; - print "\n" . - "\n" . - "\n"; + $cgi->param("a", "search"); + $cgi->param("h", $search_hash); + print $cgi->startform(-method => "get", -action => $my_uri) . + "
\n" . + $cgi->hidden(-name => "p") . "\n" . + $cgi->hidden(-name => "a") . "\n" . + $cgi->hidden(-name => "h") . "\n" . + $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . + "
" . + $cgi->end_form() . "\n"; } - print "
Project" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=project")}, "Project") . "Description" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=descr")}, "Description") . "Owner" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=owner")}, "Owner") . "Last Change" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=age")}, "Last Change") . "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . - "
\n"; + print "
\n"; +} + +sub git_footer_html { + print "
\n"; + if (defined $project) { + my $descr = git_read_description($project); + if (defined $descr) { + print "\n"; + } + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n"; + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n"; + } + print "
\n" . + "\n" . + ""; +} + +sub die_error { + my $status = shift || "403 Forbidden"; + my $error = shift || "Malformed query, file missing or permission denied"; + + git_header_html($status); + print "
\n" . + "

\n" . + "$status - $error\n" . + "
\n" . + "
\n"; git_footer_html(); + exit; } -sub read_info_ref { - my $type = shift || ""; - my %refs; - # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 - # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} - open my $fd, "$projectroot/$project/info/refs" or return; - while (my $line = <$fd>) { - chomp $line; - # attention: for $type == "" it saves only last path part of ref name - # e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb' - if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { - if (defined $refs{$1}) { - $refs{$1} .= " / $2"; - } else { - $refs{$1} = $2; +## ---------------------------------------------------------------------- +## functions printing or outputting HTML: navigation + +sub git_page_nav { + my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; + $extra = '' if !defined $extra; # pager or formats + + my @navs = qw(summary shortlog log commit commitdiff tree); + if ($suppress) { + @navs = grep { $_ ne $suppress } @navs; + } + + my %arg = map { $_, ''} @navs; + if (defined $head) { + for (qw(commit commitdiff)) { + $arg{$_} = ";h=$head"; + } + if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { + for (qw(shortlog log)) { + $arg{$_} = ";h=$head"; } } } - close $fd or return; - return \%refs; + $arg{tree} .= ";h=$treehead" if defined $treehead; + $arg{tree} .= ";hb=$treebase" if defined $treebase; + + print "
\n" . + (join " | ", + map { $_ eq $current + ? $_ + : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") + } + @navs); + print "
\n$extra
\n" . + "
\n"; } -sub git_get_referencing { - my ($refs, $id) = @_; +sub git_get_paging_nav { + my ($action, $hash, $head, $page, $nrevs) = @_; + my $paging_nav; - if (defined $refs->{$id}) { - return ' ' . esc_html($refs->{$id}) . ''; + + if ($hash ne $head || $page) { + $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action")}, "HEAD"); } else { - return ""; + $paging_nav .= "HEAD"; + } + + if ($page > 0) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)), + -accesskey => "p", -title => "Alt-p"}, "prev"); + } else { + $paging_nav .= " ⋅ prev"; + } + + if ($nrevs >= (100 * ($page+1)-1)) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)), + -accesskey => "n", -title => "Alt-n"}, "next"); + } else { + $paging_nav .= " ⋅ next"; } + + return $paging_nav; } -sub git_read_refs { - my $ref_dir = shift; - my @reflist; +## ...................................................................... +## functions printing or outputting HTML: div + +sub git_header_div { + my ($action, $title, $hash, $hash_base) = @_; + my $rest = ''; + + $rest .= ";h=$hash" if $hash; + $rest .= ";hb=$hash_base" if $hash_base; + + print "
\n" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action$rest"), + -class => "title"}, $title ? $title : $action) . "\n" . + "
\n"; +} - my @refs; - opendir my $dh, "$projectroot/$project/$ref_dir"; - while (my $dir = readdir($dh)) { - if ($dir =~ m/^\./) { - next; - } - if (-d "$projectroot/$project/$ref_dir/$dir") { - opendir my $dh2, "$projectroot/$project/$ref_dir/$dir"; - my @subdirs = grep !m/^\./, readdir $dh2; - closedir($dh2); - foreach my $subdir (@subdirs) { - push @refs, "$dir/$subdir" - } - next; - } - push @refs, $dir; - } - closedir($dh); - foreach my $ref_file (@refs) { - my $ref_id = git_read_hash("$project/$ref_dir/$ref_file"); - my $type = git_get_type($ref_id) || next; - my %ref_item; - my %co; - $ref_item{'type'} = $type; - $ref_item{'id'} = $ref_id; - $ref_item{'epoch'} = 0; - $ref_item{'age'} = "unknown"; - if ($type eq "tag") { - my %tag = git_read_tag($ref_id); - $ref_item{'comment'} = $tag{'comment'}; - if ($tag{'type'} eq "commit") { - %co = git_read_commit($tag{'object'}); - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } elsif (defined($tag{'epoch'})) { - my $age = time - $tag{'epoch'}; - $ref_item{'epoch'} = $tag{'epoch'}; - $ref_item{'age'} = age_string($age); - } - $ref_item{'reftype'} = $tag{'type'}; - $ref_item{'name'} = $tag{'name'}; - $ref_item{'refid'} = $tag{'object'}; - } elsif ($type eq "commit"){ - %co = git_read_commit($ref_id); - $ref_item{'reftype'} = "commit"; - $ref_item{'name'} = $ref_file; - $ref_item{'title'} = $co{'title'}; - $ref_item{'refid'} = $ref_id; - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } else { - $ref_item{'reftype'} = $type; - $ref_item{'name'} = $ref_file; - $ref_item{'refid'} = $ref_id; - } +sub git_print_page_path { + my $name = shift; + my $type = shift; - push @reflist, \%ref_item; + if (!defined $name) { + print "
/
\n"; + } elsif ($type =~ "blob") { + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "
\n"; + } else { + print "
" . esc_html($name) . "
\n"; } - # sort tags by age - @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; - return \@reflist; } +## ...................................................................... +## functions printing large fragments of HTML + sub git_shortlog_body { # uses global variable $project my ($revlist, $from, $to, $refs, $extra) = @_; @@ -1163,106 +1131,291 @@ sub git_shortlog_body { print "
\n"; } -sub git_tags_body { - # uses global variable $project - my ($taglist, $from, $to, $extra) = @_; - $from = 0 unless defined $from; - $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); +sub git_tags_body { + # uses global variable $project + my ($taglist, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $comment_lines = $tag{'comment'}; + my $comment = shift @$comment_lines; + my $comment_short; + if (defined $comment) { + $comment_short = chop_str($comment, 30, 5); + } + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + ""; + if (defined $comment) { + if (length($comment_short) < length($comment)) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list", -title => $comment}, $comment_short); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list"}, $comment); + } + } + print ""; + if ($tag{'type'} eq "tag") { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag"); + } else { + print " "; + } + print "" . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); + if ($tag{'reftype'} eq "commit") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); + } elsif ($tag{'reftype'} eq "blob") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw"); + } + print "
$extra
\n"; +} + +sub git_heads_body { + # uses global variable $project + my ($taglist, $head, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $curr = $tag{'id'} eq $head; + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + ($tag{'id'} eq $head ? "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" : "") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . + "
$extra
\n"; +} + +## ---------------------------------------------------------------------- +## functions printing large fragments, format as one of arguments + +sub git_diff_print { + my $from = shift; + my $from_name = shift; + my $to = shift; + my $to_name = shift; + my $format = shift || "html"; + + my $from_tmp = "/dev/null"; + my $to_tmp = "/dev/null"; + my $pid = $$; + + # create tmp from-file + if (defined $from) { + $from_tmp = "$git_temp/gitweb_" . $$ . "_from"; + open my $fd2, "> $from_tmp"; + open my $fd, "-|", $GIT, "cat-file", "blob", $from; + my @file = <$fd>; + print $fd2 @file; + close $fd2; + close $fd; + } + + # create tmp to-file + if (defined $to) { + $to_tmp = "$git_temp/gitweb_" . $$ . "_to"; + open my $fd2, "> $to_tmp"; + open my $fd, "-|", $GIT, "cat-file", "blob", $to; + my @file = <$fd>; + print $fd2 @file; + close $fd2; + close $fd; + } + + open my $fd, "-|", "/usr/bin/diff -u -p -L \'$from_name\' -L \'$to_name\' $from_tmp $to_tmp"; + if ($format eq "plain") { + undef $/; + print <$fd>; + $/ = "\n"; + } else { + while (my $line = <$fd>) { + chomp $line; + my $char = substr($line, 0, 1); + my $diff_class = ""; + if ($char eq '+') { + $diff_class = " add"; + } elsif ($char eq "-") { + $diff_class = " rem"; + } elsif ($char eq "@") { + $diff_class = " chunk_header"; + } elsif ($char eq "\\") { + # skip errors + next; + } + while ((my $pos = index($line, "\t")) != -1) { + if (my $count = (8 - (($pos-1) % 8))) { + my $spaces = ' ' x $count; + $line =~ s/\t/$spaces/; + } + } + print "
" . esc_html($line) . "
\n"; + } + } + close $fd; + + if (defined $from) { + unlink($from_tmp); + } + if (defined $to) { + unlink($to_tmp); + } +} + + +## ====================================================================== +## ====================================================================== +## actions + +# git-logo (cached in browser for one day) +sub git_logo { + binmode STDOUT, ':raw'; + print $cgi->header(-type => 'image/png', -expires => '+1d'); + # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g' + print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" . + "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" . + "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" . + "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" . + "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" . + "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" . + "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" . + "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" . + "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" . + "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" . + "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" . + "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" . + "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"; +} - print "\n"; - my $alternate = 0; - for (my $i = $from; $i <= $to; $i++) { - my $entry = $taglist->[$i]; - my %tag = %$entry; - my $comment_lines = $tag{'comment'}; - my $comment = shift @$comment_lines; - my $comment_short; - if (defined $comment) { - $comment_short = chop_str($comment, 30, 5); - } - if ($alternate) { - print "\n"; - } else { - print "\n"; +sub git_project_list { + my @list = git_read_projects(); + my @projects; + if (!@list) { + die_error(undef, "No project found."); + } + foreach my $pr (@list) { + my $head = git_read_head($pr->{'path'}); + if (!defined $head) { + next; } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - ""; + push @projects, $pr; } - if (defined $extra) { - print "\n" . - "\n" . - "\n"; + git_header_html(); + if (-f $home_text) { + print "
\n"; + open (my $fd, $home_text); + print <$fd>; + close $fd; + print "
\n"; } - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . - ""; - if (defined $comment) { - if (length($comment_short) < length($comment)) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), - -class => "list", -title => $comment}, $comment_short); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), - -class => "list"}, $comment); - } + $ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}"; + my %co = git_read_commit($head); + if (!%co) { + next; } - print ""; - if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag"); - } else { - print " "; + $pr->{'commit'} = \%co; + if (!defined $pr->{'descr'}) { + my $descr = git_read_description($pr->{'path'}) || ""; + $pr->{'descr'} = chop_str($descr, 25, 5); } - print "" . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); - if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); - } elsif ($tag{'reftype'} eq "blob") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw"); + if (!defined $pr->{'owner'}) { + $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || ""; } - print "
$extra
\n"; -} - -sub git_heads_body { - # uses global variable $project - my ($taglist, $head, $from, $to, $extra) = @_; - $from = 0 unless defined $from; - $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); - - print "\n"; + print "
\n" . + "\n"; + if (!defined($order) || (defined($order) && ($order eq "project"))) { + @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; + print "\n"; + } else { + print "\n"; + } + if (defined($order) && ($order eq "descr")) { + @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; + print "\n"; + } else { + print "\n"; + } + if (defined($order) && ($order eq "owner")) { + @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; + print "\n"; + } else { + print "\n"; + } + if (defined($order) && ($order eq "age")) { + @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; + print "\n"; + } else { + print "\n"; + } + print "\n" . + "\n"; my $alternate = 0; - for (my $i = $from; $i <= $to; $i++) { - my $entry = $taglist->[$i]; - my %tag = %$entry; - my $curr = $tag{'id'} eq $head; + foreach my $pr (@projects) { if ($alternate) { print "\n"; } else { print "\n"; } $alternate ^= 1; - print "\n" . - ($tag{'id'} eq $head ? "\n" . + print "\n" . + "\n" . + "\n"; + print "\n" . "\n" . - ""; - } - if (defined $extra) { - print "\n" . - "\n" . "\n"; } print "
Project" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=project")}, "Project") . "Description" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=descr")}, "Description") . "Owner" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=owner")}, "Owner") . "Last Change" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=age")}, "Last Change") . "
$tag{'age'}" : "") . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . - "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . "
$extra
\n"; + git_footer_html(); } sub git_summary { @@ -1326,20 +1479,6 @@ sub git_summary { git_footer_html(); } -sub git_print_page_path { - my $name = shift; - my $type = shift; - - if (!defined $name) { - print "
/
\n"; - } elsif ($type =~ "blob") { - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "
\n"; - } else { - print "
" . esc_html($name) . "
\n"; - } -} - sub git_tag { my $head = git_read_head($project); git_header_html(); @@ -1500,128 +1639,50 @@ HTML my $spaces = ' ' x $count; $data =~ s/\t/$spaces/; } - } - $data = esc_html ($data); - - print < -
$short_rev..$age_str$author$lineno$data
\n\n"; - close $fd or print "Reading blob failed.\n"; - print "
"; - git_footer_html(); -} - -sub git_tags { - my $head = git_read_head($project); - git_header_html(); - git_page_nav('','', $head,undef,$head); - git_header_div('summary', $project); - - my $taglist = git_read_refs("refs/tags"); - if (defined @$taglist) { - git_tags_body($taglist); - } - git_footer_html(); -} - -sub git_heads { - my $head = git_read_head($project); - git_header_html(); - git_page_nav('','', $head,undef,$head); - git_header_div('summary', $project); - - my $taglist = git_read_refs("refs/heads"); - my $alternate = 0; - if (defined @$taglist) { - git_heads_body($taglist, $head); - } - git_footer_html(); -} - -sub git_get_hash_by_path { - my $base = shift; - my $path = shift || return undef; - - my $tree = $base; - - open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path - or die_error(undef, "Open git-ls-tree failed."); - my $line = <$fd>; - close $fd or return undef; - - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; - return $3; -} - -sub mimetype_guess_file { - my $filename = shift; - my $mimemap = shift; - -r $mimemap or return undef; - - my %mimemap; - open(MIME, $mimemap) or return undef; - while () { - my ($mime, $exts) = split(/\t+/); - my @exts = split(/\s+/, $exts); - foreach my $ext (@exts) { - $mimemap{$ext} = $mime; - } - } - close(MIME); + } + $data = esc_html ($data); - $filename =~ /\.(.*?)$/; - return $mimemap{$1}; + print < + $short_rev.. + $age_str + $author + $lineno + $data + +HTML + } # while (my $line = <$fd>) + print "\n\n"; + close $fd or print "Reading blob failed.\n"; + print "
"; + git_footer_html(); } -sub mimetype_guess { - my $filename = shift; - my $mime; - $filename =~ /\./ or return undef; +sub git_tags { + my $head = git_read_head($project); + git_header_html(); + git_page_nav('','', $head,undef,$head); + git_header_div('summary', $project); - if ($mimetypes_file) { - my $file = $mimetypes_file; - #$file =~ m#^/# or $file = "$projectroot/$path/$file"; - $mime = mimetype_guess_file($filename, $file); + my $taglist = git_read_refs("refs/tags"); + if (defined @$taglist) { + git_tags_body($taglist); } - $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); - return $mime; + git_footer_html(); } -sub git_blob_plain_mimetype { - my $fd = shift; - my $filename = shift; - - if ($filename) { - my $mime = mimetype_guess($filename); - $mime and return $mime; - } - - # just in case - return $default_blob_plain_mimetype unless $fd; +sub git_heads { + my $head = git_read_head($project); + git_header_html(); + git_page_nav('','', $head,undef,$head); + git_header_div('summary', $project); - if (-T $fd) { - return 'text/plain' . - ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); - } elsif (! $filename) { - return 'application/octet-stream'; - } elsif ($filename =~ m/\.png$/i) { - return 'image/png'; - } elsif ($filename =~ m/\.gif$/i) { - return 'image/gif'; - } elsif ($filename =~ m/\.jpe?g$/i) { - return 'image/jpeg'; - } else { - return 'application/octet-stream'; + my $taglist = git_read_refs("refs/heads"); + my $alternate = 0; + if (defined @$taglist) { + git_heads_body($taglist, $head); } + git_footer_html(); } sub git_blob_plain { @@ -1793,98 +1854,6 @@ sub git_tree { git_footer_html(); } -sub git_rss { - # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ - open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) - or die_error(undef, "Open git-rev-list failed."); - my @revlist = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading rev-list failed."); - print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n"; - print "\n"; - print "$project\n". - "" . esc_html("$my_url?p=$project;a=summary") . "\n". - "$project log\n". - "en\n"; - - for (my $i = 0; $i <= $#revlist; $i++) { - my $commit = $revlist[$i]; - my %co = git_read_commit($commit); - # we read 150, we always show 30 and the ones more recent than 48 hours - if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) { - last; - } - my %cd = date_str($co{'committer_epoch'}); - open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next; - my @difftree = map { chomp; $_ } <$fd>; - close $fd or next; - print "\n" . - "" . - sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) . - "\n" . - "" . esc_html($co{'author'}) . "\n" . - "$cd{'rfc2822'}\n" . - "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . - "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . - "" . esc_html($co{'title'}) . "\n" . - "" . - "\n"; - } - print "
\n"; - foreach my $line (@difftree) { - if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { - next; - } - my $file = validate_input(unquote($7)); - $file = decode("utf8", $file, Encode::FB_DEFAULT); - print "$file
\n"; - } - print "]]>\n" . - "
\n" . - "
\n"; - } - print "
"; -} - -sub git_opml { - my @list = git_read_projects(); - - print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n". - "". - " $site_name Git OPML Export\n". - "\n". - "\n". - "\n"; - - foreach my $pr (@list) { - my %proj = %$pr; - my $head = git_read_head($proj{'path'}); - if (!defined $head) { - next; - } - $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}"; - my %co = git_read_commit($head); - if (!%co) { - next; - } - - my $path = esc_html(chop_str($proj{'path'}, 25, 5)); - my $rss = "$my_url?p=$proj{'path'};a=rss"; - my $html = "$my_url?p=$proj{'path'};a=summary"; - print "\n"; - } - print "\n". - "\n". - "\n"; -} - sub git_log { my $head = git_read_head($project); if (!defined $hash) { @@ -2556,3 +2525,98 @@ sub git_shortlog { git_footer_html(); } + +## ...................................................................... +## feeds (RSS, OPML) + +sub git_rss { + # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ + open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) + or die_error(undef, "Open git-rev-list failed."); + my @revlist = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading rev-list failed."); + print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); + print "\n". + "\n"; + print "\n"; + print "$project\n". + "" . esc_html("$my_url?p=$project;a=summary") . "\n". + "$project log\n". + "en\n"; + + for (my $i = 0; $i <= $#revlist; $i++) { + my $commit = $revlist[$i]; + my %co = git_read_commit($commit); + # we read 150, we always show 30 and the ones more recent than 48 hours + if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) { + last; + } + my %cd = date_str($co{'committer_epoch'}); + open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next; + my @difftree = map { chomp; $_ } <$fd>; + close $fd or next; + print "\n" . + "" . + sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) . + "\n" . + "" . esc_html($co{'author'}) . "\n" . + "$cd{'rfc2822'}\n" . + "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . + "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . + "" . esc_html($co{'title'}) . "\n" . + "" . + "\n"; + } + print "
\n"; + foreach my $line (@difftree) { + if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { + next; + } + my $file = validate_input(unquote($7)); + $file = decode("utf8", $file, Encode::FB_DEFAULT); + print "$file
\n"; + } + print "]]>\n" . + "
\n" . + "
\n"; + } + print "
"; +} + +sub git_opml { + my @list = git_read_projects(); + + print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); + print "\n". + "\n". + "". + " $site_name Git OPML Export\n". + "\n". + "\n". + "\n"; + + foreach my $pr (@list) { + my %proj = %$pr; + my $head = git_read_head($proj{'path'}); + if (!defined $head) { + next; + } + $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}"; + my %co = git_read_commit($head); + if (!%co) { + next; + } + + my $path = esc_html(chop_str($proj{'path'}, 25, 5)); + my $rss = "$my_url?p=$proj{'path'};a=rss"; + my $html = "$my_url?p=$proj{'path'};a=summary"; + print "\n"; + } + print "\n". + "\n". + "\n"; +} -- cgit v1.2.1 From 8e85cdc4efd15fa70dfbbfebaf3ef9a758c2772c Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Mon, 31 Jul 2006 23:46:25 +0200 Subject: gitweb: use a hash to lookup the sub for an action Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 81 +++++++++++++++++-------------------------------------- 1 file changed, 25 insertions(+), 56 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 16c368155..4e79390af 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -161,65 +161,34 @@ if (defined $searchtext) { } # dispatch -if (!defined $action || $action eq "summary") { - git_summary(); - exit; -} elsif ($action eq "heads") { - git_heads(); - exit; -} elsif ($action eq "tags") { - git_tags(); - exit; -} elsif ($action eq "blob") { - git_blob(); - exit; -} elsif ($action eq "blob_plain") { - git_blob_plain(); - exit; -} elsif ($action eq "tree") { - git_tree(); - exit; -} elsif ($action eq "rss") { - git_rss(); - exit; -} elsif ($action eq "commit") { - git_commit(); - exit; -} elsif ($action eq "log") { - git_log(); - exit; -} elsif ($action eq "blobdiff") { - git_blobdiff(); - exit; -} elsif ($action eq "blobdiff_plain") { - git_blobdiff_plain(); - exit; -} elsif ($action eq "commitdiff") { - git_commitdiff(); - exit; -} elsif ($action eq "commitdiff_plain") { - git_commitdiff_plain(); - exit; -} elsif ($action eq "history") { - git_history(); - exit; -} elsif ($action eq "search") { - git_search(); - exit; -} elsif ($action eq "shortlog") { - git_shortlog(); - exit; -} elsif ($action eq "tag") { - git_tag(); - exit; -} elsif ($action eq "blame") { - git_blame2(); - exit; -} else { +my %actions = ( + "blame" => \&git_blame2, + "blobdiff" => \&git_blobdiff, + "blobdiff_plain" => \&git_blobdiff_plain, + "blob" => \&git_blob, + "blob_plain" => \&git_blob_plain, + "commitdiff" => \&git_commitdiff, + "commitdiff_plain" => \&git_commitdiff_plain, + "commit" => \&git_commit, + "heads" => \&git_heads, + "history" => \&git_history, + "log" => \&git_log, + "rss" => \&git_rss, + "search" => \&git_search, + "shortlog" => \&git_shortlog, + "summary" => \&git_summary, + "tag" => \&git_tag, + "tags" => \&git_tags, + "tree" => \&git_tree, +); + +$action = 'summary' if (!defined($action)); +if (!defined($actions{$action})) { undef $action; die_error(undef, "Unknown action."); - exit; } +$actions{$action}->(); +exit; ## ====================================================================== ## validation, quoting/unquoting and escaping -- cgit v1.2.1 From 7a13b999a5aa6de236c7b16139c765eb9e5cb3c4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 31 Jul 2006 19:18:34 -0700 Subject: gitweb: There can be more than two levels of subdirectories Earlier code to read .git/refs/{tags,heads} hierarchy had a hardcoded up-to-two-level assumption. Lift it by using File::Find. Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 4e79390af..9569af09b 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -14,6 +14,7 @@ use CGI::Util qw(unescape); use CGI::Carp qw(fatalsToBrowser); use Encode; use Fcntl ':mode'; +use File::Find qw(); binmode STDOUT, ':utf8'; our $cgi = new CGI; @@ -697,23 +698,14 @@ sub git_read_refs { my @reflist; my @refs; - opendir my $dh, "$projectroot/$project/$ref_dir"; - while (my $dir = readdir($dh)) { - if ($dir =~ m/^\./) { - next; - } - if (-d "$projectroot/$project/$ref_dir/$dir") { - opendir my $dh2, "$projectroot/$project/$ref_dir/$dir"; - my @subdirs = grep !m/^\./, readdir $dh2; - closedir($dh2); - foreach my $subdir (@subdirs) { - push @refs, "$dir/$subdir" - } - next; + my $pfxlen = length("$projectroot/$project/$ref_dir"); + File::Find::find(sub { + return if (/^\./); + if (-f $_) { + push @refs, substr($File::Find::name, $pfxlen + 1); } - push @refs, $dir; - } - closedir($dh); + }, "$projectroot/$project/$ref_dir"); + foreach my $ref_file (@refs) { my $ref_id = git_read_hash("$project/$ref_dir/$ref_file"); my $type = git_get_type($ref_id) || next; -- cgit v1.2.1 From b77aeb249a0ebdb8daa85ba58bbecde537f84025 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 31 Jul 2006 19:12:18 -0700 Subject: gitweb: an obvious cut and paste error. Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 9569af09b..902b96a91 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1433,7 +1433,7 @@ sub git_summary { my $headlist = git_read_refs("refs/heads"); if (defined @$headlist) { git_header_div('heads'); - git_heads_body($taglist, $head, 0, 15, + git_heads_body($headlist, $head, 0, 15, $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...")); } -- cgit v1.2.1 From 46b059d718777ab146b8089afd3d2fcdd6000154 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 31 Jul 2006 19:24:37 -0700 Subject: gitweb: fix use of uninitialized value. Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 902b96a91..f65b5d5ce 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -779,9 +779,11 @@ sub mimetype_guess_file { open(MIME, $mimemap) or return undef; while () { my ($mime, $exts) = split(/\t+/); - my @exts = split(/\s+/, $exts); - foreach my $ext (@exts) { - $mimemap{$ext} = $mime; + if (defined $exts) { + my @exts = split(/\s+/, $exts); + foreach my $ext (@exts) { + $mimemap{$ext} = $mime; + } } } close(MIME); -- cgit v1.2.1 From 498934a7e5410b815a9089c1b5205271d73e0f11 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 31 Jul 2006 19:36:50 -0700 Subject: gitweb: when showing history of a tree, show tree link not blob Otherwise clicking on that erroneous blob link would produce nonsense. Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index f65b5d5ce..3cd4ce2a0 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -2303,7 +2303,7 @@ sub git_history { "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob"); + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$ftype;hb=$commit;f=$file_name")}, $ftype); my $blob = git_get_hash_by_path($hash_base, $file_name); my $blob_parent = git_get_hash_by_path($commit, $file_name); if (defined $blob && defined $blob_parent && $blob ne $blob_parent) { -- cgit v1.2.1 From 5d1acf4da0d22d73c6b5d70887003d8fa7642ecc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 31 Jul 2006 21:00:16 -0700 Subject: gitweb: avoid undefined value warning in print_page_path The function unconditionally tried to match $type but some callers did not pass it. Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 3cd4ce2a0..9dc300d45 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -1035,7 +1035,7 @@ sub git_print_page_path { if (!defined $name) { print "
/
\n"; - } elsif ($type =~ "blob") { + } elsif (defined $type && $type eq 'blob') { print "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "
\n"; } else { @@ -1550,7 +1550,7 @@ sub git_blame { " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name); + git_print_page_path($file_name, 'blob'); print "
\n"; print < @@ -1773,7 +1773,7 @@ sub git_tree { if (defined $file_name) { $base = esc_html("$file_name/"); } - git_print_page_path($file_name); + git_print_page_path($file_name, 'tree'); print "
\n"; print "\n"; my $alternate = 0; -- cgit v1.2.1 From 6326b60cfd9c953e64ddab9659587afeedccd95b Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 1 Aug 2006 02:59:12 +0200 Subject: gitweb: Refactoring git_project_list Slightly reworking git_project_list, including moving setting $order, as it is used only in this action. Mostly reindent. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.cgi | 57 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 9dc300d45..9448b7279 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -81,14 +81,6 @@ if (defined $action) { } } -our $order = $cgi->param('o'); -if (defined $order) { - if ($order =~ m/[^0-9a-zA-Z_]/) { - undef $order; - die_error(undef, "Invalid order parameter."); - } -} - our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); if (defined $project) { $project =~ s|^/||; $project =~ s|/$||; @@ -1297,10 +1289,15 @@ sub git_logo { } sub git_project_list { + my $order = $cgi->param('o'); + if (defined $order && $order !~ m/project|descr|owner|age/) { + die_error(undef, "Invalid order parameter '$order'."); + } + my @list = git_read_projects(); my @projects; if (!@list) { - die_error(undef, "No project found."); + die_error(undef, "No projects found."); } foreach my $pr (@list) { my $head = git_read_head($pr->{'path'}); @@ -1322,6 +1319,7 @@ sub git_project_list { } push @projects, $pr; } + git_header_html(); if (-f $home_text) { print "
\n"; @@ -1332,29 +1330,42 @@ sub git_project_list { } print "
\n" . "\n"; - if (!defined($order) || (defined($order) && ($order eq "project"))) { + $order ||= "project"; + if ($order eq "project") { @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; print "\n"; } else { - print "\n"; + print "\n"; } - if (defined($order) && ($order eq "descr")) { + if ($order eq "descr") { @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; print "\n"; } else { - print "\n"; + print "\n"; } - if (defined($order) && ($order eq "owner")) { + if ($order eq "owner") { @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; print "\n"; } else { - print "\n"; + print "\n"; } - if (defined($order) && ($order eq "age")) { + if ($order eq "age") { @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; print "\n"; } else { - print "\n"; + print "\n"; } print "\n" . "\n"; @@ -1366,14 +1377,16 @@ sub git_project_list { print "\n"; } $alternate ^= 1; - print "\n" . + print "\n" . "\n" . "\n"; - print "\n" . + print "\n" . "\n" . "\n"; } -- cgit v1.2.1 From 5d043a3d856bd40d8b34b8836a561e438d23573b Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Tue, 1 Aug 2006 21:34:08 +0200 Subject: gitweb: fill in gitweb configuration by Makefile Generate gitweb/gitweb.cgi to reduce the need to patch gitweb.cgi by the end user. The GIT installation directory is already known by the Makefile, and can be inserted directly into gitweb. All other gitweb configuration parameters can now be specified by providing GITWEB_* variables while building GIT. These are described in gitweb/README. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- Makefile | 25 +- gitweb/README | 17 + gitweb/gitweb.cgi | 2598 ---------------------------------------------------- gitweb/gitweb.perl | 2597 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2637 insertions(+), 2600 deletions(-) delete mode 100755 gitweb/gitweb.cgi create mode 100755 gitweb/gitweb.perl diff --git a/Makefile b/Makefile index 15864e23b..fb2b28868 100644 --- a/Makefile +++ b/Makefile @@ -116,6 +116,13 @@ template_dir = $(prefix)/share/git-core/templates/ GIT_PYTHON_DIR = $(prefix)/share/git-core/python # DESTDIR= +# default configuration for gitweb +GITWEB_SITENAME = +GITWEB_PROJECTROOT = /pub/git +GITWEB_LIST = +GITWEB_HOMETEXT = indextext.html +GITWEB_CSS = gitweb.css + export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR CC = gcc @@ -514,7 +521,7 @@ LIB_OBJS += $(COMPAT_OBJS) export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir ### Build rules -all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk +all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi all: $(MAKE) -C templates @@ -572,6 +579,20 @@ git-status: git-commit cp $< $@+ mv $@+ $@ +gitweb/gitweb.cgi: gitweb/gitweb.perl + rm -f $@ $@+ + sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ + -e 's|@@GIT_VERSION@@|$(GIT_VERSION)|g' \ + -e 's|@@GIT_BINDIR@@|$(bindir)|g' \ + -e 's|@@GITWEB_SITENAME@@|$(GITWEB_SITENAME)|g' \ + -e 's|@@GITWEB_PROJECTROOT@@|$(GITWEB_PROJECTROOT)|g' \ + -e 's|@@GITWEB_LIST@@|$(GITWEB_LIST)|g' \ + -e 's|@@GITWEB_HOMETEXT@@|$(GITWEB_HOMETEXT)|g' \ + -e 's|@@GITWEB_CSS@@|$(GITWEB_CSS)|g' \ + $< >$@+ + chmod +x $@+ + mv $@+ $@ + git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css rm -f $@ $@+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ @@ -582,7 +603,7 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css -e '/@@GITWEB_CGI@@/d' \ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ -e '/@@GITWEB_CSS@@/d' \ - $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+ + $@.sh > $@+ chmod +x $@+ mv $@+ $@ diff --git a/gitweb/README b/gitweb/README index 8d672762e..ed939e2fb 100644 --- a/gitweb/README +++ b/gitweb/README @@ -5,5 +5,22 @@ The one working on: From the git version 1.4.0 gitweb is bundled with git. + +How to configure gitweb for your local system: + +You can specify the following configuration variables when building GIT: + * GITWEB_SITENAME + Shown in the title of all generated pages, defaults to the servers name. + * GITWEB_PROJECTROOT + The root directory for all projects shown by gitweb. + * GITWEB_LIST + points to a directory to scan for projects (defaults to project root) + or to a file for explicit listing of projects. + * GITWEB_HOMETEXT + points to an .html file which is included on the gitweb project + overview page. + * GITWEB_CSS + Points to the location where you put gitweb.css on your web server. + Any comment/question/concern to: Kay Sievers diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi deleted file mode 100755 index 9448b7279..000000000 --- a/gitweb/gitweb.cgi +++ /dev/null @@ -1,2598 +0,0 @@ -#!/usr/bin/perl - -# gitweb - simple web interface to track changes in git repositories -# -# (C) 2005-2006, Kay Sievers -# (C) 2005, Christian Gierke -# -# This program is licensed under the GPLv2 - -use strict; -use warnings; -use CGI qw(:standard :escapeHTML -nosticky); -use CGI::Util qw(unescape); -use CGI::Carp qw(fatalsToBrowser); -use Encode; -use Fcntl ':mode'; -use File::Find qw(); -binmode STDOUT, ':utf8'; - -our $cgi = new CGI; -our $version = "267"; -our $my_url = $cgi->url(); -our $my_uri = $cgi->url(-absolute => 1); -our $rss_link = ""; - -# core git executable to use -# this can just be "git" if your webserver has a sensible PATH -our $GIT = "/usr/bin/git"; - -# absolute fs-path which will be prepended to the project path -#our $projectroot = "/pub/scm"; -our $projectroot = "/home/kay/public_html/pub/scm"; - -# version of the core git binary -our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; - -# location for temporary files needed for diffs -our $git_temp = "/tmp/gitweb"; -if (! -d $git_temp) { - mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); -} - -# target of the home link on top of all pages -our $home_link = $my_uri; - -# name of your site or organization to appear in page titles -# replace this with something more descriptive for clearer bookmarks -our $site_name = $ENV{'SERVER_NAME'} || "Untitled"; - -# html text to include at home page -our $home_text = "indextext.html"; - -# URI of default stylesheet -our $stylesheet = "gitweb.css"; - -# source of projects list -#our $projects_list = $projectroot; -our $projects_list = "index/index.aux"; - -# default blob_plain mimetype and default charset for text/plain blob -our $default_blob_plain_mimetype = 'text/plain'; -our $default_text_plain_charset = undef; - -# file to use for guessing MIME types before trying /etc/mime.types -# (relative to the current git repository) -our $mimetypes_file = undef; - -# input validation and dispatch -our $action = $cgi->param('a'); -if (defined $action) { - if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { - undef $action; - die_error(undef, "Invalid action parameter."); - } - if ($action eq "git-logo.png") { - git_logo(); - exit; - } elsif ($action eq "opml") { - git_opml(); - exit; - } -} - -our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); -if (defined $project) { - $project =~ s|^/||; $project =~ s|/$||; - $project = validate_input($project); - if (!defined($project)) { - die_error(undef, "Invalid project parameter."); - } - if (!(-d "$projectroot/$project")) { - undef $project; - die_error(undef, "No such directory."); - } - if (!(-e "$projectroot/$project/HEAD")) { - undef $project; - die_error(undef, "No such project."); - } - $rss_link = ""; - $ENV{'GIT_DIR'} = "$projectroot/$project"; -} else { - git_project_list(); - exit; -} - -our $file_name = $cgi->param('f'); -if (defined $file_name) { - $file_name = validate_input($file_name); - if (!defined($file_name)) { - die_error(undef, "Invalid file parameter."); - } -} - -our $hash = $cgi->param('h'); -if (defined $hash) { - $hash = validate_input($hash); - if (!defined($hash)) { - die_error(undef, "Invalid hash parameter."); - } -} - -our $hash_parent = $cgi->param('hp'); -if (defined $hash_parent) { - $hash_parent = validate_input($hash_parent); - if (!defined($hash_parent)) { - die_error(undef, "Invalid hash parent parameter."); - } -} - -our $hash_base = $cgi->param('hb'); -if (defined $hash_base) { - $hash_base = validate_input($hash_base); - if (!defined($hash_base)) { - die_error(undef, "Invalid hash base parameter."); - } -} - -our $page = $cgi->param('pg'); -if (defined $page) { - if ($page =~ m/[^0-9]$/) { - undef $page; - die_error(undef, "Invalid page parameter."); - } -} - -our $searchtext = $cgi->param('s'); -if (defined $searchtext) { - if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) { - undef $searchtext; - die_error(undef, "Invalid search parameter."); - } - $searchtext = quotemeta $searchtext; -} - -# dispatch -my %actions = ( - "blame" => \&git_blame2, - "blobdiff" => \&git_blobdiff, - "blobdiff_plain" => \&git_blobdiff_plain, - "blob" => \&git_blob, - "blob_plain" => \&git_blob_plain, - "commitdiff" => \&git_commitdiff, - "commitdiff_plain" => \&git_commitdiff_plain, - "commit" => \&git_commit, - "heads" => \&git_heads, - "history" => \&git_history, - "log" => \&git_log, - "rss" => \&git_rss, - "search" => \&git_search, - "shortlog" => \&git_shortlog, - "summary" => \&git_summary, - "tag" => \&git_tag, - "tags" => \&git_tags, - "tree" => \&git_tree, -); - -$action = 'summary' if (!defined($action)); -if (!defined($actions{$action})) { - undef $action; - die_error(undef, "Unknown action."); -} -$actions{$action}->(); -exit; - -## ====================================================================== -## validation, quoting/unquoting and escaping - -sub validate_input { - my $input = shift; - - if ($input =~ m/^[0-9a-fA-F]{40}$/) { - return $input; - } - if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) { - return undef; - } - if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) { - return undef; - } - return $input; -} - -# quote unsafe chars, but keep the slash, even when it's not -# correct, but quoted slashes look too horrible in bookmarks -sub esc_param { - my $str = shift; - $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg; - $str =~ s/\+/%2B/g; - $str =~ s/ /\+/g; - return $str; -} - -# replace invalid utf8 character with SUBSTITUTION sequence -sub esc_html { - my $str = shift; - $str = decode("utf8", $str, Encode::FB_DEFAULT); - $str = escapeHTML($str); - $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) - return $str; -} - -# git may return quoted and escaped filenames -sub unquote { - my $str = shift; - if ($str =~ m/^"(.*)"$/) { - $str = $1; - $str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg; - } - return $str; -} - -## ---------------------------------------------------------------------- -## HTML aware string manipulation - -sub chop_str { - my $str = shift; - my $len = shift; - my $add_len = shift || 10; - - # allow only $len chars, but don't cut a word if it would fit in $add_len - # if it doesn't fit, cut it if it's still longer than the dots we would add - $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/; - my $body = $1; - my $tail = $2; - if (length($tail) > 4) { - $tail = " ..."; - $body =~ s/&[^;]*$//; # remove chopped character entities - } - return "$body$tail"; -} - -## ---------------------------------------------------------------------- -## functions returning short strings - -# CSS class for given age value (in seconds) -sub age_class { - my $age = shift; - - if ($age < 60*60*2) { - return "age0"; - } elsif ($age < 60*60*24*2) { - return "age1"; - } else { - return "age2"; - } -} - -# convert age in seconds to "nn units ago" string -sub age_string { - my $age = shift; - my $age_str; - - if ($age > 60*60*24*365*2) { - $age_str = (int $age/60/60/24/365); - $age_str .= " years ago"; - } elsif ($age > 60*60*24*(365/12)*2) { - $age_str = int $age/60/60/24/(365/12); - $age_str .= " months ago"; - } elsif ($age > 60*60*24*7*2) { - $age_str = int $age/60/60/24/7; - $age_str .= " weeks ago"; - } elsif ($age > 60*60*24*2) { - $age_str = int $age/60/60/24; - $age_str .= " days ago"; - } elsif ($age > 60*60*2) { - $age_str = int $age/60/60; - $age_str .= " hours ago"; - } elsif ($age > 60*2) { - $age_str = int $age/60; - $age_str .= " min ago"; - } elsif ($age > 2) { - $age_str = int $age; - $age_str .= " sec ago"; - } else { - $age_str .= " right now"; - } - return $age_str; -} - -# convert file mode in octal to symbolic file mode string -sub mode_str { - my $mode = oct shift; - - if (S_ISDIR($mode & S_IFMT)) { - return 'drwxr-xr-x'; - } elsif (S_ISLNK($mode)) { - return 'lrwxrwxrwx'; - } elsif (S_ISREG($mode)) { - # git cares only about the executable bit - if ($mode & S_IXUSR) { - return '-rwxr-xr-x'; - } else { - return '-rw-r--r--'; - }; - } else { - return '----------'; - } -} - -# convert file mode in octal to file type string -sub file_type { - my $mode = oct shift; - - if (S_ISDIR($mode & S_IFMT)) { - return "directory"; - } elsif (S_ISLNK($mode)) { - return "symlink"; - } elsif (S_ISREG($mode)) { - return "file"; - } else { - return "unknown"; - } -} - -## ---------------------------------------------------------------------- -## functions returning short HTML fragments, or transforming HTML fragments -## which don't beling to other sections - -# format line of commit message or tag comment -sub format_log_line_html { - my $line = shift; - - $line = esc_html($line); - $line =~ s/ / /g; - if ($line =~ m/([0-9a-fA-F]{40})/) { - my $hash_text = $1; - if (git_get_type($hash_text) eq "commit") { - my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text); - $line =~ s/$hash_text/$link/; - } - } - return $line; -} - -# format marker of refs pointing to given object -sub git_get_referencing { - my ($refs, $id) = @_; - - if (defined $refs->{$id}) { - return ' ' . esc_html($refs->{$id}) . ''; - } else { - return ""; - } -} - -## ---------------------------------------------------------------------- -## git utility subroutines, invoking git commands - -# get HEAD ref of given project as hash -sub git_read_head { - my $project = shift; - my $oENV = $ENV{'GIT_DIR'}; - my $retval = undef; - $ENV{'GIT_DIR'} = "$projectroot/$project"; - if (open my $fd, "-|", $GIT, "rev-parse", "--verify", "HEAD") { - my $head = <$fd>; - close $fd; - if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) { - $retval = $1; - } - } - if (defined $oENV) { - $ENV{'GIT_DIR'} = $oENV; - } - return $retval; -} - -# get type of given object -sub git_get_type { - my $hash = shift; - - open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return; - my $type = <$fd>; - close $fd or return; - chomp $type; - return $type; -} - -sub git_get_project_config { - my $key = shift; - - return unless ($key); - $key =~ s/^gitweb\.//; - return if ($key =~ m/\W/); - - my $val = qx($GIT repo-config --get gitweb.$key); - return ($val); -} - -sub git_get_project_config_bool { - my $val = git_get_project_config (@_); - if ($val and $val =~ m/true|yes|on/) { - return (1); - } - return; # implicit false -} - -# get hash of given path at given ref -sub git_get_hash_by_path { - my $base = shift; - my $path = shift || return undef; - - my $tree = $base; - - open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path - or die_error(undef, "Open git-ls-tree failed."); - my $line = <$fd>; - close $fd or return undef; - - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; - return $3; -} - -## ...................................................................... -## git utility functions, directly accessing git repository - -# assumes that PATH is not symref -sub git_read_hash { - my $path = shift; - - open my $fd, "$projectroot/$path" or return undef; - my $head = <$fd>; - close $fd; - chomp $head; - if ($head =~ m/^[0-9a-fA-F]{40}$/) { - return $head; - } -} - -sub git_read_description { - my $path = shift; - - open my $fd, "$projectroot/$path/description" or return undef; - my $descr = <$fd>; - close $fd; - chomp $descr; - return $descr; -} - -sub git_read_projects { - my @list; - - if (-d $projects_list) { - # search in directory - my $dir = $projects_list; - opendir my ($dh), $dir or return undef; - while (my $dir = readdir($dh)) { - if (-e "$projectroot/$dir/HEAD") { - my $pr = { - path => $dir, - }; - push @list, $pr - } - } - closedir($dh); - } elsif (-f $projects_list) { - # read from file(url-encoded): - # 'git%2Fgit.git Linus+Torvalds' - # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' - # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' - open my ($fd), $projects_list or return undef; - while (my $line = <$fd>) { - chomp $line; - my ($path, $owner) = split ' ', $line; - $path = unescape($path); - $owner = unescape($owner); - if (!defined $path) { - next; - } - if (-e "$projectroot/$path/HEAD") { - my $pr = { - path => $path, - owner => decode("utf8", $owner, Encode::FB_DEFAULT), - }; - push @list, $pr - } - } - close $fd; - } - @list = sort {$a->{'path'} cmp $b->{'path'}} @list; - return @list; -} - -sub read_info_ref { - my $type = shift || ""; - my %refs; - # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 - # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} - open my $fd, "$projectroot/$project/info/refs" or return; - while (my $line = <$fd>) { - chomp $line; - # attention: for $type == "" it saves only last path part of ref name - # e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb' - if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { - if (defined $refs{$1}) { - $refs{$1} .= " / $2"; - } else { - $refs{$1} = $2; - } - } - } - close $fd or return; - return \%refs; -} - -## ---------------------------------------------------------------------- -## parse to hash functions - -sub date_str { - my $epoch = shift; - my $tz = shift || "-0000"; - - my %date; - my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); - my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); - my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch); - $date{'hour'} = $hour; - $date{'minute'} = $min; - $date{'mday'} = $mday; - $date{'day'} = $days[$wday]; - $date{'month'} = $months[$mon]; - $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; - $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; - - $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; - my $local = $epoch + ((int $1 + ($2/60)) * 3600); - ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); - $date{'hour_local'} = $hour; - $date{'minute_local'} = $min; - $date{'tz_local'} = $tz; - return %date; -} - -sub git_read_tag { - my $tag_id = shift; - my %tag; - my @comment; - - open my $fd, "-|", $GIT, "cat-file", "tag", $tag_id or return; - $tag{'id'} = $tag_id; - while (my $line = <$fd>) { - chomp $line; - if ($line =~ m/^object ([0-9a-fA-F]{40})$/) { - $tag{'object'} = $1; - } elsif ($line =~ m/^type (.+)$/) { - $tag{'type'} = $1; - } elsif ($line =~ m/^tag (.+)$/) { - $tag{'name'} = $1; - } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) { - $tag{'author'} = $1; - $tag{'epoch'} = $2; - $tag{'tz'} = $3; - } elsif ($line =~ m/--BEGIN/) { - push @comment, $line; - last; - } elsif ($line eq "") { - last; - } - } - push @comment, <$fd>; - $tag{'comment'} = \@comment; - close $fd or return; - if (!defined $tag{'name'}) { - return - }; - return %tag -} - -sub git_read_commit { - my $commit_id = shift; - my $commit_text = shift; - - my @commit_lines; - my %co; - - if (defined $commit_text) { - @commit_lines = @$commit_text; - } else { - $/ = "\0"; - open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return; - @commit_lines = split '\n', <$fd>; - close $fd or return; - $/ = "\n"; - pop @commit_lines; - } - my $header = shift @commit_lines; - if (!($header =~ m/^[0-9a-fA-F]{40}/)) { - return; - } - ($co{'id'}, my @parents) = split ' ', $header; - $co{'parents'} = \@parents; - $co{'parent'} = $parents[0]; - 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/^author (.*) ([0-9]+) (.*)$/) { - $co{'author'} = $1; - $co{'author_epoch'} = $2; - $co{'author_tz'} = $3; - if ($co{'author'} =~ m/^([^<]+) 50) { - $title =~ s/^Automatic //; - $title =~ s/^merge (of|with) /Merge ... /i; - if (length($title) > 50) { - $title =~ s/(http|rsync):\/\///; - } - if (length($title) > 50) { - $title =~ s/(master|www|rsync)\.//; - } - if (length($title) > 50) { - $title =~ s/kernel.org:?//; - } - if (length($title) > 50) { - $title =~ s/\/pub\/scm//; - } - } - $co{'title_short'} = chop_str($title, 50, 5); - last; - } - } - # remove added spaces - foreach my $line (@commit_lines) { - $line =~ s/^ //; - } - $co{'comment'} = \@commit_lines; - - my $age = time - $co{'committer_epoch'}; - $co{'age'} = $age; - $co{'age_string'} = age_string($age); - my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'}); - if ($age > 60*60*24*7*2) { - $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; - $co{'age_string_age'} = $co{'age_string'}; - } else { - $co{'age_string_date'} = $co{'age_string'}; - $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; - } - return %co; -} - -## ...................................................................... -## parse to array of hashes functions - -sub git_read_refs { - my $ref_dir = shift; - my @reflist; - - my @refs; - my $pfxlen = length("$projectroot/$project/$ref_dir"); - File::Find::find(sub { - return if (/^\./); - if (-f $_) { - push @refs, substr($File::Find::name, $pfxlen + 1); - } - }, "$projectroot/$project/$ref_dir"); - - foreach my $ref_file (@refs) { - my $ref_id = git_read_hash("$project/$ref_dir/$ref_file"); - my $type = git_get_type($ref_id) || next; - my %ref_item; - my %co; - $ref_item{'type'} = $type; - $ref_item{'id'} = $ref_id; - $ref_item{'epoch'} = 0; - $ref_item{'age'} = "unknown"; - if ($type eq "tag") { - my %tag = git_read_tag($ref_id); - $ref_item{'comment'} = $tag{'comment'}; - if ($tag{'type'} eq "commit") { - %co = git_read_commit($tag{'object'}); - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } elsif (defined($tag{'epoch'})) { - my $age = time - $tag{'epoch'}; - $ref_item{'epoch'} = $tag{'epoch'}; - $ref_item{'age'} = age_string($age); - } - $ref_item{'reftype'} = $tag{'type'}; - $ref_item{'name'} = $tag{'name'}; - $ref_item{'refid'} = $tag{'object'}; - } elsif ($type eq "commit"){ - %co = git_read_commit($ref_id); - $ref_item{'reftype'} = "commit"; - $ref_item{'name'} = $ref_file; - $ref_item{'title'} = $co{'title'}; - $ref_item{'refid'} = $ref_id; - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } else { - $ref_item{'reftype'} = $type; - $ref_item{'name'} = $ref_file; - $ref_item{'refid'} = $ref_id; - } - - push @reflist, \%ref_item; - } - # sort tags by age - @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; - return \@reflist; -} - -## ---------------------------------------------------------------------- -## filesystem-related functions - -sub get_file_owner { - my $path = shift; - - my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path); - my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid); - if (!defined $gcos) { - return undef; - } - my $owner = $gcos; - $owner =~ s/[,;].*$//; - return decode("utf8", $owner, Encode::FB_DEFAULT); -} - -## ...................................................................... -## mimetype related functions - -sub mimetype_guess_file { - my $filename = shift; - my $mimemap = shift; - -r $mimemap or return undef; - - my %mimemap; - open(MIME, $mimemap) or return undef; - while () { - my ($mime, $exts) = split(/\t+/); - if (defined $exts) { - my @exts = split(/\s+/, $exts); - foreach my $ext (@exts) { - $mimemap{$ext} = $mime; - } - } - } - close(MIME); - - $filename =~ /\.(.*?)$/; - return $mimemap{$1}; -} - -sub mimetype_guess { - my $filename = shift; - my $mime; - $filename =~ /\./ or return undef; - - if ($mimetypes_file) { - my $file = $mimetypes_file; - #$file =~ m#^/# or $file = "$projectroot/$path/$file"; - $mime = mimetype_guess_file($filename, $file); - } - $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); - return $mime; -} - -sub git_blob_plain_mimetype { - my $fd = shift; - my $filename = shift; - - if ($filename) { - my $mime = mimetype_guess($filename); - $mime and return $mime; - } - - # just in case - return $default_blob_plain_mimetype unless $fd; - - if (-T $fd) { - return 'text/plain' . - ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); - } elsif (! $filename) { - return 'application/octet-stream'; - } elsif ($filename =~ m/\.png$/i) { - return 'image/png'; - } elsif ($filename =~ m/\.gif$/i) { - return 'image/gif'; - } elsif ($filename =~ m/\.jpe?g$/i) { - return 'image/jpeg'; - } else { - return 'application/octet-stream'; - } -} - -## ====================================================================== -## functions printing HTML: header, footer, error page - -sub git_header_html { - my $status = shift || "200 OK"; - my $expires = shift; - - my $title = "$site_name git"; - if (defined $project) { - $title .= " - $project"; - if (defined $action) { - $title .= "/$action"; - if (defined $file_name) { - $title .= " - $file_name"; - if ($action eq "tree" && $file_name !~ m|/$|) { - $title .= "/"; - } - } - } - } - my $content_type; - # require explicit support from the UA if we are to send the page as - # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. - # we have to do this because MSIE sometimes globs '*/*', pretending to - # support xhtml+xml but choking when it gets what it asked for. - if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { - $content_type = 'application/xhtml+xml'; - } else { - $content_type = 'text/html'; - } - print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); - print < - - - - - - - -$title - -$rss_link - - -EOF - print "
\n" . - "" . - "\"git\"" . - "\n"; - print $cgi->a({-href => esc_param($home_link)}, "projects") . " / "; - if (defined $project) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project)); - if (defined $action) { - print " / $action"; - } - print "\n"; - if (!defined $searchtext) { - $searchtext = ""; - } - my $search_hash; - if (defined $hash_base) { - $search_hash = $hash_base; - } elsif (defined $hash) { - $search_hash = $hash; - } else { - $search_hash = "HEAD"; - } - $cgi->param("a", "search"); - $cgi->param("h", $search_hash); - print $cgi->startform(-method => "get", -action => $my_uri) . - "
\n" . - $cgi->hidden(-name => "p") . "\n" . - $cgi->hidden(-name => "a") . "\n" . - $cgi->hidden(-name => "h") . "\n" . - $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . - "
" . - $cgi->end_form() . "\n"; - } - print "
\n"; -} - -sub git_footer_html { - print "
\n"; - if (defined $project) { - my $descr = git_read_description($project); - if (defined $descr) { - print "\n"; - } - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n"; - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n"; - } - print "
\n" . - "\n" . - ""; -} - -sub die_error { - my $status = shift || "403 Forbidden"; - my $error = shift || "Malformed query, file missing or permission denied"; - - git_header_html($status); - print "
\n" . - "

\n" . - "$status - $error\n" . - "
\n" . - "
\n"; - git_footer_html(); - exit; -} - -## ---------------------------------------------------------------------- -## functions printing or outputting HTML: navigation - -sub git_page_nav { - my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; - $extra = '' if !defined $extra; # pager or formats - - my @navs = qw(summary shortlog log commit commitdiff tree); - if ($suppress) { - @navs = grep { $_ ne $suppress } @navs; - } - - my %arg = map { $_, ''} @navs; - if (defined $head) { - for (qw(commit commitdiff)) { - $arg{$_} = ";h=$head"; - } - if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { - for (qw(shortlog log)) { - $arg{$_} = ";h=$head"; - } - } - } - $arg{tree} .= ";h=$treehead" if defined $treehead; - $arg{tree} .= ";hb=$treebase" if defined $treebase; - - print "
\n" . - (join " | ", - map { $_ eq $current - ? $_ - : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") - } - @navs); - print "
\n$extra
\n" . - "
\n"; -} - -sub git_get_paging_nav { - my ($action, $hash, $head, $page, $nrevs) = @_; - my $paging_nav; - - - if ($hash ne $head || $page) { - $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action")}, "HEAD"); - } else { - $paging_nav .= "HEAD"; - } - - if ($page > 0) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)), - -accesskey => "p", -title => "Alt-p"}, "prev"); - } else { - $paging_nav .= " ⋅ prev"; - } - - if ($nrevs >= (100 * ($page+1)-1)) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } - - return $paging_nav; -} - -## ...................................................................... -## functions printing or outputting HTML: div - -sub git_header_div { - my ($action, $title, $hash, $hash_base) = @_; - my $rest = ''; - - $rest .= ";h=$hash" if $hash; - $rest .= ";hb=$hash_base" if $hash_base; - - print "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action$rest"), - -class => "title"}, $title ? $title : $action) . "\n" . - "
\n"; -} - -sub git_print_page_path { - my $name = shift; - my $type = shift; - - if (!defined $name) { - print "
/
\n"; - } elsif (defined $type && $type eq 'blob') { - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "
\n"; - } else { - print "
" . esc_html($name) . "
\n"; - } -} - -## ...................................................................... -## functions printing large fragments of HTML - -sub git_shortlog_body { - # uses global variable $project - my ($revlist, $from, $to, $refs, $extra) = @_; - $from = 0 unless defined $from; - $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to); - - print "
Project" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=project")}, "Project") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("o=project"), + -class => "header"}, "Project") . + "Description" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=descr")}, "Description") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("o=descr"), + -class => "header"}, "Description") . + "Owner" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=owner")}, "Owner") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("o=owner"), + -class => "header"}, "Owner") . + "Last Change" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=age")}, "Last Change") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("o=age"), + -class => "header"}, "Last Change") . + "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), + -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "{'commit'}{'age'}) . "\">" . + $pr->{'commit'}{'age_string'} . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . "
\n"; - my $alternate = 0; - for (my $i = $from; $i <= $to; $i++) { - my $commit = $revlist->[$i]; - #my $ref = defined $refs ? git_get_referencing($refs, $commit) : ''; - my $ref = git_get_referencing($refs, $commit); - my %co = git_read_commit($commit); - my %ad = date_str($co{'author_epoch'}); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - # git_summary() used print "\n" . - print "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - } - if (defined $extra) { - print "\n" . - "\n" . - "\n"; - } - print "
$co{'age_string'}$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; - if (length($co{'title_short'}) < length($co{'title'})) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), - -class => "list", -title => "$co{'title'}"}, - "" . esc_html($co{'title_short'}) . "$ref"); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), - -class => "list"}, - "" . esc_html($co{'title'}) . "$ref"); - } - print "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - "
$extra
\n"; -} - -sub git_tags_body { - # uses global variable $project - my ($taglist, $from, $to, $extra) = @_; - $from = 0 unless defined $from; - $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); - - print "\n"; - my $alternate = 0; - for (my $i = $from; $i <= $to; $i++) { - my $entry = $taglist->[$i]; - my %tag = %$entry; - my $comment_lines = $tag{'comment'}; - my $comment = shift @$comment_lines; - my $comment_short; - if (defined $comment) { - $comment_short = chop_str($comment, 30, 5); - } - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - ""; - } - if (defined $extra) { - print "\n" . - "\n" . - "\n"; - } - print "
$tag{'age'}" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . - ""; - if (defined $comment) { - if (length($comment_short) < length($comment)) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), - -class => "list", -title => $comment}, $comment_short); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), - -class => "list"}, $comment); - } - } - print ""; - if ($tag{'type'} eq "tag") { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag"); - } else { - print " "; - } - print "" . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); - if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); - } elsif ($tag{'reftype'} eq "blob") { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw"); - } - print "
$extra
\n"; -} - -sub git_heads_body { - # uses global variable $project - my ($taglist, $head, $from, $to, $extra) = @_; - $from = 0 unless defined $from; - $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); - - print "\n"; - my $alternate = 0; - for (my $i = $from; $i <= $to; $i++) { - my $entry = $taglist->[$i]; - my %tag = %$entry; - my $curr = $tag{'id'} eq $head; - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - ($tag{'id'} eq $head ? "\n" . - "\n" . - ""; - } - if (defined $extra) { - print "\n" . - "\n" . - "\n"; - } - print "
$tag{'age'}" : "") . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . - "
$extra
\n"; -} - -## ---------------------------------------------------------------------- -## functions printing large fragments, format as one of arguments - -sub git_diff_print { - my $from = shift; - my $from_name = shift; - my $to = shift; - my $to_name = shift; - my $format = shift || "html"; - - my $from_tmp = "/dev/null"; - my $to_tmp = "/dev/null"; - my $pid = $$; - - # create tmp from-file - if (defined $from) { - $from_tmp = "$git_temp/gitweb_" . $$ . "_from"; - open my $fd2, "> $from_tmp"; - open my $fd, "-|", $GIT, "cat-file", "blob", $from; - my @file = <$fd>; - print $fd2 @file; - close $fd2; - close $fd; - } - - # create tmp to-file - if (defined $to) { - $to_tmp = "$git_temp/gitweb_" . $$ . "_to"; - open my $fd2, "> $to_tmp"; - open my $fd, "-|", $GIT, "cat-file", "blob", $to; - my @file = <$fd>; - print $fd2 @file; - close $fd2; - close $fd; - } - - open my $fd, "-|", "/usr/bin/diff -u -p -L \'$from_name\' -L \'$to_name\' $from_tmp $to_tmp"; - if ($format eq "plain") { - undef $/; - print <$fd>; - $/ = "\n"; - } else { - while (my $line = <$fd>) { - chomp $line; - my $char = substr($line, 0, 1); - my $diff_class = ""; - if ($char eq '+') { - $diff_class = " add"; - } elsif ($char eq "-") { - $diff_class = " rem"; - } elsif ($char eq "@") { - $diff_class = " chunk_header"; - } elsif ($char eq "\\") { - # skip errors - next; - } - while ((my $pos = index($line, "\t")) != -1) { - if (my $count = (8 - (($pos-1) % 8))) { - my $spaces = ' ' x $count; - $line =~ s/\t/$spaces/; - } - } - print "
" . esc_html($line) . "
\n"; - } - } - close $fd; - - if (defined $from) { - unlink($from_tmp); - } - if (defined $to) { - unlink($to_tmp); - } -} - - -## ====================================================================== -## ====================================================================== -## actions - -# git-logo (cached in browser for one day) -sub git_logo { - binmode STDOUT, ':raw'; - print $cgi->header(-type => 'image/png', -expires => '+1d'); - # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g' - print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" . - "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" . - "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" . - "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" . - "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" . - "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" . - "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" . - "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" . - "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" . - "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" . - "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" . - "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" . - "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"; -} - -sub git_project_list { - my $order = $cgi->param('o'); - if (defined $order && $order !~ m/project|descr|owner|age/) { - die_error(undef, "Invalid order parameter '$order'."); - } - - my @list = git_read_projects(); - my @projects; - if (!@list) { - die_error(undef, "No projects found."); - } - foreach my $pr (@list) { - my $head = git_read_head($pr->{'path'}); - if (!defined $head) { - next; - } - $ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}"; - my %co = git_read_commit($head); - if (!%co) { - next; - } - $pr->{'commit'} = \%co; - if (!defined $pr->{'descr'}) { - my $descr = git_read_description($pr->{'path'}) || ""; - $pr->{'descr'} = chop_str($descr, 25, 5); - } - if (!defined $pr->{'owner'}) { - $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || ""; - } - push @projects, $pr; - } - - git_header_html(); - if (-f $home_text) { - print "
\n"; - open (my $fd, $home_text); - print <$fd>; - close $fd; - print "
\n"; - } - print "\n" . - "\n"; - $order ||= "project"; - if ($order eq "project") { - @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "descr") { - @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "owner") { - @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; - print "\n"; - } else { - print "\n"; - } - if ($order eq "age") { - @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; - print "\n"; - } else { - print "\n"; - } - print "\n" . - "\n"; - my $alternate = 0; - foreach my $pr (@projects) { - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n"; - print "\n" . - "\n" . - "\n"; - } - print "
Project" . - $cgi->a({-href => "$my_uri?" . esc_param("o=project"), - -class => "header"}, "Project") . - "Description" . - $cgi->a({-href => "$my_uri?" . esc_param("o=descr"), - -class => "header"}, "Description") . - "Owner" . - $cgi->a({-href => "$my_uri?" . esc_param("o=owner"), - -class => "header"}, "Owner") . - "Last Change" . - $cgi->a({-href => "$my_uri?" . esc_param("o=age"), - -class => "header"}, "Last Change") . - "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), - -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . - $pr->{'commit'}{'age_string'} . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . - "
\n"; - git_footer_html(); -} - -sub git_summary { - my $descr = git_read_description($project) || "none"; - my $head = git_read_head($project); - my %co = git_read_commit($head); - my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); - - my $owner; - if (-f $projects_list) { - open (my $fd , $projects_list); - while (my $line = <$fd>) { - chomp $line; - my ($pr, $ow) = split ' ', $line; - $pr = unescape($pr); - $ow = unescape($ow); - if ($pr eq $project) { - $owner = decode("utf8", $ow, Encode::FB_DEFAULT); - last; - } - } - close $fd; - } - if (!defined $owner) { - $owner = get_file_owner("$projectroot/$project"); - } - - my $refs = read_info_ref(); - git_header_html(); - git_page_nav('summary','', $head); - - print "
 
\n"; - print "\n" . - "\n" . - "\n" . - "\n" . - "
description" . esc_html($descr) . "
owner$owner
last change$cd{'rfc2822'}
\n"; - - open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) - or die_error(undef, "Open git-rev-list failed."); - my @revlist = map { chomp; $_ } <$fd>; - close $fd; - git_header_div('shortlog'); - git_shortlog_body(\@revlist, 0, 15, $refs, - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...")); - - my $taglist = git_read_refs("refs/tags"); - if (defined @$taglist) { - git_header_div('tags'); - git_tags_body($taglist, 0, 15, - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...")); - } - - my $headlist = git_read_refs("refs/heads"); - if (defined @$headlist) { - git_header_div('heads'); - git_heads_body($headlist, $head, 0, 15, - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...")); - } - - git_footer_html(); -} - -sub git_tag { - my $head = git_read_head($project); - git_header_html(); - git_page_nav('','', $head,undef,$head); - my %tag = git_read_tag($hash); - git_header_div('commit', esc_html($tag{'name'}), $hash); - print "
\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - if (defined($tag{'author'})) { - my %ad = date_str($tag{'epoch'}, $tag{'tz'}); - print "\n"; - print "\n"; - } - print "
object" . $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'object'}) . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'type'}) . "
author" . esc_html($tag{'author'}) . "
" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "
\n\n" . - "
\n"; - print "
"; - my $comment = $tag{'comment'}; - foreach my $line (@$comment) { - print esc_html($line) . "
\n"; - } - print "
\n"; - git_footer_html(); -} - -sub git_blame2 { - my $fd; - my $ftype; - die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame')); - die_error('404 Not Found', "File name not defined") if (!$file_name); - $hash_base ||= git_read_head($project); - die_error(undef, "Reading commit failed") unless ($hash_base); - my %co = git_read_commit($hash_base) - or die_error(undef, "Reading commit failed"); - if (!defined $hash) { - $hash = git_get_hash_by_path($hash_base, $file_name, "blob") - or die_error(undef, "Error looking up file"); - } - $ftype = git_get_type($hash); - if ($ftype !~ "blob") { - die_error("400 Bad Request", "object is not a blob"); - } - open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) - or die_error(undef, "Open git-blame failed."); - git_header_html(); - my $formats_nav = - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); - git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - git_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, $ftype); - my @rev_color = (qw(light dark)); - my $num_colors = scalar(@rev_color); - my $current_color = 0; - my $last_rev; - print "
\n"; - print "\n"; - print "\n"; - while (<$fd>) { - /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/; - my $full_rev = $1; - my $rev = substr($full_rev, 0, 8); - my $lineno = $2; - my $data = $3; - - if (!defined $last_rev) { - $last_rev = $full_rev; - } elsif ($last_rev ne $full_rev) { - $last_rev = $full_rev; - $current_color = ++$current_color % $num_colors; - } - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - } - print "
CommitLineData
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "" . esc_html($lineno) . "" . esc_html($data) . "
\n"; - print "
"; - close $fd or print "Reading blob failed\n"; - git_footer_html(); -} - -sub git_blame { - my $fd; - die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame')); - die_error('404 Not Found', "What file will it be, master?") if (!$file_name); - $hash_base ||= git_read_head($project); - die_error(undef, "Reading commit failed.") unless ($hash_base); - my %co = git_read_commit($hash_base) - or die_error(undef, "Reading commit failed."); - if (!defined $hash) { - $hash = git_get_hash_by_path($hash_base, $file_name, "blob") - or die_error(undef, "Error lookup file."); - } - open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base) - or die_error(undef, "Open git-annotate failed."); - git_header_html(); - my $formats_nav = - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); - git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - git_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, 'blob'); - print "
\n"; - print < - - Commit - Age - Author - Line - Data - -HTML - my @line_class = (qw(light dark)); - my $line_class_len = scalar (@line_class); - my $line_class_num = $#line_class; - while (my $line = <$fd>) { - my $long_rev; - my $short_rev; - my $author; - my $time; - my $lineno; - my $data; - my $age; - my $age_str; - my $age_class; - - chomp $line; - $line_class_num = ($line_class_num + 1) % $line_class_len; - - if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) \+\d\d\d\d\t(\d+)\)(.*)$/) { - $long_rev = $1; - $author = $2; - $time = $3; - $lineno = $4; - $data = $5; - } else { - print qq( Unable to parse: $line\n); - next; - } - $short_rev = substr ($long_rev, 0, 8); - $age = time () - $time; - $age_str = age_string ($age); - $age_str =~ s/ / /g; - $age_class = age_class($age); - $author = esc_html ($author); - $author =~ s/ / /g; - # escape tabs - while ((my $pos = index($data, "\t")) != -1) { - if (my $count = (8 - ($pos % 8))) { - my $spaces = ' ' x $count; - $data =~ s/\t/$spaces/; - } - } - $data = esc_html ($data); - - print < - $short_rev.. - $age_str - $author - $lineno - $data - -HTML - } # while (my $line = <$fd>) - print "\n\n"; - close $fd or print "Reading blob failed.\n"; - print "
"; - git_footer_html(); -} - -sub git_tags { - my $head = git_read_head($project); - git_header_html(); - git_page_nav('','', $head,undef,$head); - git_header_div('summary', $project); - - my $taglist = git_read_refs("refs/tags"); - if (defined @$taglist) { - git_tags_body($taglist); - } - git_footer_html(); -} - -sub git_heads { - my $head = git_read_head($project); - git_header_html(); - git_page_nav('','', $head,undef,$head); - git_header_div('summary', $project); - - my $taglist = git_read_refs("refs/heads"); - my $alternate = 0; - if (defined @$taglist) { - git_heads_body($taglist, $head); - } - git_footer_html(); -} - -sub git_blob_plain { - if (!defined $hash) { - if (defined $file_name) { - my $base = $hash_base || git_read_head($project); - $hash = git_get_hash_by_path($base, $file_name, "blob") - or die_error(undef, "Error lookup file."); - } else { - die_error(undef, "No file name defined."); - } - } - my $type = shift; - open my $fd, "-|", $GIT, "cat-file", "blob", $hash - or die_error("Couldn't cat $file_name, $hash"); - - $type ||= git_blob_plain_mimetype($fd, $file_name); - - # save as filename, even when no $file_name is given - my $save_as = "$hash"; - if (defined $file_name) { - $save_as = $file_name; - } elsif ($type =~ m/^text\//) { - $save_as .= '.txt'; - } - - print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\""); - undef $/; - binmode STDOUT, ':raw'; - print <$fd>; - binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi - $/ = "\n"; - close $fd; -} - -sub git_blob { - if (!defined $hash) { - if (defined $file_name) { - my $base = $hash_base || git_read_head($project); - $hash = git_get_hash_by_path($base, $file_name, "blob") - or die_error(undef, "Error lookup file."); - } else { - die_error(undef, "No file name defined."); - } - } - my $have_blame = git_get_project_config_bool ('blame'); - open my $fd, "-|", $GIT, "cat-file", "blob", $hash - or die_error(undef, "Couldn't cat $file_name, $hash."); - my $mimetype = git_blob_plain_mimetype($fd, $file_name); - if ($mimetype !~ m/^text\//) { - close $fd; - return git_blob_plain($mimetype); - } - git_header_html(); - my $formats_nav = ''; - if (defined $hash_base && (my %co = git_read_commit($hash_base))) { - if (defined $file_name) { - if ($have_blame) { - $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") . " | "; - } - $formats_nav .= - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head"); - } else { - $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain"); - } - git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - git_header_div('commit', esc_html($co{'title'}), $hash_base); - } else { - print "
\n" . - "

\n" . - "
$hash
\n"; - } - git_print_page_path($file_name, "blob"); - print "
\n"; - my $nr; - while (my $line = <$fd>) { - chomp $line; - $nr++; - while ((my $pos = index($line, "\t")) != -1) { - if (my $count = (8 - ($pos % 8))) { - my $spaces = ' ' x $count; - $line =~ s/\t/$spaces/; - } - } - printf "
%4i %s
\n", $nr, $nr, $nr, esc_html($line); - } - close $fd or print "Reading blob failed.\n"; - print "
"; - git_footer_html(); -} - -sub git_tree { - if (!defined $hash) { - $hash = git_read_head($project); - if (defined $file_name) { - my $base = $hash_base || $hash; - $hash = git_get_hash_by_path($base, $file_name, "tree"); - } - if (!defined $hash_base) { - $hash_base = $hash; - } - } - $/ = "\0"; - open my $fd, "-|", $GIT, "ls-tree", '-z', $hash - or die_error(undef, "Open git-ls-tree failed."); - my @entries = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading tree failed."); - $/ = "\n"; - - my $refs = read_info_ref(); - my $ref = git_get_referencing($refs, $hash_base); - git_header_html(); - my $base_key = ""; - my $base = ""; - if (defined $hash_base && (my %co = git_read_commit($hash_base))) { - $base_key = ";hb=$hash_base"; - git_page_nav('tree','', $hash_base); - git_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); - } else { - print "
\n"; - print "

\n"; - print "
$hash
\n"; - } - if (defined $file_name) { - $base = esc_html("$file_name/"); - } - git_print_page_path($file_name, 'tree'); - print "
\n"; - print "\n"; - my $alternate = 0; - foreach my $line (@entries) { - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; - my $t_mode = $1; - my $t_type = $2; - my $t_hash = $3; - my $t_name = validate_input($4); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n"; - if ($t_type eq "blob") { - print "\n" . - "\n"; - } elsif ($t_type eq "tree") { - print "\n" . - "\n"; - } - print "\n"; - } - print "
" . mode_str($t_mode) . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") . -# " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, esc_html($t_name)) . - "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash_base;f=$base$t_name")}, "history") . - "
\n" . - "
"; - git_footer_html(); -} - -sub git_log { - my $head = git_read_head($project); - if (!defined $hash) { - $hash = $head; - } - if (!defined $page) { - $page = 0; - } - my $refs = read_info_ref(); - - my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", $GIT, "rev-list", $limit, $hash - or die_error(undef, "Open git-rev-list failed."); - my @revlist = map { chomp; $_ } <$fd>; - close $fd; - - my $paging_nav = git_get_paging_nav('log', $hash, $head, $page, $#revlist); - - git_header_html(); - git_page_nav('log','', $hash,undef,undef, $paging_nav); - - if (!@revlist) { - my %co = git_read_commit($hash); - - git_header_div('summary', $project); - print "
Last change $co{'age_string'}.

\n"; - } - for (my $i = ($page * 100); $i <= $#revlist; $i++) { - my $commit = $revlist[$i]; - my $ref = git_get_referencing($refs, $commit); - my %co = git_read_commit($commit); - next if !%co; - my %ad = date_str($co{'author_epoch'}); - git_header_div('commit', - "$co{'age_string'}" . - esc_html($co{'title'}) . $ref, - $commit); - print "
\n" . - "
\n" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - "
\n" . - "
\n" . - "" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]
\n" . - "
\n" . - "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - foreach my $line (@$comment) { - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - next; - } - if ($line eq "") { - if ($empty) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - print format_log_line_html($line) . "
\n"; - } - if (!$empty) { - print "
\n"; - } - print "
\n"; - } - git_footer_html(); -} - -sub git_commit { - my %co = git_read_commit($hash); - if (!%co) { - die_error(undef, "Unknown commit object."); - } - my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); - my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); - - my $parent = $co{'parent'}; - if (!defined $parent) { - $parent = "--root"; - } - open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $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."); - - # non-textual hash id's can be cached - my $expires; - if ($hash =~ m/^[0-9a-fA-F]{40}$/) { - $expires = "+1d"; - } - my $refs = read_info_ref(); - my $ref = git_get_referencing($refs, $co{'id'}); - my $formats_nav = ''; - if (defined $file_name && defined $co{'parent'}) { - my $parent = $co{'parent'}; - $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame"); - } - git_header_html(undef, $expires); - git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', - $hash, $co{'tree'}, $hash, - $formats_nav); - - if (defined $co{'parent'}) { - git_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); - } else { - git_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash); - } - print "
\n" . - "\n"; - print "\n". - "" . - "" . - "\n"; - print "\n"; - print "\n"; - print "\n"; - print "" . - "" . - "" . - "" . - "\n"; - my $parents = $co{'parents'}; - foreach my $par (@$parents) { - print "" . - "" . - "" . - "" . - "\n"; - } - print "
author" . esc_html($co{'author'}) . "
$ad{'rfc2822'}"; - if ($ad{'hour_local'} < 6) { - printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); - } else { - printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); - } - print "
committer" . esc_html($co{'committer'}) . "
$cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "
commit$co{'id'}
tree" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) . - "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . - "
parent" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") . - "
". - "
\n"; - print "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - my $signed = 0; - foreach my $line (@$comment) { - # print only one empty line - if ($line eq "") { - if ($empty || $signed) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - $signed = 1; - print "" . esc_html($line) . "
\n"; - } else { - $signed = 0; - print format_log_line_html($line) . "
\n"; - } - } - print "
\n"; - print "
\n"; - if ($#difftree > 10) { - print(($#difftree + 1) . " files changed:\n"); - } - print "
\n"; - print "\n"; - my $alternate = 0; - foreach my $line (@difftree) { - # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' - # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { - next; - } - my $from_mode = $1; - my $to_mode = $2; - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $similarity = $6; - my $file = validate_input(unquote($7)); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - if ($status eq "A") { - my $mode_chng = ""; - if (S_ISREG(oct $to_mode)) { - $mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777); - } - print "\n" . - "\n" . - "\n"; - } elsif ($status eq "D") { - print "\n" . - "\n" . - "\n" - } elsif ($status eq "M" || $status eq "T") { - my $mode_chnge = ""; - if ($from_mode != $to_mode) { - $mode_chnge = " [changed"; - if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) { - $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode); - } - if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) { - if (S_ISREG($from_mode) && S_ISREG($to_mode)) { - $mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777); - } elsif (S_ISREG($to_mode)) { - $mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777); - } - } - $mode_chnge .= "]\n"; - } - print "\n" . - "\n" . - "\n"; - } elsif ($status eq "R") { - my ($from_file, $to_file) = split "\t", $file; - my $mode_chng = ""; - if ($from_mode != $to_mode) { - $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777); - } - print "\n" . - "\n" . - "\n"; - } - print "\n"; - } - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "[new " . file_type($to_mode) . "$mode_chng]" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "[deleted " . file_type($from_mode). "]" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . - ""; - if ($to_id ne $from_id) { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)); - } else { - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)); - } - print "$mode_chnge"; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob"); - if ($to_id ne $from_id) { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff"); - } - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n"; - print "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "[moved from " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) . - " with " . (int $similarity) . "% similarity$mode_chng]" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob"); - if ($to_id ne $from_id) { - print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file")}, "diff"); - } - print "
\n"; - git_footer_html(); -} - -sub git_blobdiff { - mkdir($git_temp, 0700); - git_header_html(); - if (defined $hash_base && (my %co = git_read_commit($hash_base))) { - my $formats_nav = - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); - git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - git_header_div('commit', esc_html($co{'title'}), $hash_base); - } else { - print "
\n" . - "

\n" . - "
$hash vs $hash_parent
\n"; - } - git_print_page_path($file_name, "blob"); - print "
\n" . - "
blob:" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) . - " -> blob:" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, $hash) . - "
\n"; - git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash); - print "
"; - git_footer_html(); -} - -sub git_blobdiff_plain { - mkdir($git_temp, 0700); - print $cgi->header(-type => "text/plain", -charset => 'utf-8'); - git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash, "plain"); -} - -sub git_commitdiff { - mkdir($git_temp, 0700); - my %co = git_read_commit($hash); - if (!%co) { - die_error(undef, "Unknown commit object."); - } - if (!defined $hash_parent) { - $hash_parent = $co{'parent'}; - } - open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed."); - my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed."); - - # non-textual hash id's can be cached - my $expires; - if ($hash =~ m/^[0-9a-fA-F]{40}$/) { - $expires = "+1d"; - } - my $refs = read_info_ref(); - my $ref = git_get_referencing($refs, $co{'id'}); - my $formats_nav = - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); - git_header_html(undef, $expires); - git_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); - git_header_div('commit', esc_html($co{'title'}) . $ref, $hash); - print "
\n"; - my $comment = $co{'comment'}; - my $empty = 0; - my $signed = 0; - my @log = @$comment; - # remove first and empty lines after that - shift @log; - while (defined $log[0] && $log[0] eq "") { - shift @log; - } - foreach my $line (@log) { - if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { - next; - } - if ($line eq "") { - if ($empty) { - next; - } - $empty = 1; - } else { - $empty = 0; - } - print format_log_line_html($line) . "
\n"; - } - print "
\n"; - foreach my $line (@difftree) { - # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' - # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; - my $from_mode = $1; - my $to_mode = $2; - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $file = validate_input(unquote($6)); - if ($status eq "A") { - print "
" . file_type($to_mode) . ":" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" . - "
\n"; - git_diff_print(undef, "/dev/null", $to_id, "b/$file"); - } elsif ($status eq "D") { - print "
" . file_type($from_mode) . ":" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" . - "
\n"; - git_diff_print($from_id, "a/$file", undef, "/dev/null"); - } elsif ($status eq "M") { - if ($from_id ne $to_id) { - print "
" . - file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . - " -> " . - file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id); - print "
\n"; - git_diff_print($from_id, "a/$file", $to_id, "b/$file"); - } - } - } - print "
\n" . - "
"; - git_footer_html(); -} - -sub git_commitdiff_plain { - mkdir($git_temp, 0700); - open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed."); - my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed."); - - # try to figure out the next tag after this commit - my $tagname; - my $refs = read_info_ref("tags"); - open $fd, "-|", $GIT, "rev-list", "HEAD"; - my @commits = map { chomp; $_ } <$fd>; - close $fd; - foreach my $commit (@commits) { - if (defined $refs->{$commit}) { - $tagname = $refs->{$commit} - } - if ($commit eq $hash) { - last; - } - } - - print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\""); - my %co = git_read_commit($hash); - my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); - my $comment = $co{'comment'}; - print "From: $co{'author'}\n" . - "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n". - "Subject: $co{'title'}\n"; - if (defined $tagname) { - print "X-Git-Tag: $tagname\n"; - } - print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" . - "\n"; - - foreach my $line (@$comment) {; - print "$line\n"; - } - print "---\n\n"; - - foreach my $line (@difftree) { - $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $file = $6; - if ($status eq "A") { - git_diff_print(undef, "/dev/null", $to_id, "b/$file", "plain"); - } elsif ($status eq "D") { - git_diff_print($from_id, "a/$file", undef, "/dev/null", "plain"); - } elsif ($status eq "M") { - git_diff_print($from_id, "a/$file", $to_id, "b/$file", "plain"); - } - } -} - -sub git_history { - if (!defined $hash_base) { - $hash_base = git_read_head($project); - } - my $ftype; - my %co = git_read_commit($hash_base); - if (!%co) { - die_error(undef, "Unknown commit object."); - } - my $refs = read_info_ref(); - git_header_html(); - git_page_nav('','', $hash_base,$co{'tree'},$hash_base); - git_header_div('commit', esc_html($co{'title'}), $hash_base); - if (!defined $hash && defined $file_name) { - $hash = git_get_hash_by_path($hash_base, $file_name); - } - if (defined $hash) { - $ftype = git_get_type($hash); - } - git_print_page_path($file_name, $ftype); - - open my $fd, "-|", - $GIT, "rev-list", "--full-history", $hash_base, "--", $file_name; - print "\n"; - my $alternate = 0; - while (my $line = <$fd>) { - if ($line =~ m/^([0-9a-fA-F]{40})/){ - my $commit = $1; - my %co = git_read_commit($commit); - if (!%co) { - next; - } - my $ref = git_get_referencing($refs, $commit); - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - } - } - print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, "" . - esc_html(chop_str($co{'title'}, 50)) . "$ref") . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$ftype;hb=$commit;f=$file_name")}, $ftype); - my $blob = git_get_hash_by_path($hash_base, $file_name); - my $blob_parent = git_get_hash_by_path($commit, $file_name); - if (defined $blob && defined $blob_parent && $blob ne $blob_parent) { - print " | " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name")}, - "diff to current"); - } - print "
\n"; - close $fd; - git_footer_html(); -} - -sub git_search { - if (!defined $searchtext) { - die_error("", "Text field empty."); - } - if (!defined $hash) { - $hash = git_read_head($project); - } - my %co = git_read_commit($hash); - if (!%co) { - die_error(undef, "Unknown commit object."); - } - # 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 $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; - } - git_header_html(); - git_page_nav('','', $hash,$co{'tree'},$hash); - git_header_div('commit', esc_html($co{'title'}), $hash); - - print "\n"; - my $alternate = 0; - if ($commit_search) { - $/ = "\0"; - open my $fd, "-|", $GIT, "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) { - next; - } - if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) { - next; - } - my @commit_lines = split "\n", $commit_text; - my %co = git_read_commit(undef, \@commit_lines); - if (!%co) { - next; - } - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - } - close $fd; - } - - if ($pickaxe_search) { - $/ = "\n"; - open my $fd, "-|", "$GIT rev-list $hash | $GIT diff-tree -r --stdin -S\'$searchtext\'"; - undef %co; - my @files; - while (my $line = <$fd>) { - if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { - my %set; - $set{'file'} = $6; - $set{'from_id'} = $3; - $set{'to_id'} = $4; - $set{'id'} = $set{'to_id'}; - if ($set{'id'} =~ m/0{40}/) { - $set{'id'} = $set{'from_id'}; - } - if ($set{'id'} =~ m/0{40}/) { - next; - } - push @files, \%set; - } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){ - if (%co) { - if ($alternate) { - print "\n"; - } else { - print "\n"; - } - $alternate ^= 1; - print "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - } - %co = git_read_commit($1); - } - } - close $fd; - } - print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "" . esc_html(chop_str($co{'title'}, 50)) . "
"); - my $comment = $co{'comment'}; - foreach my $line (@$comment) { - if ($line =~ m/^(.*)($searchtext)(.*)$/i) { - my $lead = esc_html($1) || ""; - $lead = chop_str($lead, 30, 10); - my $match = esc_html($2) || ""; - my $trail = esc_html($3) || ""; - $trail = chop_str($trail, 30, 10); - my $text = "$lead$match$trail"; - print chop_str($text, 80, 5) . "
\n"; - } - } - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree"); - print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "" . - esc_html(chop_str($co{'title'}, 50)) . "
"); - while (my $setref = shift @files) { - my %set = %$setref; - print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}"), class => "list"}, - "" . esc_html($set{'file'}) . "") . - "
\n"; - } - print "
" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree"); - print "
\n"; - git_footer_html(); -} - -sub git_shortlog { - my $head = git_read_head($project); - if (!defined $hash) { - $hash = $head; - } - if (!defined $page) { - $page = 0; - } - my $refs = read_info_ref(); - - my $limit = sprintf("--max-count=%i", (100 * ($page+1))); - open my $fd, "-|", $GIT, "rev-list", $limit, $hash - or die_error(undef, "Open git-rev-list failed."); - my @revlist = map { chomp; $_ } <$fd>; - close $fd; - - my $paging_nav = git_get_paging_nav('shortlog', $hash, $head, $page, $#revlist); - my $next_link = ''; - if ($#revlist >= (100 * ($page+1)-1)) { - $next_link = - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), - -title => "Alt-n"}, "next"); - } - - - git_header_html(); - git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); - git_header_div('summary', $project); - - git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link); - - git_footer_html(); -} - -## ...................................................................... -## feeds (RSS, OPML) - -sub git_rss { - # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ - open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) - or die_error(undef, "Open git-rev-list failed."); - my @revlist = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading rev-list failed."); - print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n"; - print "\n"; - print "$project\n". - "" . esc_html("$my_url?p=$project;a=summary") . "\n". - "$project log\n". - "en\n"; - - for (my $i = 0; $i <= $#revlist; $i++) { - my $commit = $revlist[$i]; - my %co = git_read_commit($commit); - # we read 150, we always show 30 and the ones more recent than 48 hours - if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) { - last; - } - my %cd = date_str($co{'committer_epoch'}); - open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next; - my @difftree = map { chomp; $_ } <$fd>; - close $fd or next; - print "\n" . - "" . - sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) . - "\n" . - "" . esc_html($co{'author'}) . "\n" . - "$cd{'rfc2822'}\n" . - "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . - "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . - "" . esc_html($co{'title'}) . "\n" . - "" . - "\n"; - } - print "
\n"; - foreach my $line (@difftree) { - if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { - next; - } - my $file = validate_input(unquote($7)); - $file = decode("utf8", $file, Encode::FB_DEFAULT); - print "$file
\n"; - } - print "]]>\n" . - "
\n" . - "
\n"; - } - print "
"; -} - -sub git_opml { - my @list = git_read_projects(); - - print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n". - "". - " $site_name Git OPML Export\n". - "\n". - "\n". - "\n"; - - foreach my $pr (@list) { - my %proj = %$pr; - my $head = git_read_head($proj{'path'}); - if (!defined $head) { - next; - } - $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}"; - my %co = git_read_commit($head); - if (!%co) { - next; - } - - my $path = esc_html(chop_str($proj{'path'}, 25, 5)); - my $rss = "$my_url?p=$proj{'path'};a=rss"; - my $html = "$my_url?p=$proj{'path'};a=summary"; - print "\n"; - } - print "\n". - "\n". - "\n"; -} diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl new file mode 100755 index 000000000..6e4261d5f --- /dev/null +++ b/gitweb/gitweb.perl @@ -0,0 +1,2597 @@ +#!/usr/bin/perl + +# gitweb - simple web interface to track changes in git repositories +# +# (C) 2005-2006, Kay Sievers +# (C) 2005, Christian Gierke +# +# This program is licensed under the GPLv2 + +use strict; +use warnings; +use CGI qw(:standard :escapeHTML -nosticky); +use CGI::Util qw(unescape); +use CGI::Carp qw(fatalsToBrowser); +use Encode; +use Fcntl ':mode'; +use File::Find qw(); +binmode STDOUT, ':utf8'; + +our $cgi = new CGI; +our $version = "@@GIT_VERSION@@"; +our $my_url = $cgi->url(); +our $my_uri = $cgi->url(-absolute => 1); +our $rss_link = ""; + +# core git executable to use +# this can just be "git" if your webserver has a sensible PATH +our $GIT = "@@GIT_BINDIR@@/git"; + +# absolute fs-path which will be prepended to the project path +#our $projectroot = "/pub/scm"; +our $projectroot = "@@GITWEB_PROJECTROOT@@"; + +# version of the core git binary +our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; + +# location for temporary files needed for diffs +our $git_temp = "/tmp/gitweb"; +if (! -d $git_temp) { + mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); +} + +# target of the home link on top of all pages +our $home_link = $my_uri; + +# 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"; + +# html text to include at home page +our $home_text = "@@GITWEB_HOMETEXT@@"; + +# URI of default stylesheet +our $stylesheet = "@@GITWEB_CSS@@"; + +# source of projects list +our $projects_list = "@@GITWEB_LIST@@" || "$projectroot"; + +# default blob_plain mimetype and default charset for text/plain blob +our $default_blob_plain_mimetype = 'text/plain'; +our $default_text_plain_charset = undef; + +# file to use for guessing MIME types before trying /etc/mime.types +# (relative to the current git repository) +our $mimetypes_file = undef; + +# input validation and dispatch +our $action = $cgi->param('a'); +if (defined $action) { + if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { + undef $action; + die_error(undef, "Invalid action parameter."); + } + if ($action eq "git-logo.png") { + git_logo(); + exit; + } elsif ($action eq "opml") { + git_opml(); + exit; + } +} + +our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); +if (defined $project) { + $project =~ s|^/||; $project =~ s|/$||; + $project = validate_input($project); + if (!defined($project)) { + die_error(undef, "Invalid project parameter."); + } + if (!(-d "$projectroot/$project")) { + undef $project; + die_error(undef, "No such directory."); + } + if (!(-e "$projectroot/$project/HEAD")) { + undef $project; + die_error(undef, "No such project."); + } + $rss_link = ""; + $ENV{'GIT_DIR'} = "$projectroot/$project"; +} else { + git_project_list(); + exit; +} + +our $file_name = $cgi->param('f'); +if (defined $file_name) { + $file_name = validate_input($file_name); + if (!defined($file_name)) { + die_error(undef, "Invalid file parameter."); + } +} + +our $hash = $cgi->param('h'); +if (defined $hash) { + $hash = validate_input($hash); + if (!defined($hash)) { + die_error(undef, "Invalid hash parameter."); + } +} + +our $hash_parent = $cgi->param('hp'); +if (defined $hash_parent) { + $hash_parent = validate_input($hash_parent); + if (!defined($hash_parent)) { + die_error(undef, "Invalid hash parent parameter."); + } +} + +our $hash_base = $cgi->param('hb'); +if (defined $hash_base) { + $hash_base = validate_input($hash_base); + if (!defined($hash_base)) { + die_error(undef, "Invalid hash base parameter."); + } +} + +our $page = $cgi->param('pg'); +if (defined $page) { + if ($page =~ m/[^0-9]$/) { + undef $page; + die_error(undef, "Invalid page parameter."); + } +} + +our $searchtext = $cgi->param('s'); +if (defined $searchtext) { + if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) { + undef $searchtext; + die_error(undef, "Invalid search parameter."); + } + $searchtext = quotemeta $searchtext; +} + +# dispatch +my %actions = ( + "blame" => \&git_blame2, + "blobdiff" => \&git_blobdiff, + "blobdiff_plain" => \&git_blobdiff_plain, + "blob" => \&git_blob, + "blob_plain" => \&git_blob_plain, + "commitdiff" => \&git_commitdiff, + "commitdiff_plain" => \&git_commitdiff_plain, + "commit" => \&git_commit, + "heads" => \&git_heads, + "history" => \&git_history, + "log" => \&git_log, + "rss" => \&git_rss, + "search" => \&git_search, + "shortlog" => \&git_shortlog, + "summary" => \&git_summary, + "tag" => \&git_tag, + "tags" => \&git_tags, + "tree" => \&git_tree, +); + +$action = 'summary' if (!defined($action)); +if (!defined($actions{$action})) { + undef $action; + die_error(undef, "Unknown action."); +} +$actions{$action}->(); +exit; + +## ====================================================================== +## validation, quoting/unquoting and escaping + +sub validate_input { + my $input = shift; + + if ($input =~ m/^[0-9a-fA-F]{40}$/) { + return $input; + } + if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) { + return undef; + } + if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) { + return undef; + } + return $input; +} + +# quote unsafe chars, but keep the slash, even when it's not +# correct, but quoted slashes look too horrible in bookmarks +sub esc_param { + my $str = shift; + $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg; + $str =~ s/\+/%2B/g; + $str =~ s/ /\+/g; + return $str; +} + +# replace invalid utf8 character with SUBSTITUTION sequence +sub esc_html { + my $str = shift; + $str = decode("utf8", $str, Encode::FB_DEFAULT); + $str = escapeHTML($str); + $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) + return $str; +} + +# git may return quoted and escaped filenames +sub unquote { + my $str = shift; + if ($str =~ m/^"(.*)"$/) { + $str = $1; + $str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg; + } + return $str; +} + +## ---------------------------------------------------------------------- +## HTML aware string manipulation + +sub chop_str { + my $str = shift; + my $len = shift; + my $add_len = shift || 10; + + # allow only $len chars, but don't cut a word if it would fit in $add_len + # if it doesn't fit, cut it if it's still longer than the dots we would add + $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/; + my $body = $1; + my $tail = $2; + if (length($tail) > 4) { + $tail = " ..."; + $body =~ s/&[^;]*$//; # remove chopped character entities + } + return "$body$tail"; +} + +## ---------------------------------------------------------------------- +## functions returning short strings + +# CSS class for given age value (in seconds) +sub age_class { + my $age = shift; + + if ($age < 60*60*2) { + return "age0"; + } elsif ($age < 60*60*24*2) { + return "age1"; + } else { + return "age2"; + } +} + +# convert age in seconds to "nn units ago" string +sub age_string { + my $age = shift; + my $age_str; + + if ($age > 60*60*24*365*2) { + $age_str = (int $age/60/60/24/365); + $age_str .= " years ago"; + } elsif ($age > 60*60*24*(365/12)*2) { + $age_str = int $age/60/60/24/(365/12); + $age_str .= " months ago"; + } elsif ($age > 60*60*24*7*2) { + $age_str = int $age/60/60/24/7; + $age_str .= " weeks ago"; + } elsif ($age > 60*60*24*2) { + $age_str = int $age/60/60/24; + $age_str .= " days ago"; + } elsif ($age > 60*60*2) { + $age_str = int $age/60/60; + $age_str .= " hours ago"; + } elsif ($age > 60*2) { + $age_str = int $age/60; + $age_str .= " min ago"; + } elsif ($age > 2) { + $age_str = int $age; + $age_str .= " sec ago"; + } else { + $age_str .= " right now"; + } + return $age_str; +} + +# convert file mode in octal to symbolic file mode string +sub mode_str { + my $mode = oct shift; + + if (S_ISDIR($mode & S_IFMT)) { + return 'drwxr-xr-x'; + } elsif (S_ISLNK($mode)) { + return 'lrwxrwxrwx'; + } elsif (S_ISREG($mode)) { + # git cares only about the executable bit + if ($mode & S_IXUSR) { + return '-rwxr-xr-x'; + } else { + return '-rw-r--r--'; + }; + } else { + return '----------'; + } +} + +# convert file mode in octal to file type string +sub file_type { + my $mode = oct shift; + + if (S_ISDIR($mode & S_IFMT)) { + return "directory"; + } elsif (S_ISLNK($mode)) { + return "symlink"; + } elsif (S_ISREG($mode)) { + return "file"; + } else { + return "unknown"; + } +} + +## ---------------------------------------------------------------------- +## functions returning short HTML fragments, or transforming HTML fragments +## which don't beling to other sections + +# format line of commit message or tag comment +sub format_log_line_html { + my $line = shift; + + $line = esc_html($line); + $line =~ s/ / /g; + if ($line =~ m/([0-9a-fA-F]{40})/) { + my $hash_text = $1; + if (git_get_type($hash_text) eq "commit") { + my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text); + $line =~ s/$hash_text/$link/; + } + } + return $line; +} + +# format marker of refs pointing to given object +sub git_get_referencing { + my ($refs, $id) = @_; + + if (defined $refs->{$id}) { + return ' ' . esc_html($refs->{$id}) . ''; + } else { + return ""; + } +} + +## ---------------------------------------------------------------------- +## git utility subroutines, invoking git commands + +# get HEAD ref of given project as hash +sub git_read_head { + my $project = shift; + my $oENV = $ENV{'GIT_DIR'}; + my $retval = undef; + $ENV{'GIT_DIR'} = "$projectroot/$project"; + if (open my $fd, "-|", $GIT, "rev-parse", "--verify", "HEAD") { + my $head = <$fd>; + close $fd; + if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) { + $retval = $1; + } + } + if (defined $oENV) { + $ENV{'GIT_DIR'} = $oENV; + } + return $retval; +} + +# get type of given object +sub git_get_type { + my $hash = shift; + + open my $fd, "-|", $GIT, "cat-file", '-t', $hash or return; + my $type = <$fd>; + close $fd or return; + chomp $type; + return $type; +} + +sub git_get_project_config { + my $key = shift; + + return unless ($key); + $key =~ s/^gitweb\.//; + return if ($key =~ m/\W/); + + my $val = qx($GIT repo-config --get gitweb.$key); + return ($val); +} + +sub git_get_project_config_bool { + my $val = git_get_project_config (@_); + if ($val and $val =~ m/true|yes|on/) { + return (1); + } + return; # implicit false +} + +# get hash of given path at given ref +sub git_get_hash_by_path { + my $base = shift; + my $path = shift || return undef; + + my $tree = $base; + + open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path + or die_error(undef, "Open git-ls-tree failed."); + my $line = <$fd>; + close $fd or return undef; + + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + return $3; +} + +## ...................................................................... +## git utility functions, directly accessing git repository + +# assumes that PATH is not symref +sub git_read_hash { + my $path = shift; + + open my $fd, "$projectroot/$path" or return undef; + my $head = <$fd>; + close $fd; + chomp $head; + if ($head =~ m/^[0-9a-fA-F]{40}$/) { + return $head; + } +} + +sub git_read_description { + my $path = shift; + + open my $fd, "$projectroot/$path/description" or return undef; + my $descr = <$fd>; + close $fd; + chomp $descr; + return $descr; +} + +sub git_read_projects { + my @list; + + if (-d $projects_list) { + # search in directory + my $dir = $projects_list; + opendir my ($dh), $dir or return undef; + while (my $dir = readdir($dh)) { + if (-e "$projectroot/$dir/HEAD") { + my $pr = { + path => $dir, + }; + push @list, $pr + } + } + closedir($dh); + } elsif (-f $projects_list) { + # read from file(url-encoded): + # 'git%2Fgit.git Linus+Torvalds' + # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' + # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' + open my ($fd), $projects_list or return undef; + while (my $line = <$fd>) { + chomp $line; + my ($path, $owner) = split ' ', $line; + $path = unescape($path); + $owner = unescape($owner); + if (!defined $path) { + next; + } + if (-e "$projectroot/$path/HEAD") { + my $pr = { + path => $path, + owner => decode("utf8", $owner, Encode::FB_DEFAULT), + }; + push @list, $pr + } + } + close $fd; + } + @list = sort {$a->{'path'} cmp $b->{'path'}} @list; + return @list; +} + +sub read_info_ref { + my $type = shift || ""; + my %refs; + # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 + # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{} + open my $fd, "$projectroot/$project/info/refs" or return; + while (my $line = <$fd>) { + chomp $line; + # attention: for $type == "" it saves only last path part of ref name + # e.g. from 'refs/heads/jn/gitweb' it would leave only 'gitweb' + if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) { + if (defined $refs{$1}) { + $refs{$1} .= " / $2"; + } else { + $refs{$1} = $2; + } + } + } + close $fd or return; + return \%refs; +} + +## ---------------------------------------------------------------------- +## parse to hash functions + +sub date_str { + my $epoch = shift; + my $tz = shift || "-0000"; + + my %date; + my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); + my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch); + $date{'hour'} = $hour; + $date{'minute'} = $min; + $date{'mday'} = $mday; + $date{'day'} = $days[$wday]; + $date{'month'} = $months[$mon]; + $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; + $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; + + $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; + my $local = $epoch + ((int $1 + ($2/60)) * 3600); + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local); + $date{'hour_local'} = $hour; + $date{'minute_local'} = $min; + $date{'tz_local'} = $tz; + return %date; +} + +sub git_read_tag { + my $tag_id = shift; + my %tag; + my @comment; + + open my $fd, "-|", $GIT, "cat-file", "tag", $tag_id or return; + $tag{'id'} = $tag_id; + while (my $line = <$fd>) { + chomp $line; + if ($line =~ m/^object ([0-9a-fA-F]{40})$/) { + $tag{'object'} = $1; + } elsif ($line =~ m/^type (.+)$/) { + $tag{'type'} = $1; + } elsif ($line =~ m/^tag (.+)$/) { + $tag{'name'} = $1; + } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) { + $tag{'author'} = $1; + $tag{'epoch'} = $2; + $tag{'tz'} = $3; + } elsif ($line =~ m/--BEGIN/) { + push @comment, $line; + last; + } elsif ($line eq "") { + last; + } + } + push @comment, <$fd>; + $tag{'comment'} = \@comment; + close $fd or return; + if (!defined $tag{'name'}) { + return + }; + return %tag +} + +sub git_read_commit { + my $commit_id = shift; + my $commit_text = shift; + + my @commit_lines; + my %co; + + if (defined $commit_text) { + @commit_lines = @$commit_text; + } else { + $/ = "\0"; + open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return; + @commit_lines = split '\n', <$fd>; + close $fd or return; + $/ = "\n"; + pop @commit_lines; + } + my $header = shift @commit_lines; + if (!($header =~ m/^[0-9a-fA-F]{40}/)) { + return; + } + ($co{'id'}, my @parents) = split ' ', $header; + $co{'parents'} = \@parents; + $co{'parent'} = $parents[0]; + 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/^author (.*) ([0-9]+) (.*)$/) { + $co{'author'} = $1; + $co{'author_epoch'} = $2; + $co{'author_tz'} = $3; + if ($co{'author'} =~ m/^([^<]+) 50) { + $title =~ s/^Automatic //; + $title =~ s/^merge (of|with) /Merge ... /i; + if (length($title) > 50) { + $title =~ s/(http|rsync):\/\///; + } + if (length($title) > 50) { + $title =~ s/(master|www|rsync)\.//; + } + if (length($title) > 50) { + $title =~ s/kernel.org:?//; + } + if (length($title) > 50) { + $title =~ s/\/pub\/scm//; + } + } + $co{'title_short'} = chop_str($title, 50, 5); + last; + } + } + # remove added spaces + foreach my $line (@commit_lines) { + $line =~ s/^ //; + } + $co{'comment'} = \@commit_lines; + + my $age = time - $co{'committer_epoch'}; + $co{'age'} = $age; + $co{'age_string'} = age_string($age); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'}); + if ($age > 60*60*24*7*2) { + $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; + $co{'age_string_age'} = $co{'age_string'}; + } else { + $co{'age_string_date'} = $co{'age_string'}; + $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday; + } + return %co; +} + +## ...................................................................... +## parse to array of hashes functions + +sub git_read_refs { + my $ref_dir = shift; + my @reflist; + + my @refs; + my $pfxlen = length("$projectroot/$project/$ref_dir"); + File::Find::find(sub { + return if (/^\./); + if (-f $_) { + push @refs, substr($File::Find::name, $pfxlen + 1); + } + }, "$projectroot/$project/$ref_dir"); + + foreach my $ref_file (@refs) { + my $ref_id = git_read_hash("$project/$ref_dir/$ref_file"); + my $type = git_get_type($ref_id) || next; + my %ref_item; + my %co; + $ref_item{'type'} = $type; + $ref_item{'id'} = $ref_id; + $ref_item{'epoch'} = 0; + $ref_item{'age'} = "unknown"; + if ($type eq "tag") { + my %tag = git_read_tag($ref_id); + $ref_item{'comment'} = $tag{'comment'}; + if ($tag{'type'} eq "commit") { + %co = git_read_commit($tag{'object'}); + $ref_item{'epoch'} = $co{'committer_epoch'}; + $ref_item{'age'} = $co{'age_string'}; + } elsif (defined($tag{'epoch'})) { + my $age = time - $tag{'epoch'}; + $ref_item{'epoch'} = $tag{'epoch'}; + $ref_item{'age'} = age_string($age); + } + $ref_item{'reftype'} = $tag{'type'}; + $ref_item{'name'} = $tag{'name'}; + $ref_item{'refid'} = $tag{'object'}; + } elsif ($type eq "commit"){ + %co = git_read_commit($ref_id); + $ref_item{'reftype'} = "commit"; + $ref_item{'name'} = $ref_file; + $ref_item{'title'} = $co{'title'}; + $ref_item{'refid'} = $ref_id; + $ref_item{'epoch'} = $co{'committer_epoch'}; + $ref_item{'age'} = $co{'age_string'}; + } else { + $ref_item{'reftype'} = $type; + $ref_item{'name'} = $ref_file; + $ref_item{'refid'} = $ref_id; + } + + push @reflist, \%ref_item; + } + # sort tags by age + @reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist; + return \@reflist; +} + +## ---------------------------------------------------------------------- +## filesystem-related functions + +sub get_file_owner { + my $path = shift; + + my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path); + my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid); + if (!defined $gcos) { + return undef; + } + my $owner = $gcos; + $owner =~ s/[,;].*$//; + return decode("utf8", $owner, Encode::FB_DEFAULT); +} + +## ...................................................................... +## mimetype related functions + +sub mimetype_guess_file { + my $filename = shift; + my $mimemap = shift; + -r $mimemap or return undef; + + my %mimemap; + open(MIME, $mimemap) or return undef; + while () { + my ($mime, $exts) = split(/\t+/); + if (defined $exts) { + my @exts = split(/\s+/, $exts); + foreach my $ext (@exts) { + $mimemap{$ext} = $mime; + } + } + } + close(MIME); + + $filename =~ /\.(.*?)$/; + return $mimemap{$1}; +} + +sub mimetype_guess { + my $filename = shift; + my $mime; + $filename =~ /\./ or return undef; + + if ($mimetypes_file) { + my $file = $mimetypes_file; + #$file =~ m#^/# or $file = "$projectroot/$path/$file"; + $mime = mimetype_guess_file($filename, $file); + } + $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); + return $mime; +} + +sub git_blob_plain_mimetype { + my $fd = shift; + my $filename = shift; + + if ($filename) { + my $mime = mimetype_guess($filename); + $mime and return $mime; + } + + # just in case + return $default_blob_plain_mimetype unless $fd; + + if (-T $fd) { + return 'text/plain' . + ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); + } elsif (! $filename) { + return 'application/octet-stream'; + } elsif ($filename =~ m/\.png$/i) { + return 'image/png'; + } elsif ($filename =~ m/\.gif$/i) { + return 'image/gif'; + } elsif ($filename =~ m/\.jpe?g$/i) { + return 'image/jpeg'; + } else { + return 'application/octet-stream'; + } +} + +## ====================================================================== +## functions printing HTML: header, footer, error page + +sub git_header_html { + my $status = shift || "200 OK"; + my $expires = shift; + + my $title = "$site_name git"; + if (defined $project) { + $title .= " - $project"; + if (defined $action) { + $title .= "/$action"; + if (defined $file_name) { + $title .= " - $file_name"; + if ($action eq "tree" && $file_name !~ m|/$|) { + $title .= "/"; + } + } + } + } + my $content_type; + # require explicit support from the UA if we are to send the page as + # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. + # we have to do this because MSIE sometimes globs '*/*', pretending to + # support xhtml+xml but choking when it gets what it asked for. + if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { + $content_type = 'application/xhtml+xml'; + } else { + $content_type = 'text/html'; + } + print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); + print < + + + + + + + +$title + +$rss_link + + +EOF + print "
\n" . + "" . + "\"git\"" . + "\n"; + print $cgi->a({-href => esc_param($home_link)}, "projects") . " / "; + if (defined $project) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project)); + if (defined $action) { + print " / $action"; + } + print "\n"; + if (!defined $searchtext) { + $searchtext = ""; + } + my $search_hash; + if (defined $hash_base) { + $search_hash = $hash_base; + } elsif (defined $hash) { + $search_hash = $hash; + } else { + $search_hash = "HEAD"; + } + $cgi->param("a", "search"); + $cgi->param("h", $search_hash); + print $cgi->startform(-method => "get", -action => $my_uri) . + "
\n" . + $cgi->hidden(-name => "p") . "\n" . + $cgi->hidden(-name => "a") . "\n" . + $cgi->hidden(-name => "h") . "\n" . + $cgi->textfield(-name => "s", -value => $searchtext) . "\n" . + "
" . + $cgi->end_form() . "\n"; + } + print "
\n"; +} + +sub git_footer_html { + print "
\n"; + if (defined $project) { + my $descr = git_read_description($project); + if (defined $descr) { + print "\n"; + } + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n"; + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n"; + } + print "
\n" . + "\n" . + ""; +} + +sub die_error { + my $status = shift || "403 Forbidden"; + my $error = shift || "Malformed query, file missing or permission denied"; + + git_header_html($status); + print "
\n" . + "

\n" . + "$status - $error\n" . + "
\n" . + "
\n"; + git_footer_html(); + exit; +} + +## ---------------------------------------------------------------------- +## functions printing or outputting HTML: navigation + +sub git_page_nav { + my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_; + $extra = '' if !defined $extra; # pager or formats + + my @navs = qw(summary shortlog log commit commitdiff tree); + if ($suppress) { + @navs = grep { $_ ne $suppress } @navs; + } + + my %arg = map { $_, ''} @navs; + if (defined $head) { + for (qw(commit commitdiff)) { + $arg{$_} = ";h=$head"; + } + if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) { + for (qw(shortlog log)) { + $arg{$_} = ";h=$head"; + } + } + } + $arg{tree} .= ";h=$treehead" if defined $treehead; + $arg{tree} .= ";hb=$treebase" if defined $treebase; + + print "
\n" . + (join " | ", + map { $_ eq $current + ? $_ + : $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$_$arg{$_}")}, "$_") + } + @navs); + print "
\n$extra
\n" . + "
\n"; +} + +sub git_get_paging_nav { + my ($action, $hash, $head, $page, $nrevs) = @_; + my $paging_nav; + + + if ($hash ne $head || $page) { + $paging_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action")}, "HEAD"); + } else { + $paging_nav .= "HEAD"; + } + + if ($page > 0) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)), + -accesskey => "p", -title => "Alt-p"}, "prev"); + } else { + $paging_nav .= " ⋅ prev"; + } + + if ($nrevs >= (100 * ($page+1)-1)) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)), + -accesskey => "n", -title => "Alt-n"}, "next"); + } else { + $paging_nav .= " ⋅ next"; + } + + return $paging_nav; +} + +## ...................................................................... +## functions printing or outputting HTML: div + +sub git_header_div { + my ($action, $title, $hash, $hash_base) = @_; + my $rest = ''; + + $rest .= ";h=$hash" if $hash; + $rest .= ";hb=$hash_base" if $hash_base; + + print "
\n" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action$rest"), + -class => "title"}, $title ? $title : $action) . "\n" . + "
\n"; +} + +sub git_print_page_path { + my $name = shift; + my $type = shift; + + if (!defined $name) { + print "
/
\n"; + } elsif (defined $type && $type eq 'blob') { + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "
\n"; + } else { + print "
" . esc_html($name) . "
\n"; + } +} + +## ...................................................................... +## functions printing large fragments of HTML + +sub git_shortlog_body { + # uses global variable $project + my ($revlist, $from, $to, $refs, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $commit = $revlist->[$i]; + #my $ref = defined $refs ? git_get_referencing($refs, $commit) : ''; + my $ref = git_get_referencing($refs, $commit); + my %co = git_read_commit($commit); + my %ad = date_str($co{'author_epoch'}); + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + # git_summary() used print "\n" . + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$co{'age_string'}$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 10)) . ""; + if (length($co{'title_short'}) < length($co{'title'})) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), + -class => "list", -title => "$co{'title'}"}, + "" . esc_html($co{'title_short'}) . "$ref"); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), + -class => "list"}, + "" . esc_html($co{'title'}) . "$ref"); + } + print "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . + "
$extra
\n"; +} + +sub git_tags_body { + # uses global variable $project + my ($taglist, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $comment_lines = $tag{'comment'}; + my $comment = shift @$comment_lines; + my $comment_short; + if (defined $comment) { + $comment_short = chop_str($comment, 30, 5); + } + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + ""; + if (defined $comment) { + if (length($comment_short) < length($comment)) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list", -title => $comment}, $comment_short); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"), + -class => "list"}, $comment); + } + } + print ""; + if ($tag{'type'} eq "tag") { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag"); + } else { + print " "; + } + print "" . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'}); + if ($tag{'reftype'} eq "commit") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log"); + } elsif ($tag{'reftype'} eq "blob") { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw"); + } + print "
$extra
\n"; +} + +sub git_heads_body { + # uses global variable $project + my ($taglist, $head, $from, $to, $extra) = @_; + $from = 0 unless defined $from; + $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); + + print "\n"; + my $alternate = 0; + for (my $i = $from; $i <= $to; $i++) { + my $entry = $taglist->[$i]; + my %tag = %$entry; + my $curr = $tag{'id'} eq $head; + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + ($tag{'id'} eq $head ? "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" : "") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), + -class => "list"}, "" . esc_html($tag{'name'}) . "") . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") . + "
$extra
\n"; +} + +## ---------------------------------------------------------------------- +## functions printing large fragments, format as one of arguments + +sub git_diff_print { + my $from = shift; + my $from_name = shift; + my $to = shift; + my $to_name = shift; + my $format = shift || "html"; + + my $from_tmp = "/dev/null"; + my $to_tmp = "/dev/null"; + my $pid = $$; + + # create tmp from-file + if (defined $from) { + $from_tmp = "$git_temp/gitweb_" . $$ . "_from"; + open my $fd2, "> $from_tmp"; + open my $fd, "-|", $GIT, "cat-file", "blob", $from; + my @file = <$fd>; + print $fd2 @file; + close $fd2; + close $fd; + } + + # create tmp to-file + if (defined $to) { + $to_tmp = "$git_temp/gitweb_" . $$ . "_to"; + open my $fd2, "> $to_tmp"; + open my $fd, "-|", $GIT, "cat-file", "blob", $to; + my @file = <$fd>; + print $fd2 @file; + close $fd2; + close $fd; + } + + open my $fd, "-|", "/usr/bin/diff -u -p -L \'$from_name\' -L \'$to_name\' $from_tmp $to_tmp"; + if ($format eq "plain") { + undef $/; + print <$fd>; + $/ = "\n"; + } else { + while (my $line = <$fd>) { + chomp $line; + my $char = substr($line, 0, 1); + my $diff_class = ""; + if ($char eq '+') { + $diff_class = " add"; + } elsif ($char eq "-") { + $diff_class = " rem"; + } elsif ($char eq "@") { + $diff_class = " chunk_header"; + } elsif ($char eq "\\") { + # skip errors + next; + } + while ((my $pos = index($line, "\t")) != -1) { + if (my $count = (8 - (($pos-1) % 8))) { + my $spaces = ' ' x $count; + $line =~ s/\t/$spaces/; + } + } + print "
" . esc_html($line) . "
\n"; + } + } + close $fd; + + if (defined $from) { + unlink($from_tmp); + } + if (defined $to) { + unlink($to_tmp); + } +} + + +## ====================================================================== +## ====================================================================== +## actions + +# git-logo (cached in browser for one day) +sub git_logo { + binmode STDOUT, ':raw'; + print $cgi->header(-type => 'image/png', -expires => '+1d'); + # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g' + print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" . + "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" . + "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" . + "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" . + "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" . + "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" . + "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" . + "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" . + "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" . + "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" . + "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" . + "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" . + "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"; +} + +sub git_project_list { + my $order = $cgi->param('o'); + if (defined $order && $order !~ m/project|descr|owner|age/) { + die_error(undef, "Invalid order parameter '$order'."); + } + + my @list = git_read_projects(); + my @projects; + if (!@list) { + die_error(undef, "No projects found."); + } + foreach my $pr (@list) { + my $head = git_read_head($pr->{'path'}); + if (!defined $head) { + next; + } + $ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}"; + my %co = git_read_commit($head); + if (!%co) { + next; + } + $pr->{'commit'} = \%co; + if (!defined $pr->{'descr'}) { + my $descr = git_read_description($pr->{'path'}) || ""; + $pr->{'descr'} = chop_str($descr, 25, 5); + } + if (!defined $pr->{'owner'}) { + $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || ""; + } + push @projects, $pr; + } + + git_header_html(); + if (-f $home_text) { + print "
\n"; + open (my $fd, $home_text); + print <$fd>; + close $fd; + print "
\n"; + } + print "\n" . + "\n"; + $order ||= "project"; + if ($order eq "project") { + @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "descr") { + @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "owner") { + @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; + print "\n"; + } else { + print "\n"; + } + if ($order eq "age") { + @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects; + print "\n"; + } else { + print "\n"; + } + print "\n" . + "\n"; + my $alternate = 0; + foreach my $pr (@projects) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n"; + print "\n" . + "\n" . + "\n"; + } + print "
Project" . + $cgi->a({-href => "$my_uri?" . esc_param("o=project"), + -class => "header"}, "Project") . + "Description" . + $cgi->a({-href => "$my_uri?" . esc_param("o=descr"), + -class => "header"}, "Description") . + "Owner" . + $cgi->a({-href => "$my_uri?" . esc_param("o=owner"), + -class => "header"}, "Owner") . + "Last Change" . + $cgi->a({-href => "$my_uri?" . esc_param("o=age"), + -class => "header"}, "Last Change") . + "
" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), + -class => "list"}, esc_html($pr->{'path'})) . "" . esc_html($pr->{'descr'}) . "" . chop_str($pr->{'owner'}, 15) . "{'commit'}{'age'}) . "\">" . + $pr->{'commit'}{'age_string'} . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") . " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") . + "
\n"; + git_footer_html(); +} + +sub git_summary { + my $descr = git_read_description($project) || "none"; + my $head = git_read_head($project); + my %co = git_read_commit($head); + my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); + + my $owner; + if (-f $projects_list) { + open (my $fd , $projects_list); + while (my $line = <$fd>) { + chomp $line; + my ($pr, $ow) = split ' ', $line; + $pr = unescape($pr); + $ow = unescape($ow); + if ($pr eq $project) { + $owner = decode("utf8", $ow, Encode::FB_DEFAULT); + last; + } + } + close $fd; + } + if (!defined $owner) { + $owner = get_file_owner("$projectroot/$project"); + } + + my $refs = read_info_ref(); + git_header_html(); + git_page_nav('summary','', $head); + + print "
 
\n"; + print "\n" . + "\n" . + "\n" . + "\n" . + "
description" . esc_html($descr) . "
owner$owner
last change$cd{'rfc2822'}
\n"; + + open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) + or die_error(undef, "Open git-rev-list failed."); + my @revlist = map { chomp; $_ } <$fd>; + close $fd; + git_header_div('shortlog'); + git_shortlog_body(\@revlist, 0, 15, $refs, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...")); + + my $taglist = git_read_refs("refs/tags"); + if (defined @$taglist) { + git_header_div('tags'); + git_tags_body($taglist, 0, 15, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...")); + } + + my $headlist = git_read_refs("refs/heads"); + if (defined @$headlist) { + git_header_div('heads'); + git_heads_body($headlist, $head, 0, 15, + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...")); + } + + git_footer_html(); +} + +sub git_tag { + my $head = git_read_head($project); + git_header_html(); + git_page_nav('','', $head,undef,$head); + my %tag = git_read_tag($hash); + git_header_div('commit', esc_html($tag{'name'}), $hash); + print "
\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + if (defined($tag{'author'})) { + my %ad = date_str($tag{'epoch'}, $tag{'tz'}); + print "\n"; + print "\n"; + } + print "
object" . $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'object'}) . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'type'}) . "
author" . esc_html($tag{'author'}) . "
" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "
\n\n" . + "
\n"; + print "
"; + my $comment = $tag{'comment'}; + foreach my $line (@$comment) { + print esc_html($line) . "
\n"; + } + print "
\n"; + git_footer_html(); +} + +sub git_blame2 { + my $fd; + my $ftype; + die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame')); + die_error('404 Not Found', "File name not defined") if (!$file_name); + $hash_base ||= git_read_head($project); + die_error(undef, "Reading commit failed") unless ($hash_base); + my %co = git_read_commit($hash_base) + or die_error(undef, "Reading commit failed"); + if (!defined $hash) { + $hash = git_get_hash_by_path($hash_base, $file_name, "blob") + or die_error(undef, "Error looking up file"); + } + $ftype = git_get_type($hash); + if ($ftype !~ "blob") { + die_error("400 Bad Request", "object is not a blob"); + } + open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) + or die_error(undef, "Open git-blame failed."); + git_header_html(); + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + git_header_div('commit', esc_html($co{'title'}), $hash_base); + git_print_page_path($file_name, $ftype); + my @rev_color = (qw(light dark)); + my $num_colors = scalar(@rev_color); + my $current_color = 0; + my $last_rev; + print "
\n"; + print "\n"; + print "\n"; + while (<$fd>) { + /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/; + my $full_rev = $1; + my $rev = substr($full_rev, 0, 8); + my $lineno = $2; + my $data = $3; + + if (!defined $last_rev) { + $last_rev = $full_rev; + } elsif ($last_rev ne $full_rev) { + $last_rev = $full_rev; + $current_color = ++$current_color % $num_colors; + } + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + } + print "
CommitLineData
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "" . esc_html($lineno) . "" . esc_html($data) . "
\n"; + print "
"; + close $fd or print "Reading blob failed\n"; + git_footer_html(); +} + +sub git_blame { + my $fd; + die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame')); + die_error('404 Not Found', "What file will it be, master?") if (!$file_name); + $hash_base ||= git_read_head($project); + die_error(undef, "Reading commit failed.") unless ($hash_base); + my %co = git_read_commit($hash_base) + or die_error(undef, "Reading commit failed."); + if (!defined $hash) { + $hash = git_get_hash_by_path($hash_base, $file_name, "blob") + or die_error(undef, "Error lookup file."); + } + open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base) + or die_error(undef, "Open git-annotate failed."); + git_header_html(); + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + git_header_div('commit', esc_html($co{'title'}), $hash_base); + git_print_page_path($file_name, 'blob'); + print "
\n"; + print < + + Commit + Age + Author + Line + Data + +HTML + my @line_class = (qw(light dark)); + my $line_class_len = scalar (@line_class); + my $line_class_num = $#line_class; + while (my $line = <$fd>) { + my $long_rev; + my $short_rev; + my $author; + my $time; + my $lineno; + my $data; + my $age; + my $age_str; + my $age_class; + + chomp $line; + $line_class_num = ($line_class_num + 1) % $line_class_len; + + if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) \+\d\d\d\d\t(\d+)\)(.*)$/) { + $long_rev = $1; + $author = $2; + $time = $3; + $lineno = $4; + $data = $5; + } else { + print qq( Unable to parse: $line\n); + next; + } + $short_rev = substr ($long_rev, 0, 8); + $age = time () - $time; + $age_str = age_string ($age); + $age_str =~ s/ / /g; + $age_class = age_class($age); + $author = esc_html ($author); + $author =~ s/ / /g; + # escape tabs + while ((my $pos = index($data, "\t")) != -1) { + if (my $count = (8 - ($pos % 8))) { + my $spaces = ' ' x $count; + $data =~ s/\t/$spaces/; + } + } + $data = esc_html ($data); + + print < + $short_rev.. + $age_str + $author + $lineno + $data + +HTML + } # while (my $line = <$fd>) + print "\n\n"; + close $fd or print "Reading blob failed.\n"; + print "
"; + git_footer_html(); +} + +sub git_tags { + my $head = git_read_head($project); + git_header_html(); + git_page_nav('','', $head,undef,$head); + git_header_div('summary', $project); + + my $taglist = git_read_refs("refs/tags"); + if (defined @$taglist) { + git_tags_body($taglist); + } + git_footer_html(); +} + +sub git_heads { + my $head = git_read_head($project); + git_header_html(); + git_page_nav('','', $head,undef,$head); + git_header_div('summary', $project); + + my $taglist = git_read_refs("refs/heads"); + my $alternate = 0; + if (defined @$taglist) { + git_heads_body($taglist, $head); + } + git_footer_html(); +} + +sub git_blob_plain { + if (!defined $hash) { + if (defined $file_name) { + my $base = $hash_base || git_read_head($project); + $hash = git_get_hash_by_path($base, $file_name, "blob") + or die_error(undef, "Error lookup file."); + } else { + die_error(undef, "No file name defined."); + } + } + my $type = shift; + open my $fd, "-|", $GIT, "cat-file", "blob", $hash + or die_error("Couldn't cat $file_name, $hash"); + + $type ||= git_blob_plain_mimetype($fd, $file_name); + + # save as filename, even when no $file_name is given + my $save_as = "$hash"; + if (defined $file_name) { + $save_as = $file_name; + } elsif ($type =~ m/^text\//) { + $save_as .= '.txt'; + } + + print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\""); + undef $/; + binmode STDOUT, ':raw'; + print <$fd>; + binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi + $/ = "\n"; + close $fd; +} + +sub git_blob { + if (!defined $hash) { + if (defined $file_name) { + my $base = $hash_base || git_read_head($project); + $hash = git_get_hash_by_path($base, $file_name, "blob") + or die_error(undef, "Error lookup file."); + } else { + die_error(undef, "No file name defined."); + } + } + my $have_blame = git_get_project_config_bool ('blame'); + open my $fd, "-|", $GIT, "cat-file", "blob", $hash + or die_error(undef, "Couldn't cat $file_name, $hash."); + my $mimetype = git_blob_plain_mimetype($fd, $file_name); + if ($mimetype !~ m/^text\//) { + close $fd; + return git_blob_plain($mimetype); + } + git_header_html(); + my $formats_nav = ''; + if (defined $hash_base && (my %co = git_read_commit($hash_base))) { + if (defined $file_name) { + if ($have_blame) { + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") . " | "; + } + $formats_nav .= + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head"); + } else { + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain"); + } + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + git_header_div('commit', esc_html($co{'title'}), $hash_base); + } else { + print "
\n" . + "

\n" . + "
$hash
\n"; + } + git_print_page_path($file_name, "blob"); + print "
\n"; + my $nr; + while (my $line = <$fd>) { + chomp $line; + $nr++; + while ((my $pos = index($line, "\t")) != -1) { + if (my $count = (8 - ($pos % 8))) { + my $spaces = ' ' x $count; + $line =~ s/\t/$spaces/; + } + } + printf "
%4i %s
\n", $nr, $nr, $nr, esc_html($line); + } + close $fd or print "Reading blob failed.\n"; + print "
"; + git_footer_html(); +} + +sub git_tree { + if (!defined $hash) { + $hash = git_read_head($project); + if (defined $file_name) { + my $base = $hash_base || $hash; + $hash = git_get_hash_by_path($base, $file_name, "tree"); + } + if (!defined $hash_base) { + $hash_base = $hash; + } + } + $/ = "\0"; + open my $fd, "-|", $GIT, "ls-tree", '-z', $hash + or die_error(undef, "Open git-ls-tree failed."); + my @entries = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading tree failed."); + $/ = "\n"; + + my $refs = read_info_ref(); + my $ref = git_get_referencing($refs, $hash_base); + git_header_html(); + my $base_key = ""; + my $base = ""; + if (defined $hash_base && (my %co = git_read_commit($hash_base))) { + $base_key = ";hb=$hash_base"; + git_page_nav('tree','', $hash_base); + git_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); + } else { + print "
\n"; + print "

\n"; + print "
$hash
\n"; + } + if (defined $file_name) { + $base = esc_html("$file_name/"); + } + git_print_page_path($file_name, 'tree'); + print "
\n"; + print "\n"; + my $alternate = 0; + foreach my $line (@entries) { + #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' + $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; + my $t_mode = $1; + my $t_type = $2; + my $t_hash = $3; + my $t_name = validate_input($4); + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n"; + if ($t_type eq "blob") { + print "\n" . + "\n"; + } elsif ($t_type eq "tree") { + print "\n" . + "\n"; + } + print "\n"; + } + print "
" . mode_str($t_mode) . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") . +# " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, esc_html($t_name)) . + "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash_base;f=$base$t_name")}, "history") . + "
\n" . + "
"; + git_footer_html(); +} + +sub git_log { + my $head = git_read_head($project); + if (!defined $hash) { + $hash = $head; + } + if (!defined $page) { + $page = 0; + } + my $refs = read_info_ref(); + + my $limit = sprintf("--max-count=%i", (100 * ($page+1))); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash + or die_error(undef, "Open git-rev-list failed."); + my @revlist = map { chomp; $_ } <$fd>; + close $fd; + + my $paging_nav = git_get_paging_nav('log', $hash, $head, $page, $#revlist); + + git_header_html(); + git_page_nav('log','', $hash,undef,undef, $paging_nav); + + if (!@revlist) { + my %co = git_read_commit($hash); + + git_header_div('summary', $project); + print "
Last change $co{'age_string'}.

\n"; + } + for (my $i = ($page * 100); $i <= $#revlist; $i++) { + my $commit = $revlist[$i]; + my $ref = git_get_referencing($refs, $commit); + my %co = git_read_commit($commit); + next if !%co; + my %ad = date_str($co{'author_epoch'}); + git_header_div('commit', + "$co{'age_string'}" . + esc_html($co{'title'}) . $ref, + $commit); + print "
\n" . + "
\n" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . + "
\n" . + "
\n" . + "" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]
\n" . + "
\n" . + "
\n"; + my $comment = $co{'comment'}; + my $empty = 0; + foreach my $line (@$comment) { + if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { + next; + } + if ($line eq "") { + if ($empty) { + next; + } + $empty = 1; + } else { + $empty = 0; + } + print format_log_line_html($line) . "
\n"; + } + if (!$empty) { + print "
\n"; + } + print "
\n"; + } + git_footer_html(); +} + +sub git_commit { + my %co = git_read_commit($hash); + if (!%co) { + die_error(undef, "Unknown commit object."); + } + my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); + my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); + + my $parent = $co{'parent'}; + if (!defined $parent) { + $parent = "--root"; + } + open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $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."); + + # non-textual hash id's can be cached + my $expires; + if ($hash =~ m/^[0-9a-fA-F]{40}$/) { + $expires = "+1d"; + } + my $refs = read_info_ref(); + my $ref = git_get_referencing($refs, $co{'id'}); + my $formats_nav = ''; + if (defined $file_name && defined $co{'parent'}) { + my $parent = $co{'parent'}; + $formats_nav .= $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame"); + } + git_header_html(undef, $expires); + git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', + $hash, $co{'tree'}, $hash, + $formats_nav); + + if (defined $co{'parent'}) { + git_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); + } else { + git_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash); + } + print "
\n" . + "\n"; + print "\n". + "" . + "" . + "\n"; + print "\n"; + print "\n"; + print "\n"; + print "" . + "" . + "" . + "" . + "\n"; + my $parents = $co{'parents'}; + foreach my $par (@$parents) { + print "" . + "" . + "" . + "" . + "\n"; + } + print "
author" . esc_html($co{'author'}) . "
$ad{'rfc2822'}"; + if ($ad{'hour_local'} < 6) { + printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); + } else { + printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); + } + print "
committer" . esc_html($co{'committer'}) . "
$cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "
commit$co{'id'}
tree" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) . + "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . + "
parent" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") . + "
". + "
\n"; + print "
\n"; + my $comment = $co{'comment'}; + my $empty = 0; + my $signed = 0; + foreach my $line (@$comment) { + # print only one empty line + if ($line eq "") { + if ($empty || $signed) { + next; + } + $empty = 1; + } else { + $empty = 0; + } + if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { + $signed = 1; + print "" . esc_html($line) . "
\n"; + } else { + $signed = 0; + print format_log_line_html($line) . "
\n"; + } + } + print "
\n"; + print "
\n"; + if ($#difftree > 10) { + print(($#difftree + 1) . " files changed:\n"); + } + print "
\n"; + print "\n"; + my $alternate = 0; + foreach my $line (@difftree) { + # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' + # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' + if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { + next; + } + my $from_mode = $1; + my $to_mode = $2; + my $from_id = $3; + my $to_id = $4; + my $status = $5; + my $similarity = $6; + my $file = validate_input(unquote($7)); + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + if ($status eq "A") { + my $mode_chng = ""; + if (S_ISREG(oct $to_mode)) { + $mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777); + } + print "\n" . + "\n" . + "\n"; + } elsif ($status eq "D") { + print "\n" . + "\n" . + "\n" + } elsif ($status eq "M" || $status eq "T") { + my $mode_chnge = ""; + if ($from_mode != $to_mode) { + $mode_chnge = " [changed"; + if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) { + $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode); + } + if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) { + if (S_ISREG($from_mode) && S_ISREG($to_mode)) { + $mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777); + } elsif (S_ISREG($to_mode)) { + $mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777); + } + } + $mode_chnge .= "]\n"; + } + print "\n" . + "\n" . + "\n"; + } elsif ($status eq "R") { + my ($from_file, $to_file) = split "\t", $file; + my $mode_chng = ""; + if ($from_mode != $to_mode) { + $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777); + } + print "\n" . + "\n" . + "\n"; + } + print "\n"; + } + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "[new " . file_type($to_mode) . "$mode_chng]" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "[deleted " . file_type($from_mode). "]" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . + ""; + if ($to_id ne $from_id) { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)); + } else { + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)); + } + print "$mode_chnge"; + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob"); + if ($to_id ne $from_id) { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff"); + } + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n"; + print "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "[moved from " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) . + " with " . (int $similarity) . "% similarity$mode_chng]" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob"); + if ($to_id ne $from_id) { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file")}, "diff"); + } + print "
\n"; + git_footer_html(); +} + +sub git_blobdiff { + mkdir($git_temp, 0700); + git_header_html(); + if (defined $hash_base && (my %co = git_read_commit($hash_base))) { + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); + git_header_div('commit', esc_html($co{'title'}), $hash_base); + } else { + print "
\n" . + "

\n" . + "
$hash vs $hash_parent
\n"; + } + git_print_page_path($file_name, "blob"); + print "
\n" . + "
blob:" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) . + " -> blob:" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, $hash) . + "
\n"; + git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash); + print "
"; + git_footer_html(); +} + +sub git_blobdiff_plain { + mkdir($git_temp, 0700); + print $cgi->header(-type => "text/plain", -charset => 'utf-8'); + git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash, "plain"); +} + +sub git_commitdiff { + mkdir($git_temp, 0700); + my %co = git_read_commit($hash); + if (!%co) { + die_error(undef, "Unknown commit object."); + } + if (!defined $hash_parent) { + $hash_parent = $co{'parent'}; + } + open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash + or die_error(undef, "Open git-diff-tree failed."); + my @difftree = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading diff-tree failed."); + + # non-textual hash id's can be cached + my $expires; + if ($hash =~ m/^[0-9a-fA-F]{40}$/) { + $expires = "+1d"; + } + my $refs = read_info_ref(); + my $ref = git_get_referencing($refs, $co{'id'}); + my $formats_nav = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain"); + git_header_html(undef, $expires); + git_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); + git_header_div('commit', esc_html($co{'title'}) . $ref, $hash); + print "
\n"; + my $comment = $co{'comment'}; + my $empty = 0; + my $signed = 0; + my @log = @$comment; + # remove first and empty lines after that + shift @log; + while (defined $log[0] && $log[0] eq "") { + shift @log; + } + foreach my $line (@log) { + if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) { + next; + } + if ($line eq "") { + if ($empty) { + next; + } + $empty = 1; + } else { + $empty = 0; + } + print format_log_line_html($line) . "
\n"; + } + print "
\n"; + foreach my $line (@difftree) { + # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' + # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' + $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; + my $from_mode = $1; + my $to_mode = $2; + my $from_id = $3; + my $to_id = $4; + my $status = $5; + my $file = validate_input(unquote($6)); + if ($status eq "A") { + print "
" . file_type($to_mode) . ":" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" . + "
\n"; + git_diff_print(undef, "/dev/null", $to_id, "b/$file"); + } elsif ($status eq "D") { + print "
" . file_type($from_mode) . ":" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" . + "
\n"; + git_diff_print($from_id, "a/$file", undef, "/dev/null"); + } elsif ($status eq "M") { + if ($from_id ne $to_id) { + print "
" . + file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . + " -> " . + file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id); + print "
\n"; + git_diff_print($from_id, "a/$file", $to_id, "b/$file"); + } + } + } + print "
\n" . + "
"; + git_footer_html(); +} + +sub git_commitdiff_plain { + mkdir($git_temp, 0700); + open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash + or die_error(undef, "Open git-diff-tree failed."); + my @difftree = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading diff-tree failed."); + + # try to figure out the next tag after this commit + my $tagname; + my $refs = read_info_ref("tags"); + open $fd, "-|", $GIT, "rev-list", "HEAD"; + my @commits = map { chomp; $_ } <$fd>; + close $fd; + foreach my $commit (@commits) { + if (defined $refs->{$commit}) { + $tagname = $refs->{$commit} + } + if ($commit eq $hash) { + last; + } + } + + print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\""); + my %co = git_read_commit($hash); + my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); + my $comment = $co{'comment'}; + print "From: $co{'author'}\n" . + "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n". + "Subject: $co{'title'}\n"; + if (defined $tagname) { + print "X-Git-Tag: $tagname\n"; + } + print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" . + "\n"; + + foreach my $line (@$comment) {; + print "$line\n"; + } + print "---\n\n"; + + foreach my $line (@difftree) { + $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; + my $from_id = $3; + my $to_id = $4; + my $status = $5; + my $file = $6; + if ($status eq "A") { + git_diff_print(undef, "/dev/null", $to_id, "b/$file", "plain"); + } elsif ($status eq "D") { + git_diff_print($from_id, "a/$file", undef, "/dev/null", "plain"); + } elsif ($status eq "M") { + git_diff_print($from_id, "a/$file", $to_id, "b/$file", "plain"); + } + } +} + +sub git_history { + if (!defined $hash_base) { + $hash_base = git_read_head($project); + } + my $ftype; + my %co = git_read_commit($hash_base); + if (!%co) { + die_error(undef, "Unknown commit object."); + } + my $refs = read_info_ref(); + git_header_html(); + git_page_nav('','', $hash_base,$co{'tree'},$hash_base); + git_header_div('commit', esc_html($co{'title'}), $hash_base); + if (!defined $hash && defined $file_name) { + $hash = git_get_hash_by_path($hash_base, $file_name); + } + if (defined $hash) { + $ftype = git_get_type($hash); + } + git_print_page_path($file_name, $ftype); + + open my $fd, "-|", + $GIT, "rev-list", "--full-history", $hash_base, "--", $file_name; + print "\n"; + my $alternate = 0; + while (my $line = <$fd>) { + if ($line =~ m/^([0-9a-fA-F]{40})/){ + my $commit = $1; + my %co = git_read_commit($commit); + if (!%co) { + next; + } + my $ref = git_get_referencing($refs, $commit); + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + } + print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, "" . + esc_html(chop_str($co{'title'}, 50)) . "$ref") . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$ftype;hb=$commit;f=$file_name")}, $ftype); + my $blob = git_get_hash_by_path($hash_base, $file_name); + my $blob_parent = git_get_hash_by_path($commit, $file_name); + if (defined $blob && defined $blob_parent && $blob ne $blob_parent) { + print " | " . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name")}, + "diff to current"); + } + print "
\n"; + close $fd; + git_footer_html(); +} + +sub git_search { + if (!defined $searchtext) { + die_error("", "Text field empty."); + } + if (!defined $hash) { + $hash = git_read_head($project); + } + my %co = git_read_commit($hash); + if (!%co) { + die_error(undef, "Unknown commit object."); + } + # 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 $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; + } + git_header_html(); + git_page_nav('','', $hash,$co{'tree'},$hash); + git_header_div('commit', esc_html($co{'title'}), $hash); + + print "\n"; + my $alternate = 0; + if ($commit_search) { + $/ = "\0"; + open my $fd, "-|", $GIT, "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) { + next; + } + if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) { + next; + } + my @commit_lines = split "\n", $commit_text; + my %co = git_read_commit(undef, \@commit_lines); + if (!%co) { + next; + } + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + close $fd; + } + + if ($pickaxe_search) { + $/ = "\n"; + open my $fd, "-|", "$GIT rev-list $hash | $GIT diff-tree -r --stdin -S\'$searchtext\'"; + undef %co; + my @files; + while (my $line = <$fd>) { + if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { + my %set; + $set{'file'} = $6; + $set{'from_id'} = $3; + $set{'to_id'} = $4; + $set{'id'} = $set{'to_id'}; + if ($set{'id'} =~ m/0{40}/) { + $set{'id'} = $set{'from_id'}; + } + if ($set{'id'} =~ m/0{40}/) { + next; + } + push @files, \%set; + } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){ + if (%co) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + %co = git_read_commit($1); + } + } + close $fd; + } + print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "" . esc_html(chop_str($co{'title'}, 50)) . "
"); + my $comment = $co{'comment'}; + foreach my $line (@$comment) { + if ($line =~ m/^(.*)($searchtext)(.*)$/i) { + my $lead = esc_html($1) || ""; + $lead = chop_str($lead, 30, 10); + my $match = esc_html($2) || ""; + my $trail = esc_html($3) || ""; + $trail = chop_str($trail, 30, 10); + my $text = "$lead$match$trail"; + print chop_str($text, 80, 5) . "
\n"; + } + } + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree"); + print "
$co{'age_string_date'}" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "" . + esc_html(chop_str($co{'title'}, 50)) . "
"); + while (my $setref = shift @files) { + my %set = %$setref; + print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}"), class => "list"}, + "" . esc_html($set{'file'}) . "") . + "
\n"; + } + print "
" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree"); + print "
\n"; + git_footer_html(); +} + +sub git_shortlog { + my $head = git_read_head($project); + if (!defined $hash) { + $hash = $head; + } + if (!defined $page) { + $page = 0; + } + my $refs = read_info_ref(); + + my $limit = sprintf("--max-count=%i", (100 * ($page+1))); + open my $fd, "-|", $GIT, "rev-list", $limit, $hash + or die_error(undef, "Open git-rev-list failed."); + my @revlist = map { chomp; $_ } <$fd>; + close $fd; + + my $paging_nav = git_get_paging_nav('shortlog', $hash, $head, $page, $#revlist); + my $next_link = ''; + if ($#revlist >= (100 * ($page+1)-1)) { + $next_link = + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), + -title => "Alt-n"}, "next"); + } + + + git_header_html(); + git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); + git_header_div('summary', $project); + + git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link); + + git_footer_html(); +} + +## ...................................................................... +## feeds (RSS, OPML) + +sub git_rss { + # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ + open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) + or die_error(undef, "Open git-rev-list failed."); + my @revlist = map { chomp; $_ } <$fd>; + close $fd or die_error(undef, "Reading rev-list failed."); + print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); + print "\n". + "\n"; + print "\n"; + print "$project\n". + "" . esc_html("$my_url?p=$project;a=summary") . "\n". + "$project log\n". + "en\n"; + + for (my $i = 0; $i <= $#revlist; $i++) { + my $commit = $revlist[$i]; + my %co = git_read_commit($commit); + # we read 150, we always show 30 and the ones more recent than 48 hours + if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) { + last; + } + my %cd = date_str($co{'committer_epoch'}); + open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next; + my @difftree = map { chomp; $_ } <$fd>; + close $fd or next; + print "\n" . + "" . + sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) . + "\n" . + "" . esc_html($co{'author'}) . "\n" . + "$cd{'rfc2822'}\n" . + "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . + "" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "\n" . + "" . esc_html($co{'title'}) . "\n" . + "" . + "\n"; + } + print "
\n"; + foreach my $line (@difftree) { + if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { + next; + } + my $file = validate_input(unquote($7)); + $file = decode("utf8", $file, Encode::FB_DEFAULT); + print "$file
\n"; + } + print "]]>\n" . + "
\n" . + "
\n"; + } + print "
"; +} + +sub git_opml { + my @list = git_read_projects(); + + print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); + print "\n". + "\n". + "". + " $site_name Git OPML Export\n". + "\n". + "\n". + "\n"; + + foreach my $pr (@list) { + my %proj = %$pr; + my $head = git_read_head($proj{'path'}); + if (!defined $head) { + next; + } + $ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}"; + my %co = git_read_commit($head); + if (!%co) { + next; + } + + my $path = esc_html(chop_str($proj{'path'}, 25, 5)); + my $rss = "$my_url?p=$proj{'path'};a=rss"; + my $html = "$my_url?p=$proj{'path'};a=summary"; + print "\n"; + } + print "\n". + "\n". + "\n"; +} -- cgit v1.2.1 From 281f2f6b45bf62e5bc81a6b457f1f7d26c426ea3 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Mon, 31 Jul 2006 00:38:39 +0200 Subject: gitweb: use out-of-line GIT logo. Use the normal web server instead of the CGI to provide the git logo, just like the gitweb.css. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- Makefile | 2 ++ gitweb/README | 2 ++ gitweb/git-logo.png | Bin 0 -> 208 bytes gitweb/gitweb.perl | 29 ++++------------------------- 4 files changed, 8 insertions(+), 25 deletions(-) create mode 100644 gitweb/git-logo.png diff --git a/Makefile b/Makefile index fb2b28868..ac5db1ef4 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,7 @@ GITWEB_PROJECTROOT = /pub/git GITWEB_LIST = GITWEB_HOMETEXT = indextext.html GITWEB_CSS = gitweb.css +GITWEB_LOGO = git-logo.png export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR @@ -589,6 +590,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl -e 's|@@GITWEB_LIST@@|$(GITWEB_LIST)|g' \ -e 's|@@GITWEB_HOMETEXT@@|$(GITWEB_HOMETEXT)|g' \ -e 's|@@GITWEB_CSS@@|$(GITWEB_CSS)|g' \ + -e 's|@@GITWEB_LOGO@@|$(GITWEB_LOGO)|g' \ $< >$@+ chmod +x $@+ mv $@+ $@ diff --git a/gitweb/README b/gitweb/README index ed939e2fb..1b2180c73 100644 --- a/gitweb/README +++ b/gitweb/README @@ -21,6 +21,8 @@ You can specify the following configuration variables when building GIT: overview page. * GITWEB_CSS Points to the location where you put gitweb.css on your web server. + * GITWEB_LOGO + Points to the location where you put git-logo.png on your web server. Any comment/question/concern to: Kay Sievers diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png new file mode 100644 index 000000000..16ae8d538 Binary files /dev/null and b/gitweb/git-logo.png differ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6e4261d5f..1db1414a0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -52,6 +52,8 @@ our $home_text = "@@GITWEB_HOMETEXT@@"; # URI of default stylesheet our $stylesheet = "@@GITWEB_CSS@@"; +# URI of GIT logo +our $logo = "@@GITWEB_LOGO@@"; # source of projects list our $projects_list = "@@GITWEB_LIST@@" || "$projectroot"; @@ -71,10 +73,7 @@ if (defined $action) { undef $action; die_error(undef, "Invalid action parameter."); } - if ($action eq "git-logo.png") { - git_logo(); - exit; - } elsif ($action eq "opml") { + if ($action eq "opml") { git_opml(); exit; } @@ -873,7 +872,7 @@ $rss_link EOF print "
\n" . "" . - "\"git\"" . + "\"git\"" . "\n"; print $cgi->a({-href => esc_param($home_link)}, "projects") . " / "; if (defined $project) { @@ -1267,26 +1266,6 @@ sub git_diff_print { ## ====================================================================== ## actions -# git-logo (cached in browser for one day) -sub git_logo { - binmode STDOUT, ':raw'; - print $cgi->header(-type => 'image/png', -expires => '+1d'); - # cat git-logo.png | hexdump -e '16/1 " %02x" "\n"' | sed 's/ /\\x/g' - print "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" . - "\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" . - "\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" . - "\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" . - "\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" . - "\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" . - "\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" . - "\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" . - "\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" . - "\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" . - "\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" . - "\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" . - "\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"; -} - sub git_project_list { my $order = $cgi->param('o'); if (defined $order && $order !~ m/project|descr|owner|age/) { -- cgit v1.2.1 From ee0d3234b1e4e69819635d4fc9e9c37933c0ea82 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 1 Aug 2006 15:56:28 -0700 Subject: gitweb/README: do not bug Kay with gitweb questions anymore Signed-off-by: Junio C Hamano --- gitweb/README | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gitweb/README b/gitweb/README index 1b2180c73..b91d42a90 100644 --- a/gitweb/README +++ b/gitweb/README @@ -24,5 +24,9 @@ You can specify the following configuration variables when building GIT: * GITWEB_LOGO Points to the location where you put git-logo.png on your web server. -Any comment/question/concern to: +Originally written by: Kay Sievers + +Any comment/question/concern to: + Git mailing list + -- cgit v1.2.1 From 22987ae0bc354bb2277c068ce15e9887f7cf0626 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 1 Aug 2006 16:23:47 -0700 Subject: Makefile: gitweb/gitweb.cgi is now generated. Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index ac5db1ef4..6624c7501 100644 --- a/Makefile +++ b/Makefile @@ -804,6 +804,7 @@ clean: rm -rf $(GIT_TARNAME) .doc-tmp-dir rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz rm -f $(htmldocs).tar.gz $(manpages).tar.gz + rm -f gitweb/gitweb.cgi $(MAKE) -C Documentation/ clean $(MAKE) -C templates clean $(MAKE) -C t/ clean -- cgit v1.2.1 From c8d138a8c004ebce6ef840cfcc7c47227c2d16ba Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 2 Aug 2006 15:23:34 -0400 Subject: gitweb: optionally read config from GITWEB_CONFIG Configuration will first be taken from variables inside the gitweb.cgi script, which in turn come from the Makefile. Afterwards, the contents of GITWEB_CONFIG are read, overriding the builtin defaults. This should eliminate the need for editing the gitweb script at all. Users should edit the Makefile and/or add a config file. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Makefile | 2 ++ gitweb/gitweb.perl | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 6624c7501..a2b4acaaa 100644 --- a/Makefile +++ b/Makefile @@ -117,6 +117,7 @@ GIT_PYTHON_DIR = $(prefix)/share/git-core/python # DESTDIR= # default configuration for gitweb +GITWEB_CONFIG = gitweb_config.perl GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git GITWEB_LIST = @@ -585,6 +586,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e 's|@@GIT_VERSION@@|$(GIT_VERSION)|g' \ -e 's|@@GIT_BINDIR@@|$(bindir)|g' \ + -e 's|@@GITWEB_CONFIG@@|$(GITWEB_CONFIG)|g' \ -e 's|@@GITWEB_SITENAME@@|$(GITWEB_SITENAME)|g' \ -e 's|@@GITWEB_PROJECTROOT@@|$(GITWEB_PROJECTROOT)|g' \ -e 's|@@GITWEB_LIST@@|$(GITWEB_LIST)|g' \ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1db1414a0..d5b2de8b3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -31,14 +31,8 @@ our $GIT = "@@GIT_BINDIR@@/git"; #our $projectroot = "/pub/scm"; our $projectroot = "@@GITWEB_PROJECTROOT@@"; -# version of the core git binary -our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; - # location for temporary files needed for diffs our $git_temp = "/tmp/gitweb"; -if (! -d $git_temp) { - mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); -} # target of the home link on top of all pages our $home_link = $my_uri; @@ -56,7 +50,7 @@ our $stylesheet = "@@GITWEB_CSS@@"; our $logo = "@@GITWEB_LOGO@@"; # source of projects list -our $projects_list = "@@GITWEB_LIST@@" || "$projectroot"; +our $projects_list = "@@GITWEB_LIST@@"; # default blob_plain mimetype and default charset for text/plain blob our $default_blob_plain_mimetype = 'text/plain'; @@ -66,6 +60,17 @@ our $default_text_plain_charset = undef; # (relative to the current git repository) our $mimetypes_file = undef; +our $GITWEB_CONFIG = "@@GITWEB_CONFIG@@"; +require $GITWEB_CONFIG if -e $GITWEB_CONFIG; + +# version of the core git binary +our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; + +$projects_list ||= $projectroot; +if (! -d $git_temp) { + mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); +} + # input validation and dispatch our $action = $cgi->param('a'); if (defined $action) { -- cgit v1.2.1 From bb55f77fcd0748e0faf152a565eef65b2066cfb4 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Wed, 2 Aug 2006 22:29:36 +0200 Subject: gitweb: require $ENV{'GITWEB_CONFIG'} With this patch it is possible to use gitweb.perl for developing by loading the configuration from $GITWEB_CONFIG. This might also be useful for normal usage of gitweb. Example: % cat cfg $GIT = '/usr/bin/git'; $projectroot = '/home/matled/src/git'; $projects_list = '/home/matled/src/git/git/gitweb/list'; % cat run #!/bin/sh export GATEWAY_INTERFACE="CGI/1.1" export HTTP_ACCEPT="*/*" export REQUEST_METHOD="GET" export GITWEB_CONFIG='./cfg' export QUERY_STRING=""$1"" exec ./gitweb.perl % time ./run p=git/.git > /dev/null This makes it easy to check for warnings and do performance tests after changes, you can also pipe this to lynx -dump -force-html /dev/stdin to get more than just html. This also documents the original patch adding require $GITWEB_CONFIG. Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- gitweb/README | 5 +++++ gitweb/gitweb.perl | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gitweb/README b/gitweb/README index b91d42a90..27c6dac14 100644 --- a/gitweb/README +++ b/gitweb/README @@ -23,6 +23,11 @@ You can specify the following configuration variables when building GIT: Points to the location where you put gitweb.css on your web server. * GITWEB_LOGO Points to the location where you put git-logo.png on your web server. + * GITWEB_CONFIG + This file will be loaded using 'require'. If the environment + $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the + environment variable will be loaded instead of the file + specified when gitweb.cgi was created. Originally written by: Kay Sievers diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d5b2de8b3..b5548ec8f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -60,7 +60,7 @@ our $default_text_plain_charset = undef; # (relative to the current git repository) our $mimetypes_file = undef; -our $GITWEB_CONFIG = "@@GITWEB_CONFIG@@"; +our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "@@GITWEB_CONFIG@@"; require $GITWEB_CONFIG if -e $GITWEB_CONFIG; # version of the core git binary -- cgit v1.2.1 From 06c084d28b150ac1e2ed0c6f3ce7db6ca42dfc67 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 2 Aug 2006 13:50:20 -0700 Subject: gitweb: do not use @@FOO@@ for replaced tokens This makes it easier to run gitweb/gitweb.perl without token substitution. Using @@ makes Perl emit "unintended interpolation" warnings. Signed-off-by: Junio C Hamano --- Makefile | 18 +++++++++--------- gitweb/gitweb.perl | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index a2b4acaaa..3816ef797 100644 --- a/Makefile +++ b/Makefile @@ -584,15 +584,15 @@ git-status: git-commit gitweb/gitweb.cgi: gitweb/gitweb.perl rm -f $@ $@+ sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ - -e 's|@@GIT_VERSION@@|$(GIT_VERSION)|g' \ - -e 's|@@GIT_BINDIR@@|$(bindir)|g' \ - -e 's|@@GITWEB_CONFIG@@|$(GITWEB_CONFIG)|g' \ - -e 's|@@GITWEB_SITENAME@@|$(GITWEB_SITENAME)|g' \ - -e 's|@@GITWEB_PROJECTROOT@@|$(GITWEB_PROJECTROOT)|g' \ - -e 's|@@GITWEB_LIST@@|$(GITWEB_LIST)|g' \ - -e 's|@@GITWEB_HOMETEXT@@|$(GITWEB_HOMETEXT)|g' \ - -e 's|@@GITWEB_CSS@@|$(GITWEB_CSS)|g' \ - -e 's|@@GITWEB_LOGO@@|$(GITWEB_LOGO)|g' \ + -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \ + -e 's|++GIT_BINDIR++|$(bindir)|g' \ + -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \ + -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ + -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ + -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \ + -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \ + -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \ + -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \ $< >$@+ chmod +x $@+ mv $@+ $@ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b5548ec8f..58eb5b190 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -18,18 +18,18 @@ use File::Find qw(); binmode STDOUT, ':utf8'; our $cgi = new CGI; -our $version = "@@GIT_VERSION@@"; +our $version = "++GIT_VERSION++"; our $my_url = $cgi->url(); our $my_uri = $cgi->url(-absolute => 1); our $rss_link = ""; # core git executable to use # this can just be "git" if your webserver has a sensible PATH -our $GIT = "@@GIT_BINDIR@@/git"; +our $GIT = "++GIT_BINDIR++/git"; # absolute fs-path which will be prepended to the project path #our $projectroot = "/pub/scm"; -our $projectroot = "@@GITWEB_PROJECTROOT@@"; +our $projectroot = "++GITWEB_PROJECTROOT++"; # location for temporary files needed for diffs our $git_temp = "/tmp/gitweb"; @@ -39,18 +39,18 @@ our $home_link = $my_uri; # 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"; # html text to include at home page -our $home_text = "@@GITWEB_HOMETEXT@@"; +our $home_text = "++GITWEB_HOMETEXT++"; # URI of default stylesheet -our $stylesheet = "@@GITWEB_CSS@@"; +our $stylesheet = "++GITWEB_CSS++"; # URI of GIT logo -our $logo = "@@GITWEB_LOGO@@"; +our $logo = "++GITWEB_LOGO++"; # source of projects list -our $projects_list = "@@GITWEB_LIST@@"; +our $projects_list = "++GITWEB_LIST++"; # default blob_plain mimetype and default charset for text/plain blob our $default_blob_plain_mimetype = 'text/plain'; @@ -60,7 +60,7 @@ our $default_text_plain_charset = undef; # (relative to the current git repository) our $mimetypes_file = undef; -our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "@@GITWEB_CONFIG@@"; +our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; require $GITWEB_CONFIG if -e $GITWEB_CONFIG; # version of the core git binary -- cgit v1.2.1 From f9f02d012927cba2264b948d16008ede025f7c71 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 4 Aug 2006 15:14:27 -0700 Subject: gitweb: git_tree displays blame based on repository config git_tree() will now conditionally display "blame" depending on how "gitweb.blame" variable is configured using "git-repo-config". Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 58eb5b190..b268b6344 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1757,6 +1757,7 @@ sub git_tree { git_header_html(); my $base_key = ""; my $base = ""; + my $have_blame = git_get_project_config_bool ('blame'); if (defined $hash_base && (my %co = git_read_commit($hash_base))) { $base_key = ";hb=$hash_base"; git_page_nav('tree','', $hash_base); @@ -1792,9 +1793,11 @@ sub git_tree { $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) . "\n" . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") . -# " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob"); + if ($have_blame) { + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame"); + } + print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") . " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") . "\n"; } elsif ($t_type eq "tree") { -- cgit v1.2.1 From 154b4d78cf35025268ed74c45f182b25ebaf4acc Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 12:55:20 +0200 Subject: gitweb: Separate input validation and dispatch, add comment about opml action Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b268b6344..7f4387fde 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -71,6 +71,7 @@ if (! -d $git_temp) { mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); } +# ====================================================================== # input validation and dispatch our $action = $cgi->param('a'); if (defined $action) { @@ -78,6 +79,7 @@ if (defined $action) { undef $action; die_error(undef, "Invalid action parameter."); } + # action which does not check rest of parameters if ($action eq "opml") { git_opml(); exit; -- cgit v1.2.1 From cfd826693688245c6d8f48a5fdc1f6780ecca65b Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 12:56:04 +0200 Subject: gitweb: die_error first (optional) parameter is HTTP status Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7f4387fde..040024ccf 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -68,7 +68,7 @@ our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; $projects_list ||= $projectroot; if (! -d $git_temp) { - mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp"); + mkdir($git_temp, 0700) || die_error(undef, "Couldn't mkdir $git_temp"); } # ====================================================================== @@ -1658,7 +1658,7 @@ sub git_blob_plain { } my $type = shift; open my $fd, "-|", $GIT, "cat-file", "blob", $hash - or die_error("Couldn't cat $file_name, $hash"); + or die_error(undef, "Couldn't cat $file_name, $hash"); $type ||= git_blob_plain_mimetype($fd, $file_name); -- cgit v1.2.1 From 623e4aeb42c313360d2c1edba7f844908882c2a1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 12:56:42 +0200 Subject: gitweb: Use undef for die_error to use default first (status) parameter value Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 040024ccf..c11c2f2b7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2323,7 +2323,7 @@ sub git_history { sub git_search { if (!defined $searchtext) { - die_error("", "Text field empty."); + die_error(undef, "Text field empty."); } if (!defined $hash) { $hash = git_read_head($project); -- cgit v1.2.1 From dbd954a8969a31741a676e9e7cc2dfd00f8f4e93 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 12:58:06 +0200 Subject: gitweb: Don't undefine query parameter related variables before die_error Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c11c2f2b7..89ceb9765 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -76,7 +76,6 @@ if (! -d $git_temp) { our $action = $cgi->param('a'); if (defined $action) { if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { - undef $action; die_error(undef, "Invalid action parameter."); } # action which does not check rest of parameters @@ -89,16 +88,13 @@ if (defined $action) { our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); if (defined $project) { $project =~ s|^/||; $project =~ s|/$||; - $project = validate_input($project); - if (!defined($project)) { + if (!validate_input($project)) { die_error(undef, "Invalid project parameter."); } if (!(-d "$projectroot/$project")) { - undef $project; die_error(undef, "No such directory."); } if (!(-e "$projectroot/$project/HEAD")) { - undef $project; die_error(undef, "No such project."); } $rss_link = "param('f'); if (defined $file_name) { - $file_name = validate_input($file_name); - if (!defined($file_name)) { + if (!validate_input($file_name)) { die_error(undef, "Invalid file parameter."); } } our $hash = $cgi->param('h'); if (defined $hash) { - $hash = validate_input($hash); - if (!defined($hash)) { + if (!validate_input($hash)) { die_error(undef, "Invalid hash parameter."); } } our $hash_parent = $cgi->param('hp'); if (defined $hash_parent) { - $hash_parent = validate_input($hash_parent); - if (!defined($hash_parent)) { + if (!validate_input($hash_parent)) { die_error(undef, "Invalid hash parent parameter."); } } our $hash_base = $cgi->param('hb'); if (defined $hash_base) { - $hash_base = validate_input($hash_base); - if (!defined($hash_base)) { + if (!validate_input($hash_base)) { die_error(undef, "Invalid hash base parameter."); } } @@ -144,7 +136,6 @@ if (defined $hash_base) { our $page = $cgi->param('pg'); if (defined $page) { if ($page =~ m/[^0-9]$/) { - undef $page; die_error(undef, "Invalid page parameter."); } } @@ -152,7 +143,6 @@ if (defined $page) { our $searchtext = $cgi->param('s'); if (defined $searchtext) { if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) { - undef $searchtext; die_error(undef, "Invalid search parameter."); } $searchtext = quotemeta $searchtext; @@ -182,7 +172,6 @@ my %actions = ( $action = 'summary' if (!defined($action)); if (!defined($actions{$action})) { - undef $action; die_error(undef, "Unknown action."); } $actions{$action}->(); -- cgit v1.2.1 From e484a2d6ad26ff7a6ebe84085dda00591476014e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 13:12:51 +0200 Subject: gitweb: Cleanup and uniquify error messages Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 89ceb9765..16e838fc6 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1464,7 +1464,7 @@ sub git_blame2 { die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame')); die_error('404 Not Found', "File name not defined") if (!$file_name); $hash_base ||= git_read_head($project); - die_error(undef, "Reading commit failed") unless ($hash_base); + die_error(undef, "Couldn't find base commit.") unless ($hash_base); my %co = git_read_commit($hash_base) or die_error(undef, "Reading commit failed"); if (!defined $hash) { @@ -1473,7 +1473,7 @@ sub git_blame2 { } $ftype = git_get_type($hash); if ($ftype !~ "blob") { - die_error("400 Bad Request", "object is not a blob"); + die_error("400 Bad Request", "Object is not a blob"); } open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) or die_error(undef, "Open git-blame failed."); @@ -1520,9 +1520,9 @@ sub git_blame2 { sub git_blame { my $fd; die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame')); - die_error('404 Not Found', "What file will it be, master?") if (!$file_name); + die_error('404 Not Found', "File name not defined.") if (!$file_name); $hash_base ||= git_read_head($project); - die_error(undef, "Reading commit failed.") unless ($hash_base); + die_error(undef, "Couldn't find base commit.") unless ($hash_base); my %co = git_read_commit($hash_base) or die_error(undef, "Reading commit failed."); if (!defined $hash) { @@ -2116,7 +2116,7 @@ sub git_commitdiff { open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash or die_error(undef, "Open git-diff-tree failed."); my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed."); + close $fd or die_error(undef, "Reading git-diff-tree failed."); # non-textual hash id's can be cached my $expires; @@ -2487,7 +2487,7 @@ sub git_rss { open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) or die_error(undef, "Open git-rev-list failed."); my @revlist = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading rev-list failed."); + close $fd or die_error(undef, "Reading git-rev-list failed."); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); print "\n". "\n"; -- cgit v1.2.1 From cac4bd94fb6d5f3066614051827669476a9f0b56 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 13:13:53 +0200 Subject: gitweb: No periods for error messages Signed-off-by: Jakub Narebski Acked-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 92 +++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 16e838fc6..628490145 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -76,7 +76,7 @@ if (! -d $git_temp) { our $action = $cgi->param('a'); if (defined $action) { if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { - die_error(undef, "Invalid action parameter."); + die_error(undef, "Invalid action parameter"); } # action which does not check rest of parameters if ($action eq "opml") { @@ -89,13 +89,13 @@ our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); if (defined $project) { $project =~ s|^/||; $project =~ s|/$||; if (!validate_input($project)) { - die_error(undef, "Invalid project parameter."); + die_error(undef, "Invalid project parameter"); } if (!(-d "$projectroot/$project")) { - die_error(undef, "No such directory."); + die_error(undef, "No such directory"); } if (!(-e "$projectroot/$project/HEAD")) { - die_error(undef, "No such project."); + die_error(undef, "No such project"); } $rss_link = ""; @@ -108,42 +108,42 @@ if (defined $project) { our $file_name = $cgi->param('f'); if (defined $file_name) { if (!validate_input($file_name)) { - die_error(undef, "Invalid file parameter."); + die_error(undef, "Invalid file parameter"); } } our $hash = $cgi->param('h'); if (defined $hash) { if (!validate_input($hash)) { - die_error(undef, "Invalid hash parameter."); + die_error(undef, "Invalid hash parameter"); } } our $hash_parent = $cgi->param('hp'); if (defined $hash_parent) { if (!validate_input($hash_parent)) { - die_error(undef, "Invalid hash parent parameter."); + die_error(undef, "Invalid hash parent parameter"); } } our $hash_base = $cgi->param('hb'); if (defined $hash_base) { if (!validate_input($hash_base)) { - die_error(undef, "Invalid hash base parameter."); + die_error(undef, "Invalid hash base parameter"); } } our $page = $cgi->param('pg'); if (defined $page) { if ($page =~ m/[^0-9]$/) { - die_error(undef, "Invalid page parameter."); + die_error(undef, "Invalid page parameter"); } } our $searchtext = $cgi->param('s'); if (defined $searchtext) { if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) { - die_error(undef, "Invalid search parameter."); + die_error(undef, "Invalid search parameter"); } $searchtext = quotemeta $searchtext; } @@ -172,7 +172,7 @@ my %actions = ( $action = 'summary' if (!defined($action)); if (!defined($actions{$action})) { - die_error(undef, "Unknown action."); + die_error(undef, "Unknown action"); } $actions{$action}->(); exit; @@ -418,7 +418,7 @@ sub git_get_hash_by_path { my $tree = $base; open my $fd, "-|", $GIT, "ls-tree", $base, "--", $path - or die_error(undef, "Open git-ls-tree failed."); + or die_error(undef, "Open git-ls-tree failed"); my $line = <$fd>; close $fd or return undef; @@ -1265,13 +1265,13 @@ sub git_diff_print { sub git_project_list { my $order = $cgi->param('o'); if (defined $order && $order !~ m/project|descr|owner|age/) { - die_error(undef, "Invalid order parameter '$order'."); + die_error(undef, "Invalid order parameter '$order'"); } my @list = git_read_projects(); my @projects; if (!@list) { - die_error(undef, "No projects found."); + die_error(undef, "No projects found"); } foreach my $pr (@list) { my $head = git_read_head($pr->{'path'}); @@ -1405,7 +1405,7 @@ sub git_summary { "\n"; open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project) - or die_error(undef, "Open git-rev-list failed."); + or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; git_header_div('shortlog'); @@ -1461,10 +1461,10 @@ sub git_tag { sub git_blame2 { my $fd; my $ftype; - die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame')); + die_error(undef, "Permission denied") if (!git_get_project_config_bool ('blame')); die_error('404 Not Found', "File name not defined") if (!$file_name); $hash_base ||= git_read_head($project); - die_error(undef, "Couldn't find base commit.") unless ($hash_base); + die_error(undef, "Couldn't find base commit") unless ($hash_base); my %co = git_read_commit($hash_base) or die_error(undef, "Reading commit failed"); if (!defined $hash) { @@ -1476,7 +1476,7 @@ sub git_blame2 { die_error("400 Bad Request", "Object is not a blob"); } open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base) - or die_error(undef, "Open git-blame failed."); + or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . @@ -1519,18 +1519,18 @@ sub git_blame2 { sub git_blame { my $fd; - die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame')); - die_error('404 Not Found', "File name not defined.") if (!$file_name); + die_error('403 Permission denied', "Permission denied") if (!git_get_project_config_bool ('blame')); + die_error('404 Not Found', "File name not defined") if (!$file_name); $hash_base ||= git_read_head($project); - die_error(undef, "Couldn't find base commit.") unless ($hash_base); + die_error(undef, "Couldn't find base commit") unless ($hash_base); my %co = git_read_commit($hash_base) - or die_error(undef, "Reading commit failed."); + or die_error(undef, "Reading commit failed"); if (!defined $hash) { $hash = git_get_hash_by_path($hash_base, $file_name, "blob") - or die_error(undef, "Error lookup file."); + or die_error(undef, "Error lookup file"); } open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base) - or die_error(undef, "Open git-annotate failed."); + or die_error(undef, "Open git-annotate failed"); git_header_html(); my $formats_nav = $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") . @@ -1640,9 +1640,9 @@ sub git_blob_plain { if (defined $file_name) { my $base = $hash_base || git_read_head($project); $hash = git_get_hash_by_path($base, $file_name, "blob") - or die_error(undef, "Error lookup file."); + or die_error(undef, "Error lookup file"); } else { - die_error(undef, "No file name defined."); + die_error(undef, "No file name defined"); } } my $type = shift; @@ -1673,14 +1673,14 @@ sub git_blob { if (defined $file_name) { my $base = $hash_base || git_read_head($project); $hash = git_get_hash_by_path($base, $file_name, "blob") - or die_error(undef, "Error lookup file."); + or die_error(undef, "Error lookup file"); } else { - die_error(undef, "No file name defined."); + die_error(undef, "No file name defined"); } } my $have_blame = git_get_project_config_bool ('blame'); open my $fd, "-|", $GIT, "cat-file", "blob", $hash - or die_error(undef, "Couldn't cat $file_name, $hash."); + or die_error(undef, "Couldn't cat $file_name, $hash"); my $mimetype = git_blob_plain_mimetype($fd, $file_name); if ($mimetype !~ m/^text\//) { close $fd; @@ -1738,9 +1738,9 @@ sub git_tree { } $/ = "\0"; open my $fd, "-|", $GIT, "ls-tree", '-z', $hash - or die_error(undef, "Open git-ls-tree failed."); + or die_error(undef, "Open git-ls-tree failed"); my @entries = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading tree failed."); + close $fd or die_error(undef, "Reading tree failed"); $/ = "\n"; my $refs = read_info_ref(); @@ -1819,7 +1819,7 @@ sub git_log { my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash - or die_error(undef, "Open git-rev-list failed."); + or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; @@ -1880,7 +1880,7 @@ sub git_log { sub git_commit { my %co = git_read_commit($hash); if (!%co) { - die_error(undef, "Unknown commit object."); + die_error(undef, "Unknown commit object"); } my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'}); @@ -1890,9 +1890,9 @@ sub git_commit { $parent = "--root"; } open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash - or die_error(undef, "Open git-diff-tree failed."); + 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."); + close $fd or die_error(undef, "Reading git-diff-tree failed"); # non-textual hash id's can be cached my $expires; @@ -2108,15 +2108,15 @@ sub git_commitdiff { mkdir($git_temp, 0700); my %co = git_read_commit($hash); if (!%co) { - die_error(undef, "Unknown commit object."); + die_error(undef, "Unknown commit object"); } if (!defined $hash_parent) { $hash_parent = $co{'parent'}; } open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed."); + 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."); + close $fd or die_error(undef, "Reading git-diff-tree failed"); # non-textual hash id's can be cached my $expires; @@ -2194,9 +2194,9 @@ sub git_commitdiff { sub git_commitdiff_plain { mkdir($git_temp, 0700); open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed."); + or die_error(undef, "Open git-diff-tree failed"); my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed."); + close $fd or die_error(undef, "Reading diff-tree failed"); # try to figure out the next tag after this commit my $tagname; @@ -2254,7 +2254,7 @@ sub git_history { my $ftype; my %co = git_read_commit($hash_base); if (!%co) { - die_error(undef, "Unknown commit object."); + die_error(undef, "Unknown commit object"); } my $refs = read_info_ref(); git_header_html(); @@ -2312,14 +2312,14 @@ sub git_history { sub git_search { if (!defined $searchtext) { - die_error(undef, "Text field empty."); + die_error(undef, "Text field empty"); } if (!defined $hash) { $hash = git_read_head($project); } my %co = git_read_commit($hash); if (!%co) { - die_error(undef, "Unknown commit object."); + die_error(undef, "Unknown commit object"); } # 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 :) @@ -2457,7 +2457,7 @@ sub git_shortlog { my $limit = sprintf("--max-count=%i", (100 * ($page+1))); open my $fd, "-|", $GIT, "rev-list", $limit, $hash - or die_error(undef, "Open git-rev-list failed."); + or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; close $fd; @@ -2485,9 +2485,9 @@ sub git_shortlog { sub git_rss { # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ open my $fd, "-|", $GIT, "rev-list", "--max-count=150", git_read_head($project) - or die_error(undef, "Open git-rev-list failed."); + or die_error(undef, "Open git-rev-list failed"); my @revlist = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading git-rev-list failed."); + close $fd or die_error(undef, "Reading git-rev-list failed"); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); print "\n". "\n"; -- cgit v1.2.1 From e2860ead31579a15ee94831f2b9b55e43caa2cac Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 13:15:24 +0200 Subject: gitweb: No error messages with unescaped/unprotected user input Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 628490145..2e2629ca5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1265,7 +1265,7 @@ sub git_diff_print { sub git_project_list { my $order = $cgi->param('o'); if (defined $order && $order !~ m/project|descr|owner|age/) { - die_error(undef, "Invalid order parameter '$order'"); + die_error(undef, "Unknown order parameter"); } my @list = git_read_projects(); -- cgit v1.2.1 From 668e34d7cc1ab7d6135f0004ee7bea2ffdca0de0 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 13:16:03 +0200 Subject: gitweb: PATH_INFO=/ means no project Prepared for refactoring input validation. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e2629ca5..fdba15e2b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -86,8 +86,8 @@ if (defined $action) { } our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); -if (defined $project) { - $project =~ s|^/||; $project =~ s|/$||; +$project =~ s|^/||; $project =~ s|/$||; +if (defined $project && $project) { if (!validate_input($project)) { die_error(undef, "Invalid project parameter"); } -- cgit v1.2.1 From 10161355ba3b121c823649ef1acca526d73363f6 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 5 Aug 2006 13:18:58 +0200 Subject: gitweb: Inline $rss_link Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index fdba15e2b..1b5fec924 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -21,7 +21,6 @@ our $cgi = new CGI; our $version = "++GIT_VERSION++"; our $my_url = $cgi->url(); our $my_uri = $cgi->url(-absolute => 1); -our $rss_link = ""; # core git executable to use # this can just be "git" if your webserver has a sensible PATH @@ -97,8 +96,6 @@ if (defined $project && $project) { if (!(-e "$projectroot/$project/HEAD")) { die_error(undef, "No such project"); } - $rss_link = ""; $ENV{'GIT_DIR'} = "$projectroot/$project"; } else { git_project_list(); @@ -862,11 +859,13 @@ sub git_header_html { $title -$rss_link - - EOF - print "
\n" . + print "\n" . + "\n"; + + print "\n" . + "
\n" . "" . "\"git\"" . "\n"; -- cgit v1.2.1 From f16db173a4680aebc2f14c103a1e125c3f4d4531 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 6 Aug 2006 02:08:31 +0200 Subject: gitweb: Refactor untabifying - converting tabs to spaces Add untabify subroutine and use it. It also fixes git_diff_print which used to get the tabstop wrong. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1b5fec924..d0672cde3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -221,6 +221,20 @@ sub unquote { return $str; } +# escape tabs (convert tabs to spaces) +sub untabify { + my $line = shift; + + while ((my $pos = index($line, "\t")) != -1) { + if (my $count = (8 - ($pos % 8))) { + my $spaces = ' ' x $count; + $line =~ s/\t/$spaces/; + } + } + + return $line; +} + ## ---------------------------------------------------------------------- ## HTML aware string manipulation @@ -1237,12 +1251,7 @@ sub git_diff_print { # skip errors next; } - while ((my $pos = index($line, "\t")) != -1) { - if (my $count = (8 - (($pos-1) % 8))) { - my $spaces = ' ' x $count; - $line =~ s/\t/$spaces/; - } - } + $line = untabify($line); print "
" . esc_html($line) . "
\n"; } } @@ -1582,13 +1591,8 @@ HTML $age_class = age_class($age); $author = esc_html ($author); $author =~ s/ / /g; - # escape tabs - while ((my $pos = index($data, "\t")) != -1) { - if (my $count = (8 - ($pos % 8))) { - my $spaces = ' ' x $count; - $data =~ s/\t/$spaces/; - } - } + + $data = untabify($data); $data = esc_html ($data); print <) { chomp $line; $nr++; - while ((my $pos = index($line, "\t")) != -1) { - if (my $count = (8 - ($pos % 8))) { - my $spaces = ' ' x $count; - $line =~ s/\t/$spaces/; - } - } + $line = untabify($line); printf "
%4i %s
\n", $nr, $nr, $nr, esc_html($line); } close $fd or print "Reading blob failed.\n"; -- cgit v1.2.1 From 403ccc4f36101ca5084e659a8c3c9911ccc91de8 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Sun, 6 Aug 2006 12:47:33 +0200 Subject: Add gitweb.cgi to .gitignore Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fb0fa3f16..20df1f8b2 100644 --- a/.gitignore +++ b/.gitignore @@ -125,6 +125,7 @@ git-verify-tag git-whatchanged git-write-tree git-core-*/?* +gitweb/gitweb.cgi test-date test-delta test-dump-cache-tree -- cgit v1.2.1 From bd943f4757ab1d62d862ea12b4cf8b6b495e115f Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 6 Aug 2006 15:55:02 +0200 Subject: gitweb: check if HTTP_ACCEPT is really set Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d0672cde3..5e72b4f61 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -856,7 +856,7 @@ sub git_header_html { # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. # we have to do this because MSIE sometimes globs '*/*', pretending to # support xhtml+xml but choking when it gets what it asked for. - if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { + if (defined $cgi->http('HTTP_ACCEPT') && $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { $content_type = 'application/xhtml+xml'; } else { $content_type = 'text/html'; -- cgit v1.2.1 From b5ff2cf9a6a4246a5443e904acf3165e89654b1e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 6 Aug 2006 16:14:25 +0200 Subject: gitweb: fix commitdiff for root commits After changing all "-|" open invocations to list form, commitdiff for initial commit (without parent) got broken; it returned incorrectly empty patch earlier. Use '--root' option to git-diff-tree for initial (root) commit. No checking for empty $hash_parent in git_commitdiff_plain -- we rely on gitweb to give correct parameters for commitdiff_plain action. Noticed by Matthias Lederhofer (matled). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5e72b4f61..9be35aebd 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2109,7 +2109,7 @@ sub git_commitdiff { die_error(undef, "Unknown commit object"); } if (!defined $hash_parent) { - $hash_parent = $co{'parent'}; + $hash_parent = $co{'parent'} || '--root'; } open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash or die_error(undef, "Open git-diff-tree failed"); -- cgit v1.2.1 From e349d21ab47ae4a3753749c4579cce150c71c768 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 6 Aug 2006 17:59:52 +0200 Subject: gitweb: Skip nonmatching lines in difftree output, consistently This fixes error for commitdiff on root commit (without parents). Noticed-by: Matthias Lederhofer (matled) Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 9be35aebd..b8e266e0d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1983,7 +1983,7 @@ sub git_commit { foreach my $line (@difftree) { # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) { + if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) { next; } my $from_mode = $1; @@ -2156,7 +2156,9 @@ sub git_commitdiff { foreach my $line (@difftree) { # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; + if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { + next; + } my $from_mode = $1; my $to_mode = $2; my $from_id = $3; @@ -2230,7 +2232,9 @@ sub git_commitdiff_plain { print "---\n\n"; foreach my $line (@difftree) { - $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/; + if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { + next; + } my $from_id = $3; my $to_id = $4; my $status = $5; -- cgit v1.2.1 From 1568515d5b8350a6087c0976c841387d71075c01 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 6 Aug 2006 19:24:47 +0200 Subject: gitweb: fix commitdiff_plain for root commits Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b8e266e0d..08de2ce77 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2193,6 +2193,13 @@ sub git_commitdiff { sub git_commitdiff_plain { mkdir($git_temp, 0700); + my %co = git_read_commit($hash); + if (!%co) { + die_error(undef, "Unknown commit object"); + } + if (!defined $hash_parent) { + $hash_parent = $co{'parent'} || '--root'; + } open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash or die_error(undef, "Open git-diff-tree failed"); my @difftree = map { chomp; $_ } <$fd>; @@ -2214,7 +2221,6 @@ sub git_commitdiff_plain { } print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\""); - my %co = git_read_commit($hash); my %ad = date_str($co{'author_epoch'}, $co{'author_tz'}); my $comment = $co{'comment'}; print "From: $co{'author'}\n" . -- cgit v1.2.1 From dd04c428cfea1d9dd7684bbd9e2919fbad051b26 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 6 Aug 2006 13:25:41 +0200 Subject: gitweb: fix $project usage There were some places where $project was used even if it was not defined. Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 08de2ce77..b3bfc6bd9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -85,7 +85,10 @@ if (defined $action) { } our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); -$project =~ s|^/||; $project =~ s|/$||; +if (defined $project) { + $project =~ s|^/||; + $project =~ s|/$||; +} if (defined $project && $project) { if (!validate_input($project)) { die_error(undef, "Invalid project parameter"); @@ -874,11 +877,15 @@ sub git_header_html { $title EOF - print "\n" . - "\n"; + if (defined $project) { + printf(''."\n", + esc_param($project), + esc_param("$my_uri?p=$project;a=rss")); + } - print "\n" . + print "\n" . + "\n" . "
\n" . "" . "\"git\"" . -- cgit v1.2.1 From f1efc38bf2856f875b878177c0f0a98d9eb3bb4e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 8 Aug 2006 01:15:05 +0200 Subject: gitweb: Remove unused variables in git_shortlog_body and git_heads Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 -- 1 file changed, 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b3bfc6bd9..eabece78c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1065,7 +1065,6 @@ sub git_shortlog_body { #my $ref = defined $refs ? git_get_referencing($refs, $commit) : ''; my $ref = git_get_referencing($refs, $commit); my %co = git_read_commit($commit); - my %ad = date_str($co{'author_epoch'}); if ($alternate) { print "\n"; } else { @@ -1638,7 +1637,6 @@ sub git_heads { git_header_div('summary', $project); my $taglist = git_read_refs("refs/heads"); - my $alternate = 0; if (defined @$taglist) { git_heads_body($taglist, $head); } -- cgit v1.2.1 From d636ad9743783c728469a9b644c23c0415fe1a7b Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 4 Aug 2006 15:11:47 -0700 Subject: gitweb: bugfix: git_commit and git_commitdiff parents In git_commit() the hash base of $from_id is $parent, not $hash: - If status is "D", then action blob for $from_id wants $parent, not $hash. History needs $parent too. - If status is "R", then action blob for $from_id wants $parent, not $hash. Similarly in git_commitdiff() the hash base of $from_id is $hash_parent, not $hash. Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index eabece78c..6a9231667 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2015,11 +2015,11 @@ sub git_commit { "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "\n"; } elsif ($status eq "D") { print "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "\n" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$file"), -class => "list"}, esc_html($file)) . "\n" . "[deleted " . file_type($from_mode). "]\n" . "" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") . - " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$file")}, "blob") . + " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$parent;f=$file")}, "history") . "\n" } elsif ($status eq "M" || $status eq "T") { my $mode_chnge = ""; @@ -2061,7 +2061,7 @@ sub git_commit { print "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "\n" . "[moved from " . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$from_file"), -class => "list"}, esc_html($from_file)) . " with " . (int $similarity) . "% similarity$mode_chng]\n" . "" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob"); @@ -2177,15 +2177,17 @@ sub git_commitdiff { git_diff_print(undef, "/dev/null", $to_id, "b/$file"); } elsif ($status eq "D") { print "
" . file_type($from_mode) . ":" . - $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) . "(deleted)" . "
\n"; git_diff_print($from_id, "a/$file", undef, "/dev/null"); } elsif ($status eq "M") { if ($from_id ne $to_id) { print "
" . - file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . + file_type($from_mode) . ":" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) . " -> " . - file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id); + file_type($to_mode) . ":" . + $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id); print "
\n"; git_diff_print($from_id, "a/$file", $to_id, "b/$file"); } -- cgit v1.2.1 From 82f930deadc9a7990cbc0988255e6a5afd641f4b Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 4 Aug 2006 15:09:59 -0700 Subject: gitweb: blame table row no highlight fix Until now blame just used the commit/tree/tags/etc style of highlight-able table rows, which have alternating light/dark rows that flash when mouse pointer passes over them. This is very annoying in blame, since the text is static and it interferes with the per-revision block highlighting. Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 4 ++++ gitweb/gitweb.perl | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 460e72871..47c1ade87 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -171,6 +171,10 @@ tr.dark { background-color: #f6f6f0; } +tr.dark2 { + background-color: #f6f6f0; +} + tr.dark:hover { background-color: #edece6; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6a9231667..ae13e3e70 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1498,7 +1498,7 @@ sub git_blame2 { git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, $ftype); - my @rev_color = (qw(light dark)); + my @rev_color = (qw(light2 dark2)); my $num_colors = scalar(@rev_color); my $current_color = 0; my $last_rev; -- cgit v1.2.1