Option to create a new change for every commit not in target

One not-so-well-known feature of gerrit is that, you can set a
%base for the RevWalk in ReceiveCommits when pushing changes to
Gerrit. This makes it possible to push a commit for review and
override Gerrits default algorithm for creating new changes on
push to refs/for/*. For instance, one could submit a commit in
a private branch and, afterward, push the same commit to a
release branch.

Specifying the tip of the destination branch as the %base would
have an effect of creating a new change for every commit reachable
from the pushed commit and not reachable from the target branch.
This behavior seems to be wanted by some Gerrit users and is
implemented as a project config option by this change (although
it contradicts the Gerrit's philosophy of reviewing one commit once).
If a %base is explicitly set on during uploading changes,
new-change-for-all-not-in-target is ignored.

To avoid incidental pushes with merges,
new-change-for-all-not-in-target rejects uploads if changes
contain merge commits. In such cases to push a merge commit, you
need to explicitly set the %base parameter as described in
Uploading Changes / Selecting Merge Base documentation.

Bug: issue 1195
Change-Id: Ifa90184352c912885e52d2060356fcc039d0ef03
This commit is contained in:
Deniz Türkoglu
2014-09-08 17:02:48 +02:00
parent 64eb1e2f21
commit 52777270d9
19 changed files with 191 additions and 27 deletions

View File

@@ -15,6 +15,7 @@ gerrit create-project - Create a new hosted project
[--use-contributor-agreements | --ca]
[--use-signed-off-by | --so]
[--use-content-merge]
[--create-new-change-for-all-not-in-target]
[--require-change-id | --id]
[[--branch <REF> | -b <REF>] ...]
[--empty-commit]
@@ -134,6 +135,15 @@ Submit Types].
from either the author or the uploader in the commit message.
Disabled by default.
--create-new-change-for-all-not-in-target::
--ncfa:
If enabled, a new change is created for every commit not in
target branch. If the pushed commit is merge commit, this flag is
ignored for that push. This option also does not accept merge
commits in commit chain to avoid accidental creation of a large
number of open changes.
Disabled by default.
--require-change-id::
--id::
Require a valid link:user-changeid.html[Change-Id] footer

View File

@@ -150,6 +150,25 @@ The project is hidden and only visible to project owners. Other users
are not able to see the project even if they have read permissions
granted on the project.
=== Use target branch when determining new changes to open
The `create-new-change-for-all-not-in-target` option provides a
convenience for selecting link:user-upload.html#base[the merge base]
by setting it automatically to the target branch's tip so you can
create new changes for all commits not in the target branch.
This option is disabled if the tip of the push is a merge commit.
This option also only works if there are no merge commits in the
commit chain, in such cases it fails warning the user that such
pushes can only be performed by manually specifying
link:user-upload.html#base[bases]
This option is useful if you want to push a change to your personal
branch first and for review to another branch for example. Or in cases
where a commit is already merged into a branch and you want to create
a new open change for that commit on another branch.
=== Require Change-Id
The `Require Change-Id in commit message` option defines whether a

View File

@@ -671,6 +671,11 @@ read access to `refs/meta/config`.
"configured_value": "INHERIT",
"inherited_value": false
},
"create_new_change_for_all_not_in_target": {
"value": false,
"configured_value": "INHERIT",
"inherited_value": false
},
"require_change_id": {
"value": false,
"configured_value": "FALSE",
@@ -725,6 +730,7 @@ link:#config-input[ConfigInput] entity.
"use_contributor_agreements": "FALSE",
"use_content_merge": "INHERIT",
"use_signed_off_by": "INHERIT",
"create_new_change_for_all_not_in_target": "INHERIT",
"require_change_id": "TRUE",
"max_object_size_limit": "10m",
"submit_type": "REBASE_IF_NECESSARY",
@@ -758,6 +764,11 @@ ConfigInfo] entity.
"configured_value": "INHERIT",
"inherited_value": false
},
"create_new_change_for_all_not_in_target": {
"value": true,
"configured_value": "INHERIT",
"inherited_value": false
},
"require_change_id": {
"value": true,
"configured_value": "TRUE",
@@ -1622,25 +1633,28 @@ The `ConfigInfo` entity contains information about the effective project
configuration.
[options="header",width="50%",cols="1,^2,4"]
|=========================================
|Field Name ||Description
|`description` |optional|
|=======================================================
|Field Name ||Description
|`description` |optional|
The description of the project.
|`use_contributor_agreements`|optional|
|`use_contributor_agreements` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
authors must complete a contributor agreement on the site before
pushing any commits or changes to this project.
|`use_content_merge` |optional|
|`use_content_merge` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
Gerrit will try to perform a 3-way merge of text file content when a
file has been modified by both the destination branch and the change
being submitted. This option only takes effect if submit type is not
FAST_FORWARD_ONLY.
|`use_signed_off_by` |optional|
|`use_signed_off_by` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
each change must contain a Signed-off-by line from either the author or
the uploader in the commit message.
|`require_change_id` |optional|
|`create_new_change_for_all_not_in_target` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
a new change is created for every commit not in target branch.
|`require_change_id` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether a
valid link:user-changeid.html[Change-Id] footer in any commit uploaded
for review is required. This does not apply to commits pushed directly
@@ -1662,14 +1676,14 @@ the comment link configuration is mapped to the comment link
configuration, which has the same format as the
link:config-gerrit.html#_a_id_commentlink_a_section_commentlink[
commentlink section] of `gerrit.config`.
|`theme` |optional|
|`theme` |optional|
The theme that is configured for the project as a link:#theme-info[
ThemeInfo] entity.
|`plugin_config` |optional|
|`plugin_config` |optional|
Plugin configuration as map which maps the plugin name to a map of
parameter names to link:#config-parameter-info[ConfigParameterInfo]
entities.
|`actions` |optional|
|`actions` |optional|
Actions the caller might be able to perform on this project. The
information is a map of view names to
link:rest-api-changes.html#action-info[ActionInfo] entities.
@@ -1680,50 +1694,55 @@ link:rest-api-changes.html#action-info[ActionInfo] entities.
The `ConfigInput` entity describes a new project configuration.
[options="header",width="50%",cols="1,^2,4"]
|=========================================
|Field Name ||Description
|`description` |optional|
|======================================================
|Field Name ||Description
|`description` |optional|
The new description of the project. +
If not set, the description is removed.
|`use_contributor_agreements`|optional|
|`use_contributor_agreements` |optional|
Whether authors must complete a contributor agreement on the site
before pushing any commits or changes to this project. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
|`use_content_merge` |optional|
|`use_content_merge` |optional|
Whether Gerrit will try to perform a 3-way merge of text file content
when a file has been modified by both the destination branch and the
change being submitted. This option only takes effect if submit type is
not FAST_FORWARD_ONLY. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
|`use_signed_off_by` |optional|
|`use_signed_off_by` |optional|
Whether each change must contain a Signed-off-by line from either the
author or the uploader in the commit message. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
|`require_change_id` |optional|
|`create_new_change_for_all_not_in_target` |optional|
Whether a new change will be created for every commit not in target
branch. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
|`require_change_id` |optional|
Whether a valid link:user-changeid.html[Change-Id] footer in any commit
uploaded for review is required. This does not apply to commits pushed
directly to a branch or tag. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
|`max_object_size_limit` |optional|
|`max_object_size_limit` |optional|
The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
limit] of this project as a link:#max-object-size-limit-info[
MaxObjectSizeLimitInfo] entity. +
If set to `0`, the max object size limit is removed. +
If not set, this setting is not updated.
|`submit_type` |optional|
|`submit_type` |optional|
The default submit type of the project, can be `MERGE_IF_NECESSARY`,
`FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`, `MERGE_ALWAYS` or
`CHERRY_PICK`. +
If not set, the submit type is not updated.
|`state` |optional|
|`state` |optional|
The state of the project, can be `ACTIVE`, `READ_ONLY` or `HIDDEN`. +
Not set if the project state is `ACTIVE`. +
If not set, the project state is not updated.
|`plugin_config_values` |optional|
|`plugin_config_values` |optional|
Plugin configuration values as map which maps the plugin name to a map
of parameter names to values.
|=========================================
@@ -1970,17 +1989,20 @@ link:rest-api-groups.html#group-id[group-id]. +
If not set, the link:config-gerrit.html#repository.name.ownerGroup[
groups that are configured as default owners] are set as project
owners.
|`use_contributor_agreements`|`INHERIT` if not set|
|`use_contributor_agreements` |`INHERIT` if not set|
Whether contributor agreements should be used for the project (`TRUE`,
`FALSE`, `INHERIT`).
|`use_signed_off_by` |`INHERIT` if not set|
|`use_signed_off_by` |`INHERIT` if not set|
Whether the usage of 'Signed-Off-By' footers is required for the
project (`TRUE`, `FALSE`, `INHERIT`).
|`use_content_merge` |`INHERIT` if not set|
|`create_new_change_for_all_not_in_target` |`INHERIT` if not set|
Whether a new change is created for every commit not in target branch
for the project (`TRUE`, `FALSE`, `INHERIT`).
|`use_content_merge` |`INHERIT` if not set|
Whether content merge should be enabled for the project (`TRUE`,
`FALSE`, `INHERIT`). +
`FALSE`, if the `submit_type` is `FAST_FORWARD_ONLY`.
|`require_change_id` |`INHERIT` if not set|
|`require_change_id` |`INHERIT` if not set|
Whether the usage of Change-Ids is required for the project (`TRUE`,
`FALSE`, `INHERIT`).
|`max_object_size_limit` |optional|

View File

@@ -33,6 +33,7 @@ public class ProjectInput {
public InheritableBoolean useSignedOffBy;
public InheritableBoolean useContentMerge;
public InheritableBoolean requireChangeId;
public InheritableBoolean createNewChangeForAllNotInTarget;
public String maxObjectSizeLimit;
public Map<String, Map<String, ConfigValue>> pluginConfigValues;

View File

@@ -41,6 +41,7 @@ public interface AdminConstants extends Constants {
String useContentMerge();
String useContributorAgreements();
String useSignedOffBy();
String createNewChangeForAllNotInTarget();
String requireChangeID();
String headingMaxObjectSizeLimit();
String headingGroupOptions();

View File

@@ -23,6 +23,7 @@ projectRepoBrowser = Repository Browser
useContentMerge = Automatically resolve conflicts
useContributorAgreements = Require a valid contributor agreement to upload
useSignedOffBy = Require <code>Signed-off-by</code> in commit message
createNewChangeForAllNotInTarget = Create a new change for every commit not in the target branch
requireChangeID = Require <code>Change-Id</code> in commit message
headingMaxObjectSizeLimit = Maximum Git object size limit
headingGroupOptions = Group Options

View File

@@ -79,6 +79,7 @@ public class ProjectInfoScreen extends ProjectScreen {
private ListBox submitType;
private ListBox state;
private ListBox contentMerge;
private ListBox newChangeForAllNotInTarget;
private NpTextBox maxObjectSizeLimit;
private Label effectiveMaxObjectSizeLimit;
private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
@@ -157,6 +158,7 @@ public class ProjectInfoScreen extends ProjectScreen {
state.setEnabled(isOwner);
submitType.setEnabled(isOwner);
setEnabledForUseContentMerge();
newChangeForAllNotInTarget.setEnabled(isOwner);
descTxt.setEnabled(isOwner);
contributorAgreements.setEnabled(isOwner);
signedOffBy.setEnabled(isOwner);
@@ -213,6 +215,10 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(contentMerge);
grid.add(Util.C.useContentMerge(), contentMerge);
newChangeForAllNotInTarget = newInheritedBooleanBox();
saveEnabler.listenTo(newChangeForAllNotInTarget);
grid.add(Util.C.createNewChangeForAllNotInTarget(), newChangeForAllNotInTarget);
requireChangeID = newInheritedBooleanBox();
saveEnabler.listenTo(requireChangeID);
grid.addHtml(Util.C.requireChangeID(), requireChangeID);
@@ -338,6 +344,7 @@ public class ProjectInfoScreen extends ProjectScreen {
setBool(contributorAgreements, result.use_contributor_agreements());
setBool(signedOffBy, result.use_signed_off_by());
setBool(contentMerge, result.use_content_merge());
setBool(newChangeForAllNotInTarget, result.create_new_change_for_all_not_in_target());
setBool(requireChangeID, result.require_change_id());
setSubmitType(result.submit_type());
setState(result.state());
@@ -569,7 +576,7 @@ public class ProjectInfoScreen extends ProjectScreen {
saveProject.setEnabled(false);
ProjectApi.setConfig(getProjectKey(), descTxt.getText().trim(),
getBool(contributorAgreements), getBool(contentMerge),
getBool(signedOffBy), getBool(requireChangeID),
getBool(signedOffBy), getBool(newChangeForAllNotInTarget), getBool(requireChangeID),
maxObjectSizeLimit.getText().trim(),
SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())),
ProjectState.valueOf(state.getValue(state.getSelectedIndex())),

View File

@@ -44,6 +44,9 @@ public class ConfigInfo extends JavaScriptObject {
public final native InheritedBooleanInfo use_contributor_agreements()
/*-{ return this.use_contributor_agreements; }-*/;
public final native InheritedBooleanInfo create_new_change_for_all_not_in_target()
/*-{ return this.create_new_change_for_all_not_in_target; }-*/;
public final native InheritedBooleanInfo use_signed_off_by()
/*-{ return this.use_signed_off_by; }-*/;

