Support inheriting project submit type

Change Project to only expose the configured submit type for that
project, and modify all callers to call the equivalent method on
ProjectState which respects inheritance.

In the extension/REST API, expose the inherited submit type analogously
to the inherited boolean values, using an object containing configured
and inherited values. For backwards compatibility, leave the old
submit_type field as-is, but mark it deprecated, in the hopes that we
can eventually replace it.

Do not change the default of a project with no configured submit type,
leaving the default as MERGE_IF_NECESSARY. This avoids, for now, the
need for migrating existing projects that do not have a submit type set,
including those that were created outside of Gerrit and don't have
refs/meta/config at all.

After this change, the global `repository.<name>.defaultSubmitType`
configuration still takes effect when creating a new project. A later
change may remove support for defaultSubmitType, but the migration to
convert global config into appropriate inheritable config values is
nontrivial, so we're punting on it.

Change-Id: Ib5711baaa67b2c92239a1ff4564349a80d211c28
This commit is contained in:
Dave Borowitz 2017-10-23 11:18:23 -04:00
parent 03e51746e2
commit c8f8d2ea83
27 changed files with 317 additions and 29 deletions

View File

@ -3748,7 +3748,7 @@ Path must be absolute.
[[repository.name.defaultSubmitType]]repository.<name>.defaultSubmitType::
+
The default submit type for newly created projects. Supported values
are `MERGE_IF_NECESSARY`, `FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`,
are `INHERIT`, `MERGE_IF_NECESSARY`, `FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`,
`REBASE_ALWAYS`, `MERGE_ALWAYS` and `CHERRY_PICK`.
+
For more details see link:project-configuration.html#submit_type[Submit Types].

View File

@ -57,6 +57,12 @@ modified by any project owner through the project console, `Projects` >
its dependencies are also submitted, with exceptions documented below.
The following submit types are supported:
[[submit_type_inherit]]
* Inherit
+
Inherit the submit type from the parent project. In `All-Projects`, this
is equivalent to link:#merge_if_necessary[Merge If Necessary].
[[fast_forward_only]]
* Fast Forward Only
+

View File

@ -494,7 +494,7 @@ link:#project-input[ProjectInput].
{
"description": "This is a demo project.",
"submit_type": "CHERRY_PICK",
"submit_type": "INHERIT",
"owners": [
"MyProject-Owners"
]
@ -821,7 +821,12 @@ read access to `refs/meta/config`.
"configured_value": "15m",
"inherited_value": "20m"
},
"submit_type": "MERGE_IF_NECESSARY",
"submit_type": "INHERIT",
"default_submit_type": {
"value": "MERGE_IF_NECESSARY",
"configured_value": "INHERIT",
"inherited_value": "MERGE_IF_NECESSARY"
},
"state": "ACTIVE",
"commentlinks": {},
"plugin_config": {
@ -933,6 +938,11 @@ ConfigInfo] entity.
"inherited_value": "20m"
},
"submit_type": "REBASE_IF_NECESSARY",
"default_submit_type": {
"value": "REBASE_IF_NECESSARY",
"configured_value": "INHERIT",
"inherited_value": "REBASE_IF_NECESSARY"
},
"state": "ACTIVE",
"commentlinks": {}
}
@ -2846,10 +2856,12 @@ all new changes are set as private by default.
The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
limit] of this project as a link:#max-object-size-limit-info[
MaxObjectSizeLimitInfo] entity.
|`default_submit_type` ||
link:#submit-type-info[SubmitTypeInfo] that describes the default submit type of
the project, when not overridden at the change level.
|`submit_type` ||
The default submit type of the project, can be `MERGE_IF_NECESSARY`,
`FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`, `REBASE_ALWAYS`, `MERGE_ALWAYS` or
`CHERRY_PICK`.
Deprecated; equivalent to link:#submit-type-info[`value`] in
`default_submit_type`.
|`match_author_to_committer_date` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that indicates whether
a change's author date will be changed to match its submitter date upon submit.
@ -3317,6 +3329,27 @@ statistics of a Git repository.
|`size_of_packed_objects` |Size of packed objects in bytes.
|======================================
[[submit-type-info]]
=== SubmitTypeInfo
Information about the link:project-configuration.html#submit_type[default submit
type of a project], taking into account project inheritance.
Valid values for each field are `MERGE_IF_NECESSARY`, `FAST_FORWARD_ONLY`,
`REBASE_IF_NECESSARY`, `REBASE_ALWAYS`, `MERGE_ALWAYS` or `CHERRY_PICK`, plus
`INHERIT` where applicable.
[options="header",cols="1,6"]
|===============================
|Field Name |Description
|`value` |
The effective submit type value. Never `INHERIT`.
|`configured_value` |
The configured value, can be one of the submit types, or `INHERIT` to inherit
from the parent project.
|`inherited_value` |
The effective value that would be inherited from the parent. Never `INHERIT`.
|===============================
[[tag-info]]
=== TagInfo
The `TagInfo` entity contains information about a tag.

