Introduce SubmitPreview REST API call
Before submitting a whole topic or submission including parents
existed and the usage of Superproject subscriptions was low, it
was easy to predict, what would happen if a change was submitted
as it was just one change that was submitted. This is changing as
the submission process gets bigger unknowns by having coupling of
changes via their ancestors, topic submissions and superproject
subscriptions.
We would like to provide aid answering "What would happen if
I submit this change?" even more than just the submitted together
tab, as that would not show e.g. superproject subscriptions or the
underlying Git DAG. In case of merging, this also doesn't show
the exact tree afterwards.
This introduces a REST API call that will show exactly what will
happen by providing the exact Git DAG that would be produced if
a change was submitted now. Requirements for such a call:
* It has to be capable of dealing with multiple projects.
(superproject subscriptions, submitting topics across projects)
* easy to deal with on the client side, while transporting the
whole Git DAG.
This call returns a zip file that contains thin bundles which
can be pulled from to see the exact state after a hypothetical
submission.
As projects can be nested (e.g. project and project/foo), we cannot
just name the bundles as the projects as that may produce a
directory/file conflict inside the zip file. Projects have
limitations on how they can be named, which is enforced upon
project creation in the LocalDiskRepository class. One of the
rules is that no project name contains ".git/", which makes ".git"
a suffix that will guarantee no directory/file conflicts, which
is why the names are chosen to be "${projectname}.git"
In case of an error, no zip file is returned, but just a plain
message string.
We considered having a "dry run" submission process, that allows
submission of a change with a prefix to the target branch(es). As
an example:
You would call submit_with_prefix(change-id=234,
prefix=refs/testing/) which would create a branch
refs/testing/<change target branch> which is updated
to what that branch would look like. But also a
superproject would get a refs/testing/<superproject branch>
which could be inspected to what actually happened.
That has some disadvantages:
* Ref updates are expensive in Gerrit on Googles infrastructure as
ref updates are replicated across the globe. For such testing refs
we do not need a strong replication as we can reproduce them any
time.
* The ACLs are unclear for these testing branches (infer them from
the actual branches?)
* We’d need to cleanup these testing branches regularly (time to live
of e.g. 1 week?)
* Making sure the prefix is unique for all projects that are
involved before test submission
Change-Id: I3c976257c2b20de32373cbade9b99811586e926c
Signed-off-by: Stefan Beller <sbeller@google.com>
This commit is contained in:
@@ -6,6 +6,7 @@ acceptance_tests(
|
||||
deps = [
|
||||
':submodule_util',
|
||||
':push_for_review',
|
||||
'//gerrit-extension-api:api',
|
||||
],
|
||||
labels = ['git'],
|
||||
)
|
||||
|
||||
@@ -21,15 +21,22 @@ import static com.google.gerrit.acceptance.GitUtil.getChangeId;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||
import com.google.gerrit.extensions.client.ChangeStatus;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.testutil.ConfigSuite;
|
||||
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@NoHttpd
|
||||
public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
extends AbstractSubmoduleSubscription {
|
||||
@@ -101,22 +108,50 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
gApi.changes().id(id2).current().review(ReviewInput.approve());
|
||||
gApi.changes().id(id3).current().review(ReviewInput.approve());
|
||||
|
||||
BinaryResult request = gApi.changes().id(id1).current().submitPreview();
|
||||
Map<Branch.NameKey, RevTree> preview =
|
||||
fetchFromBundles(request);
|
||||
|
||||
gApi.changes().id(id1).current().submit();
|
||||
ObjectId subRepoId = subRepo.git().fetch().setRemote("origin").call()
|
||||
.getAdvertisedRef("refs/heads/master").getObjectId();
|
||||
|
||||
expectToHaveSubmoduleState(superRepo, "master",
|
||||
"subscribed-to-project", subRepoId);
|
||||
|
||||
// As the submodules have changed commits, the superproject tree will be
|
||||
// different, so we cannot directly compare the trees here, so make
|
||||
// assumptions only about the changed branches:
|
||||
Project.NameKey p1 = new Project.NameKey(name("super-project"));
|
||||
Project.NameKey p2 = new Project.NameKey(name("subscribed-to-project"));
|
||||
assertThat(preview).containsKey(
|
||||
new Branch.NameKey(p1, "refs/heads/master"));
|
||||
assertThat(preview).containsKey(
|
||||
new Branch.NameKey(p2, "refs/heads/master"));
|
||||
|
||||
if (getSubmitType() == SubmitType.CHERRY_PICK) {
|
||||
// each change is updated and the respective target branch is updated:
|
||||
assertThat(preview).hasSize(5);
|
||||
} else if (getSubmitType() == SubmitType.REBASE_IF_NECESSARY) {
|
||||
// Either the first is used first as is, then the second and third need
|
||||
// rebasing, or those two stay as is and the first is rebased.
|
||||
// add in 2 master branches, expect 3 or 4:
|
||||
assertThat(preview.size()).isAnyOf(3, 4);
|
||||
} else {
|
||||
assertThat(preview).hasSize(2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionUpdateIncludingChangeInSuperproject() throws Exception {
|
||||
public void testSubscriptionUpdateIncludingChangeInSuperproject()
|
||||
throws Exception {
|
||||
TestRepository<?> superRepo = createProjectWithPush("super-project");
|
||||
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
|
||||
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
|
||||
"super-project", "refs/heads/master");
|
||||
allowMatchingSubmoduleSubscription("subscribed-to-project",
|
||||
"refs/heads/master", "super-project", "refs/heads/master");
|
||||
|
||||
createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
|
||||
createSubmoduleSubscription(superRepo, "master",
|
||||
"subscribed-to-project", "master");
|
||||
|
||||
ObjectId subHEAD = subRepo.branch("HEAD").commit().insertChangeId()
|
||||
.message("some change")
|
||||
@@ -310,7 +345,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
createSubmoduleSubscription(midRepo, "master", "bottom-project", "master");
|
||||
|
||||
ObjectId bottomHead =
|
||||
pushChangeTo(bottomRepo, "refs/for/master", "some message", "same-topic");
|
||||
pushChangeTo(bottomRepo, "refs/for/master",
|
||||
"some message", "same-topic");
|
||||
ObjectId topHead =
|
||||
pushChangeTo(topRepo, "refs/for/master", "some message", "same-topic");
|
||||
|
||||
@@ -322,8 +358,10 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
|
||||
gApi.changes().id(id1).current().submit();
|
||||
|
||||
expectToHaveSubmoduleState(midRepo, "master", "bottom-project", bottomRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master", "mid-project", midRepo, "master");
|
||||
expectToHaveSubmoduleState(midRepo, "master", "bottom-project",
|
||||
bottomRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master", "mid-project",
|
||||
midRepo, "master");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -346,7 +384,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
pushSubmoduleConfig(topRepo, "master", config);
|
||||
|
||||
ObjectId bottomHead =
|
||||
pushChangeTo(bottomRepo, "refs/for/master", "some message", "same-topic");
|
||||
pushChangeTo(bottomRepo, "refs/for/master",
|
||||
"some message", "same-topic");
|
||||
ObjectId topHead =
|
||||
pushChangeTo(topRepo, "refs/for/master", "some message", "same-topic");
|
||||
|
||||
@@ -358,13 +397,16 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
|
||||
gApi.changes().id(id1).current().submit();
|
||||
|
||||
expectToHaveSubmoduleState(midRepo, "master", "bottom-project", bottomRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master", "mid-project", midRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master", "bottom-project", bottomRepo, "master");
|
||||
expectToHaveSubmoduleState(midRepo, "master",
|
||||
"bottom-project", bottomRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master",
|
||||
"mid-project", midRepo, "master");
|
||||
expectToHaveSubmoduleState(topRepo, "master",
|
||||
"bottom-project", bottomRepo, "master");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBranchCircularSubscription() throws Exception {
|
||||
|
||||
private String prepareBranchCircularSubscription() throws Exception {
|
||||
TestRepository<?> topRepo = createProjectWithPush("top-project");
|
||||
TestRepository<?> midRepo = createProjectWithPush("mid-project");
|
||||
TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
|
||||
@@ -385,15 +427,23 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
String changeId = getChangeId(bottomRepo, bottomMasterHead).get();
|
||||
|
||||
approve(changeId);
|
||||
|
||||
exception.expectMessage("Branch level circular subscriptions detected");
|
||||
exception.expectMessage("top-project,refs/heads/master");
|
||||
exception.expectMessage("mid-project,refs/heads/master");
|
||||
exception.expectMessage("bottom-project,refs/heads/master");
|
||||
gApi.changes().id(changeId).current().submit();
|
||||
return changeId;
|
||||
}
|
||||
|
||||
assertThat(hasSubmodule(midRepo, "master", "bottom-project")).isFalse();
|
||||
assertThat(hasSubmodule(topRepo, "master", "mid-project")).isFalse();
|
||||
@Test
|
||||
public void testBranchCircularSubscription() throws Exception {
|
||||
String changeId = prepareBranchCircularSubscription();
|
||||
gApi.changes().id(changeId).current().submit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBranchCircularSubscriptionPreview() throws Exception {
|
||||
String changeId = prepareBranchCircularSubscription();
|
||||
gApi.changes().id(changeId).current().submitPreview();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -401,8 +451,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
|
||||
TestRepository<?> superRepo = createProjectWithPush("super-project");
|
||||
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
|
||||
|
||||
allowMatchingSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
|
||||
"super-project", "refs/heads/master");
|
||||
allowMatchingSubmoduleSubscription("subscribed-to-project",
|
||||
"refs/heads/master", "super-project", "refs/heads/master");
|
||||
allowMatchingSubmoduleSubscription("super-project", "refs/heads/dev",
|
||||
"subscribed-to-project", "refs/heads/dev");
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
||||
import com.google.gerrit.extensions.common.LabelInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
@@ -71,13 +72,16 @@ import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@NoHttpd
|
||||
public abstract class AbstractSubmit extends AbstractDaemonTest {
|
||||
@@ -116,9 +120,155 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
|
||||
@Test
|
||||
@TestProjectInput(createEmptyCommit = false)
|
||||
public void submitToEmptyRepo() throws Exception {
|
||||
RevCommit initialHead = getRemoteHead();
|
||||
PushOneCommit.Result change = createChange();
|
||||
BinaryResult request = submitPreview(change.getChangeId());
|
||||
RevCommit headAfterSubmitPreview = getRemoteHead();
|
||||
assertThat(headAfterSubmitPreview).isEqualTo(initialHead);
|
||||
Map<Branch.NameKey, RevTree> actual =
|
||||
fetchFromBundles(request);
|
||||
assertThat(actual).hasSize(1);
|
||||
|
||||
submit(change.getChangeId());
|
||||
assertThat(getRemoteHead().getId()).isEqualTo(change.getCommit());
|
||||
assertRevTrees(project, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitSingleChange() throws Exception {
|
||||
RevCommit initialHead = getRemoteHead();
|
||||
PushOneCommit.Result change = createChange();
|
||||
BinaryResult request = submitPreview(change.getChangeId());
|
||||
RevCommit headAfterSubmit = getRemoteHead();
|
||||
assertThat(headAfterSubmit).isEqualTo(initialHead);
|
||||
assertRefUpdatedEvents();
|
||||
assertChangeMergedEvents();
|
||||
|
||||
Map<Branch.NameKey, RevTree> actual =
|
||||
fetchFromBundles(request);
|
||||
|
||||
if (getSubmitType() == SubmitType.CHERRY_PICK) {
|
||||
// The change is updated as well:
|
||||
assertThat(actual).hasSize(2);
|
||||
} else {
|
||||
assertThat(actual).hasSize(1);
|
||||
}
|
||||
|
||||
submit(change.getChangeId());
|
||||
assertRevTrees(project, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitMultipleChangesOtherMergeConflictPreview()
|
||||
throws Exception {
|
||||
RevCommit initialHead = getRemoteHead();
|
||||
|
||||
PushOneCommit.Result change =
|
||||
createChange("Change 1", "a.txt", "content");
|
||||
submit(change.getChangeId());
|
||||
|
||||
RevCommit headAfterFirstSubmit = getRemoteHead();
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result change2 = createChange("Change 2",
|
||||
"a.txt", "other content");
|
||||
PushOneCommit.Result change3 = createChange("Change 3", "d", "d");
|
||||
PushOneCommit.Result change4 = createChange("Change 4", "e", "e");
|
||||
// change 2 is not approved, but we ignore labels
|
||||
approve(change3.getChangeId());
|
||||
BinaryResult request = null;
|
||||
String msg = null;
|
||||
try {
|
||||
request = submitPreview(change4.getChangeId());
|
||||
} catch (Exception e) {
|
||||
msg = e.getMessage();
|
||||
}
|
||||
|
||||
if (getSubmitType() == SubmitType.CHERRY_PICK) {
|
||||
Map<Branch.NameKey, RevTree> s =
|
||||
fetchFromBundles(request);
|
||||
submit(change4.getChangeId());
|
||||
assertRevTrees(project, s);
|
||||
} else if (getSubmitType() == SubmitType.FAST_FORWARD_ONLY) {
|
||||
assertThat(msg).isEqualTo(
|
||||
"Failed to submit 3 changes due to the following problems:\n" +
|
||||
"Change " + change2.getChange().getId() + ": internal error: " +
|
||||
"change not processed by merge strategy\n" +
|
||||
"Change " + change3.getChange().getId() + ": internal error: " +
|
||||
"change not processed by merge strategy\n" +
|
||||
"Change " + change4.getChange().getId() + ": Project policy " +
|
||||
"requires all submissions to be a fast-forward. Please " +
|
||||
"rebase the change locally and upload again for review.");
|
||||
RevCommit headAfterSubmit = getRemoteHead();
|
||||
assertThat(headAfterSubmit).isEqualTo(headAfterFirstSubmit);
|
||||
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
|
||||
assertChangeMergedEvents(change.getChangeId(),
|
||||
headAfterFirstSubmit.name());
|
||||
} else if(getSubmitType() == SubmitType.REBASE_IF_NECESSARY) {
|
||||
String change2hash = change2.getChange().currentPatchSet()
|
||||
.getRevision().get();
|
||||
assertThat(msg).isEqualTo(
|
||||
"Cannot rebase " + change2hash + ": The change could " +
|
||||
"not be rebased due to a conflict during merge.");
|
||||
RevCommit headAfterSubmit = getRemoteHead();
|
||||
assertThat(headAfterSubmit).isEqualTo(headAfterFirstSubmit);
|
||||
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
|
||||
assertChangeMergedEvents(change.getChangeId(),
|
||||
headAfterFirstSubmit.name());
|
||||
} else {
|
||||
assertThat(msg).isEqualTo(
|
||||
"Failed to submit 3 changes due to the following problems:\n" +
|
||||
"Change " + change2.getChange().getId() + ": Change could not be " +
|
||||
"merged due to a path conflict. Please rebase the change " +
|
||||
"locally and upload the rebased commit for review.\n" +
|
||||
"Change " + change3.getChange().getId() + ": Change could not be " +
|
||||
"merged due to a path conflict. Please rebase the change " +
|
||||
"locally and upload the rebased commit for review.\n" +
|
||||
"Change " + change4.getChange().getId() + ": Change could not be " +
|
||||
"merged due to a path conflict. Please rebase the change " +
|
||||
"locally and upload the rebased commit for review.");
|
||||
RevCommit headAfterSubmit = getRemoteHead();
|
||||
assertThat(headAfterSubmit).isEqualTo(headAfterFirstSubmit);
|
||||
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
|
||||
assertChangeMergedEvents(change.getChangeId(),
|
||||
headAfterFirstSubmit.name());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitMultipleChangesPreview() throws Exception {
|
||||
RevCommit initialHead = getRemoteHead();
|
||||
PushOneCommit.Result change2 = createChange("Change 2",
|
||||
"a.txt", "other content");
|
||||
PushOneCommit.Result change3 = createChange("Change 3", "d", "d");
|
||||
PushOneCommit.Result change4 = createChange("Change 4", "e", "e");
|
||||
// change 2 is not approved, but we ignore labels
|
||||
approve(change3.getChangeId());
|
||||
BinaryResult request = submitPreview(change4.getChangeId());
|
||||
|
||||
Map<String, Map<String, Integer>> expected = new HashMap<>();
|
||||
expected.put(project.get(), new HashMap<String, Integer>());
|
||||
expected.get(project.get()).put("refs/heads/master", 3);
|
||||
Map<Branch.NameKey, RevTree> actual =
|
||||
fetchFromBundles(request);
|
||||
|
||||
assertThat(actual).containsKey(
|
||||
new Branch.NameKey(project, "refs/heads/master"));
|
||||
if (getSubmitType() == SubmitType.CHERRY_PICK) {
|
||||
assertThat(actual).hasSize(2);
|
||||
} else {
|
||||
assertThat(actual).hasSize(1);
|
||||
}
|
||||
|
||||
// check that the submit preview did not actually submit
|
||||
RevCommit headAfterSubmit = getRemoteHead();
|
||||
assertThat(headAfterSubmit).isEqualTo(initialHead);
|
||||
assertRefUpdatedEvents();
|
||||
assertChangeMergedEvents();
|
||||
|
||||
// now check we actually have the same content:
|
||||
approve(change2.getChangeId());
|
||||
submit(change4.getChangeId());
|
||||
assertRevTrees(project, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -326,6 +476,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
|
||||
assertMerged(change.changeId);
|
||||
}
|
||||
|
||||
protected BinaryResult submitPreview(String changeId) throws Exception {
|
||||
return gApi.changes().id(changeId).current().submitPreview();
|
||||
}
|
||||
|
||||
protected void assertSubmittable(String changeId) throws Exception {
|
||||
assertThat(gApi.changes().id(changeId).info().submittable)
|
||||
.named("submit bit on ChangeInfo")
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.acceptance.rest.change;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.gerrit.acceptance.GitUtil;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
@@ -24,14 +25,19 @@ import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||
import com.google.gerrit.extensions.api.projects.BranchInput;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
|
||||
|
||||
@@ -144,6 +150,12 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
|
||||
approve(change2a.getChangeId());
|
||||
approve(change2b.getChangeId());
|
||||
approve(change3.getChangeId());
|
||||
|
||||
// get a preview before submitting:
|
||||
BinaryResult request = submitPreview(change1b.getChangeId());
|
||||
Map<Branch.NameKey, RevTree> preview =
|
||||
fetchFromBundles(request);
|
||||
|
||||
submit(change1b.getChangeId());
|
||||
|
||||
RevCommit tip1 = getRemoteLog(p1, "master").get(0);
|
||||
@@ -158,11 +170,28 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
|
||||
change2b.getCommit().getShortMessage());
|
||||
assertThat(tip3.getShortMessage()).isEqualTo(
|
||||
change3.getCommit().getShortMessage());
|
||||
|
||||
// check that the preview matched what happened:
|
||||
assertThat(preview).hasSize(3);
|
||||
|
||||
assertThat(preview).containsKey(
|
||||
new Branch.NameKey(p1, "refs/heads/master"));
|
||||
assertRevTrees(p1, preview);
|
||||
|
||||
assertThat(preview).containsKey(
|
||||
new Branch.NameKey(p2, "refs/heads/master"));
|
||||
assertRevTrees(p2, preview);
|
||||
|
||||
assertThat(preview).containsKey(
|
||||
new Branch.NameKey(p3, "refs/heads/master"));
|
||||
assertRevTrees(p3, preview);
|
||||
} else {
|
||||
assertThat(tip2.getShortMessage()).isEqualTo(
|
||||
initialHead2.getShortMessage());
|
||||
assertThat(tip3.getShortMessage()).isEqualTo(
|
||||
initialHead3.getShortMessage());
|
||||
assertThat(preview).hasSize(1);
|
||||
assertThat(preview.get(new Branch.NameKey(p1, "refs/heads/master"))).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,11 +244,23 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
|
||||
approve(change3.getChangeId());
|
||||
|
||||
if (isSubmitWholeTopicEnabled()) {
|
||||
submitWithConflict(change1b.getChangeId(),
|
||||
String msg =
|
||||
"Failed to submit 5 changes due to the following problems:\n" +
|
||||
"Change " + change3.getChange().getId() + ": Change could not be " +
|
||||
"merged due to a path conflict. Please rebase the change locally " +
|
||||
"and upload the rebased commit for review.");
|
||||
"and upload the rebased commit for review.";
|
||||
|
||||
// Get a preview before submitting:
|
||||
try {
|
||||
// We cannot just use the ExpectedException infrastructure as provided
|
||||
// by AbstractDaemonTest, as then we'd stop early and not test the
|
||||
// actual submit.
|
||||
submitPreview(change1b.getChangeId());
|
||||
fail("expected failure");
|
||||
} catch (RestApiException e) {
|
||||
assertThat(e.getMessage()).isEqualTo(msg);
|
||||
}
|
||||
submitWithConflict(change1b.getChangeId(), msg);
|
||||
} else {
|
||||
submit(change1b.getChangeId());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user