Merge "Support reading VersionedMetaData from an open RevWalk"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
index b8f0ec9..6781ef1 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.projects.ProjectInput;
 import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.Util;
@@ -56,6 +57,18 @@
   @Test
   @TestProjectInput(cloneAs = "user")
   public void updateProjectConfig() throws Exception {
+    String id = testUpdateProjectConfig();
+    assertThat(gApi.changes().id(id).get().revisions).hasSize(1);
+  }
+
+  @Test
+  @TestProjectInput(cloneAs = "user", submitType = SubmitType.CHERRY_PICK)
+  public void updateProjectConfigWithCherryPick() throws Exception {
+    String id = testUpdateProjectConfig();
+    assertThat(gApi.changes().id(id).get().revisions).hasSize(2);
+  }
+
+  private String testUpdateProjectConfig() throws Exception {
     Config cfg = readProjectConfig();
     assertThat(cfg.getString("project", null, "description")).isNull();
     String desc = "new project description";
@@ -74,6 +87,11 @@
     fetchRefsMetaConfig();
     assertThat(readProjectConfig().getString("project", null, "description"))
         .isEqualTo(desc);
+    String changeRev = gApi.changes().id(id).get().currentRevision;
+    String branchRev = gApi.projects().name(project.get())
+        .branch("refs/meta/config").get().revision;
+    assertThat(changeRev).isEqualTo(branchRev);
+    return id;
   }
 
   @Test
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index b6cd992..b52844d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -937,7 +937,7 @@
           case UPDATE_NONFASTFORWARD:
             try {
               ProjectConfig cfg = new ProjectConfig(project.getNameKey());
-              cfg.load(repo, cmd.getNewId());
+              cfg.load(rp.getRevWalk(), cmd.getNewId());
               if (!cfg.getValidationErrors().isEmpty()) {
                 addError("Invalid project configuration:");
                 for (ValidationError err : cfg.getValidationErrors()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index ecba568..dc927a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -137,12 +137,36 @@
    */
   public void load(Repository db, ObjectId id) throws IOException,
       ConfigInvalidException {
-    reader = db.newObjectReader();
+    try (RevWalk walk = new RevWalk(db)) {
+      load(walk, id);
+    }
+  }
+
+  /**
+   * Load a specific version from an open walk.
+   * <p>
+   * This method is primarily useful for applying updates to a specific revision
+   * that was shown to an end-user in the user interface. If there are conflicts
+   * with another user's concurrent changes, these will be automatically
+   * detected at commit time.
+   * <p>
+   * The caller retains ownership of the walk and is responsible for closing
+   * it. However, this instance does not hold a reference to the walk or the
+   * repository after the call completes, allowing the application to retain
+   * this object for long periods of time.
+   *
+   * @param walk open walk to access to access.
+   * @param id revision to load.
+   * @throws IOException
+   * @throws ConfigInvalidException
+   */
+  public void load(RevWalk walk, ObjectId id) throws IOException,
+     ConfigInvalidException {
+    this.reader = walk.getObjectReader();
     try {
       revision = id != null ? new RevWalk(reader).parseCommit(id) : null;
       onLoad();
     } finally {
-      reader.close();
       reader = null;
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
index 01ae0b8..5e70b91 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyOp.java
@@ -145,7 +145,7 @@
       logDebug("Loading new configuration from {}", RefNames.REFS_CONFIG);
       try {
         ProjectConfig cfg = new ProjectConfig(getProject());
-        cfg.load(ctx.getRepository(), commit);
+        cfg.load(ctx.getRevWalk(), commit);
       } catch (Exception e) {
         throw new IntegrationException("Submit would store invalid"
             + " project configuration " + commit.name() + " for "