Merge "Set crossorigin="anonymous" when load plugin js files"
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 89a2036..dccb97a 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -56,7 +56,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.submit.ChangeAlreadyMergedException;
 import com.google.gerrit.server.submit.CommitMergeStatus;
-import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.submit.MergeIdenticalTreeException;
 import com.google.gerrit.server.submit.MergeSorter;
 import com.google.inject.assistedinject.Assisted;
@@ -185,8 +184,7 @@
   }
 
   public CodeReviewCommit getFirstFastForward(
-      CodeReviewCommit mergeTip, RevWalk rw, List<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+      CodeReviewCommit mergeTip, RevWalk rw, List<CodeReviewCommit> toMerge) {
     for (Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext(); ) {
       try {
         final CodeReviewCommit n = i.next();
@@ -195,19 +193,19 @@
           return n;
         }
       } catch (IOException e) {
-        throw new IntegrationException("Cannot fast-forward test during merge", e);
+        throw new StorageException("Cannot fast-forward test during merge", e);
       }
     }
     return mergeTip;
   }
 
   public List<CodeReviewCommit> reduceToMinimalMerge(
-      MergeSorter mergeSorter, Collection<CodeReviewCommit> toSort) throws IntegrationException {
+      MergeSorter mergeSorter, Collection<CodeReviewCommit> toSort) {
     List<CodeReviewCommit> result = new ArrayList<>();
     try {
       result.addAll(mergeSorter.sort(toSort));
     } catch (IOException | StorageException e) {
-      throw new IntegrationException("Branch head sorting failed", e);
+      throw new StorageException("Branch head sorting failed", e);
     }
     result.sort(CodeReviewCommit.ORDER);
     return result;
@@ -673,8 +671,10 @@
   }
 
   public boolean canMerge(
-      MergeSorter mergeSorter, Repository repo, CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
-      throws IntegrationException {
+      MergeSorter mergeSorter,
+      Repository repo,
+      CodeReviewCommit mergeTip,
+      CodeReviewCommit toMerge) {
     if (hasMissingDependencies(mergeSorter, toMerge)) {
       return false;
     }
@@ -687,7 +687,7 @@
     } catch (NoMergeBaseException e) {
       return false;
     } catch (IOException e) {
-      throw new IntegrationException("Cannot merge " + toMerge.name(), e);
+      throw new StorageException("Cannot merge " + toMerge.name(), e);
     }
   }
 
@@ -695,8 +695,7 @@
       MergeSorter mergeSorter,
       CodeReviewCommit mergeTip,
       CodeReviewRevWalk rw,
-      CodeReviewCommit toMerge)
-      throws IntegrationException {
+      CodeReviewCommit toMerge) {
     if (hasMissingDependencies(mergeSorter, toMerge)) {
       return false;
     }
@@ -706,7 +705,7 @@
           || rw.isMergedInto(mergeTip, toMerge)
           || rw.isMergedInto(toMerge, mergeTip);
     } catch (IOException e) {
-      throw new IntegrationException("Cannot fast-forward test during merge", e);
+      throw new StorageException("Cannot fast-forward test during merge", e);
     }
   }
 
@@ -715,8 +714,7 @@
       Repository repo,
       CodeReviewCommit mergeTip,
       CodeReviewRevWalk rw,
