Support branch creation via REST
A new branch can be created by PUT on '/projects/<project-name>/branches/<ref>. The WebUI was adapted to use this new REST endpoint to create branches. The old RPC for creating a branch in ProjectAdminService was deleted. Change-Id: Id94bc4737eedde383507c7c567b3b6b102b105c5
This commit is contained in:
parent
94a928b9a9
commit
5c0d6b33ff
@ -560,6 +560,44 @@ describes the branch.
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[create-branch]]
|
||||||
|
Create Branch
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
[verse]
|
||||||
|
'PUT /projects/link:#project-name[\{project-name\}]/branches/link:#branch-id[\{branch-id\}]'
|
||||||
|
|
||||||
|
Creates a new branch.
|
||||||
|
|
||||||
|
In the request body additional data for the branch can be provided as
|
||||||
|
link:#branch-input[BranchInput].
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
PUT /projects/MyProject/branches/stable HTTP/1.0
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
{
|
||||||
|
"revision": "76016386a0d8ecc7b6be212424978bb45959d668"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
As response a link:#branch-info[BranchInfo] entity is returned that
|
||||||
|
describes the created branch.
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"ref": "refs/heads/stable",
|
||||||
|
"revision": "76016386a0d8ecc7b6be212424978bb45959d668",
|
||||||
|
"can_delete": true
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
[[child-project-endpoints]]
|
[[child-project-endpoints]]
|
||||||
Child Project Endpoints
|
Child Project Endpoints
|
||||||
-----------------------
|
-----------------------
|
||||||
@ -968,6 +1006,24 @@ The `BranchInfo` entity contains information about a branch.
|
|||||||
Whether the calling user can delete this branch.
|
Whether the calling user can delete this branch.
|
||||||
|=========================
|
|=========================
|
||||||
|
|
||||||
|
[[branch-input]]
|
||||||
|
BranchInput
|
||||||
|
~~~~~~~~~~~
|
||||||
|
The `BranchInput` entity contains information for the creation of
|
||||||
|
a new branch.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^2,4"]
|
||||||
|
|=======================
|
||||||
|
|Field Name||Description
|
||||||
|
|`ref` |optional|
|
||||||
|
The name of the branch. The prefix `refs/heads/` can be
|
||||||
|
omitted. +
|
||||||
|
If set, must match the branch ID in the URL.
|
||||||
|
|`revision`|optional|
|
||||||
|
The base revision of the new branch. +
|
||||||
|
If not set, `HEAD` will be used as base revision.
|
||||||
|
|=======================
|
||||||
|
|
||||||
[[dashboard-info]]
|
[[dashboard-info]]
|
||||||
DashboardInfo
|
DashboardInfo
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -56,11 +56,6 @@ public interface ProjectAdminService extends RemoteJsonService {
|
|||||||
void listBranches(Project.NameKey projectName,
|
void listBranches(Project.NameKey projectName,
|
||||||
AsyncCallback<ListBranchesResult> callback);
|
AsyncCallback<ListBranchesResult> callback);
|
||||||
|
|
||||||
@Audit
|
|
||||||
@SignInRequired
|
|
||||||
void addBranch(Project.NameKey projectName, String branchName,
|
|
||||||
String startingRevision, AsyncCallback<AddBranchResult> callback);
|
|
||||||
|
|
||||||
@Audit
|
@Audit
|
||||||
@SignInRequired
|
@SignInRequired
|
||||||
void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
|
void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
|
||||||
|
@ -19,12 +19,13 @@ import com.google.gerrit.client.ConfirmationDialog;
|
|||||||
import com.google.gerrit.client.ErrorDialog;
|
import com.google.gerrit.client.ErrorDialog;
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
import com.google.gerrit.client.GitwebLink;
|
import com.google.gerrit.client.GitwebLink;
|
||||||
|
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.GerritCallback;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.client.ui.BranchLink;
|
import com.google.gerrit.client.ui.BranchLink;
|
||||||
import com.google.gerrit.client.ui.FancyFlexTable;
|
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||||
import com.google.gerrit.client.ui.HintTextBox;
|
import com.google.gerrit.client.ui.HintTextBox;
|
||||||
import com.google.gerrit.common.data.AddBranchResult;
|
|
||||||
import com.google.gerrit.common.data.ListBranchesResult;
|
import com.google.gerrit.common.data.ListBranchesResult;
|
||||||
import com.google.gerrit.reviewdb.client.Branch;
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
@ -185,62 +186,29 @@ public class ProjectBranchesScreen extends ProjectScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addBranch.setEnabled(false);
|
addBranch.setEnabled(false);
|
||||||
Util.PROJECT_SVC.addBranch(getProjectKey(), branchName, rev,
|
ProjectApi.createBranch(getProjectKey(), branchName, rev,
|
||||||
new GerritCallback<AddBranchResult>() {
|
new GerritCallback<BranchInfo>() {
|
||||||
public void onSuccess(final AddBranchResult result) {
|
|
||||||
addBranch.setEnabled(true);
|
|
||||||
if (!result.hasError()) {
|
|
||||||
nameTxtBox.setText("");
|
|
||||||
irevTxtBox.setText("");
|
|
||||||
display(result.getListBranchesResult().getBranches());
|
|
||||||
} else {
|
|
||||||
final AddBranchResult.Error error = result.getError();
|
|
||||||
final String msg;
|
|
||||||
switch (error.getType()) {
|
|
||||||
case INVALID_NAME:
|
|
||||||
selectAllAndFocus(nameTxtBox);
|
|
||||||
msg = Gerrit.M.invalidBranchName(branchName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case INVALID_REVISION:
|
|
||||||
selectAllAndFocus(irevTxtBox);
|
|
||||||
msg = Gerrit.M.invalidRevision(rev);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BRANCH_CREATION_NOT_ALLOWED_UNDER_REFNAME_PREFIX:
|
|
||||||
selectAllAndFocus(nameTxtBox);
|
|
||||||
msg =
|
|
||||||
Gerrit.M.branchCreationNotAllowedUnderRefnamePrefix(error
|
|
||||||
.getRefname());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BRANCH_ALREADY_EXISTS:
|
|
||||||
selectAllAndFocus(nameTxtBox);
|
|
||||||
msg = Gerrit.M.branchAlreadyExists(error.getRefname());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BRANCH_CREATION_CONFLICT:
|
|
||||||
selectAllAndFocus(nameTxtBox);
|
|
||||||
msg =
|
|
||||||
Gerrit.M.branchCreationConflict(branchName,
|
|
||||||
error.getRefname());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
msg =
|
|
||||||
Gerrit.M.branchCreationFailed(branchName,
|
|
||||||
error.toString());
|
|
||||||
}
|
|
||||||
new ErrorDialog(msg).center();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable caught) {
|
public void onSuccess(BranchInfo result) {
|
||||||
addBranch.setEnabled(true);
|
addBranch.setEnabled(true);
|
||||||
super.onFailure(caught);
|
nameTxtBox.setText("");
|
||||||
|
irevTxtBox.setText("");
|
||||||
|
Util.PROJECT_SVC.listBranches(getProjectKey(),
|
||||||
|
new GerritCallback<ListBranchesResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ListBranchesResult result) {
|
||||||
|
display(result.getBranches());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
addBranch.setEnabled(true);
|
||||||
|
selectAllAndFocus(nameTxtBox);
|
||||||
|
new ErrorDialog(caught.getMessage()).center();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void selectAllAndFocus(final TextBox textBox) {
|
private static void selectAllAndFocus(final TextBox textBox) {
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
// 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.client.projects;
|
||||||
|
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
|
||||||
|
public class BranchInfo extends JavaScriptObject {
|
||||||
|
public final native String ref() /*-{ return this.ref; }-*/;
|
||||||
|
public final native String revision() /*-{ return this.revision; }-*/;
|
||||||
|
public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
|
||||||
|
|
||||||
|
protected BranchInfo() {
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,15 @@ public class ProjectApi {
|
|||||||
.put(input, asyncCallback);
|
.put(input, asyncCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Create a new branch */
|
||||||
|
public static void createBranch(Project.NameKey projectName, String ref,
|
||||||
|
String revision, AsyncCallback<BranchInfo> asyncCallback) {
|
||||||
|
BranchInput input = BranchInput.create();
|
||||||
|
input.setRevision(revision);
|
||||||
|
new RestApi("/projects/").id(projectName.get()).view("branches").id(ref)
|
||||||
|
.ifNoneMatch().put(input, asyncCallback);
|
||||||
|
}
|
||||||
|
|
||||||
static RestApi config(Project.NameKey name) {
|
static RestApi config(Project.NameKey name) {
|
||||||
return new RestApi("/projects/").id(name.get()).view("config");
|
return new RestApi("/projects/").id(name.get()).view("config");
|
||||||
}
|
}
|
||||||
@ -53,4 +62,15 @@ public class ProjectApi {
|
|||||||
|
|
||||||
final native void setCreateEmptyCommit(boolean cc) /*-{ if(cc)this.create_empty_commit=cc; }-*/;
|
final native void setCreateEmptyCommit(boolean cc) /*-{ if(cc)this.create_empty_commit=cc; }-*/;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class BranchInput extends JavaScriptObject {
|
||||||
|
static BranchInput create() {
|
||||||
|
return (BranchInput) createObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BranchInput() {
|
||||||
|
}
|
||||||
|
|
||||||
|
final native void setRevision(String r) /*-{ if(r)this.revision=r; }-*/;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
package com.google.gerrit.httpd.rpc.project;
|
package com.google.gerrit.httpd.rpc.project;
|
||||||
|
|
||||||
import com.google.gerrit.common.data.AccessSection;
|
import com.google.gerrit.common.data.AccessSection;
|
||||||
import com.google.gerrit.common.data.AddBranchResult;
|
|
||||||
import com.google.gerrit.common.data.ListBranchesResult;
|
import com.google.gerrit.common.data.ListBranchesResult;
|
||||||
import com.google.gerrit.common.data.ProjectAccess;
|
import com.google.gerrit.common.data.ProjectAccess;
|
||||||
import com.google.gerrit.common.data.ProjectAdminService;
|
import com.google.gerrit.common.data.ProjectAdminService;
|
||||||
@ -32,7 +31,6 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
class ProjectAdminServiceImpl implements ProjectAdminService {
|
class ProjectAdminServiceImpl implements ProjectAdminService {
|
||||||
private final AddBranch.Factory addBranchFactory;
|
|
||||||
private final ChangeProjectAccess.Factory changeProjectAccessFactory;
|
private final ChangeProjectAccess.Factory changeProjectAccessFactory;
|
||||||
private final ReviewProjectAccess.Factory reviewProjectAccessFactory;
|
private final ReviewProjectAccess.Factory reviewProjectAccessFactory;
|
||||||
private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
|
private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
|
||||||
@ -43,8 +41,7 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
|
|||||||
private final ProjectDetailFactory.Factory projectDetailFactory;
|
private final ProjectDetailFactory.Factory projectDetailFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
|
ProjectAdminServiceImpl(final ChangeProjectAccess.Factory changeProjectAccessFactory,
|
||||||
final ChangeProjectAccess.Factory changeProjectAccessFactory,
|
|
||||||
final ReviewProjectAccess.Factory reviewProjectAccessFactory,
|
final ReviewProjectAccess.Factory reviewProjectAccessFactory,
|
||||||
final ChangeProjectSettings.Factory changeProjectSettingsFactory,
|
final ChangeProjectSettings.Factory changeProjectSettingsFactory,
|
||||||
final DeleteBranches.Factory deleteBranchesFactory,
|
final DeleteBranches.Factory deleteBranchesFactory,
|
||||||
@ -52,7 +49,6 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
|
|||||||
final VisibleProjectDetails.Factory visibleProjectDetailsFactory,
|
final VisibleProjectDetails.Factory visibleProjectDetailsFactory,
|
||||||
final ProjectAccessFactory.Factory projectAccessFactory,
|
final ProjectAccessFactory.Factory projectAccessFactory,
|
||||||
final ProjectDetailFactory.Factory projectDetailFactory) {
|
final ProjectDetailFactory.Factory projectDetailFactory) {
|
||||||
this.addBranchFactory = addBranchFactory;
|
|
||||||
this.changeProjectAccessFactory = changeProjectAccessFactory;
|
this.changeProjectAccessFactory = changeProjectAccessFactory;
|
||||||
this.reviewProjectAccessFactory = reviewProjectAccessFactory;
|
this.reviewProjectAccessFactory = reviewProjectAccessFactory;
|
||||||
this.changeProjectSettingsFactory = changeProjectSettingsFactory;
|
this.changeProjectSettingsFactory = changeProjectSettingsFactory;
|
||||||
@ -119,12 +115,4 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
|
|||||||
final AsyncCallback<Set<Branch.NameKey>> callback) {
|
final AsyncCallback<Set<Branch.NameKey>> callback) {
|
||||||
deleteBranchesFactory.create(projectName, toRemove).to(callback);
|
deleteBranchesFactory.create(projectName, toRemove).to(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addBranch(final Project.NameKey projectName,
|
|
||||||
final String branchName, final String startingRevision,
|
|
||||||
final AsyncCallback<AddBranchResult> callback) {
|
|
||||||
addBranchFactory.create(projectName, branchName, startingRevision).to(
|
|
||||||
callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ public class ProjectModule extends RpcServletModule {
|
|||||||
install(new FactoryModule() {
|
install(new FactoryModule() {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
factory(AddBranch.Factory.class);
|
|
||||||
factory(ChangeProjectAccess.Factory.class);
|
factory(ChangeProjectAccess.Factory.class);
|
||||||
factory(ReviewProjectAccess.Factory.class);
|
factory(ReviewProjectAccess.Factory.class);
|
||||||
factory(ChangeProjectSettings.Factory.class);
|
factory(ChangeProjectSettings.Factory.class);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.server.project;
|
package com.google.gerrit.server.project;
|
||||||
|
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.AcceptsCreate;
|
||||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||||
import com.google.gerrit.extensions.restapi.IdString;
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
@ -29,15 +30,18 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BranchesCollection implements
|
public class BranchesCollection implements
|
||||||
ChildCollection<ProjectResource, BranchResource> {
|
ChildCollection<ProjectResource, BranchResource>,
|
||||||
|
AcceptsCreate<ProjectResource> {
|
||||||
private final DynamicMap<RestView<BranchResource>> views;
|
private final DynamicMap<RestView<BranchResource>> views;
|
||||||
private final Provider<ListBranches> list;
|
private final Provider<ListBranches> list;
|
||||||
|
private final CreateBranch.Factory createBranchFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BranchesCollection(DynamicMap<RestView<BranchResource>> views,
|
BranchesCollection(DynamicMap<RestView<BranchResource>> views,
|
||||||
Provider<ListBranches> list) {
|
Provider<ListBranches> list, CreateBranch.Factory createBranchFactory) {
|
||||||
this.views = views;
|
this.views = views;
|
||||||
this.list = list;
|
this.list = list;
|
||||||
|
this.createBranchFactory = createBranchFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,4 +70,10 @@ public class BranchesCollection implements
|
|||||||
public DynamicMap<RestView<BranchResource>> views() {
|
public DynamicMap<RestView<BranchResource>> views() {
|
||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public CreateBranch create(ProjectResource parent, IdString name) {
|
||||||
|
return createBranchFactory.create(name.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2009 The Android Open Source Project
|
// Copyright (C) 2013 The Android Open Source Project
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,26 +12,28 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package com.google.gerrit.httpd.rpc.project;
|
package com.google.gerrit.server.project;
|
||||||
|
|
||||||
import com.google.gerrit.common.ChangeHooks;
|
import com.google.gerrit.common.ChangeHooks;
|
||||||
import com.google.gerrit.common.data.AddBranchResult;
|
|
||||||
import com.google.gerrit.common.errors.InvalidRevisionException;
|
import com.google.gerrit.common.errors.InvalidRevisionException;
|
||||||
import com.google.gerrit.httpd.rpc.Handler;
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.DefaultInput;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
import com.google.gerrit.reviewdb.client.Branch;
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.CreateBranch.Input;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ListBranches.BranchInfo;
|
||||||
import com.google.gerrit.server.project.RefControl;
|
|
||||||
import com.google.gerrit.server.util.MagicBranch;
|
import com.google.gerrit.server.util.MagicBranch;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.errors.RevisionSyntaxException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
@ -45,101 +47,92 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
class AddBranch extends Handler<AddBranchResult> {
|
public class CreateBranch implements RestModifyView<ProjectResource, Input> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AddBranch.class);
|
private static final Logger log = LoggerFactory.getLogger(CreateBranch.class);
|
||||||
|
|
||||||
interface Factory {
|
static class Input {
|
||||||
AddBranch create(@Assisted Project.NameKey projectName,
|
String ref;
|
||||||
@Assisted("branchName") String branchName,
|
|
||||||
@Assisted("startingRevision") String startingRevision);
|
@DefaultInput
|
||||||
|
String revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface Factory {
|
||||||
|
CreateBranch create(String ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ProjectControl.Factory projectControlFactory;
|
|
||||||
private final ListBranches.Factory listBranchesFactory;
|
|
||||||
private final IdentifiedUser identifiedUser;
|
private final IdentifiedUser identifiedUser;
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final GitReferenceUpdated referenceUpdated;
|
private final GitReferenceUpdated referenceUpdated;
|
||||||
private final ChangeHooks hooks;
|
private final ChangeHooks hooks;
|
||||||
|
private String ref;
|
||||||
private final Project.NameKey projectName;
|
|
||||||
private final String branchName;
|
|
||||||
private final String startingRevision;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AddBranch(final ProjectControl.Factory projectControlFactory,
|
CreateBranch(IdentifiedUser identifiedUser, GitRepositoryManager repoManager,
|
||||||
final ListBranches.Factory listBranchesFactory,
|
GitReferenceUpdated referenceUpdated, ChangeHooks hooks,
|
||||||
final IdentifiedUser identifiedUser,
|
@Assisted String ref) {
|
||||||
final GitRepositoryManager repoManager,
|
|
||||||
GitReferenceUpdated referenceUpdated,
|
|
||||||
final ChangeHooks hooks,
|
|
||||||
|
|
||||||
@Assisted Project.NameKey projectName,
|
|
||||||
@Assisted("branchName") String branchName,
|
|
||||||
@Assisted("startingRevision") String startingRevision) {
|
|
||||||
this.projectControlFactory = projectControlFactory;
|
|
||||||
this.listBranchesFactory = listBranchesFactory;
|
|
||||||
this.identifiedUser = identifiedUser;
|
this.identifiedUser = identifiedUser;
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.referenceUpdated = referenceUpdated;
|
this.referenceUpdated = referenceUpdated;
|
||||||
this.hooks = hooks;
|
this.hooks = hooks;
|
||||||
|
this.ref = ref;
|
||||||
this.projectName = projectName;
|
|
||||||
this.branchName = branchName;
|
|
||||||
this.startingRevision = startingRevision;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddBranchResult call() throws NoSuchProjectException, IOException {
|
public BranchInfo apply(ProjectResource rsrc, Input input)
|
||||||
final ProjectControl projectControl =
|
throws BadRequestException, ResourceConflictException, IOException {
|
||||||
projectControlFactory.controlFor(projectName);
|
if (input == null) {
|
||||||
|
input = new Input();
|
||||||
String refname = branchName;
|
|
||||||
while (refname.startsWith("/")) {
|
|
||||||
refname = refname.substring(1);
|
|
||||||
}
|
}
|
||||||
if (!refname.startsWith(Constants.R_REFS)) {
|
if (input.ref != null && !ref.equals(input.ref)) {
|
||||||
refname = Constants.R_HEADS + refname;
|
throw new BadRequestException("ref must match URL");
|
||||||
}
|
}
|
||||||
if (!Repository.isValidRefName(refname)) {
|
if (input.revision == null) {
|
||||||
return new AddBranchResult(new AddBranchResult.Error(
|
input.revision = Constants.HEAD;
|
||||||
AddBranchResult.Error.Type.INVALID_NAME, refname));
|
|
||||||
}
|
}
|
||||||
if (MagicBranch.isMagicBranch(refname)) {
|
while (ref.startsWith("/")) {
|
||||||
return new AddBranchResult(
|
ref = ref.substring(1);
|
||||||
new AddBranchResult.Error(
|
}
|
||||||
AddBranchResult.Error.Type.BRANCH_CREATION_NOT_ALLOWED_UNDER_REFNAME_PREFIX,
|
if (!ref.startsWith(Constants.R_REFS)) {
|
||||||
MagicBranch.getMagicRefNamePrefix(refname)));
|
ref = Constants.R_HEADS + ref;
|
||||||
|
}
|
||||||
|
if (!Repository.isValidRefName(ref)) {
|
||||||
|
throw new BadRequestException("invalid branch name \"" + ref + "\"");
|
||||||
|
}
|
||||||
|
if (MagicBranch.isMagicBranch(ref)) {
|
||||||
|
throw new BadRequestException("not allowed to create branches under \""
|
||||||
|
+ MagicBranch.getMagicRefNamePrefix(ref) + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Branch.NameKey name = new Branch.NameKey(projectName, refname);
|
final Branch.NameKey name = new Branch.NameKey(rsrc.getNameKey(), ref);
|
||||||
final RefControl refControl = projectControl.controlForRef(name);
|
final RefControl refControl = rsrc.getControl().controlForRef(name);
|
||||||
final Repository repo = repoManager.openRepository(projectName);
|
final Repository repo = repoManager.openRepository(rsrc.getNameKey());
|
||||||
try {
|
try {
|
||||||
final ObjectId revid = parseStartingRevision(repo);
|
final ObjectId revid = parseBaseRevision(repo, rsrc.getNameKey(), input.revision);
|
||||||
final RevWalk rw = verifyConnected(repo, revid);
|
final RevWalk rw = verifyConnected(repo, revid);
|
||||||
RevObject object = rw.parseAny(revid);
|
RevObject object = rw.parseAny(revid);
|
||||||
|
|
||||||
if (refname.startsWith(Constants.R_HEADS)) {
|
if (ref.startsWith(Constants.R_HEADS)) {
|
||||||
// Ensure that what we start the branch from is a commit. If we
|
// Ensure that what we start the branch from is a commit. If we
|
||||||
// were given a tag, deference to the commit instead.
|
// were given a tag, deference to the commit instead.
|
||||||
//
|
//
|
||||||
try {
|
try {
|
||||||
object = rw.parseCommit(object);
|
object = rw.parseCommit(object);
|
||||||
} catch (IncorrectObjectTypeException notCommit) {
|
} catch (IncorrectObjectTypeException notCommit) {
|
||||||
throw new IllegalStateException(startingRevision + " not a commit");
|
throw new BadRequestException("\"" + input.revision + "\" not a commit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!refControl.canCreate(rw, object)) {
|
if (!refControl.canCreate(rw, object)) {
|
||||||
throw new IllegalStateException("Cannot create " + refname);
|
throw new IllegalStateException("Cannot create \"" + ref + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final RefUpdate u = repo.updateRef(refname);
|
final RefUpdate u = repo.updateRef(ref);
|
||||||
u.setExpectedOldObjectId(ObjectId.zeroId());
|
u.setExpectedOldObjectId(ObjectId.zeroId());
|
||||||
u.setNewObjectId(object.copy());
|
u.setNewObjectId(object.copy());
|
||||||
u.setRefLogIdent(identifiedUser.newRefLogIdent());
|
u.setRefLogIdent(identifiedUser.newRefLogIdent());
|
||||||
u.setRefLogMessage("created via web from " + startingRevision, false);
|
u.setRefLogMessage("created via REST from " + input.revision, false);
|
||||||
final RefUpdate.Result result = u.update(rw);
|
final RefUpdate.Result result = u.update(rw);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case FAST_FORWARD:
|
case FAST_FORWARD:
|
||||||
@ -149,15 +142,16 @@ class AddBranch extends Handler<AddBranchResult> {
|
|||||||
hooks.doRefUpdatedHook(name, u, identifiedUser.getAccount());
|
hooks.doRefUpdatedHook(name, u, identifiedUser.getAccount());
|
||||||
break;
|
break;
|
||||||
case LOCK_FAILURE:
|
case LOCK_FAILURE:
|
||||||
if (repo.getRef(refname) != null) {
|
if (repo.getRef(ref) != null) {
|
||||||
return new AddBranchResult(new AddBranchResult.Error(
|
throw new ResourceConflictException("branch \"" + ref
|
||||||
AddBranchResult.Error.Type.BRANCH_ALREADY_EXISTS, refname));
|
+ "\" already exists");
|
||||||
}
|
}
|
||||||
String refPrefix = getRefPrefix(refname);
|
String refPrefix = getRefPrefix(ref);
|
||||||
while (!Constants.R_HEADS.equals(refPrefix)) {
|
while (!Constants.R_HEADS.equals(refPrefix)) {
|
||||||
if (repo.getRef(refPrefix) != null) {
|
if (repo.getRef(refPrefix) != null) {
|
||||||
return new AddBranchResult(new AddBranchResult.Error(
|
throw new ResourceConflictException("Cannot create branch \""
|
||||||
AddBranchResult.Error.Type.BRANCH_CREATION_CONFLICT, refPrefix));
|
+ ref + "\" since it conflicts with branch \"" + refPrefix
|
||||||
|
+ "\".");
|
||||||
}
|
}
|
||||||
refPrefix = getRefPrefix(refPrefix);
|
refPrefix = getRefPrefix(refPrefix);
|
||||||
}
|
}
|
||||||
@ -165,18 +159,21 @@ class AddBranch extends Handler<AddBranchResult> {
|
|||||||
throw new IOException(result.name());
|
throw new IOException(result.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BranchInfo b = new BranchInfo();
|
||||||
|
b.ref = ref;
|
||||||
|
b.revision = revid.getName();
|
||||||
|
b.setCanDelete(refControl.canDelete());
|
||||||
|
return b;
|
||||||
} catch (IOException err) {
|
} catch (IOException err) {
|
||||||
log.error("Cannot create branch " + name, err);
|
log.error("Cannot create branch \"" + name + "\"", err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
} catch (InvalidRevisionException e) {
|
} catch (InvalidRevisionException e) {
|
||||||
return new AddBranchResult(new AddBranchResult.Error(
|
throw new BadRequestException("invalid revision \"" + input.revision + "\"");
|
||||||
AddBranchResult.Error.Type.INVALID_REVISION));
|
|
||||||
} finally {
|
} finally {
|
||||||
repo.close();
|
repo.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AddBranchResult(listBranchesFactory.create(projectName).call());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getRefPrefix(final String refName) {
|
private static String getRefPrefix(final String refName) {
|
||||||
@ -187,17 +184,21 @@ class AddBranch extends Handler<AddBranchResult> {
|
|||||||
return Constants.R_HEADS;
|
return Constants.R_HEADS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectId parseStartingRevision(final Repository repo)
|
private ObjectId parseBaseRevision(Repository repo,
|
||||||
|
Project.NameKey projectName, String baseRevision)
|
||||||
throws InvalidRevisionException {
|
throws InvalidRevisionException {
|
||||||
try {
|
try {
|
||||||
final ObjectId revid = repo.resolve(startingRevision);
|
final ObjectId revid = repo.resolve(baseRevision);
|
||||||
if (revid == null) {
|
if (revid == null) {
|
||||||
throw new InvalidRevisionException();
|
throw new InvalidRevisionException();
|
||||||
}
|
}
|
||||||
return revid;
|
return revid;
|
||||||
} catch (IOException err) {
|
} catch (IOException err) {
|
||||||
log.error("Cannot resolve \"" + startingRevision + "\" in project \""
|
log.error("Cannot resolve \"" + baseRevision + "\" in project \""
|
||||||
+ projectName + "\"", err);
|
+ projectName.get() + "\"", err);
|
||||||
|
throw new InvalidRevisionException();
|
||||||
|
} catch (RevisionSyntaxException err) {
|
||||||
|
log.error("Invalid revision syntax \"" + baseRevision + "\"", err);
|
||||||
throw new InvalidRevisionException();
|
throw new InvalidRevisionException();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -53,7 +53,9 @@ public class Module extends RestApiModule {
|
|||||||
post(PROJECT_KIND, "gc").to(GarbageCollect.class);
|
post(PROJECT_KIND, "gc").to(GarbageCollect.class);
|
||||||
|
|
||||||
child(PROJECT_KIND, "branches").to(BranchesCollection.class);
|
child(PROJECT_KIND, "branches").to(BranchesCollection.class);
|
||||||
|
put(BRANCH_KIND).to(PutBranch.class);
|
||||||
get(BRANCH_KIND).to(GetBranch.class);
|
get(BRANCH_KIND).to(GetBranch.class);
|
||||||
|
install(new FactoryModuleBuilder().build(CreateBranch.Factory.class));
|
||||||
|
|
||||||
child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);
|
child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);
|
||||||
get(DASHBOARD_KIND).to(GetDashboard.class);
|
get(DASHBOARD_KIND).to(GetDashboard.class);
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
// 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.project;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
|
import com.google.gerrit.server.project.CreateBranch.Input;
|
||||||
|
|
||||||
|
public class PutBranch implements RestModifyView<BranchResource, Input> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(BranchResource rsrc, Input input)
|
||||||
|
throws ResourceConflictException {
|
||||||
|
throw new ResourceConflictException("Branch \"" + rsrc.getBranchInfo().ref
|
||||||
|
+ "\" already exists");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user