GWT UI: Support moving changes to another branch

Bug: Issue 117
Bug: Issue 6844
Change-Id: I9ad38b5be0a8be4ad31a051ef17a2de46b15b7eb
This commit is contained in:
Paladox none
2017-07-26 12:39:21 +00:00
parent 0c25f8c4f6
commit c2eb913996
8 changed files with 235 additions and 1 deletions

View File

@@ -43,6 +43,7 @@ class Actions extends Composite {
"description",
"followup",
"hashtags",
"move",
"publish",
"rebase",
"restore",
@@ -58,6 +59,7 @@ class Actions extends Composite {
private static final Binder uiBinder = GWT.create(Binder.class);
@UiField Button cherrypick;
@UiField Button move;
@UiField Button rebase;
@UiField Button revert;
@UiField Button submit;
@@ -124,6 +126,7 @@ class Actions extends Composite {
if (hasUser) {
a2b(actions, "abandon", abandon);
a2b(actions, "/", deleteChange);
a2b(actions, "move", move);
a2b(actions, "restore", restore);
a2b(actions, "revert", revert);
a2b(actions, "followup", followUp);
@@ -236,6 +239,11 @@ class Actions extends Composite {
CherryPickAction.call(cherrypick, changeInfo, revision, project, message);
}
@UiHandler("move")
void onMove(@SuppressWarnings("unused") ClickEvent e) {
MoveAction.call(move, changeInfo, project);
}
@UiHandler("revert")
void onRevert(@SuppressWarnings("unused") ClickEvent e) {
RevertAction.call(revert, changeId, project, revision, subject);

View File

@@ -63,6 +63,9 @@ limitations under the License.
<g:Button ui:field='cherrypick' styleName='' visible='false'>
<div><ui:msg>Cherry Pick</ui:msg></div>
</g:Button>
<g:Button ui:field='move' styleName='' visible='false'>
<div><ui:msg>Move Change</ui:msg></div>
</g:Button>
<g:Button ui:field='rebase' styleName='' visible='false'>
<div><ui:msg>Rebase</ui:msg></div>
</g:Button>

View File

@@ -0,0 +1,67 @@
// Copyright (C) 2017 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.change;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.Util;
import com.google.gerrit.client.info.ChangeInfo;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.MoveDialog;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.PopupPanel;
class MoveAction {
static void call(Button b, ChangeInfo info, Project.NameKey project) {
b.setEnabled(false);
new MoveDialog(project) {
{
sendButton.setText(Util.C.moveChangeSend());
}
@Override
public void onSend() {
ChangeApi.move(
info.project(),
info.legacyId().get(),
getDestinationBranch(),
getMessageText(),
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
sent = true;
hide();
Gerrit.display(PageLinks.toChange(project, result.legacyId()));
}
@Override
public void onFailure(Throwable caught) {
enableButtons(true);
super.onFailure(caught);
}
});
}
@Override
public void onClose(CloseEvent<PopupPanel> event) {
super.onClose(event);
b.setEnabled(true);
}
}.center();
}
}

View File

@@ -228,6 +228,15 @@ public class ChangeApi {
call(project, id, commit, "cherrypick").post(cherryPickInput, cb);
}
/** Move change to another branch. */
public static void move(
String project, int id, String destination, String message, AsyncCallback<ChangeInfo> cb) {
MoveInput moveInput = MoveInput.create();
moveInput.setMessage(message);
moveInput.setDestinationBranch(destination);
change(project, id).view("move").post(moveInput, cb);
}
/** Edit commit message for specific revision of a change. */
public static void message(
@Nullable String project,
@@ -356,6 +365,18 @@ public class ChangeApi {
protected CherryPickInput() {}
}
private static class MoveInput extends JavaScriptObject {
static MoveInput create() {
return (MoveInput) createObject();
}
final native void setDestinationBranch(String d) /*-{ this.destination_branch = d; }-*/;
final native void setMessage(String m) /*-{ this.message = m; }-*/;
protected MoveInput() {}
}
private static class PrivateInput extends JavaScriptObject {
static PrivateInput create() {
return (PrivateInput) createObject();

View File

@@ -145,6 +145,14 @@ public interface ChangeConstants extends Constants {
String cherryPickTitle();
String moveChangeSend();
String headingMoveBranch();
String moveChangeMessage();
String moveTitle();
String buttonRebaseChangeSend();
String rebaseConfirmMessage();

View File

@@ -78,6 +78,11 @@ headingCherryPickBranch = Cherry Pick to Branch:
cherryPickCommitMessage = Cherry Pick Commit Message:
cherryPickTitle = Code Review - Cherry Pick Change to Another Branch
headingMoveBranch = Move Change to Branch:
moveChangeSend = Move Change
moveChangeMessage = Move Change Message:
moveTitle = Code Review - Move Change to Another Branch
buttonRebaseChangeSend = Rebase
rebaseConfirmMessage = Change parent revision
rebaseNotPossibleMessage = Change is already up to date

View File

@@ -0,0 +1,107 @@
// Copyright (C) 2017 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.ui;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.Util;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
import java.util.ArrayList;
import java.util.List;
public abstract class MoveDialog extends TextAreaActionDialog {
private SuggestBox newBranch;
private List<BranchInfo> branches;
public MoveDialog(Project.NameKey project) {
super(Util.C.moveTitle(), Util.C.moveChangeMessage());
ProjectApi.getBranches(
project,
new GerritCallback<JsArray<BranchInfo>>() {
@Override
public void onSuccess(JsArray<BranchInfo> result) {
branches = Natives.asList(result);
}
});
newBranch =
new SuggestBox(
new HighlightSuggestOracle() {
@Override
protected void onRequestSuggestions(Request request, Callback done) {
List<BranchSuggestion> suggestions = new ArrayList<>();
for (BranchInfo b : branches) {
if (b.ref().contains(request.getQuery())) {
suggestions.add(new BranchSuggestion(b));
}
}
done.onSuggestionsReady(request, new Response(suggestions));
}
});
newBranch.setWidth("100%");
newBranch.getElement().getStyle().setProperty("boxSizing", "border-box");
message.setCharacterWidth(70);
FlowPanel mwrap = new FlowPanel();
mwrap.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
mwrap.add(newBranch);
panel.insert(mwrap, 0);
panel.insert(new SmallHeading(Util.C.headingMoveBranch()), 0);
}
@Override
public void center() {
super.center();
GlobalKey.dialog(this);
newBranch.setFocus(true);
}
public String getDestinationBranch() {
return newBranch.getText();
}
static class BranchSuggestion implements Suggestion {
private BranchInfo branch;
BranchSuggestion(BranchInfo branch) {
this.branch = branch;
}
@Override
public String getDisplayString() {
String refsHeads = "refs/heads/";
if (branch.ref().startsWith(refsHeads)) {
return branch.ref().substring(refsHeads.length());
}
return branch.ref();
}
@Override
public String getReplacementString() {
return branch.getShortName();
}
}
}

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
@@ -40,6 +41,7 @@ import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.update.BatchUpdate;
@@ -60,7 +62,8 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@Singleton
public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, ChangeInfo> {
public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, ChangeInfo>
implements UiAction<ChangeResource> {
private final PermissionBackend permissionBackend;
private final Provider<ReviewDb> dbProvider;
private final ChangeJson.Factory json;
@@ -209,4 +212,16 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
return true;
}
}
@Override
public UiAction.Description getDescription(ChangeResource rsrc) {
return new UiAction.Description()
.setLabel("Move Change")
.setTitle("Move change to a different branch")
.setVisible(
permissionBackend
.user(rsrc.getUser())
.project(rsrc.getProject())
.testOrFalse(ProjectPermission.CREATE_CHANGE));
}
}