Support rebase via REST
POST on '/changes/<change-id>/rebase' rebases a change via REST. POST on '/changes/<change-id>/revisions/<revision-id>/rebase' rebases a revision via REST. Change-Id: I573f914e005ea75125530bee6f8a9f37036e3563 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
@@ -710,6 +710,95 @@ the error message is contained in the response body.
|
||||
change is new
|
||||
----
|
||||
|
||||
[[rebase-change]]
|
||||
Rebase Change
|
||||
~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'POST /changes/link:#change-id[\{change-id\}]/rebase'
|
||||
|
||||
Rebases a change.
|
||||
|
||||
.Request
|
||||
----
|
||||
POST /changes/myProject~master~I3ea943139cb62e86071996f2480e58bf3eeb9dd2/rebase HTTP/1.0
|
||||
----
|
||||
|
||||
As response a link:#change-info[ChangeInfo] entity is returned that
|
||||
describes the rebased change. Information about the current patch set
|
||||
is included.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"kind": "gerritcodereview#change",
|
||||
"id": "myProject~master~I3ea943139cb62e86071996f2480e58bf3eeb9dd2",
|
||||
"project": "myProject",
|
||||
"branch": "master",
|
||||
"change_id": "I3ea943139cb62e86071996f2480e58bf3eeb9dd2",
|
||||
"subject": "Implement Feature X",
|
||||
"status": "NEW",
|
||||
"created": "2013-02-01 09:59:32.126000000",
|
||||
"updated": "2013-02-21 11:16:36.775000000",
|
||||
"mergeable": false,
|
||||
"_sortkey": "0024cf9a000012bf",
|
||||
"_number": 4799,
|
||||
"owner": {
|
||||
"name": "John Doe"
|
||||
},
|
||||
"current_revision": "27cc4558b5a3d3387dd11ee2df7a117e7e581822",
|
||||
"revisions": {
|
||||
"27cc4558b5a3d3387dd11ee2df7a117e7e581822": {
|
||||
"_number": 2,
|
||||
"fetch": {
|
||||
"http": {
|
||||
"url": "http://gerrit:8080/myProject",
|
||||
"ref": "refs/changes/99/4799/2"
|
||||
}
|
||||
},
|
||||
"commit": {
|
||||
"parents": [
|
||||
{
|
||||
"commit": "b4003890dadd406d80222bf1ad8aca09a4876b70",
|
||||
"subject": "Implement Feature A"
|
||||
}
|
||||
],
|
||||
"author": {
|
||||
"name": "John Doe",
|
||||
"email": "john.doe@example.com",
|
||||
"date": "2013-05-07 15:21:27.000000000",
|
||||
"tz": 120
|
||||
},
|
||||
"committer": {
|
||||
"name": "Gerrit Code Review",
|
||||
"email": "gerrit-server@example.com",
|
||||
"date": "2013-05-07 15:35:43.000000000",
|
||||
"tz": 120
|
||||
},
|
||||
"subject": "Implement Feature X",
|
||||
"message": "Implement Feature X\n\nAdded feature X."
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
If the change cannot be rebased, e.g. due to conflicts, the response is
|
||||
"`409 Conflict`" and the error message is contained in the response
|
||||
body.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 409 Conflict
|
||||
Content-Disposition: attachment
|
||||
Content-Type: text/plain;charset=UTF-8
|
||||
|
||||
The change could not be rebased due to a path conflict during merge.
|
||||
----
|
||||
|
||||
[[revert-change]]
|
||||
Revert Change
|
||||
~~~~~~~~~~~~~
|
||||
@@ -1218,6 +1307,95 @@ describes the applied labels.
|
||||
}
|
||||
----
|
||||
|
||||
[[rebase-revision]]
|
||||
Rebase Revision
|
||||
~~~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'POST /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/rebase'
|
||||
|
||||
Rebases a revision.
|
||||
|
||||
.Request
|
||||
----
|
||||
POST /changes/myProject~master~I3ea943139cb62e86071996f2480e58bf3eeb9dd2/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/rebase HTTP/1.0
|
||||
----
|
||||
|
||||
As response a link:#change-info[ChangeInfo] entity is returned that
|
||||
describes the rebased change. Information about the current patch set
|
||||
is included.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"kind": "gerritcodereview#change",
|
||||
"id": "myProject~master~I3ea943139cb62e86071996f2480e58bf3eeb9dd2",
|
||||
"project": "myProject",
|
||||
"branch": "master",
|
||||
"change_id": "I3ea943139cb62e86071996f2480e58bf3eeb9dd2",
|
||||
"subject": "Implement Feature X",
|
||||
"status": "NEW",
|
||||
"created": "2013-02-01 09:59:32.126000000",
|
||||
"updated": "2013-02-21 11:16:36.775000000",
|
||||
"mergeable": false,
|
||||
"_sortkey": "0024cf9a000012bf",
|
||||
"_number": 4799,
|
||||
"owner": {
|
||||
"name": "John Doe"
|
||||
},
|
||||
"current_revision": "27cc4558b5a3d3387dd11ee2df7a117e7e581822",
|
||||
"revisions": {
|
||||
"27cc4558b5a3d3387dd11ee2df7a117e7e581822": {
|
||||
"_number": 2,
|
||||
"fetch": {
|
||||
"http": {
|
||||
"url": "http://gerrit:8080/myProject",
|
||||
"ref": "refs/changes/99/4799/2"
|
||||
}
|
||||
},
|
||||
"commit": {
|
||||
"parents": [
|
||||
{
|
||||
"commit": "b4003890dadd406d80222bf1ad8aca09a4876b70",
|
||||
"subject": "Implement Feature A"
|
||||
}
|
||||
],
|
||||
"author": {
|
||||
"name": "John Doe",
|
||||
"email": "john.doe@example.com",
|
||||
"date": "2013-05-07 15:21:27.000000000",
|
||||
"tz": 120
|
||||
},
|
||||
"committer": {
|
||||
"name": "Gerrit Code Review",
|
||||
"email": "gerrit-server@example.com",
|
||||
"date": "2013-05-07 15:35:43.000000000",
|
||||
"tz": 120
|
||||
},
|
||||
"subject": "Implement Feature X",
|
||||
"message": "Implement Feature X\n\nAdded feature X."
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
If the revision cannot be rebased, e.g. due to conflicts, the response is
|
||||
"`409 Conflict`" and the error message is contained in the response
|
||||
body.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 409 Conflict
|
||||
Content-Disposition: attachment
|
||||
Content-Type: text/plain;charset=UTF-8
|
||||
|
||||
The change could not be rebased due to a path conflict during merge.
|
||||
----
|
||||
|
||||
[[submit-revision]]
|
||||
Submit Revision
|
||||
~~~~~~~~~~~~~~~
|
||||
|
@@ -62,7 +62,7 @@ class RebaseChangeHandler extends Handler<ChangeDetail> {
|
||||
EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException,
|
||||
MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||
InvalidChangeOperationException, NoSuchProjectException {
|
||||
rebaseChange.rebase(patchSetId, currentUser.getAccountId());
|
||||
rebaseChange.rebase(patchSetId, currentUser);
|
||||
return changeDetailFactory.create(patchSetId.getParentKey()).call();
|
||||
}
|
||||
}
|
||||
|
@@ -53,6 +53,7 @@ public class Module extends RestApiModule {
|
||||
post(CHANGE_KIND, "restore").to(Restore.class);
|
||||
post(CHANGE_KIND, "revert").to(Revert.class);
|
||||
post(CHANGE_KIND, "submit").to(Submit.CurrentRevision.class);
|
||||
post(CHANGE_KIND, "rebase").to(Rebase.CurrentRevision.class);
|
||||
|
||||
post(CHANGE_KIND, "reviewers").to(PostReviewers.class);
|
||||
child(CHANGE_KIND, "reviewers").to(Reviewers.class);
|
||||
@@ -63,6 +64,7 @@ public class Module extends RestApiModule {
|
||||
get(REVISION_KIND, "review").to(GetReview.class);
|
||||
post(REVISION_KIND, "review").to(PostReview.class);
|
||||
post(REVISION_KIND, "submit").to(Submit.class);
|
||||
post(REVISION_KIND, "rebase").to(Rebase.class);
|
||||
get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class);
|
||||
post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class);
|
||||
post(REVISION_KIND, "test.submit_type").to(TestSubmitType.class);
|
||||
|
@@ -0,0 +1,105 @@
|
||||
// Copyright (C) 2013 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.gerrit.common.changes.ListChangesOption;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
|
||||
import com.google.gerrit.server.change.Rebase.Input;
|
||||
import com.google.gerrit.server.changedetail.RebaseChange;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Rebase implements RestModifyView<RevisionResource, Input> {
|
||||
public static class Input {
|
||||
}
|
||||
|
||||
private final RebaseChange rebaseChange;
|
||||
private final ChangeJson json;
|
||||
|
||||
@Inject
|
||||
public Rebase(RebaseChange rebaseChange, ChangeJson json) {
|
||||
this.rebaseChange = rebaseChange;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangeInfo apply(RevisionResource rsrc, Input input)
|
||||
throws AuthException, ResourceNotFoundException,
|
||||
ResourceConflictException, EmailException, OrmException {
|
||||
ChangeControl control = rsrc.getControl();
|
||||
Change change = rsrc.getChange();
|
||||
if (!control.canRebase()) {
|
||||
throw new AuthException("rebase not permitted");
|
||||
} else if (!change.getStatus().isOpen()) {
|
||||
throw new ResourceConflictException("change is "
|
||||
+ change.getStatus().name().toLowerCase());
|
||||
}
|
||||
|
||||
try {
|
||||
rebaseChange.rebase(rsrc.getPatchSet().getId(), rsrc.getUser());
|
||||
} catch (InvalidChangeOperationException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
} catch (NoSuchChangeException e) {
|
||||
throw new ResourceNotFoundException(change.getId().toString());
|
||||
}
|
||||
|
||||
json.addOption(ListChangesOption.CURRENT_REVISION)
|
||||
.addOption(ListChangesOption.CURRENT_COMMIT);
|
||||
return json.format(change.getId());
|
||||
}
|
||||
|
||||
public static class CurrentRevision implements
|
||||
RestModifyView<ChangeResource, Input> {
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final Rebase rebase;
|
||||
|
||||
@Inject
|
||||
CurrentRevision(Provider<ReviewDb> dbProvider, Rebase rebase) {
|
||||
this.dbProvider = dbProvider;
|
||||
this.rebase = rebase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangeInfo apply(ChangeResource rsrc, Input input)
|
||||
throws AuthException, ResourceNotFoundException,
|
||||
ResourceConflictException, EmailException, OrmException {
|
||||
PatchSet ps =
|
||||
dbProvider.get().patchSets()
|
||||
.get(rsrc.getChange().currentPatchSetId());
|
||||
if (ps == null) {
|
||||
throw new ResourceConflictException("current revision is missing");
|
||||
} else if (!rsrc.getControl().isPatchVisible(ps, dbProvider.get())) {
|
||||
throw new AuthException("current revision not accessible");
|
||||
}
|
||||
return rebase.apply(new RevisionResource(rsrc, ps), input);
|
||||
}
|
||||
}
|
||||
}
|
@@ -48,6 +48,10 @@ public class RevisionResource implements RestResource {
|
||||
}
|
||||
|
||||
Account.Id getAccountId() {
|
||||
return ((IdentifiedUser) getControl().getCurrentUser()).getAccountId();
|
||||
return getUser().getAccountId();
|
||||
}
|
||||
|
||||
IdentifiedUser getUser() {
|
||||
return (IdentifiedUser) getControl().getCurrentUser();
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ApprovalsUtil;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeUtil;
|
||||
@@ -64,7 +65,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RebaseChange {
|
||||
private final ChangeControl.Factory changeControlFactory;
|
||||
private final ChangeControl.GenericFactory changeControlFactory;
|
||||
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||
private final ReviewDb db;
|
||||
private final GitRepositoryManager gitManager;
|
||||
@@ -76,7 +77,7 @@ public class RebaseChange {
|
||||
private final ProjectCache projectCache;
|
||||
|
||||
@Inject
|
||||
RebaseChange(final ChangeControl.Factory changeControlFactory,
|
||||
RebaseChange(final ChangeControl.GenericFactory changeControlFactory,
|
||||
final PatchSetInfoFactory patchSetInfoFactory, final ReviewDb db,
|
||||
@GerritPersonIdent final PersonIdent myIdent,
|
||||
final GitRepositoryManager gitManager,
|
||||
@@ -123,12 +124,12 @@ public class RebaseChange {
|
||||
* @throws IOException thrown if rebase is not possible or not needed
|
||||
* @throws InvalidChangeOperationException thrown if rebase is not allowed
|
||||
*/
|
||||
public void rebase(final PatchSet.Id patchSetId, final Account.Id uploader)
|
||||
public void rebase(final PatchSet.Id patchSetId, final IdentifiedUser uploader)
|
||||
throws NoSuchChangeException, EmailException, OrmException, IOException,
|
||||
InvalidChangeOperationException {
|
||||
final Change.Id changeId = patchSetId.getParentKey();
|
||||
final ChangeControl changeControl =
|
||||
changeControlFactory.validateFor(changeId);
|
||||
changeControlFactory.validateFor(changeId, uploader);
|
||||
if (!changeControl.canRebase()) {
|
||||
throw new InvalidChangeOperationException(
|
||||
"Cannot rebase: New patch sets are not allowed to be added to change: "
|
||||
@@ -152,7 +153,7 @@ public class RebaseChange {
|
||||
rw.parseCommit(ObjectId.fromString(baseRev));
|
||||
|
||||
final PatchSet newPatchSet =
|
||||
rebase(git, rw, inserter, patchSetId, change, uploader, baseCommit,
|
||||
rebase(git, rw, inserter, patchSetId, change, uploader.getAccountId(), baseCommit,
|
||||
mergeUtilFactory.create(
|
||||
changeControl.getProjectControl().getProjectState(), true));
|
||||
|
||||
@@ -167,7 +168,7 @@ public class RebaseChange {
|
||||
}
|
||||
final ReplacePatchSetSender cm =
|
||||
rebasedPatchSetSenderFactory.create(change);
|
||||
cm.setFrom(uploader);
|
||||
cm.setFrom(uploader.getAccountId());
|
||||
cm.setPatchSet(newPatchSet);
|
||||
cm.addReviewers(oldReviewers);
|
||||
cm.addExtraCC(oldCC);
|
||||
|
@@ -56,10 +56,12 @@ public class ChangeControl {
|
||||
|
||||
public static class GenericFactory {
|
||||
private final ProjectControl.GenericFactory projectControl;
|
||||
private final Provider<ReviewDb> db;
|
||||
|
||||
@Inject
|
||||
GenericFactory(ProjectControl.GenericFactory p) {
|
||||
GenericFactory(ProjectControl.GenericFactory p, Provider<ReviewDb> d) {
|
||||
projectControl = p;
|
||||
db = d;
|
||||
}
|
||||
|
||||
public ChangeControl controlFor(Change change, CurrentUser user)
|
||||
@@ -71,6 +73,29 @@ public class ChangeControl {
|
||||
throw new NoSuchChangeException(change.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public ChangeControl controlFor(Change.Id id, CurrentUser user)
|
||||
throws NoSuchChangeException {
|
||||
final Change change;
|
||||
try {
|
||||
change = db.get().changes().get(id);
|
||||
if (change == null) {
|
||||
throw new NoSuchChangeException(id);
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
throw new NoSuchChangeException(id, e);
|
||||
}
|
||||
return controlFor(change, user);
|
||||
}
|
||||
|
||||
public ChangeControl validateFor(Change.Id id, CurrentUser user)
|
||||
throws NoSuchChangeException, OrmException {
|
||||
ChangeControl c = controlFor(id, user);
|
||||
if (!c.isVisible(db.get())) {
|
||||
throw new NoSuchChangeException(c.getChange().getId());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory {
|
||||
|
Reference in New Issue
Block a user