-      CodeReviewCommit toMerge)
-      throws IntegrationException {
+      CodeReviewCommit toMerge) {
     if (mergeTip == null) {
       // The branch is unborn. Fast-forward is possible.
       //
@@ -740,7 +738,7 @@
         m.setBase(toMerge.getParent(0));
         return m.merge(mergeTip, toMerge);
       } catch (IOException e) {
-        throw new IntegrationException(
+        throw new StorageException(
             String.format(
                 "Cannot merge commit %s with mergetip %s", toMerge.name(), mergeTip.name()),
             e);
@@ -757,12 +755,11 @@
         || canMerge(mergeSorter, repo, mergeTip, toMerge);
   }
 
-  public boolean hasMissingDependencies(MergeSorter mergeSorter, CodeReviewCommit toMerge)
-      throws IntegrationException {
+  public boolean hasMissingDependencies(MergeSorter mergeSorter, CodeReviewCommit toMerge) {
     try {
       return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge);
     } catch (IOException | StorageException e) {
-      throw new IntegrationException("Branch head sorting failed", e);
+      throw new StorageException("Branch head sorting failed", e);
     }
   }
 
@@ -775,7 +772,7 @@
       BranchNameKey destBranch,
       CodeReviewCommit mergeTip,
       CodeReviewCommit n)
-      throws IntegrationException, InvalidMergeStrategyException {
+      throws InvalidMergeStrategyException {
     ThreeWayMerger m = newThreeWayMerger(inserter, repoConfig);
     try {
       if (m.merge(mergeTip, n)) {
@@ -788,10 +785,10 @@
         failed(rw, mergeTip, n, getCommitMergeStatus(e.getReason()));
       } catch (IOException e2) {
         logger.atSevere().withCause(e2).log("Failed to set merge failure status for " + n.name());
-        throw new IntegrationException("Cannot merge " + n.name(), e);
+        throw new StorageException("Cannot merge " + n.name(), e);
       }
     } catch (IOException e) {
-      throw new IntegrationException("Cannot merge " + n.name(), e);
+      throw new StorageException("Cannot merge " + n.name(), e);
     }
     return mergeTip;
   }
@@ -964,8 +961,7 @@
   }
 
   public void markCleanMerges(
-      RevWalk rw, RevFlag canMergeFlag, CodeReviewCommit mergeTip, Set<RevCommit> alreadyAccepted)
-      throws IntegrationException {
+      RevWalk rw, RevFlag canMergeFlag, CodeReviewCommit mergeTip, Set<RevCommit> alreadyAccepted) {
     if (mergeTip == null) {
       // If mergeTip is null here, branchTip was null, indicating a new branch
       // at the start of the merge process. We also elected to merge nothing,
@@ -993,7 +989,7 @@
         }
       }
     } catch (IOException e) {
-      throw new IntegrationException("Cannot mark clean merges", e);
+      throw new StorageException("Cannot mark clean merges", e);
     }
   }
 
@@ -1003,8 +999,7 @@
       RevFlag canMergeFlag,
       CodeReviewCommit oldTip,
       CodeReviewCommit mergeTip,
