Merge changes from topic 'kill-submitted'

* changes:
  Remove the state SUBMITTED altogether
  Merging Changes: Do not enter submitted state
  Test author and committer for different submit strategies
This commit is contained in:
Dave Borowitz
2015-07-10 21:44:49 +00:00
committed by Gerrit Code Review
40 changed files with 448 additions and 702 deletions

View File

@@ -43,9 +43,6 @@ status:: Current state of this change.
DRAFT;; Change is a draft change that only consists of draft patchsets. DRAFT;; Change is a draft change that only consists of draft patchsets.
SUBMITTED;; Change has been submitted and is in the merge queue.
It may be waiting for one or more dependencies.
MERGED;; Change has been merged to its branch. MERGED;; Change has been merged to its branch.
ABANDONED;; Change was abandoned by its owner or administrator. ABANDONED;; Change was abandoned by its owner or administrator.

View File

@@ -3756,8 +3756,7 @@ The `refs/heads/` prefix is omitted.
|`subject` || |`subject` ||
The subject of the change (header line of the commit message). The subject of the change (header line of the commit message).
|`status` || |`status` ||
The status of the change (`NEW`, `SUBMITTED`, `MERGED`, `ABANDONED`, The status of the change (`NEW`, `MERGED`, `ABANDONED`, `DRAFT`).
`DRAFT`).
|`created` || |`created` ||
The link:rest-api.html#timestamp[timestamp] of when the change was The link:rest-api.html#timestamp[timestamp] of when the change was
created. created.
@@ -4322,7 +4321,7 @@ link:#commit-info[CommitInfo] entity.
|`_revision_number` |optional|The revision number. |`_revision_number` |optional|The revision number.
|`_current_revision_number`|optional|The current revision number. |`_current_revision_number`|optional|The current revision number.
|`status` |optional|The status of the change. The status of |`status` |optional|The status of the change. The status of
the change is one of (`NEW`, `SUBMITTED`, `MERGED`, `ABANDONED`, `DRAFT`). the change is one of (`NEW`, `MERGED`, `ABANDONED`, `DRAFT`).
|=========================== |===========================
[[related-changes-info]] [[related-changes-info]]
@@ -4530,8 +4529,8 @@ after submitting.
|========================== |==========================
|Field Name ||Description |Field Name ||Description
|`status` || |`status` ||
The status of the change after submitting, can be `MERGED` or The status of the change after submitting is `MERGED`.
`SUBMITTED`.+ +
As `wait_for_merge` in the link:#submit-input[SubmitInput] is deprecated and As `wait_for_merge` in the link:#submit-input[SubmitInput] is deprecated and
the request always waits for the merge to be completed, you can expect the request always waits for the merge to be completed, you can expect
`MERGED` to be returned here. `MERGED` to be returned here.

View File

@@ -39,6 +39,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
@@ -67,6 +68,7 @@ import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.Transport;
import org.junit.AfterClass; import org.junit.AfterClass;
@@ -148,6 +150,10 @@ public abstract class AbstractDaemonTest {
@Inject @Inject
private Provider<AnonymousUser> anonymousUser; private Provider<AnonymousUser> anonymousUser;
@Inject
@GerritPersonIdent
protected Provider<PersonIdent> serverIdent;
protected TestRepository<InMemoryRepository> testRepo; protected TestRepository<InMemoryRepository> testRepo;
protected GerritServer server; protected GerritServer server;
protected TestAccount admin; protected TestAccount admin;

View File

@@ -79,7 +79,7 @@ public class TestAccount {
} }
public PersonIdent getIdent() { public PersonIdent getIdent() {
return new PersonIdent(username, email); return new PersonIdent(fullName, email);
} }
public String getHttpUrl(GerritServer server) { public String getHttpUrl(GerritServer server) {

View File

@@ -26,14 +26,12 @@ import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.git.CommitMergeStatus; import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
@@ -53,10 +51,6 @@ public class SubmitOnPushIT extends AbstractDaemonTest {
@Inject @Inject
private ChangeNotes.Factory changeNotesFactory; private ChangeNotes.Factory changeNotesFactory;
@Inject
@GerritPersonIdent
private PersonIdent serverIdent;
@Test @Test
public void submitOnPush() throws Exception { public void submitOnPush() throws Exception {
grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); grant(Permission.SUBMIT, project, "refs/for/refs/heads/master");
@@ -124,7 +118,7 @@ public class SubmitOnPushIT extends AbstractDaemonTest {
PushOneCommit.Result r = PushOneCommit.Result r =
push("refs/for/master%submit", "other change", "a.txt", "other content"); push("refs/for/master%submit", "other change", "a.txt", "other content");
r.assertErrorStatus(); r.assertErrorStatus();
r.assertChange(Change.Status.NEW, null, admin); r.assertChange(Change.Status.NEW, null);
r.assertMessage(CommitMergeStatus.PATH_CONFLICT.getMessage()); r.assertMessage(CommitMergeStatus.PATH_CONFLICT.getMessage());
} }
@@ -257,7 +251,7 @@ public class SubmitOnPushIT extends AbstractDaemonTest {
assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\""); assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\"");
assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email); assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email);
assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo( assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo(
serverIdent.getEmailAddress()); serverIdent.get().getEmailAddress());
} }
} }

View File

@@ -41,7 +41,6 @@ import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo; import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
@@ -61,6 +60,7 @@ import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
@@ -71,8 +71,6 @@ import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -145,9 +143,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
change1.assertChange(Change.Status.MERGED, "test-topic", admin); change1.assertChange(Change.Status.MERGED, "test-topic", admin);
change2.assertChange(Change.Status.MERGED, "test-topic", admin); change2.assertChange(Change.Status.MERGED, "test-topic", admin);
change3.assertChange(Change.Status.MERGED, "test-topic", admin); change3.assertChange(Change.Status.MERGED, "test-topic", admin);
// Check for the exact change to have the correct submitter.
assertSubmitter(change3);
// Also check submitters for changes submitted via the topic relationship.
assertSubmitter(change1); assertSubmitter(change1);
assertSubmitter(change2); assertSubmitter(change2);
assertSubmitter(change3);
} }
private void assertSubmitter(PushOneCommit.Result change) throws Exception { private void assertSubmitter(PushOneCommit.Result change) throws Exception {
@@ -202,22 +202,6 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
submit(changeId, HttpStatus.SC_CONFLICT); submit(changeId, HttpStatus.SC_CONFLICT);
} }
protected void submitStatusOnly(String changeId) throws Exception {
approve(changeId);
Change c = queryProvider.get().byKeyPrefix(changeId).get(0).change();
c.setStatus(Change.Status.SUBMITTED);
db.changes().update(Collections.singleton(c));
db.patchSetApprovals().insert(Collections.singleton(
new PatchSetApproval(
new PatchSetApproval.Key(
c.currentPatchSetId(),
admin.id,
LabelId.SUBMIT),
(short) 1,
new Timestamp(System.currentTimeMillis()))));
indexer.index(db, c);
}
private void submit(String changeId, int expectedStatus) throws Exception { private void submit(String changeId, int expectedStatus) throws Exception {
approve(changeId); approve(changeId);
SubmitInput subm = new SubmitInput(); SubmitInput subm = new SubmitInput();
@@ -265,6 +249,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
} }
} }
protected void assertNew(String changeId) throws Exception {
assertThat(get(changeId).status).isEqualTo(ChangeStatus.NEW);
}
protected void assertApproved(String changeId) throws Exception { protected void assertApproved(String changeId) throws Exception {
ChangeInfo c = get(changeId, DETAILED_LABELS); ChangeInfo c = get(changeId, DETAILED_LABELS);
LabelInfo cr = c.labels.get("Code-Review"); LabelInfo cr = c.labels.get("Code-Review");
@@ -273,6 +261,16 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(admin.getId()); assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(admin.getId());
} }
protected void assertPersonEquals(PersonIdent expected,
PersonIdent actual) {
assertThat(actual.getEmailAddress())
.isEqualTo(expected.getEmailAddress());
assertThat(actual.getName())
.isEqualTo(expected.getName());
assertThat(actual.getTimeZone())
.isEqualTo(expected.getTimeZone());
}
protected void assertSubmitter(String changeId, int psId) protected void assertSubmitter(String changeId, int psId)
throws OrmException { throws OrmException {
ChangeNotes cn = notesFactory.create( ChangeNotes cn = notesFactory.create(
@@ -283,6 +281,15 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(submitter.getAccountId()).isEqualTo(admin.getId()); assertThat(submitter.getAccountId()).isEqualTo(admin.getId());
} }
protected void assertNoSubmitter(String changeId, int psId)
throws OrmException {
ChangeNotes cn = notesFactory.create(
getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change());
PatchSetApproval submitter = approvalsUtil.getSubmitter(
db, cn, new PatchSet.Id(cn.getChangeId(), psId));
assertThat(submitter).isNull();
}
protected void assertCherryPick(TestRepository<?> testRepo, protected void assertCherryPick(TestRepository<?> testRepo,
boolean contentMerge) throws IOException { boolean contentMerge) throws IOException {
assertRebase(testRepo, contentMerge); assertRebase(testRepo, contentMerge);

View File

@@ -56,7 +56,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
@Test @Test
public void createEmptyChange_InvalidStatus() throws Exception { public void createEmptyChange_InvalidStatus() throws Exception {
ChangeInfo ci = newChangeInfo(ChangeStatus.SUBMITTED); ChangeInfo ci = newChangeInfo(ChangeStatus.MERGED);
assertCreateFails(ci, BadRequestException.class, assertCreateFails(ci, BadRequestException.class,
"unsupported change status"); "unsupported change status");
} }

View File

@@ -65,6 +65,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
assertCurrentRevision(change2.getChangeId(), 2, newHead); assertCurrentRevision(change2.getChangeId(), 2, newHead);
assertSubmitter(change2.getChangeId(), 1); assertSubmitter(change2.getChangeId(), 1);
assertSubmitter(change2.getChangeId(), 2); assertSubmitter(change2.getChangeId(), 2);
assertPersonEquals(admin.getIdent(), newHead.getAuthorIdent());
assertPersonEquals(admin.getIdent(), newHead.getCommitterIdent());
} }
@Test @Test
@@ -106,7 +108,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
submitWithConflict(change2.getChangeId()); submitWithConflict(change2.getChangeId());
assertThat(getRemoteHead()).isEqualTo(oldHead); assertThat(getRemoteHead()).isEqualTo(oldHead);
assertCurrentRevision(change2.getChangeId(), 1, change2.getCommitId()); assertCurrentRevision(change2.getChangeId(), 1, change2.getCommitId());
assertSubmitter(change2.getChangeId(), 1); assertNoSubmitter(change2.getChangeId(), 1);
} }
@Test @Test
@@ -146,7 +148,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
submitWithConflict(change3.getChangeId()); submitWithConflict(change3.getChangeId());
assertThat(getRemoteHead()).isEqualTo(oldHead); assertThat(getRemoteHead()).isEqualTo(oldHead);
assertCurrentRevision(change3.getChangeId(), 1, change3.getCommitId()); assertCurrentRevision(change3.getChangeId(), 1, change3.getCommitId());
assertSubmitter(change3.getChangeId(), 1); assertNoSubmitter(change3.getChangeId(), 1);
} }
@Test @Test
@@ -162,24 +164,17 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
testRepo.reset(initialHead); testRepo.reset(initialHead);
PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); PushOneCommit.Result change4 = createChange("Change 4", "d", "d");
submitStatusOnly(change2.getChangeId()); approve(change2.getChangeId());
submitStatusOnly(change3.getChangeId()); approve(change3.getChangeId());
submit(change4.getChangeId()); submit(change4.getChangeId());
List<RevCommit> log = getRemoteLog(); List<RevCommit> log = getRemoteLog();
assertThat(log.get(0).getShortMessage()).isEqualTo( assertThat(log.get(0).getShortMessage()).isEqualTo(
change4.getCommit().getShortMessage()); change4.getCommit().getShortMessage());
assertThat(log.get(0).getParent(0)).isEqualTo(log.get(1)); assertThat(log.get(1).getId()).isEqualTo(initialHead.getId());
assertThat(log.get(1).getShortMessage()).isEqualTo( assertNew(change2.getChangeId());
change3.getCommit().getShortMessage()); assertNew(change3.getChangeId());
assertThat(log.get(1).getParent(0)).isEqualTo(log.get(2));
assertThat(log.get(2).getShortMessage()).isEqualTo(
change2.getCommit().getShortMessage());
assertThat(log.get(2).getParent(0)).isEqualTo(log.get(3));
assertThat(log.get(3).getId()).isEqualTo(initialHead.getId());
} }
@Test @Test
@@ -231,6 +226,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
// Tip has not changed. // Tip has not changed.
List<RevCommit> log = getRemoteLog(); List<RevCommit> log = getRemoteLog();
assertThat(log.get(0)).isEqualTo(initialHead.getId()); assertThat(log.get(0)).isEqualTo(initialHead.getId());
assertNoSubmitter(change3.getChangeId(), 1);
} }
@Test @Test
@@ -238,57 +234,16 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
RevCommit initialHead = getRemoteHead(); RevCommit initialHead = getRemoteHead();
testRepo.reset(initialHead); testRepo.reset(initialHead);
createChange("Change 2", "b", "b"); PushOneCommit.Result change2 = createChange("Change 2", "b", "b");
PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); PushOneCommit.Result change3 = createChange("Change 3", "c", "c");
createChange("Change 4", "d", "d"); PushOneCommit.Result change4 = createChange("Change 5", "e", "e");
PushOneCommit.Result change5 = createChange("Change 5", "e", "e");
// Out of the above, only submit 3 and 5. // Out of the above, only submit 4. 2,3 are not related to 4
submitStatusOnly(change3.getChangeId()); // by topic or ancestor (due to cherrypicking!)
submit(change5.getChangeId()); approve(change3.getChangeId());
submit(change4.getChangeId());
ChangeInfo info3 = get(change3.getChangeId()); assertNew(change2.getChangeId());
assertThat(info3.status).isEqualTo(ChangeStatus.MERGED); assertNew(change3.getChangeId());
List<RevCommit> log = getRemoteLog();
assertThat(log.get(0).getShortMessage())
.isEqualTo(change5.getCommit().getShortMessage());
assertThat(log.get(1).getShortMessage())
.isEqualTo(change3.getCommit().getShortMessage());
assertThat(log.get(2).getShortMessage())
.isEqualTo(initialHead.getShortMessage());
}
@Test
public void submitChangeAfterParentFailsDueToConflict() throws Exception {
RevCommit initialHead = getRemoteHead();
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "b", "b1");
submit(change2.getChangeId());
testRepo.reset(initialHead);
PushOneCommit.Result change3 = createChange("Change 3", "b", "b2");
assertThat(change3.getCommit().getParent(0)).isEqualTo(initialHead);
PushOneCommit.Result change4 = createChange("Change 3", "c", "c3");
submitStatusOnly(change3.getChangeId());
submitStatusOnly(change4.getChangeId());
// Merge fails; change3 contains the delta "b1" -> "b2", which cannot be
// applied against tip.
// As change4 sits on top of change 3 we need to trigger submission there
// to include it into the mergeing
submitWithConflict(change4.getChangeId());
// change4 is a clean merge, so should succeed in the same run where change3
// failed.
ChangeInfo info4 = get(change4.getChangeId());
assertThat(info4.status).isEqualTo(ChangeStatus.MERGED);
List<RevCommit> log = getRemoteLog();
assertThat(log.get(0).getShortMessage())
.isEqualTo(change4.getCommit().getShortMessage());
assertThat(log.get(1).getShortMessage())
.isEqualTo(change2.getCommit().getShortMessage());
} }
} }

