]> Lady’s Gitweb - Gitweb/commitdiff
gitweb: add patch view
authorGiuseppe Bilotta <redacted>
Thu, 18 Dec 2008 07:13:16 +0000 (08:13 +0100)
committerLady <redacted>
Mon, 6 Apr 2026 04:50:38 +0000 (00:50 -0400)
The output of commitdiff_plain is not intended for git-am:
 * when given a range of commits, commitdiff_plain publishes a single
   patch with the message from the first commit, instead of a patchset
 * the hand-built email format replicates the commit summary both as
   email subject and as first line of the email itself, resulting in
   a duplication if the output is used with git-am.

We thus create a new view that can be fed to git-am directly, allowing
patch exchange via gitweb. The new view exposes the output of git
format-patch directly, limiting it to a single patch in the case of a
single commit.

A configurable upper limit defaulting to 16 is imposed on the number of
commits which will be included in a patchset, to prevent DoS attacks on
the server. Setting the limit to 0 will disable the patch view, setting
it to a negative number will remove the limit.

Signed-off-by: Giuseppe Bilotta <redacted>
Signed-off-by: Junio C Hamano <redacted>
gitweb.perl

index f6a1143cd9a02f4495f3344d70aa297021cba114c09da59270052819cc8d0293..e9847469b36a70376e7904c5f1165913f9bb46e6218517d93a698cb4da77dc09 100755 (executable)
@@ -331,6 +331,21 @@ our %feature = (
        'ctags' => {
                'override' => 0,
                'default' => [0]},
+
+       # The maximum number of patches in a patchset generated in patch
+       # view. Set this to 0 or undef to disable patch view, or to a
+       # negative number to remove any limit.
+
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'patches'}{'default'} = [0];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'patches'}{'override'} = 1;
+       # and in project config gitweb.patches = 0|n;
+       # where n is the maximum number of patches allowed in a patchset.
+       'patches' => {
+               'sub' => \&feature_patches,
+               'override' => 0,
+               'default' => [16]},
 );
 
 sub gitweb_get_feature {
@@ -389,6 +404,16 @@ sub feature_snapshot {
        return @fmts;
 }
 
+sub feature_patches {
+       my @val = (git_get_project_config('patches', '--int'));
+
+       if (@val) {
+               return @val;
+       }
+
+       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.
@@ -482,6 +507,7 @@ our %actions = (
        "heads" => \&git_heads,
        "history" => \&git_history,
        "log" => \&git_log,
+       "patch" => \&git_patch,
        "rss" => \&git_rss,
        "atom" => \&git_atom,
        "search" => \&git_search,
@@ -5347,6 +5373,13 @@ sub git_blobdiff_plain {
 
 sub git_commitdiff {
        my $format = shift || 'html';
+
+       my $patch_max;
+       if ($format eq 'patch') {
+               ($patch_max) = gitweb_get_feature('patches');
+               die_error(403, "Patch view not allowed") unless $patch_max;
+       }
+
        $hash ||= $hash_base || "HEAD";
        my %co = parse_commit($hash)
            or die_error(404, "Unknown commit object");
@@ -5444,7 +5477,23 @@ sub git_commitdiff {
                open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
                        '-p', $hash_parent_param, $hash, "--"
                        or die_error(500, "Open git-diff-tree failed");
-
+       } elsif ($format eq 'patch') {
+               # For commit ranges, we limit the output to the number of
+               # patches specified in the 'patches' feature.
+               # For single commits, we limit the output to a single patch,
+               # diverging from the git-format-patch default.
+               my @commit_spec = ();
+               if ($hash_parent) {
+                       if ($patch_max > 0) {
+                               push @commit_spec, "-$patch_max";
+                       }
+                       push @commit_spec, '-n', "$hash_parent..$hash";
+               } else {
+                       push @commit_spec, '-1', '--root', $hash;
+               }
+               open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
+                       '--stdout', @commit_spec
+                       or die_error(500, "Open git-format-patch failed");
        } else {
                die_error(400, "Unknown commitdiff format");
        }
@@ -5493,6 +5542,14 @@ sub git_commitdiff {
                        print to_utf8($line) . "\n";
                }
                print "---\n\n";
+       } elsif ($format eq 'patch') {
+               my $filename = basename($project) . "-$hash.patch";
+
+               print $cgi->header(
+                       -type => 'text/plain',
+                       -charset => 'utf-8',
+                       -expires => $expires,
+                       -content_disposition => 'inline; filename="' . "$filename" . '"');
        }
 
        # write patch
@@ -5514,6 +5571,11 @@ sub git_commitdiff {
                print <$fd>;
                close $fd
                        or print "Reading git-diff-tree failed\n";
+       } elsif ($format eq 'patch') {
+               local $/ = undef;
+               print <$fd>;
+               close $fd
+                       or print "Reading git-format-patch failed\n";
        }
 }
 
@@ -5521,6 +5583,11 @@ sub git_commitdiff_plain {
        git_commitdiff('plain');
 }
 
+# format-patch-style patches
+sub git_patch {
+       git_commitdiff('patch');
+}
+
 sub git_history {
        if (!defined $hash_base) {
                $hash_base = git_get_head_hash($project);
This page took 0.354287 seconds and 4 git commands to generate.