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:
Edwin Kempin 2013-05-09 19:54:37 +02:00 committed by Shawn Pearce
parent 94a928b9a9
commit 5c0d6b33ff
11 changed files with 245 additions and 151 deletions

View File

@ -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
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -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,

View File

@ -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) {

View File

@ -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() {
}
}

View File

@ -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; }-*/;
}
} }

View File

@ -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);
}
} }

View File

@ -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);

View File

@ -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());
}
} }

View File

@ -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();
} }
} }

View File

@ -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);

View File

@ -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");
}
}