Allow VersionedMetaData.onSave to skip committing

Change-Id: I65349b2fde9fd22381fa5e4a35f6265924c626f0
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java
index ff648f5..cd4a0b8 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java
@@ -88,7 +88,7 @@
   }
 
   @Override
-  protected void onSave(CommitBuilder commit) throws IOException,
+  protected boolean onSave(CommitBuilder commit) throws IOException,
       ConfigInvalidException {
     throw new UnsupportedOperationException();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index 97ae746..1db7b4a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -746,7 +746,7 @@
   }
 
   @Override
-  protected void onSave(CommitBuilder commit) throws IOException,
+  protected boolean onSave(CommitBuilder commit) throws IOException,
       ConfigInvalidException {
     if (commit.getMessage() == null || "".equals(commit.getMessage())) {
       commit.setMessage("Updated project configuration\n");
@@ -786,6 +786,7 @@
 
     saveConfig(PROJECT_CONFIG, rc);
     saveGroupList();
+    return true;
   }
 
   public static final String validMaxObjectSizeLimit(String value)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
index 476ccae..b4f41a0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectLevelConfig.java
@@ -88,11 +88,12 @@
   }
 
   @Override
-  protected void onSave(CommitBuilder commit) throws IOException,
+  protected boolean onSave(CommitBuilder commit) throws IOException,
       ConfigInvalidException {
     if (commit.getMessage() == null || "".equals(commit.getMessage())) {
       commit.setMessage("Updated configuration\n");
     }
     saveConfig(fileName, cfg);
+    return true;
   }
 }
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 4cee593..c665d67 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
@@ -61,9 +61,17 @@
   /** @return name of the reference storing this configuration. */
   protected abstract String getRefName();
 
+  /** Set up the metadata, parsing any state from the loaded revision. */
   protected abstract void onLoad() throws IOException, ConfigInvalidException;
 
-  protected abstract void onSave(CommitBuilder commit) throws IOException,
+  /**
+   * Save any changes to the metadata in a commit.
+   *
+   * @return true if the commit should proceed, false to abort.
+   * @throws IOException
+   * @throws ConfigInvalidException
+   */
+  protected abstract boolean onSave(CommitBuilder commit) throws IOException,
       ConfigInvalidException;
 
   /** @return revision of the metadata that was loaded. */
@@ -189,7 +197,7 @@
         write(VersionedMetaData.this, commit);
       }
 
-      private void doSave(VersionedMetaData config, CommitBuilder commit) throws IOException {
+      private boolean doSave(VersionedMetaData config, CommitBuilder commit) throws IOException {
         DirCache nt = config.newTree;
         ObjectReader r = config.reader;
         ObjectInserter i = config.inserter;
@@ -197,7 +205,7 @@
           config.newTree = newTree;
           config.reader = reader;
           config.inserter = inserter;
-          config.onSave(commit);
+          return config.onSave(commit);
         } catch (ConfigInvalidException e) {
           throw new IOException("Cannot update " + getRefName() + " in "
               + db.getDirectory() + ": " + e.getMessage(), e);
@@ -210,7 +218,9 @@
 
       @Override
       public void write(VersionedMetaData config, CommitBuilder commit) throws IOException {
-        doSave(config, commit);
+        if (!doSave(config, commit)) {
+          return;
+        }
 
         final ObjectId res = newTree.writeTree(inserter);
         if (res.equals(srcTree) && !update.allowEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 4a514df..5062deb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -307,7 +307,7 @@
   }
 
   @Override
-  protected void onSave(CommitBuilder commit) {
+  protected boolean onSave(CommitBuilder commit) {
     throw new UnsupportedOperationException(
         getClass().getSimpleName() + " is read-only");
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 59d583a..d16ae4c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -265,9 +265,9 @@
   }
 
   @Override
-  protected void onSave(CommitBuilder commit) {
+  protected boolean onSave(CommitBuilder commit) {
     if (approvals.isEmpty() && reviewers.isEmpty()) {
-      return;
+      return false;
     }
     int ps = psId != null ? psId.get() : change.currentPatchSetId().get();
     StringBuilder msg = new StringBuilder();
@@ -293,6 +293,7 @@
       }
     }
     commit.setMessage(msg.toString());
+    return true;
   }
 
   private static StringBuilder addFooter(StringBuilder sb, FooterKey footer) {