diff options
author | Shawn O. Pearce <sop@google.com> | 2009-02-10 20:07:56 -0800 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2009-02-10 20:07:56 -0800 |
commit | 3a8874b34ec2a722b01853846d166927c1a774c9 (patch) | |
tree | 45d533fa32dad0419404f4c67b0517831a6a91ee | |
parent | 99bab581a0d1ebf411e792dbac0a2c32c328c4be (diff) |
Add a Branches tab to the project admin screen
This tab allows project owners to view branches, delete branches,
and create new branches from any Git commit SHA-1 expression we
can recognize in JGit (which is most of the expressions, except
reflog queries).
Bug: GERRIT-20
Signed-off-by: Shawn O. Pearce <sop@google.com>
8 files changed, 602 insertions, 0 deletions
diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/src/main/java/com/google/gerrit/client/admin/AdminConstants.java index 03d0197d48..41c8df4c3f 100644 --- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java +++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.java @@ -19,6 +19,8 @@ import com.google.gwt.i18n.client.Constants; public interface AdminConstants extends Constants { String defaultAccountName(); String defaultAccountGroupName(); + String defaultBranchName(); + String defaultRevisionSpec(); String buttonDeleteGroupMembers(); String buttonAddGroupMember(); @@ -43,8 +45,14 @@ public interface AdminConstants extends Constants { String columnApprovalCategory(); String columnRightRange(); + String columnBranchName(); + String initialRevision(); + String buttonAddBranch(); + String buttonDeleteBranch(); + String groupListTitle(); String projectListTitle(); String projectAdminTabGeneral(); + String projectAdminTabBranches(); String projectAdminTabAccess(); } diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index dca7ce07b1..94bff9eb3c 100644 --- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties @@ -1,5 +1,7 @@ defaultAccountName = Name or Email defaultAccountGroupName = Group Name +defaultBranchName = Branch Name +defaultRevisionSpec = Revision (Branch or SHA-1) buttonDeleteGroupMembers = Delete buttonAddGroupMember = Add @@ -24,7 +26,13 @@ columnProjectDescription = Description columnApprovalCategory = Category columnRightRange = Permitted Range +columnBranchName = Branch Name +initialRevision = Initial Revision +buttonAddBranch = Create Branch +buttonDeleteBranch = Delete + groupListTitle = Groups projectListTitle = Projects projectAdminTabGeneral = General +projectAdminTabBranches = Branches projectAdminTabAccess = Access diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java b/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java index d6509ffc46..f6d40563c3 100644 --- a/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java +++ b/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java @@ -17,6 +17,7 @@ package com.google.gerrit.client.admin; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.Link; import com.google.gerrit.client.reviewdb.Project; +import com.google.gerrit.client.reviewdb.ProjectRight; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.ui.AccountScreen; import com.google.gerrit.client.ui.LazyTabChild; @@ -29,6 +30,7 @@ import java.util.List; public class ProjectAdminScreen extends AccountScreen { static final String INFO_TAB = "info"; + static final String BRANCH_TAB = "branches"; static final String ACCESS_TAB = "access"; private String initialTabToken; @@ -73,6 +75,16 @@ public class ProjectAdminScreen extends AccountScreen { }, Util.C.projectAdminTabGeneral()); tabTokens.add(Link.toProjectAdmin(projectId, INFO_TAB)); + if (!ProjectRight.WILD_PROJECT.equals(projectId)) { + tabs.add(new LazyTabChild<ProjectBranchesPanel>() { + @Override + protected ProjectBranchesPanel create() { + return new ProjectBranchesPanel(projectId); + } + }, Util.C.projectAdminTabBranches()); + tabTokens.add(Link.toProjectAdmin(projectId, BRANCH_TAB)); + } + tabs.add(new LazyTabChild<ProjectRightsPanel>() { @Override protected ProjectRightsPanel create() { diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java b/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java index 85308622b4..21da108bbf 100644 --- a/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java +++ b/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java @@ -15,6 +15,7 @@ package com.google.gerrit.client.admin; import com.google.gerrit.client.reviewdb.ApprovalCategory; +import com.google.gerrit.client.reviewdb.Branch; import com.google.gerrit.client.reviewdb.Project; import com.google.gerrit.client.reviewdb.ProjectRight; import com.google.gerrit.client.rpc.SignInRequired; @@ -47,4 +48,15 @@ public interface ProjectAdminService extends RemoteJsonService { void addRight(Project.Id projectId, ApprovalCategory.Id categoryId, String groupName, short min, short max, AsyncCallback<ProjectDetail> callback); + + @SignInRequired + void listBranches(Project.Id project, AsyncCallback<List<Branch>> callback); + + @SignInRequired + void addBranch(Project.Id project, String branchName, + String startingRevision, AsyncCallback<List<Branch>> callback); + + @SignInRequired + void deleteBranch(Set<Branch.NameKey> ids, + AsyncCallback<Set<Branch.NameKey>> callback); } diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java new file mode 100644 index 0000000000..0253fa88ee --- /dev/null +++ b/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java @@ -0,0 +1,316 @@ +// Copyright 2009 Google Inc. +// +// 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.admin; + +import com.google.gerrit.client.data.GitwebLink; +import com.google.gerrit.client.reviewdb.Branch; +import com.google.gerrit.client.reviewdb.Project; +import com.google.gerrit.client.rpc.Common; +import com.google.gerrit.client.rpc.GerritCallback; +import com.google.gerrit.client.rpc.InvalidNameException; +import com.google.gerrit.client.rpc.InvalidRevisionException; +import com.google.gerrit.client.ui.FancyFlexTable; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusListenerAdapter; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.SourcesTableEvents; +import com.google.gwt.user.client.ui.TableListener; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; +import com.google.gwtjsonrpc.client.RemoteJsonException; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ProjectBranchesPanel extends Composite { + private Project.Id projectId; + + private BranchesTable branches; + private Button delBranch; + private Button addBranch; + private TextBox nameTxtBox; + private TextBox irevTxtBox; + + public ProjectBranchesPanel(final Project.Id toShow) { + final FlowPanel body = new FlowPanel(); + initBranches(body); + initWidget(body); + + projectId = toShow; + } + + @Override + public void onLoad() { + enableForm(false); + super.onLoad(); + + Util.PROJECT_SVC.listBranches(projectId, + new GerritCallback<List<Branch>>() { + public void onSuccess(final List<Branch> result) { + enableForm(true); + branches.display(result); + branches.finishDisplay(true); + } + }); + } + + private void enableForm(final boolean on) { + delBranch.setEnabled(on); + addBranch.setEnabled(on); + nameTxtBox.setEnabled(on); + irevTxtBox.setEnabled(on); + } + + private void initBranches(final Panel body) { + final FlowPanel addPanel = new FlowPanel(); + addPanel.setStyleName("gerrit-AddSshKeyPanel"); + + final Grid addGrid = new Grid(2, 2); + + nameTxtBox = new TextBox(); + nameTxtBox.setVisibleLength(50); + nameTxtBox.setText(Util.C.defaultBranchName()); + nameTxtBox.addStyleName("gerrit-InputFieldTypeHint"); + nameTxtBox.addFocusListener(new FocusListenerAdapter() { + @Override + public void onFocus(Widget sender) { + if (Util.C.defaultBranchName().equals(nameTxtBox.getText())) { + nameTxtBox.setText(""); + nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint"); + } + } + + @Override + public void onLostFocus(Widget sender) { + if ("".equals(nameTxtBox.getText())) { + nameTxtBox.setText(Util.C.defaultBranchName()); + nameTxtBox.addStyleName("gerrit-InputFieldTypeHint"); + } + } + }); + addGrid.setText(0, 0, Util.C.columnBranchName() + ":"); + addGrid.setWidget(0, 1, nameTxtBox); + + irevTxtBox = new TextBox(); + irevTxtBox.setVisibleLength(50); + irevTxtBox.setText(Util.C.defaultRevisionSpec()); + irevTxtBox.addStyleName("gerrit-InputFieldTypeHint"); + irevTxtBox.addFocusListener(new FocusListenerAdapter() { + @Override + public void onFocus(Widget sender) { + if (Util.C.defaultRevisionSpec().equals(irevTxtBox.getText())) { + irevTxtBox.setText(""); + irevTxtBox.removeStyleName("gerrit-InputFieldTypeHint"); + } + } + + @Override + public void onLostFocus(Widget sender) { + if ("".equals(irevTxtBox.getText())) { + irevTxtBox.setText(Util.C.defaultRevisionSpec()); + irevTxtBox.addStyleName("gerrit-InputFieldTypeHint"); + } + } + }); + addGrid.setText(1, 0, Util.C.initialRevision() + ":"); + addGrid.setWidget(1, 1, irevTxtBox); + + addBranch = new Button(Util.C.buttonAddBranch()); + addBranch.addClickListener(new ClickListener() { + public void onClick(final Widget sender) { + doAddNewBranch(); + } + }); + addPanel.add(addGrid); + addPanel.add(addBranch); + + branches = new BranchesTable(); + + delBranch = new Button(Util.C.buttonDeleteBranch()); + delBranch.addClickListener(new ClickListener() { + public void onClick(final Widget sender) { + branches.deleteChecked(); + } + }); + + body.add(branches); + body.add(delBranch); + body.add(addPanel); + } + + private void doAddNewBranch() { + String branchName = nameTxtBox.getText(); + if ("".equals(branchName) || Util.C.defaultBranchName().equals(branchName)) { + return; + } + + String rev = irevTxtBox.getText(); + if ("".equals(rev) || Util.C.defaultRevisionSpec().equals(rev)) { + return; + } + + if (!branchName.startsWith(Branch.R_REFS)) { + branchName = Branch.R_HEADS + branchName; + } + + addBranch.setEnabled(false); + Util.PROJECT_SVC.addBranch(projectId, branchName, rev, + new GerritCallback<List<Branch>>() { + public void onSuccess(final List<Branch> result) { + addBranch.setEnabled(true); + nameTxtBox.setText(""); + irevTxtBox.setText(""); + branches.display(result); + } + + @Override + public void onFailure(final Throwable caught) { + if (caught instanceof InvalidNameException + || caught instanceof RemoteJsonException + && caught.getMessage().equals(InvalidNameException.MESSAGE)) { + nameTxtBox.selectAll(); + nameTxtBox.setFocus(true); + + } else if (caught instanceof InvalidRevisionException + || caught instanceof RemoteJsonException + && caught.getMessage().equals(InvalidRevisionException.MESSAGE)) { + irevTxtBox.selectAll(); + irevTxtBox.setFocus(true); + } + + addBranch.setEnabled(true); + super.onFailure(caught); + } + }); + } + + private class BranchesTable extends FancyFlexTable<Branch> { + BranchesTable() { + table.setText(0, 2, Util.C.columnBranchName()); + table.setHTML(0, 3, " "); + table.addTableListener(new TableListener() { + public void onCellClicked(SourcesTableEvents sender, int row, int cell) { + if (cell != 1 && getRowItem(row) != null) { + movePointerTo(row); + } + } + }); + + final FlexCellFormatter fmt = table.getFlexCellFormatter(); + fmt.addStyleName(0, 1, S_ICON_HEADER); + fmt.addStyleName(0, 2, S_DATA_HEADER); + fmt.addStyleName(0, 3, S_DATA_HEADER); + } + + @Override + protected Object getRowItemKey(final Branch item) { + return item.getId(); + } + + @Override + protected boolean onKeyPress(final char keyCode, final int modifiers) { + if (super.onKeyPress(keyCode, modifiers)) { + return true; + } + if (modifiers == 0) { + switch (keyCode) { + case 's': + case 'c': + toggleCurrentRow(); + return true; + } + } + return false; + } + + @Override + protected void onOpenItem(final Branch item) { + toggleCurrentRow(); + } + + private void toggleCurrentRow() { + final CheckBox cb = (CheckBox) table.getWidget(getCurrentRow(), 1); + cb.setChecked(!cb.isChecked()); + } + + void deleteChecked() { + final HashSet<Branch.NameKey> ids = new HashSet<Branch.NameKey>(); + for (int row = 1; row < table.getRowCount(); row++) { + final Branch k = getRowItem(row); + if (k != null && table.getWidget(row, 1) instanceof CheckBox + && ((CheckBox) table.getWidget(row, 1)).isChecked()) { + ids.add(k.getNameKey()); + } + } + if (ids.isEmpty()) { + return; + } + + Util.PROJECT_SVC.deleteBranch(ids, + new GerritCallback<Set<Branch.NameKey>>() { + public void onSuccess(final Set<Branch.NameKey> deleted) { + for (int row = 1; row < table.getRowCount();) { + final Branch k = getRowItem(row); + if (k != null && deleted.contains(k.getNameKey())) { + table.removeRow(row); + } else { + row++; + } + } + } + }); + } + + void display(final List<Branch> result) { + while (1 < table.getRowCount()) + table.removeRow(table.getRowCount() - 1); + + for (final Branch k : result) { + final int row = table.getRowCount(); + table.insertRow(row); + applyDataRowStyle(row); + populate(row, k); + } + } + + void populate(final int row, final Branch k) { + final GitwebLink c = Common.getGerritConfig().getGitwebLink(); + + table.setWidget(row, 1, new CheckBox()); + table.setText(row, 2, k.getShortName()); + if (c != null) { + table.setWidget(row, 3, new Anchor("(gitweb)", false, c.toBranch(k + .getNameKey()))); + } else { + table.setHTML(row, 3, " "); + } + + final FlexCellFormatter fmt = table.getFlexCellFormatter(); + fmt.addStyleName(row, 1, S_ICON_CELL); + fmt.addStyleName(row, 2, S_DATA_CELL); + fmt.addStyleName(row, 3, S_DATA_CELL); + + setRowItem(row, k); + } + } +} diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java new file mode 100644 index 0000000000..c32eeebd2d --- /dev/null +++ b/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java @@ -0,0 +1,24 @@ +// Copyright 2009 Google Inc. +// +// 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.rpc; + +/** Error indicating the entity name is invalid as supplied. */ +public class InvalidNameException extends Exception { + public static final String MESSAGE = "Invalid Name"; + + public InvalidNameException() { + super(MESSAGE); + } +} diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java new file mode 100644 index 0000000000..801c6d24fc --- /dev/null +++ b/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java @@ -0,0 +1,24 @@ +// Copyright 2009 Google Inc. +// +// 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.rpc; + +/** Error indicating the revision is invalid as supplied. */ +public class InvalidRevisionException extends Exception { + public static final String MESSAGE = "Invalid Revision"; + + public InvalidRevisionException() { + super(MESSAGE); + } +} diff --git a/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java b/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java index 7b9adf90fa..356f1a7990 100644 --- a/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java +++ b/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java @@ -21,11 +21,14 @@ import com.google.gerrit.client.reviewdb.Account; import com.google.gerrit.client.reviewdb.AccountGroup; import com.google.gerrit.client.reviewdb.ApprovalCategory; import com.google.gerrit.client.reviewdb.ApprovalCategoryValue; +import com.google.gerrit.client.reviewdb.Branch; import com.google.gerrit.client.reviewdb.Project; import com.google.gerrit.client.reviewdb.ProjectRight; import com.google.gerrit.client.reviewdb.ReviewDb; import com.google.gerrit.client.rpc.BaseServiceImplementation; import com.google.gerrit.client.rpc.Common; +import com.google.gerrit.client.rpc.InvalidNameException; +import com.google.gerrit.client.rpc.InvalidRevisionException; import com.google.gerrit.client.rpc.NoSuchEntityException; import com.google.gerrit.git.InvalidRepositoryException; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -34,9 +37,16 @@ import com.google.gwtorm.client.OrmException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.spearce.jgit.errors.IncorrectObjectTypeException; +import org.spearce.jgit.errors.MissingObjectException; import org.spearce.jgit.lib.Constants; import org.spearce.jgit.lib.LockFile; +import org.spearce.jgit.lib.ObjectId; +import org.spearce.jgit.lib.Ref; +import org.spearce.jgit.lib.RefUpdate; import org.spearce.jgit.lib.Repository; +import org.spearce.jgit.revwalk.ObjectWalk; +import org.spearce.jgit.revwalk.RevCommit; import java.io.File; import java.io.IOException; @@ -250,6 +260,194 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation }); } + public void listBranches(final Project.Id project, + final AsyncCallback<List<Branch>> callback) { + run(callback, new Action<List<Branch>>() { + public List<Branch> run(ReviewDb db) throws OrmException, Failure { + final ProjectCache.Entry e = Common.getProjectCache().get(project); + if (e == null) { + throw new Failure(new NoSuchEntityException()); + } + assertCanRead(e.getProject().getNameKey()); + return db.branches().byProject(e.getProject().getNameKey()).toList(); + } + }); + } + + public void deleteBranch(final Set<Branch.NameKey> ids, + final AsyncCallback<Set<Branch.NameKey>> callback) { + run(callback, new Action<Set<Branch.NameKey>>() { + public Set<Branch.NameKey> run(ReviewDb db) throws OrmException, Failure { + final Set<Branch.NameKey> deleted = new HashSet<Branch.NameKey>(); + final Set<Project.Id> owned = ids(myOwnedProjects(db)); + Boolean amAdmin = null; + for (final Branch.NameKey k : ids) { + final ProjectCache.Entry e; + + e = Common.getProjectCache().get(k.getParentKey()); + if (e == null) { + throw new Failure(new NoSuchEntityException()); + } + if (!owned.contains(e.getProject().getId())) { + if (amAdmin == null) { + amAdmin = + Common.getGroupCache().isAdministrator(Common.getAccountId()); + } + if (!amAdmin) { + throw new Failure(new NoSuchEntityException()); + } + } + } + for (final Branch.NameKey k : ids) { + final Branch m = db.branches().get(k); + if (m == null) { + continue; + } + final Repository r; + + try { + r = server.getRepositoryCache().get(k.getParentKey().get()); + } catch (InvalidRepositoryException e) { + throw new Failure(new NoSuchEntityException()); + } + + final RefUpdate.Result result; + try { + final RefUpdate u = r.updateRef(m.getName()); + u.setForceUpdate(true); + result = u.delete(); + } catch (IOException e) { + log.error("Cannot delete " + k, e); + continue; + } + + switch (result) { + case NEW: + case NO_CHANGE: + case FAST_FORWARD: + case FORCED: + db.branches().delete(Collections.singleton(m)); + deleted.add(m.getNameKey()); + break; + + case REJECTED_CURRENT_BRANCH: + log.warn("Cannot delete " + k + ": " + result.name()); + break; + + default: + log.error("Cannot delete " + k + ": " + result.name()); + break; + } + } + return deleted; + } + }); + } + + public void addBranch(final Project.Id projectId, final String branchName, + final String startingRevision, final AsyncCallback<List<Branch>> callback) { + run(callback, new Action<List<Branch>>() { + public List<Branch> run(ReviewDb db) throws OrmException, Failure { + String refname = branchName; + if (!refname.startsWith(Constants.R_REFS)) { + refname = Constants.R_HEADS + refname; + } + if (!Repository.isValidRefName(refname)) { + throw new Failure(new InvalidNameException()); + } + + final Account me = Common.getAccountCache().get(Common.getAccountId()); + if (me == null) { + throw new Failure(new NoSuchEntityException()); + } + final ProjectCache.Entry pce = Common.getProjectCache().get(projectId); + if (pce == null) { + throw new Failure(new NoSuchEntityException()); + } + assertAmProjectOwner(db, projectId); + + final String repoName = pce.getProject().getName(); + final Repository repo; + try { + repo = server.getRepositoryCache().get(repoName); + } catch (InvalidRepositoryException e1) { + throw new Failure(new NoSuchEntityException()); + } + + // Convert the name given by the user into a valid object. + // + final ObjectId revid; + try { + revid = repo.resolve(startingRevision); + if (revid == null) { + throw new Failure(new InvalidRevisionException()); + } + } catch (IOException err) { + log.error("Cannot resolve \"" + startingRevision + "\" in " + + repoName, err); + throw new Failure(new InvalidRevisionException()); + } + + // Ensure it is fully connected in this repository. If not, + // we can't safely create a ref to it as objects are missing + // + final RevCommit revcommit; + final ObjectWalk rw = new ObjectWalk(repo); + try { + try { + revcommit = rw.parseCommit(revid); + rw.markStart(revcommit); + } catch (IncorrectObjectTypeException err) { + throw new Failure(new InvalidRevisionException()); + } + for (final Ref r : repo.getAllRefs().values()) { + try { + rw.markUninteresting(rw.parseAny(r.getObjectId())); + } catch (MissingObjectException err) { + continue; + } + } + rw.checkConnectivity(); + } catch (IncorrectObjectTypeException err) { + throw new Failure(new InvalidRevisionException()); + } catch (MissingObjectException err) { + throw new Failure(new InvalidRevisionException()); + } catch (IOException err) { + log.error("Repository " + repoName + " possibly corrupt", err); + throw new Failure(new InvalidRevisionException()); + } + + final Branch.NameKey name = + new Branch.NameKey(pce.getProject().getNameKey(), refname); + try { + final RefUpdate u = repo.updateRef(refname); + u.setExpectedOldObjectId(ObjectId.zeroId()); + u.setNewObjectId(revid); + u.setRefLogIdent(ChangeUtil.toPersonIdent(me)); + u.setRefLogMessage("created via web", true); + final RefUpdate.Result result = u.update(rw); + switch (result) { + case FAST_FORWARD: + case NEW: + case NO_CHANGE: + break; + default: + log.error("Cannot create branch " + name + ": " + result.name()); + throw new Failure(new IOException(result.name())); + } + } catch (IOException err) { + log.error("Cannot create branch " + name, err); + throw new Failure(err); + } + + final Branch.Id id = new Branch.Id(db.nextBranchId()); + final Branch newBranch = new Branch(name, id); + db.branches().insert(Collections.singleton(newBranch)); + return db.branches().byProject(pce.getProject().getNameKey()).toList(); + } + }); + } + private void assertAmProjectOwner(final ReviewDb db, final Project.Id projectId) throws Failure { final ProjectCache.Entry p = Common.getProjectCache().get(projectId); |