Merge changes Iea17346e,Ifba1d457,I7895e7c1
* changes: Move static types out of ChangeRebuilderImpl Move ChangeRebuilder(Impl) to a subpackage ChangeBundle: Remove infeasible TODO
This commit is contained in:
@@ -59,10 +59,10 @@ import com.google.gerrit.server.git.RepoRefCache;
|
|||||||
import com.google.gerrit.server.git.UpdateException;
|
import com.google.gerrit.server.git.UpdateException;
|
||||||
import com.google.gerrit.server.notedb.ChangeBundle;
|
import com.google.gerrit.server.notedb.ChangeBundle;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.notedb.ChangeRebuilder.NoPatchSetsException;
|
|
||||||
import com.google.gerrit.server.notedb.NoteDbChangeState;
|
import com.google.gerrit.server.notedb.NoteDbChangeState;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
||||||
import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
|
import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
|
||||||
import com.google.gerrit.testutil.ConfigSuite;
|
import com.google.gerrit.testutil.ConfigSuite;
|
||||||
import com.google.gerrit.testutil.NoteDbChecker;
|
import com.google.gerrit.testutil.NoteDbChecker;
|
||||||
import com.google.gerrit.testutil.NoteDbMode;
|
import com.google.gerrit.testutil.NoteDbMode;
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ import com.google.gerrit.server.git.GitRepositoryManager;
|
|||||||
import com.google.gerrit.server.git.WorkQueue;
|
import com.google.gerrit.server.git.WorkQueue;
|
||||||
import com.google.gerrit.server.index.DummyIndexModule;
|
import com.google.gerrit.server.index.DummyIndexModule;
|
||||||
import com.google.gerrit.server.index.change.ReindexAfterUpdate;
|
import com.google.gerrit.server.index.change.ReindexAfterUpdate;
|
||||||
import com.google.gerrit.server.notedb.ChangeRebuilder;
|
|
||||||
import com.google.gerrit.server.notedb.NotesMigration;
|
import com.google.gerrit.server.notedb.NotesMigration;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import com.google.gerrit.extensions.restapi.Response;
|
|||||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.change.Rebuild.Input;
|
import com.google.gerrit.server.change.Rebuild.Input;
|
||||||
import com.google.gerrit.server.notedb.ChangeRebuilder;
|
|
||||||
import com.google.gerrit.server.notedb.NotesMigration;
|
import com.google.gerrit.server.notedb.NotesMigration;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
|
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ 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.PatchLineCommentsUtil;
|
import com.google.gerrit.server.PatchLineCommentsUtil;
|
||||||
import com.google.gerrit.server.ReviewerSet;
|
import com.google.gerrit.server.ReviewerSet;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
|
||||||
import com.google.gwtorm.client.Column;
|
import com.google.gwtorm.client.Column;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
@@ -241,10 +242,7 @@ public class ChangeBundle {
|
|||||||
checkColumns(Change.Id.class, 1);
|
checkColumns(Change.Id.class, 1);
|
||||||
|
|
||||||
checkColumns(Change.class,
|
checkColumns(Change.class,
|
||||||
1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18,
|
1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18, 101);
|
||||||
// TODO(dborowitz): It's potentially possible to compare noteDbState in
|
|
||||||
// the Change with the state implied by a ChangeNotes.
|
|
||||||
101);
|
|
||||||
checkColumns(ChangeMessage.Key.class, 1, 2);
|
checkColumns(ChangeMessage.Key.class, 1, 2);
|
||||||
checkColumns(ChangeMessage.class, 1, 2, 3, 4, 5, 6);
|
checkColumns(ChangeMessage.class, 1, 2, 3, 4, 5, 6);
|
||||||
checkColumns(PatchSet.Id.class, 1, 2);
|
checkColumns(PatchSet.Id.class, 1, 2);
|
||||||
|
|||||||
@@ -61,20 +61,21 @@ import java.util.Locale;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ChangeNoteUtil {
|
public class ChangeNoteUtil {
|
||||||
static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
|
public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
|
||||||
static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
|
public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
|
||||||
static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
|
public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
|
||||||
static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
|
public static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
|
||||||
static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
|
public static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
|
||||||
static final FooterKey FOOTER_LABEL = new FooterKey("Label");
|
public static final FooterKey FOOTER_LABEL = new FooterKey("Label");
|
||||||
static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
|
public static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
|
||||||
static final FooterKey FOOTER_STATUS = new FooterKey("Status");
|
public static final FooterKey FOOTER_STATUS = new FooterKey("Status");
|
||||||
static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
|
public static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
|
||||||
static final FooterKey FOOTER_SUBMISSION_ID = new FooterKey("Submission-id");
|
public static final FooterKey FOOTER_SUBMISSION_ID =
|
||||||
static final FooterKey FOOTER_SUBMITTED_WITH =
|
new FooterKey("Submission-id");
|
||||||
|
public static final FooterKey FOOTER_SUBMITTED_WITH =
|
||||||
new FooterKey("Submitted-with");
|
new FooterKey("Submitted-with");
|
||||||
static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
|
public static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
|
||||||
static final FooterKey FOOTER_TAG = new FooterKey("Tag");
|
public static final FooterKey FOOTER_TAG = new FooterKey("Tag");
|
||||||
|
|
||||||
private static final String AUTHOR = "Author";
|
private static final String AUTHOR = "Author";
|
||||||
private static final String BASE_PATCH_SET = "Base-for-patch-set";
|
private static final String BASE_PATCH_SET = "Base-for-patch-set";
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import com.google.gerrit.server.ReviewerSet;
|
|||||||
import com.google.gerrit.server.ReviewerStatusUpdate;
|
import com.google.gerrit.server.ReviewerStatusUpdate;
|
||||||
import com.google.gerrit.server.git.RefCache;
|
import com.google.gerrit.server.git.RefCache;
|
||||||
import com.google.gerrit.server.git.RepoRefCache;
|
import com.google.gerrit.server.git.RepoRefCache;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
this.commitSubject = commitSubject;
|
this.commitSubject = commitSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSubject(String subject) {
|
public void setSubject(String subject) {
|
||||||
this.subject = subject;
|
this.subject = subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ 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.git.RepoRefCache;
|
import com.google.gerrit.server.git.RepoRefCache;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager.StagedResult;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.StagedResult;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class NoteDbChangeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static NoteDbChangeState parse(Change.Id id, String str) {
|
public static NoteDbChangeState parse(Change.Id id, String str) {
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,7 @@ public class NoteDbChangeState {
|
|||||||
return id.get().equals(draftIds.get(accountId));
|
return id.get().equals(draftIds.get(accountId));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isUpToDate(RefCache changeRepoRefs, RefCache draftsRepoRefs)
|
public boolean isUpToDate(RefCache changeRepoRefs, RefCache draftsRepoRefs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (!isChangeUpToDate(changeRepoRefs)) {
|
if (!isChangeUpToDate(changeRepoRefs)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import com.google.gerrit.reviewdb.client.Change.Id;
|
|||||||
import com.google.gerrit.reviewdb.client.Project.NameKey;
|
import com.google.gerrit.reviewdb.client.Project.NameKey;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
|
|
||||||
|
|||||||
@@ -119,10 +119,10 @@ public class NoteDbUpdateManager implements AutoCloseable {
|
|||||||
@Nullable abstract NoteDbUpdateManager.StagedResult staged();
|
@Nullable abstract NoteDbUpdateManager.StagedResult staged();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class OpenRepo implements AutoCloseable {
|
public static class OpenRepo implements AutoCloseable {
|
||||||
final Repository repo;
|
public final Repository repo;
|
||||||
final RevWalk rw;
|
public final RevWalk rw;
|
||||||
final ChainedReceiveCommands cmds;
|
public final ChainedReceiveCommands cmds;
|
||||||
|
|
||||||
private final InMemoryInserter tempIns;
|
private final InMemoryInserter tempIns;
|
||||||
@Nullable private final ObjectInserter finalIns;
|
@Nullable private final ObjectInserter finalIns;
|
||||||
@@ -143,7 +143,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
|
|||||||
this.close = close;
|
this.close = close;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<ObjectId> getObjectId(String refName) throws IOException {
|
public Optional<ObjectId> getObjectId(String refName) throws IOException {
|
||||||
return cmds.get(refName);
|
return cmds.get(refName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,17 +233,17 @@ public class NoteDbUpdateManager implements AutoCloseable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteDbUpdateManager setCheckExpectedState(boolean checkExpectedState) {
|
public NoteDbUpdateManager setCheckExpectedState(boolean checkExpectedState) {
|
||||||
this.checkExpectedState = checkExpectedState;
|
this.checkExpectedState = checkExpectedState;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenRepo getChangeRepo() throws IOException {
|
public OpenRepo getChangeRepo() throws IOException {
|
||||||
initChangeRepo();
|
initChangeRepo();
|
||||||
return changeRepo;
|
return changeRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenRepo getAllUsersRepo() throws IOException {
|
public OpenRepo getAllUsersRepo() throws IOException {
|
||||||
initAllUsersRepo();
|
initAllUsersRepo();
|
||||||
return allUsersRepo;
|
return allUsersRepo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import com.google.gerrit.reviewdb.client.Change;
|
|||||||
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.notedb.NoteDbUpdateManager.Result;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gwtorm.server.OrmRuntimeException;
|
||||||
|
|
||||||
|
class AbortUpdateException extends OrmRuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
AbortUpdateException() {
|
||||||
|
super("aborted");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
class ApprovalEvent extends Event {
|
||||||
|
private PatchSetApproval psa;
|
||||||
|
|
||||||
|
ApprovalEvent(PatchSetApproval psa, Timestamp changeCreatedOn) {
|
||||||
|
super(psa.getPatchSetId(), psa.getAccountId(), psa.getGranted(),
|
||||||
|
changeCreatedOn, psa.getTag());
|
||||||
|
this.psa = psa;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) {
|
||||||
|
checkUpdate(update);
|
||||||
|
update.putApproval(psa.getLabel(), psa.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class ChangeMessageEvent extends Event {
|
||||||
|
private static final Pattern TOPIC_SET_REGEXP =
|
||||||
|
Pattern.compile("^Topic set to (.+)$");
|
||||||
|
private static final Pattern TOPIC_CHANGED_REGEXP =
|
||||||
|
Pattern.compile("^Topic changed from (.+) to (.+)$");
|
||||||
|
private static final Pattern TOPIC_REMOVED_REGEXP =
|
||||||
|
Pattern.compile("^Topic (.+) removed$");
|
||||||
|
|
||||||
|
private static final Pattern STATUS_ABANDONED_REGEXP =
|
||||||
|
Pattern.compile("^Abandoned(\n.*)*$");
|
||||||
|
private static final Pattern STATUS_RESTORED_REGEXP =
|
||||||
|
Pattern.compile("^Restored(\n.*)*$");
|
||||||
|
|
||||||
|
private final ChangeMessage message;
|
||||||
|
private final Change noteDbChange;
|
||||||
|
|
||||||
|
ChangeMessageEvent(ChangeMessage message, Change noteDbChange,
|
||||||
|
Timestamp changeCreatedOn) {
|
||||||
|
super(message.getPatchSetId(), message.getAuthor(),
|
||||||
|
message.getWrittenOn(), changeCreatedOn, message.getTag());
|
||||||
|
this.message = message;
|
||||||
|
this.noteDbChange = noteDbChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws OrmException {
|
||||||
|
checkUpdate(update);
|
||||||
|
update.setChangeMessage(message.getMessage());
|
||||||
|
setTopic(update);
|
||||||
|
setStatus(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTopic(ChangeUpdate update) {
|
||||||
|
String msg = message.getMessage();
|
||||||
|
if (msg == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Matcher m = TOPIC_SET_REGEXP.matcher(msg);
|
||||||
|
if (m.matches()) {
|
||||||
|
String topic = m.group(1);
|
||||||
|
update.setTopic(topic);
|
||||||
|
noteDbChange.setTopic(topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = TOPIC_CHANGED_REGEXP.matcher(msg);
|
||||||
|
if (m.matches()) {
|
||||||
|
String topic = m.group(2);
|
||||||
|
update.setTopic(topic);
|
||||||
|
noteDbChange.setTopic(topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOPIC_REMOVED_REGEXP.matcher(msg).matches()) {
|
||||||
|
update.setTopic(null);
|
||||||
|
noteDbChange.setTopic(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStatus(ChangeUpdate update) {
|
||||||
|
String msg = message.getMessage();
|
||||||
|
if (msg == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (STATUS_ABANDONED_REGEXP.matcher(msg).matches()) {
|
||||||
|
update.setStatus(Change.Status.ABANDONED);
|
||||||
|
noteDbChange.setStatus(Change.Status.ABANDONED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STATUS_RESTORED_REGEXP.matcher(msg).matches()) {
|
||||||
|
update.setStatus(Change.Status.NEW);
|
||||||
|
noteDbChange.setStatus(Change.Status.NEW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package com.google.gerrit.server.notedb;
|
package com.google.gerrit.server.notedb.rebuild;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
@@ -20,6 +20,8 @@ import com.google.common.util.concurrent.ListeningExecutorService;
|
|||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
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.notedb.ChangeBundle;
|
||||||
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
@@ -12,19 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package com.google.gerrit.server.notedb;
|
package com.google.gerrit.server.notedb.rebuild;
|
||||||
|
|
||||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
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.common.base.Preconditions.checkState;
|
||||||
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
|
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
|
||||||
import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
|
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
|
||||||
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.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
@@ -56,20 +53,25 @@ import com.google.gerrit.server.PatchLineCommentsUtil;
|
|||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
import com.google.gerrit.server.git.ChainedReceiveCommands;
|
import com.google.gerrit.server.git.ChainedReceiveCommands;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeBundle;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeDraftUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeNoteUtil;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.NoteDbChangeState;
|
||||||
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager.OpenRepo;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.OpenRepo;
|
||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
|
||||||
|
import com.google.gerrit.server.notedb.NotesMigration;
|
||||||
|
import com.google.gerrit.server.notedb.ReviewerStateInternal;
|
||||||
import com.google.gerrit.server.patch.PatchListCache;
|
import com.google.gerrit.server.patch.PatchListCache;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gwtorm.server.AtomicUpdate;
|
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.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.InvalidObjectIdException;
|
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectInserter;
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
@@ -92,8 +94,6 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class ChangeRebuilderImpl extends ChangeRebuilder {
|
public class ChangeRebuilderImpl extends ChangeRebuilder {
|
||||||
private static final Logger log =
|
private static final Logger log =
|
||||||
@@ -108,13 +108,13 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
* historically didn't necessarily use the same timestamp, and tended to call
|
* historically didn't necessarily use the same timestamp, and tended to call
|
||||||
* {@code System.currentTimeMillis()} independently.
|
* {@code System.currentTimeMillis()} independently.
|
||||||
*/
|
*/
|
||||||
static final long MAX_WINDOW_MS = SECONDS.toMillis(3);
|
public static final long MAX_WINDOW_MS = SECONDS.toMillis(3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum amount of time between two consecutive events to consider them
|
* The maximum amount of time between two consecutive events to consider them
|
||||||
* to be in the same batch.
|
* to be in the same batch.
|
||||||
*/
|
*/
|
||||||
private static final long MAX_DELTA_MS = SECONDS.toMillis(1);
|
static final long MAX_DELTA_MS = SECONDS.toMillis(1);
|
||||||
|
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
private final ChangeDraftUpdate.Factory draftUpdateFactory;
|
private final ChangeDraftUpdate.Factory draftUpdateFactory;
|
||||||
@@ -168,24 +168,6 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AbortUpdateException extends OrmRuntimeException {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
AbortUpdateException() {
|
|
||||||
super("aborted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConflictingUpdateException extends OrmRuntimeException {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
ConflictingUpdateException(Change change, String expectedNoteDbState) {
|
|
||||||
super(String.format(
|
|
||||||
"Expected change %s to have noteDbState %s but was %s",
|
|
||||||
change.getId(), expectedNoteDbState, change.getNoteDbState()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result rebuild(NoteDbUpdateManager manager,
|
public Result rebuild(NoteDbUpdateManager manager,
|
||||||
ChangeBundle bundle) throws NoSuchChangeException, IOException,
|
ChangeBundle bundle) throws NoSuchChangeException, IOException,
|
||||||
@@ -466,7 +448,7 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
ChangeUpdate update = updateFactory.create(
|
ChangeUpdate update = updateFactory.create(
|
||||||
change,
|
change,
|
||||||
events.getAccountId(),
|
events.getAccountId(),
|
||||||
events.newAuthorIdent(),
|
newAuthorIdent(events),
|
||||||
events.getWhen(),
|
events.getWhen(),
|
||||||
labelNameComparator);
|
labelNameComparator);
|
||||||
update.setAllowWriteToNewRef(true);
|
update.setAllowWriteToNewRef(true);
|
||||||
@@ -488,7 +470,7 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
ChangeDraftUpdate update = draftUpdateFactory.create(
|
ChangeDraftUpdate update = draftUpdateFactory.create(
|
||||||
change,
|
change,
|
||||||
events.getAccountId(),
|
events.getAccountId(),
|
||||||
events.newAuthorIdent(),
|
newAuthorIdent(events),
|
||||||
events.getWhen());
|
events.getWhen());
|
||||||
update.setPatchSetId(events.getPatchSetId());
|
update.setPatchSetId(events.getPatchSetId());
|
||||||
for (PatchLineCommentEvent e : events) {
|
for (PatchLineCommentEvent e : events) {
|
||||||
@@ -498,6 +480,16 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
events.clear();
|
events.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PersonIdent newAuthorIdent(EventList<?> events) {
|
||||||
|
Account.Id id = events.getAccountId();
|
||||||
|
if (id == null) {
|
||||||
|
return new PersonIdent(serverIdent, events.getWhen());
|
||||||
|
}
|
||||||
|
return changeNoteUtil.newIdent(
|
||||||
|
accountCache.get(id).getAccount(), events.getWhen(), serverIdent,
|
||||||
|
anonymousCowardName);
|
||||||
|
}
|
||||||
|
|
||||||
private List<HashtagsEvent> getHashtagsEvents(Change change,
|
private List<HashtagsEvent> getHashtagsEvents(Change change,
|
||||||
NoteDbUpdateManager manager) throws IOException {
|
NoteDbUpdateManager manager) throws IOException {
|
||||||
String refName = changeMetaRef(change.getId());
|
String refName = changeMetaRef(change.getId());
|
||||||
@@ -591,470 +583,10 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private abstract static class Event {
|
static void createChange(ChangeUpdate update, Change change) {
|
||||||
// NOTE: EventList only supports direct subclasses, not an arbitrary
|
|
||||||
// hierarchy.
|
|
||||||
|
|
||||||
final Account.Id who;
|
|
||||||
final Timestamp when;
|
|
||||||
final String tag;
|
|
||||||
final boolean predatesChange;
|
|
||||||
PatchSet.Id psId;
|
|
||||||
|
|
||||||
protected Event(PatchSet.Id psId, Account.Id who, Timestamp when,
|
|
||||||
Timestamp changeCreatedOn, String tag) {
|
|
||||||
this.psId = psId;
|
|
||||||
this.who = who;
|
|
||||||
this.tag = tag;
|
|
||||||
// Truncate timestamps at the change's createdOn timestamp.
|
|
||||||
predatesChange = when.before(changeCreatedOn);
|
|
||||||
this.when = predatesChange ? changeCreatedOn : when;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkUpdate(AbstractChangeUpdate update) {
|
|
||||||
checkState(Objects.equals(update.getPatchSetId(), psId),
|
|
||||||
"cannot apply event for %s to update for %s",
|
|
||||||
update.getPatchSetId(), psId);
|
|
||||||
checkState(when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS,
|
|
||||||
"event at %s outside update window starting at %s",
|
|
||||||
when, update.getWhen());
|
|
||||||
checkState(Objects.equals(update.getNullableAccountId(), who),
|
|
||||||
"cannot apply event by %s to update by %s",
|
|
||||||
who, update.getNullableAccountId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether this event type must be unique per {@link ChangeUpdate},
|
|
||||||
* i.e. there may be at most one of this type.
|
|
||||||
*/
|
|
||||||
abstract boolean uniquePerUpdate();
|
|
||||||
|
|
||||||
abstract void apply(ChangeUpdate update) throws OrmException, IOException;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return MoreObjects.toStringHelper(this)
|
|
||||||
.add("psId", psId)
|
|
||||||
.add("who", who)
|
|
||||||
.add("when", when)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EventList<E extends Event> extends ArrayList<E> {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private E getLast() {
|
|
||||||
return get(size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getLastTime() {
|
|
||||||
return getLast().when.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getFirstTime() {
|
|
||||||
return get(0).when.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean canAdd(E e) {
|
|
||||||
if (isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (e instanceof FinalUpdatesEvent) {
|
|
||||||
return false; // FinalUpdatesEvent always gets its own update.
|
|
||||||
}
|
|
||||||
|
|
||||||
Event last = getLast();
|
|
||||||
if (!Objects.equals(e.who, last.who)
|
|
||||||
|| !e.psId.equals(last.psId)
|
|
||||||
|| !Objects.equals(e.tag, last.tag)) {
|
|
||||||
return false; // Different patch set, author, or tag.
|
|
||||||
}
|
|
||||||
|
|
||||||
long t = e.when.getTime();
|
|
||||||
long tFirst = getFirstTime();
|
|
||||||
long tLast = getLastTime();
|
|
||||||
checkArgument(t >= tLast,
|
|
||||||
"event %s is before previous event in list %s", e, last);
|
|
||||||
if (t - tLast > MAX_DELTA_MS || t - tFirst > MAX_WINDOW_MS) {
|
|
||||||
return false; // Too much time elapsed.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!e.uniquePerUpdate()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (Event o : this) {
|
|
||||||
if (e.getClass() == o.getClass()) {
|
|
||||||
return false; // Only one event of this type allowed per update.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(dborowitz): Additional heuristics, like keeping events separate if
|
|
||||||
// they affect overlapping fields within a single entity.
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timestamp getWhen() {
|
|
||||||
return get(0).when;
|
|
||||||
}
|
|
||||||
|
|
||||||
PatchSet.Id getPatchSetId() {
|
|
||||||
PatchSet.Id id = checkNotNull(get(0).psId);
|
|
||||||
for (int i = 1; i < size(); i++) {
|
|
||||||
checkState(get(i).psId.equals(id),
|
|
||||||
"mismatched patch sets in EventList: %s != %s", id, get(i).psId);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Account.Id getAccountId() {
|
|
||||||
Account.Id id = get(0).who;
|
|
||||||
for (int i = 1; i < size(); i++) {
|
|
||||||
checkState(Objects.equals(id, get(i).who),
|
|
||||||
"mismatched users in EventList: %s != %s", id, get(i).who);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
PersonIdent newAuthorIdent() {
|
|
||||||
Account.Id id = getAccountId();
|
|
||||||
if (id == null) {
|
|
||||||
return new PersonIdent(serverIdent, getWhen());
|
|
||||||
}
|
|
||||||
return changeNoteUtil.newIdent(
|
|
||||||
accountCache.get(id).getAccount(), getWhen(), serverIdent,
|
|
||||||
anonymousCowardName);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTag() {
|
|
||||||
return getLast().tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createChange(ChangeUpdate update, Change change) {
|
|
||||||
update.setSubjectForCommit("Create change");
|
update.setSubjectForCommit("Create change");
|
||||||
update.setChangeId(change.getKey().get());
|
update.setChangeId(change.getKey().get());
|
||||||
update.setBranch(change.getDest().get());
|
update.setBranch(change.getDest().get());
|
||||||
update.setSubject(change.getOriginalSubject());
|
update.setSubject(change.getOriginalSubject());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CreateChangeEvent extends Event {
|
|
||||||
private final Change change;
|
|
||||||
|
|
||||||
private static PatchSet.Id psId(Change change, Integer minPsNum) {
|
|
||||||
int n;
|
|
||||||
if (minPsNum == null) {
|
|
||||||
// There were no patch sets for the change at all, so something is very
|
|
||||||
// wrong. Bail and use 1 as the patch set.
|
|
||||||
n = 1;
|
|
||||||
} else {
|
|
||||||
n = minPsNum;
|
|
||||||
}
|
|
||||||
return new PatchSet.Id(change.getId(), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateChangeEvent(Change change, Integer minPsNum) {
|
|
||||||
super(psId(change, minPsNum), change.getOwner(), change.getCreatedOn(),
|
|
||||||
change.getCreatedOn(), null);
|
|
||||||
this.change = change;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws IOException, OrmException {
|
|
||||||
checkUpdate(update);
|
|
||||||
createChange(update, change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ApprovalEvent extends Event {
|
|
||||||
private PatchSetApproval psa;
|
|
||||||
|
|
||||||
ApprovalEvent(PatchSetApproval psa, Timestamp changeCreatedOn) {
|
|
||||||
super(psa.getPatchSetId(), psa.getAccountId(), psa.getGranted(),
|
|
||||||
changeCreatedOn, psa.getTag());
|
|
||||||
this.psa = psa;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) {
|
|
||||||
checkUpdate(update);
|
|
||||||
update.putApproval(psa.getLabel(), psa.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ReviewerEvent extends Event {
|
|
||||||
private Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer;
|
|
||||||
|
|
||||||
ReviewerEvent(
|
|
||||||
Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer,
|
|
||||||
Timestamp changeCreatedOn) {
|
|
||||||
super(
|
|
||||||
// Reviewers aren't generally associated with a particular patch set
|
|
||||||
// (although as an implementation detail they were in ReviewDb). Just
|
|
||||||
// use the latest patch set at the time of the event.
|
|
||||||
null,
|
|
||||||
reviewer.getColumnKey(), reviewer.getValue(), changeCreatedOn, null);
|
|
||||||
this.reviewer = reviewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws IOException, OrmException {
|
|
||||||
checkUpdate(update);
|
|
||||||
update.putReviewer(reviewer.getColumnKey(), reviewer.getRowKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PatchSetEvent extends Event {
|
|
||||||
private final Change change;
|
|
||||||
private final PatchSet ps;
|
|
||||||
private final RevWalk rw;
|
|
||||||
private boolean createChange;
|
|
||||||
|
|
||||||
PatchSetEvent(Change change, PatchSet ps, RevWalk rw) {
|
|
||||||
super(ps.getId(), ps.getUploader(), ps.getCreatedOn(),
|
|
||||||
change.getCreatedOn(), null);
|
|
||||||
this.change = change;
|
|
||||||
this.ps = ps;
|
|
||||||
this.rw = rw;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws IOException, OrmException {
|
|
||||||
checkUpdate(update);
|
|
||||||
if (createChange) {
|
|
||||||
createChange(update, change);
|
|
||||||
} else {
|
|
||||||
update.setSubject(change.getSubject());
|
|
||||||
update.setSubjectForCommit("Create patch set " + ps.getPatchSetId());
|
|
||||||
}
|
|
||||||
setRevision(update, ps);
|
|
||||||
List<String> groups = ps.getGroups();
|
|
||||||
if (!groups.isEmpty()) {
|
|
||||||
update.setGroups(ps.getGroups());
|
|
||||||
}
|
|
||||||
if (ps.isDraft()) {
|
|
||||||
update.setPatchSetState(PatchSetState.DRAFT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRevision(ChangeUpdate update, PatchSet ps)
|
|
||||||
throws IOException {
|
|
||||||
String rev = ps.getRevision().get();
|
|
||||||
String cert = ps.getPushCertificate();
|
|
||||||
ObjectId id;
|
|
||||||
try {
|
|
||||||
id = ObjectId.fromString(rev);
|
|
||||||
} catch (InvalidObjectIdException e) {
|
|
||||||
update.setRevisionForMissingCommit(rev, cert);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
update.setCommit(rw, id, cert);
|
|
||||||
} catch (MissingObjectException e) {
|
|
||||||
update.setRevisionForMissingCommit(rev, cert);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PatchLineCommentEvent extends Event {
|
|
||||||
public final PatchLineComment c;
|
|
||||||
private final Change change;
|
|
||||||
private final PatchSet ps;
|
|
||||||
private final PatchListCache cache;
|
|
||||||
|
|
||||||
PatchLineCommentEvent(PatchLineComment c, Change change, PatchSet ps,
|
|
||||||
PatchListCache cache) {
|
|
||||||
super(PatchLineCommentsUtil.getCommentPsId(c), c.getAuthor(),
|
|
||||||
c.getWrittenOn(), change.getCreatedOn(), c.getTag());
|
|
||||||
this.c = c;
|
|
||||||
this.change = change;
|
|
||||||
this.ps = ps;
|
|
||||||
this.cache = cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws OrmException {
|
|
||||||
checkUpdate(update);
|
|
||||||
if (c.getRevId() == null) {
|
|
||||||
setCommentRevId(c, cache, change, ps);
|
|
||||||
}
|
|
||||||
update.putComment(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyDraft(ChangeDraftUpdate draftUpdate) throws OrmException {
|
|
||||||
if (c.getRevId() == null) {
|
|
||||||
setCommentRevId(c, cache, change, ps);
|
|
||||||
}
|
|
||||||
draftUpdate.putComment(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class HashtagsEvent extends Event {
|
|
||||||
private final Set<String> hashtags;
|
|
||||||
|
|
||||||
HashtagsEvent(PatchSet.Id psId, Account.Id who, Timestamp when,
|
|
||||||
Set<String> hashtags, Timestamp changeCreatdOn) {
|
|
||||||
super(psId, who, when, changeCreatdOn,
|
|
||||||
// Somewhat confusingly, hashtags do not use the setTag method on
|
|
||||||
// AbstractChangeUpdate, so pass null as the tag.
|
|
||||||
null);
|
|
||||||
this.hashtags = hashtags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
// Since these are produced from existing commits in the old NoteDb graph,
|
|
||||||
// we know that there must be one per commit in the rebuilt graph.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws OrmException {
|
|
||||||
update.setHashtags(hashtags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ChangeMessageEvent extends Event {
|
|
||||||
private static final Pattern TOPIC_SET_REGEXP =
|
|
||||||
Pattern.compile("^Topic set to (.+)$");
|
|
||||||
private static final Pattern TOPIC_CHANGED_REGEXP =
|
|
||||||
Pattern.compile("^Topic changed from (.+) to (.+)$");
|
|
||||||
private static final Pattern TOPIC_REMOVED_REGEXP =
|
|
||||||
Pattern.compile("^Topic (.+) removed$");
|
|
||||||
|
|
||||||
private static final Pattern STATUS_ABANDONED_REGEXP =
|
|
||||||
Pattern.compile("^Abandoned(\n.*)*$");
|
|
||||||
private static final Pattern STATUS_RESTORED_REGEXP =
|
|
||||||
Pattern.compile("^Restored(\n.*)*$");
|
|
||||||
|
|
||||||
private final ChangeMessage message;
|
|
||||||
private final Change noteDbChange;
|
|
||||||
|
|
||||||
ChangeMessageEvent(ChangeMessage message, Change noteDbChange,
|
|
||||||
Timestamp changeCreatedOn) {
|
|
||||||
super(message.getPatchSetId(), message.getAuthor(),
|
|
||||||
message.getWrittenOn(), changeCreatedOn, message.getTag());
|
|
||||||
this.message = message;
|
|
||||||
this.noteDbChange = noteDbChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws OrmException {
|
|
||||||
checkUpdate(update);
|
|
||||||
update.setChangeMessage(message.getMessage());
|
|
||||||
setTopic(update);
|
|
||||||
setStatus(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTopic(ChangeUpdate update) {
|
|
||||||
String msg = message.getMessage();
|
|
||||||
if (msg == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Matcher m = TOPIC_SET_REGEXP.matcher(msg);
|
|
||||||
if (m.matches()) {
|
|
||||||
String topic = m.group(1);
|
|
||||||
update.setTopic(topic);
|
|
||||||
noteDbChange.setTopic(topic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = TOPIC_CHANGED_REGEXP.matcher(msg);
|
|
||||||
if (m.matches()) {
|
|
||||||
String topic = m.group(2);
|
|
||||||
update.setTopic(topic);
|
|
||||||
noteDbChange.setTopic(topic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TOPIC_REMOVED_REGEXP.matcher(msg).matches()) {
|
|
||||||
update.setTopic(null);
|
|
||||||
noteDbChange.setTopic(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStatus(ChangeUpdate update) {
|
|
||||||
String msg = message.getMessage();
|
|
||||||
if (msg == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (STATUS_ABANDONED_REGEXP.matcher(msg).matches()) {
|
|
||||||
update.setStatus(Change.Status.ABANDONED);
|
|
||||||
noteDbChange.setStatus(Change.Status.ABANDONED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (STATUS_RESTORED_REGEXP.matcher(msg).matches()) {
|
|
||||||
update.setStatus(Change.Status.NEW);
|
|
||||||
noteDbChange.setStatus(Change.Status.NEW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FinalUpdatesEvent extends Event {
|
|
||||||
private final Change change;
|
|
||||||
private final Change noteDbChange;
|
|
||||||
|
|
||||||
FinalUpdatesEvent(Change change, Change noteDbChange) {
|
|
||||||
super(change.currentPatchSetId(), change.getOwner(),
|
|
||||||
change.getLastUpdatedOn(), change.getCreatedOn(), null);
|
|
||||||
this.change = change;
|
|
||||||
this.noteDbChange = noteDbChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean uniquePerUpdate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
void apply(ChangeUpdate update) throws OrmException {
|
|
||||||
if (!Objects.equals(change.getTopic(), noteDbChange.getTopic())) {
|
|
||||||
update.setTopic(change.getTopic());
|
|
||||||
}
|
|
||||||
if (!Objects.equals(change.getStatus(), noteDbChange.getStatus())) {
|
|
||||||
// TODO(dborowitz): Stamp approximate approvals at this time.
|
|
||||||
update.fixStatus(change.getStatus());
|
|
||||||
}
|
|
||||||
if (change.getSubmissionId() != null) {
|
|
||||||
update.setSubmissionId(change.getSubmissionId());
|
|
||||||
}
|
|
||||||
if (!update.isEmpty()) {
|
|
||||||
update.setSubjectForCommit("Final NoteDb migration updates");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gwtorm.server.OrmRuntimeException;
|
||||||
|
|
||||||
|
class ConflictingUpdateException extends OrmRuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
ConflictingUpdateException(Change change, String expectedNoteDbState) {
|
||||||
|
super(String.format(
|
||||||
|
"Expected change %s to have noteDbState %s but was %s",
|
||||||
|
change.getId(), expectedNoteDbState, change.getNoteDbState()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
class CreateChangeEvent extends Event {
|
||||||
|
private final Change change;
|
||||||
|
|
||||||
|
private static PatchSet.Id psId(Change change, Integer minPsNum) {
|
||||||
|
int n;
|
||||||
|
if (minPsNum == null) {
|
||||||
|
// There were no patch sets for the change at all, so something is very
|
||||||
|
// wrong. Bail and use 1 as the patch set.
|
||||||
|
n = 1;
|
||||||
|
} else {
|
||||||
|
n = minPsNum;
|
||||||
|
}
|
||||||
|
return new PatchSet.Id(change.getId(), n);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateChangeEvent(Change change, Integer minPsNum) {
|
||||||
|
super(psId(change, minPsNum), change.getOwner(), change.getCreatedOn(),
|
||||||
|
change.getCreatedOn(), null);
|
||||||
|
this.change = change;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws IOException, OrmException {
|
||||||
|
checkUpdate(update);
|
||||||
|
ChangeRebuilderImpl.createChange(update, change);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl.MAX_WINDOW_MS;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.server.notedb.AbstractChangeUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
abstract class Event {
|
||||||
|
// NOTE: EventList only supports direct subclasses, not an arbitrary
|
||||||
|
// hierarchy.
|
||||||
|
|
||||||
|
final Account.Id who;
|
||||||
|
final Timestamp when;
|
||||||
|
final String tag;
|
||||||
|
final boolean predatesChange;
|
||||||
|
PatchSet.Id psId;
|
||||||
|
|
||||||
|
protected Event(PatchSet.Id psId, Account.Id who, Timestamp when,
|
||||||
|
Timestamp changeCreatedOn, String tag) {
|
||||||
|
this.psId = psId;
|
||||||
|
this.who = who;
|
||||||
|
this.tag = tag;
|
||||||
|
// Truncate timestamps at the change's createdOn timestamp.
|
||||||
|
predatesChange = when.before(changeCreatedOn);
|
||||||
|
this.when = predatesChange ? changeCreatedOn : when;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkUpdate(AbstractChangeUpdate update) {
|
||||||
|
checkState(Objects.equals(update.getPatchSetId(), psId),
|
||||||
|
"cannot apply event for %s to update for %s",
|
||||||
|
update.getPatchSetId(), psId);
|
||||||
|
checkState(when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS,
|
||||||
|
"event at %s outside update window starting at %s",
|
||||||
|
when, update.getWhen());
|
||||||
|
checkState(Objects.equals(update.getNullableAccountId(), who),
|
||||||
|
"cannot apply event by %s to update by %s",
|
||||||
|
who, update.getNullableAccountId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this event type must be unique per {@link ChangeUpdate},
|
||||||
|
* i.e. there may be at most one of this type.
|
||||||
|
*/
|
||||||
|
abstract boolean uniquePerUpdate();
|
||||||
|
|
||||||
|
abstract void apply(ChangeUpdate update) throws OrmException, IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("psId", psId)
|
||||||
|
.add("who", who)
|
||||||
|
.add("when", when)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
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 com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
class EventList<E extends Event> extends ArrayList<E> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private E getLast() {
|
||||||
|
return get(size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLastTime() {
|
||||||
|
return getLast().when.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getFirstTime() {
|
||||||
|
return get(0).when.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canAdd(E e) {
|
||||||
|
if (isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (e instanceof FinalUpdatesEvent) {
|
||||||
|
return false; // FinalUpdatesEvent always gets its own update.
|
||||||
|
}
|
||||||
|
|
||||||
|
Event last = getLast();
|
||||||
|
if (!Objects.equals(e.who, last.who)
|
||||||
|
|| !e.psId.equals(last.psId)
|
||||||
|
|| !Objects.equals(e.tag, last.tag)) {
|
||||||
|
return false; // Different patch set, author, or tag.
|
||||||
|
}
|
||||||
|
|
||||||
|
long t = e.when.getTime();
|
||||||
|
long tFirst = getFirstTime();
|
||||||
|
long tLast = getLastTime();
|
||||||
|
checkArgument(t >= tLast,
|
||||||
|
"event %s is before previous event in list %s", e, last);
|
||||||
|
if (t - tLast > ChangeRebuilderImpl.MAX_DELTA_MS || t - tFirst > ChangeRebuilderImpl.MAX_WINDOW_MS) {
|
||||||
|
return false; // Too much time elapsed.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.uniquePerUpdate()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (Event o : this) {
|
||||||
|
if (e.getClass() == o.getClass()) {
|
||||||
|
return false; // Only one event of this type allowed per update.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dborowitz): Additional heuristics, like keeping events separate if
|
||||||
|
// they affect overlapping fields within a single entity.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timestamp getWhen() {
|
||||||
|
return get(0).when;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchSet.Id getPatchSetId() {
|
||||||
|
PatchSet.Id id = checkNotNull(get(0).psId);
|
||||||
|
for (int i = 1; i < size(); i++) {
|
||||||
|
checkState(get(i).psId.equals(id),
|
||||||
|
"mismatched patch sets in EventList: %s != %s", id, get(i).psId);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Account.Id getAccountId() {
|
||||||
|
Account.Id id = get(0).who;
|
||||||
|
for (int i = 1; i < size(); i++) {
|
||||||
|
checkState(Objects.equals(id, get(i).who),
|
||||||
|
"mismatched users in EventList: %s != %s", id, get(i).who);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTag() {
|
||||||
|
return getLast().tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
class FinalUpdatesEvent extends Event {
|
||||||
|
private final Change change;
|
||||||
|
private final Change noteDbChange;
|
||||||
|
|
||||||
|
FinalUpdatesEvent(Change change, Change noteDbChange) {
|
||||||
|
super(change.currentPatchSetId(), change.getOwner(),
|
||||||
|
change.getLastUpdatedOn(), change.getCreatedOn(), null);
|
||||||
|
this.change = change;
|
||||||
|
this.noteDbChange = noteDbChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws OrmException {
|
||||||
|
if (!Objects.equals(change.getTopic(), noteDbChange.getTopic())) {
|
||||||
|
update.setTopic(change.getTopic());
|
||||||
|
}
|
||||||
|
if (!Objects.equals(change.getStatus(), noteDbChange.getStatus())) {
|
||||||
|
// TODO(dborowitz): Stamp approximate approvals at this time.
|
||||||
|
update.fixStatus(change.getStatus());
|
||||||
|
}
|
||||||
|
if (change.getSubmissionId() != null) {
|
||||||
|
update.setSubmissionId(change.getSubmissionId());
|
||||||
|
}
|
||||||
|
if (!update.isEmpty()) {
|
||||||
|
update.setSubjectForCommit("Final NoteDb migration updates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class HashtagsEvent extends Event {
|
||||||
|
private final Set<String> hashtags;
|
||||||
|
|
||||||
|
HashtagsEvent(PatchSet.Id psId, Account.Id who, Timestamp when,
|
||||||
|
Set<String> hashtags, Timestamp changeCreatdOn) {
|
||||||
|
super(psId, who, when, changeCreatdOn,
|
||||||
|
// Somewhat confusingly, hashtags do not use the setTag method on
|
||||||
|
// AbstractChangeUpdate, so pass null as the tag.
|
||||||
|
null);
|
||||||
|
this.hashtags = hashtags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
// Since these are produced from existing commits in the old NoteDb graph,
|
||||||
|
// we know that there must be one per commit in the rebuilt graph.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws OrmException {
|
||||||
|
update.setHashtags(hashtags);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.server.PatchLineCommentsUtil;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeDraftUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gerrit.server.patch.PatchListCache;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
class PatchLineCommentEvent extends Event {
|
||||||
|
public final PatchLineComment c;
|
||||||
|
private final Change change;
|
||||||
|
private final PatchSet ps;
|
||||||
|
private final PatchListCache cache;
|
||||||
|
|
||||||
|
PatchLineCommentEvent(PatchLineComment c, Change change, PatchSet ps,
|
||||||
|
PatchListCache cache) {
|
||||||
|
super(PatchLineCommentsUtil.getCommentPsId(c), c.getAuthor(),
|
||||||
|
c.getWrittenOn(), change.getCreatedOn(), c.getTag());
|
||||||
|
this.c = c;
|
||||||
|
this.change = change;
|
||||||
|
this.ps = ps;
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws OrmException {
|
||||||
|
checkUpdate(update);
|
||||||
|
if (c.getRevId() == null) {
|
||||||
|
setCommentRevId(c, cache, change, ps);
|
||||||
|
}
|
||||||
|
update.putComment(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyDraft(ChangeDraftUpdate draftUpdate) throws OrmException {
|
||||||
|
if (c.getRevId() == null) {
|
||||||
|
setCommentRevId(c, cache, change, ps);
|
||||||
|
}
|
||||||
|
draftUpdate.putComment(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.PatchSetState;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.InvalidObjectIdException;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class PatchSetEvent extends Event {
|
||||||
|
private final Change change;
|
||||||
|
private final PatchSet ps;
|
||||||
|
private final RevWalk rw;
|
||||||
|
boolean createChange;
|
||||||
|
|
||||||
|
PatchSetEvent(Change change, PatchSet ps, RevWalk rw) {
|
||||||
|
super(ps.getId(), ps.getUploader(), ps.getCreatedOn(),
|
||||||
|
change.getCreatedOn(), null);
|
||||||
|
this.change = change;
|
||||||
|
this.ps = ps;
|
||||||
|
this.rw = rw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws IOException, OrmException {
|
||||||
|
checkUpdate(update);
|
||||||
|
if (createChange) {
|
||||||
|
ChangeRebuilderImpl.createChange(update, change);
|
||||||
|
} else {
|
||||||
|
update.setSubject(change.getSubject());
|
||||||
|
update.setSubjectForCommit("Create patch set " + ps.getPatchSetId());
|
||||||
|
}
|
||||||
|
setRevision(update, ps);
|
||||||
|
List<String> groups = ps.getGroups();
|
||||||
|
if (!groups.isEmpty()) {
|
||||||
|
update.setGroups(ps.getGroups());
|
||||||
|
}
|
||||||
|
if (ps.isDraft()) {
|
||||||
|
update.setPatchSetState(PatchSetState.DRAFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRevision(ChangeUpdate update, PatchSet ps)
|
||||||
|
throws IOException {
|
||||||
|
String rev = ps.getRevision().get();
|
||||||
|
String cert = ps.getPushCertificate();
|
||||||
|
ObjectId id;
|
||||||
|
try {
|
||||||
|
id = ObjectId.fromString(rev);
|
||||||
|
} catch (InvalidObjectIdException e) {
|
||||||
|
update.setRevisionForMissingCommit(rev, cert);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
update.setCommit(rw, id, cert);
|
||||||
|
} catch (MissingObjectException e) {
|
||||||
|
update.setRevisionForMissingCommit(rev, cert);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (C) 2016 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.notedb.rebuild;
|
||||||
|
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gerrit.server.notedb.ReviewerStateInternal;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
class ReviewerEvent extends Event {
|
||||||
|
private Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer;
|
||||||
|
|
||||||
|
ReviewerEvent(
|
||||||
|
Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer,
|
||||||
|
Timestamp changeCreatedOn) {
|
||||||
|
super(
|
||||||
|
// Reviewers aren't generally associated with a particular patch set
|
||||||
|
// (although as an implementation detail they were in ReviewDb). Just
|
||||||
|
// use the latest patch set at the time of the event.
|
||||||
|
null,
|
||||||
|
reviewer.getColumnKey(), reviewer.getValue(), changeCreatedOn, null);
|
||||||
|
this.reviewer = reviewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean uniquePerUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(ChangeUpdate update) throws IOException, OrmException {
|
||||||
|
checkUpdate(update);
|
||||||
|
update.putReviewer(reviewer.getColumnKey(), reviewer.getRowKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ 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.reviewdb.client.RevId;
|
import com.google.gerrit.reviewdb.client.RevId;
|
||||||
import com.google.gerrit.server.ReviewerSet;
|
import com.google.gerrit.server.ReviewerSet;
|
||||||
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
|
||||||
import com.google.gerrit.testutil.TestChanges;
|
import com.google.gerrit.testutil.TestChanges;
|
||||||
import com.google.gerrit.testutil.TestTimeUtil;
|
import com.google.gerrit.testutil.TestTimeUtil;
|
||||||
import com.google.gwtorm.client.KeyUtil;
|
import com.google.gwtorm.client.KeyUtil;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import com.google.gerrit.server.PatchLineCommentsUtil;
|
|||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.notedb.ChangeBundle;
|
import com.google.gerrit.server.notedb.ChangeBundle;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.notedb.ChangeRebuilder;
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|||||||
Reference in New Issue
Block a user