Merge "CreateMergePatchSet: Implement 'author' to tweak Git commit author"
This commit is contained in:
@@ -7325,6 +7325,13 @@ it's set. Otherwise, the current branch tip of the destination branch will be us
|
||||
|`merge` ||
|
||||
The detail of the source commit for merge as a link:#merge-input[MergeInput]
|
||||
entity.
|
||||
|`author` |optional|
|
||||
An link:rest-api-accounts.html#account-input[AccountInput] entity
|
||||
that will set the author of the commit to create. The author must be
|
||||
specified as name/email combination.
|
||||
The caller needs "Forge Author" permission when using this field.
|
||||
This field does not affect the owner of the change, which will
|
||||
continue to use the identity of the caller.
|
||||
|==================================
|
||||
|
||||
[[move-input]]
|
||||
|
||||
@@ -14,9 +14,12 @@
|
||||
|
||||
package com.google.gerrit.extensions.common;
|
||||
|
||||
import com.google.gerrit.extensions.api.accounts.AccountInput;
|
||||
|
||||
public class MergePatchSetInput {
|
||||
public String subject;
|
||||
public boolean inheritParent;
|
||||
public String baseChange;
|
||||
public MergeInput merge;
|
||||
public AccountInput author;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
import com.google.gerrit.server.permissions.ChangePermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.restapi.project.CommitsCollection;
|
||||
@@ -128,6 +129,13 @@ public class CreateMergePatchSet implements RestModifyView<ChangeResource, Merge
|
||||
psUtil.checkPatchSetNotLocked(rsrc.getNotes());
|
||||
|
||||
rsrc.permissions().check(ChangePermission.ADD_PATCH_SET);
|
||||
if (in.author != null) {
|
||||
permissionBackend
|
||||
.currentUser()
|
||||
.project(rsrc.getProject())
|
||||
.ref(rsrc.getChange().getDest().branch())
|
||||
.check(RefPermission.FORGE_AUTHOR);
|
||||
}
|
||||
|
||||
ProjectState projectState =
|
||||
projectCache.get(rsrc.getProject()).orElseThrow(illegalState(rsrc.getProject()));
|
||||
@@ -137,6 +145,10 @@ public class CreateMergePatchSet implements RestModifyView<ChangeResource, Merge
|
||||
if (merge == null || Strings.isNullOrEmpty(merge.source)) {
|
||||
throw new BadRequestException("merge.source must be non-empty");
|
||||
}
|
||||
if (in.author != null
|
||||
&& (Strings.isNullOrEmpty(in.author.email) || Strings.isNullOrEmpty(in.author.name))) {
|
||||
throw new BadRequestException("Author must specify name and email");
|
||||
}
|
||||
in.baseChange = Strings.nullToEmpty(in.baseChange).trim();
|
||||
|
||||
PatchSet ps = psUtil.current(rsrc.getNotes());
|
||||
@@ -166,7 +178,10 @@ public class CreateMergePatchSet implements RestModifyView<ChangeResource, Merge
|
||||
|
||||
Timestamp now = TimeUtil.nowTs();
|
||||
IdentifiedUser me = user.get().asIdentifiedUser();
|
||||
PersonIdent author = me.newCommitterIdent(now, serverTimeZone);
|
||||
PersonIdent author =
|
||||
in.author == null
|
||||
? me.newCommitterIdent(now, serverTimeZone)
|
||||
: new PersonIdent(in.author.name, in.author.email, now, serverTimeZone);
|
||||
CodeReviewCommit newCommit =
|
||||
createMergeCommit(
|
||||
in,
|
||||
|
||||
@@ -47,7 +47,6 @@ import static com.google.gerrit.extensions.client.ListChangesOption.TRACKING_IDS
|
||||
import static com.google.gerrit.extensions.client.ReviewerState.CC;
|
||||
import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
|
||||
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
|
||||
import static com.google.gerrit.git.ObjectIds.abbreviateName;
|
||||
import static com.google.gerrit.server.StarredChangesUtil.DEFAULT_LABEL;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
|
||||
@@ -142,14 +141,11 @@ import com.google.gerrit.extensions.common.CommentInfo;
|
||||
import com.google.gerrit.extensions.common.CommitInfo;
|
||||
import com.google.gerrit.extensions.common.GitPerson;
|
||||
import com.google.gerrit.extensions.common.LabelInfo;
|
||||
import com.google.gerrit.extensions.common.MergeInput;
|
||||
import com.google.gerrit.extensions.common.MergePatchSetInput;
|
||||
import com.google.gerrit.extensions.common.RevisionInfo;
|
||||
import com.google.gerrit.extensions.common.TrackingIdInfo;
|
||||
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
@@ -183,7 +179,6 @@ import com.google.gerrit.testing.FakeEmailSender.Message;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
@@ -3188,407 +3183,6 @@ public class ChangeIT extends AbstractDaemonTest {
|
||||
r2.assertOkStatus();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
|
||||
TestWorkInProgressStateChangedListener wipStateChangedListener =
|
||||
new TestWorkInProgressStateChangedListener();
|
||||
try (Registration registration =
|
||||
extensionRegistry.newRegistration().add(wipStateChangedListener)) {
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.containsGitConflicts).isNull();
|
||||
assertThat(changeInfo.workInProgress).isNull();
|
||||
}
|
||||
assertThat(wipStateChangedListener.invoked).isFalse();
|
||||
|
||||
// To get the revisions, we must retrieve the change with more change options.
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
|
||||
// Verify the message that has been posted on the change.
|
||||
List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
|
||||
assertThat(messages).hasSize(2);
|
||||
assertThat(Iterables.getLast(messages).message).isEqualTo("Uploaded patch set 2.");
|
||||
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.message)
|
||||
.contains(subject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_SubjectCarriesOverByDefault() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
PushOneCommit.Result result = createChange();
|
||||
String changeId = result.getChangeId();
|
||||
String subject = result.getChange().change().getSubject();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result pushResult =
|
||||
pushFactory.create(user.newIdent(), testRepo).to("refs/heads/dev");
|
||||
pushResult.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = null;
|
||||
|
||||
// Ensure subject carries over
|
||||
gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).get();
|
||||
assertThat(changeInfo.subject).isEqualTo(subject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_Conflict() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, "change 1", fileName, "content 1")
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change 2", fileName, "content 2")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
ResourceConflictException thrown =
|
||||
assertThrows(
|
||||
ResourceConflictException.class,
|
||||
() -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("merge conflict(s):\n" + fileName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_ConflictAllowed() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.allowConflicts = true;
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
TestWorkInProgressStateChangedListener wipStateChangedListener =
|
||||
new TestWorkInProgressStateChangedListener();
|
||||
try (Registration registration =
|
||||
extensionRegistry.newRegistration().add(wipStateChangedListener)) {
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.containsGitConflicts).isTrue();
|
||||
assertThat(changeInfo.workInProgress).isTrue();
|
||||
}
|
||||
assertThat(wipStateChangedListener.invoked).isTrue();
|
||||
assertThat(wipStateChangedListener.wip).isTrue();
|
||||
|
||||
// To get the revisions, we must retrieve the change with more change options.
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
|
||||
// Verify that the file content in the created patch set is correct.
|
||||
// We expect that it has conflict markers to indicate the conflict.
|
||||
BinaryResult bin = gApi.changes().id(changeId).current().file(fileName).content();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
bin.writeTo(os);
|
||||
String fileContent = new String(os.toByteArray(), UTF_8);
|
||||
String sourceSha1 = abbreviateName(changeA.getCommit(), 6);
|
||||
String targetSha1 = abbreviateName(currentMaster.getCommit(), 6);
|
||||
assertThat(fileContent)
|
||||
.isEqualTo(
|
||||
"<<<<<<< TARGET BRANCH ("
|
||||
+ targetSha1
|
||||
+ " "
|
||||
+ targetSubject
|
||||
+ ")\n"
|
||||
+ targetContent
|
||||
+ "\n"
|
||||
+ "=======\n"
|
||||
+ sourceContent
|
||||
+ "\n"
|
||||
+ ">>>>>>> SOURCE BRANCH ("
|
||||
+ sourceSha1
|
||||
+ " "
|
||||
+ sourceSubject
|
||||
+ ")\n");
|
||||
|
||||
// Verify the message that has been posted on the change.
|
||||
List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
|
||||
assertThat(messages).hasSize(2);
|
||||
assertThat(Iterables.getLast(messages).message)
|
||||
.isEqualTo(
|
||||
"Uploaded patch set 2.\n\n"
|
||||
+ "The following files contain Git conflicts:\n"
|
||||
+ "* "
|
||||
+ fileName
|
||||
+ "\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_ConflictAllowedNotSupportedByMergeStrategy() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.allowConflicts = true;
|
||||
mergeInput.strategy = "simple-two-way-in-core";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"merge with conflicts is not supported with merge strategy: " + mergeInput.strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetInheritParent() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
PushOneCommit.Result r = createChange();
|
||||
String changeId = r.getChangeId();
|
||||
String parent = r.getCommit().getParent(0).getName();
|
||||
|
||||
// advance master branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2 inherit parent of ps1";
|
||||
in.inheritParent = true;
|
||||
gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isNotEqualTo(currentMaster.getCommit().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetCannotBaseOnInvisibleChange() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("foo");
|
||||
createBranch("bar");
|
||||
|
||||
// Create a merged commit on 'foo' branch.
|
||||
merge(createChange("refs/for/foo"));
|
||||
|
||||
// Create the base change on 'bar' branch.
|
||||
testRepo.reset(initialHead);
|
||||
String baseChange = createChange("refs/for/bar").getChangeId();
|
||||
gApi.changes().id(baseChange).setPrivate(true, "set private");
|
||||
|
||||
// Create the destination change on 'master' branch.
|
||||
requestScopeOperations.setApiUser(user.id());
|
||||
testRepo.reset(initialHead);
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
UnprocessableEntityException thrown =
|
||||
assertThrows(
|
||||
UnprocessableEntityException.class,
|
||||
() ->
|
||||
gApi.changes()
|
||||
.id(changeId)
|
||||
.createMergePatchSet(createMergePatchSetInput(baseChange)));
|
||||
assertThat(thrown).hasMessageThat().contains("Read not permitted for " + baseChange);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetBaseOnChange() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("foo");
|
||||
createBranch("bar");
|
||||
|
||||
// Create a merged commit on 'foo' branch.
|
||||
merge(createChange("refs/for/foo"));
|
||||
|
||||
// Create the base change on 'bar' branch.
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result result = createChange("refs/for/bar");
|
||||
String baseChange = result.getChangeId();
|
||||
String expectedParent = result.getCommit().getName();
|
||||
|
||||
// Create the destination change on 'master' branch.
|
||||
testRepo.reset(initialHead);
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
gApi.changes().id(changeId).createMergePatchSet(createMergePatchSetInput(baseChange));
|
||||
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.subject).isEqualTo("create ps2");
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(expectedParent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithUnupportedMergeStrategy() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch("dev");
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.strategy = "unsupported-strategy";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("invalid merge strategy: " + mergeInput.strategy);
|
||||
}
|
||||
|
||||
private MergePatchSetInput createMergePatchSetInput(String baseChange) {
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "foo";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "create ps2";
|
||||
in.inheritParent = false;
|
||||
in.baseChange = baseChange;
|
||||
return in;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkLabelsForUnsubmittedChange() throws Exception {
|
||||
PushOneCommit.Result r = createChange();
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
package com.google.gerrit.acceptance.api.change;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
|
||||
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
|
||||
import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS;
|
||||
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_COMMIT;
|
||||
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
|
||||
import static com.google.gerrit.git.ObjectIds.abbreviateName;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.ExtensionRegistry;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
||||
import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate;
|
||||
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
|
||||
import com.google.gerrit.entities.BranchNameKey;
|
||||
import com.google.gerrit.entities.Permission;
|
||||
import com.google.gerrit.extensions.api.accounts.AccountInput;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
||||
import com.google.gerrit.extensions.common.CommitInfo;
|
||||
import com.google.gerrit.extensions.common.MergeInput;
|
||||
import com.google.gerrit.extensions.common.MergePatchSetInput;
|
||||
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.inject.Inject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CreateMergePatchSetIT extends AbstractDaemonTest {
|
||||
|
||||
@Inject private ProjectOperations projectOperations;
|
||||
@Inject private RequestScopeOperations requestScopeOperations;
|
||||
@Inject private ExtensionRegistry extensionRegistry;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
projectOperations
|
||||
.project(project)
|
||||
.forUpdate()
|
||||
.add(allow(Permission.FORGE_AUTHOR).ref("refs/*").group(REGISTERED_USERS))
|
||||
.update();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
|
||||
TestWorkInProgressStateChangedListener wipStateChangedListener =
|
||||
new TestWorkInProgressStateChangedListener();
|
||||
try (ExtensionRegistry.Registration registration =
|
||||
extensionRegistry.newRegistration().add(wipStateChangedListener)) {
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.containsGitConflicts).isNull();
|
||||
assertThat(changeInfo.workInProgress).isNull();
|
||||
}
|
||||
assertThat(wipStateChangedListener.invoked).isFalse();
|
||||
|
||||
// To get the revisions, we must retrieve the change with more change options.
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
|
||||
// Verify the message that has been posted on the change.
|
||||
List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
|
||||
assertThat(messages).hasSize(2);
|
||||
assertThat(Iterables.getLast(messages).message).isEqualTo("Uploaded patch set 2.");
|
||||
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.message)
|
||||
.contains(subject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_SubjectCarriesOverByDefault() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
PushOneCommit.Result result = createChange();
|
||||
String changeId = result.getChangeId();
|
||||
String subject = result.getChange().change().getSubject();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result pushResult =
|
||||
pushFactory.create(user.newIdent(), testRepo).to("refs/heads/dev");
|
||||
pushResult.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = null;
|
||||
|
||||
// Ensure subject carries over
|
||||
gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).get();
|
||||
assertThat(changeInfo.subject).isEqualTo(subject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_Conflict() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, "change 1", fileName, "content 1")
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change 2", fileName, "content 2")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
ResourceConflictException thrown =
|
||||
assertThrows(
|
||||
ResourceConflictException.class,
|
||||
() -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("merge conflict(s):\n" + fileName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_ConflictAllowed() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.allowConflicts = true;
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
TestWorkInProgressStateChangedListener wipStateChangedListener =
|
||||
new TestWorkInProgressStateChangedListener();
|
||||
try (ExtensionRegistry.Registration registration =
|
||||
extensionRegistry.newRegistration().add(wipStateChangedListener)) {
|
||||
ChangeInfo changeInfo = gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.containsGitConflicts).isTrue();
|
||||
assertThat(changeInfo.workInProgress).isTrue();
|
||||
}
|
||||
assertThat(wipStateChangedListener.invoked).isTrue();
|
||||
assertThat(wipStateChangedListener.wip).isTrue();
|
||||
|
||||
// To get the revisions, we must retrieve the change with more change options.
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
|
||||
// Verify that the file content in the created patch set is correct.
|
||||
// We expect that it has conflict markers to indicate the conflict.
|
||||
BinaryResult bin = gApi.changes().id(changeId).current().file(fileName).content();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
bin.writeTo(os);
|
||||
String fileContent = new String(os.toByteArray(), UTF_8);
|
||||
String sourceSha1 = abbreviateName(changeA.getCommit(), 6);
|
||||
String targetSha1 = abbreviateName(currentMaster.getCommit(), 6);
|
||||
assertThat(fileContent)
|
||||
.isEqualTo(
|
||||
"<<<<<<< TARGET BRANCH ("
|
||||
+ targetSha1
|
||||
+ " "
|
||||
+ targetSubject
|
||||
+ ")\n"
|
||||
+ targetContent
|
||||
+ "\n"
|
||||
+ "=======\n"
|
||||
+ sourceContent
|
||||
+ "\n"
|
||||
+ ">>>>>>> SOURCE BRANCH ("
|
||||
+ sourceSha1
|
||||
+ " "
|
||||
+ sourceSubject
|
||||
+ ")\n");
|
||||
|
||||
// Verify the message that has been posted on the change.
|
||||
List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
|
||||
assertThat(messages).hasSize(2);
|
||||
assertThat(Iterables.getLast(messages).message)
|
||||
.isEqualTo(
|
||||
"Uploaded patch set 2.\n\n"
|
||||
+ "The following files contain Git conflicts:\n"
|
||||
+ "* "
|
||||
+ fileName
|
||||
+ "\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSet_ConflictAllowedNotSupportedByMergeStrategy() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.allowConflicts = true;
|
||||
mergeInput.strategy = "simple-two-way-in-core";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"merge with conflicts is not supported with merge strategy: " + mergeInput.strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetInheritParent() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
PushOneCommit.Result r = createChange();
|
||||
String changeId = r.getChangeId();
|
||||
String parent = r.getCommit().getParent(0).getName();
|
||||
|
||||
// advance master branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2 inherit parent of ps1";
|
||||
in.inheritParent = true;
|
||||
gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.subject).isEqualTo(in.subject);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isNotEqualTo(currentMaster.getCommit().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetCannotBaseOnInvisibleChange() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "foo"));
|
||||
createBranch(BranchNameKey.create(project, "bar"));
|
||||
|
||||
// Create a merged commit on 'foo' branch.
|
||||
merge(createChange("refs/for/foo"));
|
||||
|
||||
// Create the base change on 'bar' branch.
|
||||
testRepo.reset(initialHead);
|
||||
String baseChange = createChange("refs/for/bar").getChangeId();
|
||||
gApi.changes().id(baseChange).setPrivate(true, "set private");
|
||||
|
||||
// Create the destination change on 'master' branch.
|
||||
requestScopeOperations.setApiUser(user.id());
|
||||
testRepo.reset(initialHead);
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
UnprocessableEntityException thrown =
|
||||
assertThrows(
|
||||
UnprocessableEntityException.class,
|
||||
() ->
|
||||
gApi.changes()
|
||||
.id(changeId)
|
||||
.createMergePatchSet(createMergePatchSetInput(baseChange)));
|
||||
assertThat(thrown).hasMessageThat().contains("Read not permitted for " + baseChange);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetBaseOnChange() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "foo"));
|
||||
createBranch(BranchNameKey.create(project, "bar"));
|
||||
|
||||
// Create a merged commit on 'foo' branch.
|
||||
merge(createChange("refs/for/foo"));
|
||||
|
||||
// Create the base change on 'bar' branch.
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result result = createChange("refs/for/bar");
|
||||
String baseChange = result.getChangeId();
|
||||
String expectedParent = result.getCommit().getName();
|
||||
|
||||
// Create the destination change on 'master' branch.
|
||||
testRepo.reset(initialHead);
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
gApi.changes().id(changeId).createMergePatchSet(createMergePatchSetInput(baseChange));
|
||||
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.subject).isEqualTo("create ps2");
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(expectedParent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithUnupportedMergeStrategy() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
String fileName = "shared.txt";
|
||||
String sourceSubject = "source change";
|
||||
String sourceContent = "source content";
|
||||
String targetSubject = "target change";
|
||||
String targetContent = "target content";
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster =
|
||||
pushFactory
|
||||
.create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
|
||||
.to("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
mergeInput.strategy = "unsupported-strategy";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "update change by merge ps2";
|
||||
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("invalid merge strategy: " + mergeInput.strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithOtherAuthor() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
in.author = new AccountInput();
|
||||
in.author.name = "Other Author";
|
||||
in.author.email = "otherauthor@example.com";
|
||||
gApi.changes().id(changeId).createMergePatchSet(in);
|
||||
|
||||
// To get the revisions, we must retrieve the change with more change options.
|
||||
ChangeInfo changeInfo =
|
||||
gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
|
||||
assertThat(changeInfo.revisions).hasSize(2);
|
||||
assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.parents.get(0).commit)
|
||||
.isEqualTo(parent);
|
||||
|
||||
// Verify the message that has been posted on the change.
|
||||
List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
|
||||
assertThat(messages).hasSize(2);
|
||||
assertThat(Iterables.getLast(messages).message).isEqualTo("Uploaded patch set 2.");
|
||||
|
||||
CommitInfo commitInfo = changeInfo.revisions.get(changeInfo.currentRevision).commit;
|
||||
assertThat(commitInfo.message).contains(subject);
|
||||
assertThat(commitInfo.author.name).isEqualTo("Other Author");
|
||||
assertThat(commitInfo.author.email).isEqualTo("otherauthor@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithSpecificAuthorButNoForgeAuthorPermission() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
in.author = new AccountInput();
|
||||
in.author.name = "Foo";
|
||||
in.author.email = "foo@example.com";
|
||||
|
||||
projectOperations
|
||||
.project(project)
|
||||
.forUpdate()
|
||||
.remove(
|
||||
TestProjectUpdate.permissionKey(Permission.FORGE_AUTHOR)
|
||||
.ref("refs/*")
|
||||
.group(REGISTERED_USERS))
|
||||
.add(block(Permission.FORGE_AUTHOR).ref("refs/*").group(REGISTERED_USERS))
|
||||
.update();
|
||||
AuthException ex =
|
||||
assertThrows(
|
||||
AuthException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("not permitted: forge author on refs/heads/master");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithMissingNameFailsWithBadRequestException() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
in.author = new AccountInput();
|
||||
in.author.name = "Foo";
|
||||
|
||||
projectOperations
|
||||
.project(project)
|
||||
.forUpdate()
|
||||
.add(block(Permission.FORGE_AUTHOR).ref("refs/*").group(REGISTERED_USERS))
|
||||
.update();
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("Author must specify name and email");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMergePatchSetWithMissingEmailFailsWithBadRequestException() throws Exception {
|
||||
RevCommit initialHead = projectOperations.project(project).getHead("master");
|
||||
createBranch(BranchNameKey.create(project, "dev"));
|
||||
|
||||
// create a change for master
|
||||
String changeId = createChange().getChangeId();
|
||||
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
|
||||
currentMaster.assertOkStatus();
|
||||
String parent = currentMaster.getCommit().getName();
|
||||
|
||||
// push a commit into dev branch
|
||||
testRepo.reset(initialHead);
|
||||
PushOneCommit.Result changeA =
|
||||
pushFactory
|
||||
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
|
||||
.to("refs/heads/dev");
|
||||
changeA.assertOkStatus();
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "dev";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
String subject = "update change by merge ps2";
|
||||
in.subject = subject;
|
||||
in.author = new AccountInput();
|
||||
in.author.email = "Foo";
|
||||
|
||||
projectOperations
|
||||
.project(project)
|
||||
.forUpdate()
|
||||
.add(block(Permission.FORGE_AUTHOR).ref("refs/*").group(REGISTERED_USERS))
|
||||
.update();
|
||||
BadRequestException ex =
|
||||
assertThrows(
|
||||
BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("Author must specify name and email");
|
||||
}
|
||||
|
||||
private MergePatchSetInput createMergePatchSetInput(String baseChange) {
|
||||
MergeInput mergeInput = new MergeInput();
|
||||
mergeInput.source = "foo";
|
||||
MergePatchSetInput in = new MergePatchSetInput();
|
||||
in.merge = mergeInput;
|
||||
in.subject = "create ps2";
|
||||
in.inheritParent = false;
|
||||
in.baseChange = baseChange;
|
||||
return in;
|
||||
}
|
||||
|
||||
private static class TestWorkInProgressStateChangedListener
|
||||
implements WorkInProgressStateChangedListener {
|
||||
boolean invoked;
|
||||
Boolean wip;
|
||||
|
||||
@Override
|
||||
public void onWorkInProgressStateChanged(Event event) {
|
||||
this.invoked = true;
|
||||
this.wip =
|
||||
event.getChange().workInProgress != null ? event.getChange().workInProgress : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user