-      Iterable<Change.Id> alreadyMerged)
-      throws IntegrationException {
+      Iterable<Change.Id> alreadyMerged) {
     if (mergeTip == null) {
       return expected;
     }
@@ -1035,7 +1030,7 @@
       }
       return Sets.difference(expected, found);
     } catch (IOException e) {
-      throw new IntegrationException("Cannot check if changes were merged", e);
+      throw new StorageException("Cannot check if changes were merged", e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java b/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
index 6faa7af..834356b 100644
--- a/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.validators.OnSubmitValidationListener.Arguments;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gerrit.server.submit.IntegrationException;
+import com.google.gerrit.server.submit.IntegrationConflictException;
 import com.google.gerrit.server.update.ChainedReceiveCommands;
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.inject.Inject;
@@ -38,12 +38,12 @@
 
   public void validate(
       Project.NameKey project, ObjectReader objectReader, ChainedReceiveCommands commands)
-      throws IntegrationException {
+      throws IntegrationConflictException {
     try (RevWalk rw = new RevWalk(objectReader)) {
       Arguments args = new Arguments(project, rw, commands);
       listeners.runEach(l -> l.preBranchUpdate(args), ValidationException.class);
     } catch (ValidationException e) {
-      throw new IntegrationException(e.getMessage(), e);
+      throw new IntegrationConflictException(e.getMessage(), e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index 93e65be..6eb6871d 100644
--- a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
-import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.submit.SubmitDryRun;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -163,7 +162,7 @@
           args.conflictsCache.put(conflictsKey, conflicts);
           return conflicts;
         }
-      } catch (IntegrationException | NoSuchProjectException | StorageException | IOException e) {
+      } catch (NoSuchProjectException | StorageException | IOException e) {
         ObjectId finalOther = other;
         warnWithOccasionalStackTrace(
             e,
@@ -181,8 +180,7 @@
       return 5;
     }
 
-    private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw)
-        throws IntegrationException {
+    private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw) {
       try {
         Set<RevCommit> accepted = new HashSet<>();
         SubmitDryRun.addCommits(changeDataCache.getAlreadyAccepted(repo), rw, accepted);
@@ -192,7 +190,7 @@
         }
         return accepted;
       } catch (StorageException | IOException e) {
-        throw new IntegrationException("Failed to determine already accepted commits.", e);
+        throw new StorageException("Failed to determine already accepted commits.", e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index 4886a4f..183e6fb 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -103,7 +102,7 @@
       return Response.ok(changeInfo);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
-    } catch (IntegrationException | NoSuchChangeException e) {
+    } catch (NoSuchChangeException e) {
       throw new ResourceConflictException(e.getMessage());
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 9354727..ea404ae 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -56,7 +56,7 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gerrit.server.submit.IntegrationException;
+import com.google.gerrit.server.submit.IntegrationConflictException;
 import com.google.gerrit.server.submit.MergeIdenticalTreeException;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.UpdateException;
@@ -155,15 +155,14 @@
    * @throws IOException Unable to open repository or read from the database.
    * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
    *     key exist in the branch.
-   * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
    * @throws UpdateException Problem updating the database using batchUpdateFactory.
    * @throws RestApiException Error such as invalid SHA1
    * @throws ConfigInvalidException Can't find account to notify.
    * @throws NoSuchProjectException Can't find project state.
    */
   public Result cherryPick(Change change, PatchSet patch, CherryPickInput input, BranchNameKey dest)
-      throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
-          RestApiException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, InvalidChangeOperationException, UpdateException, RestApiException,
+          ConfigInvalidException, NoSuchProjectException {
     return cherryPick(
         change,
         change.getProject(),
@@ -193,7 +192,6 @@
    * @throws IOException Unable to open repository or read from the database.
    * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
    *     key exist in the branch.
-   * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
    * @throws UpdateException Problem updating the database using batchUpdateFactory.
    * @throws RestApiException Error such as invalid SHA1
    * @throws ConfigInvalidException Can't find account to notify.
@@ -205,8 +203,8 @@
       ObjectId sourceCommit,
       CherryPickInput input,
       BranchNameKey dest)
-      throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
-          RestApiException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, InvalidChangeOperationException, UpdateException, RestApiException,
+          ConfigInvalidException, NoSuchProjectException {
     return cherryPick(
         sourceChange,
         project,
@@ -251,7 +249,6 @@
    * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
    *     key exist in the branch. Also thrown when idForNewChange is not null but cherry-pick only
    *     creates a new patchset rather than a new change.
-   * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
    * @throws UpdateException Problem updating the database using batchUpdateFactory.
    * @throws RestApiException Error such as invalid SHA1
    * @throws ConfigInvalidException Can't find account to notify.
@@ -270,8 +267,8 @@
       @Nullable ObjectId changeIdForNewChange,
       @Nullable Change.Id idForNewChange,
       @Nullable String groupName)
-      throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
-          RestApiException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, InvalidChangeOperationException, UpdateException, RestApiException,
+          ConfigInvalidException, NoSuchProjectException {
 
     IdentifiedUser identifiedUser = user.get();
     try (Repository git = gitManager.openRepository(project);
@@ -408,7 +405,7 @@
           return Result.create(changeId, cherryPickCommit.getFilesWithGitConflicts());
         }
       } catch (MergeIdenticalTreeException | MergeConflictException e) {
-        throw new IntegrationException("Cherry pick failed: " + e.getMessage());
+        throw new IntegrationConflictException("Cherry pick failed: " + e.getMessage());
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index 2773e29..f51257f 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -34,7 +33,6 @@
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -99,8 +97,6 @@
       return Response.ok(changeInfo);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
-    } catch (IntegrationException e) {
-      throw new ResourceConflictException(e.getMessage());
     }
   }
 }
diff --git a/java/com/google/gerrit/server/submit/CherryPick.java b/java/com/google/gerrit/server/submit/CherryPick.java
index 8b7b2cd..b66006a 100644
--- a/java/com/google/gerrit/server/submit/CherryPick.java
+++ b/java/com/google/gerrit/server/submit/CherryPick.java
@@ -45,8 +45,7 @@
   }
 
   @Override
-  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge) {
     List<CodeReviewCommit> sorted = CodeReviewCommit.ORDER.sortedCopy(toMerge);
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
     boolean first = true;
@@ -90,7 +89,7 @@
 
     @Override
     protected void updateRepoImpl(RepoContext ctx)
-        throws IntegrationException, IOException, MethodNotAllowedException {
+        throws IntegrationConflictException, IOException, MethodNotAllowedException {
       // If there is only one parent, a cherry-pick can be done by taking the
       // delta relative to that one parent and redoing that on the current merge
       // tip.
@@ -181,7 +180,7 @@
     }
 
     @Override
-    public void updateRepoImpl(RepoContext ctx) throws IntegrationException, IOException {
+    public void updateRepoImpl(RepoContext ctx) throws IntegrationConflictException, IOException {
       if (args.mergeUtil.hasMissingDependencies(args.mergeSorter, toMerge)) {
         // One or more dependencies were not met. The status was already marked
         // on the commit so we have nothing further to perform at this time.
@@ -217,8 +216,7 @@
   }
 
   static boolean dryRun(
-      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
-      throws IntegrationException {
+      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge) {
     return args.mergeUtil.canCherryPick(args.mergeSorter, args.repo, mergeTip, args.rw, toMerge);
   }
 }
diff --git a/java/com/google/gerrit/server/submit/FastForwardOnly.java b/java/com/google/gerrit/server/submit/FastForwardOnly.java
index 5a471ac..494884e 100644
--- a/java/com/google/gerrit/server/submit/FastForwardOnly.java
+++ b/java/com/google/gerrit/server/submit/FastForwardOnly.java
@@ -26,8 +26,7 @@
   }
 
   @Override
-  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge) {
     List<CodeReviewCommit> sorted = args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
     CodeReviewCommit newTipCommit =
@@ -53,8 +52,7 @@
   }
 
   static boolean dryRun(
-      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
-      throws IntegrationException {
+      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge) {
     return args.mergeUtil.canFastForward(args.mergeSorter, mergeTip, args.rw, toMerge);
   }
 }
diff --git a/java/com/google/gerrit/server/submit/FastForwardOp.java b/java/com/google/gerrit/server/submit/FastForwardOp.java
index c83e113..3fe4d8c 100644
--- a/java/com/google/gerrit/server/submit/FastForwardOp.java
+++ b/java/com/google/gerrit/server/submit/FastForwardOp.java
@@ -26,7 +26,7 @@
   }
 
   @Override
-  protected void updateRepoImpl(RepoContext ctx) throws IntegrationException {
+  protected void updateRepoImpl(RepoContext ctx) {
     if (args.project.is(BooleanProjectConfig.REJECT_EMPTY_COMMIT)
         && toMerge.getParentCount() > 0
         && toMerge.getTree().equals(toMerge.getParent(0).getTree())) {
diff --git a/java/com/google/gerrit/server/submit/IntegrationConflictException.java b/java/com/google/gerrit/server/submit/IntegrationConflictException.java
new file mode 100644
index 0000000..bfc02bb
--- /dev/null
+++ b/java/com/google/gerrit/server/submit/IntegrationConflictException.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.submit;
+
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+
+/**
+ * Exception to be thrown if integrating (aka merging) a change into the destination branch is not
+ * possible due to conflicts.
+ *
+ * <p>Throwing this exception results in a {@code 409 Conflict} response to the calling user. The
+ * exception message is returned as error message to the user.
+ */
+public class IntegrationConflictException extends ResourceConflictException {
+  private static final long serialVersionUID = 1L;
+
+  public IntegrationConflictException(String msg) {
+    super(msg);
+  }
+
+  public IntegrationConflictException(String msg, Throwable why) {
+    super(msg, why);
+  }
+}
diff --git a/java/com/google/gerrit/server/submit/IntegrationException.java b/java/com/google/gerrit/server/submit/IntegrationException.java
deleted file mode 100644
index 5028b76..0000000
--- a/java/com/google/gerrit/server/submit/IntegrationException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.submit;
-
-/** Indicates an integration operation (see {@link MergeOp}) failed. */
-public class IntegrationException extends Exception {
-  private static final long serialVersionUID = 1L;
-
-  public IntegrationException(String msg) {
-    super(msg);
-  }
-
-  public IntegrationException(Throwable why) {
-    super(why);
-  }
-
-  public IntegrationException(String msg, Throwable why) {
-    super(msg, why);
-  }
-}
diff --git a/java/com/google/gerrit/server/submit/MergeAlways.java b/java/com/google/gerrit/server/submit/MergeAlways.java
index 9aab854..c3f186a 100644
--- a/java/com/google/gerrit/server/submit/MergeAlways.java
+++ b/java/com/google/gerrit/server/submit/MergeAlways.java
@@ -25,8 +25,7 @@
   }
 
   @Override
-  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge) {
     List<CodeReviewCommit> sorted = args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
     if (args.mergeTip.getInitialTip() == null && !sorted.isEmpty()) {
@@ -43,8 +42,7 @@
   }
 
   static boolean dryRun(
-      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
-      throws IntegrationException {
+      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge) {
     return args.mergeUtil.canMerge(args.mergeSorter, args.repo, mergeTip, toMerge);
   }
 }
diff --git a/java/com/google/gerrit/server/submit/MergeIfNecessary.java b/java/com/google/gerrit/server/submit/MergeIfNecessary.java
index d49e7fe..82499b3 100644
--- a/java/com/google/gerrit/server/submit/MergeIfNecessary.java
+++ b/java/com/google/gerrit/server/submit/MergeIfNecessary.java
@@ -25,8 +25,7 @@
   }
 
   @Override
-  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge) {
     List<CodeReviewCommit> sorted = args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
 
@@ -48,8 +47,7 @@
   }
 
   static boolean dryRun(
-      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
-      throws IntegrationException {
+      SubmitDryRun.Arguments args, CodeReviewCommit mergeTip, CodeReviewCommit toMerge) {
     return args.mergeUtil.canFastForward(args.mergeSorter, mergeTip, args.rw, toMerge)
         || args.mergeUtil.canMerge(args.mergeSorter, args.repo, mergeTip, toMerge);
   }
diff --git a/java/com/google/gerrit/server/submit/MergeOneOp.java b/java/com/google/gerrit/server/submit/MergeOneOp.java
index 4555a32..f1b93e1 100644
--- a/java/com/google/gerrit/server/submit/MergeOneOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOneOp.java
@@ -28,7 +28,7 @@
   }
 
   @Override
-  public void updateRepoImpl(RepoContext ctx) throws IntegrationException, IOException {
+  public void updateRepoImpl(RepoContext ctx) throws IntegrationConflictException, IOException {
     PersonIdent caller =
         ctx.getIdentifiedUser()
             .newCommitterIdent(args.serverIdent.getWhen(), args.serverIdent.getTimeZone());
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 79ae43b..800fe0e 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -506,12 +506,7 @@
                     logger.atFine().log("Bypassing submit rules");
                     bypassSubmitRules(cs, isRetry);
                   }
-                  try {
-                    integrateIntoHistory(cs);
-                  } catch (IntegrationException e) {
-                    logger.atWarning().withCause(e).log("Error from integrateIntoHistory");
-                    throw new ResourceConflictException(e.getMessage(), e);
-                  }
+                  integrateIntoHistory(cs);
                   return null;
                 })
             .listener(retryTracker)
@@ -585,8 +580,7 @@
     }
   }
 
-  private void integrateIntoHistory(ChangeSet cs)
-      throws IntegrationException, RestApiException, UpdateException {
+  private void integrateIntoHistory(ChangeSet cs) throws RestApiException, UpdateException {
     checkArgument(!cs.furtherHiddenChanges(), "cannot integrate hidden changes into history");
     logger.atFine().log("Beginning merge attempt on %s", cs);
     Map<BranchNameKey, BranchBatch> toSubmit = new HashMap<>();
@@ -595,7 +589,7 @@
     try {
       cbb = cs.changesByBranch();
     } catch (StorageException e) {
-      throw new IntegrationException("Error reading changes to submit", e);
+      throw new StorageException("Error reading changes to submit", e);
     }
     Set<BranchNameKey> branches = cbb.keySet();
 
@@ -626,8 +620,10 @@
       }
     } catch (NoSuchProjectException e) {
       throw new ResourceNotFoundException(e.getMessage());
-    } catch (IOException | SubmoduleException e) {
-      throw new IntegrationException(e);
+    } catch (IOException e) {
+      throw new StorageException(e);
+    } catch (SubmoduleException e) {
+      throw new IntegrationConflictException(e.getMessage(), e);
     } catch (UpdateException e) {
       if (e.getCause() instanceof LockFailureException) {
         // Lock failures are a special case: RetryHelper depends on this specific causal chain in
@@ -645,13 +641,10 @@
       //
       // If you happen across one of these, the correct fix is to convert the
       // inner IntegrationException to a ResourceConflictException.
-      String msg;
-      if (e.getCause() instanceof IntegrationException) {
-        msg = e.getCause().getMessage();
-      } else {
-        msg = genericMergeError(cs);
+      if (e.getCause() instanceof IntegrationConflictException) {
+        throw (IntegrationConflictException) e.getCause();
       }
-      throw new IntegrationException(msg, e);
+      throw new StorageException(genericMergeError(cs), e);
     }
   }
 
@@ -665,7 +658,7 @@
 
   private List<SubmitStrategy> getSubmitStrategies(
       Map<BranchNameKey, BranchBatch> toSubmit, SubmoduleOp submoduleOp, boolean dryrun)
-      throws IntegrationException, NoSuchProjectException, IOException {
+      throws IntegrationConflictException, NoSuchProjectException, IOException {
     List<SubmitStrategy> strategies = new ArrayList<>();
     Set<BranchNameKey> allBranches = submoduleOp.getBranchesInOrder();
     Set<CodeReviewCommit> allCommits =
@@ -711,8 +704,7 @@
     return strategies;
   }
 
-  private Set<RevCommit> getAlreadyAccepted(OpenRepo or, CodeReviewCommit branchTip)
-      throws IntegrationException {
+  private Set<RevCommit> getAlreadyAccepted(OpenRepo or, CodeReviewCommit branchTip) {
     Set<RevCommit> alreadyAccepted = new HashSet<>();
 
     if (branchTip != null) {
@@ -731,7 +723,7 @@
         }
       }
     } catch (IOException e) {
-      throw new IntegrationException("Failed to determine already accepted commits.", e);
+      throw new StorageException("Failed to determine already accepted commits.", e);
     }
 
     logger.atFine().log("Found %d existing heads: %s", alreadyAccepted.size(), alreadyAccepted);
@@ -746,8 +738,7 @@
     abstract Set<CodeReviewCommit> commits();
   }
 
-  private BranchBatch validateChangeList(OpenRepo or, Collection<ChangeData> submitted)
-      throws IntegrationException {
+  private BranchBatch validateChangeList(OpenRepo or, Collection<ChangeData> submitted) {
     logger.atFine().log("Validating %d changes", submitted.size());
     Set<CodeReviewCommit> toSubmit = new LinkedHashSet<>(submitted.size());
     SetMultimap<ObjectId, PatchSet.Id> revisions = getRevisions(or, submitted);
@@ -862,8 +853,7 @@
     return new AutoValue_MergeOp_BranchBatch(submitType, toSubmit);
   }
 
-  private SetMultimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or, Collection<ChangeData> cds)
-      throws IntegrationException {
+  private SetMultimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or, Collection<ChangeData> cds) {
     try {
       List<String> refNames = new ArrayList<>(cds.size());
       for (ChangeData cd : cds) {
@@ -883,7 +873,7 @@
       }
       return revisions;
     } catch (IOException | StorageException e) {
-      throw new IntegrationException("Failed to validate changes", e);
+      throw new StorageException("Failed to validate changes", e);
     }
   }
 
@@ -892,14 +882,14 @@
     return str.isOk() ? str.type : null;
   }
 
-  private OpenRepo openRepo(Project.NameKey project) throws IntegrationException {
+  private OpenRepo openRepo(Project.NameKey project) {
     try {
       return orm.getRepo(project);
     } catch (NoSuchProjectException e) {
       logger.atWarning().log("Project %s no longer exists, abandoning open changes.", project);
       abandonAllOpenChangeForDeletedProject(project);
     } catch (IOException e) {
-      throw new IntegrationException("Error opening project " + project, e);
+      throw new StorageException("Error opening project " + project, e);
     }
     return null;
   }
diff --git a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
index 89468fd4..b32c712 100644
--- a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
+++ b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.change.NotifyResolver;
 import com.google.gerrit.server.git.CodeReviewCommit;
@@ -84,7 +85,7 @@
       branches = Maps.newHashMapWithExpectedSize(1);
     }
 
-    OpenBranch getBranch(BranchNameKey branch) throws IntegrationException {
+    OpenBranch getBranch(BranchNameKey branch) throws IntegrationConflictException {
       OpenBranch ob = branches.get(branch);
       if (ob == null) {
         ob = new OpenBranch(this, branch);
@@ -133,7 +134,7 @@
     final CodeReviewCommit oldTip;
     MergeTip mergeTip;
 
-    OpenBranch(OpenRepo or, BranchNameKey name) throws IntegrationException {
+    OpenBranch(OpenRepo or, BranchNameKey name) throws IntegrationConflictException {
       try {
         Ref ref = or.getRepo().exactRef(name.branch());
         if (ref != null) {
@@ -142,11 +143,11 @@
             || Objects.equals(RefNames.REFS_CONFIG, name.branch())) {
           oldTip = null;
         } else {
-          throw new IntegrationException(
+          throw new IntegrationConflictException(
               "The destination branch " + name + " does not exist anymore.");
         }
       } catch (IOException e) {
-        throw new IntegrationException("Cannot open branch " + name, e);
+        throw new StorageException("Cannot open branch " + name, e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index 65e18ad..30fb580 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -54,13 +54,12 @@
   }
 
   @Override
-  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge) {
     List<CodeReviewCommit> sorted;
     try {
       sorted = args.rebaseSorter.sort(toMerge);
     } catch (IOException | StorageException e) {
-      throw new IntegrationException("Commit sorting failed", e);
+      throw new StorageException("Commit sorting failed", e);
     }
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
     boolean first = true;
@@ -118,7 +117,7 @@
 
     @Override
     public void updateRepoImpl(RepoContext ctx)
-        throws IntegrationException, InvalidChangeOperationException, RestApiException, IOException,
+        throws InvalidChangeOperationException, RestApiException, IOException,
             PermissionBackendException {
       if (args.mergeUtil.canFastForward(
           args.mergeSorter, args.mergeTip.getCurrentTip(), args.rw, toMerge)) {
@@ -193,7 +192,7 @@
           rebaseOp.updateRepo(ctx);
         } catch (MergeConflictException | NoSuchChangeException e) {
           toMerge.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT);
-          throw new IntegrationException(
+          throw new IntegrationConflictException(
               "Cannot rebase " + toMerge.name() + ": " + e.getMessage(), e);
         }
         newCommit = args.rw.parseCommit(rebaseOp.getRebasedCommit());
@@ -260,7 +259,7 @@
     }
 
     @Override
-    public void updateRepoImpl(RepoContext ctx) throws IntegrationException, IOException {
+    public void updateRepoImpl(RepoContext ctx) throws IntegrationConflictException, IOException {
       // There are multiple parents, so this is a merge commit. We don't want
       // to rebase the merge as clients can't easily rebase their history with
       // that merge present and replaced by an equivalent merge with a different
@@ -306,8 +305,7 @@
       SubmitDryRun.Arguments args,
       Repository repo,
       CodeReviewCommit mergeTip,
-      CodeReviewCommit toMerge)
-      throws IntegrationException {
+      CodeReviewCommit toMerge) {
     // Test for merge instead of cherry pick to avoid false negatives
     // on commit chains.
     return args.mergeUtil.canMerge(args.mergeSorter, repo, mergeTip, toMerge);
diff --git a/java/com/google/gerrit/server/submit/SubmitDryRun.java b/java/com/google/gerrit/server/submit/SubmitDryRun.java
index fa4e156..bcd7923 100644
--- a/java/com/google/gerrit/server/submit/SubmitDryRun.java
+++ b/java/com/google/gerrit/server/submit/SubmitDryRun.java
@@ -22,6 +22,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.CodeReviewCommit;
@@ -117,7 +118,7 @@
       ObjectId tip,
       ObjectId toMerge,
       Set<RevCommit> alreadyAccepted)
-      throws IntegrationException, NoSuchProjectException, IOException {
+      throws NoSuchProjectException, IOException {
     CodeReviewCommit tipCommit = rw.parseCommit(tip);
     CodeReviewCommit toMergeCommit = rw.parseCommit(toMerge);
     RevFlag canMerge = rw.newFlag("CAN_MERGE");
@@ -152,7 +153,7 @@
       default:
         String errorMsg = "No submit strategy for: " + submitType;
         logger.atSevere().log(errorMsg);
-        throw new IntegrationException(errorMsg);
+        throw new StorageException(errorMsg);
     }
   }
 
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategy.java b/java/com/google/gerrit/server/submit/SubmitStrategy.java
index 85d39b8..4010ad7 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategy.java
@@ -249,12 +249,8 @@
    * @param toMerge the set of submitted commits that should be merged using this submit strategy.
    *     Implementations are responsible for ordering of commits, and will not modify the input in
    *     place.
-   * @throws IntegrationException if an error occurred initializing the operations (as opposed to an
-   *     error during execution, which will be reported only when the batch update executes the
-   *     operations).
    */
-  public final void addOps(BatchUpdate bu, Set<CodeReviewCommit> toMerge)
-      throws IntegrationException {
+  public final void addOps(BatchUpdate bu, Set<CodeReviewCommit> toMerge) {
     List<SubmitStrategyOp> ops = buildOps(toMerge);
     Set<CodeReviewCommit> added = Sets.newHashSetWithExpectedSize(ops.size());
 
@@ -289,6 +285,5 @@
     }
   }
 
-  protected abstract List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge)
-      throws IntegrationException;
+  protected abstract List<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge);
 }
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
index a015665..1cc78ff 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
@@ -17,6 +17,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.SubmissionId;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.server.IdentifiedUser;
@@ -55,8 +56,7 @@
       SubmissionId submissionId,
       SubmitInput submitInput,
       SubmoduleOp submoduleOp,
-      boolean dryrun)
-      throws IntegrationException {
+      boolean dryrun) {
     SubmitStrategy.Arguments args =
         argsFactory.create(
             submitType,
@@ -89,7 +89,7 @@
       default:
         String errorMsg = "No submit strategy for: " + submitType;
         logger.atSevere().log(errorMsg);
-        throw new IntegrationException(errorMsg);
+        throw new StorageException(errorMsg);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
index f8bcfc1..8cc595d 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
@@ -50,13 +50,9 @@
 
   @Override
   public void afterUpdateRepos() throws ResourceConflictException {
-    try {
-      markCleanMerges();
-      List<Change.Id> alreadyMerged = checkCommitStatus();
-      findUnmergedChanges(alreadyMerged);
-    } catch (IntegrationException e) {
-      throw new ResourceConflictException(e.getMessage(), e);
-    }
+    markCleanMerges();
+    List<Change.Id> alreadyMerged = checkCommitStatus();
+    findUnmergedChanges(alreadyMerged);
   }
 
   @Override
@@ -66,8 +62,7 @@
     }
   }
 
-  private void findUnmergedChanges(List<Change.Id> alreadyMerged)
-      throws ResourceConflictException, IntegrationException {
+  private void findUnmergedChanges(List<Change.Id> alreadyMerged) throws ResourceConflictException {
     for (SubmitStrategy strategy : strategies) {
       if (strategy instanceof CherryPick) {
         // Can't do this sanity check for CherryPick since:
@@ -91,7 +86,7 @@
     commitStatus.maybeFailVerbose();
   }
 
-  private void markCleanMerges() throws IntegrationException {
+  private void markCleanMerges() {
     for (SubmitStrategy strategy : strategies) {
       SubmitStrategy.Arguments args = strategy.args;
       RevCommit initialTip = args.mergeTip.getInitialTip();
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index abd8e46..1625528 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -138,8 +138,7 @@
     args.submoduleOp.addBranchTip(getDest(), tipAfter);
   }
 
-  private void checkProjectConfig(RepoContext ctx, CodeReviewCommit commit)
-      throws IntegrationException {
+  private void checkProjectConfig(RepoContext ctx, CodeReviewCommit commit) {
     String refName = getDest().branch();
     if (RefNames.REFS_CONFIG.equals(refName)) {
       logger.atFine().log("Loading new configuration from %s", RefNames.REFS_CONFIG);
@@ -147,7 +146,7 @@
         ProjectConfig cfg = args.projectConfigFactory.create(getProject());
         cfg.load(ctx.getRevWalk(), commit);
       } catch (Exception e) {
-        throw new IntegrationException(
+        throw new StorageException(
             "Submit would store invalid"
                 + " project configuration "
                 + commit.name()
@@ -542,7 +541,8 @@
    *
    * @param commit
    */
-  protected CodeReviewCommit amendGitlink(CodeReviewCommit commit) throws IntegrationException {
+  protected CodeReviewCommit amendGitlink(CodeReviewCommit commit)
+      throws IntegrationConflictException {
     if (!args.submoduleOp.hasSubscription(args.destBranch)) {
       return commit;
     }
@@ -550,8 +550,11 @@
     // Modify the commit with gitlink update
     try {
       return args.submoduleOp.composeGitlinksCommit(args.destBranch, commit);
-    } catch (SubmoduleException | IOException e) {
-      throw new IntegrationException(
+    } catch (IOException e) {
+      throw new StorageException(
+          "cannot update gitlink for the commit at branch: " + args.destBranch, e);
+    } catch (SubmoduleException e) {
+      throw new IntegrationConflictException(
           "cannot update gitlink for the commit at branch: " + args.destBranch, e);
     }
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index 388c4f4..d742fad 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -27,11 +27,11 @@
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.server.config.UrlFormatter;
 import com.google.gerrit.server.git.ChangeMessageModifier;
 import com.google.gerrit.server.query.change.ChangeData;
@@ -129,8 +129,7 @@
     ChangeMessageModifier modifier2 = (msg, orig, tip, dest) -> msg + "A-footer: value\n";
     try (Registration registration =
         extensionRegistry.newRegistration().add(modifier1).add(modifier2)) {
-      ResourceConflictException thrown =
-          assertThrows(ResourceConflictException.class, () -> submitWithRebase());
+      StorageException thrown = assertThrows(StorageException.class, () -> submitWithRebase());
       Throwable cause = Throwables.getRootCause(thrown);
       assertThat(cause).isInstanceOf(RuntimeException.class);
       assertThat(cause).hasMessageThat().isEqualTo("boom");
@@ -146,8 +145,7 @@
             .newRegistration()
             .add(modifier1, "modifier-1")
             .add(modifier2, "modifier-2")) {
-      ResourceConflictException thrown =
-          assertThrows(ResourceConflictException.class, () -> submitWithRebase());
+      StorageException thrown = assertThrows(StorageException.class, () -> submitWithRebase());
       Throwable cause = Throwables.getRootCause(thrown);
       assertThat(cause).isInstanceOf(RuntimeException.class);
       assertThat(cause)