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:
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user