View File

@ -133,6 +133,8 @@ public interface AdminConstants extends Constants {
String headingProjectSubmitType();
String projectSubmitType_INHERIT();
String projectSubmitType_FAST_FORWARD_ONLY();
String projectSubmitType_MERGE_ALWAYS();

View File

@ -57,6 +57,7 @@ headingAgreements = Contributor Agreements
headingAuditLog = Audit Log
headingProjectSubmitType = Submit Type
projectSubmitType_INHERIT = Inherit
projectSubmitType_FAST_FORWARD_ONLY = Fast Forward Only
projectSubmitType_MERGE_IF_NECESSARY = Merge if Necessary
projectSubmitType_REBASE_ALWAYS = Rebase Always

View File

@ -30,6 +30,7 @@ import com.google.gerrit.client.projects.ConfigInfo;
import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterInfo;
import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
import com.google.gerrit.client.projects.ConfigInfo.InheritedBooleanInfo;
import com.google.gerrit.client.projects.ConfigInfo.SubmitTypeInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.GerritCallback;
@ -335,13 +336,15 @@ public class ProjectInfoScreen extends ProjectScreen {
grid.addHtml(AdminConstants.I.useSignedOffBy(), signedOffBy);
}
private void setSubmitType(SubmitType newSubmitType) {
private void setSubmitType(SubmitTypeInfo newSubmitType) {
int index = -1;
if (submitType != null) {
if (newSubmitType != null) {
for (int i = 0; i < submitType.getItemCount(); i++) {
if (newSubmitType.name().equals(submitType.getValue(i))) {
if (submitType.getValue(i).equals(SubmitType.INHERIT.name())) {
submitType.setItemText(i, getInheritString(newSubmitType));
}
if (newSubmitType.configuredValue().name().equals(submitType.getValue(i))) {
index = i;
break;
}
}
submitType.setSelectedIndex(index);
@ -349,6 +352,13 @@ public class ProjectInfoScreen extends ProjectScreen {
}
}
private static String getInheritString(SubmitTypeInfo submitType) {
return Util.toLongString(SubmitType.INHERIT)
+ " ("
+ Util.toLongString(submitType.inheritedValue())
+ ")";
}
private void setState(ProjectState newState) {
if (state != null) {
for (int i = 0; i < state.getItemCount(); i++) {
@ -419,7 +429,7 @@ public class ProjectInfoScreen extends ProjectScreen {
setBool(privateByDefault, result.privateByDefault());
setBool(enableReviewerByEmail, result.enableReviewerByEmail());
setBool(matchAuthorToCommitterDate, result.matchAuthorToCommitterDate());
setSubmitType(result.submitType());
setSubmitType(result.defaultSubmitType());
setState(result.state());
maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
if (result.maxObjectSizeLimit().inheritedValue() != null) {

View File

@ -35,6 +35,8 @@ public class Util {
return "";
}
switch (type) {
case INHERIT:
return AdminConstants.I.projectSubmitType_INHERIT();
case FAST_FORWARD_ONLY:
return AdminConstants.I.projectSubmitType_FAST_FORWARD_ONLY();
case MERGE_IF_NECESSARY:

View File

@ -70,6 +70,8 @@ public class ConfigInfo extends JavaScriptObject {
return SubmitType.valueOf(submitTypeRaw());
}
public final native SubmitTypeInfo defaultSubmitType() /*-{ return this.default_submit_type; }-*/;
public final native NativeMap<NativeMap<ConfigParameterInfo>> pluginConfig()
/*-{ return this.plugin_config || {}; }-*/ ;
@ -232,4 +234,26 @@ public class ConfigInfo extends JavaScriptObject {
protected ConfigParameterValue() {}
}
public static class SubmitTypeInfo extends JavaScriptObject {
public final SubmitType value() {
return SubmitType.valueOf(valueRaw());
}
public final SubmitType configuredValue() {
return SubmitType.valueOf(configuredValueRaw());
}
public final SubmitType inheritedValue() {
return SubmitType.valueOf(inheritedValueRaw());
}
private final native String valueRaw() /*-{ return this.value; }-*/;
private final native String configuredValueRaw() /*-{ return this.configured_value; }-*/;
private final native String inheritedValueRaw() /*-{ return this.inherited_value; }-*/;
protected SubmitTypeInfo() {}
}
}

View File

@ -176,7 +176,9 @@ public class ProjectApi {
in.setRejectImplicitMerges(rejectImplicitMerges);
in.setPrivateByDefault(privateByDefault);
in.setMaxObjectSizeLimit(maxObjectSizeLimit);
in.setSubmitType(submitType);
if (submitType != null) {
in.setSubmitType(submitType);
}
in.setState(state);
in.setPluginConfigValues(pluginConfigValues);
in.setEnableReviewerByEmail(enableReviewerByEmail);

View File

@ -48,6 +48,9 @@ public class SubmitTypeRecord {
public final String errorMessage;
private SubmitTypeRecord(Status status, SubmitType type, String errorMessage) {
if (type == SubmitType.INHERIT) {
throw new IllegalArgumentException("Cannot output submit type " + type);
}
this.status = status;
this.type = type;
this.errorMessage = errorMessage;

View File

@ -37,7 +37,9 @@ public class ConfigInfo {
public InheritedBooleanInfo matchAuthorToCommitterDate;
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
@Deprecated // Equivalent to defaultSubmitType.value
public SubmitType submitType;
public SubmitTypeInfo defaultSubmitType;
public ProjectState state;
public Map<String, Map<String, ConfigParameterInfo>> pluginConfig;
public Map<String, ActionInfo> actions;
@ -72,4 +74,10 @@ public class ConfigInfo {
public List<String> permittedValues;
public List<String> values;
}
public static class SubmitTypeInfo {
public SubmitType value;
public SubmitType configuredValue;
public SubmitType inheritedValue;
}
}

View File

@ -15,10 +15,11 @@
package com.google.gerrit.extensions.client;
public enum SubmitType {
INHERIT,
FAST_FORWARD_ONLY,
MERGE_IF_NECESSARY,
REBASE_IF_NECESSARY,
REBASE_ALWAYS,
MERGE_ALWAYS,
CHERRY_PICK
CHERRY_PICK;
}

View File

@ -25,6 +25,12 @@ import java.util.Map;
/** Projects match a source code repository managed by Gerrit */
public final class Project {
/** Default submit type for new projects. */
public static final SubmitType DEFAULT_SUBMIT_TYPE = SubmitType.MERGE_IF_NECESSARY;
/** Default submit type for root project (All-Projects). */
public static final SubmitType DEFAULT_ALL_PROJECTS_SUBMIT_TYPE = SubmitType.MERGE_IF_NECESSARY;
/** Project name key */
public static class NameKey extends StringKey<com.google.gwtorm.client.Key<?>> {
private static final long serialVersionUID = 1L;
@ -137,7 +143,14 @@ public final class Project {
maxObjectSizeLimit = limit;
}
public SubmitType getSubmitType() {
/**
* Submit type as configured in {@code project.config}.
*
* <p>Does not take inheritance into account, i.e. may return {@link SubmitType#INHERIT}.
*
* @return submit type.
*/
public SubmitType getConfiguredSubmitType() {
return submitType;
}

View File

@ -14,6 +14,7 @@
package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
@ -59,6 +60,7 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
public static final ImmutableBiMap<SubmitType, Character> SUBMIT_TYPES =
new ImmutableBiMap.Builder<SubmitType, Character>()
.put(SubmitType.INHERIT, 'I')
.put(SubmitType.FAST_FORWARD_ONLY, 'F')
.put(SubmitType.MERGE_IF_NECESSARY, 'M')
.put(SubmitType.REBASE_ALWAYS, 'P')
@ -98,6 +100,11 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
private String mergeStrategy;
public EntryKey(ObjectId commit, ObjectId into, SubmitType submitType, String mergeStrategy) {
checkArgument(
submitType != SubmitType.INHERIT,
"Cannot cache %s.%s",
SubmitType.class.getSimpleName(),
submitType);
this.commit = checkNotNull(commit, "commit");
this.into = checkNotNull(into, "into");
this.submitType = checkNotNull(submitType, "submitType");

View File

@ -16,6 +16,7 @@ package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.common.data.Permission.isPermission;
import static com.google.gerrit.reviewdb.client.Project.DEFAULT_SUBMIT_TYPE;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
@ -42,7 +43,6 @@ import com.google.gerrit.common.data.SubscribeSection;
import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.reviewdb.client.Branch;
@ -151,7 +151,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final String PLUGIN = "plugin";
private static final SubmitType DEFAULT_SUBMIT_ACTION = SubmitType.MERGE_IF_NECESSARY;
private static final ProjectState DEFAULT_STATE_VALUE = ProjectState.ACTIVE;
private static final String EXTENSION_PANELS = "extension-panels";
@ -519,7 +518,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
p.setMaxObjectSizeLimit(rc.getString(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, DEFAULT_SUBMIT_ACTION));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, DEFAULT_SUBMIT_TYPE));
p.setState(getEnum(rc, PROJECT, null, KEY_STATE, DEFAULT_STATE_VALUE));
p.setDefaultDashboard(rc.getString(DASHBOARD, null, KEY_DEFAULT));
@ -1043,7 +1042,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
KEY_MAX_OBJECT_SIZE_LIMIT,
validMaxObjectSizeLimit(p.getMaxObjectSizeLimit()));
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), DEFAULT_SUBMIT_ACTION);
set(rc, SUBMIT, null, KEY_ACTION, p.getConfiguredSubmitType(), DEFAULT_SUBMIT_TYPE);
set(rc, PROJECT, null, KEY_STATE, p.getState(), DEFAULT_STATE_VALUE);

View File

@ -133,6 +133,7 @@ public class SubmitDryRun {
return RebaseIfNecessary.dryRun(args, repo, tipCommit, toMergeCommit);
case REBASE_ALWAYS:
return RebaseAlways.dryRun(args, repo, tipCommit, toMergeCommit);
case INHERIT:
default:
String errorMsg = "No submit strategy for: " + submitType;
log.error(errorMsg);

View File

@ -96,6 +96,7 @@ public class SubmitStrategyFactory {
return new RebaseIfNecessary(args);
case REBASE_ALWAYS:
return new RebaseAlways(args);
case INHERIT:
default:
String errorMsg = "No submit strategy for: " + submitType;
log.error(errorMsg);

View File

@ -465,6 +465,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
case REBASE_IF_NECESSARY:
case REBASE_ALWAYS:
return message(ctx, commit, CommitMergeStatus.CLEAN_REBASE);
case INHERIT:
default:
throw new IllegalStateException(
"unexpected submit type "

View File

@ -31,6 +31,7 @@ import com.google.gerrit.common.data.RefConfigSection;
import com.google.gerrit.common.data.SubscribeSection;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.api.projects.ThemeInfo;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.reviewdb.client.Branch;
@ -490,6 +491,16 @@ public class ProjectState {
return getGroups(getLocalAccessSections());
}
public SubmitType getSubmitType() {
for (ProjectState s : tree()) {
SubmitType t = s.getProject().getConfiguredSubmitType();
if (t != SubmitType.INHERIT) {
return t;
}
}
return Project.DEFAULT_ALL_PROJECTS_SUBMIT_TYPE;
}
private static Set<GroupReference> getGroups(List<SectionMatcher> sectionMatcherList) {
final Set<GroupReference> all = new HashSet<>();
for (SectionMatcher matcher : sectionMatcherList) {

View File

@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
@ -42,6 +43,7 @@ import java.util.Map;
import java.util.TreeMap;
public class ConfigInfoImpl extends ConfigInfo {
@SuppressWarnings("deprecation")
public ConfigInfoImpl(
boolean serverEnableSignedPush,
ProjectState projectState,
@ -79,7 +81,18 @@ public class ConfigInfoImpl extends ConfigInfo {
maxObjectSizeLimit.inheritedValue = config.getFormattedMaxObjectSizeLimit();
this.maxObjectSizeLimit = maxObjectSizeLimit;
this.submitType = p.getSubmitType();
this.defaultSubmitType = new SubmitTypeInfo();
this.defaultSubmitType.value = projectState.getSubmitType();
this.defaultSubmitType.configuredValue =
MoreObjects.firstNonNull(
projectState.getConfig().getProject().getConfiguredSubmitType(),
Project.DEFAULT_SUBMIT_TYPE);
ProjectState parent =
projectState.isAllProjects() ? projectState : projectState.parents().get(0);
this.defaultSubmitType.inheritedValue = parent.getSubmitType();
this.submitType = this.defaultSubmitType.value;
this.state =
p.getState() != com.google.gerrit.extensions.client.ProjectState.ACTIVE
? p.getState()

View File

@ -47,7 +47,7 @@ public class PRED_project_default_submit_type_1 extends Predicate.P1 {
Term a1 = arg1.dereference();
ProjectState projectState = StoredValues.PROJECT_STATE.get(engine);
SubmitType submitType = projectState.getProject().getSubmitType();
SubmitType submitType = projectState.getSubmitType();
if (!a1.unify(term[submitType.ordinal()], engine.trail)) {
return engine.fail();
}

View File

@ -219,13 +219,13 @@ public class ProjectIT extends AbstractDaemonTest {
RevCommit initialHead = getRemoteHead(project, RefNames.REFS_CONFIG);
ConfigInfo info = gApi.projects().name(project.get()).config();
assertThat(info.submitType).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(info.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
ConfigInput input = new ConfigInput();
input.submitType = SubmitType.CHERRY_PICK;
info = gApi.projects().name(project.get()).config(input);
assertThat(info.submitType).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(info.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
info = gApi.projects().name(project.get()).config();
assertThat(info.submitType).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(info.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
RevCommit updatedHead = getRemoteHead(project, RefNames.REFS_CONFIG);
eventRecorder.assertRefUpdatedEvents(
@ -233,6 +233,7 @@ public class ProjectIT extends AbstractDaemonTest {
}
@Test
@SuppressWarnings("deprecation")
public void setConfig() throws Exception {
ConfigInput input = createTestConfigInput();
ConfigInfo info = gApi.projects().name(project.get()).config(input);
@ -250,9 +251,13 @@ public class ProjectIT extends AbstractDaemonTest {
.isEqualTo(input.createNewChangeForAllNotInTarget);
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo(input.maxObjectSizeLimit);
assertThat(info.submitType).isEqualTo(input.submitType);
assertThat(info.defaultSubmitType.value).isEqualTo(input.submitType);
assertThat(info.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(info.defaultSubmitType.configuredValue).isEqualTo(input.submitType);
assertThat(info.state).isEqualTo(input.state);
}
@SuppressWarnings("deprecation")
@Test
public void setPartialConfig() throws Exception {
ConfigInput input = createTestConfigInput();
@ -276,6 +281,9 @@ public class ProjectIT extends AbstractDaemonTest {
.isEqualTo(input.createNewChangeForAllNotInTarget);
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo(input.maxObjectSizeLimit);
assertThat(info.submitType).isEqualTo(input.submitType);
assertThat(info.defaultSubmitType.value).isEqualTo(input.submitType);
assertThat(info.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(info.defaultSubmitType.configuredValue).isEqualTo(input.submitType);
assertThat(info.state).isEqualTo(input.state);
}

View File

@ -236,6 +236,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
break;
case MERGE_ALWAYS:
case MERGE_IF_NECESSARY:
case INHERIT:
assertThat(e.getMessage())
.isEqualTo(
"Failed to submit 3 changes due to the following problems:\n"

View File

@ -24,9 +24,12 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
@ -181,7 +184,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
Project project = projectCache.get(new Project.NameKey(newProjectName)).getProject();
assertProjectInfo(project, p);
assertThat(project.getDescription()).isEqualTo(in.description);
assertThat(project.getSubmitType()).isEqualTo(in.submitType);
assertThat(project.getConfiguredSubmitType()).isEqualTo(in.submitType);
assertThat(project.getBooleanConfig(BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS))
.isEqualTo(in.useContributorAgreements);
assertThat(project.getBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY))
@ -331,6 +334,84 @@ public class CreateProjectIT extends AbstractDaemonTest {
}
}
@SuppressWarnings("deprecation")
@Test
public void createProjectWithDefaultInheritedSubmitType() throws Exception {
String parent = name("parent");
ProjectInput pin = new ProjectInput();
pin.name = parent;
ConfigInfo cfg = gApi.projects().create(pin).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
ConfigInput cin = new ConfigInput();
cin.submitType = SubmitType.CHERRY_PICK;
gApi.projects().name(parent).config(cin);
cfg = gApi.projects().name(parent).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
String child = name("child");
pin = new ProjectInput();
pin.submitType = SubmitType.INHERIT;
pin.parent = parent;
pin.name = child;
cfg = gApi.projects().create(pin).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.INHERIT);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.CHERRY_PICK);
cin = new ConfigInput();
cin.submitType = SubmitType.REBASE_IF_NECESSARY;
gApi.projects().name(parent).config(cin);
cfg = gApi.projects().name(parent).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
cfg = gApi.projects().name(child).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.INHERIT);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.REBASE_IF_NECESSARY);
}
@SuppressWarnings("deprecation")
@Test
@GerritConfig(
name = "repository.testinheritedsubmittype/*.defaultSubmitType",
value = "CHERRY_PICK"
)
public void repositoryConfigTakesPrecedenceOverInheritedSubmitType() throws Exception {
// Can't use name() since we need to specify this project name in gerrit.config prior to
// startup. Pick something reasonably unique instead.
String parent = "testinheritedsubmittype";
ProjectInput pin = new ProjectInput();
pin.name = parent;
pin.submitType = SubmitType.MERGE_ALWAYS;
ConfigInfo cfg = gApi.projects().create(pin).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.MERGE_ALWAYS);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_ALWAYS);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.MERGE_ALWAYS);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
String child = parent + "/child";
pin = new ProjectInput();
pin.parent = parent;
pin.name = child;
cfg = gApi.projects().create(pin).config();
assertThat(cfg.submitType).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.configuredValue).isEqualTo(SubmitType.CHERRY_PICK);
assertThat(cfg.defaultSubmitType.inheritedValue).isEqualTo(SubmitType.MERGE_ALWAYS);
}
private void assertHead(String projectName, String expectedRef) throws Exception {
try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName))) {
assertThat(repo.exactRef(Constants.HEAD).getTarget().getName()).isEqualTo(expectedRef);

View File

@ -103,7 +103,8 @@ limitations under the License.
id="submitTypeSelect"
bind-value="{{_repoConfig.submit_type}}">
<select disabled$="[[_readOnly]]">
<template is="dom-repeat" items="[[_submitTypes]]">
<template is="dom-repeat"
items="[[_formatSubmitTypeSelect(_repoConfig)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>

View File

@ -21,6 +21,7 @@
};
const SUBMIT_TYPES = {
// Exclude INHERIT, which is handled specially.
mergeIfNecessary: {
value: 'MERGE_IF_NECESSARY',
label: 'Merge if necessary',
@ -129,6 +130,15 @@
promises.push(this.$.restAPI.getProjectConfig(this.repo).then(
config => {
if (config.default_submit_type) {
// The gr-select is bound to submit_type, which needs to be the
// *configured* submit type. When default_submit_type is
// present, the server reports the *effective* submit type in
// submit_type, so we need to overwrite it before storing the
// config in this.
config.submit_type =
config.default_submit_type.configured_value;
}
if (!config.state) {
config.state = STATES.active.value;
}
@ -183,6 +193,36 @@
];
},
_formatSubmitTypeSelect(projectConfig) {
if (!projectConfig) { return; }
const allValues = Object.values(SUBMIT_TYPES);
const type = projectConfig.default_submit_type;
if (!type) {
// Server is too old to report default_submit_type, so assume INHERIT
// is not a valid value.
return allValues;
}
let inheritLabel = 'Inherit';
if (type.inherited_value) {
let inherited = type.inherited_value;
for (const val of allValues) {
if (val.value === type.inherited_value) {
inherited = val.label;
break;
}
}
inheritLabel = `Inherit (${inherited})`;
}
return [
{
label: inheritLabel,
value: 'INHERIT',
},
...allValues,
];
},
_isLoading() {
return this._loading || this._loading === undefined;
},
@ -195,6 +235,12 @@
const configInputObj = {};
for (const key in p) {
if (p.hasOwnProperty(key)) {
if (key === 'default_submit_type') {
// default_submit_type is not in the input type, and the
// configured value was already copied to submit_type by
// _loadProject. Omit this property when saving.
continue;
}
if (typeof p[key] === 'object') {
configInputObj[key] = p[key].configured_value;
} else {

View File

@ -99,6 +99,11 @@ limitations under the License.
},
max_object_size_limit: {},
submit_type: 'MERGE_IF_NECESSARY',
default_submit_type: {
value: 'MERGE_IF_NECESSARY',
configured_value: 'INHERIT',
inherited_value: 'MERGE_IF_NECESSARY',
},
});
},
getConfig() {
@ -252,7 +257,16 @@ limitations under the License.
});
});
test('fields update and save correctly', done => {
test('inherited submit type value is calculated correctly', () => {
return element._loadRepo().then(() => {
const sel = element.$.submitTypeSelect;
assert.equal(sel.bindValue, 'INHERIT');
assert.equal(
sel.nativeSelect.options[0].text, 'Inherit (Merge if necessary)');
});
});
test('fields update and save correctly', () => {
// test notedb
element._noteDbEnabled = false;
@ -289,7 +303,7 @@ limitations under the License.
const button = Polymer.dom(element.root).querySelector('gr-button');
element._loadRepo().then(() => {
return element._loadRepo().then(() => {
assert.isTrue(button.hasAttribute('disabled'));
assert.isFalse(element.$.Title.classList.contains('edited'));
element.$.descriptionInput.bindValue = configInputObj.description;
@ -327,12 +341,11 @@ limitations under the License.
element._formatRepoConfigForSave(element._repoConfig);
assert.deepEqual(formattedObj, configInputObj);
element._handleSaveRepoConfig().then(() => {
return element._handleSaveRepoConfig().then(() => {
assert.isTrue(button.hasAttribute('disabled'));
assert.isFalse(element.$.Title.classList.contains('edited'));
assert.isTrue(saveStub.lastCall.calledWithExactly(REPO,
configInputObj));
done();
});
});
});