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>
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user