Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  StarredChangesUtil: Fix NPE when ref to be deleted doesn't exist
  StarredChangesUtil: Throw LockFailureException on LOCK_FAILURE
  Add test for creating a change on a non-existing base change
  Rebase: Do not fail with 500 ISE if non-existing change is given as base
  Fix detecting changes of parent trees when computing change kind for merge commit

Change-Id: I8ec9c55d86c3b425e0bce289f4c92cd5b6adbc80
This commit is contained in:
David Pursehouse
2019-08-22 10:57:32 +09:00
6 changed files with 82 additions and 9 deletions

View File

@@ -40,6 +40,7 @@ import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.index.change.ChangeField; import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexer; import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.logging.TraceContext; import com.google.gerrit.server.logging.TraceContext;
@@ -248,15 +249,21 @@ public class StarredChangesUtil {
for (Account.Id accountId : byChangeFromIndex(changeId).keySet()) { for (Account.Id accountId : byChangeFromIndex(changeId).keySet()) {
String refName = RefNames.refsStarredChanges(changeId, accountId); String refName = RefNames.refsStarredChanges(changeId, accountId);
Ref ref = repo.getRefDatabase().getRef(refName); Ref ref = repo.getRefDatabase().getRef(refName);
batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName)); if (ref != null) {
batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName));
}
} }
batchUpdate.execute(rw, NullProgressMonitor.INSTANCE); batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
for (ReceiveCommand command : batchUpdate.getCommands()) { for (ReceiveCommand command : batchUpdate.getCommands()) {
if (command.getResult() != ReceiveCommand.Result.OK) { if (command.getResult() != ReceiveCommand.Result.OK) {
throw new IOException( String message =
String.format( String.format(
"Unstar change %d failed, ref %s could not be deleted: %s", "Unstar change %d failed, ref %s could not be deleted: %s",
changeId.get(), command.getRefName(), command.getResult())); changeId.get(), command.getRefName(), command.getResult());
if (command.getResult() == ReceiveCommand.Result.LOCK_FAILURE) {
throw new LockFailureException(message, batchUpdate);
}
throw new IOException(message);
} }
} }
indexer.index(dbProvider.get(), project, changeId); indexer.index(dbProvider.get(), project, changeId);

View File

