notedb: Rewrite comments to be keyed by RevId

Rather than maintain two separate maps, one for each side, use a
single multimap of SHA-1 (RevId) to PatchLineComment. Callers are
responsible for looking up the RevId they need (e.g. with the
PatchListCache), but in practice they rarely do.

Change-Id: I5597cc6c12a881e7cf7f3c786b9697b4f7b9749f
This commit is contained in:
Dave Borowitz
2015-04-29 09:21:13 -07:00
parent 1d7de014a2
commit 9d4c51ce49
13 changed files with 395 additions and 416 deletions

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.server;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
@@ -105,8 +107,7 @@ public class PatchLineCommentsUtil {
notes.load();
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getBaseComments().values());
comments.addAll(notes.getPatchSetComments().values());
comments.addAll(notes.getComments().values());
return sort(comments);
}
@@ -165,13 +166,7 @@ public class PatchLineCommentsUtil {
return sort(
db.patchComments().publishedByChangeFile(changeId, file).toList());
}
notes.load();
List<PatchLineComment> comments = Lists.newArrayList();
addCommentsOnFile(comments, notes.getBaseComments().values(), file);
addCommentsOnFile(comments, notes.getPatchSetComments().values(),
file);
return sort(comments);
return commentsOnFile(notes.load().getComments().values(), file);
}
public List<PatchLineComment> publishedByPatchSet(ReviewDb db,
@@ -180,11 +175,7 @@ public class PatchLineCommentsUtil {
return sort(
db.patchComments().publishedByPatchSet(psId).toList());
}
notes.load();
List<PatchLineComment> comments = new ArrayList<>();
comments.addAll(notes.getPatchSetComments().get(psId));
comments.addAll(notes.getBaseComments().get(psId));
return sort(comments);
return commentsOnPatchSet(notes.load().getComments().values(), psId);
}
public List<PatchLineComment> draftByPatchSetAuthor(ReviewDb db,
@@ -194,11 +185,8 @@ public class PatchLineCommentsUtil {
return sort(
db.patchComments().draftByPatchSetAuthor(psId, author).toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getDraftBaseComments(author).row(psId).values());
comments.addAll(notes.getDraftPsComments(author).row(psId).values());
return sort(comments);
return commentsOnPatchSet(
notes.load().getDraftComments(author).values(), psId);
}
public List<PatchLineComment> draftByChangeFileAuthor(ReviewDb db,
@@ -210,12 +198,8 @@ public class PatchLineCommentsUtil {
.draftByChangeFileAuthor(notes.getChangeId(), file, author)
.toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
addCommentsOnFile(comments, notes.getDraftBaseComments(author).values(),
file);
addCommentsOnFile(comments, notes.getDraftPsComments(author).values(),
file);
return sort(comments);
return commentsOnFile(
notes.load().getDraftComments(author).values(), file);
}
public List<PatchLineComment> draftByChangeAuthor(ReviewDb db,
@@ -225,8 +209,7 @@ public class PatchLineCommentsUtil {
return sort(db.patchComments().byChange(notes.getChangeId()).toList());
}
List<PatchLineComment> comments = Lists.newArrayList();
comments.addAll(notes.getDraftBaseComments(author).values());
comments.addAll(notes.getDraftPsComments(author).values());
comments.addAll(notes.getDraftComments(author).values());
return sort(comments);
}
@@ -236,9 +219,8 @@ public class PatchLineCommentsUtil {
return sort(db.patchComments().draftByAuthor(author).toList());
}
Set<String> refNames =
getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS);
// TODO(dborowitz): Just scan author space.
Set<String> refNames = getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS);
List<PatchLineComment> comments = Lists.newArrayList();
for (String refName : refNames) {
Account.Id id = Account.Id.fromRefPart(refName);
@@ -246,10 +228,8 @@ public class PatchLineCommentsUtil {
continue;
}
Change.Id changeId = Change.Id.parse(refName);
DraftCommentNotes draftNotes =
draftFactory.create(changeId, author).load();
comments.addAll(draftNotes.getDraftBaseComments().values());
comments.addAll(draftNotes.getDraftPsComments().values());
comments.addAll(
draftFactory.create(changeId, author).load().getComments().values());
}
return sort(comments);
}
@@ -286,33 +266,45 @@ public class PatchLineCommentsUtil {
db.patchComments().delete(comments);
}
private static Collection<PatchLineComment> addCommentsOnFile(
Collection<PatchLineComment> commentsOnFile,
private static List<PatchLineComment> commentsOnFile(
Collection<PatchLineComment> allComments,
String file) {
List<PatchLineComment> result = new ArrayList<>(allComments.size());
for (PatchLineComment c : allComments) {
String currentFilename = c.getKey().getParentKey().getFileName();
if (currentFilename.equals(file)) {
commentsOnFile.add(c);
result.add(c);
}
}
return commentsOnFile;
return sort(result);
}
public static void setCommentRevId(PatchLineComment c,
private static List<PatchLineComment> commentsOnPatchSet(
Collection<PatchLineComment> allComments,
PatchSet.Id psId) {
List<PatchLineComment> result = new ArrayList<>(allComments.size());
for (PatchLineComment c : allComments) {
if (getCommentPsId(c).equals(psId)) {
result.add(c);
}
}
return sort(result);
}
public static RevId setCommentRevId(PatchLineComment c,
PatchListCache cache, Change change, PatchSet ps) throws OrmException {
if (c.getRevId() != null) {
return;
}
PatchList patchList;
if (c.getRevId() == null) {
try {
patchList = cache.get(change, ps);
} catch (PatchListNotAvailableException e) {
throw new OrmException(e);
}
// TODO(dborowitz): Bypass cache if side is REVISION.
PatchList patchList = cache.get(change, ps);
c.setRevId((c.getSide() == (short) 0)
? new RevId(ObjectId.toString(patchList.getOldId()))
: new RevId(ObjectId.toString(patchList.getNewId())));
} catch (PatchListNotAvailableException e) {
throw new OrmException(e);
}
}
return c.getRevId();
}
private Set<String> getRefNamesAllUsers(String prefix) throws OrmException {

View File

@@ -370,8 +370,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
e.setStatus(PatchLineComment.Status.PUBLISHED);
e.setWrittenOn(timestamp);
e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1);
setCommentRevId(e, patchListCache, rsrc.getChange(),
rsrc.getPatchSet());
setCommentRevId(e, patchListCache, rsrc.getChange(), rsrc.getPatchSet());
e.setMessage(c.message);
if (c.range != null) {
e.setRange(new CommentRange(
@@ -396,8 +395,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
for (PatchLineComment e : drafts.values()) {
e.setStatus(PatchLineComment.Status.PUBLISHED);
e.setWrittenOn(timestamp);
setCommentRevId(e, patchListCache, rsrc.getChange(),
rsrc.getPatchSet());
setCommentRevId(e, patchListCache, rsrc.getChange(), rsrc.getPatchSet());
ups.add(e);
}
break;

View File

@@ -96,8 +96,7 @@ public class PutDraftComment implements RestModifyView<DraftCommentResource, Dra
Collections.singleton(update(c, in)));
} else {
if (c.getRevId() == null) {
setCommentRevId(c, patchListCache, rsrc.getChange(),
rsrc.getPatchSet());
setCommentRevId(c, patchListCache, rsrc.getChange(), rsrc.getPatchSet());
}
plcUtil.updateComments(db.get(), update,
Collections.singleton(update(c, in)));

View File

@@ -16,14 +16,15 @@ package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.addCommentToMap;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
@@ -46,8 +47,12 @@ import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -190,76 +195,55 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
noteMap = NoteMap.newEmptyMap();
}
Table<PatchSet.Id, String, PatchLineComment> baseDrafts =
draftNotes.getDraftBaseComments();
Table<PatchSet.Id, String, PatchLineComment> psDrafts =
draftNotes.getDraftPsComments();
boolean draftsEmpty = baseDrafts.isEmpty() && psDrafts.isEmpty();
// There is no need to rewrite the note for one of the sides of the patch
// set if all of the modifications were made to the comments of one side,
// so we set these flags to potentially save that extra work.
boolean baseSideChanged = false;
boolean revisionSideChanged = false;
// We must define these RevIds so that if this update deletes all
// remaining comments on a given side, then we can remove that note.
// However, if this update doesn't delete any comments, it is okay for these
// to be null because they won't be used.
RevId baseRevId = null;
RevId psRevId = null;
Map<RevId, List<PatchLineComment>> allComments = new HashMap<>();
boolean hasComments = false;
int n = deleteComments.size() + upsertComments.size();
Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(n);
Set<PatchLineComment.Key> updatedKeys = Sets.newHashSetWithExpectedSize(n);
for (PatchLineComment c : deleteComments) {
if (c.getSide() == (short) 0) {
baseSideChanged = true;
baseRevId = c.getRevId();
baseDrafts.remove(psId, c.getKey().get());
} else {
revisionSideChanged = true;
psRevId = c.getRevId();
psDrafts.remove(psId, c.getKey().get());
}
allComments.put(c.getRevId(), new ArrayList<PatchLineComment>());
updatedRevs.add(c.getRevId());
updatedKeys.add(c.getKey());
}
for (PatchLineComment c : upsertComments) {
if (c.getSide() == (short) 0) {
baseSideChanged = true;
baseDrafts.put(psId, c.getKey().get(), c);
} else {
revisionSideChanged = true;
psDrafts.put(psId, c.getKey().get(), c);
hasComments = true;
addCommentToMap(allComments, c);
updatedRevs.add(c.getRevId());
updatedKeys.add(c.getKey());
}
// Re-add old comments for updated revisions so the new note contents
// includes both old and new comments merged in the right order.
//
// writeCommentsToNoteMap doesn't touch notes for SHA-1s that are not
// mentioned in the input map, so by omitting comments for those revisions,
// we avoid the work of having to re-serialize identical comment data for
// those revisions.
ListMultimap<RevId, PatchLineComment> existing =
draftNotes.getComments();
for (Map.Entry<RevId, PatchLineComment> e : existing.entries()) {
PatchLineComment c = e.getValue();
if (updatedRevs.contains(c.getRevId())
&& !updatedKeys.contains(c.getKey())) {
hasComments = true;
addCommentToMap(allComments, e.getValue());
}
}
List<PatchLineComment> newBaseDrafts =
Lists.newArrayList(baseDrafts.row(psId).values());
List<PatchLineComment> newPsDrafts =
Lists.newArrayList(psDrafts.row(psId).values());
updateNoteMap(baseSideChanged, noteMap, newBaseDrafts,
baseRevId);
updateNoteMap(revisionSideChanged, noteMap, newPsDrafts,
psRevId);
removedAllComments.set(
baseDrafts.isEmpty() && psDrafts.isEmpty() && !draftsEmpty);
// If we touched every revision and there are no comments left, set the flag
// for the caller to delete the entire ref.
boolean touchedAllRevs = updatedRevs.equals(existing.keySet());
if (touchedAllRevs && !hasComments) {
removedAllComments.set(touchedAllRevs && !hasComments);
return null;
}
commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter);
return noteMap.writeTree(inserter);
}
private void updateNoteMap(boolean changed, NoteMap noteMap,
List<PatchLineComment> comments, RevId commitId)
throws IOException {
if (changed) {
if (comments.isEmpty()) {
commentsUtil.removeNote(noteMap, commitId);
} else {
commentsUtil.writeCommentsToNoteMap(noteMap, comments, inserter);
}
}
}
public RevCommit commit() throws IOException {
BatchMetaDataUpdate batch = openUpdate();
try {
@@ -279,16 +263,14 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
if (migration.writeChanges()) {
AtomicBoolean removedAllComments = new AtomicBoolean();
ObjectId treeId = storeCommentsInNotes(removedAllComments);
if (treeId != null) {
if (removedAllComments.get()) {
batch.removeRef(getRefName());
} else {
} else if (treeId != null) {
builder.setTreeId(treeId);
batch.write(builder);
}
}
}
}
@Override
protected Project.NameKey getProjectName() {

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
@@ -26,7 +25,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.collect.Table;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account;
@@ -36,6 +34,7 @@ import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
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.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -53,7 +52,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/** View of a single {@link Change} based on the log of its notes branch. */
@@ -138,8 +136,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
private ImmutableList<Account.Id> allPastReviewers;
private ImmutableList<SubmitRecord> submitRecords;
private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS;
private ImmutableListMultimap<RevId, PatchLineComment> comments;
private ImmutableSet<String> hashtags;
NoteMap noteMap;
@@ -194,28 +191,15 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return changeMessages;
}
/** @return inline comments on each patchset's base (side == 0). */
public ImmutableListMultimap<PatchSet.Id, PatchLineComment>
getBaseComments() {
return commentsForBase;
/** @return inline comments on each revision. */
public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
return comments;
}
/** @return inline comments on each patchset (side == 1). */
public ImmutableListMultimap<PatchSet.Id, PatchLineComment>
getPatchSetComments() {
return commentsForPS;
}
public Table<PatchSet.Id, String, PatchLineComment> getDraftBaseComments(
public ImmutableListMultimap<RevId, PatchLineComment> getDraftComments(
Account.Id author) throws OrmException {
loadDraftComments(author);
return draftCommentNotes.getDraftBaseComments();
}
public Table<PatchSet.Id, String, PatchLineComment> getDraftPsComments(
Account.Id author) throws OrmException {
loadDraftComments(author);
return draftCommentNotes.getDraftPsComments();
return draftCommentNotes.getComments();
}
/**
@@ -234,6 +218,11 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
}
@VisibleForTesting
DraftCommentNotes getDraftCommentNotes() {
return draftCommentNotes;
}
public boolean containsComment(PatchLineComment c) throws OrmException {
if (containsCommentPublished(c)) {
return true;
@@ -243,11 +232,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
public boolean containsCommentPublished(PatchLineComment c) {
PatchSet.Id psId = getCommentPsId(c);
List<PatchLineComment> list = (c.getSide() == (short) 0)
? getBaseComments().get(psId)
: getPatchSetComments().get(psId);
for (PatchLineComment l : list) {
for (PatchLineComment l : getComments().values()) {
if (c.getKey().equals(l.getKey())) {
return true;
}
@@ -282,8 +267,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
approvals = parser.buildApprovals();
changeMessages = parser.buildMessages();
commentsForBase = ImmutableListMultimap.copyOf(parser.commentsForBase);
commentsForPS = ImmutableListMultimap.copyOf(parser.commentsForPs);
comments = ImmutableListMultimap.copyOf(parser.comments);
noteMap = parser.commentNoteMap;
if (parser.hashtags != null) {
@@ -310,8 +294,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
reviewers = ImmutableSetMultimap.of();
submitRecords = ImmutableList.of();
changeMessages = ImmutableListMultimap.of();
commentsForBase = ImmutableListMultimap.of();
commentsForPS = ImmutableListMultimap.of();
comments = ImmutableListMultimap.of();
hashtags = ImmutableSet.of();
}

View File

@@ -44,6 +44,7 @@ import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.util.LabelVote;
@@ -72,8 +73,7 @@ class ChangeNotesParser implements AutoCloseable {
final Map<Account.Id, ReviewerState> reviewers;
final List<Account.Id> allPastReviewers;
final List<SubmitRecord> submitRecords;
final Multimap<PatchSet.Id, PatchLineComment> commentsForPs;
final Multimap<PatchSet.Id, PatchLineComment> commentsForBase;
final Multimap<RevId, PatchLineComment> comments;
NoteMap commentNoteMap;
Change.Status status;
Set<String> hashtags;
@@ -99,8 +99,7 @@ class ChangeNotesParser implements AutoCloseable {
allPastReviewers = Lists.newArrayList();
submitRecords = Lists.newArrayListWithExpectedSize(1);
changeMessages = LinkedListMultimap.create();
commentsForPs = ArrayListMultimap.create();
commentsForBase = ArrayListMultimap.create();
comments = ArrayListMultimap.create();
}
@Override
@@ -275,7 +274,7 @@ class ChangeNotesParser implements AutoCloseable {
throws IOException, ConfigInvalidException {
commentNoteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
ChangeNoteUtil.changeRefName(changeId), walk, changeId,
commentsForBase, commentsForPs, PatchLineComment.Status.PUBLISHED);
comments, PatchLineComment.Status.PUBLISHED);
}
private void parseApproval(PatchSet.Id psId, Account.Id accountId,

View File

@@ -20,6 +20,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.addCommentToMap;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting;
@@ -28,14 +29,13 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AnonymousCowardName;
@@ -57,6 +57,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -91,8 +92,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private String subject;
private List<SubmitRecord> submitRecords;
private final CommentsInNotesUtil commentsUtil;
private List<PatchLineComment> commentsForBase;
private List<PatchLineComment> commentsForPs;
private List<PatchLineComment> comments;
private Set<String> hashtags;
private String changeMessage;
private ChangeNotes notes;
@@ -161,8 +161,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.commentsUtil = commentsUtil;
this.approvals = Maps.newTreeMap(labelNameComparator);
this.reviewers = Maps.newLinkedHashMap();
this.commentsForPs = Lists.newArrayList();
this.commentsForBase = Lists.newArrayList();
this.comments = Lists.newArrayList();
}
public void setStatus(Change.Status status) {
@@ -238,11 +237,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
"A comment already exists with the same key as the following comment,"
+ " so we cannot insert this comment: %s", c);
}
if (c.getSide() == 0) {
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
comments.add(c);
}
private void insertDraftComment(PatchLineComment c) throws OrmException {
@@ -263,11 +258,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
checkArgument(!notes.containsCommentPublished(c),
"Cannot update a comment that has already been published and saved");
}
if (c.getSide() == 0) {
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
comments.add(c);
}
private void upsertDraftComment(PatchLineComment c) {
@@ -286,11 +277,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
checkArgument(!notes.containsCommentPublished(c),
"Cannot update a comment that has already been published and saved");
}
if (c.getSide() == 0) {
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
comments.add(c);
}
private void updateDraftComment(PatchLineComment c) throws OrmException {
@@ -356,31 +343,23 @@ public class ChangeUpdate extends AbstractChangeUpdate {
if (noteMap == null) {
noteMap = NoteMap.newEmptyMap();
}
if (commentsForPs.isEmpty() && commentsForBase.isEmpty()) {
if (comments.isEmpty()) {
return null;
}
Multimap<PatchSet.Id, PatchLineComment> allCommentsOnBases =
notes.getBaseComments();
Multimap<PatchSet.Id, PatchLineComment> allCommentsOnPs =
notes.getPatchSetComments();
// This writes all comments for the base of this PS to the note map.
if (!commentsForBase.isEmpty()) {
List<PatchLineComment> baseCommentsForThisPs =
new ArrayList<>(allCommentsOnBases.get(psId));
baseCommentsForThisPs.addAll(commentsForBase);
commentsUtil.writeCommentsToNoteMap(noteMap, baseCommentsForThisPs,
inserter);
Map<RevId, List<PatchLineComment>> allComments = Maps.newHashMap();
for (Map.Entry<RevId, Collection<PatchLineComment>> e
: notes.getComments().asMap().entrySet()) {
List<PatchLineComment> comments = new ArrayList<>();
for (PatchLineComment c : e.getValue()) {
comments.add(c);
}
// This write all comments for this PS to the note map.
if (!commentsForPs.isEmpty()) {
List<PatchLineComment> commentsForThisPs =
new ArrayList<>(allCommentsOnPs.get(psId));
commentsForThisPs.addAll(commentsForPs);
commentsUtil.writeCommentsToNoteMap(noteMap, commentsForThisPs, inserter);
allComments.put(e.getKey(), comments);
}
for (PatchLineComment c : comments) {
addCommentToMap(allComments, c);
}
commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter);
return noteMap.writeTree(inserter);
}
@@ -504,8 +483,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private boolean isEmpty() {
return approvals.isEmpty()
&& changeMessage == null
&& commentsForBase.isEmpty()
&& commentsForPs.isEmpty()
&& comments.isEmpty()
&& reviewers.isEmpty()
&& status == null
&& subject == null

View File

@@ -63,9 +63,11 @@ import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Utility functions to parse PatchLineComments out of a note byte array and
@@ -86,8 +88,7 @@ public class CommentsInNotesUtil {
public static NoteMap parseCommentsFromNotes(Repository repo, String refName,
RevWalk walk, Change.Id changeId,
Multimap<PatchSet.Id, PatchLineComment> commentsForBase,
Multimap<PatchSet.Id, PatchLineComment> commentsForPs,
Multimap<RevId, PatchLineComment> comments,
Status status)
throws IOException, ConfigInvalidException {
Ref ref = repo.getRef(refName);
@@ -99,20 +100,14 @@ public class CommentsInNotesUtil {
RevCommit commit = walk.parseCommit(ref.getObjectId());
NoteMap noteMap = NoteMap.read(reader, commit);
for (Note note: noteMap) {
for (Note note : noteMap) {
byte[] bytes =
reader.open(note.getData(), OBJ_BLOB).getCachedBytes(MAX_NOTE_SZ);
List<PatchLineComment> result = parseNote(bytes, changeId, status);
if (result == null || result.isEmpty()) {
continue;
}
PatchSet.Id psId = result.get(0).getKey().getParentKey().getParentKey();
short side = result.get(0).getSide();
if (side == 0) {
commentsForBase.putAll(psId, result);
} else {
commentsForPs.putAll(psId, result);
}
comments.putAll(new RevId(note.name()), result);
}
return noteMap;
}
@@ -524,19 +519,47 @@ public class CommentsInNotesUtil {
return buf.toByteArray();
}
/**
* Write comments for multiple revisions to a note map.
* <p>
* Mutates the map in-place. only notes for SHA-1s found as keys in the map
* are modified; all other notes are left untouched.
*
* @param noteMap note map to modify.
* @param allComments map of revision to all comments for that revision;
* callers are responsible for reading the original comments and applying
* any changes. Differs from a multimap in that present-but-empty values
* are significant, and indicate the note for that SHA-1 should be
* deleted.
* @param inserter object inserter for writing notes.
* @throws IOException if an error occurred.
*/
public void writeCommentsToNoteMap(NoteMap noteMap,
List<PatchLineComment> allComments, ObjectInserter inserter)
Map<RevId, List<PatchLineComment>> allComments, ObjectInserter inserter)
throws IOException {
checkArgument(!allComments.isEmpty(),
"No comments to write; to delete, use removeNoteFromNoteMap().");
ObjectId commit =
ObjectId.fromString(allComments.get(0).getRevId().get());
Collections.sort(allComments, ChangeNotes.PLC_ORDER);
noteMap.set(commit, inserter.insert(OBJ_BLOB, buildNote(allComments)));
for (Map.Entry<RevId, List<PatchLineComment>> e : allComments.entrySet()) {
List<PatchLineComment> comments = e.getValue();
ObjectId commit = ObjectId.fromString(e.getKey().get());
if (comments.isEmpty()) {
noteMap.remove(commit);
continue;
}
Collections.sort(comments, ChangeNotes.PLC_ORDER);
// We allow comments for multiple commits to be written in the same
// update, even though the rest of the metadata update is associated with
// a single patch set.
noteMap.set(commit, inserter.insert(OBJ_BLOB, buildNote(comments)));
}
}
public void removeNote(NoteMap noteMap, RevId commitId)
throws IOException {
noteMap.remove(ObjectId.fromString(commitId.get()));
static void addCommentToMap(Map<RevId, List<PatchLineComment>> map,
PatchLineComment c) {
List<PatchLineComment> list = map.get(c.getRevId());
if (list == null) {
list = new ArrayList<>();
map.put(c.getRevId(), list);
}
list.add(c);
}
}

View File

@@ -14,18 +14,14 @@
package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.reviewdb.client.Account;
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.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -70,8 +66,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
private final AllUsersName draftsProject;
private final Account.Id author;
private final Table<PatchSet.Id, String, PatchLineComment> draftBaseComments;
private final Table<PatchSet.Id, String, PatchLineComment> draftPsComments;
private ImmutableListMultimap<RevId, PatchLineComment> comments;
private NoteMap noteMap;
DraftCommentNotes(GitRepositoryManager repoManager, NotesMigration migration,
@@ -79,9 +74,6 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
super(repoManager, migration, changeId);
this.draftsProject = draftsProject;
this.author = author;
this.draftBaseComments = HashBasedTable.create();
this.draftPsComments = HashBasedTable.create();
}
public NoteMap getNoteMap() {
@@ -92,32 +84,18 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
return author;
}
/**
* @return a defensive copy of the table containing all draft comments
* on this change with side == 0. The row key is the comment's PatchSet.Id,
* the column key is the comment's UUID, and the value is the comment.
*/
public Table<PatchSet.Id, String, PatchLineComment>
getDraftBaseComments() {
return HashBasedTable.create(draftBaseComments);
}
/**
* @return a defensive copy of the table containing all draft comments
* on this change with side == 1. The row key is the comment's PatchSet.Id,
* the column key is the comment's UUID, and the value is the comment.
*/
public Table<PatchSet.Id, String, PatchLineComment>
getDraftPsComments() {
return HashBasedTable.create(draftPsComments);
public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
// TODO(dborowitz): Defensive copy?
return comments;
}
public boolean containsComment(PatchLineComment c) {
Table<PatchSet.Id, String, PatchLineComment> t =
c.getSide() == (short) 0
? getDraftBaseComments()
: getDraftPsComments();
return t.contains(getCommentPsId(c), c.getKey().get());
for (PatchLineComment existing : comments.values()) {
if (c.getKey().equals(existing.getKey())) {
return true;
}
}
return false;
}
@Override
@@ -129,6 +107,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
protected void onLoad() throws IOException, ConfigInvalidException {
ObjectId rev = getRevision();
if (rev == null) {
loadDefaults();
return;
}
@@ -137,8 +116,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
getChangeId(), walk, rev, repoManager, draftsProject, author)) {
parser.parseDraftComments();
buildCommentTable(draftBaseComments, parser.draftBaseComments);
buildCommentTable(draftPsComments, parser.draftPsComments);
comments = ImmutableListMultimap.copyOf(parser.comments);
noteMap = parser.noteMap;
}
}
@@ -152,20 +130,11 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
@Override
protected void loadDefaults() {
// Do nothing; tables are final and initialized in constructor.
comments = ImmutableListMultimap.of();
}
@Override
protected Project.NameKey getProjectName() {
return draftsProject;
}
private void buildCommentTable(
Table<PatchSet.Id, String, PatchLineComment> commentTable,
Multimap<PatchSet.Id, PatchLineComment> allComments) {
for (PatchLineComment c : allComments.values()) {
commentTable.put(getCommentPsId(c), c.getKey().get(), c);
}
}
}

