| // Copyright (C) 2021 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.acceptance.server.change; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME; |
| import static com.google.gerrit.entities.Patch.COMMIT_MSG; |
| import static com.google.gerrit.entities.Patch.MERGE_LIST; |
| import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.MoreCollectors; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.NoHttpd; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.extensions.api.changes.DraftInput; |
| import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput; |
| import com.google.gerrit.extensions.client.Comment; |
| import com.google.gerrit.extensions.client.Side; |
| import com.google.gerrit.extensions.common.ChangeInfo; |
| import com.google.gerrit.extensions.common.ChangeInput; |
| import com.google.gerrit.extensions.common.CommentInfo; |
| import com.google.gerrit.extensions.common.ContextLineInfo; |
| import com.google.gerrit.server.change.FileContentUtil; |
| import com.google.inject.Inject; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| import java.util.stream.IntStream; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| @NoHttpd |
| public class CommentContextIT extends AbstractDaemonTest { |
| /** The commit message of a single commit. */ |
| private static final String SUBJECT = |
| String.join( |
| "\n", |
| "Commit Header", |
| "", |
| "This commit is doing something extremely important", |
| "", |
| "Footer: value"); |
| |
| private static final String FILE_CONTENT = |
| String.join("\n", "Line 1 of file", "", "Line 3 of file", "", "", "Line 6 of file"); |
| private static final ObjectId dummyCommit = |
| ObjectId.fromString("93e2901bc0b4719ef6081ee6353b49c9cdd97614"); |
| |
| @Inject private RequestScopeOperations requestScopeOperations; |
| |
| @Before |
| public void setup() throws Exception { |
| requestScopeOperations.setApiUser(user.id()); |
| } |
| |
| @Test |
| public void commentContextForGitSubmoduleFiles() throws Exception { |
| String submodulePath = "submodule_path"; |
| |
| PushOneCommit push = |
| pushFactory.create(admin.newIdent(), testRepo).addGitSubmodule(submodulePath, dummyCommit); |
| PushOneCommit.Result pushResult = push.to("refs/for/master"); |
| String changeId = pushResult.getChangeId(); |
| CommentInput comment = |
| CommentsUtil.newComment(submodulePath, Side.REVISION, 1, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, pushResult.getCommit().name(), comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).path).isEqualTo(submodulePath); |
| assertThat(comments.get(0).contextLines) |
| .isEqualTo(createContextLines("1", "Subproject commit " + dummyCommit.getName())); |
| } |
| |
| @Test |
| public void commentContextForRootCommitOnParentSideReturnsEmptyContext() throws Exception { |
| // Create a change in a new branch, making the patchset commit a root commit |
| ChangeInfo changeInfo = createChangeInNewBranch("newBranch"); |
| String changeId = changeInfo.changeId; |
| String revision = changeInfo.revisions.keySet().iterator().next(); |
| |
| // Write a comment on the parent side of the commit message. Set parent=1 because if unset, our |
| // handler in PostReview assumes we want to write on the auto-merge commit and fails the |
| // pre-condition. |
| CommentInput comment = CommentsUtil.newComment(COMMIT_MSG, Side.PARENT, 0, "comment", false); |
| comment.parent = 1; |
| CommentsUtil.addComments(gApi, changeId, revision, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| assertThat(comments).hasSize(1); |
| CommentInfo c = comments.stream().collect(MoreCollectors.onlyElement()); |
| assertThat(c.commitId).isEqualTo(ObjectId.zeroId().name()); |
| assertThat(c.contextLines).isEmpty(); |
| } |
| |
| @Test |
| public void commentContextForCommitMessageForLineComment() throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, FILE_CONTENT, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = CommentsUtil.newComment(COMMIT_MSG, Side.REVISION, 7, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| |
| // The first few lines of the commit message are the headers, e.g. |
| // Parent: ... |
| // Author: ... |
| // AuthorDate: ... |
| // etc... |
| assertThat(comments.get(0).contextLines) |
| .containsExactlyElementsIn(createContextLines("7", "Commit Header")); |
| } |
| |
| @Test |
| public void commentContextForMergeList() throws Exception { |
| PushOneCommit.Result result = createMergeCommitChange("refs/for/master"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = CommentsUtil.newComment(MERGE_LIST, Side.REVISION, 1, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).contextLines) |
| .containsExactlyElementsIn(createContextLines("1", "Merge List:")); |
| } |
| |
| @Test |
| public void commentContextForCommitMessageForRangeComment() throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, FILE_CONTENT, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = |
| CommentsUtil.newComment( |
| COMMIT_MSG, Side.REVISION, createCommentRange(7, 9), "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| |
| // The first few lines of the commit message are the headers, e.g. |
| // Parent: ... |
| // Author: ... |
| // AuthorDate: ... |
| // etc... |
| assertThat(comments.get(0).contextLines) |
| .containsExactlyElementsIn( |
| createContextLines( |
| "7", |
| "Commit Header", |
| "8", |
| "", |
| "9", |
| "This commit is doing something extremely important")); |
| } |
| |
| @Test |
| public void commentContextForCommitMessageInvalidLine() throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, FILE_CONTENT, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = |
| CommentsUtil.newComment(COMMIT_MSG, Side.REVISION, 100, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).contextLines).isEmpty(); |
| } |
| |
| @Test |
| public void listChangeCommentsWithContextEnabled() throws Exception { |
| PushOneCommit.Result r1 = createChange(); |
| |
| ImmutableList.Builder<String> content = ImmutableList.builder(); |
| for (int i = 1; i <= 10; i++) { |
| content.add("line_" + i); |
| } |
| |
| PushOneCommit.Result r2 = |
| pushFactory |
| .create( |
| admin.newIdent(), |
| testRepo, |
| PushOneCommit.SUBJECT, |
| FILE_NAME, |
| content.build().stream().collect(Collectors.joining("\n")), |
| r1.getChangeId()) |
| .to("refs/for/master"); |
| |
| CommentsUtil.addCommentOnLine(gApi, r2, "nit: please fix", 1); |
| CommentsUtil.addCommentOnRange(gApi, r2, "looks good", createCommentRange(2, 5)); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(r2.getChangeId()).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(2); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("nit: please fix")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn(createContextLines("1", "line_1")); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("looks good")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn( |
| createContextLines("2", "line_2", "3", "line_3", "4", "line_4", "5", "line_5")); |
| } |
| |
| @Test |
| public void listChangeDraftsWithContextEnabled() throws Exception { |
| PushOneCommit.Result r1 = createChange(); |
| PushOneCommit.Result r2 = |
| pushFactory |
| .create( |
| admin.newIdent(), |
| testRepo, |
| PushOneCommit.SUBJECT, |
| FILE_NAME, |
| "line_1\nline_2\nline_3", |
| r1.getChangeId()) |
| .to("refs/for/master"); |
| |
| DraftInput in = CommentsUtil.newDraft(FILE_NAME, Side.REVISION, 2, "comment 1"); |
| gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).createDraft(in); |
| |
| // Test the getAsList interface |
| List<CommentInfo> comments = |
| gApi.changes().id(r2.getChangeId()).draftsRequest().withContext(true).getAsList(); |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).message).isEqualTo("comment 1"); |
| assertThat(comments.get(0).contextLines) |
| .containsExactlyElementsIn(createContextLines("2", "line_2")); |
| |
| // Also test the get interface |
| Map<String, List<CommentInfo>> commentsMap = |
| gApi.changes().id(r2.getChangeId()).draftsRequest().withContext(true).get(); |
| assertThat(commentsMap).hasSize(1); |
| assertThat(commentsMap.values().iterator().next()).hasSize(1); |
| CommentInfo onlyComment = commentsMap.values().iterator().next().get(0); |
| assertThat(onlyComment.message).isEqualTo("comment 1"); |
| assertThat(onlyComment.contextLines) |
| .containsExactlyElementsIn(createContextLines("2", "line_2")); |
| } |
| |
| @Test |
| public void commentContextForCommentsOnDifferentPatchsets() throws Exception { |
| PushOneCommit.Result r1 = createChange(); |
| |
| ImmutableList.Builder<String> content = ImmutableList.builder(); |
| for (int i = 1; i <= 10; i++) { |
| content.add("line_" + i); |
| } |
| |
| PushOneCommit.Result r2 = |
| pushFactory |
| .create( |
| admin.newIdent(), |
| testRepo, |
| PushOneCommit.SUBJECT, |
| FILE_NAME, |
| String.join("\n", content.build()), |
| r1.getChangeId()) |
| .to("refs/for/master"); |
| |
| PushOneCommit.Result r3 = |
| pushFactory |
| .create( |
| admin.newIdent(), |
| testRepo, |
| PushOneCommit.SUBJECT, |
| FILE_NAME, |
| content.build().stream().collect(Collectors.joining("\n")), |
| r1.getChangeId()) |
| .to("refs/for/master"); |
| |
| CommentsUtil.addCommentOnLine(gApi, r2, "r2: please fix", 1); |
| CommentsUtil.addCommentOnRange(gApi, r2, "r2: looks good", createCommentRange(2, 3)); |
| CommentsUtil.addCommentOnLine(gApi, r3, "r3: please fix", 6); |
| CommentsUtil.addCommentOnRange(gApi, r3, "r3: looks good", createCommentRange(7, 8)); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(r2.getChangeId()).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(4); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("r2: please fix")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn(createContextLines("1", "line_1")); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("r2: looks good")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn(createContextLines("2", "line_2", "3", "line_3")); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("r3: please fix")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn(createContextLines("6", "line_6")); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("r3: looks good")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn(createContextLines("7", "line_7", "8", "line_8")); |
| } |
| |
| @Test |
| public void commentContextIsEmptyForPatchsetLevelComments() throws Exception { |
| PushOneCommit.Result result = createChange(); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = |
| CommentsUtil.newCommentWithOnlyMandatoryFields(PATCHSET_LEVEL, "comment"); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).contextLines).isEmpty(); |
| } |
| |
| @Test |
| public void commentContextWithZeroPadding() throws Exception { |
| String changeId = createChangeWithComment(3, 4); |
| assertContextLines(changeId, /* contextPadding= */ 0, ImmutableList.of(3, 4)); |
| } |
| |
| @Test |
| public void commentContextWithSmallPadding() throws Exception { |
| String changeId = createChangeWithComment(3, 4); |
| assertContextLines(changeId, /* contextPadding= */ 1, ImmutableList.of(2, 3, 4, 5)); |
| } |
| |
| @Test |
| public void commentContextWithSmallPaddingAtTheBeginningOfFile() throws Exception { |
| String changeId = createChangeWithComment(1, 2); |
| assertContextLines(changeId, /* contextPadding= */ 2, ImmutableList.of(1, 2, 3, 4)); |
| } |
| |
| @Test |
| public void commentContextWithPaddingLargerThanFileSize() throws Exception { |
| String changeId = createChangeWithComment(3, 3); |
| assertContextLines( |
| changeId, |
| /* contextPadding= */ 20, |
| ImmutableList.of(1, 2, 3, 4, 5, 6)); // file only contains six lines. |
| } |
| |
| @Test |
| public void commentContextWithLargePaddingReturnsAdjustedMaximumPadding() throws Exception { |
| String changeId = createChangeWithCommentLarge(250, 250); |
| assertContextLines( |
| changeId, |
| /* contextPadding= */ 300, |
| IntStream.range(200, 301).boxed().collect(ImmutableList.toImmutableList())); |
| } |
| |
| @Test |
| public void commentContextReturnsCorrectContentTypeForCommitMessage() throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, FILE_CONTENT, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = CommentsUtil.newComment(COMMIT_MSG, Side.REVISION, 7, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).path).isEqualTo(COMMIT_MSG); |
| assertThat(comments.get(0).sourceContentType) |
| .isEqualTo(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE); |
| } |
| |
| @Test |
| public void commentContextReturnsCorrectContentType_java() throws Exception { |
| String javaContent = |
| "public class Main {\n" |
| + " public static void main(String[]args){\n" |
| + " if(args==null){\n" |
| + " System.err.println(\"Something\");\n" |
| + " }\n" |
| + " }\n" |
| + " }"; |
| String fileName = "src.java"; |
| String changeId = createChangeWithContent(fileName, javaContent, /* line= */ 4); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).path).isEqualTo(fileName); |
| assertThat(comments.get(0).contextLines) |
| .isEqualTo(createContextLines("4", " System.err.println(\"Something\");")); |
| assertThat(comments.get(0).sourceContentType).isEqualTo("text/x-java"); |
| } |
| |
| @Test |
| public void commentContextReturnsCorrectContentType_cpp() throws Exception { |
| String cppContent = |
| "#include <iostream>\n" |
| + "\n" |
| + "int main() {\n" |
| + " std::cout << \"Hello World!\";\n" |
| + " return 0;\n" |
| + "}"; |
| String fileName = "src.cpp"; |
| String changeId = createChangeWithContent(fileName, cppContent, /* line= */ 4); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat(comments.get(0).path).isEqualTo(fileName); |
| assertThat(comments.get(0).contextLines) |
| .isEqualTo(createContextLines("4", " std::cout << \"Hello World!\";")); |
| assertThat(comments.get(0).sourceContentType).isEqualTo("text/x-c++src"); |
| } |
| |
| @Test |
| public void listChangeCommentsWithContextEnabled_twoRangeCommentsWithTheSameContext() |
| throws Exception { |
| PushOneCommit.Result r1 = createChange(); |
| |
| ImmutableList.Builder<String> content = ImmutableList.builder(); |
| for (int i = 1; i <= 10; i++) { |
| content.add("line_" + i); |
| } |
| |
| PushOneCommit.Result r2 = |
| pushFactory |
| .create( |
| admin.newIdent(), |
| testRepo, |
| PushOneCommit.SUBJECT, |
| FILE_NAME, |
| content.build().stream().collect(Collectors.joining("\n")), |
| r1.getChangeId()) |
| .to("refs/for/master"); |
| |
| CommentsUtil.addCommentOnRange(gApi, r2, "looks good", createCommentRange(2, 5)); |
| CommentsUtil.addCommentOnRange(gApi, r2, "are you sure?", createCommentRange(2, 5)); |
| |
| List<CommentInfo> comments = |
| gApi.changes().id(r2.getChangeId()).commentsRequest().withContext(true).getAsList(); |
| |
| assertThat(comments).hasSize(2); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("looks good")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn( |
| createContextLines("2", "line_2", "3", "line_3", "4", "line_4", "5", "line_5")); |
| |
| assertThat( |
| comments.stream() |
| .filter(c -> c.message.equals("are you sure?")) |
| .collect(MoreCollectors.onlyElement()) |
| .contextLines) |
| .containsExactlyElementsIn( |
| createContextLines("2", "line_2", "3", "line_3", "4", "line_4", "5", "line_5")); |
| } |
| |
| private String createChangeWithContent(String fileName, String fileContent, int line) |
| throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, fileName, fileContent, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| CommentInput comment = CommentsUtil.newComment(fileName, Side.REVISION, line, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| return changeId; |
| } |
| |
| private String createChangeWithComment(int startLine, int endLine) throws Exception { |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, FILE_CONTENT, "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| Comment.Range commentRange = createCommentRange(startLine, endLine); |
| CommentInput comment = |
| CommentsUtil.newComment(FILE_NAME, Side.REVISION, commentRange, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| return changeId; |
| } |
| |
| private String createChangeWithCommentLarge(int startLine, int endLine) throws Exception { |
| StringBuilder largeContent = new StringBuilder(); |
| for (int i = 0; i < 1000; i++) { |
| largeContent.append("line " + i + "\n"); |
| } |
| PushOneCommit.Result result = |
| createChange(testRepo, "master", SUBJECT, FILE_NAME, largeContent.toString(), "topic"); |
| String changeId = result.getChangeId(); |
| String ps1 = result.getCommit().name(); |
| |
| Comment.Range commentRange = createCommentRange(startLine, endLine); |
| CommentInput comment = |
| CommentsUtil.newComment(FILE_NAME, Side.REVISION, commentRange, "comment", false); |
| CommentsUtil.addComments(gApi, changeId, ps1, comment); |
| return changeId; |
| } |
| |
| private void assertContextLines( |
| String changeId, int contextPadding, ImmutableList<Integer> expectedLines) throws Exception { |
| List<CommentInfo> comments = |
| gApi.changes() |
| .id(changeId) |
| .commentsRequest() |
| .withContext(true) |
| .contextPadding(contextPadding) |
| .getAsList(); |
| |
| assertThat(comments).hasSize(1); |
| assertThat( |
| comments.get(0).contextLines.stream() |
| .map(c -> c.lineNumber) |
| .collect(Collectors.toList())) |
| .containsExactlyElementsIn(expectedLines); |
| } |
| |
| private Comment.Range createCommentRange(int startLine, int endLine) { |
| Comment.Range range = new Comment.Range(); |
| range.startLine = startLine; |
| range.endLine = endLine; |
| return range; |
| } |
| |
| private List<ContextLineInfo> createContextLines(String... args) { |
| List<ContextLineInfo> result = new ArrayList<>(); |
| for (int i = 0; i < args.length; i += 2) { |
| int lineNbr = Integer.parseInt(args[i]); |
| String contextLine = args[i + 1]; |
| ContextLineInfo info = new ContextLineInfo(lineNbr, contextLine); |
| result.add(info); |
| } |
| return result; |
| } |
| |
| private ChangeInfo createChangeInNewBranch(String branchName) throws Exception { |
| ChangeInput in = new ChangeInput(); |
| in.project = project.get(); |
| in.branch = branchName; |
| in.newBranch = true; |
| in.subject = "New changes"; |
| return gApi.changes().create(in).get(); |
| } |
| } |