Describe core revision actions cherrypick, rebase, submit

Exporting these actions as UiCommands makes them visible in
the actions map of a RevisionInfo if CURRENT_ACTIONS option
was requested by the client.

This gives the client hints about which actions the caller
can invoke at the time the RevisionInfo was retrieved.

Change-Id: I17510aed35e000e1d9fc9eb24c3646b4a72bfac9
This commit is contained in:
Shawn Pearce 2013-07-12 12:00:51 -07:00
parent 7e90035c09
commit d80cd41489
7 changed files with 162 additions and 11 deletions

View File

@ -14,7 +14,6 @@
package com.google.gerrit.httpd.rpc.changedetail; package com.google.gerrit.httpd.rpc.changedetail;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.common.data.ChangeDetail; import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.ChangeInfo; import com.google.gerrit.common.data.ChangeInfo;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
@ -136,9 +135,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
changeId)); changeId));
detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet()); detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
detail.setCanCherryPick(control.getProjectControl().canUpload());
detail.setCanCherryPick(control.getProjectControl().canPushToAtLeastOneRef() == Capable.OK);
detail.setCanEdit(control.getRefControl().canWrite()); detail.setCanEdit(control.getRefControl().canWrite());
detail.setCanEditCommitMessage(change.getStatus().isOpen() && control.canAddPatchSet()); detail.setCanEditCommitMessage(change.getStatus().isOpen() && control.canAddPatchSet());
detail.setCanEditTopicName(control.canEditTopicName()); detail.setCanEditTopicName(control.canEditTopicName());

View File

@ -18,11 +18,11 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiCommand;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.CherryPick.Input; import com.google.gerrit.server.change.CherryPick.Input;
import com.google.gerrit.server.change.CherryPickChange;
import com.google.gerrit.server.git.MergeException; import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.InvalidChangeOperationException;
@ -30,7 +30,11 @@ import com.google.gerrit.server.project.RefControl;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
class CherryPick implements RestModifyView<RevisionResource, Input> { import java.util.EnumSet;
import java.util.Set;
class CherryPick implements RestModifyView<RevisionResource, Input>,
UiCommand<RevisionResource> {
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;
private final Provider<CherryPickChange> cherryPickChange; private final Provider<CherryPickChange> cherryPickChange;
private final ChangeJson json; private final ChangeJson json;
@ -90,4 +94,34 @@ class CherryPick implements RestModifyView<RevisionResource, Input> {
throw new ResourceConflictException(e.getMessage()); throw new ResourceConflictException(e.getMessage());
} }
} }
@Override
public Set<Place> getPlaces() {
return EnumSet.of(Place.PATCHSET_ACTION_PANEL);
}
@Override
public String getLabel(RevisionResource resource) {
return "Cherry Pick To";
}
@Override
public String getTitle(RevisionResource resource) {
return "Cherry pick change to a different branch";
}
@Override
public boolean isVisible(RevisionResource resource) {
return isEnabled(resource);
}
@Override
public boolean isEnabled(RevisionResource resource) {
return resource.getControl().getProjectControl().canUpload();
}
@Override
public String getConfirmationMessage(RevisionResource resource) {
return null;
}
} }

View File

@ -61,18 +61,17 @@ public class Module extends RestApiModule {
delete(REVIEWER_KIND).to(DeleteReviewer.class); delete(REVIEWER_KIND).to(DeleteReviewer.class);
child(CHANGE_KIND, "revisions").to(Revisions.class); child(CHANGE_KIND, "revisions").to(Revisions.class);
post(REVISION_KIND, "cherrypick").to(CherryPick.class);
get(REVISION_KIND, "commit").to(GetCommit.class); get(REVISION_KIND, "commit").to(GetCommit.class);
get(REVISION_KIND, "review").to(GetReview.class); get(REVISION_KIND, "review").to(GetReview.class);
post(REVISION_KIND, "review").to(PostReview.class); post(REVISION_KIND, "review").to(PostReview.class);
post(REVISION_KIND, "submit").to(Submit.class); post(REVISION_KIND, "submit").to(Submit.class);
post(REVISION_KIND, "rebase").to(Rebase.class); post(REVISION_KIND, "rebase").to(Rebase.class);
get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class);
get(REVISION_KIND, "patch").to(GetPatch.class); get(REVISION_KIND, "patch").to(GetPatch.class);
get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class);
post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class); post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class);
post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class); post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class);
post(REVISION_KIND, "cherrypick").to(CherryPick.class);
child(REVISION_KIND, "drafts").to(Drafts.class); child(REVISION_KIND, "drafts").to(Drafts.class);
put(REVISION_KIND, "drafts").to(CreateDraft.class); put(REVISION_KIND, "drafts").to(CreateDraft.class);
get(DRAFT_KIND).to(GetDraft.class); get(DRAFT_KIND).to(GetDraft.class);

