summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2009-03-27 16:53:32 -0700
committerShawn O. Pearce <sop@google.com>2009-03-27 16:53:32 -0700
commit97cd0caa391d09fb785d27a9cc09c4547e15aa3c (patch)
tree119cf31e44b80949b22a03b24d9b7c71906c0453
parenta1cfa14357641aae91e05c23e259b71eac7edfd6 (diff)
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>
-rw-r--r--Documentation/project-setup.txt36
-rw-r--r--src/main/java/com/google/gerrit/client/admin/AdminConstants.java5
-rw-r--r--src/main/java/com/google/gerrit/client/admin/AdminConstants.properties5
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java4
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java61
-rw-r--r--src/main/java/com/google/gerrit/client/admin/Util.java17
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Project.java39
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java2
-rw-r--r--src/main/java/com/google/gerrit/git/MergeOp.java33
-rw-r--r--src/main/java/com/google/gerrit/pgm/ImportProjectSubmitTypes.java89
-rw-r--r--src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java18
-rw-r--r--src/main/webapp/WEB-INF/sql/upgrade008_009.sql9
12 files changed, 306 insertions, 12 deletions
diff --git a/Documentation/project-setup.txt b/Documentation/project-setup.txt
index 3e7f5fa4c8..c71507e152 100644
--- a/Documentation/project-setup.txt
+++ b/Documentation/project-setup.txt
@@ -43,11 +43,13 @@ for a project named `project`.
INSERT INTO projects
(project_id
,use_contributor_agreements
+ ,submit_type
,owner_group_id
,name)
VALUES
(nextval('project_id')
,'Y'
+ ,'M'
,(SELECT admin_group_id FROM system_config)
,'new/project');
@@ -61,6 +63,40 @@ for a project named `project`.
,'new/project');
====
+Change Submit Action
+--------------------
+
+The method Gerrit uses to submit a change to a project can be
+modified by any project owner through the project console, `Admin` >
+`Projects`. The following methods are supported:
+
+* Fast Forward Only
++
+This method produces a strictly linear history. All merges must
+be handled on the client, prior to uploading to Gerrit for review.
++
+To submit a change, the change must be a strict superset of the
+destination branch. That is, the change must already contain the
+tip of the destination branch at submit time.
+
+* Merge If Necessary
++
+This is the default for a new project (and why `\'M'` is suggested
+above in the insert statement).
++
+If the change being submitted is a strict superset of the destination
+branch, then the branch is fast-forwarded to the change. If not,
+then a merge commit is automatically created. This is identical
+to the classical `git merge` behavior, or `git merge \--ff`.
+
+* Always Merge
++
+Always produce a merge commit, even if the change is a strict
+superset of the destination branch. This is identical to the
+behavior of `git merge \--no-ff`, and may be useful if the
+project needs to follow submits with `git log \--first-parent`.
+
+
Registering Additional Branches
-------------------------------
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 d3b93df9f5..1feb05353e 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -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();
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 94bff9eb3c..16c57d7a2d 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -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
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 d92e066d22..f9a4898558 100644
--- a/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
+++ b/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
@@ -42,6 +42,10 @@ public interface ProjectAdminService extends RemoteJsonService {
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);
@SignInRequired
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
index e0c9f25283..35350d5028 100644
--- a/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
+++ b/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
@@ -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());
}
}
diff --git a/src/main/java/com/google/gerrit/client/admin/Util.java b/src/main/java/com/google/gerrit/client/admin/Util.java
index 9b87cf19df..40603d3f74 100644
--- a/src/main/java/com/google/gerrit/client/admin/Util.java
+++ b/src/main/java/com/google/gerrit/client/admin/Util.java
@@ -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();
+ }
+ }
}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Project.java b/src/main/java/com/google/gerrit/client/reviewdb/Project.java
index 4a9f0f2ea7..df97cfe0ed 100644
--- a/src/main/java/com/google/gerrit/client/reviewdb/Project.java
+++ b/src/main/java/com/google/gerrit/client/reviewdb/Project.java
@@ -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();
+ }
}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java b/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
index 1f2d9599c2..a9f853a2f0 100644
--- a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
+++ b/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
@@ -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();
diff --git a/src/main/java/com/google/gerrit/git/MergeOp.java b/src/main/java/com/google/gerrit/git/MergeOp.java
index ac5da117e8..e77e817d3b 100644
--- a/src/main/java/com/google/gerrit/git/MergeOp.java
+++ b/src/main/java/com/google/gerrit/git/MergeOp.java
@@ -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;
diff --git a/src/main/java/com/google/gerrit/pgm/ImportProjectSubmitTypes.java b/src/main/java/com/google/gerrit/pgm/ImportProjectSubmitTypes.java
new file mode 100644
index 0000000000..24371253ca
--- /dev/null
+++ b/src/main/java/com/google/gerrit/pgm/ImportProjectSubmitTypes.java
@@ -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();
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java b/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java
index 1cfc31a6dc..62e91c98d5 100644
--- a/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java
+++ b/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java
@@ -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>() {
diff --git a/src/main/webapp/WEB-INF/sql/upgrade008_009.sql b/src/main/webapp/WEB-INF/sql/upgrade008_009.sql
new file mode 100644
index 0000000000..562cf0a5f6
--- /dev/null
+++ b/src/main/webapp/WEB-INF/sql/upgrade008_009.sql
@@ -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;