]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: Add combined diff support to git_difftree_body
[Gitweb] / gitweb.perl
index b17b174083468d80aca82a795085b7eda2f19a33587567b360cbe6ae67e183aa..c7ec156172f20d09c611cfecdb543837087dc8a089aceb020d83f7a0e882c0aa 100755 (executable)
@@ -72,6 +72,10 @@ our $logo_label = "git homepage";
 # source of projects list
 our $projects_list = "++GITWEB_LIST++";
 
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
 # show repository only if this file exists
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
@@ -561,12 +565,6 @@ sub validate_refname {
        return $input;
 }
 
-# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT);
-sub to_utf8 {
-       my $str = shift;
-       return decode("utf8", $str, Encode::FB_DEFAULT);
-}
-
 # quote unsafe chars, but keep the slash, even when it's not
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
@@ -591,7 +589,7 @@ sub esc_html ($;%) {
        my $str = shift;
        my %opts = @_;
 
-       $str = to_utf8($str);
+       $str = decode_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
                $str =~ s/ / /g;
@@ -605,7 +603,7 @@ sub esc_path {
        my $str = shift;
        my %opts = @_;
 
-       $str = to_utf8($str);
+       $str = decode_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
                $str =~ s/ / /g;
@@ -888,7 +886,7 @@ sub format_subject_html {
 
        if (length($short) < length($long)) {
                return $cgi->a({-href => $href, -class => "list subject",
-                               -title => to_utf8($long)},
+                               -title => decode_utf8($long)},
                       esc_html($short) . $extra);
        } else {
                return $cgi->a({-href => $href, -class => "list subject"},
@@ -1018,6 +1016,30 @@ sub git_get_hash_by_path {
        return $3;
 }
 
+# get path of entry with given hash at given tree-ish (ref)
+# used to get 'from' filename for combined diff (merge commit) for renames
+sub git_get_path_by_hash {
+       my $base = shift || return;
+       my $hash = shift || return;
+
+       local $/ = "\0";
+
+       open my $fd, "-|", git_cmd(), "ls-tree", '-r', '-t', '-z', $base
+               or return undef;
+       while (my $line = <$fd>) {
+               chomp $line;
+
+               #'040000 tree 595596a6a9117ddba9fe379b6b012b558bac8423  gitweb'
+               #'100644 blob e02e90f0429be0d2a69b76571101f20b8f75530f  gitweb/README'
+               if ($line =~ m/(?:[0-9]+) (?:.+) $hash\t(.+)$/) {
+                       close $fd;
+                       return $1;
+               }
+       }
+       close $fd;
+       return undef;
+}
+
 ## ......................................................................
 ## git utility functions, directly accessing git repository
 
@@ -1123,7 +1145,7 @@ sub git_get_projects_list {
                        if (check_export_ok("$projectroot/$path")) {
                                my $pr = {
                                        path => $path,
-                                       owner => to_utf8($owner),
+                                       owner => decode_utf8($owner),
                                };
                                push @list, $pr;
                                (my $forks_path = $path) =~ s/\.git$//;
@@ -1132,7 +1154,6 @@ sub git_get_projects_list {
                }
                close $fd;
        }
-       @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
        return @list;
 }
 
@@ -1154,7 +1175,7 @@ sub git_get_project_owner {
                        $pr = unescape($pr);
                        $ow = unescape($ow);
                        if ($pr eq $project) {
-                               $owner = to_utf8($ow);
+                               $owner = decode_utf8($ow);
                                last;
                        }
                }
@@ -1499,6 +1520,17 @@ sub parse_difftree_raw_line {
                        $res{'file'} = unquote($7);
                }
        }
+       # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh'
+       # combined diff (for merge commit)
+       elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) {
+               $res{'nparents'}  = length($1);
+               $res{'from_mode'} = [ split(' ', $2) ];
+               $res{'to_mode'} = pop @{$res{'from_mode'}};
+               $res{'from_id'} = [ split(' ', $3) ];
+               $res{'to_id'} = pop @{$res{'from_id'}};
+               $res{'status'} = [ split('', $4) ];
+               $res{'to_file'} = unquote($5);
+       }
        # 'c512b523472485aef4fff9e57b229d9d243c967f'
        elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
                $res{'commit'} = $1;
@@ -1628,7 +1660,7 @@ sub get_file_owner {
        }
        my $owner = $gcos;
        $owner =~ s/[,;].*$//;
-       return to_utf8($owner);
+       return decode_utf8($owner);
 }
 
 ## ......................................................................
@@ -1711,7 +1743,7 @@ sub git_header_html {
 
        my $title = "$site_name";
        if (defined $project) {
-               $title .= " - " . to_utf8($project);
+               $title .= " - " . decode_utf8($project);
                if (defined $action) {
                        $title .= "/$action";
                        if (defined $file_name) {
@@ -1984,7 +2016,7 @@ sub git_print_page_path {
 
        print "<div class=\"page_path\">";
        print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
-                     -title => 'tree root'}, to_utf8("[$project]"));
+                     -title => 'tree root'}, decode_utf8("[$project]"));
        print " / ";
        if (defined $name) {
                my @dirname = split '/', $name;
@@ -2203,7 +2235,8 @@ sub git_print_tree_entry {
 ## functions printing large fragments of HTML
 
 sub git_difftree_body {
-       my ($difftree, $hash, $parent) = @_;
+       my ($difftree, $hash, @parents) = @_;
+       my ($parent) = $parents[0];
        my ($have_blame) = gitweb_check_feature('blame');
        print "<div class=\"list_head\">\n";
        if ($#{$difftree} > 10) {
@@ -2211,7 +2244,9 @@ sub git_difftree_body {
        }
        print "</div>\n";
 
-       print "<table class=\"diff_tree\">\n";
+       print "<table class=\"" .
+             (@parents > 1 ? "combined " : "") .
+             "diff_tree\">\n";
        my $alternate = 1;
        my $patchno = 0;
        foreach my $line (@{$difftree}) {
@@ -2224,6 +2259,96 @@ sub git_difftree_body {
                }
                $alternate ^= 1;
 
+               if (exists $diff{'nparents'}) { # combined diff
+
+                       if ($diff{'to_id'} ne ('0' x 40)) {
+                               # file exists in the result (child) commit
+                               print "<td>" .
+                                     $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+                                                            file_name=>$diff{'to_file'},
+                                                            hash_base=>$hash),
+                                             -class => "list"}, esc_path($diff{'to_file'})) .
+                                     "</td>\n";
+                       } else {
+                               print "<td>" .
+                                     esc_path($diff{'to_file'}) .
+                                     "</td>\n";
+                       }
+
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print "<td class=\"link\">" .
+                                     $cgi->a({-href => "#patch$patchno"}, "patch") .
+                                     " | " .
+                                     "</td>\n";
+                       }
+
+                       my $has_history = 0;
+                       my $not_deleted = 0;
+                       for (my $i = 0; $i < $diff{'nparents'}; $i++) {
+                               my $hash_parent = $parents[$i];
+                               my $from_hash = $diff{'from_id'}[$i];
+                               my $from_path = undef;
+                               my $status = $diff{'status'}[$i];
+
+                               $has_history ||= ($status ne 'A');
+                               $not_deleted ||= ($status ne 'D');
+
+                               if ($status eq 'R' || $status eq 'C') {
+                                       $from_path = git_get_path_by_hash($hash_parent, $from_hash);
+                               }
+
+                               if ($status eq 'A') {
+                                       print "<td  class=\"link\" align=\"right\"> | </td>\n";
+                               } elsif ($status eq 'D') {
+                                       print "<td class=\"link\">" .
+                                             $cgi->a({-href => href(action=>"blob",
+                                                                    hash_base=>$hash,
+                                                                    hash=>$from_hash,
+                                                                    file_name=>$from_path)},
+                                                     "blob" . ($i+1)) .
+                                             " | </td>\n";
+                               } else {
+                                       if ($diff{'to_id'} eq $from_hash) {
+                                               print "<td class=\"link nochange\">";
+                                       } else {
+                                               print "<td class=\"link\">";
+                                       }
+                                       print $cgi->a({-href => href(action=>"blobdiff",
+                                                                    hash=>$diff{'to_id'},
+                                                                    hash_parent=>$from_hash,
+                                                                    hash_base=>$hash,
+                                                                    hash_parent_base=>$hash_parent,
+                                                                    file_name=>$diff{'to_file'},
+                                                                    file_parent=>$from_path)},
+                                                     "diff" . ($i+1)) .
+                                             " | </td>\n";
+                               }
+                       }
+
+                       print "<td class=\"link\">";
+                       if ($not_deleted) {
+                               print $cgi->a({-href => href(action=>"blob",
+                                                            hash=>$diff{'to_id'},
+                                                            file_name=>$diff{'to_file'},
+                                                            hash_base=>$hash)},
+                                             "blob");
+                               print " | " if ($has_history);
+                       }
+                       if ($has_history) {
+                               print $cgi->a({-href => href(action=>"history",
+                                                            file_name=>$diff{'to_file'},
+                                                            hash_base=>$hash)},
+                                             "history");
+                       }
+                       print "</td>\n";
+
+                       print "</tr>\n";
+                       next; # instead of 'else' clause, to avoid extra indent
+               }
+               # else ordinary diff
+
                my ($to_mode_oct, $to_mode_str, $to_file_type);
                my ($from_mode_oct, $from_mode_str, $from_file_type);
                if ($diff{'to_mode'} ne ('0' x 6)) {
@@ -2396,6 +2521,7 @@ sub git_patchset_body {
        my ($fd, $difftree, $hash, $hash_parent) = @_;
 
        my $patch_idx = 0;
+       my $patch_number = 0;
        my $patch_line;
        my $diffinfo;
        my (%from, %to);
@@ -2417,6 +2543,7 @@ sub git_patchset_body {
                # git diff header
                #assert($patch_line =~ m/^diff /) if DEBUG;
                #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
+               $patch_number++;
                push @diff_header, $patch_line;
 
                # extended diff header
@@ -2579,6 +2706,7 @@ sub git_patchset_body {
        } continue {
                print "</div>\n"; # class="patch"
        }
+       print "<div class=\"diff nodifferences\">No differences found</div>\n" if (!$patch_number);
 
        print "</div>\n"; # class="patchset"
 }
@@ -2599,7 +2727,7 @@ sub git_project_list_body {
                ($pr->{'age'}, $pr->{'age_string'}) = @aa;
                if (!defined $pr->{'descr'}) {
                        my $descr = git_get_project_description($pr->{'path'}) || "";
-                       $pr->{'descr_long'} = to_utf8($descr);
+                       $pr->{'descr_long'} = decode_utf8($descr);
                        $pr->{'descr'} = chop_str($descr, 25, 5);
                }
                if (!defined $pr->{'owner'}) {
@@ -2619,7 +2747,7 @@ sub git_project_list_body {
                push @projects, $pr;
        }
 
-       $order ||= "project";
+       $order ||= $default_projects_order;
        $from = 0 unless defined $from;
        $to = $#projects if (!defined $to || $#projects < $to);
 
@@ -2978,7 +3106,7 @@ sub git_search_grep_body {
 
 sub git_project_list {
        my $order = $cgi->param('o');
-       if (defined $order && $order !~ m/project|descr|owner|age/) {
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(undef, "Unknown order parameter");
        }
 
@@ -3001,7 +3129,7 @@ sub git_project_list {
 
 sub git_forks {
        my $order = $cgi->param('o');
-       if (defined $order && $order !~ m/project|descr|owner|age/) {
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(undef, "Unknown order parameter");
        }
 
@@ -3631,7 +3759,7 @@ sub git_snapshot {
                $hash = git_get_head_hash($project);
        }
 
-       my $filename = to_utf8(basename($project)) . "-$hash.tar.$suffix";
+       my $filename = decode_utf8(basename($project)) . "-$hash.tar.$suffix";
 
        print $cgi->header(
                -type => "application/$ctype",
This page took 0.33546 seconds and 4 git commands to generate.