Merge changes from topic 'submit-whole-topic-mergeability-check'
* changes: AbstractSubmit: Check submit button is there before clicking it ChangeNotes: Enforce project passed to factory matches change AbstractDaemonTest: Extract methods for parsing resources AbstractSubmit: Use testing time TestTimeUtil: Also stub out JGit's SystemReader ChangeNotesParser: Don't always bump lastUpdatedOn
This commit is contained in:
@@ -68,7 +68,8 @@ public class GetRevisionActions implements ETagView<RevisionResource> {
|
||||
rsrc.getChangeResource().prepareETag(h, user);
|
||||
h.putBoolean(Submit.wholeTopicEnabled(config));
|
||||
ReviewDb db = dbProvider.get();
|
||||
ChangeSet cs = mergeSuperSet.completeChangeSet(db, rsrc.getChange());
|
||||
ChangeSet cs =
|
||||
mergeSuperSet.completeChangeSet(db, rsrc.getChange(), user);
|
||||
for (ChangeData cd : cs.changes()) {
|
||||
new ChangeResource(cd.changeControl()).prepareETag(h, user);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RevId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeMessagesUtil;
|
||||
@@ -47,7 +46,6 @@ import com.google.gerrit.server.git.MergeOp;
|
||||
import com.google.gerrit.server.git.MergeSuperSet;
|
||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.InternalChangeQuery;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
@@ -117,7 +115,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final ChangeData.Factory changeDataFactory;
|
||||
private final ChangeMessagesUtil cmUtil;
|
||||
private final ChangeControl.GenericFactory changeControlFactory;
|
||||
private final ChangeNotes.Factory changeNotesFactory;
|
||||
private final Provider<MergeOp> mergeOpProvider;
|
||||
private final MergeSuperSet mergeSuperSet;
|
||||
@@ -137,7 +134,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
GitRepositoryManager repoManager,
|
||||
ChangeData.Factory changeDataFactory,
|
||||
ChangeMessagesUtil cmUtil,
|
||||
ChangeControl.GenericFactory changeControlFactory,
|
||||
ChangeNotes.Factory changeNotesFactory,
|
||||
Provider<MergeOp> mergeOpProvider,
|
||||
MergeSuperSet mergeSuperSet,
|
||||
@@ -149,7 +145,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
this.repoManager = repoManager;
|
||||
this.changeDataFactory = changeDataFactory;
|
||||
this.cmUtil = cmUtil;
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
this.changeNotesFactory = changeNotesFactory;
|
||||
this.mergeOpProvider = mergeOpProvider;
|
||||
this.mergeSuperSet = mergeSuperSet;
|
||||
@@ -233,19 +228,16 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
|
||||
/**
|
||||
* @param cs set of changes to be submitted at once
|
||||
* @param project the name of the project
|
||||
* @param identifiedUser the user who is checking to submit
|
||||
* @return a reason why any of the changes is not submittable or null
|
||||
*/
|
||||
private String problemsForSubmittingChangeset(ChangeSet cs,
|
||||
Project.NameKey project, IdentifiedUser identifiedUser) {
|
||||
IdentifiedUser identifiedUser) {
|
||||
try {
|
||||
@SuppressWarnings("resource")
|
||||
ReviewDb db = dbProvider.get();
|
||||
for (PatchSet.Id psId : cs.patchIds()) {
|
||||
ChangeControl changeControl = changeControlFactory
|
||||
.controlFor(db, project, psId.getParentKey(), identifiedUser);
|
||||
ChangeData c = changeDataFactory.create(db, changeControl);
|
||||
for (ChangeData c : cs.changes()) {
|
||||
ChangeControl changeControl = c.changeControl(identifiedUser);
|
||||
|
||||
if (!changeControl.isVisible(db)) {
|
||||
return BLOCKED_HIDDEN_SUBMIT_TOOLTIP;
|
||||
@@ -270,7 +262,7 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
}
|
||||
} catch (ResourceConflictException e) {
|
||||
return BLOCKED_SUBMIT_TOOLTIP;
|
||||
} catch (NoSuchChangeException | OrmException e) {
|
||||
} catch (OrmException e) {
|
||||
log.error("Error checking if change is submittable", e);
|
||||
throw new OrmRuntimeException("Could not determine problems for the change", e);
|
||||
}
|
||||
@@ -329,7 +321,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
|
||||
ChangeSet cs;
|
||||
try {
|
||||
cs = mergeSuperSet.completeChangeSet(db, cd.change());
|
||||
cs = mergeSuperSet.completeChangeSet(
|
||||
db, cd.change(), resource.getControl().getUser());
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new OrmRuntimeException("Could not determine complete set of " +
|
||||
"changes to be submitted", e);
|
||||
@@ -343,8 +336,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
|
||||
&& !Strings.isNullOrEmpty(topic)
|
||||
&& topicSize > 1;
|
||||
|
||||
String submitProblems = problemsForSubmittingChangeset(cs,
|
||||
resource.getProject(), resource.getUser());
|
||||
String submitProblems =
|
||||
problemsForSubmittingChangeset(cs, resource.getUser());
|
||||
if (submitProblems != null) {
|
||||
return new UiAction.Description()
|
||||
.setLabel(treatWithTopic
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.change.WalkSorter.PatchSetData;
|
||||
import com.google.gerrit.server.git.ChangeSet;
|
||||
import com.google.gerrit.server.git.MergeSuperSet;
|
||||
@@ -74,7 +75,7 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
|
||||
Change c = resource.getChange();
|
||||
List<ChangeData> cds;
|
||||
if (c.getStatus().isOpen()) {
|
||||
cds = getForOpenChange(c);
|
||||
cds = getForOpenChange(c, resource.getControl().getUser());
|
||||
} else if (c.getStatus().asChangeStatus() == ChangeStatus.MERGED) {
|
||||
cds = getForMergedChange(c);
|
||||
} else {
|
||||
@@ -99,9 +100,9 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
|
||||
}
|
||||
}
|
||||
|
||||
private List<ChangeData> getForOpenChange(Change c)
|
||||
private List<ChangeData> getForOpenChange(Change c, CurrentUser user)
|
||||
throws OrmException, IOException {
|
||||
ChangeSet cs = mergeSuperSet.completeChangeSet(dbProvider.get(), c);
|
||||
ChangeSet cs = mergeSuperSet.completeChangeSet(dbProvider.get(), c, user);
|
||||
return cs.changes().asList();
|
||||
}
|
||||
|
||||
|
||||
@@ -547,7 +547,7 @@ public class MergeOp implements AutoCloseable {
|
||||
this.db = db;
|
||||
logDebug("Beginning integration of {}", change);
|
||||
try {
|
||||
ChangeSet cs = mergeSuperSet.completeChangeSet(db, change);
|
||||
ChangeSet cs = mergeSuperSet.completeChangeSet(db, change, caller);
|
||||
checkState(cs.ids().contains(change.getId()),
|
||||
"change %s missing from %s", change.getId(), cs);
|
||||
this.commits = new CommitStatus(cs);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.change.Submit;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.index.ChangeField;
|
||||
@@ -90,21 +91,22 @@ public class MergeSuperSet {
|
||||
this.repoManager = repoManager;
|
||||
}
|
||||
|
||||
public ChangeSet completeChangeSet(ReviewDb db, Change change)
|
||||
public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
|
||||
throws MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||
OrmException {
|
||||
ChangeData cd =
|
||||
changeDataFactory.create(db, change.getProject(), change.getId());
|
||||
cd.changeControl(user);
|
||||
if (Submit.wholeTopicEnabled(cfg)) {
|
||||
return completeChangeSetIncludingTopics(db, new ChangeSet(cd));
|
||||
return completeChangeSetIncludingTopics(db, new ChangeSet(cd), user);
|
||||
} else {
|
||||
return completeChangeSetWithoutTopic(db, new ChangeSet(cd));
|
||||
return completeChangeSetWithoutTopic(db, new ChangeSet(cd), user);
|
||||
}
|
||||
}
|
||||
|
||||
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes)
|
||||
throws MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||
OrmException {
|
||||
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes,
|
||||
CurrentUser user) throws MissingObjectException,
|
||||
IncorrectObjectTypeException, IOException, OrmException {
|
||||
List<ChangeData> ret = new ArrayList<>();
|
||||
|
||||
Multimap<Project.NameKey, Change.Id> pc = changes.changesByProject();
|
||||
@@ -113,6 +115,7 @@ public class MergeSuperSet {
|
||||
RevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
|
||||
for (Change.Id cId : pc.get(project)) {
|
||||
ChangeData cd = changeDataFactory.create(db, project, cId);
|
||||
cd.changeControl(user);
|
||||
|
||||
SubmitTypeRecord str = cd.submitTypeRecord();
|
||||
if (!str.isOk()) {
|
||||
@@ -169,11 +172,12 @@ public class MergeSuperSet {
|
||||
}
|
||||
|
||||
private ChangeSet completeChangeSetIncludingTopics(
|
||||
ReviewDb db, ChangeSet changes) throws MissingObjectException,
|
||||
IncorrectObjectTypeException, IOException, OrmException {
|
||||
ReviewDb db, ChangeSet changes, CurrentUser user)
|
||||
throws MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||
OrmException {
|
||||
Set<String> topicsTraversed = new HashSet<>();
|
||||
boolean done = false;
|
||||
ChangeSet newCs = completeChangeSetWithoutTopic(db, changes);
|
||||
ChangeSet newCs = completeChangeSetWithoutTopic(db, changes, user);
|
||||
while (!done) {
|
||||
List<ChangeData> chgs = new ArrayList<>();
|
||||
done = true;
|
||||
@@ -187,7 +191,7 @@ public class MergeSuperSet {
|
||||
}
|
||||
}
|
||||
changes = new ChangeSet(chgs);
|
||||
newCs = completeChangeSetWithoutTopic(db, changes);
|
||||
newCs = completeChangeSetWithoutTopic(db, changes, user);
|
||||
}
|
||||
return newCs;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.notedb;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
|
||||
@@ -174,6 +175,10 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
|
||||
public ChangeNotes create(ReviewDb db, Project.NameKey project,
|
||||
Change.Id changeId) throws OrmException {
|
||||
Change change = db.changes().get(changeId);
|
||||
checkArgument(change.getProject().equals(project),
|
||||
"passed project %s when creating ChangeNotes for %s, but actual"
|
||||
+ " project is %s",
|
||||
project, changeId, change.getProject());
|
||||
// TODO: Throw NoSuchChangeException when the change is not found in the
|
||||
// database
|
||||
return new ChangeNotes(repoManager, migration, allUsersProvider, project,
|
||||
|
||||
@@ -195,15 +195,15 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
Timestamp ts =
|
||||
new Timestamp(commit.getCommitterIdent().getWhen().getTime());
|
||||
|
||||
boolean updateTs = commit.getParentCount() == 0;
|
||||
createdOn = ts;
|
||||
if (lastUpdatedOn == null) {
|
||||
lastUpdatedOn = ts;
|
||||
}
|
||||
if (branch == null) {
|
||||
branch = parseBranch(commit);
|
||||
updateTs |= branch != null;
|
||||
}
|
||||
if (status == null) {
|
||||
status = parseStatus(commit);
|
||||
updateTs |= status != null;
|
||||
}
|
||||
|
||||
PatchSet.Id psId = parsePatchSetId(commit);
|
||||
@@ -221,6 +221,7 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
|
||||
if (changeId == null) {
|
||||
changeId = parseChangeId(commit);
|
||||
updateTs |= changeId != null;
|
||||
}
|
||||
|
||||
String currSubject = parseSubject(commit);
|
||||
@@ -229,21 +230,28 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
subject = currSubject;
|
||||
}
|
||||
originalSubject = currSubject;
|
||||
updateTs = true;
|
||||
}
|
||||
|
||||
parseChangeMessage(psId, accountId, commit, ts);
|
||||
updateTs |= parseChangeMessage(psId, accountId, commit, ts) != null;
|
||||
if (topic == null) {
|
||||
topic = parseTopic(commit);
|
||||
updateTs |= topic != null;
|
||||
}
|
||||
|
||||
Set<String> oldHashtags = hashtags;
|
||||
parseHashtags(commit);
|
||||
updateTs |= hashtags != oldHashtags;
|
||||
|
||||
if (submissionId == null) {
|
||||
submissionId = parseSubmissionId(commit);
|
||||
updateTs |= submissionId != null;
|
||||
}
|
||||
|
||||
ObjectId currRev = parseRevision(commit);
|
||||
if (currRev != null) {
|
||||
parsePatchSet(psId, currRev, accountId, ts);
|
||||
updateTs = true;
|
||||
}
|
||||
parseGroups(psId, commit);
|
||||
|
||||
@@ -251,16 +259,24 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
// Only parse the most recent set of submit records; any older ones are
|
||||
// still there, but not currently used.
|
||||
parseSubmitRecords(commit.getFooterLines(FOOTER_SUBMITTED_WITH));
|
||||
updateTs |= !submitRecords.isEmpty();
|
||||
}
|
||||
|
||||
for (String line : commit.getFooterLines(FOOTER_LABEL)) {
|
||||
parseApproval(psId, accountId, ts, line);
|
||||
updateTs = true;
|
||||
}
|
||||
|
||||
for (ReviewerStateInternal state : ReviewerStateInternal.values()) {
|
||||
for (String line : commit.getFooterLines(state.getFooterKey())) {
|
||||
parseReviewer(state, line);
|
||||
}
|
||||
// Don't update timestamp when a reviewer was added, matching RevewDb
|
||||
// behavior.
|
||||
}
|
||||
|
||||
if (lastUpdatedOn == null && updateTs) {
|
||||
lastUpdatedOn = ts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,20 +434,20 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
throw invalidFooter(FOOTER_PATCH_SET, psIdLine);
|
||||
}
|
||||
|
||||
private void parseChangeMessage(PatchSet.Id psId, Account.Id accountId,
|
||||
RevCommit commit, Timestamp ts) {
|
||||
private ChangeMessage parseChangeMessage(PatchSet.Id psId,
|
||||
Account.Id accountId, RevCommit commit, Timestamp ts) {
|
||||
byte[] raw = commit.getRawBuffer();
|
||||
int size = raw.length;
|
||||
Charset enc = RawParseUtils.parseEncoding(raw);
|
||||
|
||||
int subjectStart = RawParseUtils.commitMessage(raw, 0);
|
||||
if (subjectStart < 0 || subjectStart >= size) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
int subjectEnd = RawParseUtils.endOfParagraph(raw, subjectStart);
|
||||
if (subjectEnd == size) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
int changeMessageStart;
|
||||
@@ -441,7 +457,7 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
} else if (raw[subjectEnd] == '\r') {
|
||||
changeMessageStart = subjectEnd + 4; //\r\n\r\n ends paragraph
|
||||
} else {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
int ptr = size - 1;
|
||||
@@ -461,7 +477,7 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
}
|
||||
|
||||
if (ptr <= changeMessageStart) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
String changeMsgString = RawParseUtils.decode(enc, raw,
|
||||
@@ -474,6 +490,7 @@ class ChangeNotesParser implements AutoCloseable {
|
||||
changeMessage.setMessage(changeMsgString);
|
||||
changeMessagesByPatchSet.put(psId, changeMessage);
|
||||
allChangeMessages.add(changeMessage);
|
||||
return changeMessage;
|
||||
}
|
||||
|
||||
private void parseNotes()
|
||||
|
||||
@@ -618,6 +618,13 @@ public class ChangeData {
|
||||
|
||||
public ChangeControl changeControl(CurrentUser user) throws OrmException {
|
||||
if (changeControl != null) {
|
||||
CurrentUser oldUser = user;
|
||||
// TODO(dborowitz): This is a hack; general CurrentUser equality would be
|
||||
// better.
|
||||
if (user.isIdentifiedUser() && oldUser.isIdentifiedUser()
|
||||
&& user.getAccountId().equals(oldUser.getAccountId())) {
|
||||
return changeControl;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"user already specified: " + changeControl.getUser());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user