Support different project level merge policies
Replace the hidden "gerrit.fastforwardonly" config file option with a project level property stored in the database containing one of three different values: * FAST_FORWARD_ONLY: Never create a merge commit * MERGE_IF_NECESSARY: Standard/default behavior * MERGE_ALWAYS: Always create a merge commit Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -32,10 +32,15 @@ public interface AdminConstants extends Constants {
|
||||
|
||||
String headingOwner();
|
||||
String headingDescription();
|
||||
String headingSubmitType();
|
||||
String headingMembers();
|
||||
String headingCreateGroup();
|
||||
String headingAccessRights();
|
||||
|
||||
String projectSubmitType_FAST_FORWARD_ONLY();
|
||||
String projectSubmitType_MERGE_ALWAYS();
|
||||
String projectSubmitType_MERGE_IF_NECESSARY();
|
||||
|
||||
String columnMember();
|
||||
String columnEmailAddress();
|
||||
String columnGroupName();
|
||||
|
@@ -13,10 +13,15 @@ buttonAddProjectRight = Add Access Right
|
||||
|
||||
headingOwner = Owners
|
||||
headingDescription = Description
|
||||
headingSubmitType = Change Submit Action
|
||||
headingMembers = Members
|
||||
headingCreateGroup = Create New Group
|
||||
headingAccessRights = Access Rights
|
||||
|
||||
projectSubmitType_FAST_FORWARD_ONLY = Fast Forward Only
|
||||
projectSubmitType_MERGE_IF_NECESSARY = Merge If Necessary
|
||||
projectSubmitType_MERGE_ALWAYS = Always Merge
|
||||
|
||||
columnMember = Member
|
||||
columnEmailAddress = Email Address
|
||||
columnGroupName = Group Name
|
||||
|
@@ -41,6 +41,10 @@ public interface ProjectAdminService extends RemoteJsonService {
|
||||
void changeProjectOwner(Project.Id projectId, String newOwnerName,
|
||||
AsyncCallback<VoidResult> callback);
|
||||
|
||||
@SignInRequired
|
||||
void changeProjectSubmitType(Project.Id projectId,
|
||||
Project.SubmitType newSubmitType, AsyncCallback<VoidResult> callback);
|
||||
|
||||
@SignInRequired
|
||||
void deleteRight(Set<ProjectRight.Key> ids, AsyncCallback<VoidResult> callback);
|
||||
|
||||
|
@@ -22,9 +22,11 @@ import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
|
||||
import com.google.gerrit.client.ui.SmallHeading;
|
||||
import com.google.gerrit.client.ui.TextSaveButtonListener;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.ChangeListener;
|
||||
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.ListBox;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.SuggestBox;
|
||||
import com.google.gwt.user.client.ui.TextArea;
|
||||
@@ -41,6 +43,10 @@ public class ProjectInfoPanel extends Composite {
|
||||
private SuggestBox ownerTxt;
|
||||
private Button saveOwner;
|
||||
|
||||
private Panel submitTypePanel;
|
||||
private ListBox submitType;
|
||||
private Project.SubmitType currentSubmitType;
|
||||
|
||||
private TextArea descTxt;
|
||||
private Button saveDesc;
|
||||
|
||||
@@ -48,6 +54,7 @@ public class ProjectInfoPanel extends Composite {
|
||||
final FlowPanel body = new FlowPanel();
|
||||
initOwner(body);
|
||||
initDescription(body);
|
||||
initSubmitType(body);
|
||||
initWidget(body);
|
||||
|
||||
projectId = toShow;
|
||||
@@ -72,6 +79,7 @@ public class ProjectInfoPanel extends Composite {
|
||||
}
|
||||
|
||||
private void enableForm(final boolean on) {
|
||||
submitType.setEnabled(on);
|
||||
ownerTxtBox.setEnabled(on);
|
||||
descTxt.setEnabled(on);
|
||||
}
|
||||
@@ -132,6 +140,56 @@ public class ProjectInfoPanel extends Composite {
|
||||
new TextSaveButtonListener(descTxt, saveDesc);
|
||||
}
|
||||
|
||||
private void initSubmitType(final Panel body) {
|
||||
submitTypePanel = new VerticalPanel();
|
||||
submitTypePanel.add(new SmallHeading(Util.C.headingSubmitType()));
|
||||
|
||||
submitType = new ListBox();
|
||||
for (final Project.SubmitType type : Project.SubmitType.values()) {
|
||||
submitType.addItem(Util.toLongString(type), type.name());
|
||||
}
|
||||
submitType.addChangeListener(new ChangeListener() {
|
||||
public void onChange(Widget sender) {
|
||||
final int i = submitType.getSelectedIndex();
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
final Project.SubmitType newSubmitType =
|
||||
Project.SubmitType.valueOf(submitType.getValue(i));
|
||||
submitType.setEnabled(false);
|
||||
Util.PROJECT_SVC.changeProjectSubmitType(projectId, newSubmitType,
|
||||
new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
currentSubmitType = newSubmitType;
|
||||
submitType.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable caught) {
|
||||
submitType.setEnabled(false);
|
||||
setSubmitType(currentSubmitType);
|
||||
super.onFailure(caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
submitTypePanel.add(submitType);
|
||||
body.add(submitTypePanel);
|
||||
}
|
||||
|
||||
private void setSubmitType(final Project.SubmitType newSubmitType) {
|
||||
currentSubmitType = newSubmitType;
|
||||
if (submitType != null) {
|
||||
for (int i = 0; i < submitType.getItemCount(); i++) {
|
||||
if (newSubmitType.name().equals(submitType.getValue(i))) {
|
||||
submitType.setSelectedIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
submitType.setSelectedIndex(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void display(final ProjectDetail result) {
|
||||
final Project project = result.project;
|
||||
final AccountGroup owner = result.groups.get(project.getOwnerGroupId());
|
||||
@@ -143,10 +201,13 @@ public class ProjectInfoPanel extends Composite {
|
||||
|
||||
if (ProjectRight.WILD_PROJECT.equals(project.getId())) {
|
||||
ownerPanel.setVisible(false);
|
||||
submitTypePanel.setVisible(false);
|
||||
} else {
|
||||
ownerPanel.setVisible(true);
|
||||
submitTypePanel.setVisible(true);
|
||||
}
|
||||
|
||||
descTxt.setText(project.getDescription());
|
||||
setSubmitType(project.getSubmitType());
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwtjsonrpc.client.JsonUtil;
|
||||
|
||||
@@ -30,4 +31,20 @@ public class Util {
|
||||
PROJECT_SVC = GWT.create(ProjectAdminService.class);
|
||||
JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
|
||||
}
|
||||
|
||||
public static String toLongString(final Project.SubmitType type) {
|
||||
if (type == null) {
|
||||
return "";
|
||||
}
|
||||
switch (type) {
|
||||
case FAST_FORWARD_ONLY:
|
||||
return C.projectSubmitType_FAST_FORWARD_ONLY();
|
||||
case MERGE_IF_NECESSARY:
|
||||
return C.projectSubmitType_MERGE_IF_NECESSARY();
|
||||
case MERGE_ALWAYS:
|
||||
return C.projectSubmitType_MERGE_ALWAYS();
|
||||
default:
|
||||
return type.name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -81,6 +81,33 @@ public final class Project {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum SubmitType {
|
||||
FAST_FORWARD_ONLY('F'),
|
||||
|
||||
MERGE_IF_NECESSARY('M'),
|
||||
|
||||
MERGE_ALWAYS('A');
|
||||
|
||||
private final char code;
|
||||
|
||||
private SubmitType(final char c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
public char getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static SubmitType forCode(final char c) {
|
||||
for (final SubmitType s : SubmitType.values()) {
|
||||
if (s.code == c) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Column
|
||||
protected NameKey name;
|
||||
|
||||
@@ -96,6 +123,9 @@ public final class Project {
|
||||
@Column
|
||||
protected boolean useContributorAgreements;
|
||||
|
||||
@Column
|
||||
protected char submitType;
|
||||
|
||||
protected Project() {
|
||||
}
|
||||
|
||||
@@ -103,6 +133,7 @@ public final class Project {
|
||||
name = newName;
|
||||
projectId = newId;
|
||||
useContributorAgreements = true;
|
||||
setSubmitType(SubmitType.MERGE_IF_NECESSARY);
|
||||
}
|
||||
|
||||
public Project.Id getId() {
|
||||
@@ -140,4 +171,12 @@ public final class Project {
|
||||
public void setUseContributorAgreements(final boolean u) {
|
||||
useContributorAgreements = u;
|
||||
}
|
||||
|
||||
public SubmitType getSubmitType() {
|
||||
return SubmitType.forCode(submitType);
|
||||
}
|
||||
|
||||
public void setSubmitType(final SubmitType type) {
|
||||
submitType = type.getCode();
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ import com.google.gwtorm.client.Sequence;
|
||||
|
||||
/** The review service database schema. */
|
||||
public interface ReviewDb extends Schema {
|
||||
public static final int VERSION = 8;
|
||||
public static final int VERSION = 9;
|
||||
|
||||
@Relation
|
||||
SchemaVersionAccess schemaVersion();
|
||||
|
@@ -15,12 +15,14 @@
|
||||
package com.google.gerrit.git;
|
||||
|
||||
import com.google.gerrit.client.data.ApprovalType;
|
||||
import com.google.gerrit.client.data.ProjectCache;
|
||||
import com.google.gerrit.client.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.client.reviewdb.Branch;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.ChangeApproval;
|
||||
import com.google.gerrit.client.reviewdb.ChangeMessage;
|
||||
import com.google.gerrit.client.reviewdb.PatchSet;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.Common;
|
||||
import com.google.gerrit.client.workflow.FunctionState;
|
||||
@@ -84,6 +86,7 @@ public class MergeOp {
|
||||
private final GerritServer server;
|
||||
private final PersonIdent myIdent;
|
||||
private final Branch.NameKey destBranch;
|
||||
private Project destProject;
|
||||
private final List<CodeReviewCommit> toMerge;
|
||||
private List<Change> submitted;
|
||||
private final Map<Change.Id, CommitMergeStatus> status;
|
||||
@@ -103,6 +106,13 @@ public class MergeOp {
|
||||
}
|
||||
|
||||
public void merge() throws MergeException {
|
||||
final ProjectCache.Entry pe =
|
||||
Common.getProjectCache().get(destBranch.getParentKey());
|
||||
if (pe == null) {
|
||||
throw new MergeException("No such project: " + destBranch.getParentKey());
|
||||
}
|
||||
destProject = pe.getProject();
|
||||
|
||||
try {
|
||||
schema = Common.getSchemaFactory().open();
|
||||
} catch (OrmException e) {
|
||||
@@ -274,23 +284,24 @@ public class MergeOp {
|
||||
|
||||
// Take the first fast-forward available, if any is available in the set.
|
||||
//
|
||||
for (final Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext();) {
|
||||
try {
|
||||
final CodeReviewCommit n = i.next();
|
||||
if (mergeTip == null || rw.isMergedInto(mergeTip, n)) {
|
||||
mergeTip = n;
|
||||
i.remove();
|
||||
break;
|
||||
if (destProject.getSubmitType() != Project.SubmitType.MERGE_ALWAYS) {
|
||||
for (final Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext();) {
|
||||
try {
|
||||
final CodeReviewCommit n = i.next();
|
||||
if (mergeTip == null || rw.isMergedInto(mergeTip, n)) {
|
||||
mergeTip = n;
|
||||
i.remove();
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MergeException("Cannot fast-forward test during merge", e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MergeException("Cannot fast-forward test during merge", e);
|
||||
}
|
||||
}
|
||||
|
||||
// If this project only permits fast-forwards, abort everything else.
|
||||
//
|
||||
if ("true".equals(db.getConfig().getString("gerrit", null,
|
||||
"fastforwardonly"))) {
|
||||
if (destProject.getSubmitType() == Project.SubmitType.FAST_FORWARD_ONLY) {
|
||||
while (!toMerge.isEmpty()) {
|
||||
final CodeReviewCommit n = toMerge.remove(0);
|
||||
n.statusCode = CommitMergeStatus.PATH_CONFLICT;
|
||||
|
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2009 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.pgm;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.Common;
|
||||
import com.google.gerrit.git.InvalidRepositoryException;
|
||||
import com.google.gerrit.git.WorkQueue;
|
||||
import com.google.gerrit.server.GerritServer;
|
||||
import com.google.gwtjsonrpc.server.XsrfException;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
|
||||
import org.spearce.jgit.lib.ProgressMonitor;
|
||||
import org.spearce.jgit.lib.Repository;
|
||||
import org.spearce.jgit.lib.TextProgressMonitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Update project's submit_type field from their git config files. */
|
||||
public class ImportProjectSubmitTypes {
|
||||
private static final String GERRIT = "gerrit";
|
||||
private static final String FFO = "fastforwardonly";
|
||||
|
||||
public static void main(final String[] argv) throws OrmException,
|
||||
XsrfException {
|
||||
try {
|
||||
mainImpl(argv);
|
||||
} finally {
|
||||
WorkQueue.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
private static void mainImpl(final String[] argv) throws OrmException,
|
||||
XsrfException {
|
||||
final ProgressMonitor pm = new TextProgressMonitor();
|
||||
final GerritServer gs = GerritServer.getInstance();
|
||||
final ReviewDb db = Common.getSchemaFactory().open();
|
||||
try {
|
||||
final List<Project> all = db.projects().all().toList();
|
||||
pm.start(1);
|
||||
pm.beginTask("Update projects", all.size());
|
||||
for (final Project p : all) {
|
||||
if (p.getSubmitType() != null
|
||||
&& p.getSubmitType() != Project.SubmitType.MERGE_IF_NECESSARY) {
|
||||
pm.update(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
final Repository r;
|
||||
try {
|
||||
r = gs.getRepositoryCache().get(p.getName());
|
||||
} catch (InvalidRepositoryException e) {
|
||||
pm.update(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("true".equals(r.getConfig().getString(GERRIT, null, FFO))) {
|
||||
p.setSubmitType(Project.SubmitType.FAST_FORWARD_ONLY);
|
||||
db.projects().update(Collections.singleton(p));
|
||||
r.getConfig().unsetString(GERRIT, null, FFO);
|
||||
try {
|
||||
r.getConfig().save();
|
||||
} catch (IOException e) {
|
||||
// Ignore a save error
|
||||
}
|
||||
}
|
||||
pm.update(1);
|
||||
}
|
||||
pm.endTask();
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -180,6 +180,24 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
});
|
||||
}
|
||||
|
||||
public void changeProjectSubmitType(final Project.Id projectId,
|
||||
final Project.SubmitType newSubmitType,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
assertAmProjectOwner(db, projectId);
|
||||
final Project project = db.projects().get(projectId);
|
||||
if (project == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
project.setSubmitType(newSubmitType);
|
||||
db.projects().update(Collections.singleton(project));
|
||||
Common.getProjectCache().invalidate(project);
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteRight(final Set<ProjectRight.Key> keys,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
|
9
src/main/webapp/WEB-INF/sql/upgrade008_009.sql
Normal file
9
src/main/webapp/WEB-INF/sql/upgrade008_009.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Upgrade: schema_version 8 to 9
|
||||
--
|
||||
|
||||
ALTER TABLE projects ADD submit_type CHAR(1);
|
||||
UPDATE projects SET submit_type = 'M'; -- MERGE_IF_NECESSARY
|
||||
ALTER TABLE projects ALTER COLUMN submit_type SET DEFAULT ' ';
|
||||
ALTER TABLE projects ALTER COLUMN submit_type SET NOT NULL;
|
||||
|
||||
UPDATE schema_version SET version_nbr = 9;
|
Reference in New Issue
Block a user