View File

@@ -56,6 +56,8 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
assertThat(head.getParent(0).getId()).isEqualTo(change.getCommitId()); assertThat(head.getParent(0).getId()).isEqualTo(change.getCommitId());
assertSubmitter(change.getChangeId(), 1); assertSubmitter(change.getChangeId(), 1);
assertSubmitter(change2.getChangeId(), 1); assertSubmitter(change2.getChangeId(), 1);
assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
} }
@Test @Test

View File

@@ -41,6 +41,8 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
assertThat(head.getParent(0)).isEqualTo(oldHead); assertThat(head.getParent(0)).isEqualTo(oldHead);
assertThat(head.getParent(1)).isEqualTo(change.getCommitId()); assertThat(head.getParent(1)).isEqualTo(change.getCommitId());
assertSubmitter(change.getChangeId(), 1); assertSubmitter(change.getChangeId(), 1);
assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
assertPersonEquals(serverIdent.get(), head.getCommitterIdent());
} }
@Test @Test
@@ -56,23 +58,22 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
testRepo.reset(initialHead); testRepo.reset(initialHead);
PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); PushOneCommit.Result change4 = createChange("Change 4", "d", "d");
submitStatusOnly(change2.getChangeId()); approve(change2.getChangeId());
submitStatusOnly(change3.getChangeId()); approve(change3.getChangeId());
submit(change4.getChangeId()); submit(change4.getChangeId());
List<RevCommit> log = getRemoteLog(); List<RevCommit> log = getRemoteLog();
RevCommit tip = log.get(0); RevCommit tip = log.get(0);
assertThat(tip.getParent(1).getShortMessage()).isEqualTo( assertThat(tip.getParent(1).getShortMessage()).isEqualTo(
change4.getCommit().getShortMessage()); change4.getCommit().getShortMessage());
assertThat(tip.getParent(0).getShortMessage()).isEqualTo(
tip = tip.getParent(0); initialHead.getShortMessage());
assertThat(tip.getParent(1).getShortMessage()).isEqualTo(
change3.getCommit().getShortMessage());
tip = tip.getParent(0);
assertThat(tip.getParent(1).getShortMessage()).isEqualTo(
change2.getCommit().getShortMessage());
assertThat(tip.getParent(0).getId()).isEqualTo(initialHead.getId()); assertThat(tip.getParent(0).getId()).isEqualTo(initialHead.getId());
assertPersonEquals(admin.getIdent(), tip.getAuthorIdent());
assertPersonEquals(serverIdent.get(), tip.getCommitterIdent());
assertNew(change2.getChangeId());
assertNew(change3.getChangeId());
} }
} }

View File

@@ -29,6 +29,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
assertThat(head.getId()).isEqualTo(change.getCommitId()); assertThat(head.getId()).isEqualTo(change.getCommitId());
assertThat(head.getParent(0)).isEqualTo(oldHead); assertThat(head.getParent(0)).isEqualTo(oldHead);
assertSubmitter(change.getChangeId(), 1); assertSubmitter(change.getChangeId(), 1);
assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
} }
@Test @Test
@@ -44,23 +46,32 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
testRepo.reset(initialHead); testRepo.reset(initialHead);
PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); PushOneCommit.Result change4 = createChange("Change 4", "d", "d");
submitStatusOnly(change2.getChangeId()); // Change 2 stays untouched.
submitStatusOnly(change3.getChangeId()); approve(change2.getChangeId());
submit(change4.getChangeId()); // Change 3 is a fast-forward, no need to merge.
submit(change3.getChangeId());
RevCommit tip = getRemoteLog().get(0); RevCommit tip = getRemoteLog().get(0);
assertThat(tip.getShortMessage()).isEqualTo(
change3.getCommit().getShortMessage());
assertThat(tip.getParent(0).getId()).isEqualTo(
initialHead.getId());
assertPersonEquals(admin.getIdent(), tip.getAuthorIdent());
assertPersonEquals(admin.getIdent(), tip.getCommitterIdent());
// We need to merge change 4.
submit(change4.getChangeId());
tip = getRemoteLog().get(0);
assertThat(tip.getParent(1).getShortMessage()).isEqualTo( assertThat(tip.getParent(1).getShortMessage()).isEqualTo(
change4.getCommit().getShortMessage()); change4.getCommit().getShortMessage());
assertThat(tip.getParent(0).getShortMessage()).isEqualTo(
tip = tip.getParent(0);
assertThat(tip.getParent(1).getShortMessage()).isEqualTo(
change3.getCommit().getShortMessage()); change3.getCommit().getShortMessage());
tip = tip.getParent(0); assertPersonEquals(admin.getIdent(), tip.getAuthorIdent());
assertThat(tip.getShortMessage()).isEqualTo( assertPersonEquals(serverIdent.get(), tip.getCommitterIdent());
change2.getCommit().getShortMessage());
assertThat(tip.getParent(0).getId()).isEqualTo(initialHead.getId()); assertNew(change2.getChangeId());
} }
@Test @Test
@@ -184,6 +195,10 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
initialHead2.getShortMessage()); initialHead2.getShortMessage());
assertThat(tip3.getShortMessage()).isEqualTo( assertThat(tip3.getShortMessage()).isEqualTo(
change3Conflict.getCommit().getShortMessage()); change3Conflict.getCommit().getShortMessage());
assertNoSubmitter(change1a.getChangeId(), 1);
assertNoSubmitter(change2a.getChangeId(), 1);
assertNoSubmitter(change2b.getChangeId(), 1);
assertNoSubmitter(change3.getChangeId(), 1);
} else { } else {
assertThat(tip1.getShortMessage()).isEqualTo( assertThat(tip1.getShortMessage()).isEqualTo(
change1b.getCommit().getShortMessage()); change1b.getCommit().getShortMessage());
@@ -191,6 +206,9 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
initialHead2.getShortMessage()); initialHead2.getShortMessage());
assertThat(tip3.getShortMessage()).isEqualTo( assertThat(tip3.getShortMessage()).isEqualTo(
change3Conflict.getCommit().getShortMessage()); change3Conflict.getCommit().getShortMessage());
assertNoSubmitter(change2a.getChangeId(), 1);
assertNoSubmitter(change2b.getChangeId(), 1);
assertNoSubmitter(change3.getChangeId(), 1);
} }
} }

View File

@@ -43,6 +43,8 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmit {
assertApproved(change.getChangeId()); assertApproved(change.getChangeId());
assertCurrentRevision(change.getChangeId(), 1, head); assertCurrentRevision(change.getChangeId(), 1, head);
assertSubmitter(change.getChangeId(), 1); assertSubmitter(change.getChangeId(), 1);
assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
} }
@Test @Test
@@ -65,6 +67,8 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmit {
assertCurrentRevision(change2.getChangeId(), 2, head); assertCurrentRevision(change2.getChangeId(), 2, head);
assertSubmitter(change2.getChangeId(), 1); assertSubmitter(change2.getChangeId(), 1);
assertSubmitter(change2.getChangeId(), 2); assertSubmitter(change2.getChangeId(), 2);
assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
assertPersonEquals(serverIdent.get(), head.getCommitterIdent());
} }
@Test @Test
@@ -107,6 +111,6 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmit {
RevCommit head = getRemoteHead(); RevCommit head = getRemoteHead();
assertThat(head).isEqualTo(oldHead); assertThat(head).isEqualTo(oldHead);
assertCurrentRevision(change2.getChangeId(), 1, change2.getCommitId()); assertCurrentRevision(change2.getChangeId(), 1, change2.getCommitId());
assertSubmitter(change2.getChangeId(), 1); assertNoSubmitter(change2.getChangeId(), 1);
} }
} }

View File

@@ -102,9 +102,9 @@ public class GetCommitIT extends AbstractDaemonTest {
assertThat(info.subject).isEqualTo("test commit"); assertThat(info.subject).isEqualTo("test commit");
assertThat(info.message).isEqualTo( assertThat(info.message).isEqualTo(
"test commit\n\nChange-Id: " + r.getChangeId() + "\n"); "test commit\n\nChange-Id: " + r.getChangeId() + "\n");
assertThat(info.author.name).isEqualTo("admin"); assertThat(info.author.name).isEqualTo("Administrator");
assertThat(info.author.email).isEqualTo("admin@example.com"); assertThat(info.author.email).isEqualTo("admin@example.com");
assertThat(info.committer.name).isEqualTo("admin"); assertThat(info.committer.name).isEqualTo("Administrator");
assertThat(info.committer.email).isEqualTo("admin@example.com"); assertThat(info.committer.email).isEqualTo("admin@example.com");
CommitInfo parent = Iterables.getOnlyElement(info.parents); CommitInfo parent = Iterables.getOnlyElement(info.parents);

View File

@@ -135,7 +135,6 @@ public class PageLinks {
case MERGED: case MERGED:
return "status:merged"; return "status:merged";
case NEW: case NEW:
case SUBMITTED:
default: default:
return "status:open"; return "status:open";
} }

View File

