Merge "Upgrade elasticsearch-rest-client to 6.5.4" into stable-2.15
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CommitIncludedInIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/CommitIncludedInIT.java
similarity index 87%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CommitIncludedInIT.java
rename to gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/CommitIncludedInIT.java
index 61f14e4..7768916 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CommitIncludedInIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/CommitIncludedInIT.java
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.acceptance.rest.project;
+package com.google.gerrit.acceptance.api.project;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
-import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.extensions.api.changes.IncludedInInfo;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.projects.TagInput;
@@ -26,6 +26,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
+@NoHttpd
 public class CommitIncludedInIT extends AbstractDaemonTest {
   @Test
   public void includedInOpenChange() throws Exception {
@@ -58,10 +59,6 @@
   }
 
   private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
-    RestResponse r =
-        userRestSession.get("/projects/" + project.get() + "/commits/" + id.name() + "/in");
-    IncludedInInfo result = newGson().fromJson(r.getReader(), IncludedInInfo.class);
-    r.consume();
-    return result;
+    return gApi.projects().name(project.get()).commit(id.name()).includedIn();
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 7d6f589..2c80285 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -381,6 +382,41 @@
     setMaxObjectSize("100 foo");
   }
 
+  @Test
+  public void setHead() throws Exception {
+    assertThat(gApi.projects().name(project.get()).head()).isEqualTo("refs/heads/master");
+    gApi.projects().name(project.get()).branch("test1").create(new BranchInput());
+    gApi.projects().name(project.get()).branch("test2").create(new BranchInput());
+    for (String head : new String[] {"test1", "refs/heads/test2"}) {
+      gApi.projects().name(project.get()).head(head);
+      assertThat(gApi.projects().name(project.get()).head()).isEqualTo(RefNames.fullName(head));
+    }
+  }
+
+  @Test
+  public void setHeadToNonexistentBranch() throws Exception {
+    exception.expect(UnprocessableEntityException.class);
+    gApi.projects().name(project.get()).head("does-not-exist");
+  }
+
+  @Test
+  public void setHeadToSameBranch() throws Exception {
+    gApi.projects().name(project.get()).branch("test").create(new BranchInput());
+    for (String head : new String[] {"test", "refs/heads/test"}) {
+      gApi.projects().name(project.get()).head(head);
+      assertThat(gApi.projects().name(project.get()).head()).isEqualTo(RefNames.fullName(head));
+    }
+  }
+
+  @Test
+  public void setHeadNotAllowed() throws Exception {
+    gApi.projects().name(project.get()).branch("test").create(new BranchInput());
+    setApiUser(user);
+    exception.expect(AuthException.class);
+    exception.expectMessage("restricted to project owner");
+    gApi.projects().name(project.get()).head("test");
+  }
+
   private ConfigInput createTestConfigInput() {
     ConfigInput input = new ConfigInput();
     input.description = "some description";
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 481681e..a1b4578 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -92,9 +92,13 @@
 
   void setPrivate(boolean value, @Nullable String message) throws RestApiException;
 
-  void setWorkInProgress(String message) throws RestApiException;
+  default void setPrivate(boolean value) throws RestApiException {
+    setPrivate(value, null);
+  }
 
-  void setReadyForReview(String message) throws RestApiException;
+  void setWorkInProgress(@Nullable String message) throws RestApiException;
+
+  void setReadyForReview(@Nullable String message) throws RestApiException;
 
   default void setWorkInProgress() throws RestApiException {
     setWorkInProgress(null);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/CommitApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/CommitApi.java
index 6084962..6b3c1fb 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/CommitApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/CommitApi.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
+import com.google.gerrit.extensions.api.changes.IncludedInInfo;
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 
@@ -23,11 +24,18 @@
 
   ChangeApi cherryPick(CherryPickInput input) throws RestApiException;
 
+  IncludedInInfo includedIn() throws RestApiException;
+
   /** A default implementation for source compatibility when adding new methods to the interface. */
   class NotImplemented implements CommitApi {
     @Override
     public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
       throw new NotImplementedException();
     }
+
+    @Override
+    public IncludedInInfo includedIn() throws RestApiException {
+      throw new NotImplementedException();
+    }
   }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
index 8320ef7..86b6a27 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -170,6 +170,16 @@
 
   ListDashboardsRequest dashboards() throws RestApiException;
 
+  /** Get the name of the branch to which {@code HEAD} points. */
+  String head() throws RestApiException;
+
+  /**
+   * Set the project's {@code HEAD}.
+   *
+   * @param head the HEAD
+   */
+  void head(String head) throws RestApiException;
+
   /**
    * A default implementation which allows source compatibility when adding new methods to the
    * interface.
@@ -304,5 +314,15 @@
     public void removeDefaultDashboard() throws RestApiException {
       throw new NotImplementedException();
     }
+
+    @Override
+    public String head() throws RestApiException {
+      throw new NotImplementedException();
+    }
+
+    @Override
+    public void head(String head) throws RestApiException {
+      throw new NotImplementedException();
+    }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 0fba74a..41f591c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -330,7 +330,7 @@
   }
 
   @Override
-  public void setWorkInProgress(String message) throws RestApiException {
+  public void setWorkInProgress(@Nullable String message) throws RestApiException {
     try {
       setWip.apply(change, new WorkInProgressOp.Input(message));
     } catch (Exception e) {
@@ -339,7 +339,7 @@
   }
 
   @Override
-  public void setReadyForReview(String message) throws RestApiException {
+  public void setReadyForReview(@Nullable String message) throws RestApiException {
     try {
       setReady.apply(change, new WorkInProgressOp.Input(message));
     } catch (Exception e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/CommitApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
index cbdd03d..c0a5a2c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
@@ -19,9 +19,11 @@
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 import com.google.gerrit.extensions.api.changes.Changes;
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
+import com.google.gerrit.extensions.api.changes.IncludedInInfo;
 import com.google.gerrit.extensions.api.projects.CommitApi;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.change.CherryPickCommit;
+import com.google.gerrit.server.project.CommitIncludedIn;
 import com.google.gerrit.server.project.CommitResource;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
@@ -33,13 +35,18 @@
 
   private final Changes changes;
   private final CherryPickCommit cherryPickCommit;
+  private final CommitIncludedIn includedIn;
   private final CommitResource commitResource;
 
   @Inject
   CommitApiImpl(
-      Changes changes, CherryPickCommit cherryPickCommit, @Assisted CommitResource commitResource) {
+      Changes changes,
+      CherryPickCommit cherryPickCommit,
+      CommitIncludedIn includedIn,
+      @Assisted CommitResource commitResource) {
     this.changes = changes;
     this.cherryPickCommit = cherryPickCommit;
+    this.includedIn = includedIn;
     this.commitResource = commitResource;
   }
 
@@ -51,4 +58,13 @@
       throw asRestApiException("Cannot cherry pick", e);
     }
   }
+
+  @Override
+  public IncludedInInfo includedIn() throws RestApiException {
+    try {
+      return includedIn.apply(commitResource);
+    } catch (Exception e) {
+      throw asRestApiException("Could not extract IncludedIn data", e);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 9fd4d48..e6d5ff3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -59,6 +59,7 @@
 import com.google.gerrit.server.project.GetAccess;
 import com.google.gerrit.server.project.GetConfig;
 import com.google.gerrit.server.project.GetDescription;
+import com.google.gerrit.server.project.GetHead;
 import com.google.gerrit.server.project.ListBranches;
 import com.google.gerrit.server.project.ListChildProjects;
 import com.google.gerrit.server.project.ListDashboards;
@@ -69,6 +70,7 @@
 import com.google.gerrit.server.project.PutConfig;
 import com.google.gerrit.server.project.PutDescription;
 import com.google.gerrit.server.project.SetAccess;
+import com.google.gerrit.server.project.SetHead;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -110,6 +112,8 @@
   private final DashboardApiImpl.Factory dashboardApi;
   private final CheckAccess checkAccess;
   private final Provider<ListDashboards> listDashboards;
+  private final GetHead getHead;
+  private final SetHead setHead;
 
   @AssistedInject
   ProjectApiImpl(
@@ -139,6 +143,8 @@
       DashboardApiImpl.Factory dashboardApi,
       CheckAccess checkAccess,
       Provider<ListDashboards> listDashboards,
+      GetHead getHead,
+      SetHead setHead,
       @Assisted ProjectResource project) {
     this(
         user,
@@ -168,6 +174,8 @@
         dashboardApi,
         checkAccess,
         listDashboards,
+        getHead,
+        setHead,
         null);
   }
 
@@ -199,6 +207,8 @@
       DashboardApiImpl.Factory dashboardApi,
       CheckAccess checkAccess,
       Provider<ListDashboards> listDashboards,
+      GetHead getHead,
+      SetHead setHead,
       @Assisted String name) {
     this(
         user,
@@ -228,6 +238,8 @@
         dashboardApi,
         checkAccess,
         listDashboards,
+        getHead,
+        setHead,
         name);
   }
 
@@ -259,6 +271,8 @@
       DashboardApiImpl.Factory dashboardApi,
       CheckAccess checkAccess,
       Provider<ListDashboards> listDashboards,
+      GetHead getHead,
+      SetHead setHead,
       String name) {
     this.user = user;
     this.permissionBackend = permissionBackend;
@@ -287,6 +301,8 @@
     this.dashboardApi = dashboardApi;
     this.checkAccess = checkAccess;
     this.listDashboards = listDashboards;
+    this.getHead = getHead;
+    this.setHead = setHead;
     this.name = name;
   }
 
@@ -524,6 +540,26 @@
     };
   }
 
+  @Override
+  public String head() throws RestApiException {
+    try {
+      return getHead.apply(checkExists());
+    } catch (Exception e) {
+      throw asRestApiException("Cannot get HEAD", e);
+    }
+  }
+
+  @Override
+  public void head(String head) throws RestApiException {
+    SetHead.Input input = new SetHead.Input();
+    input.ref = head;
+    try {
+      setHead.apply(checkExists(), input);
+    } catch (Exception e) {
+      throw asRestApiException("Cannot set HEAD", e);
+    }
+  }
+
   private ProjectResource checkExists() throws ResourceNotFoundException {
     if (project == null) {
       throw new ResourceNotFoundException(name);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
index 5b36916..3c9b4bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
@@ -26,7 +26,7 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 @Singleton
-class CommitIncludedIn implements RestReadView<CommitResource> {
+public class CommitIncludedIn implements RestReadView<CommitResource> {
   private IncludedIn includedIn;
 
   @Inject