Merge "Allow to enable git protocol version 2 for upload pack" into stable-2.16
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 3927b49..0cb2b00 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -3806,6 +3806,20 @@
 If no keys are specified, web-of-trust checks are disabled. This is the
 default behavior.
 
+[[receive.enableProtocolV2]]receive.enableProtocolV2::
++
+Enable support for git protocol version 2.
++
+When this option is enabled, clients may send upload pack using git
+protocol version 2.
++
+The repository must also be configured on the server side to use protocol
+version 2 by setting `protocol.version = 2` either in the gerrit user's
+`~/.gitconfig` file (which will enable it for all repositories) or on
+a per repository basis by setting the option in the `.git/config` file
+of the repository.
++
+Defaults to false, git protocol version 2 is not enabled.
 
 [[repository]]
 === Section repository
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 08ff8a7..e74c4b2 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -49,6 +49,7 @@
 import com.google.inject.name.Named;
 import java.io.IOException;
 import java.time.Duration;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -258,6 +259,13 @@
       up.setTimeout(config.getTimeout());
       up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks)));
       up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
+      if (config.enableProtocolV2()) {
+        String header = req.getHeader("Git-Protocol");
+        if (header != null) {
+          String[] params = header.split(":");
+          up.setExtraParameters(Arrays.asList(params));
+        }
+      }
       ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
       for (UploadPackInitializer initializer : uploadPackInitializers) {
         initializer.init(state.getNameKey(), up);
diff --git a/java/com/google/gerrit/server/git/TransferConfig.java b/java/com/google/gerrit/server/git/TransferConfig.java
index 55b9448..dde524f 100644
--- a/java/com/google/gerrit/server/git/TransferConfig.java
+++ b/java/com/google/gerrit/server/git/TransferConfig.java
@@ -29,6 +29,7 @@
   private final long maxObjectSizeLimit;
   private final String maxObjectSizeLimitFormatted;
   private final boolean inheritProjectMaxObjectSizeLimit;
+  private final boolean enableProtocolV2;
 
   @Inject
   TransferConfig(@GerritServerConfig Config cfg) {
@@ -45,6 +46,7 @@
     maxObjectSizeLimitFormatted = cfg.getString("receive", null, "maxObjectSizeLimit");
     inheritProjectMaxObjectSizeLimit =
         cfg.getBoolean("receive", "inheritProjectMaxObjectSizeLimit", false);
+    enableProtocolV2 = cfg.getBoolean("receive", "enableProtocolV2", false);
 
     packConfig = new PackConfig();
     packConfig.setDeltaCompress(false);
@@ -72,4 +74,8 @@
   public boolean inheritProjectMaxObjectSizeLimit() {
     return inheritProjectMaxObjectSizeLimit;
   }
+
+  public boolean enableProtocolV2() {
+    return enableProtocolV2;
+  }
 }
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index c49ae82..5370c9e 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -29,6 +29,8 @@
 import org.kohsuke.args4j.Argument;
 
 public abstract class AbstractGitCommand extends BaseCommand {
+  private static final String GIT_PROTOCOL = "GIT_PROTOCOL";
+
   @Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
   protected ProjectState projectState;
 
@@ -45,9 +47,11 @@
   protected Repository repo;
   protected Project.NameKey projectName;
   protected Project project;
+  protected String gitProtocol;
 
   @Override
   public void start(Environment env) {
+    gitProtocol = env.getEnv().get(GIT_PROTOCOL);
     Context ctx = context.subContext(newSession(), context.getCommandLine());
     final Context old = sshScope.set(ctx);
     try {
diff --git a/java/com/google/gerrit/sshd/commands/Upload.java b/java/com/google/gerrit/sshd/commands/Upload.java
index 24a6975..c5ffc16 100644
--- a/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/java/com/google/gerrit/sshd/commands/Upload.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -52,7 +53,6 @@
     PermissionBackend.ForProject perm =
         permissionBackend.user(user).project(projectState.getNameKey());
     try {
-
       perm.check(ProjectPermission.RUN_UPLOAD_PACK);
     } catch (AuthException e) {
       throw new Failure(1, "fatal: upload-pack not permitted on this server");
@@ -65,6 +65,9 @@
     up.setPackConfig(config.getPackConfig());
     up.setTimeout(config.getTimeout());
     up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
+    if (config.enableProtocolV2() && gitProtocol != null) {
+      up.setExtraParameters(ImmutableList.of(gitProtocol));
+    }
 
     List<PreUploadHook> allPreUploadHooks = Lists.newArrayList(preUploadHooks);
     allPreUploadHooks.add(