]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: Use @diff_opts, default ('M'), as git-diff and git-diff-tree paramete
[Gitweb] / gitweb.perl
index 3a89b8ee06ab7eb36679daa6db5420f3a1d8ae9a14f06d66294345f9a8341d0b..b492242981b025fa707e0d8f1ed34774a413dd662a3248d9f551e27129ee78e2 100755 (executable)
@@ -31,9 +31,6 @@ our $GIT = "++GIT_BINDIR++/git";
 #our $projectroot = "/pub/scm";
 our $projectroot = "++GITWEB_PROJECTROOT++";
 
-# location for temporary files needed for diffs
-our $git_temp = "/tmp/gitweb";
-
 # target of the home link on top of all pages
 our $home_link = $my_uri || "/";
 
@@ -70,9 +67,16 @@ our $mimetypes_file = undef;
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
-       # feature => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...]
-       # if feature is overridable, feature-sub will be called with default options;
-       # return value indicates if to enable specified feature
+       # feature => {
+       #       'sub' => feature-sub (subroutine),
+       #       'override' => allow-override (boolean),
+       #       'default' => [ default options...] (array reference)}
+       #
+       # if feature is overridable (it means that allow-override has true value,
+       # then feature-sub will be called with default options as parameters;
+       # return value of feature-sub indicates if to enable specified feature
+       #
+       # use gitweb_check_feature(<feature>) to check if <feature> is enabled
 
        'blame' => {
                'sub' => \&feature_blame,
@@ -98,9 +102,9 @@ sub gitweb_check_feature {
 }
 
 # 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;
+# $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 {
@@ -116,9 +120,9 @@ sub feature_blame {
 }
 
 # 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;
+# $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 {
@@ -137,6 +141,16 @@ sub feature_snapshot {
        return ($ctype, $suffix, $command);
 }
 
+# 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).
+# - more costly is '-C' (or '-C', '-M'), with the cost proportional to
+#   (number of changed files + number of removed files) * (number of new files)
+# - even more costly is '-C', '--find-copies-harder' with cost
+#   (number of files in the original tree) * (number of new files)
+# - one might want to include '-B' option, e.g. '-B', '-M'
+our @diff_opts = ('-M'); # taken from git_commit
+
 our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
 require $GITWEB_CONFIG if -e $GITWEB_CONFIG;
 
@@ -144,9 +158,6 @@ require $GITWEB_CONFIG if -e $GITWEB_CONFIG;
 our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
 $projects_list ||= $projectroot;
-if (! -d $git_temp) {
-       mkdir($git_temp, 0700) || die_error(undef, "Couldn't mkdir $git_temp");
-}
 
 # ======================================================================
 # input validation and dispatch
@@ -624,26 +635,6 @@ sub git_get_hash_by_path {
        return $3;
 }
 
-# converts symbolic name to hash
-sub git_to_hash {
-       my @params = @_;
-       return undef unless @params;
-
-       open my $fd, "-|", $GIT, "rev-parse", @params
-               or return undef;
-       my @hashes = map { chomp; $_ } <$fd>;
-       close $fd;
-
-       if (wantarray) {
-               return @hashes;
-       } elsif (scalar(@hashes) == 1) {
-               # single hash
-               return $hashes[0];
-       } else {
-               return \@hashes;
-       }
-}
-
 ## ......................................................................
 ## git utility functions, directly accessing git repository
 
@@ -783,57 +774,6 @@ sub git_get_references {
        return \%refs;
 }
 
-sub git_get_following_references {
-       my $hash = shift || return undef;
-       my $type = shift;
-       my $base = shift || $hash_base || "HEAD";
-
-       my $refs = git_get_references($type);
-       open my $fd, "-|", $GIT, "rev-list", $base
-               or return undef;
-       my @commits = map { chomp; $_ } <$fd>;
-       close $fd
-               or return undef;
-
-       my @reflist;
-       my $lastref;
-
-       foreach my $commit (@commits) {
-               foreach my $ref (@{$refs->{$commit}}) {
-                       $lastref = $ref;
-                       push @reflist, $lastref;
-               }
-               if ($commit eq $hash) {
-                       last;
-               }
-       }
-
-       return wantarray ? @reflist : $lastref;
-}
-
-sub git_get_preceding_references {
-       my $hash = shift || return undef;
-       my $type = shift;
-
-       my $refs = git_get_references($type);
-       open my $fd, "-|", $GIT, "rev-list", $hash
-               or return undef;
-       my @commits = map { chomp; $_ } <$fd>;
-       close $fd
-               or return undef;
-
-       my @reflist;
-
-       foreach my $commit (@commits) {
-               foreach my $ref (@{$refs->{$commit}}) {
-                       return $ref unless wantarray;
-                       push @reflist, $ref;
-               }
-       }
-
-       return @reflist;
-}
-
 sub git_get_rev_name_tags {
        my $hash = shift || return undef;
 
@@ -1555,7 +1495,7 @@ sub git_difftree_body {
                                      "blob") .
                              " | " .
                              $cgi->a({-href => href(action=>"history", hash_base=>$parent,
-                                                    file_name=>$diff{'file'})},\
+                                                    file_name=>$diff{'file'})},
                                      "history") .
                              "</td>\n";
 