@@ -214,13 +214,13 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
rw.parseBody(next); rw.parseBody(next);
if (!next.getFullMessage().equals(prior.getFullMessage())) { if (!next.getFullMessage().equals(prior.getFullMessage())) {
if (isSameDeltaAndTree(prior, next)) { if (isSameDeltaAndTree(rw, prior, next)) {
return ChangeKind.NO_CODE_CHANGE; return ChangeKind.NO_CODE_CHANGE;
} }
return ChangeKind.REWORK; return ChangeKind.REWORK;
} }
if (isSameDeltaAndTree(prior, next)) { if (isSameDeltaAndTree(rw, prior, next)) {
return ChangeKind.NO_CHANGE; return ChangeKind.NO_CHANGE;
} }
@@ -284,7 +284,8 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
return FluentIterable.from(Arrays.asList(parents)).skip(1).toSet(); return FluentIterable.from(Arrays.asList(parents)).skip(1).toSet();
} }
private static boolean isSameDeltaAndTree(RevCommit prior, RevCommit next) { private static boolean isSameDeltaAndTree(RevWalk rw, RevCommit prior, RevCommit next)
throws IOException {
if (!Objects.equals(next.getTree(), prior.getTree())) { if (!Objects.equals(next.getTree(), prior.getTree())) {
return false; return false;
} }
@@ -298,6 +299,10 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
// Make sure that the prior/next delta is the same - not just the tree. // Make sure that the prior/next delta is the same - not just the tree.
// This is done by making sure that the parent trees are equal. // This is done by making sure that the parent trees are equal.
for (int i = 0; i < prior.getParentCount(); i++) { for (int i = 0; i < prior.getParentCount(); i++) {
// Parse parent commits so that their trees are available.
rw.parseCommit(prior.getParent(i));
rw.parseCommit(next.getParent(i));
if (!Objects.equals(next.getParent(i).getTree(), prior.getParent(i).getTree())) { if (!Objects.equals(next.getParent(i).getTree(), prior.getParent(i).getTree())) {
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
@@ -159,10 +160,17 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
return destRef.getObjectId(); return destRef.getObjectId();
} }
Base base = rebaseUtil.parseBase(rsrc, str); Base base;
if (base == null) { try {
throw new ResourceConflictException("base revision is missing: " + str); base = rebaseUtil.parseBase(rsrc, str);
if (base == null) {
throw new ResourceConflictException("base revision is missing: " + str);
}
} catch (NoSuchChangeException e) {
throw new UnprocessableEntityException(
String.format("Base change not found: %s", input.base));
} }
PatchSet.Id baseId = base.patchSet().getId(); PatchSet.Id baseId = base.patchSet().getId();
if (change.getId().equals(baseId.getParentKey())) { if (change.getId().equals(baseId.getParentKey())) {
throw new ResourceConflictException("cannot rebase change onto itself"); throw new ResourceConflictException("cannot rebase change onto itself");

View File

@@ -952,6 +952,16 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name()); assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
} }
@Test
public void rebaseOnNonExistingChange() throws Exception {
String changeId = createChange().getChangeId();
RebaseInput in = new RebaseInput();
in.base = "999999";
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("Base change not found: " + in.base);
gApi.changes().id(changeId).rebase(in);
}
@Test @Test
public void rebaseNotAllowedWithoutPermission() throws Exception { public void rebaseNotAllowedWithoutPermission() throws Exception {
// Create two changes both with the same parent // Create two changes both with the same parent

View File

@@ -247,6 +247,23 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
assertNotSticky(EnumSet.of(REWORK, NO_CODE_CHANGE, TRIVIAL_REBASE)); assertNotSticky(EnumSet.of(REWORK, NO_CODE_CHANGE, TRIVIAL_REBASE));
} }
@Test
public void notStickyWithCopyOnNoChangeWhenSecondParentIsUpdated() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().get("Code-Review").setCopyAllScoresIfNoChange(true);
u.save();
}
String changeId = createChangeForMergeCommit();
vote(admin, changeId, 2, 1);
vote(user, changeId, -2, -1);
updateSecondParent(changeId);
ChangeInfo c = detailedChange(changeId);
assertVotes(c, admin, 0, 0, null);
assertVotes(c, user, 0, 0, null);
}
@Test @Test
public void removedVotesNotSticky() throws Exception { public void removedVotesNotSticky() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) { try (ProjectConfigUpdate u = updateProject(project)) {
@@ -512,6 +529,24 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
assertThat(getChangeKind(changeId)).isEqualTo(MERGE_FIRST_PARENT_UPDATE); assertThat(getChangeKind(changeId)).isEqualTo(MERGE_FIRST_PARENT_UPDATE);
} }
private void updateSecondParent(String changeId) throws Exception {
ChangeInfo c = detailedChange(changeId);
List<CommitInfo> parents = c.revisions.get(c.currentRevision).commit.parents;
String parent1 = parents.get(0).commit;
String parent2 = parents.get(1).commit;
RevCommit commitParent1 = testRepo.getRevWalk().parseCommit(ObjectId.fromString(parent1));
testRepo.reset(parent2);
PushOneCommit.Result newParent2 = createChange("new parent 2", "p2-2.txt", "content 2-2");
PushOneCommit merge = pushFactory.create(db, admin.getIdent(), testRepo, changeId);
merge.setParents(ImmutableList.of(commitParent1, newParent2.getCommit()));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
assertThat(getChangeKind(changeId)).isEqualTo(REWORK);
}
private String cherryPick(String changeId, ChangeKind changeKind) throws Exception { private String cherryPick(String changeId, ChangeKind changeKind) throws Exception {
switch (changeKind) { switch (changeKind) {
case REWORK: case REWORK:

View File

@@ -246,6 +246,14 @@ public class CreateChangeIT extends AbstractDaemonTest {
String.format("Commit %s doesn't exist on ref refs/heads/foo", input.baseCommit)); String.format("Commit %s doesn't exist on ref refs/heads/foo", input.baseCommit));
} }
@Test
public void createChangeOnNonExistingBaseChangeFails() throws Exception {
ChangeInput input = newChangeInput(ChangeStatus.NEW);
input.baseChange = "999999";
assertCreateFails(
input, UnprocessableEntityException.class, "Base change not found: " + input.baseChange);
}
@Test @Test
public void createChangeWithoutAccessToParentCommitFails() throws Exception { public void createChangeWithoutAccessToParentCommitFails() throws Exception {
Map<String, PushOneCommit.Result> results = Map<String, PushOneCommit.Result> results =