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

View File

@@ -96,8 +96,7 @@ public class PutDraftComment implements RestModifyView<DraftCommentResource, Dra
Collections.singleton(update(c, in))); Collections.singleton(update(c, in)));
} else { } else {
if (c.getRevId() == null) { if (c.getRevId() == null) {
setCommentRevId(c, patchListCache, rsrc.getChange(), setCommentRevId(c, patchListCache, rsrc.getChange(), rsrc.getPatchSet());
rsrc.getPatchSet());
} }
plcUtil.updateComments(db.get(), update, plcUtil.updateComments(db.get(), update,
Collections.singleton(update(c, in))); 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.checkArgument;
import static com.google.common.base.Preconditions.checkState; 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 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.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.Account;
import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchLineComment.Status; 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.Project;
import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
@@ -46,8 +47,12 @@ import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
@@ -190,76 +195,55 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
noteMap = NoteMap.newEmptyMap(); noteMap = NoteMap.newEmptyMap();
} }
Table<PatchSet.Id, String, PatchLineComment> baseDrafts = Map<RevId, List<PatchLineComment>> allComments = new HashMap<>();
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;
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) { for (PatchLineComment c : deleteComments) {
if (c.getSide() == (short) 0) { allComments.put(c.getRevId(), new ArrayList<PatchLineComment>());
baseSideChanged = true; updatedRevs.add(c.getRevId());
baseRevId = c.getRevId(); updatedKeys.add(c.getKey());
baseDrafts.remove(psId, c.getKey().get());
} else {
revisionSideChanged = true;
psRevId = c.getRevId();
psDrafts.remove(psId, c.getKey().get());
}
} }
for (PatchLineComment c : upsertComments) { for (PatchLineComment c : upsertComments) {
if (c.getSide() == (short) 0) { hasComments = true;
baseSideChanged = true; addCommentToMap(allComments, c);
baseDrafts.put(psId, c.getKey().get(), c); updatedRevs.add(c.getRevId());
} else { updatedKeys.add(c.getKey());
revisionSideChanged = true; }
psDrafts.put(psId, c.getKey().get(), c);
// 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 = // If we touched every revision and there are no comments left, set the flag
Lists.newArrayList(baseDrafts.row(psId).values()); // for the caller to delete the entire ref.
List<PatchLineComment> newPsDrafts = boolean touchedAllRevs = updatedRevs.equals(existing.keySet());
Lists.newArrayList(psDrafts.row(psId).values()); if (touchedAllRevs && !hasComments) {
removedAllComments.set(touchedAllRevs && !hasComments);
updateNoteMap(baseSideChanged, noteMap, newBaseDrafts, return null;
baseRevId); }
updateNoteMap(revisionSideChanged, noteMap, newPsDrafts,
psRevId);
removedAllComments.set(
baseDrafts.isEmpty() && psDrafts.isEmpty() && !draftsEmpty);
commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter);
return noteMap.writeTree(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 { public RevCommit commit() throws IOException {
BatchMetaDataUpdate batch = openUpdate(); BatchMetaDataUpdate batch = openUpdate();
try { try {
@@ -279,16 +263,14 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
if (migration.writeChanges()) { if (migration.writeChanges()) {
AtomicBoolean removedAllComments = new AtomicBoolean(); AtomicBoolean removedAllComments = new AtomicBoolean();
ObjectId treeId = storeCommentsInNotes(removedAllComments); ObjectId treeId = storeCommentsInNotes(removedAllComments);
if (treeId != null) {
if (removedAllComments.get()) { if (removedAllComments.get()) {
batch.removeRef(getRefName()); batch.removeRef(getRefName());
} else { } else if (treeId != null) {
builder.setTreeId(treeId); builder.setTreeId(treeId);
batch.write(builder); batch.write(builder);
} }
} }
} }
}
@Override @Override
protected Project.NameKey getProjectName() { protected Project.NameKey getProjectName() {

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.server.notedb; package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST; 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.annotations.VisibleForTesting;
import com.google.common.base.Function; 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.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.Table;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account; 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.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -53,7 +52,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
/** View of a single {@link Change} based on the log of its notes branch. */ /** 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<Account.Id> allPastReviewers;
private ImmutableList<SubmitRecord> submitRecords; private ImmutableList<SubmitRecord> submitRecords;
private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages; private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase; private ImmutableListMultimap<RevId, PatchLineComment> comments;
private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS;
private ImmutableSet<String> hashtags; private ImmutableSet<String> hashtags;
NoteMap noteMap; NoteMap noteMap;
@@ -194,28 +191,15 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return changeMessages; return changeMessages;
} }
/** @return inline comments on each patchset's base (side == 0). */ /** @return inline comments on each revision. */
public ImmutableListMultimap<PatchSet.Id, PatchLineComment> public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
getBaseComments() { return comments;
return commentsForBase;
} }
/** @return inline comments on each patchset (side == 1). */ public ImmutableListMultimap<RevId, PatchLineComment> getDraftComments(
public ImmutableListMultimap<PatchSet.Id, PatchLineComment>
getPatchSetComments() {
return commentsForPS;
}
public Table<PatchSet.Id, String, PatchLineComment> getDraftBaseComments(
Account.Id author) throws OrmException { Account.Id author) throws OrmException {
loadDraftComments(author); loadDraftComments(author);
return draftCommentNotes.getDraftBaseComments(); return draftCommentNotes.getComments();
}
public Table<PatchSet.Id, String, PatchLineComment> getDraftPsComments(
Account.Id author) throws OrmException {
loadDraftComments(author);
return draftCommentNotes.getDraftPsComments();
} }
/** /**
@@ -234,6 +218,11 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
} }
} }
@VisibleForTesting
DraftCommentNotes getDraftCommentNotes() {
return draftCommentNotes;
}
public boolean containsComment(PatchLineComment c) throws OrmException { public boolean containsComment(PatchLineComment c) throws OrmException {
if (containsCommentPublished(c)) { if (containsCommentPublished(c)) {
return true; return true;
@@ -243,11 +232,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
} }
public boolean containsCommentPublished(PatchLineComment c) { public boolean containsCommentPublished(PatchLineComment c) {
PatchSet.Id psId = getCommentPsId(c); for (PatchLineComment l : getComments().values()) {
List<PatchLineComment> list = (c.getSide() == (short) 0)
? getBaseComments().get(psId)
: getPatchSetComments().get(psId);
for (PatchLineComment l : list) {
if (c.getKey().equals(l.getKey())) { if (c.getKey().equals(l.getKey())) {
return true; return true;
} }
@@ -282,8 +267,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
} }
approvals = parser.buildApprovals(); approvals = parser.buildApprovals();
changeMessages = parser.buildMessages(); changeMessages = parser.buildMessages();
commentsForBase = ImmutableListMultimap.copyOf(parser.commentsForBase); comments = ImmutableListMultimap.copyOf(parser.comments);
commentsForPS = ImmutableListMultimap.copyOf(parser.commentsForPs);
noteMap = parser.commentNoteMap; noteMap = parser.commentNoteMap;
if (parser.hashtags != null) { if (parser.hashtags != null) {
@@ -310,8 +294,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
reviewers = ImmutableSetMultimap.of(); reviewers = ImmutableSetMultimap.of();
submitRecords = ImmutableList.of(); submitRecords = ImmutableList.of();
changeMessages = ImmutableListMultimap.of(); changeMessages = ImmutableListMultimap.of();
commentsForBase = ImmutableListMultimap.of(); comments = ImmutableListMultimap.of();
commentsForPS = ImmutableListMultimap.of();
hashtags = ImmutableSet.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.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.util.LabelVote; import com.google.gerrit.server.util.LabelVote;
@@ -72,8 +73,7 @@ class ChangeNotesParser implements AutoCloseable {
final Map<Account.Id, ReviewerState> reviewers; final Map<Account.Id, ReviewerState> reviewers;
final List<Account.Id> allPastReviewers; final List<Account.Id> allPastReviewers;
final List<SubmitRecord> submitRecords; final List<SubmitRecord> submitRecords;
final Multimap<PatchSet.Id, PatchLineComment> commentsForPs; final Multimap<RevId, PatchLineComment> comments;
final Multimap<PatchSet.Id, PatchLineComment> commentsForBase;
NoteMap commentNoteMap; NoteMap commentNoteMap;
Change.Status status; Change.Status status;
Set<String> hashtags; Set<String> hashtags;
@@ -99,8 +99,7 @@ class ChangeNotesParser implements AutoCloseable {
allPastReviewers = Lists.newArrayList(); allPastReviewers = Lists.newArrayList();
submitRecords = Lists.newArrayListWithExpectedSize(1); submitRecords = Lists.newArrayListWithExpectedSize(1);
changeMessages = LinkedListMultimap.create(); changeMessages = LinkedListMultimap.create();
commentsForPs = ArrayListMultimap.create(); comments = ArrayListMultimap.create();
commentsForBase = ArrayListMultimap.create();
} }
@Override @Override
@@ -275,7 +274,7 @@ class ChangeNotesParser implements AutoCloseable {
throws IOException, ConfigInvalidException { throws IOException, ConfigInvalidException {
commentNoteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo, commentNoteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
ChangeNoteUtil.changeRefName(changeId), walk, changeId, ChangeNoteUtil.changeRefName(changeId), walk, changeId,
commentsForBase, commentsForPs, PatchLineComment.Status.PUBLISHED); comments, PatchLineComment.Status.PUBLISHED);
} }
private void parseApproval(PatchSet.Id psId, Account.Id accountId, 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_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS; 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.ChangeNoteUtil.FOOTER_SUBMITTED_WITH;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.addCommentToMap;
import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId;
import com.google.common.annotations.VisibleForTesting; 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.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchLineComment.Status; 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.Project;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AnonymousCowardName;
@@ -57,6 +57,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -91,8 +92,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private String subject; private String subject;
private List<SubmitRecord> submitRecords; private List<SubmitRecord> submitRecords;
private final CommentsInNotesUtil commentsUtil; private final CommentsInNotesUtil commentsUtil;
private List<PatchLineComment> commentsForBase; private List<PatchLineComment> comments;
private List<PatchLineComment> commentsForPs;
private Set<String> hashtags; private Set<String> hashtags;
private String changeMessage; private String changeMessage;
private ChangeNotes notes; private ChangeNotes notes;
@@ -161,8 +161,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.commentsUtil = commentsUtil; this.commentsUtil = commentsUtil;
this.approvals = Maps.newTreeMap(labelNameComparator); this.approvals = Maps.newTreeMap(labelNameComparator);
this.reviewers = Maps.newLinkedHashMap(); this.reviewers = Maps.newLinkedHashMap();
this.commentsForPs = Lists.newArrayList(); this.comments = Lists.newArrayList();
this.commentsForBase = Lists.newArrayList();
} }
public void setStatus(Change.Status status) { 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," "A comment already exists with the same key as the following comment,"
+ " so we cannot insert this comment: %s", c); + " so we cannot insert this comment: %s", c);
} }
if (c.getSide() == 0) { comments.add(c);
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
} }
private void insertDraftComment(PatchLineComment c) throws OrmException { private void insertDraftComment(PatchLineComment c) throws OrmException {
@@ -263,11 +258,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
checkArgument(!notes.containsCommentPublished(c), checkArgument(!notes.containsCommentPublished(c),
"Cannot update a comment that has already been published and saved"); "Cannot update a comment that has already been published and saved");
} }
if (c.getSide() == 0) { comments.add(c);
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
} }
private void upsertDraftComment(PatchLineComment c) { private void upsertDraftComment(PatchLineComment c) {
@@ -286,11 +277,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
checkArgument(!notes.containsCommentPublished(c), checkArgument(!notes.containsCommentPublished(c),
"Cannot update a comment that has already been published and saved"); "Cannot update a comment that has already been published and saved");
} }
if (c.getSide() == 0) { comments.add(c);
commentsForBase.add(c);
} else {
commentsForPs.add(c);
}
} }
private void updateDraftComment(PatchLineComment c) throws OrmException { private void updateDraftComment(PatchLineComment c) throws OrmException {
@@ -356,31 +343,23 @@ public class ChangeUpdate extends AbstractChangeUpdate {
if (noteMap == null) { if (noteMap == null) {
noteMap = NoteMap.newEmptyMap(); noteMap = NoteMap.newEmptyMap();
} }
if (commentsForPs.isEmpty() && commentsForBase.isEmpty()) { if (comments.isEmpty()) {
return null; return null;
} }
Multimap<PatchSet.Id, PatchLineComment> allCommentsOnBases = Map<RevId, List<PatchLineComment>> allComments = Maps.newHashMap();
notes.getBaseComments(); for (Map.Entry<RevId, Collection<PatchLineComment>> e
Multimap<PatchSet.Id, PatchLineComment> allCommentsOnPs = : notes.getComments().asMap().entrySet()) {
notes.getPatchSetComments(); List<PatchLineComment> comments = new ArrayList<>();
for (PatchLineComment c : e.getValue()) {
// This writes all comments for the base of this PS to the note map. comments.add(c);
if (!commentsForBase.isEmpty()) {
List<PatchLineComment> baseCommentsForThisPs =
new ArrayList<>(allCommentsOnBases.get(psId));
baseCommentsForThisPs.addAll(commentsForBase);
commentsUtil.writeCommentsToNoteMap(noteMap, baseCommentsForThisPs,
inserter);
} }
allComments.put(e.getKey(), comments);
// 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);
} }
for (PatchLineComment c : comments) {
addCommentToMap(allComments, c);
}
commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter);
return noteMap.writeTree(inserter); return noteMap.writeTree(inserter);
} }
@@ -504,8 +483,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private boolean isEmpty() { private boolean isEmpty() {
return approvals.isEmpty() return approvals.isEmpty()
&& changeMessage == null && changeMessage == null
&& commentsForBase.isEmpty() && comments.isEmpty()
&& commentsForPs.isEmpty()
&& reviewers.isEmpty() && reviewers.isEmpty()
&& status == null && status == null
&& subject == null && subject == null

View File

@@ -63,9 +63,11 @@ import java.io.PrintWriter;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Utility functions to parse PatchLineComments out of a note byte array and * 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, public static NoteMap parseCommentsFromNotes(Repository repo, String refName,
RevWalk walk, Change.Id changeId, RevWalk walk, Change.Id changeId,
Multimap<PatchSet.Id, PatchLineComment> commentsForBase, Multimap<RevId, PatchLineComment> comments,
Multimap<PatchSet.Id, PatchLineComment> commentsForPs,
Status status) Status status)
throws IOException, ConfigInvalidException { throws IOException, ConfigInvalidException {
Ref ref = repo.getRef(refName); Ref ref = repo.getRef(refName);
@@ -99,20 +100,14 @@ public class CommentsInNotesUtil {
RevCommit commit = walk.parseCommit(ref.getObjectId()); RevCommit commit = walk.parseCommit(ref.getObjectId());
NoteMap noteMap = NoteMap.read(reader, commit); NoteMap noteMap = NoteMap.read(reader, commit);
for (Note note: noteMap) { for (Note note : noteMap) {
byte[] bytes = byte[] bytes =
reader.open(note.getData(), OBJ_BLOB).getCachedBytes(MAX_NOTE_SZ); reader.open(note.getData(), OBJ_BLOB).getCachedBytes(MAX_NOTE_SZ);
List<PatchLineComment> result = parseNote(bytes, changeId, status); List<PatchLineComment> result = parseNote(bytes, changeId, status);
if (result == null || result.isEmpty()) { if (result == null || result.isEmpty()) {
continue; continue;
} }
PatchSet.Id psId = result.get(0).getKey().getParentKey().getParentKey(); comments.putAll(new RevId(note.name()), result);
short side = result.get(0).getSide();
if (side == 0) {
commentsForBase.putAll(psId, result);
} else {
commentsForPs.putAll(psId, result);
}
} }
return noteMap; return noteMap;
} }
@@ -524,19 +519,47 @@ public class CommentsInNotesUtil {
return buf.toByteArray(); 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, public void writeCommentsToNoteMap(NoteMap noteMap,
List<PatchLineComment> allComments, ObjectInserter inserter) Map<RevId, List<PatchLineComment>> allComments, ObjectInserter inserter)
throws IOException { throws IOException {
checkArgument(!allComments.isEmpty(), for (Map.Entry<RevId, List<PatchLineComment>> e : allComments.entrySet()) {
"No comments to write; to delete, use removeNoteFromNoteMap()."); List<PatchLineComment> comments = e.getValue();
ObjectId commit = ObjectId commit = ObjectId.fromString(e.getKey().get());
ObjectId.fromString(allComments.get(0).getRevId().get()); if (comments.isEmpty()) {
Collections.sort(allComments, ChangeNotes.PLC_ORDER); noteMap.remove(commit);
noteMap.set(commit, inserter.insert(OBJ_BLOB, buildNote(allComments))); 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) static void addCommentToMap(Map<RevId, List<PatchLineComment>> map,
throws IOException { PatchLineComment c) {
noteMap.remove(ObjectId.fromString(commitId.get())); 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; 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.annotations.VisibleForTesting;
import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment; 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.Project;
import com.google.gerrit.reviewdb.client.RefNames; 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.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -70,8 +66,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
private final AllUsersName draftsProject; private final AllUsersName draftsProject;
private final Account.Id author; private final Account.Id author;
private final Table<PatchSet.Id, String, PatchLineComment> draftBaseComments; private ImmutableListMultimap<RevId, PatchLineComment> comments;
private final Table<PatchSet.Id, String, PatchLineComment> draftPsComments;
private NoteMap noteMap; private NoteMap noteMap;
DraftCommentNotes(GitRepositoryManager repoManager, NotesMigration migration, DraftCommentNotes(GitRepositoryManager repoManager, NotesMigration migration,
@@ -79,9 +74,6 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
super(repoManager, migration, changeId); super(repoManager, migration, changeId);
this.draftsProject = draftsProject; this.draftsProject = draftsProject;
this.author = author; this.author = author;
this.draftBaseComments = HashBasedTable.create();
this.draftPsComments = HashBasedTable.create();
} }
public NoteMap getNoteMap() { public NoteMap getNoteMap() {
@@ -92,32 +84,18 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
return author; return author;
} }
/** public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
* @return a defensive copy of the table containing all draft comments // TODO(dborowitz): Defensive copy?
* on this change with side == 0. The row key is the comment's PatchSet.Id, return comments;
* 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 boolean containsComment(PatchLineComment c) { public boolean containsComment(PatchLineComment c) {
Table<PatchSet.Id, String, PatchLineComment> t = for (PatchLineComment existing : comments.values()) {
c.getSide() == (short) 0 if (c.getKey().equals(existing.getKey())) {
? getDraftBaseComments() return true;
: getDraftPsComments(); }
return t.contains(getCommentPsId(c), c.getKey().get()); }
return false;
} }
@Override @Override
@@ -129,6 +107,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
protected void onLoad() throws IOException, ConfigInvalidException { protected void onLoad() throws IOException, ConfigInvalidException {
ObjectId rev = getRevision(); ObjectId rev = getRevision();
if (rev == null) { if (rev == null) {
loadDefaults();
return; return;
} }
@@ -137,8 +116,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
getChangeId(), walk, rev, repoManager, draftsProject, author)) { getChangeId(), walk, rev, repoManager, draftsProject, author)) {
parser.parseDraftComments(); parser.parseDraftComments();
buildCommentTable(draftBaseComments, parser.draftBaseComments); comments = ImmutableListMultimap.copyOf(parser.comments);
buildCommentTable(draftPsComments, parser.draftPsComments);
noteMap = parser.noteMap; noteMap = parser.noteMap;
} }
} }
@@ -152,20 +130,11 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
@Override @Override
protected void loadDefaults() { protected void loadDefaults() {
// Do nothing; tables are final and initialized in constructor. comments = ImmutableListMultimap.of();
} }
@Override @Override
protected Project.NameKey getProjectName() { protected Project.NameKey getProjectName() {
return draftsProject; 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.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment; 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.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -34,8 +34,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException; import java.io.IOException;
class DraftCommentNotesParser implements AutoCloseable { class DraftCommentNotesParser implements AutoCloseable {
final Multimap<PatchSet.Id, PatchLineComment> draftBaseComments; final Multimap<RevId, PatchLineComment> comments;
final Multimap<PatchSet.Id, PatchLineComment> draftPsComments;
NoteMap noteMap; NoteMap noteMap;
private final Change.Id changeId; private final Change.Id changeId;
@@ -53,8 +52,7 @@ class DraftCommentNotesParser implements AutoCloseable {
this.repo = repoManager.openMetadataRepository(draftsProject); this.repo = repoManager.openMetadataRepository(draftsProject);
this.author = author; this.author = author;
draftBaseComments = ArrayListMultimap.create(); comments = ArrayListMultimap.create();
draftPsComments = ArrayListMultimap.create();
} }
@Override @Override
@@ -66,7 +64,6 @@ class DraftCommentNotesParser implements AutoCloseable {
walk.markStart(walk.parseCommit(tip)); walk.markStart(walk.parseCommit(tip));
noteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo, noteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
RefNames.refsDraftComments(author, changeId), RefNames.refsDraftComments(author, changeId),
walk, changeId, draftBaseComments, walk, changeId, comments, PatchLineComment.Status.DRAFT);
draftPsComments, PatchLineComment.Status.DRAFT);
} }
} }

View File

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

View File

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