View File

@@ -19,8 +19,8 @@ import com.google.common.collect.Multimap;
import com.google.gerrit.reviewdb.client.Account;
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.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -34,8 +34,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
class DraftCommentNotesParser implements AutoCloseable {
final Multimap<PatchSet.Id, PatchLineComment> draftBaseComments;
final Multimap<PatchSet.Id, PatchLineComment> draftPsComments;
final Multimap<RevId, PatchLineComment> comments;
NoteMap noteMap;
private final Change.Id changeId;
@@ -53,8 +52,7 @@ class DraftCommentNotesParser implements AutoCloseable {
this.repo = repoManager.openMetadataRepository(draftsProject);
this.author = author;
draftBaseComments = ArrayListMultimap.create();
draftPsComments = ArrayListMultimap.create();
comments = ArrayListMultimap.create();
}
@Override
@@ -66,7 +64,6 @@ class DraftCommentNotesParser implements AutoCloseable {
walk.markStart(walk.parseCommit(tip));
noteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
RefNames.refsDraftComments(author, changeId),
walk, changeId, draftBaseComments,
draftPsComments, PatchLineComment.Status.DRAFT);
walk, changeId, comments, PatchLineComment.Status.DRAFT);
}
}

View File

@@ -234,23 +234,23 @@ public class CommentsTest {
plc1 = newPatchLineComment(psId1, "Comment1", null,
"FileOne.txt", Side.REVISION, 3, ownerId, timeBase,
"First Comment", new CommentRange(1, 2, 3, 4));
plc1.setRevId(new RevId("ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD"));
plc1.setRevId(new RevId("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"));
plc2 = newPatchLineComment(psId1, "Comment2", "Comment1",
"FileOne.txt", Side.REVISION, 3, otherUserId, timeBase + 1000,
"Reply to First Comment", new CommentRange(1, 2, 3, 4));
plc2.setRevId(new RevId("ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD"));
plc2.setRevId(new RevId("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"));
plc3 = newPatchLineComment(psId1, "Comment3", "Comment1",
"FileOne.txt", Side.PARENT, 3, ownerId, timeBase + 2000,
"First Parent Comment", new CommentRange(1, 2, 3, 4));
plc3.setRevId(new RevId("CDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEF"));
plc3.setRevId(new RevId("cdefcdefcdefcdefcdefcdefcdefcdefcdefcdef"));
plc4 = newPatchLineComment(psId2, "Comment4", null, "FileOne.txt",
Side.REVISION, 3, ownerId, timeBase + 3000, "Second Comment",
new CommentRange(1, 2, 3, 4), Status.DRAFT);
plc4.setRevId(new RevId("BCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDE"));
plc4.setRevId(new RevId("bcdebcdebcdebcdebcdebcdebcdebcdebcdebcde"));
plc5 = newPatchLineComment(psId2, "Comment5", null, "FileOne.txt",
Side.REVISION, 5, ownerId, timeBase + 4000, "Third Comment",
new CommentRange(3, 4, 5, 6), Status.DRAFT);
plc5.setRevId(new RevId("BCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDE"));
plc5.setRevId(new RevId("bcdebcdebcdebcdebcdebcdebcdebcdebcdebcde"));
List<PatchLineComment> commentsByOwner = Lists.newArrayList();
commentsByOwner.add(plc1);

View File

@@ -203,16 +203,16 @@ public class AbstractChangeNotesTest {
return label;
}
protected PatchLineComment newPublishedPatchLineComment(PatchSet.Id psId,
protected PatchLineComment newPublishedComment(PatchSet.Id psId,
String filename, String UUID, CommentRange range, int line,
IdentifiedUser commenter, String parentUUID, Timestamp t,
String message, short side, String commitSHA1) {
return newPatchLineComment(psId, filename, UUID, range, line, commenter,
return newComment(psId, filename, UUID, range, line, commenter,
parentUUID, t, message, side, commitSHA1,
PatchLineComment.Status.PUBLISHED);
}
protected PatchLineComment newPatchLineComment(PatchSet.Id psId,
protected PatchLineComment newComment(PatchSet.Id psId,
String filename, String UUID, CommentRange range, int line,
IdentifiedUser commenter, String parentUUID, Timestamp t,
String message, short side, String commitSHA1,

View File

@@ -22,12 +22,11 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account;
@@ -38,8 +37,8 @@ import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
import com.google.gwtorm.server.OrmException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -47,12 +46,12 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.notes.Note;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.Test;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.LinkedHashSet;
@@ -424,7 +423,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
PatchSet.Id psId = c.currentPatchSetId();
BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
BatchMetaDataUpdate batch = update1.openUpdateInBatch(bru);
PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1",
PatchLineComment comment1 = newPublishedComment(psId, "file1",
uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update1.setPatchSetId(psId);
@@ -451,7 +450,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals1 =
notesWithComments.buildApprovals();
assertThat(approvals1).isEmpty();
assertThat(notesWithComments.commentsForBase).hasSize(1);
assertThat(notesWithComments.comments).hasSize(1);
notesWithComments.close();
ChangeNotesParser notesWithApprovals =
@@ -460,7 +459,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals2 =
notesWithApprovals.buildApprovals();
assertThat(approvals2).hasSize(1);
assertThat(notesWithApprovals.commentsForBase).hasSize(1);
assertThat(notesWithApprovals.comments).hasSize(1);
notesWithApprovals.close();
} finally {
batch.close();
@@ -674,7 +673,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Timestamp time3 = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1",
PatchLineComment comment1 = newPublishedComment(psId, "file1",
uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update.setPatchSetId(psId);
@@ -683,7 +682,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
CommentRange range2 = new CommentRange(2, 1, 3, 1);
PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1",
PatchLineComment comment2 = newPublishedComment(psId, "file1",
uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2,
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update.setPatchSetId(psId);
@@ -692,7 +691,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
CommentRange range3 = new CommentRange(3, 1, 4, 1);
PatchLineComment comment3 = newPublishedPatchLineComment(psId, "file2",
PatchLineComment comment3 = newPublishedComment(psId, "file2",
uuid3, range3, range3.getEndLine(), otherUser, null, time3, message3,
(short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update.setPatchSetId(psId);
@@ -753,7 +752,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Timestamp time2 = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1",
PatchLineComment comment1 = newPublishedComment(psId, "file1",
uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update.setPatchSetId(psId);
@@ -762,7 +761,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
CommentRange range2 = new CommentRange(2, 1, 3, 1);
PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1",
PatchLineComment comment2 = newPublishedComment(psId, "file1",
uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
update.setPatchSetId(psId);
@@ -808,6 +807,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
String uuid1 = "uuid1";
String uuid2 = "uuid2";
String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
String messageForBase = "comment for base";
String messageForPS = "comment for ps";
CommentRange range = new CommentRange(1, 1, 2, 1);
@@ -815,32 +816,26 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
PatchSet.Id psId = c.currentPatchSetId();
PatchLineComment commentForBase =
newPublishedPatchLineComment(psId, "filename", uuid1,
newPublishedComment(psId, "filename", uuid1,
range, range.getEndLine(), otherUser, null, now, messageForBase,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
(short) 0, rev1);
update.setPatchSetId(psId);
update.upsertComment(commentForBase);
update.commit();
update = newUpdate(c, otherUser);
PatchLineComment commentForPS =
newPublishedPatchLineComment(psId, "filename", uuid2,
newPublishedComment(psId, "filename", uuid2,
range, range.getEndLine(), otherUser, null, now, messageForPS,
(short) 1, "abcd4567abcd4567abcd4567abcd4567abcd4567");
(short) 1, rev2);
update.setPatchSetId(psId);
update.upsertComment(commentForPS);
update.commit();
ChangeNotes notes = newNotes(c);
Multimap<PatchSet.Id, PatchLineComment> commentsForBase =
notes.getBaseComments();
Multimap<PatchSet.Id, PatchLineComment> commentsForPS =
notes.getPatchSetComments();
assertThat(commentsForBase).hasSize(1);
assertThat(commentsForPS).hasSize(1);
assertThat(commentsForBase.get(psId)).containsExactly(commentForBase);
assertThat(commentsForPS.get(psId)).containsExactly(commentForPS);
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(
new RevId(rev1), commentForBase,
new RevId(rev2), commentForPS));
}
@Test
@@ -848,6 +843,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename = "filename";
@@ -856,31 +852,25 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp timeForComment1 = TimeUtil.nowTs();
Timestamp timeForComment2 = TimeUtil.nowTs();
PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename,
PatchLineComment comment1 = newPublishedComment(psId, filename,
uuid1, range, range.getEndLine(), otherUser, null, timeForComment1,
"comment 1", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
"comment 1", side, rev);
update.setPatchSetId(psId);
update.upsertComment(comment1);
update.commit();
update = newUpdate(c, otherUser);
PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename,
PatchLineComment comment2 = newPublishedComment(psId, filename,
uuid2, range, range.getEndLine(), otherUser, null, timeForComment2,
"comment 2", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
"comment 2", side, rev);
update.setPatchSetId(psId);
update.upsertComment(comment2);
update.commit();
ChangeNotes notes = newNotes(c);
Multimap<PatchSet.Id, PatchLineComment> commentsForBase =
notes.getBaseComments();
Multimap<PatchSet.Id, PatchLineComment> commentsForPS =
notes.getPatchSetComments();
assertThat(commentsForBase).isEmpty();
assertThat(commentsForPS).hasSize(2);
assertThat(commentsForPS.get(psId))
.containsExactly(comment1, comment2).inOrder();
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(
new RevId(rev), comment1,
new RevId(rev), comment2)).inOrder();
}
@Test
@@ -888,6 +878,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
throws Exception {
Change c = newChange();
String uuid = "uuid";
String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename1 = "filename1";
@@ -896,37 +887,33 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp now = TimeUtil.nowTs();
PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename1,
PatchLineComment comment1 = newPublishedComment(psId, filename1,
uuid, range, range.getEndLine(), otherUser, null, now, "comment 1",
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
side, rev);
update.setPatchSetId(psId);
update.upsertComment(comment1);
update.commit();
update = newUpdate(c, otherUser);
PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename2,
PatchLineComment comment2 = newPublishedComment(psId, filename2,
uuid, range, range.getEndLine(), otherUser, null, now, "comment 2",
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
side, rev);
update.setPatchSetId(psId);
update.upsertComment(comment2);
update.commit();
ChangeNotes notes = newNotes(c);
ListMultimap<PatchSet.Id, PatchLineComment> commentsForBase =
notes.getBaseComments();
ListMultimap<PatchSet.Id, PatchLineComment> commentsForPS =
notes.getPatchSetComments();
assertThat(commentsForBase).isEmpty();
assertThat(commentsForPS).hasSize(2);
assertThat(commentsForPS.get(psId))
.containsExactly(comment1, comment2).inOrder();
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(
new RevId(rev), comment1,
new RevId(rev), comment2)).inOrder();
}
@Test
public void patchLineCommentMultiplePatchsets() throws Exception {
Change c = newChange();
String uuid = "uuid";
String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -934,9 +921,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp now = TimeUtil.nowTs();
PatchLineComment comment1 = newPublishedPatchLineComment(ps1, filename,
PatchLineComment comment1 = newPublishedComment(ps1, filename,
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
side, rev1);
update.setPatchSetId(ps1);
update.upsertComment(comment1);
update.commit();
@@ -946,31 +933,24 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
now = TimeUtil.nowTs();
PatchLineComment comment2 = newPublishedPatchLineComment(ps2, filename,
PatchLineComment comment2 = newPublishedComment(ps2, filename,
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
side, "abcd4567abcd4567abcd4567abcd4567abcd4567");
side, rev2);
update.setPatchSetId(ps2);
update.upsertComment(comment2);
update.commit();
ChangeNotes notes = newNotes(c);
LinkedListMultimap<PatchSet.Id, PatchLineComment> commentsForBase =
LinkedListMultimap.create(notes.getBaseComments());
LinkedListMultimap<PatchSet.Id, PatchLineComment> commentsForPS =
LinkedListMultimap.create(notes.getPatchSetComments());
assertThat(commentsForBase).isEmpty();
assertThat(commentsForPS).hasSize(2);
assertThat(commentsForPS).containsExactly(
ImmutableListMultimap.of(
ps1, comment1,
ps2, comment2));
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(
new RevId(rev1), comment1,
new RevId(rev2), comment2));
}
@Test
public void patchLineCommentSingleDraftToPublished() throws Exception {
Change c = newChange();
String uuid = "uuid";
String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -978,16 +958,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp now = TimeUtil.nowTs();
PatchLineComment comment1 = newPatchLineComment(ps1, filename, uuid,
range, range.getEndLine(), otherUser, null, now, "comment on ps1", side,
"abcd4567abcd4567abcd4567abcd4567abcd4567", Status.DRAFT);
PatchLineComment comment1 = newComment(ps1, filename, uuid, range,
range.getEndLine(), otherUser, null, now, "comment on ps1", side,
rev, Status.DRAFT);
update.setPatchSetId(ps1);
update.insertComment(comment1);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftPsComments(otherUserId)).hasSize(1);
assertThat(notes.getDraftBaseComments(otherUserId)).isEmpty();
assertThat(notes.getDraftComments(otherUserId)).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment1));
assertThat(notes.getComments()).isEmpty();
comment1.setStatus(Status.PUBLISHED);
update = newUpdate(c, otherUser);
@@ -996,46 +977,44 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftPsComments(otherUserId).values()).isEmpty();
assertThat(notes.getDraftBaseComments(otherUserId).values()).isEmpty();
assertThat(notes.getBaseComments()).isEmpty();
assertThat(notes.getPatchSetComments().values()).containsExactly(comment1);
assertThat(notes.getDraftComments(otherUserId)).isEmpty();
assertThat(notes.getComments()).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment1));
}
@Test
public void patchLineCommentMultipleDraftsSameSidePublishOne()
throws OrmException, IOException {
throws Exception {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
CommentRange range1 = new CommentRange(1, 1, 2, 2);
CommentRange range2 = new CommentRange(2, 2, 3, 3);
String filename = "filename1";
short side = (short) 1;
Timestamp now = TimeUtil.nowTs();
String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
PatchSet.Id psId = c.currentPatchSetId();
// Write two drafts on the same side of one patch set.
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
PatchLineComment comment1 = newPatchLineComment(psId, filename, uuid1,
PatchLineComment comment1 = newComment(psId, filename, uuid1,
range1, range1.getEndLine(), otherUser, null, now, "comment on ps1",
side, commitSHA1, Status.DRAFT);
PatchLineComment comment2 = newPatchLineComment(psId, filename, uuid2,
side, rev, Status.DRAFT);
PatchLineComment comment2 = newComment(psId, filename, uuid2,
range2, range2.getEndLine(), otherUser, null, now, "other on ps1",
side, commitSHA1, Status.DRAFT);
side, rev, Status.DRAFT);
update.insertComment(comment1);
update.insertComment(comment2);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftBaseComments(otherUserId)).isEmpty();
assertThat(notes.getDraftPsComments(otherUserId).values())
.containsExactly(comment1, comment2);
assertThat(notes.getDraftComments(otherUserId)).containsExactly(
ImmutableMultimap.of(
new RevId(rev), comment1,
new RevId(rev), comment2)).inOrder();
assertThat(notes.getComments()).isEmpty();
// Publish first draft.
update = newUpdate(c, otherUser);
@@ -1045,47 +1024,46 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
notes = newNotes(c);
assertThat(notes.getPatchSetComments().get(psId)).containsExactly(comment1);
assertThat(notes.getDraftPsComments(otherUserId).values())
.containsExactly(comment2);
assertThat(notes.getBaseComments()).isEmpty();
assertThat(notes.getDraftBaseComments(otherUserId)).isEmpty();
assertThat(notes.getDraftComments(otherUserId)).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment2));
assertThat(notes.getComments()).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment1));
}
@Test
public void patchLineCommentsMultipleDraftsBothSidesPublishAll()
throws OrmException, IOException {
throws Exception {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
CommentRange range1 = new CommentRange(1, 1, 2, 2);
CommentRange range2 = new CommentRange(2, 2, 3, 3);
String filename = "filename1";
Timestamp now = TimeUtil.nowTs();
String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
String baseSHA1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
PatchSet.Id psId = c.currentPatchSetId();
// Write two drafts, one on each side of the patchset.
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
PatchLineComment baseComment = newPatchLineComment(psId, filename, uuid1,
PatchLineComment baseComment = newComment(psId, filename, uuid1,
range1, range1.getEndLine(), otherUser, null, now, "comment on base",
(short) 0, baseSHA1, Status.DRAFT);
PatchLineComment psComment = newPatchLineComment(psId, filename, uuid2,
(short) 0, rev1, Status.DRAFT);
PatchLineComment psComment = newComment(psId, filename, uuid2,
range2, range2.getEndLine(), otherUser, null, now, "comment on ps",
(short) 1, commitSHA1, Status.DRAFT);
(short) 1, rev2, Status.DRAFT);
update.insertComment(baseComment);
update.insertComment(psComment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftBaseComments(otherUserId).values())
.containsExactly(baseComment);
assertThat(notes.getDraftPsComments(otherUserId).values())
.containsExactly(psComment);
assertThat(notes.getDraftComments(otherUserId)).containsExactly(
ImmutableMultimap.of(
new RevId(rev1), baseComment,
new RevId(rev2), psComment));
assertThat(notes.getComments()).isEmpty();
// Publish both comments.
update = newUpdate(c, otherUser);
@@ -1098,13 +1076,98 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).isEmpty();
assertThat(notes.getComments()).containsExactly(
ImmutableMultimap.of(
new RevId(rev1), baseComment,
new RevId(rev2), psComment));
}
assertThat(notes.getBaseComments().get(psId)).containsExactly(baseComment);
assertThat(notes.getPatchSetComments().get(psId))
.containsExactly(psComment);
@Test
public void patchLineCommentsDeleteAllDrafts() throws Exception {
Change c = newChange();
String uuid = "uuid";
String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
ObjectId objId = ObjectId.fromString(rev);
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename = "filename";
short side = (short) 1;
assertThat(notes.getDraftBaseComments(otherUserId)).isEmpty();
assertThat(notes.getDraftPsComments(otherUserId)).isEmpty();
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp now = TimeUtil.nowTs();
PatchLineComment comment = newComment(psId, filename, uuid, range,
range.getEndLine(), otherUser, null, now, "comment on ps1", side,
rev, Status.DRAFT);
update.setPatchSetId(psId);
update.upsertComment(comment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).hasSize(1);
assertThat(notes.getDraftCommentNotes().getNoteMap().contains(objId))
.isTrue();
update = newUpdate(c, otherUser);
now = TimeUtil.nowTs();
update.setPatchSetId(psId);
update.deleteComment(comment);
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).isEmpty();
assertThat(notes.getDraftCommentNotes().getNoteMap()).isNull();
}
@Test
public void patchLineCommentsDeleteAllDraftsForOneRevision()
throws Exception {
Change c = newChange();
String uuid = "uuid";
String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
ObjectId objId1 = ObjectId.fromString(rev1);
ObjectId objId2 = ObjectId.fromString(rev2);
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
short side = (short) 1;
ChangeUpdate update = newUpdate(c, otherUser);
Timestamp now = TimeUtil.nowTs();
PatchLineComment comment1 = newComment(ps1, filename,
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
side, rev1, Status.DRAFT);
update.setPatchSetId(ps1);
update.upsertComment(comment1);
update.commit();
incrementPatchSet(c);
PatchSet.Id ps2 = c.currentPatchSetId();
update = newUpdate(c, otherUser);
now = TimeUtil.nowTs();
PatchLineComment comment2 = newComment(ps2, filename,
uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
side, rev2, Status.DRAFT);
update.setPatchSetId(ps2);
update.upsertComment(comment2);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).hasSize(2);
update = newUpdate(c, otherUser);
now = TimeUtil.nowTs();
update.setPatchSetId(ps2);
update.deleteComment(comment2);
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).hasSize(1);
NoteMap noteMap = notes.getDraftCommentNotes().getNoteMap();
assertThat(noteMap.contains(objId1)).isTrue();
assertThat(noteMap.contains(objId2)).isFalse();
}
@Test
@@ -1112,22 +1175,20 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
String uuid = "uuid";
String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String messageForBase = "comment for base";
Timestamp now = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
PatchLineComment commentForBase =
newPublishedPatchLineComment(psId, "filename", uuid,
null, 0, otherUser, null, now, messageForBase,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
PatchLineComment comment = newPublishedComment(
psId, "filename", uuid, null, 0, otherUser, null, now, messageForBase,
(short) 0, rev);
update.setPatchSetId(psId);
update.upsertComment(commentForBase);
update.upsertComment(comment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getPatchSetComments()).isEmpty();
assertThat(notes.getBaseComments().get(psId))
.containsExactly(commentForBase);
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment));
}
@Test
@@ -1135,21 +1196,19 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
String uuid = "uuid";
String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
String messageForBase = "comment for base";
Timestamp now = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
PatchLineComment commentForBase =
newPublishedPatchLineComment(psId, "filename", uuid,
null, 1, otherUser, null, now, messageForBase,
(short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
PatchLineComment comment = newPublishedComment(
psId, "filename", uuid, null, 1, otherUser, null, now, messageForBase,
(short) 0, rev);
update.setPatchSetId(psId);
update.upsertComment(commentForBase);
update.upsertComment(comment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getPatchSetComments()).isEmpty();
assertThat(notes.getBaseComments().get(psId))
.containsExactly(commentForBase);
assertThat(newNotes(c).getComments()).containsExactly(
ImmutableMultimap.of(new RevId(rev), comment));
}
}