From: Junio C Hamano Date: Wed, 25 Oct 2006 20:18:06 +0000 (-0700) Subject: Merge branch 'jc/web-blame' X-Git-Tag: v1.4.4-rc1~69 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/8e95026f2942c2c485d9686736d861a6477480b5?ds=inline;hp=-c Merge branch 'jc/web-blame' * jc/web-blame: gitweb: spell "blame --porcelain" with -p blame: Document and add help text for -f, -n, and -p gitweb: blame porcelain: lineno and orig lineno swapped Remove git-annotate.perl and create a builtin-alias for git-blame gitweb: use blame --porcelain git-blame --porcelain blame.c: move code to output metainfo into a separate function. git-blame: --show-number (and -n) git-blame: --show-name (and -f) blame.c: whitespace and formatting clean-up. Gitweb - provide site headers and footers gitweb: blame: Mouse-over commit-8 shows author and date gitweb: blame: print commit-8 on the leading row of a commit-block Revert 954a6183756a073723a7c9fd8d2feb13132876b0 gitweb: prepare for repositories with packed refs. gitweb: make leftmost column of blame less cluttered. --- 8e95026f2942c2c485d9686736d861a6477480b5 diff --combined Makefile index 66c8b4b127,473ff271fe..95f37fe777 --- a/Makefile +++ b/Makefile @@@ -132,6 -132,8 +132,8 @@@ GITWEB_HOMETEXT = indextext.htm GITWEB_CSS = gitweb.css GITWEB_LOGO = git-logo.png GITWEB_FAVICON = git-favicon.png + GITWEB_SITE_HEADER = + GITWEB_SITE_FOOTER = export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR @@@ -173,7 -175,7 +175,7 @@@ SCRIPT_SH = SCRIPT_PERL = \ git-archimport.perl git-cvsimport.perl git-relink.perl \ git-shortlog.perl git-rerere.perl \ - git-annotate.perl git-cvsserver.perl \ + git-cvsserver.perl \ git-svnimport.perl git-cvsexportcommit.perl \ git-send-email.perl git-svn.perl @@@ -265,6 -267,7 +267,7 @@@ LIB_OBJS = BUILTIN_OBJS = \ builtin-add.o \ + builtin-annotate.o \ builtin-apply.o \ builtin-archive.o \ builtin-cat-file.o \ @@@ -675,6 -678,8 +678,8 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \ -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \ -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \ + -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \ + -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \ $< >$@+ chmod +x $@+ mv $@+ $@ @@@ -760,8 -765,6 +765,8 @@@ $(LIB_FILE): $(LIB_OBJS rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o +$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ + xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h $(XDIFF_LIB): $(XDIFF_OBJS) rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) diff --combined builtin.h index f71b9629b9,2c5d900acc..4ac32d7780 --- a/builtin.h +++ b/builtin.h @@@ -11,9 -11,9 +11,10 @@@ extern int mailinfo(FILE *in, FILE *out extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip); extern void stripspace(FILE *in, FILE *out); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); +extern void prune_packed_objects(int); extern int cmd_add(int argc, const char **argv, const char *prefix); + extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); extern int cmd_archive(int argc, const char **argv, const char *prefix); extern int cmd_cat_file(int argc, const char **argv, const char *prefix); diff --combined git.c index e089b53571,32c356e044..235b6fea15 --- a/git.c +++ b/git.c @@@ -219,6 -219,7 +219,7 @@@ static void handle_internal_command(in int option; } commands[] = { { "add", cmd_add, RUN_SETUP }, + { "annotate", cmd_annotate, }, { "apply", cmd_apply }, { "archive", cmd_archive }, { "cat-file", cmd_cat_file, RUN_SETUP }, @@@ -226,7 -227,7 +227,7 @@@ { "check-ref-format", cmd_check_ref_format }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "count-objects", cmd_count_objects, RUN_SETUP }, - { "diff", cmd_diff, RUN_SETUP }, + { "diff", cmd_diff, RUN_SETUP | USE_PAGER }, { "diff-files", cmd_diff_files, RUN_SETUP }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-stages", cmd_diff_stages, RUN_SETUP }, @@@ -258,7 -259,7 +259,7 @@@ { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, - { "tar-tree", cmd_tar_tree, RUN_SETUP }, + { "tar-tree", cmd_tar_tree }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, diff --combined gitweb/gitweb.perl index bc8d8eb238,34b88ce8eb..9eb19bb82e --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@@ -41,21 -41,26 +41,31 @@@ our $home_link_str = "++GITWEB_HOME_LIN # replace this with something more descriptive for clearer bookmarks our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled"; + # filename of html text to include at top of each page + our $site_header = "++GITWEB_SITE_HEADER++"; # html text to include at home page our $home_text = "++GITWEB_HOMETEXT++"; + # filename of html text to include at bottom of each page + our $site_footer = "++GITWEB_SITE_FOOTER++"; + + # URI of stylesheets + our @stylesheets = ("++GITWEB_CSS++"); + our $stylesheet; + # default is not to define style sheet, but it can be overwritten later + undef $stylesheet; -# URI of GIT logo +# URI of default stylesheet +our $stylesheet = "++GITWEB_CSS++"; +# URI of GIT logo (72x27 size) our $logo = "++GITWEB_LOGO++"; # URI of GIT favicon, assumed to be image/png type our $favicon = "++GITWEB_FAVICON++"; -our $githelp_url = "http://git.or.cz/"; -our $githelp_label = "git homepage"; +# URI and label (title) of GIT logo link +#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/"; +#our $logo_label = "git documentation"; +our $logo_url = "http://git.or.cz/"; +our $logo_label = "git homepage"; # source of projects list our $projects_list = "++GITWEB_LIST++"; @@@ -93,66 -98,21 +103,66 @@@ our %feature = # # use gitweb_check_feature() to check if is enabled + # Enable the 'blame' blob view, showing the last commit that modified + # each line in the file. This can be very CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'blame'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.blame = 0|1; 'blame' => { 'sub' => \&feature_blame, 'override' => 0, 'default' => [0]}, + # Enable the 'snapshot' link, providing a compressed tarball of any + # tree. This can potentially generate high traffic if you have large + # project. + + # To disable system wide have in $GITWEB_CONFIG + # $feature{'snapshot'}{'default'} = [undef]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.snapshot = none|gzip|bzip2; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, # => [content-encoding, suffix, program] 'default' => ['x-gzip', 'gz', 'gzip']}, + # Enable the pickaxe search, which will list the commits that modified + # a given string in a file. This can be practical and quite faster + # alternative to 'blame', but still potentially CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pickaxe'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'pickaxe'}{'override'} = 1; + # and in project config gitweb.pickaxe = 0|1; 'pickaxe' => { 'sub' => \&feature_pickaxe, 'override' => 0, 'default' => [1]}, + + # Make gitweb use an alternative format of the URLs which can be + # more readable and natural-looking: project name is embedded + # directly in the path and the query string contains other + # auxiliary information. All gitweb installations recognize + # URL in either format; this configures in which formats gitweb + # generates links. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pathinfo'}{'default'} = [1]; + # Project specific override is not supported. + + # Note that you will need to change the default location of CSS, + # favicon, logo and possibly other files to an absolute URL. Also, + # if gitweb.cgi serves as your indexfile, you will need to force + # $my_uri to contain the script name in your $GITWEB_CONFIG. + 'pathinfo' => { + 'override' => 0, + 'default' => [0]}, ); sub gitweb_check_feature { @@@ -163,13 -123,15 +173,13 @@@ $feature{$name}{'override'}, @{$feature{$name}{'default'}}); if (!$override) { return @defaults; } + if (!defined $sub) { + warn "feature $name is not overrideable"; + return @defaults; + } return $sub->(@defaults); } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'blame'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.blame = 0|1; - sub feature_blame { my ($val) = git_get_project_config('blame', '--bool'); @@@ -182,6 -144,12 +192,6 @@@ return $_[0]; } -# To disable system wide have in $GITWEB_CONFIG -# $feature{'snapshot'}{'default'} = [undef]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.snapshot = none|gzip|bzip2 - sub feature_snapshot { my ($ctype, $suffix, $command) = @_; @@@ -205,6 -173,12 +215,6 @@@ sub gitweb_have_snapshot return $have_snapshot; } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'pickaxe'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'pickaxe'}{'override'} = 1; -# and in project config gitweb.pickaxe = 0|1; - sub feature_pickaxe { my ($val) = git_get_project_config('pickaxe', '--bool'); @@@ -217,6 -191,22 +227,22 @@@ return ($_[0]); } + # checking HEAD file with -e is fragile if the repository was + # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed + # and then pruned. + sub check_head_link { + my ($dir) = @_; + my $headfile = "$dir/HEAD"; + return ((-e $headfile) || + (-l $headfile && readlink($headfile) =~ /^refs\/heads\//)); + } + + sub check_export_ok { + my ($dir) = @_; + return (check_head_link($dir) && + (!$export_ok || -e "$dir/$export_ok")); + } + # rename detection options for git-diff and git-diff-tree # - default is '-M', with the cost proportional to # (number of removed files) * (number of new files). @@@ -249,7 -239,7 +275,7 @@@ our $project = $cgi->param('p') if (defined $project) { if (!validate_pathname($project) || !(-d "$projectroot/$project") || - !(-e "$projectroot/$project/HEAD") || + !check_head_link("$projectroot/$project") || ($export_ok && !(-e "$projectroot/$project/$export_ok")) || ($strict_export && !project_in_list($project))) { undef $project; @@@ -326,7 -316,7 +352,7 @@@ sub evaluate_path_info # find which part of PATH_INFO is project $project = $path_info; $project =~ s,/+$,,; - while ($project && !-e "$projectroot/$project/HEAD") { + while ($project && !check_head_link("$projectroot/$project")) { $project =~ s,/*[^/]*$,,; } # validate project @@@ -412,10 -402,6 +438,10 @@@ exit sub href(%) { my %params = @_; + my $href = $my_uri; + + # XXX: Warning: If you touch this, check the search form for updating, + # too. my @mapping = ( project => "p", @@@ -434,19 -420,6 +460,19 @@@ $params{'project'} = $project unless exists $params{'project'}; + my ($use_pathinfo) = gitweb_check_feature('pathinfo'); + if ($use_pathinfo) { + # use PATH_INFO for project name + $href .= "/$params{'project'}" if defined $params{'project'}; + delete $params{'project'}; + + # Summary just uses the project path URL + if (defined $params{'action'} && $params{'action'} eq 'summary') { + delete $params{'action'}; + } + } + + # now encode the parameters explicitly my @result = (); for (my $i = 0; $i < @mapping; $i += 2) { my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]); @@@ -454,9 -427,7 +480,9 @@@ push @result, $symbol . "=" . esc_param($params{$name}); } } - return "$my_uri?" . join(';', @result); + $href .= "?" . join(';', @result) if scalar @result; + + return $href; } @@@ -496,12 -467,6 +522,12 @@@ sub validate_refname return $input; } +# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT); +sub to_utf8 { + my $str = shift; + return decode("utf8", $str, Encode::FB_DEFAULT); +} + # quote unsafe chars, but keep the slash, even when it's not # correct, but quoted slashes look too horrible in bookmarks sub esc_param { @@@ -524,7 -489,7 +550,7 @@@ sub esc_url # replace invalid utf8 character with SUBSTITUTION sequence sub esc_html { my $str = shift; - $str = decode("utf8", $str, Encode::FB_DEFAULT); + $str = to_utf8($str); $str = escapeHTML($str); $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) $str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1) @@@ -727,7 -692,7 +753,7 @@@ sub format_subject_html if (length($short) < length($long)) { return $cgi->a({-href => $href, -class => "list subject", - -title => decode("utf8", $long, Encode::FB_DEFAULT)}, + -title => to_utf8($long)}, esc_html($short) . $extra); } else { return $cgi->a({-href => $href, -class => "list subject"}, @@@ -878,8 -843,7 +904,7 @@@ sub git_get_projects_list my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - if (-e "$projectroot/$subdir/HEAD" && (!$export_ok || - -e "$projectroot/$subdir/$export_ok")) { + if (check_export_ok("$projectroot/$subdir")) { push @list, { path => $subdir }; $File::Find::prune = 1; } @@@ -900,11 -864,10 +925,10 @@@ if (!defined $path) { next; } - if (-e "$projectroot/$path/HEAD" && (!$export_ok || - -e "$projectroot/$path/$export_ok")) { + if (check_export_ok("$projectroot/$path")) { my $pr = { path => $path, - owner => decode("utf8", $owner, Encode::FB_DEFAULT), + owner => to_utf8($owner), }; push @list, $pr } @@@ -933,7 -896,7 +957,7 @@@ sub git_get_project_owner $pr = unescape($pr); $ow = unescape($ow); if ($pr eq $project) { - $owner = decode("utf8", $ow, Encode::FB_DEFAULT); + $owner = to_utf8($ow); last; } } @@@ -1011,6 -974,9 +1035,9 @@@ sub parse_date $date{'hour_local'} = $hour; $date{'minute_local'} = $min; $date{'tz_local'} = $tz; + $date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s", + 1900+$year, $mon+1, $mday, + $hour, $min, $sec, $tz); return %date; } @@@ -1059,11 -1025,12 +1086,11 @@@ sub parse_commit if (defined $commit_text) { @commit_lines = @$commit_text; } else { - $/ = "\0"; + local $/ = "\0"; open my $fd, "-|", git_cmd(), "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; @@@ -1123,9 -1090,6 +1150,9 @@@ last; } } + if ($co{'title'} eq "") { + $co{'title'} = $co{'title_short'} = '(no commit message)'; + } # remove added spaces foreach my $line (@commit_lines) { $line =~ s/^ //; @@@ -1297,7 -1261,7 +1324,7 @@@ sub get_file_owner } my $owner = $gcos; $owner =~ s/[,;].*$//; - return decode("utf8", $owner, Encode::FB_DEFAULT); + return to_utf8($owner); } ## ...................................................................... @@@ -1416,8 -1380,17 +1443,17 @@@ sub git_header_html $title - EOF + # print out each stylesheet that exist + if (defined $stylesheet) { + #provides backwards capability for those people who define style sheet in a config file + print ''."\n"; + } else { + foreach my $stylesheet (@stylesheets) { + next unless $stylesheet; + print ''."\n"; + } + } if (defined $project) { printf(''."\n", @@@ -1435,11 -1408,20 +1471,18 @@@ } print "\n" . - "\n" . - "
\n" . + "\n"; + + if (-f $site_header) { + open (my $fd, $site_header); + print <$fd>; + close $fd; + } + + print "
\n" . - "" . - "\"git\"" . - "\n"; + $cgi->a({-href => esc_url($logo_url), + -title => $logo_label}, + qq()); print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); @@@ -1460,7 -1442,6 +1503,7 @@@ } $cgi->param("a", "search"); $cgi->param("h", $search_hash); + $cgi->param("p", $project); print $cgi->startform(-method => "get", -action => $my_uri) . "
\n" . $cgi->hidden(-name => "p") . "\n" . @@@ -1488,8 -1469,15 +1531,15 @@@ sub git_footer_html print $cgi->a({-href => href(project=>undef, action=>"project_index"), -class => "rss_logo"}, "TXT") . "\n"; } - print "
\n" . - "\n" . + print "
\n" ; + + if (-f $site_footer) { + open (my $fd, $site_footer); + print <$fd>; + close $fd; + } + + print "\n" . ""; } @@@ -1614,16 -1602,17 +1664,16 @@@ sub git_print_page_path my $type = shift; my $hb = shift; - if (!defined $name) { - print "
/
\n"; - } else { + + print "
"; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), + -title => 'tree root'}, "[$project]"); + print " / "; + if (defined $name) { my @dirname = split '/', $name; my $basename = pop @dirname; my $fullname = ''; - print "
"; - print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), - -title => 'tree root'}, "[$project]"); - print " / "; foreach my $dir (@dirname) { $fullname .= ($fullname ? '/' : '') . $dir; print $cgi->a({-href => href(action=>"tree", file_name=>$fullname, @@@ -1639,12 -1628,11 +1689,12 @@@ print $cgi->a({-href => href(action=>"tree", file_name=>$file_name, hash_base=>$hb), -title => $name}, esc_html($basename)); + print " / "; } else { print esc_html($basename); } - print "
\n"; } + print "
\n"; } # sub git_print_log (\@;%) { @@@ -1721,13 -1709,13 +1771,13 @@@ sub git_print_tree_entry if ($t->{'type'} eq "blob") { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key), - -class => "list"}, esc_html($t->{'name'})) . "\n"; + file_name=>"$basedir$t->{'name'}", %base_key), + -class => "list"}, esc_html($t->{'name'})) . "\n"; print ""; if ($have_blame) { print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "blame"); + file_name=>"$basedir$t->{'name'}", %base_key)}, + "blame"); } if (defined $hash_base) { if ($have_blame) { @@@ -1739,8 -1727,8 +1789,8 @@@ } print " | " . $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base, - file_name=>"$basedir$t->{'name'}")}, - "raw"); + file_name=>"$basedir$t->{'name'}")}, + "raw"); print "\n"; } elsif ($t->{'type'} eq "tree") { @@@ -1808,7 -1796,7 +1858,7 @@@ sub git_difftree_body print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chng\n"; print ""; @@@ -1835,11 -1823,11 +1885,11 @@@ print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, - file_name=>$diff{'file'})}, - "blame") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed @@@ -1860,8 -1848,8 +1910,8 @@@ } print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, - hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chnge\n"; print ""; @@@ -1872,19 -1860,19 +1922,19 @@@ print $cgi->a({-href => "#patch$patchno"}, "patch"); } else { print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'file'})}, - "diff"); + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'file'})}, + "diff"); } print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, - file_name=>$diff{'file'})}, - "blame") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$hash, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied @@@ -1912,19 -1900,19 +1962,19 @@@ print $cgi->a({-href => "#patch$patchno"}, "patch"); } else { print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, - "diff"); + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, + "diff"); } print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, - file_name=>$diff{'from_file'})}, - "blame") . " | "; + file_name=>$diff{'from_file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'from_file'})}, - "history"); + file_name=>$diff{'from_file'})}, + "history"); print "\n"; } # we should not encounter Unmerged (U) or Unknown (X) status @@@ -1976,14 -1964,14 +2026,14 @@@ sub git_patchset_body print "
" . file_type($diffinfo->{'to_mode'}) . ":" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'to_id'}) . "(new)" . + $diffinfo->{'to_id'}) . " (new)" . "
\n"; # class="diff_info" } elsif ($diffinfo->{'status'} eq "D") { # deleted print "
" . file_type($diffinfo->{'from_mode'}) . ":" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'from_id'}) . "(deleted)" . + $diffinfo->{'from_id'}) . " (deleted)" . "
\n"; # class="diff_info" } elsif ($diffinfo->{'status'} eq "R" || # renamed @@@ -2087,10 -2075,8 +2137,10 @@@ sub git_shortlog_body print "\n" . "" . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . " | " . - $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot"); + $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree"); + if (gitweb_have_snapshot()) { + print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot"); + } print "\n" . "\n"; } @@@ -2518,7 -2504,8 +2568,8 @@@ sub git_blame2 if ($ftype !~ "blob") { die_error("400 Bad Request", "Object is not a blob"); } - open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base) + open ($fd, "-|", git_cmd(), "blame", '-p', '--', + $file_name, $hash_base) or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = @@@ -2542,25 -2529,52 +2593,52 @@@ HTML - while (<$fd>) { - /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/; - my $full_rev = $1; + my %metainfo = (); + while (1) { + $_ = <$fd>; + last unless defined $_; + my ($full_rev, $orig_lineno, $lineno, $group_size) = + /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/; + if (!exists $metainfo{$full_rev}) { + $metainfo{$full_rev} = {}; + } + my $meta = $metainfo{$full_rev}; + while (<$fd>) { + last if (s/^\t//); + if (/^(\S+) (.*)$/) { + $meta->{$1} = $2; + } + } + my $data = $_; my $rev = substr($full_rev, 0, 8); - my $lineno = $2; - my $data = $3; - - if (!defined $last_rev) { - $last_rev = $full_rev; - } elsif ($last_rev ne $full_rev) { - $last_rev = $full_rev; + my $author = $meta->{'author'}; + my %date = parse_date($meta->{'author-time'}, + $meta->{'author-tz'}); + my $date = $date{'iso-tz'}; + if ($group_size) { $current_color = ++$current_color % $num_colors; } print "\n"; - print "\n"; - print "\n"; + if ($group_size) { + print "\n"; + } + my $blamed = href(action => 'blame', + file_name => $meta->{'filename'}, + hash_base => $full_rev); + print ""; print "\n"; print "\n"; } @@@ -2833,7 -2847,7 +2911,7 @@@ sub git_tree my $refs = git_get_references(); my $ref = format_ref_marker($refs, $hash_base); git_header_html(); - my $base = ""; + my $basedir = ''; my ($have_blame) = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { my @views_nav = (); @@@ -2850,7 -2864,7 +2928,7 @@@ # FIXME: Should be available when we have no hash base as well. push @views_nav, $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, - "snapshot"); + "snapshot"); } git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav)); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); @@@ -2861,39 -2875,12 +2939,39 @@@ print "
$hash
\n"; } if (defined $file_name) { - $base = esc_html("$file_name/"); + $basedir = $file_name; + if ($basedir ne '' && substr($basedir, -1) ne '/') { + $basedir .= '/'; + } } git_print_page_path($file_name, 'tree', $hash_base); print "
\n"; print "
CommitLineData
" . - $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, - esc_html($rev)) . "" . - esc_html($lineno) . " 1); + print ">"; + print $cgi->a({-href => href(action=>"commit", + hash=>$full_rev, + file_name=>$file_name)}, + esc_html($rev)); + print ""; + print $cgi->a({ -href => "$blamed#l$orig_lineno", + -id => "l$lineno", + -class => "linenr" }, + esc_html($lineno)); + print "" . esc_html($data) . "
\n"; my $alternate = 1; + # '..' (top directory) link if possible + if (defined $hash_base && + defined $file_name && $file_name =~ m![^/]+$!) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + my $up = $file_name; + $up =~ s!/?[^/]+$!!; + undef $up unless $up; + # based on git_print_tree_entry + print '\n"; + print '\n"; + print "\n"; + + print "\n"; + } foreach my $line (@entries) { my %t = parse_ls_tree_line($line, -z => 1); @@@ -2904,7 -2891,7 +2982,7 @@@ } $alternate ^= 1; - git_print_tree_entry(\%t, $base, $hash_base, $have_blame); + git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame); print "\n"; } @@@ -3035,7 -3022,7 +3113,7 @@@ sub git_commit "blame"); } git_header_html(undef, $expires); - git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', + git_print_page_nav('commit', '', $hash, $co{'tree'}, $hash, join (' | ', @views_nav)); @@@ -3678,7 -3665,7 +3756,7 @@@ XM "\n"; } print "
\n"; @@@ -3687,7 -3674,7 +3765,7 @@@ next; } my $file = esc_html(unquote($7)); - $file = decode("utf8", $file, Encode::FB_DEFAULT); + $file = to_utf8($file); print "$file
\n"; } print "]]>\n" .
' . mode_str('040000') . "'; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, + file_name=>$up)}, + ".."); + print "