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:
David Pursehouse 2016-09-17 08:04:35 +00:00 committed by Gerrit Code Review
commit 18d1dd1546
29 changed files with 813 additions and 525 deletions

View File

@ -59,10 +59,10 @@ import com.google.gerrit.server.git.RepoRefCache;
import com.google.gerrit.server.git.UpdateException;
import com.google.gerrit.server.notedb.ChangeBundle;
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.NoteDbUpdateManager;
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.NoteDbChecker;
import com.google.gerrit.testutil.NoteDbMode;

View File

@ -47,8 +47,8 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.index.DummyIndexModule;
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.rebuild.ChangeRebuilder;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;

View File

@ -20,8 +20,8 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.server.ReviewDb;
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.rebuild.ChangeRebuilder;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;

View File

@ -27,6 +27,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
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.inject.Inject;
import com.google.inject.Provider;

View File

@ -53,6 +53,7 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
import com.google.gwtorm.client.Column;
import com.google.gwtorm.server.OrmException;
@ -241,10 +242,7 @@ public class ChangeBundle {
checkColumns(Change.Id.class, 1);
checkColumns(Change.class,
1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18,
// TODO(dborowitz): It's potentially possible to compare noteDbState in
// the Change with the state implied by a ChangeNotes.
101);
1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18, 101);
checkColumns(ChangeMessage.Key.class, 1, 2);
checkColumns(ChangeMessage.class, 1, 2, 3, 4, 5, 6);
checkColumns(PatchSet.Id.class, 1, 2);

View File

@ -61,20 +61,21 @@ import java.util.Locale;
import java.util.Set;
public class ChangeNoteUtil {
static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
static final FooterKey FOOTER_LABEL = new FooterKey("Label");
static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
static final FooterKey FOOTER_STATUS = new FooterKey("Status");
static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
static final FooterKey FOOTER_SUBMISSION_ID = new FooterKey("Submission-id");
static final FooterKey FOOTER_SUBMITTED_WITH =
public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
public static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
public static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
public static final FooterKey FOOTER_LABEL = new FooterKey("Label");
public static final FooterKey FOOTER_PATCH_SET = new FooterKey("Patch-set");
public static final FooterKey FOOTER_STATUS = new FooterKey("Status");
public static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
public static final FooterKey FOOTER_SUBMISSION_ID =
new FooterKey("Submission-id");
public static final FooterKey FOOTER_SUBMITTED_WITH =
new FooterKey("Submitted-with");
static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
static final FooterKey FOOTER_TAG = new FooterKey("Tag");
public static final FooterKey FOOTER_TOPIC = new FooterKey("Topic");
public static final FooterKey FOOTER_TAG = new FooterKey("Tag");
private static final String AUTHOR = "Author";
private static final String BASE_PATCH_SET = "Base-for-patch-set";

View File

@ -52,6 +52,7 @@ import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.git.RefCache;
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.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;

View File

@ -284,7 +284,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.commitSubject = commitSubject;
}
void setSubject(String subject) {
public void setSubject(String subject) {
this.subject = subject;
}

View File

@ -31,6 +31,7 @@ import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.RepoRefCache;
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.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;

View File

@ -78,7 +78,7 @@ public class NoteDbChangeState {
}
@VisibleForTesting
static NoteDbChangeState parse(Change.Id id, String str) {
public static NoteDbChangeState parse(Change.Id id, String str) {
if (str == null) {
return null;
}
@ -204,7 +204,7 @@ public class NoteDbChangeState {
return id.get().equals(draftIds.get(accountId));
}
boolean isUpToDate(RefCache changeRepoRefs, RefCache draftsRepoRefs)
public boolean isUpToDate(RefCache changeRepoRefs, RefCache draftsRepoRefs)
throws IOException {
if (!isChangeUpToDate(changeRepoRefs)) {
return false;

View File

@ -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.server.ReviewDb;
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.name.Names;

View File

@ -119,10 +119,10 @@ public class NoteDbUpdateManager implements AutoCloseable {
@Nullable abstract NoteDbUpdateManager.StagedResult staged();
}
static class OpenRepo implements AutoCloseable {
final Repository repo;
final RevWalk rw;
final ChainedReceiveCommands cmds;
public static class OpenRepo implements AutoCloseable {
public final Repository repo;
public final RevWalk rw;
public final ChainedReceiveCommands cmds;
private final InMemoryInserter tempIns;
@Nullable private final ObjectInserter finalIns;
@ -143,7 +143,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
this.close = close;
}
Optional<ObjectId> getObjectId(String refName) throws IOException {
public Optional<ObjectId> getObjectId(String refName) throws IOException {
return cmds.get(refName);
}
@ -233,17 +233,17 @@ public class NoteDbUpdateManager implements AutoCloseable {
return this;
}
NoteDbUpdateManager setCheckExpectedState(boolean checkExpectedState) {
public NoteDbUpdateManager setCheckExpectedState(boolean checkExpectedState) {
this.checkExpectedState = checkExpectedState;
return this;
}
OpenRepo getChangeRepo() throws IOException {
public OpenRepo getChangeRepo() throws IOException {
initChangeRepo();
return changeRepo;
}
OpenRepo getAllUsersRepo() throws IOException {
public OpenRepo getAllUsersRepo() throws IOException {
initAllUsersRepo();
return allUsersRepo;
}

View File

@ -20,6 +20,8 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
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.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;

View File

@ -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");
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// 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.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.Project;
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.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;

View File

@ -12,19 +12,16 @@
// See the License for the specific language governing permissions and
// 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.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
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_PATCH_SET;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
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.config.AnonymousCowardName;
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.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.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
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.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@ -92,8 +94,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ChangeRebuilderImpl extends ChangeRebuilder {
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
* {@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
* 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 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
public Result rebuild(NoteDbUpdateManager manager,
ChangeBundle bundle) throws NoSuchChangeException, IOException,
@ -466,7 +448,7 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
ChangeUpdate update = updateFactory.create(
change,
events.getAccountId(),
events.newAuthorIdent(),
newAuthorIdent(events),
events.getWhen(),
labelNameComparator);
update.setAllowWriteToNewRef(true);
@ -488,7 +470,7 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
ChangeDraftUpdate update = draftUpdateFactory.create(
change,
events.getAccountId(),
events.newAuthorIdent(),
newAuthorIdent(events),
events.getWhen());
update.setPatchSetId(events.getPatchSetId());
for (PatchLineCommentEvent e : events) {
@ -498,6 +480,16 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
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,
NoteDbUpdateManager manager) throws IOException {
String refName = changeMetaRef(change.getId());
@ -591,470 +583,10 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
}
};
private abstract static 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();
}
}
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) {
static void createChange(ChangeUpdate update, Change change) {
update.setSubjectForCommit("Create change");
update.setChangeId(change.getKey().get());
update.setBranch(change.getDest().get());
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");
}
}
}
}

View File

@ -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()));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
}

View File

@ -39,6 +39,7 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId;
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.TestTimeUtil;
import com.google.gwtorm.client.KeyUtil;

View File

@ -27,7 +27,7 @@ import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeBundle;
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.Provider;
import com.google.inject.Singleton;