diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java index b882c9f7b2..f3536f93d7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java @@ -186,6 +186,14 @@ public abstract class AbstractSubmit extends AbstractDaemonTest { return push.to("refs/for/master/" + topic); } + protected PushOneCommit.Result createChange(TestRepository repo, + String branch, String subject, String fileName, String content, + String topic) throws Exception { + PushOneCommit push = + pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content); + return push.to("refs/for/" + branch + "/" + name(topic)); + } + protected void submit(String changeId) throws Exception { submit(changeId, HttpStatus.SC_OK); } @@ -303,12 +311,19 @@ public abstract class AbstractSubmit extends AbstractDaemonTest { return getHead(repo, "HEAD"); } - protected RevCommit getRemoteHead() throws IOException { + protected RevCommit getRemoteHead(Project.NameKey project, String branch) + throws IOException { try (Repository repo = repoManager.openRepository(project)) { - return getHead(repo, "refs/heads/master"); + return getHead(repo, "refs/heads/" + branch); } } + protected RevCommit getRemoteHead() + throws IOException { + return getRemoteHead(project, "master"); + } + + protected List getRemoteLog(Project.NameKey project, String branch) throws IOException { try (Repository repo = repoManager.openRepository(project); diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java index 95aef9eebd..a9beefec50 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java @@ -3,8 +3,11 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.extensions.api.projects.BranchInput; import com.google.gerrit.extensions.client.SubmitType; +import com.google.gerrit.reviewdb.client.Project; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -45,8 +48,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge { submitStatusOnly(change3.getChangeId()); submit(change4.getChangeId()); - List log = getRemoteLog(); - RevCommit tip = log.get(0); + RevCommit tip = getRemoteLog().get(0); assertThat(tip.getParent(1).getShortMessage()).isEqualTo( change4.getCommit().getShortMessage()); @@ -60,4 +62,226 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge { assertThat(tip.getParent(0).getId()).isEqualTo(initialHead.getId()); } + + @Test + public void submitChangesAcrossRepos() throws Exception { + Project.NameKey p1 = createProject("project-where-we-submit"); + Project.NameKey p2 = createProject("project-impacted-via-topic"); + Project.NameKey p3 = createProject("project-impacted-indirectly-via-topic"); + + RevCommit initialHead2 = getRemoteHead(p2, "master"); + RevCommit initialHead3 = getRemoteHead(p3, "master"); + + TestRepository repo1 = cloneProject(p1); + TestRepository repo2 = cloneProject(p2); + TestRepository repo3 = cloneProject(p3); + + PushOneCommit.Result change1a = createChange(repo1, "master", + "An ancestor of the change we want to submit", + "a.txt", "1", "dependent-topic"); + PushOneCommit.Result change1b = createChange(repo1, "master", + "We're interested in submitting this change", + "a.txt", "2", "topic-to-submit"); + + PushOneCommit.Result change2a = createChange(repo2, "master", + "indirection level 1", + "a.txt", "1", "topic-indirect"); + PushOneCommit.Result change2b = createChange(repo2, "master", + "should go in with first change", + "a.txt", "2", "dependent-topic"); + + PushOneCommit.Result change3 = createChange(repo3, "master", + "indirection level 2", + "a.txt", "1", "topic-indirect"); + + approve(change1a.getChangeId()); + approve(change2a.getChangeId()); + approve(change2b.getChangeId()); + approve(change3.getChangeId()); + submit(change1b.getChangeId()); + + RevCommit tip1 = getRemoteLog(p1, "master").get(0); + RevCommit tip2 = getRemoteLog(p2, "master").get(0); + RevCommit tip3 = getRemoteLog(p3, "master").get(0); + + assertThat(tip1.getShortMessage()).isEqualTo( + change1b.getCommit().getShortMessage()); + + if (isSubmitWholeTopicEnabled()) { + assertThat(tip2.getShortMessage()).isEqualTo( + change2b.getCommit().getShortMessage()); + assertThat(tip3.getShortMessage()).isEqualTo( + change3.getCommit().getShortMessage()); + } else { + assertThat(tip2.getShortMessage()).isEqualTo( + initialHead2.getShortMessage()); + assertThat(tip3.getShortMessage()).isEqualTo( + initialHead3.getShortMessage()); + } + } + + @Test + public void submitChangesAcrossReposBlocked() throws Exception { + Project.NameKey p1 = createProject("project-where-we-submit"); + Project.NameKey p2 = createProject("project-impacted-via-topic"); + Project.NameKey p3 = createProject("project-impacted-indirectly-via-topic"); + + TestRepository repo1 = cloneProject(p1); + TestRepository repo2 = cloneProject(p2); + TestRepository repo3 = cloneProject(p3); + + RevCommit initialHead1 = getRemoteHead(p1, "master"); + RevCommit initialHead2 = getRemoteHead(p2, "master"); + RevCommit initialHead3 = getRemoteHead(p3, "master"); + + PushOneCommit.Result change1a = createChange(repo1, "master", + "An ancestor of the change we want to submit", + "a.txt", "1", "dependent-topic"); + PushOneCommit.Result change1b = createChange(repo1, "master", + "we're interested to submit this change", + "a.txt", "2", "topic-to-submit"); + + PushOneCommit.Result change2a = createChange(repo2, "master", + "indirection level 2a", + "a.txt", "1", "topic-indirect"); + PushOneCommit.Result change2b = createChange(repo2, "master", + "should go in with first change", + "a.txt", "2", "dependent-topic"); + + PushOneCommit.Result change3 = createChange(repo3, "master", + "indirection level 2b", + "a.txt", "1", "topic-indirect"); + + // Create a merge conflict for change3 which is only indirectly related + // via topics. + repo3.reset(initialHead3); + PushOneCommit.Result change3Conflict = createChange(repo3, "master", + "conflicting change", + "a.txt", "2\n2", "conflicting-topic"); + submit(change3Conflict.getChangeId()); + RevCommit tipConflict = getRemoteLog(p3, "master").get(0); + assertThat(tipConflict.getShortMessage()).isEqualTo( + change3Conflict.getCommit().getShortMessage()); + + approve(change1a.getChangeId()); + approve(change2a.getChangeId()); + approve(change2b.getChangeId()); + approve(change3.getChangeId()); + + if (isSubmitWholeTopicEnabled()) { + submitWithConflict(change1b.getChangeId()); + } else { + submit(change1b.getChangeId()); + } + + RevCommit tip1 = getRemoteLog(p1, "master").get(0); + RevCommit tip2 = getRemoteLog(p2, "master").get(0); + RevCommit tip3 = getRemoteLog(p3, "master").get(0); + if (isSubmitWholeTopicEnabled()) { + assertThat(tip1.getShortMessage()).isEqualTo( + initialHead1.getShortMessage()); + assertThat(tip2.getShortMessage()).isEqualTo( + initialHead2.getShortMessage()); + assertThat(tip3.getShortMessage()).isEqualTo( + change3Conflict.getCommit().getShortMessage()); + } else { + assertThat(tip1.getShortMessage()).isEqualTo( + change1b.getCommit().getShortMessage()); + assertThat(tip2.getShortMessage()).isEqualTo( + initialHead2.getShortMessage()); + assertThat(tip3.getShortMessage()).isEqualTo( + change3Conflict.getCommit().getShortMessage()); + } + } + + @Test + public void submitWithMergedAncestorsOnOtherBranch() throws Exception { + PushOneCommit.Result change1 = createChange(testRepo, "master", + "base commit", + "a.txt", "1", ""); + submit(change1.getChangeId()); + + gApi.projects() + .name(project.get()) + .branch("branch") + .create(new BranchInput()); + + PushOneCommit.Result change2 = createChange(testRepo, "master", + "We want to commit this to master first", + "a.txt", "2", ""); + + submit(change2.getChangeId()); + + RevCommit tip1 = getRemoteLog(project, "master").get(0); + assertThat(tip1.getShortMessage()).isEqualTo( + change2.getCommit().getShortMessage()); + + RevCommit tip2 = getRemoteLog(project, "branch").get(0); + assertThat(tip2.getShortMessage()).isEqualTo( + change1.getCommit().getShortMessage()); + + PushOneCommit.Result change3 = createChange(testRepo, "branch", + "This commit is based on master, which includes change2, " + + "but is targeted at branch, which doesn't include it.", + "a.txt", "3", ""); + + submit(change3.getChangeId()); + + List log3 = getRemoteLog(project, "branch"); + assertThat(log3.get(0).getShortMessage()).isEqualTo( + change3.getCommit().getShortMessage()); + assertThat(log3.get(1).getShortMessage()).isEqualTo( + change2.getCommit().getShortMessage()); + } + + @Test + public void submitWithOpenAncestorsOnOtherBranch() throws Exception { + PushOneCommit.Result change1 = createChange(testRepo, "master", + "base commit", + "a.txt", "1", ""); + submit(change1.getChangeId()); + + gApi.projects() + .name(project.get()) + .branch("branch") + .create(new BranchInput()); + + PushOneCommit.Result change2 = createChange(testRepo, "master", + "We want to commit this to master first", + "a.txt", "2", ""); + + approve(change2.getChangeId()); + + RevCommit tip1 = getRemoteLog(project, "master").get(0); + assertThat(tip1.getShortMessage()).isEqualTo( + change1.getCommit().getShortMessage()); + + RevCommit tip2 = getRemoteLog(project, "branch").get(0); + assertThat(tip2.getShortMessage()).isEqualTo( + change1.getCommit().getShortMessage()); + + PushOneCommit.Result change3a = createChange(testRepo, "branch", + "This commit is based on change2 pending for master, " + + "but is targeted itself at branch, which doesn't include it.", + "a.txt", "3", "a-topic-here"); + + Project.NameKey p3 = createProject("project-related-to-change3"); + TestRepository repo3 = cloneProject(p3); + RevCommit initialHead = getRemoteHead(p3, "master"); + PushOneCommit.Result change3b = createChange(repo3, "master", + "some accompanying changes for change3a in another repo " + + "tied together via topic", + "a.txt", "1", "a-topic-here"); + approve(change3b.getChangeId()); + + submitWithConflict(change3a.getChangeId()); + + RevCommit tipbranch = getRemoteLog(project, "branch").get(0); + assertThat(tipbranch.getShortMessage()).isEqualTo( + change1.getCommit().getShortMessage()); + + RevCommit tipmaster = getRemoteLog(p3, "master").get(0); + assertThat(tipmaster.getShortMessage()).isEqualTo( + initialHead.getShortMessage()); + } }