summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/server/restapi/project/CreateBranch.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/server/restapi/project/CreateBranch.java')
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateBranch.java200
1 files changed, 200 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
new file mode 100644
index 0000000000..62106e8c1d
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -0,0 +1,200 @@
+// 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.restapi.project;
+
+import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.api.projects.BranchInfo;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.RefNames;
+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.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.BranchResource;
+import com.google.gerrit.server.project.CreateRefControl;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.RefUtil;
+import com.google.gerrit.server.project.RefValidationHelper;
+import com.google.gerrit.server.util.MagicBranch;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+@Singleton
+public class CreateBranch
+ implements RestCollectionCreateView<ProjectResource, BranchResource, BranchInput> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Provider<IdentifiedUser> identifiedUser;
+ private final PermissionBackend permissionBackend;
+ private final GitRepositoryManager repoManager;
+ private final GitReferenceUpdated referenceUpdated;
+ private final RefValidationHelper refCreationValidator;
+ private final CreateRefControl createRefControl;
+
+ @Inject
+ CreateBranch(
+ Provider<IdentifiedUser> identifiedUser,
+ PermissionBackend permissionBackend,
+ GitRepositoryManager repoManager,
+ GitReferenceUpdated referenceUpdated,
+ RefValidationHelper.Factory refHelperFactory,
+ CreateRefControl createRefControl) {
+ this.identifiedUser = identifiedUser;
+ this.permissionBackend = permissionBackend;
+ this.repoManager = repoManager;
+ this.referenceUpdated = referenceUpdated;
+ this.refCreationValidator = refHelperFactory.create(ReceiveCommand.Type.CREATE);
+ this.createRefControl = createRefControl;
+ }
+
+ @Override
+ public BranchInfo apply(ProjectResource rsrc, IdString id, BranchInput input)
+ throws BadRequestException, AuthException, ResourceConflictException, IOException,
+ PermissionBackendException, NoSuchProjectException {
+ String ref = id.get();
+ if (input == null) {
+ input = new BranchInput();
+ }
+ if (input.ref != null && !ref.equals(input.ref)) {
+ throw new BadRequestException("ref must match URL");
+ }
+ if (input.revision == null) {
+ input.revision = Constants.HEAD;
+ }
+ while (ref.startsWith("/")) {
+ ref = ref.substring(1);
+ }
+ ref = RefNames.fullName(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(rsrc.getNameKey(), ref);
+ try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
+ ObjectId revid = RefUtil.parseBaseRevision(repo, rsrc.getNameKey(), input.revision);
+ RevWalk rw = RefUtil.verifyConnected(repo, revid);
+ RevObject object = rw.parseAny(revid);
+
+ if (ref.startsWith(Constants.R_HEADS)) {
+ // Ensure that what we start the branch from is a commit. If we
+ // were given a tag, deference to the commit instead.
+ //
+ try {
+ object = rw.parseCommit(object);
+ } catch (IncorrectObjectTypeException notCommit) {
+ throw new BadRequestException("\"" + input.revision + "\" not a commit");
+ }
+ }
+
+ createRefControl.checkCreateRef(identifiedUser, repo, name, object);
+
+ try {
+ final RefUpdate u = repo.updateRef(ref);
+ u.setExpectedOldObjectId(ObjectId.zeroId());
+ u.setNewObjectId(object.copy());
+ u.setRefLogIdent(identifiedUser.get().newRefLogIdent());
+ u.setRefLogMessage("created via REST from " + input.revision, false);
+ refCreationValidator.validateRefOperation(rsrc.getName(), identifiedUser.get(), u);
+ final RefUpdate.Result result = u.update(rw);
+ switch (result) {
+ case FAST_FORWARD:
+ case NEW:
+ case NO_CHANGE:
+ referenceUpdated.fire(
+ name.getParentKey(), u, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
+ break;
+ case LOCK_FAILURE:
+ if (repo.getRefDatabase().exactRef(ref) != null) {
+ throw new ResourceConflictException("branch \"" + ref + "\" already exists");
+ }
+ String refPrefix = RefUtil.getRefPrefix(ref);
+ while (!Constants.R_HEADS.equals(refPrefix)) {
+ if (repo.getRefDatabase().exactRef(refPrefix) != null) {
+ throw new ResourceConflictException(
+ "Cannot create branch \""
+ + ref
+ + "\" since it conflicts with branch \""
+ + refPrefix
+ + "\".");
+ }
+ refPrefix = RefUtil.getRefPrefix(refPrefix);
+ }
+ // fall through
+ // $FALL-THROUGH$
+ case FORCED:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ {
+ throw new IOException(result.name());
+ }
+ }
+
+ BranchInfo info = new BranchInfo();
+ info.ref = ref;
+ info.revision = revid.getName();
+
+ if (isConfigRef(name.get())) {
+ // Never allow to delete the meta config branch.
+ info.canDelete = null;
+ } else {
+ info.canDelete =
+ permissionBackend.currentUser().ref(name).testOrFalse(RefPermission.DELETE)
+ && rsrc.getProjectState().statePermitsWrite()
+ ? true
+ : null;
+ }
+ return info;
+ } catch (IOException err) {
+ logger.atSevere().withCause(err).log("Cannot create branch \"%s\"", name);
+ throw err;
+ }
+ } catch (RefUtil.InvalidRevisionException e) {
+ throw new BadRequestException("invalid revision \"" + input.revision + "\"");
+ }
+ }
+}