Support setting current patch set in NoteDb
Until now, NoteDb was not explicitly recording the current patch set, and instead just assuming that the highest-numbered non-deleted patch set was current. This is usually fine, but there is at least one corner case where it's not. Specifically, when pushing an old patch set of a change directly to a branch: * The change moves to state MERGED. * The currentPatchSetId field gets set to the corresponding patch set ID of the merged commit, even if this is less than the maximum patch set in the database. Teach NoteDb to handle this case with a footer "Current: true" indicating the patch set mentioned in the "Patch-set" footer is the current patch set. Creating a new patch set also implicitly sets it to current, matching existing data. Various parts of the code in ChangeRebuilderImpl and ChangeBundle were assuming that any entities referring to patch sets greater than the current patch set were ignorable. Remove this assumption, and ensure we're handling these entities properly. Add a test for the above case. Change-Id: I099fd703458ffd63a31009ceaaa1c1d2c59d4669
This commit is contained in:
@@ -17,6 +17,7 @@ package com.google.gerrit.acceptance.git;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
|
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
|
||||||
import static com.google.gerrit.acceptance.GitUtil.pushHead;
|
import static com.google.gerrit.acceptance.GitUtil.pushHead;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
@@ -233,6 +234,34 @@ public class SubmitOnPushIT extends AbstractDaemonTest {
|
|||||||
assertThat(cd.patchSet(psId2).getRevision().get()).isEqualTo(c2.name());
|
assertThat(cd.patchSet(psId2).getRevision().get()).isEqualTo(c2.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeOnPushToBranchWithOldPatchset() throws Exception {
|
||||||
|
grant(Permission.PUSH, project, "refs/heads/master");
|
||||||
|
PushOneCommit.Result r = pushTo("refs/for/master");
|
||||||
|
r.assertOkStatus();
|
||||||
|
RevCommit c1 = r.getCommit();
|
||||||
|
PatchSet.Id psId1 = r.getPatchSetId();
|
||||||
|
String changeId = r.getChangeId();
|
||||||
|
assertThat(psId1.get()).isEqualTo(1);
|
||||||
|
|
||||||
|
r = amendChange(changeId);
|
||||||
|
ChangeData cd = r.getChange();
|
||||||
|
PatchSet.Id psId2 = cd.change().currentPatchSetId();
|
||||||
|
assertThat(psId2.getParentKey()).isEqualTo(psId1.getParentKey());
|
||||||
|
assertThat(psId2.get()).isEqualTo(2);
|
||||||
|
|
||||||
|
testRepo.reset(c1);
|
||||||
|
assertPushOk(
|
||||||
|
pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
|
||||||
|
|
||||||
|
cd = changeDataFactory.create(db, project, psId1.getParentKey());
|
||||||
|
Change c = cd.change();
|
||||||
|
assertThat(c.getStatus()).isEqualTo(Change.Status.MERGED);
|
||||||
|
assertThat(c.currentPatchSetId()).isEqualTo(psId1);
|
||||||
|
assertThat(cd.patchSets().stream().map(ps -> ps.getId()).collect(toList()))
|
||||||
|
.containsExactly(psId1, psId2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeMultipleOnPushToBranchWithNewPatchset() throws Exception {
|
public void mergeMultipleOnPushToBranchWithNewPatchset() throws Exception {
|
||||||
grant(Permission.PUSH, project, "refs/heads/master");
|
grant(Permission.PUSH, project, "refs/heads/master");
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ 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.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.reviewdb.client.RevId;
|
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
||||||
import com.google.gerrit.server.ChangeUtil;
|
import com.google.gerrit.server.ChangeUtil;
|
||||||
@@ -73,6 +72,8 @@ import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
|
|||||||
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
|
||||||
import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
|
import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
|
||||||
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
|
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
|
||||||
|
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||||
|
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||||
import com.google.gerrit.server.project.Util;
|
import com.google.gerrit.server.project.Util;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
import com.google.gerrit.testutil.ConfigSuite;
|
import com.google.gerrit.testutil.ConfigSuite;
|
||||||
@@ -150,6 +151,9 @@ public class ChangeRebuilderIT extends AbstractDaemonTest {
|
|||||||
@Inject
|
@Inject
|
||||||
private ChangeBundleReader bundleReader;
|
private ChangeBundleReader bundleReader;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PatchSetInfoFactory patchSetInfoFactory;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
assume().that(NoteDbMode.readWrite()).isFalse();
|
assume().that(NoteDbMode.readWrite()).isFalse();
|
||||||
@@ -849,28 +853,6 @@ public class ChangeRebuilderIT extends AbstractDaemonTest {
|
|||||||
assertThat(notes.getComments()).isEmpty();
|
assertThat(notes.getComments()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void skipPatchSetsGreaterThanCurrentPatchSet() throws Exception {
|
|
||||||
PushOneCommit.Result r = createChange();
|
|
||||||
Change change = r.getChange().change();
|
|
||||||
Change.Id id = change.getId();
|
|
||||||
|
|
||||||
PatchSet badPs =
|
|
||||||
new PatchSet(new PatchSet.Id(id, change.currentPatchSetId().get() + 1));
|
|
||||||
badPs.setCreatedOn(TimeUtil.nowTs());
|
|
||||||
badPs.setUploader(new Account.Id(12345));
|
|
||||||
badPs.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
|
|
||||||
db.patchSets().insert(Collections.singleton(badPs));
|
|
||||||
indexer.index(db, change.getProject(), id);
|
|
||||||
|
|
||||||
checker.rebuildAndCheckChanges(id);
|
|
||||||
|
|
||||||
setNotesMigration(true, true);
|
|
||||||
ChangeNotes notes = notesFactory.create(db, project, id);
|
|
||||||
assertThat(notes.getPatchSets().keySet())
|
|
||||||
.containsExactly(change.currentPatchSetId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void leadingSpacesInSubject() throws Exception {
|
public void leadingSpacesInSubject() throws Exception {
|
||||||
String subj = " " + PushOneCommit.SUBJECT;
|
String subj = " " + PushOneCommit.SUBJECT;
|
||||||
@@ -1143,6 +1125,43 @@ public class ChangeRebuilderIT extends AbstractDaemonTest {
|
|||||||
assertThat(ps.getId()).isEqualTo(psId1);
|
assertThat(ps.getId()).isEqualTo(psId1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highestNumberedPatchSetIsNotCurrent() throws Exception {
|
||||||
|
PushOneCommit.Result r1 = createChange();
|
||||||
|
PatchSet.Id psId1 = r1.getPatchSetId();
|
||||||
|
Change.Id id = psId1.getParentKey();
|
||||||
|
PushOneCommit.Result r2 = amendChange(r1.getChangeId());
|
||||||
|
PatchSet.Id psId2 = r2.getPatchSetId();
|
||||||
|
|
||||||
|
try (BatchUpdate bu = batchUpdateFactory.create(db, project,
|
||||||
|
identifiedUserFactory.create(user.getId()), TimeUtil.nowTs())) {
|
||||||
|
bu.addOp(id, new BatchUpdate.Op() {
|
||||||
|
@Override
|
||||||
|
public boolean updateChange(ChangeContext ctx)
|
||||||
|
throws PatchSetInfoNotAvailableException {
|
||||||
|
ctx.getChange().setCurrentPatchSet(
|
||||||
|
patchSetInfoFactory.get(ctx.getDb(), ctx.getNotes(), psId1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bu.execute();
|
||||||
|
}
|
||||||
|
ChangeNotes notes = notesFactory.create(db, project, id);
|
||||||
|
assertThat(psUtil.byChangeAsMap(db, notes).keySet())
|
||||||
|
.containsExactly(psId1, psId2);
|
||||||
|
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
|
||||||
|
|
||||||
|
assertThat(db.changes().get(id).currentPatchSetId()).isEqualTo(psId1);
|
||||||
|
|
||||||
|
checker.rebuildAndCheckChanges(id);
|
||||||
|
setNotesMigration(true, true);
|
||||||
|
|
||||||
|
notes = notesFactory.create(db, project, id);
|
||||||
|
assertThat(psUtil.byChangeAsMap(db, notes).keySet())
|
||||||
|
.containsExactly(psId1, psId2);
|
||||||
|
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertChangesReadOnly(RestApiException e) throws Exception {
|
private void assertChangesReadOnly(RestApiException e) throws Exception {
|
||||||
Throwable cause = e.getCause();
|
Throwable cause = e.getCause();
|
||||||
assertThat(cause).isInstanceOf(UpdateException.class);
|
assertThat(cause).isInstanceOf(UpdateException.class);
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ public class MergedByPushOp extends BatchUpdate.Op {
|
|||||||
// we cannot reconstruct the submit records for when this change was
|
// we cannot reconstruct the submit records for when this change was
|
||||||
// submitted, this is why we must fix the status
|
// submitted, this is why we must fix the status
|
||||||
update.fixStatus(Change.Status.MERGED);
|
update.fixStatus(Change.Status.MERGED);
|
||||||
|
update.setCurrentPatchSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder msgBuf = new StringBuilder();
|
StringBuilder msgBuf = new StringBuilder();
|
||||||
|
|||||||
@@ -365,8 +365,7 @@ public class ChangeBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<PatchSet.Id> validPatchSetPredicate() {
|
private Predicate<PatchSet.Id> validPatchSetPredicate() {
|
||||||
Predicate<PatchSet.Id> upToCurrent = upToCurrentPredicate();
|
return patchSets::containsKey;
|
||||||
return p -> upToCurrent.apply(p) && patchSets.containsKey(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ChangeMessage> filterChangeMessages() {
|
private Collection<ChangeMessage> filterChangeMessages() {
|
||||||
@@ -380,19 +379,6 @@ public class ChangeBundle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<PatchSet.Id> upToCurrentPredicate() {
|
|
||||||
PatchSet.Id current = change.currentPatchSetId();
|
|
||||||
if (current == null) {
|
|
||||||
return Predicates.alwaysFalse();
|
|
||||||
}
|
|
||||||
int max = current.get();
|
|
||||||
return p -> p.get() <= max;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<PatchSet.Id, PatchSet> filterPatchSets() {
|
|
||||||
return Maps.filterKeys(patchSets, upToCurrentPredicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void diffChanges(List<String> diffs, ChangeBundle bundleA,
|
private static void diffChanges(List<String> diffs, ChangeBundle bundleA,
|
||||||
ChangeBundle bundleB) {
|
ChangeBundle bundleB) {
|
||||||
Change a = bundleA.change;
|
Change a = bundleA.change;
|
||||||
@@ -659,8 +645,8 @@ public class ChangeBundle {
|
|||||||
|
|
||||||
private static void diffPatchSets(List<String> diffs, ChangeBundle bundleA,
|
private static void diffPatchSets(List<String> diffs, ChangeBundle bundleA,
|
||||||
ChangeBundle bundleB) {
|
ChangeBundle bundleB) {
|
||||||
Map<PatchSet.Id, PatchSet> as = bundleA.filterPatchSets();
|
Map<PatchSet.Id, PatchSet> as = bundleA.patchSets;
|
||||||
Map<PatchSet.Id, PatchSet> bs = bundleB.filterPatchSets();
|
Map<PatchSet.Id, PatchSet> bs = bundleB.patchSets;
|
||||||
for (PatchSet.Id id : diffKeySets(diffs, as, bs)) {
|
for (PatchSet.Id id : diffKeySets(diffs, as, bs)) {
|
||||||
PatchSet a = as.get(id);
|
PatchSet a = as.get(id);
|
||||||
PatchSet b = bs.get(id);
|
PatchSet b = bs.get(id);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ public class ChangeNoteUtil {
|
|||||||
public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
|
public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
|
||||||
public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
|
public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
|
||||||
public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
|
public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");
|
||||||
|
public static final FooterKey FOOTER_CURRENT = new FooterKey("Current");
|
||||||
public static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
|
public static final FooterKey FOOTER_GROUPS = new FooterKey("Groups");
|
||||||
public static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
|
public static final FooterKey FOOTER_HASHTAGS = new FooterKey("Hashtags");
|
||||||
public static final FooterKey FOOTER_LABEL = new FooterKey("Label");
|
public static final FooterKey FOOTER_LABEL = new FooterKey("Label");
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
|
|||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
|
||||||
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CURRENT;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_GROUPS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_GROUPS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
|
||||||
@@ -90,7 +91,6 @@ import java.util.Iterator;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NavigableSet;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -137,6 +137,7 @@ class ChangeNotesParser {
|
|||||||
private final TreeMap<PatchSet.Id, PatchSet> patchSets;
|
private final TreeMap<PatchSet.Id, PatchSet> patchSets;
|
||||||
private final Set<PatchSet.Id> deletedPatchSets;
|
private final Set<PatchSet.Id> deletedPatchSets;
|
||||||
private final Map<PatchSet.Id, PatchSetState> patchSetStates;
|
private final Map<PatchSet.Id, PatchSetState> patchSetStates;
|
||||||
|
private final List<PatchSet.Id> currentPatchSets;
|
||||||
private final Map<ApprovalKey, PatchSetApproval> approvals;
|
private final Map<ApprovalKey, PatchSetApproval> approvals;
|
||||||
private final List<PatchSetApproval> bufferedApprovals;
|
private final List<PatchSetApproval> bufferedApprovals;
|
||||||
private final List<ChangeMessage> allChangeMessages;
|
private final List<ChangeMessage> allChangeMessages;
|
||||||
@@ -157,7 +158,6 @@ class ChangeNotesParser {
|
|||||||
private String originalSubject;
|
private String originalSubject;
|
||||||
private String submissionId;
|
private String submissionId;
|
||||||
private String tag;
|
private String tag;
|
||||||
private PatchSet.Id currentPatchSetId;
|
|
||||||
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
|
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
|
||||||
|
|
||||||
ChangeNotesParser(Change.Id changeId, ObjectId tip, ChangeNotesRevWalk walk,
|
ChangeNotesParser(Change.Id changeId, ObjectId tip, ChangeNotesRevWalk walk,
|
||||||
@@ -179,6 +179,7 @@ class ChangeNotesParser {
|
|||||||
patchSets = Maps.newTreeMap(comparing(PatchSet.Id::get));
|
patchSets = Maps.newTreeMap(comparing(PatchSet.Id::get));
|
||||||
deletedPatchSets = new HashSet<>();
|
deletedPatchSets = new HashSet<>();
|
||||||
patchSetStates = new HashMap<>();
|
patchSetStates = new HashMap<>();
|
||||||
|
currentPatchSets = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeNotesState parseAll()
|
ChangeNotesState parseAll()
|
||||||
@@ -218,7 +219,7 @@ class ChangeNotesParser {
|
|||||||
lastUpdatedOn,
|
lastUpdatedOn,
|
||||||
ownerId,
|
ownerId,
|
||||||
branch,
|
branch,
|
||||||
currentPatchSetId,
|
buildCurrentPatchSetId(),
|
||||||
subject,
|
subject,
|
||||||
topic,
|
topic,
|
||||||
originalSubject,
|
originalSubject,
|
||||||
@@ -239,6 +240,17 @@ class ChangeNotesParser {
|
|||||||
comments);
|
comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PatchSet.Id buildCurrentPatchSetId() {
|
||||||
|
// currentPatchSets are in parse order, i.e. newest first. Pick the first
|
||||||
|
// patch set that was marked as current, excluding deleted patch sets.
|
||||||
|
for (PatchSet.Id psId : currentPatchSets) {
|
||||||
|
if (patchSets.containsKey(psId)) {
|
||||||
|
return psId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Multimap<PatchSet.Id, PatchSetApproval> buildApprovals() {
|
private Multimap<PatchSet.Id, PatchSetApproval> buildApprovals() {
|
||||||
Multimap<PatchSet.Id, PatchSetApproval> result =
|
Multimap<PatchSet.Id, PatchSetApproval> result =
|
||||||
MultimapBuilder.hashKeys().arrayListValues().build();
|
MultimapBuilder.hashKeys().arrayListValues().build();
|
||||||
@@ -339,6 +351,7 @@ class ChangeNotesParser {
|
|||||||
parsePatchSet(psId, currRev, accountId, ts);
|
parsePatchSet(psId, currRev, accountId, ts);
|
||||||
}
|
}
|
||||||
parseGroups(psId, commit);
|
parseGroups(psId, commit);
|
||||||
|
parseCurrentPatchSet(psId, commit);
|
||||||
|
|
||||||
if (submitRecords.isEmpty()) {
|
if (submitRecords.isEmpty()) {
|
||||||
// Only parse the most recent set of submit records; any older ones are
|
// Only parse the most recent set of submit records; any older ones are
|
||||||
@@ -485,6 +498,28 @@ class ChangeNotesParser {
|
|||||||
ps.setGroups(PatchSet.splitGroups(groupsStr));
|
ps.setGroups(PatchSet.splitGroups(groupsStr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseCurrentPatchSet(PatchSet.Id psId, ChangeNotesCommit commit)
|
||||||
|
throws ConfigInvalidException {
|
||||||
|
// This commit implies a new current patch set if either it creates a new
|
||||||
|
// patch set, or sets the current field explicitly.
|
||||||
|
boolean current = false;
|
||||||
|
if (parseOneFooter(commit, FOOTER_COMMIT) != null) {
|
||||||
|
current = true;
|
||||||
|
} else {
|
||||||
|
String currentStr = parseOneFooter(commit, FOOTER_CURRENT);
|
||||||
|
if (Boolean.TRUE.toString().equalsIgnoreCase(currentStr)) {
|
||||||
|
current = true;
|
||||||
|
} else if (currentStr != null) {
|
||||||
|
// Only "true" is allowed; unsetting the current patch set makes no
|
||||||
|
// sense.
|
||||||
|
throw invalidFooter(FOOTER_CURRENT, currentStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current) {
|
||||||
|
currentPatchSets.add(psId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void parseHashtags(ChangeNotesCommit commit)
|
private void parseHashtags(ChangeNotesCommit commit)
|
||||||
throws ConfigInvalidException {
|
throws ConfigInvalidException {
|
||||||
// Commits are parsed in reverse order and only the last set of hashtags
|
// Commits are parsed in reverse order and only the last set of hashtags
|
||||||
@@ -937,13 +972,7 @@ class ChangeNotesParser {
|
|||||||
// (or otherwise missing) patch sets. This is safer than trying to prevent
|
// (or otherwise missing) patch sets. This is safer than trying to prevent
|
||||||
// insertion, as it will also filter out items racily added after the patch
|
// insertion, as it will also filter out items racily added after the patch
|
||||||
// set was deleted.
|
// set was deleted.
|
||||||
NavigableSet<PatchSet.Id> all = patchSets.navigableKeySet();
|
changeMessagesByPatchSet.keys().retainAll(patchSets.keySet());
|
||||||
if (!all.isEmpty()) {
|
|
||||||
currentPatchSetId = all.last();
|
|
||||||
} else {
|
|
||||||
currentPatchSetId = null;
|
|
||||||
}
|
|
||||||
changeMessagesByPatchSet.keys().retainAll(all);
|
|
||||||
|
|
||||||
int pruned = pruneEntitiesForMissingPatchSets(
|
int pruned = pruneEntitiesForMissingPatchSets(
|
||||||
allChangeMessages, ChangeMessage::getPatchSetId, missing);
|
allChangeMessages, ChangeMessage::getPatchSetId, missing);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
|
|||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
|
||||||
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CURRENT;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_GROUPS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_GROUPS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
|
||||||
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
|
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
|
||||||
@@ -144,6 +145,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
private String pushCert;
|
private String pushCert;
|
||||||
private boolean isAllowWriteToNewtRef;
|
private boolean isAllowWriteToNewtRef;
|
||||||
private String psDescription;
|
private String psDescription;
|
||||||
|
private boolean currentPatchSet;
|
||||||
|
|
||||||
private ChangeDraftUpdate draftUpdate;
|
private ChangeDraftUpdate draftUpdate;
|
||||||
private RobotCommentUpdate robotCommentUpdate;
|
private RobotCommentUpdate robotCommentUpdate;
|
||||||
@@ -440,6 +442,10 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
this.psState = psState;
|
this.psState = psState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCurrentPatchSet() {
|
||||||
|
this.currentPatchSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void setGroups(List<String> groups) {
|
public void setGroups(List<String> groups) {
|
||||||
checkNotNull(groups, "groups may not be null");
|
checkNotNull(groups, "groups may not be null");
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
@@ -567,6 +573,10 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
|
|
||||||
addPatchSetFooter(msg, ps);
|
addPatchSetFooter(msg, ps);
|
||||||
|
|
||||||
|
if (currentPatchSet) {
|
||||||
|
addFooter(msg, FOOTER_CURRENT, Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
if (psDescription != null) {
|
if (psDescription != null) {
|
||||||
addFooter(msg, FOOTER_PATCH_SET_DESCRIPTION, psDescription);
|
addFooter(msg, FOOTER_PATCH_SET_DESCRIPTION, psDescription);
|
||||||
}
|
}
|
||||||
@@ -714,7 +724,8 @@ public class ChangeUpdate extends AbstractChangeUpdate {
|
|||||||
&& psState == null
|
&& psState == null
|
||||||
&& groups == null
|
&& groups == null
|
||||||
&& tag == null
|
&& tag == null
|
||||||
&& psDescription == null;
|
&& psDescription == null
|
||||||
|
&& !currentPatchSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeDraftUpdate getDraftUpdate() {
|
ChangeDraftUpdate getDraftUpdate() {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
@@ -78,8 +79,6 @@ import org.eclipse.jgit.lib.Ref;
|
|||||||
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.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
@@ -93,9 +92,6 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ChangeRebuilderImpl extends ChangeRebuilder {
|
public class ChangeRebuilderImpl extends ChangeRebuilder {
|
||||||
private static final Logger log =
|
|
||||||
LoggerFactory.getLogger(ChangeRebuilderImpl.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum amount of time between the ReviewDb timestamp of the first and
|
* The maximum amount of time between the ReviewDb timestamp of the first and
|
||||||
* last events batched together into a single NoteDb update.
|
* last events batched together into a single NoteDb update.
|
||||||
@@ -275,7 +271,6 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
throw new NoPatchSetsException(change.getId());
|
throw new NoPatchSetsException(change.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchSet.Id currPsId = change.currentPatchSetId();
|
|
||||||
// We will rebuild all events, except for draft comments, in buckets based
|
// We will rebuild all events, except for draft comments, in buckets based
|
||||||
// on author and timestamp.
|
// on author and timestamp.
|
||||||
List<Event> events = new ArrayList<>();
|
List<Event> events = new ArrayList<>();
|
||||||
@@ -293,12 +288,6 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
Maps.newHashMapWithExpectedSize(bundle.getPatchSets().size());
|
Maps.newHashMapWithExpectedSize(bundle.getPatchSets().size());
|
||||||
|
|
||||||
for (PatchSet ps : bundle.getPatchSets()) {
|
for (PatchSet ps : bundle.getPatchSets()) {
|
||||||
if (ps.getId().get() > currPsId.get()) {
|
|
||||||
log.info(
|
|
||||||
"Skipping patch set {}, which is higher than current patch set {}",
|
|
||||||
ps.getId(), currPsId);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PatchSetEvent pse =
|
PatchSetEvent pse =
|
||||||
new PatchSetEvent(change, ps, manager.getChangeRepo().rw);
|
new PatchSetEvent(change, ps, manager.getChangeRepo().rw);
|
||||||
patchSetEvents.put(ps.getId(), pse);
|
patchSetEvents.put(ps.getId(), pse);
|
||||||
@@ -342,7 +331,8 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
events.addAll(msgEvents);
|
events.addAll(msgEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortAndFillEvents(change, noteDbChange, events, minPsNum);
|
sortAndFillEvents(
|
||||||
|
change, noteDbChange, bundle.getPatchSets(), events, minPsNum);
|
||||||
|
|
||||||
EventList<Event> el = new EventList<>();
|
EventList<Event> el = new EventList<>();
|
||||||
for (Event e : events) {
|
for (Event e : events) {
|
||||||
@@ -401,8 +391,9 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sortAndFillEvents(Change change, Change noteDbChange,
|
private void sortAndFillEvents(Change change, Change noteDbChange,
|
||||||
|
ImmutableCollection<PatchSet> patchSets,
|
||||||
List<Event> events, Integer minPsNum) {
|
List<Event> events, Integer minPsNum) {
|
||||||
Event finalUpdates = new FinalUpdatesEvent(change, noteDbChange);
|
Event finalUpdates = new FinalUpdatesEvent(change, noteDbChange, patchSets);
|
||||||
events.add(finalUpdates);
|
events.add(finalUpdates);
|
||||||
setPostSubmitDeps(events);
|
setPostSubmitDeps(events);
|
||||||
new EventSorter(events).sort();
|
new EventSorter(events).sort();
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.notedb.rebuild;
|
package com.google.gerrit.server.notedb.rebuild;
|
||||||
|
|
||||||
|
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.server.notedb.ChangeUpdate;
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
@@ -23,12 +27,15 @@ import java.util.Objects;
|
|||||||
class FinalUpdatesEvent extends Event {
|
class FinalUpdatesEvent extends Event {
|
||||||
private final Change change;
|
private final Change change;
|
||||||
private final Change noteDbChange;
|
private final Change noteDbChange;
|
||||||
|
private final ImmutableCollection<PatchSet> patchSets;
|
||||||
|
|
||||||
FinalUpdatesEvent(Change change, Change noteDbChange) {
|
FinalUpdatesEvent(Change change, Change noteDbChange,
|
||||||
|
ImmutableCollection<PatchSet> patchSets) {
|
||||||
super(change.currentPatchSetId(), change.getOwner(), change.getOwner(),
|
super(change.currentPatchSetId(), change.getOwner(), change.getOwner(),
|
||||||
change.getLastUpdatedOn(), change.getCreatedOn(), null);
|
change.getLastUpdatedOn(), change.getCreatedOn(), null);
|
||||||
this.change = change;
|
this.change = change;
|
||||||
this.noteDbChange = noteDbChange;
|
this.noteDbChange = noteDbChange;
|
||||||
|
this.patchSets = patchSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,11 +61,20 @@ class FinalUpdatesEvent extends Event {
|
|||||||
// TODO(dborowitz): Parse intermediate values out from messages.
|
// TODO(dborowitz): Parse intermediate values out from messages.
|
||||||
update.setAssignee(change.getAssignee());
|
update.setAssignee(change.getAssignee());
|
||||||
}
|
}
|
||||||
|
if (!patchSets.isEmpty() && !highestNumberedPatchSetIsCurrent()) {
|
||||||
|
update.setCurrentPatchSet();
|
||||||
|
}
|
||||||
if (!update.isEmpty()) {
|
if (!update.isEmpty()) {
|
||||||
update.setSubjectForCommit("Final NoteDb migration updates");
|
update.setSubjectForCommit("Final NoteDb migration updates");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean highestNumberedPatchSetIsCurrent() {
|
||||||
|
PatchSet.Id max =
|
||||||
|
patchSets.stream().map(PatchSet::getId).max(intKeyOrdering()).get();
|
||||||
|
return max.equals(change.currentPatchSetId());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isSubmit() {
|
protected boolean isSubmit() {
|
||||||
return change.getStatus() == Change.Status.MERGED;
|
return change.getStatus() == Change.Status.MERGED;
|
||||||
|
|||||||
@@ -769,39 +769,6 @@ public class ChangeBundleTest extends GerritBaseTests {
|
|||||||
+ "Only in B:\n " + cm1);
|
+ "Only in B:\n " + cm1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void diffChangeMessagesIgnoresMessagesOnPatchSetGreaterThanCurrent()
|
|
||||||
throws Exception {
|
|
||||||
Change c = TestChanges.newChange(project, accountId);
|
|
||||||
|
|
||||||
PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
|
|
||||||
ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
|
|
||||||
ps1.setUploader(accountId);
|
|
||||||
ps1.setCreatedOn(TimeUtil.nowTs());
|
|
||||||
PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
|
|
||||||
ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
|
|
||||||
ps2.setUploader(accountId);
|
|
||||||
ps2.setCreatedOn(TimeUtil.nowTs());
|
|
||||||
|
|
||||||
assertThat(c.currentPatchSetId()).isEqualTo(ps1.getId());
|
|
||||||
|
|
||||||
ChangeMessage cm1 = new ChangeMessage(
|
|
||||||
new ChangeMessage.Key(c.getId(), "uuid1"),
|
|
||||||
accountId, TimeUtil.nowTs(), ps1.getId());
|
|
||||||
cm1.setMessage("a message");
|
|
||||||
ChangeMessage cm2 = new ChangeMessage(
|
|
||||||
new ChangeMessage.Key(c.getId(), "uuid2"),
|
|
||||||
accountId, TimeUtil.nowTs(), ps2.getId());
|
|
||||||
cm2.setMessage("other message");
|
|
||||||
|
|
||||||
ChangeBundle b1 = new ChangeBundle(c, messages(cm1, cm2),
|
|
||||||
patchSets(ps1, ps2), approvals(), comments(), reviewers(), REVIEW_DB);
|
|
||||||
ChangeBundle b2 = new ChangeBundle(c, messages(cm1), patchSets(ps1),
|
|
||||||
approvals(), comments(), reviewers(), NOTE_DB);
|
|
||||||
assertNoDiffs(b1, b2);
|
|
||||||
assertNoDiffs(b2, b1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void diffPatchSetIdSets() throws Exception {
|
public void diffPatchSetIdSets() throws Exception {
|
||||||
Change c = TestChanges.newChange(project, accountId);
|
Change c = TestChanges.newChange(project, accountId);
|
||||||
@@ -919,7 +886,7 @@ public class ChangeBundleTest extends GerritBaseTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void diffIgnoresPatchSetsGreaterThanCurrent() throws Exception {
|
public void diffPatchSetsGreaterThanCurrent() throws Exception {
|
||||||
Change c = TestChanges.newChange(project, accountId);
|
Change c = TestChanges.newChange(project, accountId);
|
||||||
|
|
||||||
PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
|
PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
|
||||||
@@ -932,6 +899,13 @@ public class ChangeBundleTest extends GerritBaseTests {
|
|||||||
ps2.setCreatedOn(TimeUtil.nowTs());
|
ps2.setCreatedOn(TimeUtil.nowTs());
|
||||||
assertThat(ps2.getId().get()).isGreaterThan(c.currentPatchSetId().get());
|
assertThat(ps2.getId().get()).isGreaterThan(c.currentPatchSetId().get());
|
||||||
|
|
||||||
|
ChangeMessage cm1 = new ChangeMessage(
|
||||||
|
new ChangeMessage.Key(c.getId(), "uuid1"),
|
||||||
|
accountId, TimeUtil.nowTs(), c.currentPatchSetId());
|
||||||
|
ChangeMessage cm2 = new ChangeMessage(
|
||||||
|
new ChangeMessage.Key(c.getId(), "uuid2"),
|
||||||
|
accountId, TimeUtil.nowTs(), c.currentPatchSetId());
|
||||||
|
|
||||||
PatchSetApproval a1 = new PatchSetApproval(
|
PatchSetApproval a1 = new PatchSetApproval(
|
||||||
new PatchSetApproval.Key(
|
new PatchSetApproval.Key(
|
||||||
ps1.getId(), accountId, new LabelId("Code-Review")),
|
ps1.getId(), accountId, new LabelId("Code-Review")),
|
||||||
@@ -944,26 +918,44 @@ public class ChangeBundleTest extends GerritBaseTests {
|
|||||||
TimeUtil.nowTs());
|
TimeUtil.nowTs());
|
||||||
|
|
||||||
// Both ReviewDb.
|
// Both ReviewDb.
|
||||||
ChangeBundle b1 = new ChangeBundle(c, messages(), patchSets(ps1),
|
ChangeBundle b1 = new ChangeBundle(c, messages(cm1), patchSets(ps1),
|
||||||
approvals(a1), comments(), reviewers(), REVIEW_DB);
|
approvals(a1), comments(), reviewers(), REVIEW_DB);
|
||||||
ChangeBundle b2 = new ChangeBundle(c, messages(), patchSets(ps1, ps2),
|
ChangeBundle b2 = new ChangeBundle(c, messages(cm1, cm2),
|
||||||
approvals(a1, a2), comments(), reviewers(), REVIEW_DB);
|
patchSets(ps1, ps2), approvals(a1, a2), comments(), reviewers(),
|
||||||
assertNoDiffs(b1, b2);
|
REVIEW_DB);
|
||||||
|
assertDiffs(b1, b2,
|
||||||
|
"ChangeMessage.Key sets differ: [] only in A; [" + cm2.getKey()
|
||||||
|
+ "] only in B",
|
||||||
|
"PatchSet.Id sets differ:"
|
||||||
|
+ " [] only in A; [" + ps2.getId() + "] only in B",
|
||||||
|
"PatchSetApproval.Key sets differ:"
|
||||||
|
+ " [] only in A; [" + a2.getKey() + "] only in B");
|
||||||
|
|
||||||
// One NoteDb.
|
// One NoteDb.
|
||||||
b1 = new ChangeBundle(c, messages(), patchSets(ps1), approvals(a1),
|
b1 = new ChangeBundle(c, messages(cm1), patchSets(ps1), approvals(a1),
|
||||||
comments(), reviewers(), NOTE_DB);
|
comments(), reviewers(), NOTE_DB);
|
||||||
b2 = new ChangeBundle(c, messages(), patchSets(ps1, ps2), approvals(a1, a2),
|
b2 = new ChangeBundle(c, messages(cm1, cm2), patchSets(ps1, ps2), approvals(a1, a2),
|
||||||
comments(), reviewers(), REVIEW_DB);
|
comments(), reviewers(), REVIEW_DB);
|
||||||
assertNoDiffs(b1, b2);
|
assertDiffs(b1, b2,
|
||||||
assertNoDiffs(b2, b1);
|
"ChangeMessages differ for Change.Id " + c.getId() + "\n"
|
||||||
|
+ "Only in B:\n " + cm2,
|
||||||
|
"PatchSet.Id sets differ:"
|
||||||
|
+ " [] only in A; [" + ps2.getId() + "] only in B",
|
||||||
|
"PatchSetApproval.Key sets differ:"
|
||||||
|
+ " [] only in A; [" + a2.getKey() + "] only in B");
|
||||||
|
|
||||||
// Both NoteDb.
|
// Both NoteDb.
|
||||||
b1 = new ChangeBundle(c, messages(), patchSets(ps1), approvals(a1),
|
b1 = new ChangeBundle(c, messages(cm1), patchSets(ps1), approvals(a1),
|
||||||
comments(), reviewers(), NOTE_DB);
|
comments(), reviewers(), NOTE_DB);
|
||||||
b2 = new ChangeBundle(c, messages(), patchSets(ps1, ps2), approvals(a1, a2),
|
b2 = new ChangeBundle(c, messages(cm1, cm2), patchSets(ps1, ps2), approvals(a1, a2),
|
||||||
comments(), reviewers(), NOTE_DB);
|
comments(), reviewers(), NOTE_DB);
|
||||||
assertNoDiffs(b1, b2);
|
assertDiffs(b1, b2,
|
||||||
|
"ChangeMessages differ for Change.Id " + c.getId() + "\n"
|
||||||
|
+ "Only in B:\n " + cm2,
|
||||||
|
"PatchSet.Id sets differ:"
|
||||||
|
+ " [] only in A; [" + ps2.getId() + "] only in B",
|
||||||
|
"PatchSetApproval.Key sets differ:"
|
||||||
|
+ " [] only in A; [" + a2.getKey() + "] only in B");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -448,6 +448,26 @@ public class ChangeNotesParserTest extends AbstractChangeNotesTest {
|
|||||||
+ "subject: This is a test change\n");
|
+ "subject: This is a test change\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void currentPatchSet() throws Exception {
|
||||||
|
assertParseSucceeds("Update change\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "Patch-set: 1\n"
|
||||||
|
+ "Current: true");
|
||||||
|
assertParseSucceeds("Update change\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "Patch-set: 1\n"
|
||||||
|
+ "Current: tRUe");
|
||||||
|
assertParseFails("Update change\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "Patch-set: 1\n"
|
||||||
|
+ "Current: false");
|
||||||
|
assertParseFails("Update change\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "Patch-set: 1\n"
|
||||||
|
+ "Current: blah");
|
||||||
|
}
|
||||||
|
|
||||||
private RevCommit writeCommit(String body) throws Exception {
|
private RevCommit writeCommit(String body) throws Exception {
|
||||||
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
|
ChangeNoteUtil noteUtil = injector.getInstance(ChangeNoteUtil.class);
|
||||||
return writeCommit(body, noteUtil.newIdent(
|
return writeCommit(body, noteUtil.newIdent(
|
||||||
|
|||||||
@@ -2631,6 +2631,38 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
|
|||||||
assertThat(notes.getComments()).hasSize(numComments);
|
assertThat(notes.getComments()).hasSize(numComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void currentPatchSet() throws Exception {
|
||||||
|
Change c = newChange();
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
|
||||||
|
|
||||||
|
incrementPatchSet(c);
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
|
||||||
|
|
||||||
|
ChangeUpdate update = newUpdate(c, changeOwner);
|
||||||
|
update.setPatchSetId(new PatchSet.Id(c.getId(), 1));
|
||||||
|
update.setCurrentPatchSet();
|
||||||
|
update.commit();
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
|
||||||
|
|
||||||
|
incrementPatchSet(c);
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(3);
|
||||||
|
|
||||||
|
// Delete PS3, PS1 becomes current, as the most recent event explicitly set
|
||||||
|
// it to current.
|
||||||
|
update = newUpdate(c, changeOwner);
|
||||||
|
update.setPatchSetState(PatchSetState.DELETED);
|
||||||
|
update.commit();
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
|
||||||
|
|
||||||
|
// Delete PS1, PS2 becomes current.
|
||||||
|
update = newUpdate(c, changeOwner);
|
||||||
|
update.setPatchSetId(new PatchSet.Id(c.getId(), 1));
|
||||||
|
update.setPatchSetState(PatchSetState.DELETED);
|
||||||
|
update.commit();
|
||||||
|
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean testJson() {
|
private boolean testJson() {
|
||||||
return noteUtil.getWriteJson();
|
return noteUtil.getWriteJson();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,6 +356,20 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
|
|||||||
commit);
|
commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void currentPatchSet() throws Exception {
|
||||||
|
Change c = newChange();
|
||||||
|
ChangeUpdate update = newUpdate(c, changeOwner);
|
||||||
|
update.setCurrentPatchSet();
|
||||||
|
update.commit();
|
||||||
|
|
||||||
|
assertBodyEquals("Update patch set 1\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "Patch-set: 1\n"
|
||||||
|
+ "Current: true\n",
|
||||||
|
update.getResult());
|
||||||
|
}
|
||||||
|
|
||||||
private RevCommit parseCommit(ObjectId id) throws Exception {
|
private RevCommit parseCommit(ObjectId id) throws Exception {
|
||||||
if (id instanceof RevCommit) {
|
if (id instanceof RevCommit) {
|
||||||
return (RevCommit) id;
|
return (RevCommit) id;
|
||||||
|
|||||||
Reference in New Issue
Block a user