Visualize in which revisions a merged change is included
Introduced a new "Included in" panel on merged changes. The panel will produce sorted lists all branches and tags which contains the merged change. Provides a simple way for users to determine in which revisions of the project the change is included. Change-Id: If94b2604607f53a2e45330b56d55cc5de8288054
This commit is contained in:
parent
04132a143f
commit
56f76b2bd0
@ -26,6 +26,8 @@ import com.google.gwtjsonrpc.client.RpcImpl.Version;
|
|||||||
public interface ChangeDetailService extends RemoteJsonService {
|
public interface ChangeDetailService extends RemoteJsonService {
|
||||||
void changeDetail(Change.Id id, AsyncCallback<ChangeDetail> callback);
|
void changeDetail(Change.Id id, AsyncCallback<ChangeDetail> callback);
|
||||||
|
|
||||||
|
void includedInDetail(Change.Id id, AsyncCallback<IncludedInDetail> callback);
|
||||||
|
|
||||||
void patchSetDetail(PatchSet.Id key, AsyncCallback<PatchSetDetail> callback);
|
void patchSetDetail(PatchSet.Id key, AsyncCallback<PatchSetDetail> callback);
|
||||||
|
|
||||||
@SignInRequired
|
@SignInRequired
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (C) 2010 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.common.data;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IncludedInDetail {
|
||||||
|
private List<String> branches;
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
public IncludedInDetail() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBranches(final List<String> b) {
|
||||||
|
Collections.sort(b);
|
||||||
|
branches = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getBranches() {
|
||||||
|
return branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTags(final List<String> t) {
|
||||||
|
Collections.sort(t);
|
||||||
|
tags = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
@ -63,6 +63,7 @@ public interface ChangeConstants extends Constants {
|
|||||||
String prevPatchLinkIcon();
|
String prevPatchLinkIcon();
|
||||||
String nextPatchLinkIcon();
|
String nextPatchLinkIcon();
|
||||||
|
|
||||||
|
String changeScreenIncludedIn();
|
||||||
String changeScreenDependencies();
|
String changeScreenDependencies();
|
||||||
String changeScreenDependsOn();
|
String changeScreenDependsOn();
|
||||||
String changeScreenNeededBy();
|
String changeScreenNeededBy();
|
||||||
@ -79,6 +80,9 @@ public interface ChangeConstants extends Constants {
|
|||||||
String changeInfoBlockStatus();
|
String changeInfoBlockStatus();
|
||||||
String changePermalink();
|
String changePermalink();
|
||||||
|
|
||||||
|
String includedInTableBranch();
|
||||||
|
String includedInTableTag();
|
||||||
|
|
||||||
String messageNoAuthor();
|
String messageNoAuthor();
|
||||||
String messageExpandRecent();
|
String messageExpandRecent();
|
||||||
String messageExpandAll();
|
String messageExpandAll();
|
||||||
|
@ -40,6 +40,7 @@ patchTablePrev = Previous file
|
|||||||
patchTableNext = Next file
|
patchTableNext = Next file
|
||||||
patchTableOpen = Open file
|
patchTableOpen = Open file
|
||||||
|
|
||||||
|
changeScreenIncludedIn = Included in
|
||||||
changeScreenDependencies = Dependencies
|
changeScreenDependencies = Dependencies
|
||||||
changeScreenDependsOn = Depends On
|
changeScreenDependsOn = Depends On
|
||||||
changeScreenNeededBy = Needed By
|
changeScreenNeededBy = Needed By
|
||||||
@ -56,6 +57,9 @@ changeInfoBlockUpdated = Updated
|
|||||||
changeInfoBlockStatus = Status
|
changeInfoBlockStatus = Status
|
||||||
changePermalink = Permalink
|
changePermalink = Permalink
|
||||||
|
|
||||||
|
includedInTableBranch = Branch Name
|
||||||
|
includedInTableTag = Tag Name
|
||||||
|
|
||||||
messageNoAuthor = Gerrit Code Review
|
messageNoAuthor = Gerrit Code Review
|
||||||
messageExpandRecent = Expand Recent
|
messageExpandRecent = Expand Recent
|
||||||
messageExpandAll = Expand All
|
messageExpandAll = Expand All
|
||||||
|
@ -34,6 +34,7 @@ import com.google.gerrit.reviewdb.Account;
|
|||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
import com.google.gerrit.reviewdb.ChangeMessage;
|
import com.google.gerrit.reviewdb.ChangeMessage;
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.Change.Status;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
import com.google.gwt.event.dom.client.ClickHandler;
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
import com.google.gwt.event.dom.client.KeyPressEvent;
|
||||||
@ -64,6 +65,8 @@ public class ChangeScreen extends Screen {
|
|||||||
private ChangeDescriptionBlock descriptionBlock;
|
private ChangeDescriptionBlock descriptionBlock;
|
||||||
private ApprovalTable approvals;
|
private ApprovalTable approvals;
|
||||||
|
|
||||||
|
private IncludedInTable includedInTable;
|
||||||
|
private DisclosurePanel includedInPanel;
|
||||||
private DisclosurePanel dependenciesPanel;
|
private DisclosurePanel dependenciesPanel;
|
||||||
private ChangeTable dependencies;
|
private ChangeTable dependencies;
|
||||||
private ChangeTable.Section dependsOn;
|
private ChangeTable.Section dependsOn;
|
||||||
@ -171,6 +174,12 @@ public class ChangeScreen extends Screen {
|
|||||||
approvals = new ApprovalTable();
|
approvals = new ApprovalTable();
|
||||||
add(approvals);
|
add(approvals);
|
||||||
|
|
||||||
|
includedInPanel = new DisclosurePanel(Util.C.changeScreenIncludedIn());
|
||||||
|
includedInTable = new IncludedInTable(changeId);
|
||||||
|
|
||||||
|
includedInPanel.setContent(includedInTable);
|
||||||
|
add(includedInPanel);
|
||||||
|
|
||||||
dependencies = new ChangeTable() {
|
dependencies = new ChangeTable() {
|
||||||
{
|
{
|
||||||
table.setWidth("98%");
|
table.setWidth("98%");
|
||||||
@ -219,6 +228,13 @@ public class ChangeScreen extends Screen {
|
|||||||
setStarred(detail.isStarred());
|
setStarred(detail.isStarred());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Status.MERGED == detail.getChange().getStatus()) {
|
||||||
|
includedInPanel.setVisible(true);
|
||||||
|
includedInPanel.addOpenHandler(includedInTable);
|
||||||
|
} else {
|
||||||
|
includedInPanel.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
dependencies.setAccountInfoCache(detail.getAccounts());
|
dependencies.setAccountInfoCache(detail.getAccounts());
|
||||||
approvals.setAccountInfoCache(detail.getAccounts());
|
approvals.setAccountInfoCache(detail.getAccounts());
|
||||||
|
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (C) 2010 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.client.changes;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
|
import com.google.gerrit.common.data.IncludedInDetail;
|
||||||
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gwt.event.logical.shared.OpenEvent;
|
||||||
|
import com.google.gwt.event.logical.shared.OpenHandler;
|
||||||
|
import com.google.gwt.user.client.ui.Composite;
|
||||||
|
import com.google.gwt.user.client.ui.DisclosurePanel;
|
||||||
|
import com.google.gwt.user.client.ui.Grid;
|
||||||
|
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
|
||||||
|
|
||||||
|
|
||||||
|
/** Displays a table of Branches and Tags containing the change record. */
|
||||||
|
public class IncludedInTable extends Composite implements
|
||||||
|
OpenHandler<DisclosurePanel> {
|
||||||
|
private final Grid table;
|
||||||
|
private final Change.Id changeId;
|
||||||
|
private boolean loaded = false;
|
||||||
|
|
||||||
|
public IncludedInTable(final Change.Id chId) {
|
||||||
|
changeId = chId;
|
||||||
|
table = new Grid(1, 1);
|
||||||
|
initWidget(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadTable(final IncludedInDetail detail) {
|
||||||
|
int row = 0;
|
||||||
|
table.resizeRows(detail.getBranches().size() + 1);
|
||||||
|
table.addStyleName(Gerrit.RESOURCES.css().changeTable());
|
||||||
|
final CellFormatter fmt = table.getCellFormatter();
|
||||||
|
fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
table.setText(row, 0, Util.C.includedInTableBranch());
|
||||||
|
|
||||||
|
for (final String branch : detail.getBranches()) {
|
||||||
|
fmt.addStyleName(++row, 0, Gerrit.RESOURCES.css().dataCell());
|
||||||
|
fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().leftMostCell());
|
||||||
|
table.setText(row, 0, branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!detail.getTags().isEmpty()) {
|
||||||
|
table.resizeRows(table.getRowCount() + 2 + detail.getTags().size());
|
||||||
|
row++;
|
||||||
|
fmt.addStyleName(++row, 0, Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
table.setText(row, 0, Util.C.includedInTableTag());
|
||||||
|
|
||||||
|
for (final String tag : detail.getTags()) {
|
||||||
|
fmt.addStyleName(++row, 0, Gerrit.RESOURCES.css().dataCell());
|
||||||
|
fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().leftMostCell());
|
||||||
|
table.setText(row, 0, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.setVisible(true);
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(OpenEvent<DisclosurePanel> event) {
|
||||||
|
if (!loaded) {
|
||||||
|
Util.DETAIL_SVC.includedInDetail(changeId,
|
||||||
|
new GerritCallback<IncludedInDetail>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final IncludedInDetail result) {
|
||||||
|
loadTable(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ package com.google.gerrit.httpd.rpc.changedetail;
|
|||||||
|
|
||||||
import com.google.gerrit.common.data.ChangeDetail;
|
import com.google.gerrit.common.data.ChangeDetail;
|
||||||
import com.google.gerrit.common.data.ChangeDetailService;
|
import com.google.gerrit.common.data.ChangeDetailService;
|
||||||
|
import com.google.gerrit.common.data.IncludedInDetail;
|
||||||
import com.google.gerrit.common.data.PatchSetDetail;
|
import com.google.gerrit.common.data.PatchSetDetail;
|
||||||
import com.google.gerrit.common.data.PatchSetPublishDetail;
|
import com.google.gerrit.common.data.PatchSetPublishDetail;
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
@ -25,14 +26,17 @@ import com.google.inject.Inject;
|
|||||||
|
|
||||||
class ChangeDetailServiceImpl implements ChangeDetailService {
|
class ChangeDetailServiceImpl implements ChangeDetailService {
|
||||||
private final ChangeDetailFactory.Factory changeDetail;
|
private final ChangeDetailFactory.Factory changeDetail;
|
||||||
|
private final IncludedInDetailFactory.Factory includedInDetail;
|
||||||
private final PatchSetDetailFactory.Factory patchSetDetail;
|
private final PatchSetDetailFactory.Factory patchSetDetail;
|
||||||
private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail;
|
private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail,
|
ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail,
|
||||||
|
final IncludedInDetailFactory.Factory includedInDetail,
|
||||||
final PatchSetDetailFactory.Factory patchSetDetail,
|
final PatchSetDetailFactory.Factory patchSetDetail,
|
||||||
final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) {
|
final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) {
|
||||||
this.changeDetail = changeDetail;
|
this.changeDetail = changeDetail;
|
||||||
|
this.includedInDetail = includedInDetail;
|
||||||
this.patchSetDetail = patchSetDetail;
|
this.patchSetDetail = patchSetDetail;
|
||||||
this.patchSetPublishDetail = patchSetPublishDetail;
|
this.patchSetPublishDetail = patchSetPublishDetail;
|
||||||
}
|
}
|
||||||
@ -42,6 +46,11 @@ class ChangeDetailServiceImpl implements ChangeDetailService {
|
|||||||
changeDetail.create(id).to(callback);
|
changeDetail.create(id).to(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void includedInDetail(final Change.Id id,
|
||||||
|
final AsyncCallback<IncludedInDetail> callback) {
|
||||||
|
includedInDetail.create(id).to(callback);
|
||||||
|
}
|
||||||
|
|
||||||
public void patchSetDetail(final PatchSet.Id id,
|
public void patchSetDetail(final PatchSet.Id id,
|
||||||
final AsyncCallback<PatchSetDetail> callback) {
|
final AsyncCallback<PatchSetDetail> callback) {
|
||||||
patchSetDetail.create(id).to(callback);
|
patchSetDetail.create(id).to(callback);
|
||||||
|
@ -30,6 +30,7 @@ public class ChangeModule extends RpcServletModule {
|
|||||||
protected void configure() {
|
protected void configure() {
|
||||||
factory(AbandonChange.Factory.class);
|
factory(AbandonChange.Factory.class);
|
||||||
factory(ChangeDetailFactory.Factory.class);
|
factory(ChangeDetailFactory.Factory.class);
|
||||||
|
factory(IncludedInDetailFactory.Factory.class);
|
||||||
factory(PatchSetDetailFactory.Factory.class);
|
factory(PatchSetDetailFactory.Factory.class);
|
||||||
factory(PatchSetPublishDetailFactory.Factory.class);
|
factory(PatchSetPublishDetailFactory.Factory.class);
|
||||||
factory(SubmitAction.Factory.class);
|
factory(SubmitAction.Factory.class);
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright (C) 2010 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.httpd.rpc.changedetail;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.data.IncludedInDetail;
|
||||||
|
import com.google.gerrit.common.errors.InvalidRevisionException;
|
||||||
|
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||||
|
import com.google.gerrit.httpd.rpc.Handler;
|
||||||
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.ReviewDb;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Creates a {@link IncludedInDetail} of a {@link Change}. */
|
||||||
|
class IncludedInDetailFactory extends Handler<IncludedInDetail> {
|
||||||
|
interface Factory {
|
||||||
|
IncludedInDetailFactory create(Change.Id id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ReviewDb db;
|
||||||
|
private final ChangeControl.Factory changeControlFactory;
|
||||||
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final Change.Id changeId;
|
||||||
|
|
||||||
|
private IncludedInDetail detail;
|
||||||
|
private ChangeControl control;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
IncludedInDetailFactory(final ReviewDb db,
|
||||||
|
final ChangeControl.Factory changeControlFactory,
|
||||||
|
final GitRepositoryManager repoManager, @Assisted final Change.Id changeId) {
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
|
this.repoManager = repoManager;
|
||||||
|
this.changeId = changeId;
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IncludedInDetail call() throws OrmException, NoSuchChangeException,
|
||||||
|
NoSuchEntityException, IOException, InvalidRevisionException {
|
||||||
|
control = changeControlFactory.validateFor(changeId);
|
||||||
|
final PatchSet patch =
|
||||||
|
db.patchSets().get(control.getChange().currentPatchSetId());
|
||||||
|
final Repository repo =
|
||||||
|
repoManager.openRepository(control.getProject().getName());
|
||||||
|
final Map<String, Ref> refsHeads =
|
||||||
|
repo.getRefDatabase().getRefs(Constants.R_HEADS);
|
||||||
|
final Map<String, Ref> refsTags =
|
||||||
|
repo.getRefDatabase().getRefs(Constants.R_TAGS);
|
||||||
|
RevWalk rw = new RevWalk(repo);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final RevCommit rev =
|
||||||
|
rw.parseCommit(ObjectId.fromString(patch.getRevision().get()));
|
||||||
|
final List<String> branches = new ArrayList<String>();
|
||||||
|
for (final String branch : refsHeads.keySet()) {
|
||||||
|
if (rw.isMergedInto(rev, rw.parseCommit(refsHeads.get(branch)
|
||||||
|
.getObjectId()))) {
|
||||||
|
branches.add(branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final List<String> tags = new ArrayList<String>();
|
||||||
|
for (final String tag : refsTags.keySet()) {
|
||||||
|
if (rw.isMergedInto(rev, rw
|
||||||
|
.parseCommit(refsTags.get(tag).getObjectId()))) {
|
||||||
|
tags.add(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
detail = new IncludedInDetail();
|
||||||
|
detail.setBranches(branches);
|
||||||
|
detail.setTags(tags);
|
||||||
|
|
||||||
|
return detail;
|
||||||
|
} catch (IncorrectObjectTypeException err) {
|
||||||
|
throw new InvalidRevisionException();
|
||||||
|
} catch (MissingObjectException err) {
|
||||||
|
throw new InvalidRevisionException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user