View File

@@ -85,6 +85,7 @@ public class ProjectApi {
public static void setConfig(Project.NameKey name, String description,
InheritableBoolean useContributorAgreements,
InheritableBoolean useContentMerge, InheritableBoolean useSignedOffBy,
InheritableBoolean createNewChangeForAllNotInTarget,
InheritableBoolean requireChangeId, String maxObjectSizeLimit,
SubmitType submitType, ProjectState state,
Map<String, Map<String, ConfigParameterValue>> pluginConfigValues,
@@ -95,6 +96,7 @@ public class ProjectApi {
in.setUseContentMerge(useContentMerge);
in.setUseSignedOffBy(useSignedOffBy);
in.setRequireChangeId(requireChangeId);
in.setCreateNewChangeForAllNotInTarget(createNewChangeForAllNotInTarget);
in.setMaxObjectSizeLimit(maxObjectSizeLimit);
in.setSubmitType(submitType);
in.setState(state);
@@ -209,6 +211,12 @@ public class ProjectApi {
private final native void setRequireChangeIdRaw(String v)
/*-{ if(v)this.require_change_id=v; }-*/;
final void setCreateNewChangeForAllNotInTarget(InheritableBoolean v) {
setCreateNewChangeForAllNotInTargetRaw(v.name());
}
private final native void setCreateNewChangeForAllNotInTargetRaw(String v)
/*-{ if(v)this.create_new_change_for_all_not_in_target=v; }-*/;
final native void setMaxObjectSizeLimit(String l)
/*-{ if(l)this.max_object_size_limit=l; }-*/;

View File

@@ -94,6 +94,8 @@ public final class Project {
protected String themeName;
protected InheritableBoolean createNewChangeForAllNotInTarget;
protected Project() {
}
@@ -105,6 +107,7 @@ public final class Project {
useSignedOffBy = InheritableBoolean.INHERIT;
requireChangeID = InheritableBoolean.INHERIT;
useContentMerge = InheritableBoolean.INHERIT;
createNewChangeForAllNotInTarget = InheritableBoolean.INHERIT;
}
public Project.NameKey getNameKey() {
@@ -159,6 +162,15 @@ public final class Project {
requireChangeID = cid;
}
public InheritableBoolean getCreateNewChangeForAllNotInTarget() {
return createNewChangeForAllNotInTarget;
}
public void setCreateNewChangeForAllNotInTarget(
InheritableBoolean useAllNotInTarget) {
this.createNewChangeForAllNotInTarget = useAllNotInTarget;
}
public void setMaxObjectSizeLimit(final String limit) {
maxObjectSizeLimit = limit;
}
@@ -212,6 +224,7 @@ public final class Project {
submitType = update.submitType;
state = update.state;
maxObjectSizeLimit = update.maxObjectSizeLimit;
createNewChangeForAllNotInTarget = update.createNewChangeForAllNotInTarget;
}
/**

View File

@@ -115,6 +115,8 @@ public class ProjectConfig extends VersionedMetaData {
private static final String RECEIVE = "receive";
private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy";
private static final String KEY_REQUIRE_CHANGE_ID = "requireChangeId";
private static final String KEY_USE_ALL_NOT_IN_TARGET =
"createNewChangeForAllNotInTarget";
private static final String KEY_MAX_OBJECT_SIZE_LIMIT = "maxObjectSizeLimit";
private static final String KEY_REQUIRE_CONTRIBUTOR_AGREEMENT =
"requireContributorAgreement";
@@ -419,6 +421,7 @@ public class ProjectConfig extends VersionedMetaData {
p.setUseContributorAgreements(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, InheritableBoolean.INHERIT));
p.setUseSignedOffBy(getEnum(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, InheritableBoolean.INHERIT));
p.setRequireChangeID(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, InheritableBoolean.INHERIT));
p.setCreateNewChangeForAllNotInTarget(getEnum(rc, RECEIVE, null, KEY_USE_ALL_NOT_IN_TARGET, InheritableBoolean.INHERIT));
p.setMaxObjectSizeLimit(rc.getString(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
@@ -818,6 +821,7 @@ public class ProjectConfig extends VersionedMetaData {
set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.getUseContributorAgreements(), InheritableBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.getUseSignedOffBy(), InheritableBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.getRequireChangeID(), InheritableBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_USE_ALL_NOT_IN_TARGET, p.getCreateNewChangeForAllNotInTarget(), InheritableBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT, validMaxObjectSizeLimit(p.getMaxObjectSizeLimit()));
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);

View File

@@ -311,6 +311,7 @@ public class ReceiveCommits {
private final ReceivePack rp;
private final NoteMap rejectCommits;
private MagicBranchInput magicBranch;
private boolean newChangeForAllNotInTarget;
private List<CreateRequest> newChanges = Collections.emptyList();
private final Map<Change.Id, ReplaceRequest> replaceByChange =
@@ -428,6 +429,7 @@ public class ReceiveCommits {
ProjectState ps = projectControl.getProjectState();
this.newChangeForAllNotInTarget = ps.isCreateNewChangeForAllNotInTarget();
rp.setAllowCreates(true);
rp.setAllowDeletes(true);
rp.setAllowNonFastForwards(true);
@@ -1293,6 +1295,21 @@ public class ReceiveCommits {
}
RevWalk walk = rp.getRevWalk();
RevCommit tip;
try {
tip = walk.parseCommit(magicBranch.cmd.getNewId());
} catch (IOException ex) {
magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", ex);
return;
}
// If tip is a merge commit or %base was specified,
// ignore newChangeForAllNotInTarget
if (tip.getParentCount() > 1 || magicBranch.base != null) {
newChangeForAllNotInTarget = false;
}
if (magicBranch.base != null) {
magicBranch.baseCommit = Lists.newArrayListWithCapacity(
magicBranch.base.size());
@@ -1313,6 +1330,18 @@ public class ReceiveCommits {
return;
}
}
} else if (newChangeForAllNotInTarget) {
String destBranch = magicBranch.dest.get();
try {
ObjectId baseHead = repo.getRef(destBranch).getObjectId();
magicBranch.baseCommit =
Collections.singletonList(walk.parseCommit(baseHead));
} catch (IOException ex) {
log.warn(String.format("Project %s cannot read %s", project.getName(),
destBranch), ex);
reject(cmd, "internal server error");
return;
}
}
// Validate that the new commits are connected with the target
@@ -1321,7 +1350,6 @@ public class ReceiveCommits {
// commits and the target branch head.
//
try {
final RevCommit tip = walk.parseCommit(magicBranch.cmd.getNewId());
Ref targetRef = rp.getAdvertisedRefs().get(magicBranch.ctl.getRefName());
if (targetRef == null || targetRef.getObjectId() == null) {
// The destination branch does not yet exist. Assume the
@@ -1462,6 +1490,13 @@ public class ReceiveCommits {
return Collections.emptyList();
}
// Don't allow merges to be uploaded in commit chain via all-not-in-target
if (newChangeForAllNotInTarget && c.getParentCount() > 1) {
reject(magicBranch.cmd,
"Pushing merges in commit chains with 'all not in target' is not allowed,\n"
+ "to override please set the base manually");
}
Change.Key changeKey = new Change.Key("I" + c.name());
final List<String> idList = c.getFooterLines(CHANGE_ID);
if (idList.isEmpty()) {

View File

@@ -43,6 +43,7 @@ public class ConfigInfo {
public InheritedBooleanInfo useContributorAgreements;
public InheritedBooleanInfo useContentMerge;
public InheritedBooleanInfo useSignedOffBy;
public InheritedBooleanInfo createNewChangeForAllNotInTarget;
public InheritedBooleanInfo requireChangeId;
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
public SubmitType submitType;
@@ -68,17 +69,23 @@ public class ConfigInfo {
InheritedBooleanInfo useSignedOffBy = new InheritedBooleanInfo();
InheritedBooleanInfo useContentMerge = new InheritedBooleanInfo();
InheritedBooleanInfo requireChangeId = new InheritedBooleanInfo();
InheritedBooleanInfo createNewChangeForAllNotInTarget =
new InheritedBooleanInfo();
useContributorAgreements.value = projectState.isUseContributorAgreements();
useSignedOffBy.value = projectState.isUseSignedOffBy();
useContentMerge.value = projectState.isUseContentMerge();
requireChangeId.value = projectState.isRequireChangeID();
createNewChangeForAllNotInTarget.value =
projectState.isCreateNewChangeForAllNotInTarget();
useContributorAgreements.configuredValue =
p.getUseContributorAgreements();
useSignedOffBy.configuredValue = p.getUseSignedOffBy();
useContentMerge.configuredValue = p.getUseContentMerge();
requireChangeId.configuredValue = p.getRequireChangeID();
createNewChangeForAllNotInTarget.configuredValue =
p.getCreateNewChangeForAllNotInTarget();
ProjectState parentState = Iterables.getFirst(projectState
.parents(), null);
@@ -88,12 +95,15 @@ public class ConfigInfo {
useSignedOffBy.inheritedValue = parentState.isUseSignedOffBy();
useContentMerge.inheritedValue = parentState.isUseContentMerge();
requireChangeId.inheritedValue = parentState.isRequireChangeID();
createNewChangeForAllNotInTarget.inheritedValue =
parentState.isCreateNewChangeForAllNotInTarget();
}
this.useContributorAgreements = useContributorAgreements;
this.useSignedOffBy = useSignedOffBy;
this.useContentMerge = useContentMerge;
this.requireChangeId = requireChangeId;
this.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
MaxObjectSizeLimitInfo maxObjectSizeLimit = new MaxObjectSizeLimitInfo();
maxObjectSizeLimit.value =

View File

@@ -124,6 +124,9 @@ public class CreateProject implements RestModifyView<TopLevelResource, ProjectIn
? InheritableBoolean.FALSE : MoreObjects.firstNonNull(
input.useContentMerge,
InheritableBoolean.INHERIT);
args.newChangeForAllNotInTarget =
MoreObjects.firstNonNull(input.createNewChangeForAllNotInTarget,
InheritableBoolean.INHERIT);
args.changeIdRequired =
MoreObjects.firstNonNull(input.requireChangeId, InheritableBoolean.INHERIT);
try {

View File

@@ -33,6 +33,7 @@ public class CreateProjectArgs {
public boolean permissionsOnly;
public List<String> branch;
public InheritableBoolean contentMerge;
public InheritableBoolean newChangeForAllNotInTarget;
public InheritableBoolean changeIdRequired;
public boolean createEmptyCommit;
public String maxObjectSizeLimit;
@@ -42,6 +43,7 @@ public class CreateProjectArgs {
signedOffBy = InheritableBoolean.INHERIT;
contentMerge = InheritableBoolean.INHERIT;
changeIdRequired = InheritableBoolean.INHERIT;
newChangeForAllNotInTarget = InheritableBoolean.INHERIT;
submitType = SubmitType.MERGE_IF_NECESSARY;
}

View File

@@ -193,6 +193,7 @@ public class PerformCreateProject {
.setUseContributorAgreements(createProjectArgs.contributorAgreements);
newProject.setUseSignedOffBy(createProjectArgs.signedOffBy);
newProject.setUseContentMerge(createProjectArgs.contentMerge);
newProject.setCreateNewChangeForAllNotInTarget(createProjectArgs.newChangeForAllNotInTarget);
newProject.setRequireChangeID(createProjectArgs.changeIdRequired);
newProject.setMaxObjectSizeLimit(createProjectArgs.maxObjectSizeLimit);
if (createProjectArgs.newParent != null) {

View File

@@ -404,6 +404,15 @@ public class ProjectState {
});
}
public boolean isCreateNewChangeForAllNotInTarget() {
return getInheritableBoolean(new Function<Project, InheritableBoolean>() {
@Override
public InheritableBoolean apply(Project input) {
return input.getCreateNewChangeForAllNotInTarget();
}
});
}
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = Maps.newLinkedHashMap();
for (ProjectState s : treeInOrder()) {

View File

@@ -66,6 +66,7 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
public InheritableBoolean useContributorAgreements;
public InheritableBoolean useContentMerge;
public InheritableBoolean useSignedOffBy;
public InheritableBoolean createNewChangeForAllNotInTarget;
public InheritableBoolean requireChangeId;
public String maxObjectSizeLimit;
public SubmitType submitType;
@@ -146,6 +147,11 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
if (input.useSignedOffBy != null) {
p.setUseSignedOffBy(input.useSignedOffBy);
}
if (input.createNewChangeForAllNotInTarget != null) {
p.setCreateNewChangeForAllNotInTarget(input.createNewChangeForAllNotInTarget);
}
if (input.requireChangeId != null) {
p.setRequireChangeID(input.requireChangeId);
}

View File

@@ -87,6 +87,9 @@ final class CreateProjectCommand extends SshCommand {
@Option(name = "--change-id", usage = "if change-id is required")
private InheritableBoolean requireChangeID = InheritableBoolean.INHERIT;
@Option(name = "--new-change-for-all-not-in-target", usage = "if a new change will be created for every commit not in target branch")
private InheritableBoolean createNewChangeForAllNotInTarget = InheritableBoolean.INHERIT;
@Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
void setUseContributorArgreements(boolean on) {
contributorAgreements = InheritableBoolean.TRUE;
@@ -107,6 +110,11 @@ final class CreateProjectCommand extends SshCommand {
requireChangeID = InheritableBoolean.TRUE;
}
@Option(name = "--create-new-change-for-all-not-in-target", aliases = {"--ncfa"}, usage = "if a new change will be created for every commit not in target branch")
void setNewChangeForAllNotInTarget(boolean on) {
createNewChangeForAllNotInTarget = InheritableBoolean.TRUE;
}
@Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
+ "(default: master)")
private List<String> branch;
@@ -166,6 +174,7 @@ final class CreateProjectCommand extends SshCommand {
input.useSignedOffBy = signedOffBy;
input.useContentMerge = contentMerge;
input.requireChangeId = requireChangeID;
input.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
input.branches = branch;
input.createEmptyCommit = createEmptyCommit;
input.maxObjectSizeLimit = maxObjectSizeLimit;