@@ -28,39 +28,12 @@ public enum ChangeStatus {
* <p> * <p>
* Changes in the NEW state can be moved to: * Changes in the NEW state can be moved to:
* <ul> * <ul>
* <li>{@link #SUBMITTED} - when the Submit Patch Set action is used; * <li>{@link #MERGED} - when the Submit Patch Set action is used;
* <li>{@link #ABANDONED} - when the Abandon action is used. * <li>{@link #ABANDONED} - when the Abandon action is used.
* </ul> * </ul>
*/ */
NEW, NEW,
/**
* Change is open, but has been submitted to the merge queue.
*
* <p>
* A change enters the SUBMITTED state when an authorized user presses the
* "submit" action through the web UI, requesting that Gerrit merge the
* change's current patch set into the destination branch.
*
* <p>
* Typically a change resides in the SUBMITTED for only a brief sub-second
* period while the merge queue fires and the destination branch is updated.
* However, if a dependency commit (directly or transitively) is not yet
* merged into the branch, the change will hang in the SUBMITTED state
* indefinitely.
*
* <p>
* Changes in the SUBMITTED state can be moved to:
* <ul>
* <li>{@link #NEW} - when a replacement patch set is supplied, OR when a
* merge conflict is detected;
* <li>{@link #MERGED} - when the change has been successfully merged into
* the destination branch;
* <li>{@link #ABANDONED} - when the Abandon action is used.
* </ul>
*/
SUBMITTED,
/** /**
* Change is a draft change that only consists of draft patchsets. * Change is a draft change that only consists of draft patchsets.
* *

View File

@@ -18,7 +18,6 @@ import com.google.gwt.i18n.client.Constants;
public interface ChangeConstants extends Constants { public interface ChangeConstants extends Constants {
String statusLongNew(); String statusLongNew();
String statusLongSubmitted();
String statusLongMerged(); String statusLongMerged();
String statusLongAbandoned(); String statusLongAbandoned();
String statusLongDraft(); String statusLongDraft();

View File

@@ -1,5 +1,4 @@
statusLongNew = Review in Progress statusLongNew = Review in Progress
statusLongSubmitted = Submitted, Merge Pending
statusLongMerged = Merged statusLongMerged = Merged
statusLongAbandoned = Abandoned statusLongAbandoned = Abandoned
statusLongDraft = Draft statusLongDraft = Draft

View File

@@ -34,8 +34,6 @@ public class Util {
return C.statusLongDraft(); return C.statusLongDraft();
case NEW: case NEW:
return C.statusLongNew(); return C.statusLongNew();
case SUBMITTED:
return C.statusLongSubmitted();
case MERGED: case MERGED:
return C.statusLongMerged(); return C.statusLongMerged();
case ABANDONED: case ABANDONED:

View File

@@ -251,8 +251,6 @@ public final class Change {
private static final char MIN_OPEN = 'a'; private static final char MIN_OPEN = 'a';
/** Database constant for {@link Status#NEW}. */ /** Database constant for {@link Status#NEW}. */
public static final char STATUS_NEW = 'n'; public static final char STATUS_NEW = 'n';
/** Database constant for {@link Status#SUBMITTED}. */
public static final char STATUS_SUBMITTED = 's';
/** Database constant for {@link Status#DRAFT}. */ /** Database constant for {@link Status#DRAFT}. */
public static final char STATUS_DRAFT = 'd'; public static final char STATUS_DRAFT = 'd';
/** Maximum database status constant for an open change. */ /** Maximum database status constant for an open change. */
@@ -285,39 +283,12 @@ public final class Change {
* <p> * <p>
* Changes in the NEW state can be moved to: * Changes in the NEW state can be moved to:
* <ul> * <ul>
* <li>{@link #SUBMITTED} - when the Submit Patch Set action is used; * <li>{@link #MERGED} - when the Submit Patch Set action is used;
* <li>{@link #ABANDONED} - when the Abandon action is used. * <li>{@link #ABANDONED} - when the Abandon action is used.
* </ul> * </ul>
*/ */
NEW(STATUS_NEW, ChangeStatus.NEW), NEW(STATUS_NEW, ChangeStatus.NEW),
/**
* Change is open, but has been submitted to the merge queue.
*
* <p>
* A change enters the SUBMITTED state when an authorized user presses the
* "submit" action through the web UI, requesting that Gerrit merge the
* change's current patch set into the destination branch.
*
* <p>
* Typically a change resides in the SUBMITTED for only a brief sub-second
* period while the merge queue fires and the destination branch is updated.
* However, if a dependency commit (a {@link PatchSetAncestor}, directly or
* transitively) is not yet merged into the branch, the change will hang in
* the SUBMITTED state indefinitely.
*
* <p>
* Changes in the SUBMITTED state can be moved to:
* <ul>
* <li>{@link #NEW} - when a replacement patch set is supplied, OR when a
* merge conflict is detected;
* <li>{@link #MERGED} - when the change has been successfully merged into
* the destination branch;
* <li>{@link #ABANDONED} - when the Abandon action is used.
* </ul>
*/
SUBMITTED(STATUS_SUBMITTED, ChangeStatus.SUBMITTED),
/** /**
* Change is a draft change that only consists of draft patchsets. * Change is a draft change that only consists of draft patchsets.
* *

View File

@@ -244,7 +244,8 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
null /*inserter*/, null /*inserter*/,
canMerge, canMerge,
accepted, accepted,
key.load.dest).dryRun(tip, rev); key.load.dest,
null).dryRun(tip, rev);
} }
} finally { } finally {
key.load = null; key.load = null;

View File

@@ -18,17 +18,12 @@ import static com.google.gerrit.common.data.SubmitRecord.Status.OK;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable; import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.ParameterizedString; import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.api.changes.SubmitInput; import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -38,34 +33,24 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil; import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ProjectUtil; import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.account.AccountsCollection; import com.google.gerrit.server.account.AccountsCollection;
import com.google.gerrit.server.config.GerritServerConfig; 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.LabelNormalizer;
import com.google.gerrit.server.git.MergeOp; import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeUpdate;
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;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException; import com.google.gwtorm.server.OrmRuntimeException;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -73,15 +58,12 @@ import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -106,31 +88,19 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
private static final String CLICK_FAILURE_TOOLTIP = private static final String CLICK_FAILURE_TOOLTIP =
"Clicking the button would fail."; "Clicking the button would fail.";
public enum Status {
SUBMITTED, MERGED
}
public static class Output { public static class Output {
public Status status;
transient Change change; transient Change change;
private Output(Status s, Change c) { private Output(Change c) {
status = s;
change = c; change = c;
} }
} }
private final PersonIdent serverIdent;
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final IdentifiedUser.GenericFactory userFactory;
private final ChangeData.Factory changeDataFactory; private final ChangeData.Factory changeDataFactory;
private final ChangeUpdate.Factory updateFactory;
private final ApprovalsUtil approvalsUtil;
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final Provider<MergeOp> mergeOpProvider; private final Provider<MergeOp> mergeOpProvider;
private final ChangeIndexer indexer;
private final LabelNormalizer labelNormalizer;
private final AccountsCollection accounts; private final AccountsCollection accounts;
private final ChangesCollection changes; private final ChangesCollection changes;
private final String label; private final String label;
@@ -141,34 +111,22 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
private final Provider<InternalChangeQuery> queryProvider; private final Provider<InternalChangeQuery> queryProvider;
@Inject @Inject
Submit(@GerritPersonIdent PersonIdent serverIdent, Submit(Provider<ReviewDb> dbProvider,
Provider<ReviewDb> dbProvider,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
IdentifiedUser.GenericFactory userFactory,
ChangeData.Factory changeDataFactory, ChangeData.Factory changeDataFactory,
ChangeUpdate.Factory updateFactory,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
Provider<MergeOp> mergeOpProvider, Provider<MergeOp> mergeOpProvider,
AccountsCollection accounts, AccountsCollection accounts,
ChangesCollection changes, ChangesCollection changes,
ChangeIndexer indexer,
LabelNormalizer labelNormalizer,
@GerritServerConfig Config cfg, @GerritServerConfig Config cfg,
Provider<InternalChangeQuery> queryProvider) { Provider<InternalChangeQuery> queryProvider) {
this.serverIdent = serverIdent;
this.dbProvider = dbProvider; this.dbProvider = dbProvider;
this.repoManager = repoManager; this.repoManager = repoManager;
this.userFactory = userFactory;
this.changeDataFactory = changeDataFactory; this.changeDataFactory = changeDataFactory;
this.updateFactory = updateFactory;
this.approvalsUtil = approvalsUtil;
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.mergeOpProvider = mergeOpProvider; this.mergeOpProvider = mergeOpProvider;
this.accounts = accounts; this.accounts = accounts;
this.changes = changes; this.changes = changes;
this.indexer = indexer;
this.labelNormalizer = labelNormalizer;
this.label = MoreObjects.firstNonNull( this.label = MoreObjects.firstNonNull(
Strings.emptyToNull(cfg.getString("change", null, "submitLabel")), Strings.emptyToNull(cfg.getString("change", null, "submitLabel")),
"Submit"); "Submit");
@@ -212,7 +170,16 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
rsrc.getPatchSet().getRevision().get())); rsrc.getPatchSet().getRevision().get()));
} }
ChangeSet submittedChanges = ChangeSet.create(submit(rsrc, caller, false)); List<Change> changes;
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 {
mergeOpProvider.get().merge(submittedChanges, caller, true); mergeOpProvider.get().merge(submittedChanges, caller, true);
@@ -225,10 +192,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
throw new ResourceConflictException("change is deleted"); throw new ResourceConflictException("change is deleted");
} }
switch (change.getStatus()) { switch (change.getStatus()) {
case SUBMITTED:
return new Output(Status.SUBMITTED, change);
case MERGED: case MERGED:
return new Output(Status.MERGED, change); return new Output(change);
case NEW: case NEW:
ChangeMessage msg = getConflictMessage(rsrc); ChangeMessage msg = getConflictMessage(rsrc);
if (msg != null) { if (msg != null) {
@@ -374,203 +339,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
.orNull(); .orNull();
} }
private Change submitToDatabase(final ReviewDb db, final Change.Id changeId,
final Timestamp timestamp) throws OrmException,
ResourceConflictException {
Change ret = db.changes().atomicUpdate(changeId,
new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
if (change.getStatus().isOpen()) {
change.setStatus(Change.Status.SUBMITTED);
change.setLastUpdatedOn(timestamp);
return change;
}
return null;
}
});
if (ret != null) {
return ret;
} else {
throw new ResourceConflictException("change " + changeId + " is "
+ status(db.changes().get(changeId)));
}
}
private Change submitThisChange(RevisionResource rsrc, IdentifiedUser caller,
boolean force) throws ResourceConflictException, OrmException,
IOException {
ReviewDb db = dbProvider.get();
ChangeData cd = changeDataFactory.create(db, rsrc.getControl());
List<SubmitRecord> submitRecords = checkSubmitRule(cd,
rsrc.getPatchSet(), force);
final Timestamp timestamp = TimeUtil.nowTs();
Change change = rsrc.getChange();
ChangeUpdate update = updateFactory.create(rsrc.getControl(), timestamp);
update.submit(submitRecords);
db.changes().beginTransaction(change.getId());
try {
BatchMetaDataUpdate batch = approve(rsrc.getPatchSet().getId(),
cd.changeControl(), update, caller, timestamp);
// Write update commit after all normalized label commits.
batch.write(update, new CommitBuilder());
change = submitToDatabase(db, change.getId(), timestamp);
db.commit();
} finally {
db.rollback();
}
indexer.index(db, change);
return change;
}
private List<Change> submitWholeTopic(RevisionResource rsrc, IdentifiedUser caller,
boolean force, String topic) throws ResourceConflictException, OrmException,
IOException {
Preconditions.checkNotNull(topic);
final Timestamp timestamp = TimeUtil.nowTs();
ReviewDb db = dbProvider.get();
ChangeData cd = changeDataFactory.create(db, rsrc.getControl());
List<ChangeData> changesByTopic = getChangesByTopic(topic);
String problems = problemsForSubmittingChanges(changesByTopic, caller);
if (problems != null) {
throw new ResourceConflictException(problems);
}
Change change = rsrc.getChange();
ChangeUpdate update = updateFactory.create(rsrc.getControl(), timestamp);
List<SubmitRecord> submitRecords = checkSubmitRule(cd,
rsrc.getPatchSet(), force);
update.submit(submitRecords);
db.changes().beginTransaction(change.getId());
try {
for (ChangeData c : changesByTopic) {
BatchMetaDataUpdate batch = approve(c.currentPatchSet().getId(),
c.changeControl(), update, caller, timestamp);
// Write update commit after all normalized label commits.
batch.write(update, new CommitBuilder());
submitToDatabase(db, c.getId(), timestamp);
}
db.commit();
} finally {
db.rollback();
}
List<Change.Id> ids = new ArrayList<>(changesByTopic.size());
List<Change> ret = new ArrayList<>(changesByTopic.size());
for (ChangeData c : changesByTopic) {
ids.add(c.getId());
ret.add(c.change());
}
indexer.indexAsync(ids).checkedGet();
return ret;
}
public List<Change> submit(RevisionResource rsrc, IdentifiedUser caller,
boolean force) throws ResourceConflictException, OrmException,
IOException {
String topic = rsrc.getChange().getTopic();
if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) {
return submitWholeTopic(rsrc, caller, force, topic);
} else {
return Arrays.asList(submitThisChange(rsrc, caller, force));
}
}
private BatchMetaDataUpdate approve(PatchSet.Id psId, ChangeControl control,
ChangeUpdate update, IdentifiedUser caller, Timestamp timestamp)
throws OrmException {
Map<PatchSetApproval.Key, PatchSetApproval> byKey = Maps.newHashMap();
for (PatchSetApproval psa :
approvalsUtil.byPatchSet(dbProvider.get(), control, psId)) {
if (!byKey.containsKey(psa.getKey())) {
byKey.put(psa.getKey(), psa);
}
}
PatchSetApproval submit = ApprovalsUtil.getSubmitter(psId, byKey.values());
if (submit == null
|| !submit.getAccountId().equals(caller.getAccountId())) {
submit = new PatchSetApproval(
new PatchSetApproval.Key(
psId,
caller.getAccountId(),
LabelId.SUBMIT),
(short) 1, TimeUtil.nowTs());
byKey.put(submit.getKey(), submit);
}
submit.setValue((short) 1);
submit.setGranted(timestamp);
// Flatten out existing approvals for this patch set based upon the current
// permissions. Once the change is closed the approvals are not updated at
// presentation view time, except for zero votes used to indicate a reviewer
// was added. So we need to make sure votes are accurate now. This way if
// permissions get modified in the future, historical records stay accurate.
LabelNormalizer.Result normalized =
labelNormalizer.normalize(control, byKey.values());
// TODO(dborowitz): Don't use a label in notedb; just check when status
// change happened.
update.putApproval(submit.getLabel(), submit.getValue());
dbProvider.get().patchSetApprovals().upsert(normalized.getNormalized());
dbProvider.get().patchSetApprovals().delete(normalized.deleted());
try {
return saveToBatch(control, update, normalized, timestamp);
} catch (IOException e) {
throw new OrmException(e);
}
}
private BatchMetaDataUpdate saveToBatch(ChangeControl ctl,
ChangeUpdate callerUpdate, LabelNormalizer.Result normalized,
Timestamp timestamp) throws IOException {
Table<Account.Id, String, Optional<Short>> byUser = HashBasedTable.create();
for (PatchSetApproval psa : normalized.updated()) {
byUser.put(psa.getAccountId(), psa.getLabel(),
Optional.of(psa.getValue()));
}
for (PatchSetApproval psa : normalized.deleted()) {
byUser.put(psa.getAccountId(), psa.getLabel(), Optional.<Short> absent());
}
BatchMetaDataUpdate batch = callerUpdate.openUpdate();
for (Account.Id accountId : byUser.rowKeySet()) {
if (!accountId.equals(callerUpdate.getUser().getAccountId())) {
ChangeUpdate update = updateFactory.create(
ctl.forUser(userFactory.create(dbProvider, accountId)), timestamp);
update.setSubject("Finalize approvals at submit");
putApprovals(update, byUser.row(accountId));
CommitBuilder commit = new CommitBuilder();
commit.setCommitter(new PersonIdent(serverIdent, timestamp));
batch.write(update, commit);
}
}
putApprovals(callerUpdate,
byUser.row(callerUpdate.getUser().getAccountId()));
return batch;
}
private static void putApprovals(ChangeUpdate update,
Map<String, Optional<Short>> approvals) {
for (Map.Entry<String, Optional<Short>> e : approvals.entrySet()) {
if (e.getValue().isPresent()) {
update.putApproval(e.getKey(), e.getValue().get());
} else {
update.removeApproval(e.getKey());
}
}
}
private List<SubmitRecord> checkSubmitRule(ChangeData cd, private List<SubmitRecord> checkSubmitRule(ChangeData cd,
PatchSet patchSet, boolean force) PatchSet patchSet, boolean force)
throws ResourceConflictException, OrmException { throws ResourceConflictException, OrmException {

View File

@@ -33,18 +33,22 @@ public abstract class ChangeSet {
ImmutableSetMultimap.builder(); ImmutableSetMultimap.builder();
ImmutableSetMultimap.Builder<Project.NameKey, Change.Id> pcb = ImmutableSetMultimap.Builder<Project.NameKey, Change.Id> pcb =
ImmutableSetMultimap.builder(); ImmutableSetMultimap.builder();
ImmutableSetMultimap.Builder<Branch.NameKey, Change.Id> cbb =
ImmutableSetMultimap.builder();
for (Change c : changes) { for (Change c : changes) {
Project.NameKey project = c.getDest().getParentKey(); Branch.NameKey branch = c.getDest();
Project.NameKey project = branch.getParentKey();
pb.add(project); pb.add(project);
bb.add(c.getDest()); bb.add(branch);
ib.add(c.getId()); ib.add(c.getId());
pbb.put(project, c.getDest()); pbb.put(project, branch);
pcb.put(project, c.getId()); pcb.put(project, 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(), pbb.build(), pcb.build()); ib.build(), pbb.build(), pcb.build(), cbb.build());
} }
public static ChangeSet create(Change change) { public static ChangeSet create(Change change) {
@@ -58,6 +62,8 @@ public abstract class ChangeSet {
branchesByProject(); branchesByProject();
public abstract ImmutableSetMultimap<Project.NameKey, Change.Id> public abstract ImmutableSetMultimap<Project.NameKey, Change.Id>
changesByProject(); changesByProject();
public abstract ImmutableSetMultimap<Branch.NameKey, Change.Id>
changesByBranch();
@Override @Override
public int hashCode() { public int hashCode() {

View File

@@ -20,11 +20,12 @@ import static org.eclipse.jgit.lib.RefDatabase.ALL;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Table;
import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
@@ -35,6 +36,7 @@ import com.google.gerrit.reviewdb.client.Account;
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.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
@@ -44,15 +46,14 @@ import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil; import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer; import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.config.GerritRequestModule; import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.config.RequestScopedReviewDbProvider; import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
import com.google.gerrit.server.git.strategy.SubmitStrategy; import com.google.gerrit.server.git.strategy.SubmitStrategy;
import com.google.gerrit.server.git.strategy.SubmitStrategyFactory; import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
import com.google.gerrit.server.git.validators.MergeValidationException; import com.google.gerrit.server.git.validators.MergeValidationException;
@@ -89,6 +90,7 @@ import com.jcraft.jsch.HostKey;
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
@@ -105,6 +107,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -144,6 +147,7 @@ public class MergeOp {
private final GitReferenceUpdated gitRefUpdated; private final GitReferenceUpdated gitRefUpdated;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final IdentifiedUser.GenericFactory identifiedUserFactory; private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final LabelNormalizer labelNormalizer;
private final MergedSender.Factory mergedSenderFactory; private final MergedSender.Factory mergedSenderFactory;
private final MergeSuperSet mergeSuperSet; private final MergeSuperSet mergeSuperSet;
private final MergeValidators.Factory mergeValidatorsFactory; private final MergeValidators.Factory mergeValidatorsFactory;
@@ -151,12 +155,13 @@ public class MergeOp {
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final InternalChangeQuery internalChangeQuery; private final InternalChangeQuery internalChangeQuery;
private final SchemaFactory<ReviewDb> schemaFactory; private final SchemaFactory<ReviewDb> schemaFactory;
private final Submit submit; private final PersonIdent serverIdent;
private final SubmitStrategyFactory submitStrategyFactory; private final SubmitStrategyFactory submitStrategyFactory;
private final Provider<SubmoduleOp> subOpProvider; private final Provider<SubmoduleOp> subOpProvider;
private final TagCache tagCache; private final TagCache tagCache;
private final WorkQueue workQueue; private final WorkQueue workQueue;
private final Map<Change.Id, List<SubmitRecord>> records;
private final Map<Change.Id, CodeReviewCommit> commits; private final Map<Change.Id, CodeReviewCommit> commits;
private final PerThreadRequestScope.Scoper threadScoper; private final PerThreadRequestScope.Scoper threadScoper;
private String logPrefix; private String logPrefix;
@@ -185,6 +190,7 @@ public class MergeOp {
GitReferenceUpdated gitRefUpdated, GitReferenceUpdated gitRefUpdated,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
IdentifiedUser.GenericFactory identifiedUserFactory, IdentifiedUser.GenericFactory identifiedUserFactory,
LabelNormalizer labelNormalizer,
MergedSender.Factory mergedSenderFactory, MergedSender.Factory mergedSenderFactory,
MergeSuperSet mergeSuperSet, MergeSuperSet mergeSuperSet,
MergeValidators.Factory mergeValidatorsFactory, MergeValidators.Factory mergeValidatorsFactory,
@@ -192,7 +198,7 @@ public class MergeOp {
ProjectCache projectCache, ProjectCache projectCache,
InternalChangeQuery internalChangeQuery, InternalChangeQuery internalChangeQuery,
SchemaFactory<ReviewDb> schemaFactory, SchemaFactory<ReviewDb> schemaFactory,
Submit submit, @GerritPersonIdent PersonIdent serverIdent,
SubmitStrategyFactory submitStrategyFactory, SubmitStrategyFactory submitStrategyFactory,
Provider<SubmoduleOp> subOpProvider, Provider<SubmoduleOp> subOpProvider,
TagCache tagCache, TagCache tagCache,
@@ -208,6 +214,7 @@ public class MergeOp {
this.gitRefUpdated = gitRefUpdated; this.gitRefUpdated = gitRefUpdated;
this.repoManager = repoManager; this.repoManager = repoManager;
this.identifiedUserFactory = identifiedUserFactory; this.identifiedUserFactory = identifiedUserFactory;
this.labelNormalizer = labelNormalizer;
this.mergedSenderFactory = mergedSenderFactory; this.mergedSenderFactory = mergedSenderFactory;
this.mergeSuperSet = mergeSuperSet; this.mergeSuperSet = mergeSuperSet;
this.mergeValidatorsFactory = mergeValidatorsFactory; this.mergeValidatorsFactory = mergeValidatorsFactory;
@@ -215,7 +222,7 @@ public class MergeOp {
this.projectCache = projectCache; this.projectCache = projectCache;
this.internalChangeQuery = internalChangeQuery; this.internalChangeQuery = internalChangeQuery;
this.schemaFactory = schemaFactory; this.schemaFactory = schemaFactory;
this.submit = submit; this.serverIdent = serverIdent;
this.submitStrategyFactory = submitStrategyFactory; this.submitStrategyFactory = submitStrategyFactory;
this.subOpProvider = subOpProvider; this.subOpProvider = subOpProvider;
this.tagCache = tagCache; this.tagCache = tagCache;
@@ -223,6 +230,8 @@ public class MergeOp {
commits = new HashMap<>(); commits = new HashMap<>();
pendingRefUpdates = new HashMap<>(); pendingRefUpdates = new HashMap<>();
openBranches = new HashMap<>(); openBranches = new HashMap<>();
pendingRefUpdates = new HashMap<>();
records = new HashMap<>();
mergeTips = new HashMap<>(); mergeTips = new HashMap<>();
Injector child = injector.createChildInjector(new AbstractModule() { Injector child = injector.createChildInjector(new AbstractModule() {
@@ -379,40 +388,11 @@ public class MergeOp {
throws ResourceConflictException, OrmException { throws ResourceConflictException, OrmException {
for (Change.Id id : cs.ids()) { for (Change.Id id : cs.ids()) {
ChangeData cd = changeDataFactory.create(db, id); ChangeData cd = changeDataFactory.create(db, id);
if (cd.change().getStatus() != Change.Status.NEW if (cd.change().getStatus() != Change.Status.NEW){
&& cd.change().getStatus() != Change.Status.SUBMITTED){
throw new OrmException("Change " + cd.change().getChangeId() throw new OrmException("Change " + cd.change().getChangeId()
+ " is in state " + cd.change().getStatus()); + " is in state " + cd.change().getStatus());
} else { } else {
checkSubmitRule(cd); records.put(cd.change().getId(), checkSubmitRule(cd));
}
}
}
// For historic reasons we will first go into the submitted state
// TODO(sbeller): remove this when we get rid of Change.Status.SUBMITTED
private void submitAllChanges(ChangeSet cs, IdentifiedUser caller,
boolean force) throws OrmException, ResourceConflictException,
IOException {
for (Change.Id id : cs.ids()) {
ChangeData cd = changeDataFactory.create(db, id);
switch (cd.change().getStatus()) {
case ABANDONED:
throw new ResourceConflictException("Change " + cd.getId() +
" was abandoned while processing this change set");
case DRAFT:
throw new ResourceConflictException("Cannot submit draft " + cd.getId());
case NEW:
RevisionResource rsrc =
new RevisionResource(new ChangeResource(cd.changeControl(), null),
cd.currentPatchSet());
logDebug("Submitting change id {}", cd.change().getId());
submit.submit(rsrc, caller, force);
break;
case MERGED:
// we're racing here, but having it already merged is fine.
case SUBMITTED:
// ok
} }
} }
} }
@@ -428,17 +408,11 @@ public class MergeOp {
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 (checkPermissions) {
logDebug("Submitting all calculated changes while "
+ "enforcing submit rules");
submitAllChanges(cs, caller, false);
logDebug("Checking permissions"); logDebug("Checking permissions");
checkPermissions(cs); checkPermissions(cs);
} else {
logDebug("Submitting all calculated changes ignoring submit rules");
submitAllChanges(cs, caller, true);
} }
try { try {
integrateIntoHistory(cs); integrateIntoHistory(cs, caller);
} catch (MergeException e) { } catch (MergeException e) {
logError("Merge Conflict", e); logError("Merge Conflict", e);
throw new ResourceConflictException("Merge Conflict", e); throw new ResourceConflictException("Merge Conflict", e);
@@ -453,10 +427,10 @@ public class MergeOp {
} }
} }
private void integrateIntoHistory(ChangeSet cs) private void integrateIntoHistory(ChangeSet cs, IdentifiedUser caller)
throws MergeException, NoSuchChangeException, ResourceConflictException { throws MergeException, NoSuchChangeException, ResourceConflictException {
logDebug("Beginning merge attempt on {}", cs); logDebug("Beginning merge attempt on {}", cs);
Map<Branch.NameKey, ListMultimap<SubmitType, Change>> toSubmit = Map<Branch.NameKey, ListMultimap<SubmitType, ChangeData>> toSubmit =
new HashMap<>(); new HashMap<>();
try { try {
openSchema(); openSchema();
@@ -465,24 +439,25 @@ public class MergeOp {
openRepository(project); openRepository(project);
for (Branch.NameKey branch : cs.branchesByProject().get(project)) { for (Branch.NameKey branch : cs.branchesByProject().get(project)) {
setDestProject(branch); setDestProject(branch);
ListMultimap<SubmitType, Change> submitting =
validateChangeList(internalChangeQuery.submitted(branch)); List<ChangeData> cds = new ArrayList<>();
for (Change.Id id : cs.changesByBranch().get(branch)) {
cds.add(changeDataFactory.create(db, id));
}
ListMultimap<SubmitType, ChangeData> submitting =
validateChangeList(cds);
toSubmit.put(branch, submitting); toSubmit.put(branch, submitting);
Set<SubmitType> submitTypes = new HashSet<>(submitting.keySet()); Set<SubmitType> submitTypes = new HashSet<>(submitting.keySet());
for (SubmitType submitType : submitTypes) { for (SubmitType submitType : submitTypes) {
SubmitStrategy strategy = createStrategy(branch, submitType, SubmitStrategy strategy = createStrategy(branch, submitType,
getBranchTip(branch)); getBranchTip(branch), caller);
MergeTip mergeTip = preMerge(strategy, submitting.get(submitType), MergeTip mergeTip = preMerge(strategy, submitting.get(submitType),
getBranchTip(branch)); getBranchTip(branch));
mergeTips.put(branch, mergeTip); mergeTips.put(branch, mergeTip);
if (submitType != SubmitType.CHERRY_PICK) { updateChangeStatus(submitting.get(submitType), branch,
// For cherry picking we have relaxed atomic guarantees true, caller);
// as traditionally Gerrit kept going cherry picking if one
// failed. We want to keep it for now.
updateChangeStatus(submitting.get(submitType), branch, true);
}
} }
inserter.flush(); inserter.flush();
} }
@@ -498,9 +473,10 @@ public class MergeOp {
pendingRefUpdates.remove(branch); pendingRefUpdates.remove(branch);
setDestProject(branch); setDestProject(branch);
ListMultimap<SubmitType, Change> submitting = toSubmit.get(branch); ListMultimap<SubmitType, ChangeData> submitting = toSubmit.get(branch);
for (SubmitType submitType : submitting.keySet()) { for (SubmitType submitType : submitting.keySet()) {
updateChangeStatus(submitting.get(submitType), branch, false); updateChangeStatus(submitting.get(submitType), branch,
false, caller);
updateSubmoduleSubscriptions(subOp, branch, getBranchTip(branch)); updateSubmoduleSubscriptions(subOp, branch, getBranchTip(branch));
} }
if (update != null) { if (update != null) {
@@ -526,15 +502,15 @@ public class MergeOp {
} }
private MergeTip preMerge(SubmitStrategy strategy, private MergeTip preMerge(SubmitStrategy strategy,
List<Change> submitted, CodeReviewCommit branchTip) List<ChangeData> submitted, CodeReviewCommit branchTip)
throws MergeException { throws MergeException, OrmException {
logDebug("Running submit strategy {} for {} commits {}", logDebug("Running submit strategy {} for {} commits {}",
strategy.getClass().getSimpleName(), submitted.size(), submitted); strategy.getClass().getSimpleName(), submitted.size(), submitted);
List<CodeReviewCommit> toMerge = new ArrayList<>(submitted.size()); List<CodeReviewCommit> toMerge = new ArrayList<>(submitted.size());
for (Change c : submitted) { for (ChangeData cd : submitted) {
CodeReviewCommit commit = commits.get(c.getId()); CodeReviewCommit commit = commits.get(cd.change().getId());
checkState(commit != null, checkState(commit != null,
"commit for %s not found by validateChangeList", c.getId()); "commit for %s not found by validateChangeList", cd.change().getId());
toMerge.add(commit); toMerge.add(commit);
} }
MergeTip mergeTip = strategy.run(branchTip, toMerge); MergeTip mergeTip = strategy.run(branchTip, toMerge);
@@ -545,10 +521,10 @@ public class MergeOp {
} }
private SubmitStrategy createStrategy(Branch.NameKey destBranch, private SubmitStrategy createStrategy(Branch.NameKey destBranch,
SubmitType submitType, CodeReviewCommit branchTip) SubmitType submitType, CodeReviewCommit branchTip, IdentifiedUser caller)
throws MergeException, NoSuchProjectException { throws MergeException, NoSuchProjectException {
return submitStrategyFactory.create(submitType, db, repo, rw, inserter, return submitStrategyFactory.create(submitType, db, repo, rw, inserter,
canMergeFlag, getAlreadyAccepted(branchTip), destBranch); canMergeFlag, getAlreadyAccepted(branchTip), destBranch, caller);
} }
private void openRepository(Project.NameKey name) private void openRepository(Project.NameKey name)
@@ -649,10 +625,10 @@ public class MergeOp {
return alreadyAccepted; return alreadyAccepted;
} }
private ListMultimap<SubmitType, Change> validateChangeList( private ListMultimap<SubmitType, ChangeData> validateChangeList(
List<ChangeData> submitted) throws MergeException { List<ChangeData> submitted) throws MergeException {
logDebug("Validating {} changes", submitted.size()); logDebug("Validating {} changes", submitted.size());
ListMultimap<SubmitType, Change> toSubmit = ArrayListMultimap.create(); ListMultimap<SubmitType, ChangeData> toSubmit = ArrayListMultimap.create();
Map<String, Ref> allRefs; Map<String, Ref> allRefs;
try { try {
@@ -677,8 +653,8 @@ public class MergeOp {
throw new MergeException("Failed to validate changes", e); throw new MergeException("Failed to validate changes", e);
} }
Change.Id changeId = cd.getId(); Change.Id changeId = cd.getId();
if (chg.getStatus() != Change.Status.SUBMITTED) { if (chg.getStatus() != Change.Status.NEW) {
logDebug("Change {} is not submitted: {}", changeId, chg.getStatus()); logDebug("Change {} is not new: {}", changeId, chg.getStatus());
continue; continue;
} }
if (chg.currentPatchSetId() == null) { if (chg.currentPatchSetId() == null) {
@@ -762,7 +738,7 @@ public class MergeOp {
} }
commit.add(canMergeFlag); commit.add(canMergeFlag);
toSubmit.put(submitType, chg); toSubmit.put(submitType, cd);
} }
logDebug("Submitting on this run: {}", toSubmit); logDebug("Submitting on this run: {}", toSubmit);
return toSubmit; return toSubmit;
@@ -881,9 +857,10 @@ public class MergeOp {
return ""; return "";
} }
private void updateChangeStatus(List<Change> submitted, private void updateChangeStatus(List<ChangeData> submitted,
Branch.NameKey destBranch, boolean dryRun) Branch.NameKey destBranch, boolean dryRun, IdentifiedUser caller)
throws NoSuchChangeException, MergeException, ResourceConflictException { throws NoSuchChangeException, MergeException, ResourceConflictException,
OrmException {
if (!dryRun) { if (!dryRun) {
logDebug("Updating change status for {} changes", submitted.size()); logDebug("Updating change status for {} changes", submitted.size());
} else { } else {
@@ -891,7 +868,8 @@ public class MergeOp {
submitted.size()); submitted.size());
} }
MergeTip mergeTip = mergeTips.get(destBranch); MergeTip mergeTip = mergeTips.get(destBranch);
for (Change c : submitted) { for (ChangeData cd : submitted) {
Change c = cd.change();
CodeReviewCommit commit = commits.get(c.getId()); CodeReviewCommit commit = commits.get(c.getId());
CommitMergeStatus s = commit != null ? commit.getStatusCode() : null; CommitMergeStatus s = commit != null ? commit.getStatusCode() : null;
if (s == null) { if (s == null) {
@@ -903,6 +881,14 @@ public class MergeOp {
continue; continue;
} }
if (!dryRun) {
try {
setApproval(cd, caller);
} catch (IOException e) {
throw new OrmException(e);
}
}
String txt = s.getMessage(); String txt = s.getMessage();
logDebug("Status of change {} ({}) on {}: {}", c.getId(), commit.name(), logDebug("Status of change {} ({}) on {}: {}", c.getId(), commit.name(),
c.getDest(), s); c.getDest(), s);
@@ -1093,6 +1079,127 @@ public class MergeOp {
}); });
} }
private void setApproval(ChangeData cd, IdentifiedUser user)
throws OrmException, IOException {
Timestamp timestamp = TimeUtil.nowTs();
ChangeControl control = cd.changeControl();
PatchSet.Id psId = cd.currentPatchSet().getId();
PatchSet.Id psIdNewRev = commits.get(cd.change().getId())
.change().currentPatchSetId();
logDebug("Add approval for " + cd + " from user " + user);
ChangeUpdate update = updateFactory.create(control, timestamp);
List<SubmitRecord> record = records.get(cd.change().getId());
if (record != null) {
update.merge(record);
}
db.changes().beginTransaction(cd.change().getId());
try {
BatchMetaDataUpdate batch = approve(control, psId, user,
update, timestamp);
batch.write(update, new CommitBuilder());
// If the submit strategy created a new revision (rebase, cherry-pick)
// approve that as well
if (!psIdNewRev.equals(psId)) {
batch = approve(control, psIdNewRev, user,
update, timestamp);
// Write update commit after all normalized label commits.
batch.write(update, new CommitBuilder());
}
db.commit();
} finally {
db.rollback();
}
indexer.index(db, cd.change());
}
private BatchMetaDataUpdate approve(ChangeControl control, PatchSet.Id psId,
IdentifiedUser user, ChangeUpdate update, Timestamp timestamp)
throws OrmException {
Map<PatchSetApproval.Key, PatchSetApproval> byKey = Maps.newHashMap();
for (PatchSetApproval psa :
approvalsUtil.byPatchSet(db, control, psId)) {
if (!byKey.containsKey(psa.getKey())) {
byKey.put(psa.getKey(), psa);
}
}
PatchSetApproval submit = new PatchSetApproval(
new PatchSetApproval.Key(
psId,
user.getAccountId(),
LabelId.SUBMIT),
(short) 1, TimeUtil.nowTs());
byKey.put(submit.getKey(), submit);
submit.setValue((short) 1);
submit.setGranted(timestamp);
// Flatten out existing approvals for this patch set based upon the current
// permissions. Once the change is closed the approvals are not updated at
// presentation view time, except for zero votes used to indicate a reviewer
// was added. So we need to make sure votes are accurate now. This way if
// permissions get modified in the future, historical records stay accurate.
LabelNormalizer.Result normalized =
labelNormalizer.normalize(control, byKey.values());
// TODO(dborowitz): Don't use a label in notedb; just check when status
// change happened.
update.putApproval(submit.getLabel(), submit.getValue());
logDebug("Adding submit label " + submit);
db.patchSetApprovals().upsert(normalized.getNormalized());
db.patchSetApprovals().delete(normalized.deleted());
try {
return saveToBatch(control, update, normalized, timestamp);
} catch (IOException e) {
throw new OrmException(e);
}
}
private BatchMetaDataUpdate saveToBatch(ChangeControl ctl,
ChangeUpdate callerUpdate, LabelNormalizer.Result normalized,
Timestamp timestamp) throws IOException {
Table<Account.Id, String, Optional<Short>> byUser = HashBasedTable.create();
for (PatchSetApproval psa : normalized.updated()) {
byUser.put(psa.getAccountId(), psa.getLabel(),
Optional.of(psa.getValue()));
}
for (PatchSetApproval psa : normalized.deleted()) {
byUser.put(psa.getAccountId(), psa.getLabel(), Optional.<Short> absent());
}
BatchMetaDataUpdate batch = callerUpdate.openUpdate();
for (Account.Id accountId : byUser.rowKeySet()) {
if (!accountId.equals(callerUpdate.getUser().getAccountId())) {
ChangeUpdate update = updateFactory.create(
ctl.forUser(identifiedUserFactory.create(accountId)), timestamp);
update.setSubject("Finalize approvals at submit");
putApprovals(update, byUser.row(accountId));
CommitBuilder commit = new CommitBuilder();
commit.setCommitter(new PersonIdent(serverIdent, timestamp));
batch.write(update, commit);
}
}
putApprovals(callerUpdate,
byUser.row(callerUpdate.getUser().getAccountId()));
return batch;
}
private static void putApprovals(ChangeUpdate update,
Map<String, Optional<Short>> approvals) {
for (Map.Entry<String, Optional<Short>> e : approvals.entrySet()) {
if (e.getValue().isPresent()) {
update.putApproval(e.getKey(), e.getValue().get());
} else {
update.removeApproval(e.getKey());
}
}
}
private void sendMergedEmail(final Change c, final PatchSetApproval from) { private void sendMergedEmail(final Change c, final PatchSetApproval from) {
workQueue.getDefaultQueue() workQueue.getDefaultQueue()
.submit(new Runnable() { .submit(new Runnable() {

View File

@@ -71,16 +71,13 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TimeZone;
public class MergeUtil { public class MergeUtil {
private static final Logger log = LoggerFactory.getLogger(MergeUtil.class); private static final Logger log = LoggerFactory.getLogger(MergeUtil.class);
@@ -167,10 +164,6 @@ public class MergeUtil {
return result; return result;
} }
public PatchSetApproval getSubmitter(CodeReviewCommit c) {
return approvalsUtil.getSubmitter(db.get(), c.notes(), c.getPatchsetId());
}
public RevCommit createCherryPickFromCommit(Repository repo, public RevCommit createCherryPickFromCommit(Repository repo,
ObjectInserter inserter, RevCommit mergeTip, RevCommit originalCommit, ObjectInserter inserter, RevCommit mergeTip, RevCommit originalCommit,
PersonIdent cherryPickCommitterIdent, String commitMsg, RevWalk rw) PersonIdent cherryPickCommitterIdent, String commitMsg, RevWalk rw)
@@ -347,52 +340,6 @@ public class MergeUtil {
return false; return false;
} }
public PersonIdent computeMergeCommitAuthor(final PersonIdent myIdent,
final RevWalk rw, final List<CodeReviewCommit> codeReviewCommits) {
PatchSetApproval submitter = null;
for (final CodeReviewCommit c : codeReviewCommits) {
PatchSetApproval s = getSubmitter(c);
if (submitter == null
|| (s != null && s.getGranted().compareTo(submitter.getGranted()) > 0)) {
submitter = s;
}
}
// Try to use the submitter's identity for the merge commit author.
// If all of the commits being merged are created by the submitter,
// prefer the identity line they used in the commits rather than the
// preferred identity stored in the user account. This way the Git
// commit records are more consistent internally.
//
PersonIdent authorIdent;
if (submitter != null) {
IdentifiedUser who =
identifiedUserFactory.create(submitter.getAccountId());
Set<String> emails = new HashSet<>();
for (RevCommit c : codeReviewCommits) {
try {
rw.parseBody(c);
} catch (IOException e) {
log.warn("Cannot parse commit " + c.name(), e);
continue;
}
emails.add(c.getAuthorIdent().getEmailAddress());
}
final Timestamp dt = submitter.getGranted();
final TimeZone tz = myIdent.getTimeZone();
if (emails.size() == 1 && who.hasEmailAddress(emails.iterator().next())) {
authorIdent =
new PersonIdent(codeReviewCommits.get(0).getAuthorIdent(), dt, tz);
} else {
authorIdent = who.newCommitterIdent(dt, tz);
}
} else {
authorIdent = myIdent;
}
return authorIdent;
}
public boolean canMerge(final MergeSorter mergeSorter, public boolean canMerge(final MergeSorter mergeSorter,
final Repository repo, final CodeReviewCommit mergeTip, final Repository repo, final CodeReviewCommit mergeTip,
final CodeReviewCommit toMerge) final CodeReviewCommit toMerge)
@@ -497,16 +444,15 @@ public class MergeUtil {
}; };
} }
public CodeReviewCommit mergeOneCommit(final PersonIdent myIdent, public CodeReviewCommit mergeOneCommit(PersonIdent author,
final Repository repo, final RevWalk rw, final ObjectInserter inserter, PersonIdent committer, Repository repo, RevWalk rw,
final RevFlag canMergeFlag, final Branch.NameKey destBranch, ObjectInserter inserter, RevFlag canMergeFlag, Branch.NameKey destBranch,
final CodeReviewCommit mergeTip, final CodeReviewCommit n) CodeReviewCommit mergeTip, CodeReviewCommit n) throws MergeException {
throws MergeException {
final ThreeWayMerger m = newThreeWayMerger(repo, inserter); final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
try { try {
if (m.merge(new AnyObjectId[] {mergeTip, n})) { if (m.merge(new AnyObjectId[] {mergeTip, n})) {
return writeMergeCommit(myIdent, rw, inserter, canMergeFlag, destBranch, return writeMergeCommit(author, committer, rw, inserter, canMergeFlag,
mergeTip, m.getResultTreeId(), n); destBranch, mergeTip, m.getResultTreeId(), n);
} else { } else {
failed(rw, canMergeFlag, mergeTip, n, CommitMergeStatus.PATH_CONFLICT); failed(rw, canMergeFlag, mergeTip, n, CommitMergeStatus.PATH_CONFLICT);
} }
@@ -549,12 +495,12 @@ public class MergeUtil {
return failed; return failed;
} }
public CodeReviewCommit writeMergeCommit(final PersonIdent myIdent, public CodeReviewCommit writeMergeCommit(PersonIdent author,
final RevWalk rw, final ObjectInserter inserter, PersonIdent committer, RevWalk rw, ObjectInserter inserter,
final RevFlag canMergeFlag, final Branch.NameKey destBranch, RevFlag canMergeFlag, Branch.NameKey destBranch,
final CodeReviewCommit mergeTip, final ObjectId treeId, CodeReviewCommit mergeTip, ObjectId treeId, CodeReviewCommit n)
final CodeReviewCommit n) throws IOException, throws IOException, MissingObjectException,
MissingObjectException, IncorrectObjectTypeException { IncorrectObjectTypeException {
final List<CodeReviewCommit> merged = new ArrayList<>(); final List<CodeReviewCommit> merged = new ArrayList<>();
rw.resetRetain(canMergeFlag); rw.resetRetain(canMergeFlag);
rw.markStart(n); rw.markStart(n);
@@ -582,13 +528,11 @@ public class MergeUtil {
} }
} }
PersonIdent authorIdent = computeMergeCommitAuthor(myIdent, rw, merged);
final CommitBuilder mergeCommit = new CommitBuilder(); final CommitBuilder mergeCommit = new CommitBuilder();
mergeCommit.setTreeId(treeId); mergeCommit.setTreeId(treeId);
mergeCommit.setParentIds(mergeTip, n); mergeCommit.setParentIds(mergeTip, n);
mergeCommit.setAuthor(authorIdent); mergeCommit.setAuthor(author);
mergeCommit.setCommitter(myIdent); mergeCommit.setCommitter(committer);
mergeCommit.setMessage(msgbuf.toString()); mergeCommit.setMessage(msgbuf.toString());
CodeReviewCommit mergeResult = CodeReviewCommit mergeResult =
@@ -691,7 +635,7 @@ public class MergeUtil {
return id; return id;
} }
public PatchSetApproval markCleanMerges(final RevWalk rw, public void markCleanMerges(final RevWalk rw,
final RevFlag canMergeFlag, final CodeReviewCommit mergeTip, final RevFlag canMergeFlag, final CodeReviewCommit mergeTip,
final Set<RevCommit> alreadyAccepted) throws MergeException { final Set<RevCommit> alreadyAccepted) throws MergeException {
if (mergeTip == null) { if (mergeTip == null) {
@@ -699,12 +643,10 @@ public class MergeUtil {
// at the start of the merge process. We also elected to merge nothing, // at the start of the merge process. We also elected to merge nothing,
// probably due to missing dependencies. Nothing was cleanly merged. // probably due to missing dependencies. Nothing was cleanly merged.
// //
return null; return;
} }
try { try {
PatchSetApproval submitApproval = null;
rw.resetRetain(canMergeFlag); rw.resetRetain(canMergeFlag);
rw.sort(RevSort.TOPO); rw.sort(RevSort.TOPO);
rw.sort(RevSort.REVERSE, true); rw.sort(RevSort.REVERSE, true);
@@ -717,13 +659,8 @@ public class MergeUtil {
while ((c = (CodeReviewCommit) rw.next()) != null) { while ((c = (CodeReviewCommit) rw.next()) != null) {
if (c.getPatchsetId() != null) { if (c.getPatchsetId() != null) {
c.setStatusCode(CommitMergeStatus.CLEAN_MERGE); c.setStatusCode(CommitMergeStatus.CLEAN_MERGE);
if (submitApproval == null) {
submitApproval = getSubmitter(c);
}
} }
} }
return submitApproval;
} catch (IOException e) { } catch (IOException e) {
throw new MergeException("Cannot mark clean merges", e); throw new MergeException("Cannot mark clean merges", e);
} }

View File

@@ -1782,16 +1782,10 @@ public class ReceiveCommits {
} }
private void submit(ChangeControl changeCtl, PatchSet ps) private void submit(ChangeControl changeCtl, PatchSet ps)
throws OrmException, IOException, ResourceConflictException { throws OrmException, ResourceConflictException {
Submit submit = submitProvider.get(); Submit submit = submitProvider.get();
RevisionResource rsrc = new RevisionResource(changes.parse(changeCtl), ps); RevisionResource rsrc = new RevisionResource(changes.parse(changeCtl), ps);
List<Change> changes; List<Change> changes = Lists.newArrayList(rsrc.getChange());
try {
// Force submit even if submit rule evaluation fails.
changes = submit.submit(rsrc, currentUser, true);
} catch (ResourceConflictException e) {
throw new IOException(e);
}
try { try {
mergeOpProvider.get().merge(ChangeSet.create(changes), mergeOpProvider.get().merge(ChangeSet.create(changes),
(IdentifiedUser) changeCtl.getCurrentUser(), false); (IdentifiedUser) changeCtl.getCurrentUser(), false);
@@ -1802,9 +1796,6 @@ public class ReceiveCommits {
for (Change c : changes) { for (Change c : changes) {
c = db.changes().get(c.getId()); c = db.changes().get(c.getId());
switch (c.getStatus()) { switch (c.getStatus()) {
case SUBMITTED:
addMessage("Change " + c.getChangeId() + " submitted.");
break;
case MERGED: case MERGED:
addMessage("Change " + c.getChangeId() + " merged."); addMessage("Change " + c.getChangeId() + " merged.");
break; break;

View File

@@ -23,7 +23,6 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus; import com.google.gerrit.server.git.CommitMergeStatus;
@@ -132,14 +131,15 @@ public class CherryPick extends SubmitStrategy {
if (args.rw.isMergedInto(mergeTip.getCurrentTip(), n)) { if (args.rw.isMergedInto(mergeTip.getCurrentTip(), n)) {
mergeTip.moveTipTo(n, n); mergeTip.moveTipTo(n, n);
} else { } else {
CodeReviewCommit result = args.mergeUtil.mergeOneCommit( PersonIdent myIdent = args.serverIdent.get();
args.serverIdent.get(), args.repo, args.rw, args.inserter, CodeReviewCommit result = args.mergeUtil.mergeOneCommit(myIdent,
myIdent, args.repo, args.rw, args.inserter,
args.canMergeFlag, args.destBranch, mergeTip.getCurrentTip(), n); args.canMergeFlag, args.destBranch, mergeTip.getCurrentTip(), n);
mergeTip.moveTipTo(result, n); mergeTip.moveTipTo(result, n);
} }
PatchSetApproval submitApproval = args.mergeUtil.markCleanMerges(args.rw, args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag,
args.canMergeFlag, mergeTip.getCurrentTip(), args.alreadyAccepted); mergeTip.getCurrentTip(), args.alreadyAccepted);
setRefLogIdent(submitApproval); setRefLogIdent();
} else { } else {
// One or more dependencies were not met. The status was already marked on // One or more dependencies were not met. The status was already marked on
// the commit so we have nothing further to perform at this time. // the commit so we have nothing further to perform at this time.
@@ -153,33 +153,19 @@ public class CherryPick extends SubmitStrategy {
args.rw.parseBody(n); args.rw.parseBody(n);
PatchSetApproval submitAudit = args.mergeUtil.getSubmitter(n);
IdentifiedUser cherryPickUser;
PersonIdent serverNow = args.serverIdent.get();
PersonIdent cherryPickCommitterIdent;
if (submitAudit != null) {
cherryPickUser =
args.identifiedUserFactory.create(submitAudit.getAccountId());
cherryPickCommitterIdent = cherryPickUser.newCommitterIdent(
serverNow.getWhen(), serverNow.getTimeZone());
} else {
cherryPickUser = args.identifiedUserFactory.create(n.change().getOwner());
cherryPickCommitterIdent = serverNow;
}
String cherryPickCmtMsg = args.mergeUtil.createCherryPickCommitMessage(n); String cherryPickCmtMsg = args.mergeUtil.createCherryPickCommitMessage(n);
PersonIdent committer = args.caller.newCommitterIdent(
TimeUtil.nowTs(), args.serverIdent.get().getTimeZone());
CodeReviewCommit newCommit = CodeReviewCommit newCommit =
(CodeReviewCommit) args.mergeUtil.createCherryPickFromCommit(args.repo, (CodeReviewCommit) args.mergeUtil.createCherryPickFromCommit(args.repo,
args.inserter, mergeTip, n, cherryPickCommitterIdent, args.inserter, mergeTip, n, committer, cherryPickCmtMsg, args.rw);
cherryPickCmtMsg, args.rw);
PatchSet.Id id = PatchSet.Id id =
ChangeUtil.nextPatchSetId(args.repo, n.change().currentPatchSetId()); ChangeUtil.nextPatchSetId(args.repo, n.change().currentPatchSetId());
PatchSet ps = new PatchSet(id); PatchSet ps = new PatchSet(id);
ps.setCreatedOn(TimeUtil.nowTs()); ps.setCreatedOn(TimeUtil.nowTs());
ps.setUploader(cherryPickUser.getAccountId()); ps.setUploader(args.caller.getAccountId());
ps.setRevision(new RevId(newCommit.getId().getName())); ps.setRevision(new RevId(newCommit.getId().getName()));
RefUpdate ru; RefUpdate ru;
@@ -220,9 +206,9 @@ public class CherryPick extends SubmitStrategy {
newCommit.copyFrom(n); newCommit.copyFrom(n);
newCommit.setStatusCode(CommitMergeStatus.CLEAN_PICK); newCommit.setStatusCode(CommitMergeStatus.CLEAN_PICK);
newCommit.setControl( newCommit.setControl(
args.changeControlFactory.controlFor(n.change(), cherryPickUser)); args.changeControlFactory.controlFor(n.change(), args.caller));
newCommits.put(newCommit.getPatchsetId().getParentKey(), newCommit); newCommits.put(newCommit.getPatchsetId().getParentKey(), newCommit);
setRefLogIdent(submitAudit); setRefLogIdent();
return newCommit; return newCommit;
} }

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.git.strategy; package com.google.gerrit.server.git.strategy;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus; import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.git.MergeException; import com.google.gerrit.server.git.MergeException;
@@ -43,10 +42,9 @@ public class FastForwardOnly extends SubmitStrategy {
n.setStatusCode(CommitMergeStatus.NOT_FAST_FORWARD); n.setStatusCode(CommitMergeStatus.NOT_FAST_FORWARD);
} }
PatchSetApproval submitApproval = args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag,
args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag, newMergeTipCommit, newMergeTipCommit, args.alreadyAccepted);
args.alreadyAccepted); setRefLogIdent();
setRefLogIdent(submitApproval);
return mergeTip; return mergeTip;
} }

View File

@@ -14,11 +14,12 @@
package com.google.gerrit.server.git.strategy; package com.google.gerrit.server.git.strategy;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.MergeException; import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.git.MergeTip; import com.google.gerrit.server.git.MergeTip;
import org.eclipse.jgit.lib.PersonIdent;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -42,17 +43,19 @@ public class MergeAlways extends SubmitStrategy {
} }
while (!sorted.isEmpty()) { while (!sorted.isEmpty()) {
CodeReviewCommit mergedFrom = sorted.remove(0); CodeReviewCommit mergedFrom = sorted.remove(0);
PersonIdent serverIdent = args.serverIdent.get();
PersonIdent caller = args.caller.newCommitterIdent(
serverIdent.getWhen(), serverIdent.getTimeZone());
CodeReviewCommit newTip = CodeReviewCommit newTip =
args.mergeUtil.mergeOneCommit(args.serverIdent.get(), args.repo, args.rw, args.mergeUtil.mergeOneCommit(caller, serverIdent,
args.inserter, args.canMergeFlag, args.destBranch, mergeTip.getCurrentTip(), args.repo, args.rw, args.inserter, args.canMergeFlag,
mergedFrom); args.destBranch, mergeTip.getCurrentTip(), mergedFrom);
mergeTip.moveTipTo(newTip, mergedFrom); mergeTip.moveTipTo(newTip, mergedFrom);
} }
final PatchSetApproval submitApproval = args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag,
args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag, mergeTip.getCurrentTip(), mergeTip.getCurrentTip(), args.alreadyAccepted);
args.alreadyAccepted); setRefLogIdent();
setRefLogIdent(submitApproval);
return mergeTip; return mergeTip;
} }

View File

@@ -14,11 +14,12 @@
package com.google.gerrit.server.git.strategy; package com.google.gerrit.server.git.strategy;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.MergeException; import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.git.MergeTip; import com.google.gerrit.server.git.MergeTip;
import org.eclipse.jgit.lib.PersonIdent;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -48,18 +49,19 @@ public class MergeIfNecessary extends SubmitStrategy {
// For every other commit do a pair-wise merge. // For every other commit do a pair-wise merge.
while (!sorted.isEmpty()) { while (!sorted.isEmpty()) {
CodeReviewCommit mergedFrom = sorted.remove(0); CodeReviewCommit mergedFrom = sorted.remove(0);
PersonIdent serverIdent = args.serverIdent.get();
PersonIdent caller = args.caller.newCommitterIdent(
serverIdent.getWhen(), serverIdent.getTimeZone());
branchTip = branchTip =
args.mergeUtil.mergeOneCommit(args.serverIdent.get(), args.repo, args.mergeUtil.mergeOneCommit(caller, serverIdent,
args.rw, args.inserter, args.canMergeFlag, args.destBranch, args.repo, args.rw, args.inserter, args.canMergeFlag,
branchTip, mergedFrom); args.destBranch, branchTip, mergedFrom);
mergeTip.moveTipTo(branchTip, mergedFrom); mergeTip.moveTipTo(branchTip, mergedFrom);
} }
final PatchSetApproval submitApproval = args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag, branchTip,
args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag, branchTip, args.alreadyAccepted);
args.alreadyAccepted); setRefLogIdent();
setRefLogIdent(submitApproval);
return mergeTip; return mergeTip;
} }

View File

@@ -18,7 +18,6 @@ import com.google.common.collect.Lists;
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.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy; import com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy;
import com.google.gerrit.server.change.RebaseChange; import com.google.gerrit.server.change.RebaseChange;
import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit;
@@ -33,6 +32,7 @@ import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@@ -84,15 +84,11 @@ public class RebaseIfNecessary extends SubmitStrategy {
} else { } else {
try { try {
IdentifiedUser uploader =
args.identifiedUserFactory.create(args.mergeUtil
.getSubmitter(n).getAccountId());
PatchSet newPatchSet = PatchSet newPatchSet =
rebaseChange.rebase(args.repo, args.rw, args.inserter, rebaseChange.rebase(args.repo, args.rw, args.inserter,
n.change(), n.getPatchsetId(), uploader, n.change(), n.getPatchsetId(), args.caller,
mergeTip.getCurrentTip(), args.mergeUtil, mergeTip.getCurrentTip(), args.mergeUtil,
args.serverIdent.get(), false, ValidatePolicy.NONE); args.serverIdent.get(), false, ValidatePolicy.NONE);
List<PatchSetApproval> approvals = Lists.newArrayList(); List<PatchSetApproval> approvals = Lists.newArrayList();
for (PatchSetApproval a : args.approvalsUtil.byPatchSet(args.db, for (PatchSetApproval a : args.approvalsUtil.byPatchSet(args.db,
n.getControl(), n.getPatchsetId())) { n.getControl(), n.getPatchsetId())) {
@@ -109,13 +105,13 @@ public class RebaseIfNecessary extends SubmitStrategy {
newPatchSet.getId())); newPatchSet.getId()));
mergeTip.getCurrentTip().copyFrom(n); mergeTip.getCurrentTip().copyFrom(n);
mergeTip.getCurrentTip().setControl( mergeTip.getCurrentTip().setControl(
args.changeControlFactory.controlFor(n.change(), uploader)); args.changeControlFactory.controlFor(n.change(), args.caller));
mergeTip.getCurrentTip().setPatchsetId(newPatchSet.getId()); mergeTip.getCurrentTip().setPatchsetId(newPatchSet.getId());
mergeTip.getCurrentTip().setStatusCode( mergeTip.getCurrentTip().setStatusCode(
CommitMergeStatus.CLEAN_REBASE); CommitMergeStatus.CLEAN_REBASE);
newCommits.put(newPatchSet.getId().getParentKey(), newCommits.put(newPatchSet.getId().getParentKey(),
mergeTip.getCurrentTip()); mergeTip.getCurrentTip());
setRefLogIdent(args.mergeUtil.getSubmitter(n)); setRefLogIdent();
} catch (MergeConflictException e) { } catch (MergeConflictException e) {
n.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT); n.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT);
} catch (NoSuchChangeException | OrmException | IOException } catch (NoSuchChangeException | OrmException | IOException
@@ -135,15 +131,15 @@ public class RebaseIfNecessary extends SubmitStrategy {
if (args.rw.isMergedInto(mergeTip.getCurrentTip(), n)) { if (args.rw.isMergedInto(mergeTip.getCurrentTip(), n)) {
mergeTip.moveTipTo(n, n); mergeTip.moveTipTo(n, n);
} else { } else {
PersonIdent myIdent = args.serverIdent.get();
mergeTip.moveTipTo( mergeTip.moveTipTo(
args.mergeUtil.mergeOneCommit(args.serverIdent.get(), args.mergeUtil.mergeOneCommit(myIdent, myIdent,
args.repo, args.rw, args.inserter, args.canMergeFlag, args.repo, args.rw, args.inserter, args.canMergeFlag,
args.destBranch, mergeTip.getCurrentTip(), n), n); args.destBranch, mergeTip.getCurrentTip(), n), n);
} }
PatchSetApproval submitApproval = args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag,
args.mergeUtil.markCleanMerges(args.rw, args.canMergeFlag, mergeTip.getCurrentTip(), args.alreadyAccepted);
mergeTip.getCurrentTip(), args.alreadyAccepted); setRefLogIdent();
setRefLogIdent(submitApproval);
} catch (IOException e) { } catch (IOException e) {
throw new MergeException("Cannot merge " + n.name(), e); throw new MergeException("Cannot merge " + n.name(), e);
} }

View File

@@ -14,10 +14,11 @@
package com.google.gerrit.server.git.strategy; package com.google.gerrit.server.git.strategy;
import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.client.SubmitType;
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.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
@@ -66,6 +67,7 @@ public abstract class SubmitStrategy {
protected final MergeUtil mergeUtil; protected final MergeUtil mergeUtil;
protected final ChangeIndexer indexer; protected final ChangeIndexer indexer;
protected final MergeSorter mergeSorter; protected final MergeSorter mergeSorter;
protected final IdentifiedUser caller;
Arguments(IdentifiedUser.GenericFactory identifiedUserFactory, Arguments(IdentifiedUser.GenericFactory identifiedUserFactory,
Provider<PersonIdent> serverIdent, ReviewDb db, Provider<PersonIdent> serverIdent, ReviewDb db,
@@ -73,7 +75,7 @@ public abstract class SubmitStrategy {
RevWalk rw, ObjectInserter inserter, RevFlag canMergeFlag, RevWalk rw, ObjectInserter inserter, RevFlag canMergeFlag,
Set<RevCommit> alreadyAccepted, Branch.NameKey destBranch, Set<RevCommit> alreadyAccepted, Branch.NameKey destBranch,
ApprovalsUtil approvalsUtil, MergeUtil mergeUtil, ApprovalsUtil approvalsUtil, MergeUtil mergeUtil,
ChangeIndexer indexer) { ChangeIndexer indexer, IdentifiedUser caller) {
this.identifiedUserFactory = identifiedUserFactory; this.identifiedUserFactory = identifiedUserFactory;
this.serverIdent = serverIdent; this.serverIdent = serverIdent;
this.db = db; this.db = db;
@@ -89,6 +91,7 @@ public abstract class SubmitStrategy {
this.mergeUtil = mergeUtil; this.mergeUtil = mergeUtil;
this.indexer = indexer; this.indexer = indexer;
this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag); this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag);
this.caller = caller;
} }
} }
@@ -115,6 +118,7 @@ public abstract class SubmitStrategy {
public final MergeTip run(final CodeReviewCommit currentTip, public final MergeTip run(final CodeReviewCommit currentTip,
final Collection<CodeReviewCommit> toMerge) throws MergeException { final Collection<CodeReviewCommit> toMerge) throws MergeException {
refLogIdent = null; refLogIdent = null;
checkState(args.caller != null);
return _run(currentTip, toMerge); return _run(currentTip, toMerge);
} }
@@ -182,13 +186,11 @@ public abstract class SubmitStrategy {
/** /**
* Set the ref log identity if it wasn't set yet. * Set the ref log identity if it wasn't set yet.
*
* @param submitApproval the approval that submitted the patch set
*/ */
protected final void setRefLogIdent(PatchSetApproval submitApproval) { protected final void setRefLogIdent() {
if (refLogIdent == null && submitApproval != null) { if (refLogIdent == null) {
refLogIdent = args.identifiedUserFactory.create( refLogIdent = args.identifiedUserFactory.create(
submitApproval.getAccountId()) .newRefLogIdent(); args.caller.getAccountId()).newRefLogIdent();
} }
} }
} }

View File

@@ -88,14 +88,14 @@ public class SubmitStrategyFactory {
public SubmitStrategy create(final SubmitType submitType, final ReviewDb db, public SubmitStrategy create(final SubmitType submitType, final ReviewDb db,
final Repository repo, final RevWalk rw, final ObjectInserter inserter, final Repository repo, final RevWalk rw, final ObjectInserter inserter,
final RevFlag canMergeFlag, final Set<RevCommit> alreadyAccepted, final RevFlag canMergeFlag, final Set<RevCommit> alreadyAccepted,
final Branch.NameKey destBranch) final Branch.NameKey destBranch, final IdentifiedUser caller)
throws MergeException, NoSuchProjectException { throws MergeException, NoSuchProjectException {
ProjectState project = getProject(destBranch); ProjectState project = getProject(destBranch);
final SubmitStrategy.Arguments args = final SubmitStrategy.Arguments args =
new SubmitStrategy.Arguments(identifiedUserFactory, myIdent, db, new SubmitStrategy.Arguments(identifiedUserFactory, myIdent, db,
changeControlFactory, repo, rw, inserter, canMergeFlag, changeControlFactory, repo, rw, inserter, canMergeFlag,
alreadyAccepted, destBranch,approvalsUtil, alreadyAccepted, destBranch,approvalsUtil,
mergeUtilFactory.create(project), indexer); mergeUtilFactory.create(project), indexer, caller);
switch (submitType) { switch (submitType) {
case CHERRY_PICK: case CHERRY_PICK:
return new CherryPick(args, patchSetInfoFactory, gitRefUpdated); return new CherryPick(args, patchSetInfoFactory, gitRefUpdated);

View File

@@ -164,7 +164,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
} }
public void setStatus(Change.Status status) { public void setStatus(Change.Status status) {
checkArgument(status != Change.Status.SUBMITTED, checkArgument(status != Change.Status.MERGED,
"use submit(Iterable<PatchSetApproval>)"); "use submit(Iterable<PatchSetApproval>)");
this.status = status; this.status = status;
} }
@@ -177,8 +177,8 @@ public class ChangeUpdate extends AbstractChangeUpdate {
approvals.put(label, Optional.<Short> absent()); approvals.put(label, Optional.<Short> absent());
} }
public void submit(Iterable<SubmitRecord> submitRecords) { public void merge(Iterable<SubmitRecord> submitRecords) {
status = Change.Status.SUBMITTED; this.status = Change.Status.MERGED;
this.submitRecords = ImmutableList.copyOf(submitRecords); this.submitRecords = ImmutableList.copyOf(submitRecords);
checkArgument(!this.submitRecords.isEmpty(), checkArgument(!this.submitRecords.isEmpty(),
"no submit records specified at submit time"); "no submit records specified at submit time");

View File

@@ -117,7 +117,7 @@ class ConflictsPredicate extends OrPredicate<ChangeData> {
args.submitStrategyFactory.create(submitType, args.submitStrategyFactory.create(submitType,
db.get(), repo, rw, null, canMergeFlag, db.get(), repo, rw, null, canMergeFlag,
getAlreadyAccepted(repo, rw, commit), getAlreadyAccepted(repo, rw, commit),
otherChange.getDest()); otherChange.getDest(), null);
CodeReviewCommit otherCommit = CodeReviewCommit otherCommit =
(CodeReviewCommit) rw.parseCommit(other); (CodeReviewCommit) rw.parseCommit(other);
otherCommit.add(canMergeFlag); otherCommit.add(canMergeFlag);

View File

@@ -108,17 +108,6 @@ public class InternalChangeQuery {
return query(project(project)); return query(project(project));
} }
public List<ChangeData> submitted(Branch.NameKey branch) throws OrmException {
return query(and(
ref(branch),
project(branch.getParentKey()),
status(Change.Status.SUBMITTED)));
}
public List<ChangeData> allSubmitted() throws OrmException {
return query(status(Change.Status.SUBMITTED));
}
public List<ChangeData> byBranchOpen(Branch.NameKey branch) public List<ChangeData> byBranchOpen(Branch.NameKey branch)
throws OrmException { throws OrmException {
return query(and( return query(and(

View File

@@ -32,7 +32,7 @@ import java.util.List;
/** A version of the database schema. */ /** A version of the database schema. */
public abstract class SchemaVersion { public abstract class SchemaVersion {
/** The current schema version. */ /** The current schema version. */
public static final Class<Schema_108> C = Schema_108.class; public static final Class<Schema_109> C = Schema_109.class;
public static int getBinaryVersion() { public static int getBinaryVersion() {
return guessVersion(C); return guessVersion(C);

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.StatementExecutor;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class Schema_109 extends SchemaVersion {
@Inject
Schema_109(Provider<Schema_108> prior) {
super(prior);
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
String cmd = "UPDATE changes SET status = 'n' WHERE status = 's';";
ui.message("Running " + cmd);
try (StatementExecutor e = newExecutor(db)) {
e.execute(cmd);
}
ui.message("done");
}
}

View File

@@ -19,7 +19,6 @@ import static com.google.gerrit.reviewdb.client.Change.Status.ABANDONED;
import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT; import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT;
import static com.google.gerrit.reviewdb.client.Change.Status.MERGED; import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
import static com.google.gerrit.reviewdb.client.Change.Status.NEW; import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
import static com.google.gerrit.reviewdb.client.Change.Status.SUBMITTED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -183,17 +182,17 @@ public class IndexRewriteTest {
public void testGetPossibleStatus() throws Exception { public void testGetPossibleStatus() throws Exception {
assertEquals(EnumSet.allOf(Change.Status.class), status("file:a")); assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
assertEquals(EnumSet.of(NEW), status("is:new")); assertEquals(EnumSet.of(NEW), status("is:new"));
assertEquals(EnumSet.of(SUBMITTED, DRAFT, MERGED, ABANDONED), assertEquals(EnumSet.of(DRAFT, MERGED, ABANDONED),
status("-is:new")); status("-is:new"));
assertEquals(EnumSet.of(NEW, MERGED), status("is:new OR is:merged")); assertEquals(EnumSet.of(NEW, MERGED), status("is:new OR is:merged"));
EnumSet<Change.Status> none = EnumSet.noneOf(Change.Status.class); EnumSet<Change.Status> none = EnumSet.noneOf(Change.Status.class);
assertEquals(none, status("is:new is:merged")); assertEquals(none, status("is:new is:merged"));
assertEquals(none, status("(is:new is:draft) (is:merged is:submitted)")); assertEquals(none, status("(is:new is:draft) (is:merged)"));
assertEquals(none, status("(is:new is:draft) (is:merged is:submitted)")); assertEquals(none, status("(is:new is:draft) (is:merged)"));
assertEquals(EnumSet.of(MERGED, SUBMITTED), assertEquals(EnumSet.of(MERGED),
status("(is:new is:draft) OR (is:merged OR is:submitted)")); status("(is:new is:draft) OR (is:merged)"));
} }
@Test @Test

View File

@@ -287,7 +287,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, changeOwner); ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubject("Submit patch set 1"); update.setSubject("Submit patch set 1");
update.submit(ImmutableList.of( update.merge(ImmutableList.of(
submitRecord("NOT_READY", null, submitRecord("NOT_READY", null,
submitLabel("Verified", "OK", changeOwner.getAccountId()), submitLabel("Verified", "OK", changeOwner.getAccountId()),
submitLabel("Code-Review", "NEED", null)), submitLabel("Code-Review", "NEED", null)),
@@ -314,7 +314,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange(); Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner); ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubject("Submit patch set 1"); update.setSubject("Submit patch set 1");
update.submit(ImmutableList.of( update.merge(ImmutableList.of(
submitRecord("OK", null, submitRecord("OK", null,
submitLabel("Code-Review", "OK", otherUser.getAccountId())))); submitLabel("Code-Review", "OK", otherUser.getAccountId()))));
update.commit(); update.commit();
@@ -322,7 +322,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
incrementPatchSet(c); incrementPatchSet(c);
update = newUpdate(c, changeOwner); update = newUpdate(c, changeOwner);
update.setSubject("Submit patch set 2"); update.setSubject("Submit patch set 2");
update.submit(ImmutableList.of( update.merge(ImmutableList.of(
submitRecord("OK", null, submitRecord("OK", null,
submitLabel("Code-Review", "OK", changeOwner.getAccountId())))); submitLabel("Code-Review", "OK", changeOwner.getAccountId()))));
update.commit(); update.commit();

View File

@@ -108,7 +108,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, changeOwner); ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubject("Submit patch set 1"); update.setSubject("Submit patch set 1");
update.submit(ImmutableList.of( update.merge(ImmutableList.of(
submitRecord("NOT_READY", null, submitRecord("NOT_READY", null,
submitLabel("Verified", "OK", changeOwner.getAccountId()), submitLabel("Verified", "OK", changeOwner.getAccountId()),
submitLabel("Code-Review", "NEED", null)), submitLabel("Code-Review", "NEED", null)),
@@ -121,7 +121,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
assertBodyEquals("Submit patch set 1\n" assertBodyEquals("Submit patch set 1\n"
+ "\n" + "\n"
+ "Patch-set: 1\n" + "Patch-set: 1\n"
+ "Status: submitted\n" + "Status: merged\n"
+ "Submitted-with: NOT_READY\n" + "Submitted-with: NOT_READY\n"
+ "Submitted-with: OK: Verified: Change Owner <1@gerrit>\n" + "Submitted-with: OK: Verified: Change Owner <1@gerrit>\n"
+ "Submitted-with: NEED: Code-Review\n" + "Submitted-with: NEED: Code-Review\n"
@@ -173,14 +173,14 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, changeOwner); ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubject("Submit patch set 1"); update.setSubject("Submit patch set 1");
update.submit(ImmutableList.of( update.merge(ImmutableList.of(
submitRecord("RULE_ERROR", "Problem with patch set:\n1"))); submitRecord("RULE_ERROR", "Problem with patch set:\n1")));
update.commit(); update.commit();
assertBodyEquals("Submit patch set 1\n" assertBodyEquals("Submit patch set 1\n"
+ "\n" + "\n"
+ "Patch-set: 1\n" + "Patch-set: 1\n"
+ "Status: submitted\n" + "Status: merged\n"
+ "Submitted-with: RULE_ERROR Problem with patch set: 1\n", + "Submitted-with: RULE_ERROR Problem with patch set: 1\n",
update.getRevision()); update.getRevision());
} }