Merge "Use change number instead of triplet."
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
index 5d439b6..e4e11da 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
@@ -15,12 +15,14 @@
package com.googlesource.gerrit.plugins.automerger;
import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.inject.Inject;
@@ -41,9 +43,11 @@
public class ConfigLoader {
private static final Logger log = LoggerFactory.getLogger(ConfigLoader.class);
private static final String BRANCH_DELIMITER = ":";
+ private static final String DEFAULT_CONFLICT_MESSAGE = "Merge conflict found on ${branch}";
private final GerritApi gApi;
private final String pluginName;
+ private final String canonicalWebUrl;
private final AllProjectsName allProjectsName;
private final PluginConfigFactory cfgFactory;
@@ -60,8 +64,10 @@
GerritApi gApi,
AllProjectsName allProjectsName,
@PluginName String pluginName,
+ @CanonicalWebUrl String canonicalWebUrl,
PluginConfigFactory cfgFactory) {
this.gApi = gApi;
+ this.canonicalWebUrl = canonicalWebUrl;
this.pluginName = pluginName;
this.cfgFactory = cfgFactory;
this.allProjectsName = allProjectsName;
@@ -124,6 +130,34 @@
}
/**
+ * Returns the hostName.
+ *
+ * <p>Uses the hostName defined in the configuration if specified. If not, defaults to the
+ * canonicalWebUrl.
+ *
+ * @return Returns the hostname
+ * @throws ConfigInvalidException
+ */
+ public String getHostName() throws ConfigInvalidException {
+ String hostName = getConfig().getString("global", null, "hostName");
+ return hostName != null ? hostName : canonicalWebUrl;
+ }
+
+ /**
+ * Returns a string to append to the end of the merge conflict message.
+ *
+ * @return The message string, or the empty string if nothing is specified.
+ * @throws ConfigInvalidException
+ */
+ public String getConflictMessage() throws ConfigInvalidException {
+ String conflictMessage = getConfig().getString("global", null, "conflictMessage");
+ if (Strings.isNullOrEmpty(conflictMessage)) {
+ conflictMessage = DEFAULT_CONFLICT_MESSAGE;
+ }
+ return conflictMessage;
+ }
+
+ /**
* Get the projects that should be merged for the given pair of branches.
*
* @param fromBranch Branch we are merging from.
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
index fcfb82f..f953898 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
@@ -45,6 +45,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.UUID;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.slf4j.Logger;
@@ -241,7 +242,7 @@
+ " succeeded!";
reviewInput.notify = NotifyHandling.NONE;
} catch (FailedMergeException e) {
- reviewInput.message = e.displayConflicts();
+ reviewInput.message = e.getDisplayString();
reviewInput.notify = NotifyHandling.ALL;
vote = -1;
}
@@ -264,7 +265,8 @@
*/
public void createDownstreamMerges(MultipleDownstreamMergeInput mdsMergeInput)
throws RestApiException, FailedMergeException, ConfigInvalidException {
- Map<String, String> failedMerges = new HashMap<String, String>();
+ // Map from branch to error message
+ Map<String, String> failedMergeBranchMap = new TreeMap<String, String>();
List<Integer> existingDownstream;
for (String downstreamBranch : mdsMergeInput.dsBranchMap.keySet()) {
@@ -309,16 +311,20 @@
createSingleDownstreamMerge(sdsMergeInput);
}
} catch (MergeConflictException e) {
- log.debug("Merge conflict from {} to {}", mdsMergeInput.currentRevision, downstreamBranch);
- failedMerges.put(downstreamBranch, e.getMessage());
+ failedMergeBranchMap.put(downstreamBranch, e.getMessage());
log.debug("Abandoning downstream of {}", mdsMergeInput.sourceId);
abandonDownstream(
gApi.changes().id(mdsMergeInput.sourceId).info(), mdsMergeInput.currentRevision);
}
}
- if (!failedMerges.keySet().isEmpty()) {
- throw new FailedMergeException(failedMerges);
+ if (!failedMergeBranchMap.isEmpty()) {
+ throw new FailedMergeException(
+ failedMergeBranchMap,
+ mdsMergeInput.currentRevision,
+ config.getHostName(),
+ config.getConflictMessage(),
+ mdsMergeInput.topic);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/FailedMergeException.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/FailedMergeException.java
index b51d52f..6fd7983 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/FailedMergeException.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/FailedMergeException.java
@@ -14,17 +14,32 @@
package com.googlesource.gerrit.plugins.automerger;
import com.google.common.base.Joiner;
-
+import com.google.gerrit.common.data.ParameterizedString;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/** Exception class for merge conflicts. */
class FailedMergeException extends Exception {
private static final int MAX_CONFLICT_MESSAGE_LENGTH = 10000;
+ public final String currentRevision;
+ public final String conflictMessage;
+ public final String hostName;
+ public final String topic;
+ public final Map<String, String> failedMergeBranchMap;
- public final Map<String, String> failedMerges;
-
- FailedMergeException(Map<String, String> failedMerges) {
- this.failedMerges = failedMerges;
+ FailedMergeException(
+ Map<String, String> failedMergeBranchMap,
+ String currentRevision,
+ String hostName,
+ String conflictMessage,
+ String topic) {
+ this.failedMergeBranchMap = failedMergeBranchMap;
+ this.currentRevision = currentRevision;
+ this.hostName = hostName;
+ this.conflictMessage = conflictMessage;
+ this.topic = topic;
}
/**
@@ -32,38 +47,36 @@
*
* @return A string representation of the conflicts.
*/
- public String displayConflicts() {
- StringBuilder output = new StringBuilder();
- output.append("Merge conflict found on ");
- output.append(failedMergeKeys());
- output.append(". Please follow instructions at go/resolveconflict ");
- output.append("to resolve this merge conflict.\n\n");
-
- for (Map.Entry<String, String> entry : failedMerges.entrySet()) {
- String branch = entry.getKey();
- String message = entry.getValue();
- String conflictMessage = message;
- boolean truncated = false;
- if (message.length() > MAX_CONFLICT_MESSAGE_LENGTH) {
- conflictMessage = message.substring(0, MAX_CONFLICT_MESSAGE_LENGTH);
- truncated = true;
- }
- output.append(branch);
- output.append(":\n");
- output.append(conflictMessage);
- if (truncated) {
- output.append("...\n\n");
- }
+ public String getDisplayString() {
+ List<String> conflictMessages = new ArrayList<String>();
+ for (Map.Entry<String, String> branchMapEntry : failedMergeBranchMap.entrySet()) {
+ String branch = branchMapEntry.getKey();
+ String mergeConflictMessage = branchMapEntry.getValue();
+ conflictMessages.add(
+ assembleConflictMessage(
+ conflictMessage, getSubstitutionMap(branch, mergeConflictMessage)));
}
- return output.toString();
+
+ return Joiner.on("\n").join(conflictMessages);
}
- /**
- * Get the branches that we failed to merge to.
- *
- * @return The comma-separated branches that we failed to merge to.
- */
- public String failedMergeKeys() {
- return Joiner.on(", ").join(failedMerges.keySet());
+ private String assembleConflictMessage(String errorString, Map<String, String> substitutionMap) {
+ ParameterizedString pattern = new ParameterizedString(errorString);
+
+ String modifiedString = pattern.replace(substitutionMap);
+ if (errorString.length() > MAX_CONFLICT_MESSAGE_LENGTH) {
+ modifiedString = modifiedString.substring(0, MAX_CONFLICT_MESSAGE_LENGTH) + "\n...";
+ }
+ return modifiedString;
+ }
+
+ private Map<String, String> getSubstitutionMap(String branch, String mergeConflictMessage) {
+ Map<String, String> substitutionMap = new HashMap<>();
+ substitutionMap.put("branch", branch);
+ substitutionMap.put("revision", currentRevision);
+ substitutionMap.put("conflict", mergeConflictMessage);
+ substitutionMap.put("hostname", hostName);
+ substitutionMap.put("topic", topic);
+ return substitutionMap;
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
index 86ae78b..c2f9637 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
@@ -45,6 +45,7 @@
private ConfigLoader configLoader;
@Inject private AllProjectsName allProjectsName;
@Inject private PluginConfigFactory cfgFactory;
+ @Inject private String canonicalWebUrl;
private Project.NameKey manifestNameKey;
@Test
@@ -174,6 +175,19 @@
loadConfig(resourceName);
}
+ @Test
+ public void getDefaultConflictMessageTest() throws Exception {
+ defaultSetup("automerger.config");
+ assertThat(configLoader.getConflictMessage()).isEqualTo("Merge conflict found on ${branch}");
+ }
+
+ @Test
+ public void getMultilineConflictMessageTest() throws Exception {
+ defaultSetup("alternate.config");
+ assertThat(configLoader.getConflictMessage())
+ .isEqualTo("line1\n" + "line2\n" + "line3 ${branch}\n" + "line4");
+ }
+
private void setupTestRepo(
String resourceName, Project.NameKey projectNameKey, String branchName, String filename)
throws Exception {
@@ -208,6 +222,7 @@
private void loadConfig(String configFilename) throws Exception {
pushConfig(configFilename);
- configLoader = new ConfigLoader(gApi, allProjectsName, "automerger", cfgFactory);
+ configLoader =
+ new ConfigLoader(gApi, allProjectsName, "automerger", canonicalWebUrl, cfgFactory);
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/FailedMergeExceptionTest.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/FailedMergeExceptionTest.java
new file mode 100644
index 0000000..235272d
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/FailedMergeExceptionTest.java
@@ -0,0 +1,127 @@
+// Copyright (C) 2017 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.googlesource.gerrit.plugins.automerger;
+
+import java.util.TreeMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+public class FailedMergeExceptionTest {
+ private Map<String, String> failedMergeBranches;
+ private String currentRevision;
+ private String hostName;
+ private String topic;
+
+ @Before
+ public void setUp() throws Exception {
+ failedMergeBranches = new TreeMap<String, String>();
+ failedMergeBranches.put("branch1", "branch1 merge conflict");
+ failedMergeBranches.put("branch2", "branch2 merge conflict");
+ failedMergeBranches.put("branch3", "branch3 merge conflict");
+ currentRevision = "d3adb33f";
+ hostName = "livingbeef.example.com";
+ topic = "testtopic";
+ }
+
+ @Test
+ public void conflictMessageTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches,
+ currentRevision,
+ hostName,
+ "Merge conflict found on ${branch}",
+ topic);
+ String branch1String = "Merge conflict found on branch1";
+ String branch2String = "Merge conflict found on branch2";
+ String branch3String = "Merge conflict found on branch3";
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly(branch1String, branch2String, branch3String);
+ }
+
+ @Test
+ public void customConflictMessageTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(failedMergeBranches, currentRevision, hostName, "asdf", topic);
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly("asdf", "asdf", "asdf");
+ }
+
+ @Test
+ public void customConflictMessageWithRevisionSubstitutionTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches, currentRevision, hostName, "${branch} ${revision}", topic);
+ String branch1String = "branch1 " + currentRevision;
+ String branch2String = "branch2 " + currentRevision;
+ String branch3String = "branch3 " + currentRevision;
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly(branch1String, branch2String, branch3String);
+ }
+
+ @Test
+ public void customConflictMessageWithConflictSubstitutionTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches, currentRevision, hostName, "${branch}: ${conflict}", topic);
+ String branch1String = "branch1: branch1 merge conflict";
+ String branch2String = "branch2: branch2 merge conflict";
+ String branch3String = "branch3: branch3 merge conflict";
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly(branch1String, branch2String, branch3String);
+ }
+
+ @Test
+ public void customConflictMessageWithHostNameTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches, currentRevision, hostName, "${hostname}: ${conflict}", topic);
+ String branch1String = hostName + ": branch1 merge conflict";
+ String branch2String = hostName + ": branch2 merge conflict";
+ String branch3String = hostName + ": branch3 merge conflict";
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly(branch1String, branch2String, branch3String);
+ }
+
+ @Test
+ public void customConflictMessageWitTopicTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches, currentRevision, hostName, "i am ${topic}", topic);
+ String conflictMessage = "i am testtopic";
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly("i am testtopic", "i am testtopic", "i am testtopic");
+ }
+
+ @Test
+ public void customConflictMessageMultilineTest() throws Exception {
+ FailedMergeException fme =
+ new FailedMergeException(
+ failedMergeBranches, currentRevision, hostName, "${branch}a\nb\nc\nd", topic);
+ assertThat(fme.getDisplayString().split("\\n"))
+ .asList()
+ .containsExactly(
+ "branch1a", "b", "c", "d", "branch2a", "b", "c", "d", "branch3a", "b", "c", "d");
+ }
+}
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config
index eaaf49f..001a1cf 100644
--- a/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config
@@ -12,4 +12,8 @@
blankMerge = .*DO NOT MERGE.*
manifestFile = default.xml
manifestProject = platform/manifest
- ignoreProjects = platform/ignore/me
\ No newline at end of file
+ ignoreProjects = platform/ignore/me
+ conflictMessage = line1\n\
+line2\n\
+line3 ${branch}\n\
+line4
\ No newline at end of file