Merge "Add REST endpoint to list the commits that are integrated by a merge"

This commit is contained in:
David Pursehouse
2016-09-13 09:29:12 +00:00
committed by Gerrit Code Review
7 changed files with 297 additions and 1 deletions

View File

@@ -2572,6 +2572,60 @@ describes the revision.
Adding query parameter `links` (for example `/changes/.../commit?links`)
returns a link:#commit-info[CommitInfo] with the additional field `web_links`.
[[get-merge-list]]
=== Get Merge List
--
'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/mergelist'
--
Returns the list of commits that are being integrated into a target
branch by a merge commit. By default the first parent is assumed to be
uninteresting. By using the `parent` option another parent can be set
as uninteresting (parents are 1-based).
The list of commits is returned as a list of
link:#commit-info[CommitInfo] entities. Web links are only included if
the `links` option was set.
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/7e30d802b890ec8d0be45b1cc2a8ef092bcfc858/mergelist HTTP/1.0
----
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json; charset=UTF-8
)]}'
[
{
"commit": "674ac754f91e64a0efb8087e59a176484bd534d1",
"parents": [
{
"commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
"subject": "Migrate contributor agreements to All-Projects."
}
],
"author": {
"name": "Shawn O. Pearce",
"email": "sop@google.com",
"date": "2012-04-24 18:08:08.000000000",
"tz": -420
},
"committer": {
"name": "Shawn O. Pearce",
"email": "sop@google.com",
"date": "2012-04-24 18:08:08.000000000",
"tz": -420
},
"subject": "Use an EventBus to manage star icons",
"message": "Use an EventBus to manage star icons\n\nImage widgets that need to ..."
}
]
----
[[get-revision-actions]]
=== Get Revision Actions
--

View File