@@ -1655,7 +1595,7 @@ sub git_patchset_body {
        print "<div class=\"patchset\">\n";
 
        LINE:
-       while (my $patch_line @$fd>) {
+       while (my $patch_line = <$fd>) {
                chomp $patch_line;
 
                if ($patch_line =~ m/^diff /) { # "git diff" header
@@ -1975,77 +1915,6 @@ sub git_heads_body {
        print "</table>\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;
-                       }
-                       $line = untabify($line);
-                       print "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
-               }
-       }
-       close $fd;
-
-       if (defined $from) {
-               unlink($from_tmp);
-       }
-       if (defined $to) {
-               unlink($to_tmp);
-       }
-}
-
-
 ## ======================================================================
 ## ======================================================================
 ## actions
@@ -2368,7 +2237,7 @@ HTML
                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+)\)(.*)$/) {
+               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;
@@ -2433,6 +2302,12 @@ sub git_heads {
 }
 
 sub git_blob_plain {
+       # blobs defined by non-textual hash id's can be cached
+       my $expires;
+       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               $expires = "+1d";
+       }
+
        if (!defined $hash) {
                if (defined $file_name) {
                        my $base = $hash_base || git_get_head_hash($project);
@@ -2456,8 +2331,10 @@ sub git_blob_plain {
                $save_as .= '.txt';
        }
 
-       print $cgi->header(-type => "$type",
-                          -content_disposition => "inline; filename=\"$save_as\"");
+       print $cgi->header(
+               -type => "$type",
+               -expires=>$expires,
+               -content_disposition => "inline; filename=\"$save_as\"");
        undef $/;
        binmode STDOUT, ':raw';
        print <$fd>;
@@ -2467,6 +2344,12 @@ sub git_blob_plain {
 }
 
 sub git_blob {
+       # blobs defined by non-textual hash id's can be cached
+       my $expires;
+       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               $expires = "+1d";
+       }
+
        if (!defined $hash) {
                if (defined $file_name) {
                        my $base = $hash_base || git_get_head_hash($project);
@@ -2484,7 +2367,7 @@ sub git_blob {
                close $fd;
                return git_blob_plain($mimetype);
        }
-       git_header_html();
+       git_header_html(undef, $expires);
        my $formats_nav = '';
        if (defined $hash_base && (my %co = parse_commit($hash_base))) {
                if (defined $file_name) {
@@ -2720,7 +2603,7 @@ sub git_commit {
        if (!defined $parent) {
                $parent = "--root";
        }
-       open my $fd, "-|", $GIT, "diff-tree", '-r', '-M', $parent, $hash
+       open my $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts, $parent, $hash
                or die_error(undef, "Open git-diff-tree failed");
        my @difftree = map { chomp; $_ } <$fd>;
        close $fd or die_error(undef, "Reading git-diff-tree failed");
@@ -2815,16 +2698,19 @@ sub git_commit {
 }
 
 sub git_blobdiff {
+       my $format = shift || 'html';
+
        my $fd;
        my @difftree;
        my %diffinfo;
+       my $expires;
 
        # preparing $fd and %diffinfo for git_patchset_body
        # new style URI
        if (defined $hash_base && defined $hash_parent_base) {
                if (defined $file_name) {
                        # read raw output
-                       open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C', $hash_parent_base, $hash_base,
+                       open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base,
                                "--", $file_name
                                or die_error(undef, "Open git-diff-tree failed");
                        @difftree = map { chomp; $_ } <$fd>;
@@ -2837,9 +2723,12 @@ sub git_blobdiff {
                        if ($hash !~ /[0-9a-fA-F]{40}/) {
                                $hash = git_to_hash($hash);
                        }
+               } elsif (defined $hash &&
+                        $hash =~ /[0-9a-fA-F]{40}/) {
+                       # try to find filename from $hash
 
                        # read filtered raw output
-                       open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C', $hash_parent_base, $hash_base
+                       open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base
                                or die_error(undef, "Open git-diff-tree failed");
                        @difftree =
                                # ':100644 100644 03b21826... 3b93d5e7... M     ls-files.c'
@@ -2866,8 +2755,15 @@ sub git_blobdiff {
                $hash_parent ||= $diffinfo{'from_id'};
                $hash        ||= $diffinfo{'to_id'};
 
+               # non-textual hash id's can be cached
+               if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ &&
+                   $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) {
+                       $expires = '+1d';
+               }
+
                # open patch output
-               open $fd, "-|", $GIT, "diff-tree", '-r', '-p', '-M', '-C', $hash_parent_base, $hash_base,
+               open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts,
+                       '-p', $hash_parent_base, $hash_base,
                        "--", $file_name
                        or die_error(undef, "Open git-diff-tree failed");
        }
@@ -2895,8 +2791,14 @@ sub git_blobdiff {
                        $diffinfo{'to_file'}   = $hash;
                }
 
-               #open $fd, "-|", $GIT, "diff", '-p', $hash_parent, $hash
-               open $fd, "-|", $GIT, "diff", '-p', $hash, $hash_parent
+               # non-textual hash id's can be cached
+               if ($hash =~ m/^[0-9a-fA-F]{40}$/ &&
+                   $hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+                       $expires = '+1d';
+               }
+
+               # open patch output
+               open $fd, "-|", $GIT, "diff", '-p', @diff_opts, $hash_parent, $hash
                        or die_error(undef, "Open git-diff failed");
        } else  {
                die_error('404 Not Found', "Missing one of the blob diff parameters")
@@ -2904,40 +2806,67 @@ sub git_blobdiff {
        }
 
        # header
-       my $formats_nav =
-               $cgi->a({-href => href(action=>"blobdiff_plain",
-                                      hash=>$hash, hash_parent=>$hash_parent,
-                                      hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
-                                      file_name=>$file_name, file_parent=>$file_parent)},
-                       "plain");
-       git_header_html();
-       if (defined $hash_base && (my %co = parse_commit($hash_base))) {
-               git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
-               git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
-       } else {
-               print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
-               print "<div class=\"title\">$hash vs $hash_parent</div>\n";
-       }
-       if (defined $file_name) {
-               git_print_page_path($file_name, "blob", $hash_base);
+       if ($format eq 'html') {
+               my $formats_nav =
+                       $cgi->a({-href => href(action=>"blobdiff_plain",
+                                              hash=>$hash, hash_parent=>$hash_parent,
+                                              hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
+                                              file_name=>$file_name, file_parent=>$file_parent)},
+                               "plain");
+               git_header_html(undef, $expires);
+               if (defined $hash_base && (my %co = parse_commit($hash_base))) {
+                       git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+                       git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
+               } else {
+                       print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
+                       print "<div class=\"title\">$hash vs $hash_parent</div>\n";
+               }
+               if (defined $file_name) {
+                       git_print_page_path($file_name, "blob", $hash_base);
+               } else {
+                       print "<div class=\"page_path\"></div>\n";
+               }
+
+       } elsif ($format eq 'plain') {
+               print $cgi->header(
+                       -type => 'text/plain',
+                       -charset => 'utf-8',
+                       -expires => $expires,
+                       -content_disposition => qq(inline; filename="${file_name}.patch"));
+
+               print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+
        } else {
-               print "<div class=\"page_path\"></div>\n";
+               die_error(undef, "Unknown blobdiff format");
        }
 
        # patch
-       print "<div class=\"page_body\">\n";
+       if ($format eq 'html') {
+               print "<div class=\"page_body\">\n";
 
-       git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
-       close $fd;
+               git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
+               close $fd;
 
-       print "</div>\n"; # class="page_body"
-       git_footer_html();
+               print "</div>\n"; # class="page_body"
+               git_footer_html();
+
+       } else {
+               while (my $line = <$fd>) {
+                       $line =~ s!a/($hash|$hash_parent)!a/$diffinfo{'from_file'}!g;
+                       $line =~ s!b/($hash|$hash_parent)!b/$diffinfo{'to_file'}!g;
+
+                       print $line;
+
+                       last if $line =~ m!^\+\+\+!;
+               }
+               local $/ = undef;
+               print <$fd>;
+               close $fd;
+       }
 }
 
 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");
+       git_blobdiff('plain');
 }
 
 sub git_commitdiff {
@@ -2954,7 +2883,7 @@ sub git_commitdiff {
        my $fd;
        my @difftree;
        if ($format eq 'html') {
-               open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C',
+               open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts,
                        "--patch-with-raw", "--full-index", $hash_parent, $hash
                        or die_error(undef, "Open git-diff-tree failed");
 
@@ -2965,7 +2894,8 @@ sub git_commitdiff {
                }
 
        } elsif ($format eq 'plain') {
-               open $fd, "-|", $GIT, "diff-tree", '-r', '-p', '-B', $hash_parent, $hash
+               open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts,
+                       '-p', $hash_parent, $hash
                        or die_error(undef, "Open git-diff-tree failed");
 
        } else {
@@ -3274,9 +3204,12 @@ XML
                        last;
                }
                my %cd = parse_date($co{'committer_epoch'});
-               open $fd, "-|", $GIT, "diff-tree", '-r', $co{'parent'}, $co{'id'} or next;
+               open $fd, "-|", $GIT, "diff-tree", '-r', @diff_opts,
+                       $co{'parent'}, $co{'id'}
+                       or next;
                my @difftree = map { chomp; $_ } <$fd>;
-               close $fd or next;
+               close $fd
+                       or next;
                print "<item>\n" .
                      "<title>" .
                      sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
This page took 0.309069 seconds and 4 git commands to generate.