diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index 465388ed37..21b0a53990 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt @@ -318,6 +318,62 @@ As response the new parent project name is returned. "Public-Plugins" ---- +[[get-head]] +Get HEAD +~~~~~~~~ +[verse] +'GET /projects/link:#project-name[\{project-name\}]/HEAD' + +Retrieves for a project the name of the branch to which `HEAD` points. + +.Request +---- + GET /projects/plugins%2Freplication/HEAD HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + "refs/heads/master" +---- + +[[set-head]] +Set HEAD +~~~~~~~~ +[verse] +'PUT /projects/link:#project-name[\{project-name\}]/HEAD' + +Sets `HEAD` for a project. + +The new ref to which `HEAD` should point must be provided in the +request body inside a link:#head-input[HeadInput] entity. + +.Request +---- + PUT /projects/plugins%2Freplication/HEAD HTTP/1.0 + Content-Type: application/json;charset=UTF-8 + + { + "ref": "refs/heads/stable" + } +---- + +As response the new ref to which `HEAD` points is returned. + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + "refs/heads/stable" +---- + [[dashboard-endpoints]] Dashboard Endpoints ------------------- @@ -632,6 +688,20 @@ in a dashboard. Tokens such as `${project}` are not resolved. |=========================== +[[head-input]] +HeadInput +~~~~~~~~~ +The `HeadInput` entity contains information for setting `HEAD` for a +project. + +[options="header",width="50%",cols="1,6"] +|============================ +|Field Name |Description +|`ref` | +The ref to which `HEAD` should be set, the `refs/heads` prefix can be +omitted. +|============================ + [[project-description-input]] ProjectDescriptionInput ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java new file mode 100644 index 0000000000..ea39f73b44 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java @@ -0,0 +1,69 @@ +// 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.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.server.auth.AuthException; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.inject.Inject; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; + +public class GetHead implements RestReadView { + + private GitRepositoryManager repoManager; + + @Inject + GetHead(GitRepositoryManager repoManager) { + this.repoManager = repoManager; + } + + @Override + public String apply(ProjectResource rsrc) throws AuthException, + ResourceNotFoundException, IOException { + Repository repo = null; + try { + repo = repoManager.openRepository(rsrc.getNameKey()); + Ref head = repo.getRef(Constants.HEAD); + if (head == null) { + throw new ResourceNotFoundException(Constants.HEAD); + } else if (head.isSymbolic()) { + String n = head.getTarget().getName(); + if (rsrc.getControl().controlForRef(n).isVisible()) { + return n; + } + throw new AuthException(); + } else if (head.getObjectId() != null) { + if (rsrc.getControl().isOwner()) { + return head.getObjectId().name(); + } + throw new AuthException(); + } + throw new ResourceNotFoundException(Constants.HEAD); + } catch (RepositoryNotFoundException e) { + throw new ResourceNotFoundException(rsrc.getName()); + } finally { + if (repo != null) { + repo.close(); + } + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java index 6b63db356c..74a1ec36b0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java @@ -40,6 +40,9 @@ public class Module extends RestApiModule { get(PROJECT_KIND, "parent").to(GetParent.class); put(PROJECT_KIND, "parent").to(SetParent.class); + get(PROJECT_KIND, "HEAD").to(GetHead.class); + put(PROJECT_KIND, "HEAD").to(SetHead.class); + child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class); get(DASHBOARD_KIND).to(GetDashboard.class); put(DASHBOARD_KIND).to(SetDashboard.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java new file mode 100644 index 0000000000..3e7a42fc9e --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java @@ -0,0 +1,97 @@ +// 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.common.base.Strings; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.auth.AuthException; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.project.SetHead.Input; +import com.google.inject.Inject; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; + +public class SetHead implements RestModifyView { + static class Input { + @DefaultInput + String ref; + } + + private final GitRepositoryManager repoManager; + private final IdentifiedUser identifiedUser; + + @Inject + SetHead(GitRepositoryManager repoManager, IdentifiedUser identifiedUser) { + this.repoManager = repoManager; + this.identifiedUser = identifiedUser; + } + + @Override + public String apply(ProjectResource rsrc, Input input) throws AuthException, + ResourceNotFoundException, BadRequestException, + UnprocessableEntityException, IOException { + if (!rsrc.getControl().isOwner()) { + throw new AuthException("restricted to project owner"); + } + if (input == null || Strings.isNullOrEmpty(input.ref)) { + throw new BadRequestException("ref required"); + } + String ref = input.ref; + if (!ref.startsWith(Constants.R_REFS)) { + ref = Constants.R_HEADS + ref; + } + + Repository repo = null; + try { + repo = repoManager.openRepository(rsrc.getNameKey()); + if (repo.getRef(ref) == null) { + throw new UnprocessableEntityException(String.format( + "Ref Not Found: %s", ref)); + } + + if (!repo.getRef(Constants.HEAD).getTarget().getName().equals(ref)) { + final RefUpdate u = repo.updateRef(Constants.HEAD, true); + u.setRefLogIdent(identifiedUser.newRefLogIdent()); + RefUpdate.Result res = u.link(ref); + switch(res) { + case NO_CHANGE: + case RENAMED: + case FORCED: + case NEW: + break; + default: + throw new IOException("Setting HEAD failed with " + res); + } + } + return ref; + } catch (RepositoryNotFoundException e) { + throw new ResourceNotFoundException(rsrc.getName()); + } finally { + if (repo != null) { + repo.close(); + } + } + } +}