@@ -0,0 +1,80 @@
// Copyright (C) 2016 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.api.change;
import static com.google.common.truth.Truth.assertThat;
import static org.eclipse.jgit.lib.Constants.HEAD;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.extensions.common.CommitInfo;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
import java.util.List;
@NoHttpd
public class GetMergeListIT extends AbstractDaemonTest {
@Test
public void getMergeList() throws Exception {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
PushOneCommit.Result gp1 = pushFactory
.create(db, admin.getIdent(), testRepo, "grand parent 1",
ImmutableMap.of("foo", "foo-1.1", "bar", "bar-1.1"))
.to("refs/for/master");
PushOneCommit.Result p1 = pushFactory
.create(db, admin.getIdent(), testRepo, "parent 1",
ImmutableMap.of("foo", "foo-1.2", "bar", "bar-1.2"))
.to("refs/for/master");
// reset HEAD in order to create a sibling of the first change
testRepo.reset(initial);
PushOneCommit.Result gp2 = pushFactory
.create(db, admin.getIdent(), testRepo, "grand parent 1",
ImmutableMap.of("foo", "foo-2.1", "bar", "bar-2.1"))
.to("refs/for/master");
PushOneCommit.Result p2 = pushFactory
.create(db, admin.getIdent(), testRepo, "parent 2",
ImmutableMap.of("foo", "foo-2.2", "bar", "bar-2.2"))
.to("refs/for/master");
PushOneCommit m = pushFactory.create(db, admin.getIdent(), testRepo,
"merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
PushOneCommit.Result result = m.to("refs/for/master");
result.assertOkStatus();
List<CommitInfo> mergeList =
gApi.changes().id(result.getChangeId()).current().getMergeList().get();
assertThat(mergeList).hasSize(2);
assertThat(mergeList.get(0).commit).isEqualTo(p2.getCommit().name());
assertThat(mergeList.get(1).commit).isEqualTo(gp2.getCommit().name());
mergeList = gApi.changes().id(result.getChangeId()).current().getMergeList()
.withUninterestingParent(2).get();
assertThat(mergeList).hasSize(2);
assertThat(mergeList.get(0).commit).isEqualTo(p1.getCommit().name());
assertThat(mergeList.get(1).commit).isEqualTo(gp1.getCommit().name());
}
}

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.extensions.api.changes;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.common.TestSubmitRuleInput;
@@ -72,6 +73,33 @@ public interface RevisionApi {
SubmitType submitType() throws RestApiException;
SubmitType testSubmitType(TestSubmitRuleInput in) throws RestApiException;
MergeListRequest getMergeList() throws RestApiException;
abstract class MergeListRequest {
private boolean addLinks;
private int uninterestingParent = 1;
public abstract List<CommitInfo> get() throws RestApiException;
public MergeListRequest withLinks() {
this.addLinks = true;
return this;
}
public MergeListRequest withUninterestingParent(int uninterestingParent) {
this.uninterestingParent = uninterestingParent;
return this;
}
public boolean getAddLinks() {
return addLinks;
}
public int getUninterestingParent() {
return uninterestingParent;
}
}
/**
* A default implementation which allows source compatibility
* when adding new methods to the interface.
@@ -217,5 +245,10 @@ public interface RevisionApi {
throws RestApiException {
throw new NotImplementedException();
}
@Override
public MergeListRequest getMergeList() throws RestApiException {
throw new NotImplementedException();
}
}
}

View File

@@ -30,6 +30,7 @@ import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.common.TestSubmitRuleInput;
@@ -44,6 +45,7 @@ import com.google.gerrit.server.change.DeleteDraftPatchSet;
import com.google.gerrit.server.change.DraftComments;
import com.google.gerrit.server.change.FileResource;
import com.google.gerrit.server.change.Files;
import com.google.gerrit.server.change.GetMergeList;
import com.google.gerrit.server.change.GetPatch;
import com.google.gerrit.server.change.GetRevisionActions;
import com.google.gerrit.server.change.ListRevisionComments;
@@ -63,6 +65,7 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.lib.Repository;
@@ -105,6 +108,7 @@ class RevisionApiImpl implements RevisionApi {
private final GetRevisionActions revisionActions;
private final TestSubmitType testSubmitType;
private final TestSubmitType.Get getSubmitType;
private final Provider<GetMergeList> getMergeList;
@Inject
RevisionApiImpl(GitRepositoryManager repoManager,
@@ -133,6 +137,7 @@ class RevisionApiImpl implements RevisionApi {
GetRevisionActions revisionActions,
TestSubmitType testSubmitType,
TestSubmitType.Get getSubmitType,
Provider<GetMergeList> getMergeList,
@Assisted RevisionResource r) {
this.repoManager = repoManager;
this.changes = changes;
@@ -160,6 +165,7 @@ class RevisionApiImpl implements RevisionApi {
this.revisionActions = revisionActions;
this.testSubmitType = testSubmitType;
this.getSubmitType = getSubmitType;
this.getMergeList = getMergeList;
this.revision = r;
}
@@ -428,4 +434,21 @@ class RevisionApiImpl implements RevisionApi {
throw new RestApiException("Cannot test submit type", e);
}
}
@Override
public MergeListRequest getMergeList() throws RestApiException {
return new MergeListRequest() {
@Override
public List<CommitInfo> get() throws RestApiException {
try {
GetMergeList gml = getMergeList.get();
gml.setUninterestingParent(getUninterestingParent());
gml.setAddLinks(getAddLinks());
return gml.apply(revision).value();
} catch (IOException e) {
throw new RestApiException("Cannot get merge list", e);
}
}
};
}
}

View File

@@ -35,7 +35,7 @@ public class GetCommit implements RestReadView<RevisionResource> {
private final GitRepositoryManager repoManager;
private final ChangeJson.Factory json;
@Option(name = "--links", usage = "Add weblinks")
@Option(name = "--links", usage = "Include weblinks")
private boolean addLinks;
@Inject

View File

@@ -0,0 +1,105 @@
// Copyright (C) 2016 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.server.change;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class GetMergeList implements RestReadView<RevisionResource> {
private final GitRepositoryManager repoManager;
private final ChangeJson.Factory json;
@Option(name = "--parent", usage = "Uninteresting parent (1-based, default = 1)")
private int uninterestingParent = 1;
@Option(name = "--links", usage = "Include weblinks")
private boolean addLinks;
@Inject
GetMergeList(GitRepositoryManager repoManager, ChangeJson.Factory json) {
this.repoManager = repoManager;
this.json = json;
}
public void setUninterestingParent(int uninterestingParent) {
this.uninterestingParent = uninterestingParent;
}
public void setAddLinks(boolean addLinks) {
this.addLinks = addLinks;
}
@Override
public Response<List<CommitInfo>> apply(RevisionResource rsrc)
throws BadRequestException, IOException {
List<CommitInfo> result = new ArrayList<>();
Project.NameKey p = rsrc.getChange().getProject();
try (Repository repo = repoManager.openRepository(p);
RevWalk rw = new RevWalk(repo)) {
String rev = rsrc.getPatchSet().getRevision().get();
RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
rw.parseBody(commit);
if (uninterestingParent < 1
|| uninterestingParent > commit.getParentCount()) {
throw new BadRequestException("No such parent: " + uninterestingParent);
}
if (commit.getParentCount() < 2) {
return Response.<List<CommitInfo>> ok(ImmutableList.<CommitInfo> of());
}
for (int parent = 0; parent < commit.getParentCount(); parent++) {
if (parent == uninterestingParent - 1) {
rw.markUninteresting(commit.getParent(parent));
} else {
rw.markStart(commit.getParent(parent));
}
}
ChangeJson changeJson = json.create(ChangeJson.NO_OPTIONS);
RevCommit c;
while ((c = rw.next()) != null) {
CommitInfo info =
changeJson.toCommit(rsrc.getControl(), rw, c, addLinks, true);
result.add(info);
}
}
Response<List<CommitInfo>> r = Response.ok(result);
if (rsrc.isCacheable()) {
r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
}
return r;
}
}

View File

@@ -100,6 +100,7 @@ public class Module extends RestApiModule {
post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class);
post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class);
get(REVISION_KIND, "archive").to(GetArchive.class);
get(REVISION_KIND, "mergelist").to(GetMergeList.class);
child(REVISION_KIND, "drafts").to(DraftComments.class);
put(REVISION_KIND, "drafts").to(CreateDraftComment.class);