From 56f76b2bd01bc95fbcfa5cb9cbb3afa2c75eb9a6 Mon Sep 17 00:00:00 2001 From: Goran Lungberg Date: Tue, 25 May 2010 08:49:00 +0200 Subject: 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 --- .../gerrit/common/data/ChangeDetailService.java | 2 + .../gerrit/common/data/IncludedInDetail.java | 44 ++++++++ .../gerrit/client/changes/ChangeConstants.java | 4 + .../client/changes/ChangeConstants.properties | 4 + .../google/gerrit/client/changes/ChangeScreen.java | 16 +++ .../gerrit/client/changes/IncludedInTable.java | 85 ++++++++++++++++ .../rpc/changedetail/ChangeDetailServiceImpl.java | 9 ++ .../httpd/rpc/changedetail/ChangeModule.java | 1 + .../rpc/changedetail/IncludedInDetailFactory.java | 111 +++++++++++++++++++++ 9 files changed, 276 insertions(+) create mode 100644 gerrit-common/src/main/java/com/google/gerrit/common/data/IncludedInDetail.java create mode 100644 gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/IncludedInTable.java create mode 100644 gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java index 1f63adf7b1..4d362fe0b8 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java @@ -26,6 +26,8 @@ import com.google.gwtjsonrpc.client.RpcImpl.Version; public interface ChangeDetailService extends RemoteJsonService { void changeDetail(Change.Id id, AsyncCallback callback); + void includedInDetail(Change.Id id, AsyncCallback callback); + void patchSetDetail(PatchSet.Id key, AsyncCallback callback); @SignInRequired diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/IncludedInDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/IncludedInDetail.java new file mode 100644 index 0000000000..9365db8416 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/IncludedInDetail.java @@ -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 branches; + private List tags; + + public IncludedInDetail() { + } + + public void setBranches(final List b) { + Collections.sort(b); + branches = b; + } + + public List getBranches() { + return branches; + } + + public void setTags(final List t) { + Collections.sort(t); + tags = t; + } + + public List getTags() { + return tags; + } +} diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java index fed0927ca6..4e34d25f13 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java @@ -63,6 +63,7 @@ public interface ChangeConstants extends Constants { String prevPatchLinkIcon(); String nextPatchLinkIcon(); + String changeScreenIncludedIn(); String changeScreenDependencies(); String changeScreenDependsOn(); String changeScreenNeededBy(); @@ -79,6 +80,9 @@ public interface ChangeConstants extends Constants { String changeInfoBlockStatus(); String changePermalink(); + String includedInTableBranch(); + String includedInTableTag(); + String messageNoAuthor(); String messageExpandRecent(); String messageExpandAll(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties index 155d6575e7..7211c9498f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties @@ -40,6 +40,7 @@ patchTablePrev = Previous file patchTableNext = Next file patchTableOpen = Open file +changeScreenIncludedIn = Included in changeScreenDependencies = Dependencies changeScreenDependsOn = Depends On changeScreenNeededBy = Needed By @@ -56,6 +57,9 @@ changeInfoBlockUpdated = Updated changeInfoBlockStatus = Status changePermalink = Permalink +includedInTableBranch = Branch Name +includedInTableTag = Tag Name + messageNoAuthor = Gerrit Code Review messageExpandRecent = Expand Recent messageExpandAll = Expand All diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java index 9288788d50..a700dd8ea2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java @@ -34,6 +34,7 @@ import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.ChangeMessage; 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.ClickHandler; import com.google.gwt.event.dom.client.KeyPressEvent; @@ -64,6 +65,8 @@ public class ChangeScreen extends Screen { private ChangeDescriptionBlock descriptionBlock; private ApprovalTable approvals; + private IncludedInTable includedInTable; + private DisclosurePanel includedInPanel; private DisclosurePanel dependenciesPanel; private ChangeTable dependencies; private ChangeTable.Section dependsOn; @@ -171,6 +174,12 @@ public class ChangeScreen extends Screen { approvals = new ApprovalTable(); add(approvals); + includedInPanel = new DisclosurePanel(Util.C.changeScreenIncludedIn()); + includedInTable = new IncludedInTable(changeId); + + includedInPanel.setContent(includedInTable); + add(includedInPanel); + dependencies = new ChangeTable() { { table.setWidth("98%"); @@ -219,6 +228,13 @@ public class ChangeScreen extends Screen { setStarred(detail.isStarred()); } + if (Status.MERGED == detail.getChange().getStatus()) { + includedInPanel.setVisible(true); + includedInPanel.addOpenHandler(includedInTable); + } else { + includedInPanel.setVisible(false); + } + dependencies.setAccountInfoCache(detail.getAccounts()); approvals.setAccountInfoCache(detail.getAccounts()); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/IncludedInTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/IncludedInTable.java new file mode 100644 index 0000000000..533999d326 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/IncludedInTable.java @@ -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 { + 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 event) { + if (!loaded) { + Util.DETAIL_SVC.includedInDetail(changeId, + new GerritCallback() { + @Override + public void onSuccess(final IncludedInDetail result) { + loadTable(result); + } + }); + } + } +} diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java index 12fc41d5fc..ebb4502933 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java @@ -16,6 +16,7 @@ package com.google.gerrit.httpd.rpc.changedetail; import com.google.gerrit.common.data.ChangeDetail; 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.PatchSetPublishDetail; import com.google.gerrit.reviewdb.Change; @@ -25,14 +26,17 @@ import com.google.inject.Inject; class ChangeDetailServiceImpl implements ChangeDetailService { private final ChangeDetailFactory.Factory changeDetail; + private final IncludedInDetailFactory.Factory includedInDetail; private final PatchSetDetailFactory.Factory patchSetDetail; private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail; @Inject ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail, + final IncludedInDetailFactory.Factory includedInDetail, final PatchSetDetailFactory.Factory patchSetDetail, final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) { this.changeDetail = changeDetail; + this.includedInDetail = includedInDetail; this.patchSetDetail = patchSetDetail; this.patchSetPublishDetail = patchSetPublishDetail; } @@ -42,6 +46,11 @@ class ChangeDetailServiceImpl implements ChangeDetailService { changeDetail.create(id).to(callback); } + public void includedInDetail(final Change.Id id, + final AsyncCallback callback) { + includedInDetail.create(id).to(callback); + } + public void patchSetDetail(final PatchSet.Id id, final AsyncCallback callback) { patchSetDetail.create(id).to(callback); diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java index 211f505337..8605de398c 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java @@ -30,6 +30,7 @@ public class ChangeModule extends RpcServletModule { protected void configure() { factory(AbandonChange.Factory.class); factory(ChangeDetailFactory.Factory.class); + factory(IncludedInDetailFactory.Factory.class); factory(PatchSetDetailFactory.Factory.class); factory(PatchSetPublishDetailFactory.Factory.class); factory(SubmitAction.Factory.class); diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java new file mode 100644 index 0000000000..b49a07871f --- /dev/null +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java @@ -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 { + 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 refsHeads = + repo.getRefDatabase().getRefs(Constants.R_HEADS); + final Map 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 branches = new ArrayList(); + for (final String branch : refsHeads.keySet()) { + if (rw.isMergedInto(rev, rw.parseCommit(refsHeads.get(branch) + .getObjectId()))) { + branches.add(branch); + } + } + final List tags = new ArrayList(); + 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(); + } + } +} -- cgit v1.2.3