View File

@ -20,6 +20,7 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiCommand;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
@ -34,8 +35,11 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;
public class Rebase implements RestModifyView<RevisionResource, Input> { public class Rebase implements RestModifyView<RevisionResource, Input>,
UiCommand<RevisionResource> {
public static class Input { public static class Input {
} }
@ -76,6 +80,38 @@ public class Rebase implements RestModifyView<RevisionResource, Input> {
return json.format(change.getId()); return json.format(change.getId());
} }
@Override
public Set<Place> getPlaces() {
return EnumSet.of(Place.PATCHSET_ACTION_PANEL);
}
@Override
public String getLabel(RevisionResource resource) {
return "Rebase";
}
@Override
public String getTitle(RevisionResource resource) {
return "Rebase onto tip of branch or parent change";
}
@Override
public boolean isVisible(RevisionResource resource) {
return isEnabled(resource);
}
@Override
public boolean isEnabled(RevisionResource resource) {
return resource.getChange().getStatus().isOpen()
&& resource.getControl().canRebase()
&& rebaseChange.get().canRebase(resource);
}
@Override
public String getConfirmationMessage(RevisionResource resource) {
return null;
}
public static class CurrentRevision implements public static class CurrentRevision implements
RestModifyView<ChangeResource, Input> { RestModifyView<ChangeResource, Input> {
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;

View File

@ -24,6 +24,7 @@ import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiCommand;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
@ -49,9 +50,12 @@ import java.io.IOException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set;
public class Submit implements RestModifyView<RevisionResource, Input> { public class Submit implements RestModifyView<RevisionResource, Input>,
UiCommand<RevisionResource> {
public static class Input { public static class Input {
public boolean waitForMerge; public boolean waitForMerge;
} }
@ -140,6 +144,44 @@ public class Submit implements RestModifyView<RevisionResource, Input> {
} }
} }
@Override
public Set<Place> getPlaces() {
return EnumSet.of(Place.PATCHSET_ACTION_PANEL);
}
@Override
public String getLabel(RevisionResource resource) {
return String.format(
"Submit Patch Set %d",
resource.getPatchSet().getPatchSetId());
}
@Override
public String getTitle(RevisionResource resource) {
return null;
}
@Override
public boolean isVisible(RevisionResource resource) {
PatchSet.Id current = resource.getChange().currentPatchSetId();
return resource.getChange().getStatus().isOpen()
&& resource.getPatchSet().getId().equals(current)
&& isEnabled(resource);
}
@Override
public boolean isEnabled(RevisionResource resource) {
// Enable based on approximation. If the user has permission enable,
// even if the change has not reached as submittable state according
// to the project rules.
return resource.getControl().canSubmit();
}
@Override
public String getConfirmationMessage(RevisionResource resource) {
return null;
}
/** /**
* If the merge was attempted and it failed the system usually writes a * If the merge was attempted and it failed the system usually writes a
* comment as a ChangeMessage and sets status to NEW. Find the relevant * comment as a ChangeMessage and sets status to NEW. Find the relevant

View File

@ -27,6 +27,7 @@ import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.PatchSetInserter; import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil; import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
@ -351,6 +352,34 @@ public class RebaseChange {
return objectId; return objectId;
} }
public boolean canRebase(RevisionResource r) {
Repository git;
try {
git = gitManager.openRepository(r.getChange().getProject());
} catch (RepositoryNotFoundException err) {
return false;
} catch (IOException err) {
return false;
}
try {
findBaseRevision(
r.getPatchSet().getId(),
db,
r.getChange().getDest(),
git,
null,
null,
null);
return true;
} catch (IOException e) {
return false;
} catch (OrmException e) {
return false;
} finally {
git.close();
}
}
public static boolean canDoRebase(final ReviewDb db, public static boolean canDoRebase(final ReviewDb db,
final Change change, final GitRepositoryManager gitManager, final Change change, final GitRepositoryManager gitManager,
List<PatchSetAncestor> patchSetAncestors, List<PatchSetAncestor> patchSetAncestors,

View File

@ -217,6 +217,20 @@ public class ProjectControl {
|| isOwnerAnyRef()); || isOwnerAnyRef());
} }
public boolean canUpload() {
for (SectionMatcher matcher : access()) {
AccessSection section = matcher.section;
if (section.getName().startsWith("refs/for/")) {
Permission permission = section.getPermission(Permission.PUSH);
if (permission != null
&& controlForRef(section.getName()).canPerform(Permission.PUSH)) {
return true;
}
}
}
return false;
}
/** Can this user see all the refs in this projects? */ /** Can this user see all the refs in this projects? */
public boolean allRefsAreVisible() { public boolean allRefsAreVisible() {
return allRefsAreVisibleExcept(Collections.<String> emptySet()); return allRefsAreVisibleExcept(Collections.<String> emptySet());