Merge changes Ice62183c,I41c3c444
* changes: MergeOp: Report change id in case of submit rule problems Submit: Include ancestors into problem description
This commit is contained in:
@@ -871,6 +871,17 @@ abbreviated commit SHA-1 (`c9c0edb`).
|
|||||||
+
|
+
|
||||||
Default is "Submit patch set ${patchSet} into ${branch}".
|
Default is "Submit patch set ${patchSet} into ${branch}".
|
||||||
|
|
||||||
|
[[change.submitTooltipAncestors]]change.submitTooltipAncestors::
|
||||||
|
+
|
||||||
|
Tooltip for the submit button if there are ancestors which would
|
||||||
|
also be submitted by submitting the change. Additionally to the variables
|
||||||
|
as in link:#change.submitTooltip[change.submitTooltip], there is the
|
||||||
|
variable `${submitSize}` indicating the number of changes which are
|
||||||
|
submitted.
|
||||||
|
+
|
||||||
|
Default is "Submit all ${topicSize} changes of the same topic (${submitSize}
|
||||||
|
changes including ancestors and other changes related by topic)".
|
||||||
|
|
||||||
[[change.submitWholeTopic]]change.submitWholeTopic::
|
[[change.submitWholeTopic]]change.submitWholeTopic::
|
||||||
+
|
+
|
||||||
Determines if the submit button submits the whole topic instead of
|
Determines if the submit button submits the whole topic instead of
|
||||||
|
@@ -52,15 +52,14 @@ public class ActionsIT extends AbstractDaemonTest {
|
|||||||
commonActionsAssertions(actions);
|
commonActionsAssertions(actions);
|
||||||
// We want to treat a single change in a topic not as a whole topic,
|
// We want to treat a single change in a topic not as a whole topic,
|
||||||
// so regardless of how submitWholeTopic is configured:
|
// so regardless of how submitWholeTopic is configured:
|
||||||
noSubmitWholeTopicAssertions(actions);
|
noSubmitWholeTopicAssertions(actions, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void revisionActionsTwoChangeChangesInTopic() throws Exception {
|
public void revisionActionsTwoChangesInTopic() throws Exception {
|
||||||
String changeId = createChangeWithTopic().getChangeId();
|
String changeId = createChangeWithTopic().getChangeId();
|
||||||
approve(changeId);
|
approve(changeId);
|
||||||
// create another change with the same topic
|
String changeId2 = createChangeWithTopic().getChangeId();
|
||||||
createChangeWithTopic().getChangeId();
|
|
||||||
Map<String, ActionInfo> actions = getActions(changeId);
|
Map<String, ActionInfo> actions = getActions(changeId);
|
||||||
commonActionsAssertions(actions);
|
commonActionsAssertions(actions);
|
||||||
if (isSubmitWholeTopicEnabled()) {
|
if (isSubmitWholeTopicEnabled()) {
|
||||||
@@ -68,14 +67,19 @@ public class ActionsIT extends AbstractDaemonTest {
|
|||||||
assertThat(info.enabled).isNull();
|
assertThat(info.enabled).isNull();
|
||||||
assertThat(info.label).isEqualTo("Submit whole topic");
|
assertThat(info.label).isEqualTo("Submit whole topic");
|
||||||
assertThat(info.method).isEqualTo("POST");
|
assertThat(info.method).isEqualTo("POST");
|
||||||
assertThat(info.title).isEqualTo("Other changes in this topic are not ready");
|
assertThat(info.title).isEqualTo("This change depends on other " +
|
||||||
|
"changes which are not ready");
|
||||||
} else {
|
} else {
|
||||||
noSubmitWholeTopicAssertions(actions);
|
noSubmitWholeTopicAssertions(actions, 1);
|
||||||
|
|
||||||
|
assertThat(getActions(changeId2).get("submit")).isNull();
|
||||||
|
approve(changeId2);
|
||||||
|
noSubmitWholeTopicAssertions(getActions(changeId2), 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void revisionActionsTwoChangeChangesInTopic_conflicting() throws Exception {
|
public void revisionActionsTwoChangesInTopic_conflicting() throws Exception {
|
||||||
String changeId = createChangeWithTopic().getChangeId();
|
String changeId = createChangeWithTopic().getChangeId();
|
||||||
approve(changeId);
|
approve(changeId);
|
||||||
|
|
||||||
@@ -99,38 +103,66 @@ public class ActionsIT extends AbstractDaemonTest {
|
|||||||
assertThat(info.label).isEqualTo("Submit whole topic");
|
assertThat(info.label).isEqualTo("Submit whole topic");
|
||||||
assertThat(info.method).isEqualTo("POST");
|
assertThat(info.method).isEqualTo("POST");
|
||||||
assertThat(info.title).isEqualTo(
|
assertThat(info.title).isEqualTo(
|
||||||
"Clicking the button would fail for other changes in the topic");
|
"Clicking the button would fail for other changes");
|
||||||
} else {
|
} else {
|
||||||
noSubmitWholeTopicAssertions(actions);
|
noSubmitWholeTopicAssertions(actions, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void revisionActionsTwoChangeChangesInTopicReady() throws Exception {
|
public void revisionActionsTwoChangesInTopicWithAncestorReady()
|
||||||
String changeId = createChangeWithTopic().getChangeId();
|
throws Exception {
|
||||||
|
String changeId = createChange().getChangeId();
|
||||||
approve(changeId);
|
approve(changeId);
|
||||||
|
approve(changeId);
|
||||||
|
String changeId1 = createChangeWithTopic().getChangeId();
|
||||||
|
approve(changeId1);
|
||||||
// create another change with the same topic
|
// create another change with the same topic
|
||||||
String changeId2 = createChangeWithTopic().getChangeId();
|
String changeId2 = createChangeWithTopic().getChangeId();
|
||||||
approve(changeId2);
|
approve(changeId2);
|
||||||
Map<String, ActionInfo> actions = getActions(changeId);
|
Map<String, ActionInfo> actions = getActions(changeId1);
|
||||||
commonActionsAssertions(actions);
|
commonActionsAssertions(actions);
|
||||||
if (isSubmitWholeTopicEnabled()) {
|
if (isSubmitWholeTopicEnabled()) {
|
||||||
ActionInfo info = actions.get("submit");
|
ActionInfo info = actions.get("submit");
|
||||||
assertThat(info.enabled).isTrue();
|
assertThat(info.enabled).isTrue();
|
||||||
assertThat(info.label).isEqualTo("Submit whole topic");
|
assertThat(info.label).isEqualTo("Submit whole topic");
|
||||||
assertThat(info.method).isEqualTo("POST");
|
assertThat(info.method).isEqualTo("POST");
|
||||||
assertThat(info.title).isEqualTo("Submit all 2 changes of the same topic");
|
assertThat(info.title).isEqualTo("Submit all 2 changes of the same " +
|
||||||
|
"topic (3 changes including ancestors " +
|
||||||
|
"and other changes related by topic)");
|
||||||
} else {
|
} else {
|
||||||
noSubmitWholeTopicAssertions(actions);
|
noSubmitWholeTopicAssertions(actions, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void noSubmitWholeTopicAssertions(Map<String, ActionInfo> actions) {
|
@Test
|
||||||
|
public void revisionActionsReadyWithAncestors() throws Exception {
|
||||||
|
String changeId = createChange().getChangeId();
|
||||||
|
approve(changeId);
|
||||||
|
approve(changeId);
|
||||||
|
String changeId1 = createChange().getChangeId();
|
||||||
|
approve(changeId1);
|
||||||
|
String changeId2 = createChangeWithTopic().getChangeId();
|
||||||
|
approve(changeId2);
|
||||||
|
Map<String, ActionInfo> actions = getActions(changeId2);
|
||||||
|
commonActionsAssertions(actions);
|
||||||
|
// The topic contains only one change, so standard text applies
|
||||||
|
noSubmitWholeTopicAssertions(actions, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noSubmitWholeTopicAssertions(Map<String, ActionInfo> actions,
|
||||||
|
int nrChanges) {
|
||||||
ActionInfo info = actions.get("submit");
|
ActionInfo info = actions.get("submit");
|
||||||
assertThat(info.enabled).isTrue();
|
assertThat(info.enabled).isTrue();
|
||||||
assertThat(info.label).isEqualTo("Submit");
|
assertThat(info.label).isEqualTo("Submit");
|
||||||
assertThat(info.method).isEqualTo("POST");
|
assertThat(info.method).isEqualTo("POST");
|
||||||
assertThat(info.title).isEqualTo("Submit patch set 1 into master");
|
if (nrChanges == 1) {
|
||||||
|
assertThat(info.title).isEqualTo("Submit patch set 1 into master");
|
||||||
|
} else {
|
||||||
|
assertThat(info.title).isEqualTo(String.format(
|
||||||
|
"Submit patch set 1 and ancestors (%d changes " +
|
||||||
|
"altogether) into master", nrChanges));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commonActionsAssertions(Map<String, ActionInfo> actions) {
|
private void commonActionsAssertions(Map<String, ActionInfo> actions) {
|
||||||
|
@@ -46,6 +46,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
|
|||||||
import com.google.gerrit.server.git.ChangeSet;
|
import com.google.gerrit.server.git.ChangeSet;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.git.MergeOp;
|
import com.google.gerrit.server.git.MergeOp;
|
||||||
|
import com.google.gerrit.server.git.MergeSuperSet;
|
||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.project.SubmitRuleEvaluator;
|
import com.google.gerrit.server.project.SubmitRuleEvaluator;
|
||||||
@@ -64,8 +65,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -77,16 +76,21 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
|
|
||||||
private static final String DEFAULT_TOOLTIP =
|
private static final String DEFAULT_TOOLTIP =
|
||||||
"Submit patch set ${patchSet} into ${branch}";
|
"Submit patch set ${patchSet} into ${branch}";
|
||||||
|
private static final String DEFAULT_TOOLTIP_ANCESTORS =
|
||||||
|
"Submit patch set ${patchSet} and ancestors (${submitSize} changes " +
|
||||||
|
"altogether) into ${branch}";
|
||||||
private static final String DEFAULT_TOPIC_TOOLTIP =
|
private static final String DEFAULT_TOPIC_TOOLTIP =
|
||||||
"Submit all ${topicSize} changes of the same topic";
|
"Submit all ${topicSize} changes of the same topic " +
|
||||||
private static final String BLOCKED_TOPIC_TOOLTIP =
|
"(${submitSize} changes including ancestors and other " +
|
||||||
"Other changes in this topic are not ready";
|
"changes related by topic)";
|
||||||
private static final String BLOCKED_HIDDEN_TOPIC_TOOLTIP =
|
private static final String BLOCKED_SUBMIT_TOOLTIP =
|
||||||
"Other hidden changes in this topic are not ready";
|
"This change depends on other changes which are not ready";
|
||||||
private static final String CLICK_FAILURE_OTHER_TOOLTIP =
|
private static final String BLOCKED_HIDDEN_SUBMIT_TOOLTIP =
|
||||||
"Clicking the button would fail for other changes in the topic";
|
"This change depends on other hidden changes which are not ready";
|
||||||
private static final String CLICK_FAILURE_TOOLTIP =
|
private static final String CLICK_FAILURE_TOOLTIP =
|
||||||
"Clicking the button would fail.";
|
"Clicking the button would fail";
|
||||||
|
private static final String CLICK_FAILURE_OTHER_TOOLTIP =
|
||||||
|
"Clicking the button would fail for other changes";
|
||||||
|
|
||||||
public static class Output {
|
public static class Output {
|
||||||
transient Change change;
|
transient Change change;
|
||||||
@@ -100,11 +104,14 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final ChangeData.Factory changeDataFactory;
|
private final ChangeData.Factory changeDataFactory;
|
||||||
private final ChangeMessagesUtil cmUtil;
|
private final ChangeMessagesUtil cmUtil;
|
||||||
|
private final ChangeControl.GenericFactory changeControlFactory;
|
||||||
private final Provider<MergeOp> mergeOpProvider;
|
private final Provider<MergeOp> mergeOpProvider;
|
||||||
|
private final MergeSuperSet mergeSuperSet;
|
||||||
private final AccountsCollection accounts;
|
private final AccountsCollection accounts;
|
||||||
private final ChangesCollection changes;
|
private final ChangesCollection changes;
|
||||||
private final String label;
|
private final String label;
|
||||||
private final ParameterizedString titlePattern;
|
private final ParameterizedString titlePattern;
|
||||||
|
private final ParameterizedString titlePatternWithAncestors;
|
||||||
private final String submitTopicLabel;
|
private final String submitTopicLabel;
|
||||||
private final ParameterizedString submitTopicTooltip;
|
private final ParameterizedString submitTopicTooltip;
|
||||||
private final boolean submitWholeTopic;
|
private final boolean submitWholeTopic;
|
||||||
@@ -115,7 +122,9 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
ChangeData.Factory changeDataFactory,
|
ChangeData.Factory changeDataFactory,
|
||||||
ChangeMessagesUtil cmUtil,
|
ChangeMessagesUtil cmUtil,
|
||||||
|
ChangeControl.GenericFactory changeControlFactory,
|
||||||
Provider<MergeOp> mergeOpProvider,
|
Provider<MergeOp> mergeOpProvider,
|
||||||
|
MergeSuperSet mergeSuperSet,
|
||||||
AccountsCollection accounts,
|
AccountsCollection accounts,
|
||||||
ChangesCollection changes,
|
ChangesCollection changes,
|
||||||
@GerritServerConfig Config cfg,
|
@GerritServerConfig Config cfg,
|
||||||
@@ -124,7 +133,9 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.changeDataFactory = changeDataFactory;
|
this.changeDataFactory = changeDataFactory;
|
||||||
this.cmUtil = cmUtil;
|
this.cmUtil = cmUtil;
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
this.mergeOpProvider = mergeOpProvider;
|
this.mergeOpProvider = mergeOpProvider;
|
||||||
|
this.mergeSuperSet = mergeSuperSet;
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.changes = changes;
|
this.changes = changes;
|
||||||
this.label = MoreObjects.firstNonNull(
|
this.label = MoreObjects.firstNonNull(
|
||||||
@@ -133,6 +144,10 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
this.titlePattern = new ParameterizedString(MoreObjects.firstNonNull(
|
this.titlePattern = new ParameterizedString(MoreObjects.firstNonNull(
|
||||||
cfg.getString("change", null, "submitTooltip"),
|
cfg.getString("change", null, "submitTooltip"),
|
||||||
DEFAULT_TOOLTIP));
|
DEFAULT_TOOLTIP));
|
||||||
|
this.titlePatternWithAncestors = new ParameterizedString(
|
||||||
|
MoreObjects.firstNonNull(
|
||||||
|
cfg.getString("change", null, "submitTooltipAncestors"),
|
||||||
|
DEFAULT_TOOLTIP_ANCESTORS));
|
||||||
submitWholeTopic = wholeTopicEnabled(cfg);
|
submitWholeTopic = wholeTopicEnabled(cfg);
|
||||||
this.submitTopicLabel = MoreObjects.firstNonNull(
|
this.submitTopicLabel = MoreObjects.firstNonNull(
|
||||||
Strings.emptyToNull(cfg.getString("change", null, "submitTopicLabel")),
|
Strings.emptyToNull(cfg.getString("change", null, "submitTopicLabel")),
|
||||||
@@ -170,16 +185,7 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
rsrc.getPatchSet().getRevision().get()));
|
rsrc.getPatchSet().getRevision().get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Change> changes;
|
ChangeSet submittedChanges = ChangeSet.create(change);
|
||||||
if (submitWholeTopic && !Strings.isNullOrEmpty(change.getTopic())) {
|
|
||||||
changes = new ArrayList<>();
|
|
||||||
for (ChangeData cd : getChangesByTopic(change.getTopic())) {
|
|
||||||
changes.add(cd.change());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
changes = Arrays.asList(change);
|
|
||||||
}
|
|
||||||
ChangeSet submittedChanges = ChangeSet.create(changes);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ReviewDb db = dbProvider.get();
|
ReviewDb db = dbProvider.get();
|
||||||
@@ -207,22 +213,24 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param changeList list of changes to be submitted at once
|
* @param cs set of changes to be submitted at once
|
||||||
* @param identifiedUser the user who is checking to submit
|
* @param identifiedUser the user who is checking to submit
|
||||||
* @return a reason why any of the changes is not submittable or null
|
* @return a reason why any of the changes is not submittable or null
|
||||||
*/
|
*/
|
||||||
private String problemsForSubmittingChanges(
|
private String problemsForSubmittingChangeset(
|
||||||
List<ChangeData> changeList,
|
ChangeSet cs, IdentifiedUser identifiedUser) {
|
||||||
IdentifiedUser identifiedUser) {
|
|
||||||
try {
|
try {
|
||||||
for (ChangeData c : changeList) {
|
for (PatchSet.Id psId : cs.patchIds()) {
|
||||||
ChangeControl changeControl = c.changeControl().forUser(
|
ReviewDb db = dbProvider.get();
|
||||||
identifiedUser);
|
ChangeControl changeControl = changeControlFactory
|
||||||
|
.controlFor(psId.getParentKey(), identifiedUser);
|
||||||
|
ChangeData c = changeDataFactory.create(db, changeControl);
|
||||||
|
|
||||||
if (!changeControl.isVisible(dbProvider.get())) {
|
if (!changeControl.isVisible(dbProvider.get())) {
|
||||||
return BLOCKED_HIDDEN_TOPIC_TOOLTIP;
|
return BLOCKED_HIDDEN_SUBMIT_TOOLTIP;
|
||||||
}
|
}
|
||||||
if (!changeControl.canSubmit()) {
|
if (!changeControl.canSubmit()) {
|
||||||
return BLOCKED_TOPIC_TOOLTIP;
|
return BLOCKED_SUBMIT_TOOLTIP;
|
||||||
}
|
}
|
||||||
// Recheck mergeability rather than using value stored in the index,
|
// Recheck mergeability rather than using value stored in the index,
|
||||||
// which may be stale.
|
// which may be stale.
|
||||||
@@ -240,8 +248,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
checkSubmitRule(c, c.currentPatchSet(), false);
|
checkSubmitRule(c, c.currentPatchSet(), false);
|
||||||
}
|
}
|
||||||
} catch (ResourceConflictException e) {
|
} catch (ResourceConflictException e) {
|
||||||
return BLOCKED_TOPIC_TOOLTIP;
|
return BLOCKED_SUBMIT_TOOLTIP;
|
||||||
} catch (OrmException e) {
|
} catch (NoSuchChangeException | OrmException e) {
|
||||||
log.error("Error checking if change is submittable", e);
|
log.error("Error checking if change is submittable", e);
|
||||||
throw new OrmRuntimeException("Could not determine problems for the change", e);
|
throw new OrmRuntimeException("Could not determine problems for the change", e);
|
||||||
}
|
}
|
||||||
@@ -282,40 +290,55 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
|||||||
throw new OrmRuntimeException("Could not determine mergeability", e);
|
throw new OrmRuntimeException("Could not determine mergeability", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ChangeData> changesByTopic = null;
|
ChangeSet cs;
|
||||||
if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) {
|
try {
|
||||||
changesByTopic = getChangesByTopic(topic);
|
cs = mergeSuperSet.completeChangeSet(db,
|
||||||
|
ChangeSet.create(cd.change()));
|
||||||
|
} catch (OrmException | IOException e) {
|
||||||
|
throw new OrmRuntimeException("Could not determine complete set of " +
|
||||||
|
"changes to be submitted", e);
|
||||||
}
|
}
|
||||||
if (submitWholeTopic
|
|
||||||
|
int topicSize = 0;
|
||||||
|
if (!Strings.isNullOrEmpty(topic)) {
|
||||||
|
topicSize = getChangesByTopic(topic).size();
|
||||||
|
}
|
||||||
|
boolean treatWithTopic = submitWholeTopic
|
||||||
&& !Strings.isNullOrEmpty(topic)
|
&& !Strings.isNullOrEmpty(topic)
|
||||||
&& changesByTopic.size() > 1) {
|
&& topicSize > 1;
|
||||||
|
|
||||||
|
String submitProblems = problemsForSubmittingChangeset(cs,
|
||||||
|
resource.getUser());
|
||||||
|
if (submitProblems != null) {
|
||||||
|
return new UiAction.Description()
|
||||||
|
.setLabel(treatWithTopic ? submitTopicLabel : label)
|
||||||
|
.setTitle(submitProblems)
|
||||||
|
.setVisible(true)
|
||||||
|
.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (treatWithTopic) {
|
||||||
Map<String, String> params = ImmutableMap.of(
|
Map<String, String> params = ImmutableMap.of(
|
||||||
"topicSize", String.valueOf(changesByTopic.size()));
|
"topicSize", String.valueOf(topicSize),
|
||||||
String topicProblems = problemsForSubmittingChanges(changesByTopic,
|
"submitSize", String.valueOf(cs.size()));
|
||||||
resource.getUser());
|
return new UiAction.Description()
|
||||||
if (topicProblems != null) {
|
|
||||||
return new UiAction.Description()
|
|
||||||
.setLabel(submitTopicLabel)
|
|
||||||
.setTitle(topicProblems)
|
|
||||||
.setVisible(true)
|
|
||||||
.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
return new UiAction.Description()
|
|
||||||
.setLabel(submitTopicLabel)
|
.setLabel(submitTopicLabel)
|
||||||
.setTitle(Strings.emptyToNull(
|
.setTitle(Strings.emptyToNull(
|
||||||
submitTopicTooltip.replace(params)))
|
submitTopicTooltip.replace(params)))
|
||||||
.setVisible(true)
|
.setVisible(true)
|
||||||
.setEnabled(Boolean.TRUE.equals(enabled));
|
.setEnabled(Boolean.TRUE.equals(enabled));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
RevId revId = resource.getPatchSet().getRevision();
|
RevId revId = resource.getPatchSet().getRevision();
|
||||||
Map<String, String> params = ImmutableMap.of(
|
Map<String, String> params = ImmutableMap.of(
|
||||||
"patchSet", String.valueOf(resource.getPatchSet().getPatchSetId()),
|
"patchSet", String.valueOf(resource.getPatchSet().getPatchSetId()),
|
||||||
"branch", resource.getChange().getDest().getShortName(),
|
"branch", resource.getChange().getDest().getShortName(),
|
||||||
"commit", ObjectId.fromString(revId.get()).abbreviate(7).name());
|
"commit", ObjectId.fromString(revId.get()).abbreviate(7).name(),
|
||||||
|
"submitSize", String.valueOf(cs.size()));
|
||||||
|
ParameterizedString tp = cs.size() > 1 ? titlePatternWithAncestors :
|
||||||
|
titlePattern;
|
||||||
return new UiAction.Description()
|
return new UiAction.Description()
|
||||||
.setLabel(label)
|
.setLabel(label)
|
||||||
.setTitle(Strings.emptyToNull(titlePattern.replace(params)))
|
.setTitle(Strings.emptyToNull(tp.replace(params)))
|
||||||
.setVisible(true)
|
.setVisible(true)
|
||||||
.setEnabled(Boolean.TRUE.equals(enabled));
|
.setEnabled(Boolean.TRUE.equals(enabled));
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import com.google.common.collect.ImmutableSetMultimap;
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import com.google.gerrit.reviewdb.client.Branch;
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
|
||||||
/** A set of changes grouped together to be submitted atomically.*/
|
/** A set of changes grouped together to be submitted atomically.*/
|
||||||
@@ -29,6 +30,7 @@ public abstract class ChangeSet {
|
|||||||
ImmutableSet.Builder<Project.NameKey> pb = ImmutableSet.builder();
|
ImmutableSet.Builder<Project.NameKey> pb = ImmutableSet.builder();
|
||||||
ImmutableSet.Builder<Branch.NameKey> bb = ImmutableSet.builder();
|
ImmutableSet.Builder<Branch.NameKey> bb = ImmutableSet.builder();
|
||||||
ImmutableSet.Builder<Change.Id> ib = ImmutableSet.builder();
|
ImmutableSet.Builder<Change.Id> ib = ImmutableSet.builder();
|
||||||
|
ImmutableSet.Builder<PatchSet.Id> psb = ImmutableSet.builder();
|
||||||
ImmutableSetMultimap.Builder<Project.NameKey, Branch.NameKey> pbb =
|
ImmutableSetMultimap.Builder<Project.NameKey, Branch.NameKey> pbb =
|
||||||
ImmutableSetMultimap.builder();
|
ImmutableSetMultimap.builder();
|
||||||
ImmutableSetMultimap.Builder<Project.NameKey, Change.Id> pcb =
|
ImmutableSetMultimap.Builder<Project.NameKey, Change.Id> pcb =
|
||||||
@@ -42,13 +44,14 @@ public abstract class ChangeSet {
|
|||||||
pb.add(project);
|
pb.add(project);
|
||||||
bb.add(branch);
|
bb.add(branch);
|
||||||
ib.add(c.getId());
|
ib.add(c.getId());
|
||||||
|
psb.add(c.currentPatchSetId());
|
||||||
pbb.put(project, branch);
|
pbb.put(project, branch);
|
||||||
pcb.put(project, c.getId());
|
pcb.put(project, c.getId());
|
||||||
cbb.put(branch, c.getId());
|
cbb.put(branch, c.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AutoValue_ChangeSet(pb.build(), bb.build(),
|
return new AutoValue_ChangeSet(pb.build(), bb.build(), ib.build(),
|
||||||
ib.build(), pbb.build(), pcb.build(), cbb.build());
|
psb.build(), pbb.build(), pcb.build(), cbb.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChangeSet create(Change change) {
|
public static ChangeSet create(Change change) {
|
||||||
@@ -58,6 +61,7 @@ public abstract class ChangeSet {
|
|||||||
public abstract ImmutableSet<Project.NameKey> projects();
|
public abstract ImmutableSet<Project.NameKey> projects();
|
||||||
public abstract ImmutableSet<Branch.NameKey> branches();
|
public abstract ImmutableSet<Branch.NameKey> branches();
|
||||||
public abstract ImmutableSet<Change.Id> ids();
|
public abstract ImmutableSet<Change.Id> ids();
|
||||||
|
public abstract ImmutableSet<PatchSet.Id> patchIds();
|
||||||
public abstract ImmutableSetMultimap<Project.NameKey, Branch.NameKey>
|
public abstract ImmutableSetMultimap<Project.NameKey, Branch.NameKey>
|
||||||
branchesByProject();
|
branchesByProject();
|
||||||
public abstract ImmutableSetMultimap<Project.NameKey, Change.Id>
|
public abstract ImmutableSetMultimap<Project.NameKey, Change.Id>
|
||||||
@@ -69,4 +73,8 @@ public abstract class ChangeSet {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return ids().hashCode();
|
return ids().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return ids().size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -237,7 +237,9 @@ public class MergeOp {
|
|||||||
return ImmutableList.of(ok.get());
|
return ImmutableList.of(ok.get());
|
||||||
} else if (results.isEmpty()) {
|
} else if (results.isEmpty()) {
|
||||||
throw new IllegalStateException(String.format(
|
throw new IllegalStateException(String.format(
|
||||||
"SubmitRuleEvaluator.evaluate returned empty list for %s in %s",
|
"SubmitRuleEvaluator.evaluate for change %s " +
|
||||||
|
"returned empty list for %s in %s",
|
||||||
|
cd.getId(),
|
||||||
patchSet.getId(),
|
patchSet.getId(),
|
||||||
cd.change().getProject().get()));
|
cd.change().getProject().get()));
|
||||||
}
|
}
|
||||||
@@ -245,15 +247,17 @@ public class MergeOp {
|
|||||||
for (SubmitRecord record : results) {
|
for (SubmitRecord record : results) {
|
||||||
switch (record.status) {
|
switch (record.status) {
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
throw new ResourceConflictException("change is closed");
|
throw new ResourceConflictException(String.format(
|
||||||
|
"change %s is closed", cd.getId()));
|
||||||
|
|
||||||
case RULE_ERROR:
|
case RULE_ERROR:
|
||||||
throw new ResourceConflictException(String.format(
|
throw new ResourceConflictException(String.format(
|
||||||
"rule error: %s",
|
"rule error for change %s: %s",
|
||||||
record.errorMessage));
|
cd.getId(), record.errorMessage));
|
||||||
|
|
||||||
case NOT_READY:
|
case NOT_READY:
|
||||||
StringBuilder msg = new StringBuilder();
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append(cd.getId() + ":");
|
||||||
for (SubmitRecord.Label lbl : record.labels) {
|
for (SubmitRecord.Label lbl : record.labels) {
|
||||||
switch (lbl.status) {
|
switch (lbl.status) {
|
||||||
case OK:
|
case OK:
|
||||||
@@ -261,32 +265,27 @@ public class MergeOp {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case REJECT:
|
case REJECT:
|
||||||
if (msg.length() > 0) {
|
msg.append(" blocked by ").append(lbl.label);
|
||||||
msg.append("; ");
|
msg.append(";");
|
||||||
}
|
|
||||||
msg.append("blocked by ").append(lbl.label);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case NEED:
|
case NEED:
|
||||||
if (msg.length() > 0) {
|
msg.append(" needs ").append(lbl.label);
|
||||||
msg.append("; ");
|
msg.append(";");
|
||||||
}
|
|
||||||
msg.append("needs ").append(lbl.label);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case IMPOSSIBLE:
|
case IMPOSSIBLE:
|
||||||
if (msg.length() > 0) {
|
msg.append(" needs ").append(lbl.label)
|
||||||
msg.append("; ");
|
|
||||||
}
|
|
||||||
msg.append("needs ").append(lbl.label)
|
|
||||||
.append(" (check project access)");
|
.append(" (check project access)");
|
||||||
|
msg.append(";");
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException(String.format(
|
throw new IllegalStateException(String.format(
|
||||||
"Unsupported SubmitRecord.Label %s for %s in %s",
|
"Unsupported SubmitRecord.Label %s for %s in %s in %s",
|
||||||
lbl.toString(),
|
lbl.toString(),
|
||||||
patchSet.getId(),
|
patchSet.getId(),
|
||||||
|
cd.getId(),
|
||||||
cd.change().getProject().get()));
|
cd.change().getProject().get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,21 +302,37 @@ public class MergeOp {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPermissions(ChangeSet cs)
|
private void checkSubmitRulesAndState(ChangeSet cs)
|
||||||
throws ResourceConflictException, OrmException {
|
throws ResourceConflictException, OrmException {
|
||||||
|
|
||||||
|
StringBuilder msgbuf = new StringBuilder();
|
||||||
|
List<Change.Id> problemChanges = new ArrayList<>();
|
||||||
for (Change.Id id : cs.ids()) {
|
for (Change.Id id : cs.ids()) {
|
||||||
ChangeData cd = changeDataFactory.create(db, id);
|
try {
|
||||||
if (cd.change().getStatus() != Change.Status.NEW){
|
ChangeData cd = changeDataFactory.create(db, id);
|
||||||
throw new OrmException("Change " + cd.change().getChangeId()
|
if (cd.change().getStatus() != Change.Status.NEW){
|
||||||
+ " is in state " + cd.change().getStatus());
|
throw new ResourceConflictException("Change " +
|
||||||
} else {
|
cd.change().getChangeId() + " is in state " +
|
||||||
records.put(cd.change().getId(), checkSubmitRule(cd));
|
cd.change().getStatus());
|
||||||
|
} else {
|
||||||
|
records.put(cd.change().getId(), checkSubmitRule(cd));
|
||||||
|
}
|
||||||
|
} catch (ResourceConflictException e) {
|
||||||
|
msgbuf.append(e.getMessage() + "\n");
|
||||||
|
problemChanges.add(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String reason = msgbuf.toString();
|
||||||
|
if (!reason.isEmpty()) {
|
||||||
|
throw new ResourceConflictException("The change could not be " +
|
||||||
|
"submitted because it depends on change(s) " +
|
||||||
|
problemChanges.toString() + ", which could not be submitted " +
|
||||||
|
"because:\n" + reason);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void merge(ReviewDb db, ChangeSet changes, IdentifiedUser caller,
|
public void merge(ReviewDb db, ChangeSet changes, IdentifiedUser caller,
|
||||||
boolean checkPermissions) throws NoSuchChangeException,
|
boolean checkSubmitRules) throws NoSuchChangeException,
|
||||||
OrmException, ResourceConflictException {
|
OrmException, ResourceConflictException {
|
||||||
logPrefix = String.format("[%s]: ", String.valueOf(changes.hashCode()));
|
logPrefix = String.format("[%s]: ", String.valueOf(changes.hashCode()));
|
||||||
this.db = db;
|
this.db = db;
|
||||||
@@ -325,9 +340,9 @@ public class MergeOp {
|
|||||||
try {
|
try {
|
||||||
ChangeSet cs = mergeSuperSet.completeChangeSet(db, changes);
|
ChangeSet cs = mergeSuperSet.completeChangeSet(db, changes);
|
||||||
logDebug("Calculated to merge {}", cs);
|
logDebug("Calculated to merge {}", cs);
|
||||||
if (checkPermissions) {
|
if (checkSubmitRules) {
|
||||||
logDebug("Checking permissions");
|
logDebug("Checking submit rules and state");
|
||||||
checkPermissions(cs);
|
checkSubmitRulesAndState(cs);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
integrateIntoHistory(cs, caller);
|
integrateIntoHistory(cs, caller);
|
||||||
|
Reference in New Issue
Block a user