Merge "Permit booleans in project.config to be inherited"

This commit is contained in:
Shawn O. Pearce
2012-10-25 12:31:35 -07:00
committed by Gerrit Code Review
13 changed files with 302 additions and 171 deletions

View File

@@ -11,11 +11,11 @@ SYNOPSIS
'ssh' -p <port> <host> 'gerrit set-project' 'ssh' -p <port> <host> 'gerrit set-project'
[--description <DESC> | -d <DESC>] [--description <DESC> | -d <DESC>]
[--submit-type <TYPE> | -t <TYPE>] [--submit-type <TYPE> | -t <TYPE>]
[--use|no-contributor-agreements | --ca|nca] [--contributor-agreements <true|false|inherit>]
[--use|no-signed-off-by | --so|nso] [--signed-off-by <true|false|inherit>]
[--use|no-content-merge] [--content-merge <true|false|inherit>]
[--require|no-change-id | --id|nid] [--change-id <true|false|inherit>]
[--project-state | --ps] [--project-state <STATE> | --ps <STATE>]
<NAME> <NAME>
DESCRIPTION DESCRIPTION
@@ -63,26 +63,23 @@ Description values containing spaces should be quoted in single quotes
For more details see For more details see
link:project-setup.html#submit_type[Change Submit Actions]. link:project-setup.html#submit_type[Change Submit Actions].
--use|no-content-merge:: --content-merge::
If enabled, Gerrit will try to perform a 3-way merge of text If enabled, Gerrit will try to perform a 3-way merge of text
file content when a file has been modified by both the file content when a file has been modified by both the
destination branch and the change being submitted. This destination branch and the change being submitted. This
option only takes effect if submit type is not option only takes effect if submit type is not
FAST_FORWARD_ONLY. FAST_FORWARD_ONLY.
--use|no-contributor-agreements:: --contributor-agreements::
--ca|nca::
If enabled, authors must complete a contributor agreement If enabled, authors must complete a contributor agreement
on the site before pushing any commits or changes to this on the site before pushing any commits or changes to this
project. project.
--use|no-signed-off-by:: --signed-off-by::
--so|nso:
If enabled, each change must contain a Signed-off-by line If enabled, each change must contain a Signed-off-by line
from either the author or the uploader in the commit message. from either the author or the uploader in the commit message.
--require|no-change-id:: --change-id::
--id|nid::
Require a valid link:user-changeid.html[Change-Id] footer Require a valid link:user-changeid.html[Change-Id] footer
in any commit uploaded for review. This does not apply to in any commit uploaded for review. This does not apply to
commits pushed directly to a branch or tag. commits pushed directly to a branch or tag.
@@ -103,7 +100,7 @@ and use 'merge if necessary' as merge strategy:
==== ====
$ ssh -p 29418 review.example.com gerrit set-project example --submit-type MERGE_IF_NECESSARY\ $ ssh -p 29418 review.example.com gerrit set-project example --submit-type MERGE_IF_NECESSARY\
--require-change-id --no-content-merge --project-state HIDDEN --change-id true --content-merge false --project-state HIDDEN
==== ====
GERRIT GERRIT

View File

@@ -21,13 +21,16 @@ import com.google.gerrit.client.ui.OnEditEnabler;
import com.google.gerrit.client.ui.SmallHeading; import com.google.gerrit.client.ui.SmallHeading;
import com.google.gerrit.common.data.ProjectDetail; import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType; import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.VerticalPanel;
@@ -37,14 +40,14 @@ public class ProjectInfoScreen extends ProjectScreen {
private Project project; private Project project;
private Panel projectOptionsPanel; private Panel projectOptionsPanel;
private CheckBox requireChangeID; private ListBox requireChangeID;
private ListBox submitType; private ListBox submitType;
private ListBox state; private ListBox state;
private CheckBox useContentMerge; private ListBox contentMerge;
private Panel agreementsPanel; private Panel agreementsPanel;
private CheckBox useContributorAgreements; private ListBox contributorAgreements;
private CheckBox useSignedOffBy; private ListBox signedOffBy;
private NpTextArea descTxt; private NpTextArea descTxt;
private Button saveProject; private Button saveProject;
@@ -96,10 +99,10 @@ public class ProjectInfoScreen extends ProjectScreen {
final boolean canModifyState) { final boolean canModifyState) {
submitType.setEnabled(canModifyMergeType); submitType.setEnabled(canModifyMergeType);
state.setEnabled(canModifyState); state.setEnabled(canModifyState);
useContentMerge.setEnabled(canModifyMergeType); contentMerge.setEnabled(canModifyMergeType);
descTxt.setEnabled(canModifyDescription); descTxt.setEnabled(canModifyDescription);
useContributorAgreements.setEnabled(canModifyAgreements); contributorAgreements.setEnabled(canModifyAgreements);
useSignedOffBy.setEnabled(canModifyAgreements); signedOffBy.setEnabled(canModifyAgreements);
requireChangeID.setEnabled(canModifyMergeType); requireChangeID.setEnabled(canModifyMergeType);
} }
@@ -142,19 +145,33 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(state); saveEnabler.listenTo(state);
projectOptionsPanel.add(state); projectOptionsPanel.add(state);
useContentMerge = new CheckBox(Util.C.useContentMerge(), true); contentMerge = newInheritedBooleanBox();
saveEnabler.listenTo(useContentMerge); FlowPanel fp = new FlowPanel();
projectOptionsPanel.add(useContentMerge); fp.add(contentMerge);
fp.add(new InlineLabel(Util.C.useContentMerge()));
saveEnabler.listenTo(contentMerge);
projectOptionsPanel.add(fp);
requireChangeID = new CheckBox(Util.C.requireChangeID(), true); requireChangeID = newInheritedBooleanBox();
fp = new FlowPanel();
fp.add(requireChangeID);
fp.add(new InlineHTML(Util.C.requireChangeID()));
saveEnabler.listenTo(requireChangeID); saveEnabler.listenTo(requireChangeID);
projectOptionsPanel.add(requireChangeID); projectOptionsPanel.add(fp);
add(projectOptionsPanel); add(projectOptionsPanel);
} }
private static ListBox newInheritedBooleanBox() {
ListBox box = new ListBox();
for (InheritedBoolean b : InheritedBoolean.values()) {
box.addItem(b.name(), b.name());
}
return box;
}
/** /**
* Enables the {@link #useContentMerge} checkbox if the selected submit type * Enables the {@link #contentMerge} checkbox if the selected submit type
* allows the usage of content merge. * allows the usage of content merge.
* If the submit type (currently only 'Fast Forward Only') does not allow * If the submit type (currently only 'Fast Forward Only') does not allow
* content merge the useContentMerge checkbox gets disabled. * content merge the useContentMerge checkbox gets disabled.
@@ -162,10 +179,10 @@ public class ProjectInfoScreen extends ProjectScreen {
private void setEnabledForUseContentMerge() { private void setEnabledForUseContentMerge() {
if (SubmitType.FAST_FORWARD_ONLY.equals(Project.SubmitType if (SubmitType.FAST_FORWARD_ONLY.equals(Project.SubmitType
.valueOf(submitType.getValue(submitType.getSelectedIndex())))) { .valueOf(submitType.getValue(submitType.getSelectedIndex())))) {
useContentMerge.setEnabled(false); contentMerge.setEnabled(false);
useContentMerge.setValue(false); setBool(contentMerge, InheritedBoolean.FALSE);
} else { } else {
useContentMerge.setEnabled(submitType.isEnabled()); contentMerge.setEnabled(submitType.isEnabled());
} }
} }
@@ -173,13 +190,21 @@ public class ProjectInfoScreen extends ProjectScreen {
agreementsPanel = new VerticalPanel(); agreementsPanel = new VerticalPanel();
agreementsPanel.add(new SmallHeading(Util.C.headingAgreements())); agreementsPanel.add(new SmallHeading(Util.C.headingAgreements()));
useContributorAgreements = new CheckBox(Util.C.useContributorAgreements()); contributorAgreements = newInheritedBooleanBox();
saveEnabler.listenTo(useContributorAgreements); if (Gerrit.getConfig().isUseContributorAgreements()) {
agreementsPanel.add(useContributorAgreements); FlowPanel fp = new FlowPanel();
fp.add(contributorAgreements);
fp.add(new InlineLabel(Util.C.useContributorAgreements()));
saveEnabler.listenTo(contributorAgreements);
agreementsPanel.add(fp);
}
useSignedOffBy = new CheckBox(Util.C.useSignedOffBy(), true); signedOffBy = newInheritedBooleanBox();
saveEnabler.listenTo(useSignedOffBy); FlowPanel fp = new FlowPanel();
agreementsPanel.add(useSignedOffBy); fp.add(signedOffBy);
fp.add(new InlineHTML(Util.C.useSignedOffBy()));
saveEnabler.listenTo(signedOffBy);
agreementsPanel.add(fp);
add(agreementsPanel); add(agreementsPanel);
} }
@@ -209,21 +234,31 @@ public class ProjectInfoScreen extends ProjectScreen {
} }
} }
private static void setBool(ListBox box, InheritedBoolean val) {
for (int i = 0; i < box.getItemCount(); i++) {
if (val.name().equals(box.getValue(i))) {
box.setSelectedIndex(i);
break;
}
}
}
private static InheritedBoolean getBool(ListBox box) {
int i = box.getSelectedIndex();
if (i >= 0) {
return InheritedBoolean.valueOf(box.getValue(i));
}
return InheritedBoolean.INHERIT;
}
void display(final ProjectDetail result) { void display(final ProjectDetail result) {
project = result.project; project = result.project;
final boolean isall =
Gerrit.getConfig().getWildProject().equals(project.getNameKey());
projectOptionsPanel.setVisible(!isall);
agreementsPanel.setVisible(!isall);
useContributorAgreements.setVisible(Gerrit.getConfig()
.isUseContributorAgreements());
descTxt.setText(project.getDescription()); descTxt.setText(project.getDescription());
useContributorAgreements.setValue(project.isUseContributorAgreements()); setBool(contributorAgreements, project.getUseContributorAgreements());
useSignedOffBy.setValue(project.isUseSignedOffBy()); setBool(signedOffBy, project.getUseSignedOffBy());
useContentMerge.setValue(project.isUseContentMerge()); setBool(contentMerge, project.getUseContentMerge());
requireChangeID.setValue(project.isRequireChangeID()); setBool(requireChangeID, project.getRequireChangeID());
setSubmitType(project.getSubmitType()); setSubmitType(project.getSubmitType());
setState(project.getState()); setState(project.getState());
@@ -232,10 +267,10 @@ public class ProjectInfoScreen extends ProjectScreen {
private void doSave() { private void doSave() {
project.setDescription(descTxt.getText().trim()); project.setDescription(descTxt.getText().trim());
project.setUseContributorAgreements(useContributorAgreements.getValue()); project.setUseContributorAgreements(getBool(contributorAgreements));
project.setUseSignedOffBy(useSignedOffBy.getValue()); project.setUseSignedOffBy(getBool(signedOffBy));
project.setUseContentMerge(useContentMerge.getValue()); project.setUseContentMerge(getBool(contentMerge));
project.setRequireChangeID(requireChangeID.getValue()); project.setRequireChangeID(getBool(requireChangeID));
if (submitType.getSelectedIndex() >= 0) { if (submitType.getSelectedIndex() >= 0) {
project.setSubmitType(Project.SubmitType.valueOf(submitType project.setSubmitType(Project.SubmitType.valueOf(submitType
.getValue(submitType.getSelectedIndex()))); .getValue(submitType.getSelectedIndex())));

View File

@@ -85,13 +85,19 @@ public final class Project {
HIDDEN; HIDDEN;
} }
public static enum InheritedBoolean {
TRUE,
FALSE,
INHERIT;
}
protected NameKey name; protected NameKey name;
protected String description; protected String description;
protected boolean useContributorAgreements; protected InheritedBoolean useContributorAgreements;
protected boolean useSignedOffBy; protected InheritedBoolean useSignedOffBy;
protected SubmitType submitType; protected SubmitType submitType;
@@ -99,9 +105,9 @@ public final class Project {
protected NameKey parent; protected NameKey parent;
protected boolean requireChangeID; protected InheritedBoolean requireChangeID;
protected boolean useContentMerge; protected InheritedBoolean useContentMerge;
protected Project() { protected Project() {
} }
@@ -110,6 +116,10 @@ public final class Project {
name = nameKey; name = nameKey;
submitType = SubmitType.MERGE_IF_NECESSARY; submitType = SubmitType.MERGE_IF_NECESSARY;
state = State.ACTIVE; state = State.ACTIVE;
useContributorAgreements = InheritedBoolean.INHERIT;
useSignedOffBy = InheritedBoolean.INHERIT;
requireChangeID = InheritedBoolean.INHERIT;
useContentMerge = InheritedBoolean.INHERIT;
} }
public Project.NameKey getNameKey() { public Project.NameKey getNameKey() {
@@ -128,35 +138,35 @@ public final class Project {
description = d; description = d;
} }
public boolean isUseContributorAgreements() { public InheritedBoolean getUseContributorAgreements() {
return useContributorAgreements; return useContributorAgreements;
} }
public void setUseContributorAgreements(final boolean u) { public InheritedBoolean getUseSignedOffBy() {
useContributorAgreements = u;
}
public boolean isUseSignedOffBy() {
return useSignedOffBy; return useSignedOffBy;
} }
public boolean isUseContentMerge() { public InheritedBoolean getUseContentMerge() {
return useContentMerge; return useContentMerge;
} }
public boolean isRequireChangeID() { public InheritedBoolean getRequireChangeID() {
return requireChangeID; return requireChangeID;
} }
public void setUseSignedOffBy(final boolean sbo) { public void setUseContributorAgreements(final InheritedBoolean u) {
useContributorAgreements = u;
}
public void setUseSignedOffBy(final InheritedBoolean sbo) {
useSignedOffBy = sbo; useSignedOffBy = sbo;
} }
public void setUseContentMerge(final boolean cm) { public void setUseContentMerge(final InheritedBoolean cm) {
useContentMerge = cm; useContentMerge = cm;
} }
public void setRequireChangeID(final boolean cid) { public void setRequireChangeID(final InheritedBoolean cid) {
requireChangeID = cid; requireChangeID = cid;
} }

View File

@@ -132,7 +132,7 @@ public class MergeOp {
private final PersonIdent myIdent; private final PersonIdent myIdent;
private final Branch.NameKey destBranch; private final Branch.NameKey destBranch;
private Project destProject; private ProjectState destProject;
private final ListMultimap<SubmitType, CodeReviewCommit> toMerge; private final ListMultimap<SubmitType, CodeReviewCommit> toMerge;
private final List<CodeReviewCommit> potentiallyStillSubmittable; private final List<CodeReviewCommit> potentiallyStillSubmittable;
private final Map<Change.Id, CodeReviewCommit> commits; private final Map<Change.Id, CodeReviewCommit> commits;
@@ -253,11 +253,10 @@ public class MergeOp {
} }
private void setDestProject() throws MergeException { private void setDestProject() throws MergeException {
final ProjectState pe = projectCache.get(destBranch.getParentKey()); destProject = projectCache.get(destBranch.getParentKey());
if (pe == null) { if (destProject == null) {
throw new MergeException("No such project: " + destBranch.getParentKey()); throw new MergeException("No such project: " + destBranch.getParentKey());
} }
destProject = pe.getProject();
} }
private void openSchema() throws OrmException { private void openSchema() throws OrmException {
@@ -602,12 +601,13 @@ public class MergeOp {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) { if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) { if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
try { try {
ProjectConfig cfg = new ProjectConfig(destProject.getNameKey()); ProjectConfig cfg =
new ProjectConfig(destProject.getProject().getNameKey());
cfg.load(repo, mergeTip); cfg.load(repo, mergeTip);
} catch (Exception e) { } catch (Exception e) {
throw new MergeException("Submit would store invalid" throw new MergeException("Submit would store invalid"
+ " project configuration " + mergeTip.name() + " for " + " project configuration " + mergeTip.name() + " for "
+ destProject.getName(), e); + destProject.getProject().getName(), e);
} }
} }
@@ -627,10 +627,11 @@ public class MergeOp {
} }
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) { if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
projectCache.evict(destProject); projectCache.evict(destProject.getProject());
ProjectState ps = projectCache.get(destProject.getNameKey()); destProject = projectCache.get(destProject.getProject().getNameKey());
repoManager.setProjectDescription(destProject.getNameKey(), // repoManager.setProjectDescription(
ps.getProject().getDescription()); destProject.getProject().getNameKey(),
destProject.getProject().getDescription());
} }
replication.fire(destBranch.getParentKey(), branchUpdate.getName()); replication.fire(destBranch.getParentKey(), branchUpdate.getName());
@@ -732,8 +733,8 @@ public class MergeOp {
private void updateSubscriptions(final List<Change> submitted) { private void updateSubscriptions(final List<Change> submitted) {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) { if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
SubmoduleOp subOp = SubmoduleOp subOp =
subOpFactory.create(destBranch, mergeTip, rw, repo, destProject, subOpFactory.create(destBranch, mergeTip, rw, repo,
submitted, commits); destProject.getProject(), submitted, commits);
try { try {
subOp.update(); subOp.update();
} catch (SubmoduleException e) { } catch (SubmoduleException e) {

View File

@@ -282,12 +282,12 @@ public class ProjectConfig extends VersionedMetaData {
} }
p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM)); p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM));
p.setUseContributorAgreements(getBoolean(rc, RECEIVE, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, false)); p.setUseContributorAgreements(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, Project.InheritedBoolean.INHERIT));
p.setUseSignedOffBy(getBoolean(rc, RECEIVE, KEY_REQUIRE_SIGNED_OFF_BY, false)); p.setUseSignedOffBy(getEnum(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, Project.InheritedBoolean.INHERIT));
p.setRequireChangeID(getBoolean(rc, RECEIVE, KEY_REQUIRE_CHANGE_ID, false)); p.setRequireChangeID(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, Project.InheritedBoolean.INHERIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction)); p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
p.setUseContentMerge(getBoolean(rc, SUBMIT, KEY_MERGE_CONTENT, false)); p.setUseContentMerge(getEnum(rc, SUBMIT, null, KEY_MERGE_CONTENT, Project.InheritedBoolean.INHERIT));
p.setState(getEnum(rc, PROJECT, null, KEY_STATE, defaultStateValue)); p.setState(getEnum(rc, PROJECT, null, KEY_STATE, defaultStateValue));
loadAccountsSection(rc, groupsByName); loadAccountsSection(rc, groupsByName);
@@ -525,12 +525,12 @@ public class ProjectConfig extends VersionedMetaData {
} }
set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName()); set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName());
set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.isUseContributorAgreements()); set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.getUseContributorAgreements(), Project.InheritedBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.isUseSignedOffBy()); set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.getUseSignedOffBy(), Project.InheritedBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.isRequireChangeID()); set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.getRequireChangeID(), Project.InheritedBoolean.INHERIT);
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction); set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge()); set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.getUseContentMerge(), Project.InheritedBoolean.INHERIT);
set(rc, PROJECT, null, KEY_STATE, p.getState(), null); set(rc, PROJECT, null, KEY_STATE, p.getState(), null);
@@ -734,16 +734,6 @@ public class ProjectConfig extends VersionedMetaData {
saveUTF8(GROUP_LIST, buf.toString()); saveUTF8(GROUP_LIST, buf.toString());
} }
private boolean getBoolean(Config rc, String section, String name,
boolean defaultValue) {
try {
return rc.getBoolean(section, name, defaultValue);
} catch (IllegalArgumentException err) {
error(new ValidationError(PROJECT_CONFIG, err.getMessage()));
return defaultValue;
}
}
private <E extends Enum<?>> E getEnum(Config rc, String section, private <E extends Enum<?>> E getEnum(Config rc, String section,
String subsection, String name, E defaultValue) { String subsection, String name, E defaultValue) {
try { try {

View File

@@ -14,8 +14,8 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN; import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED; import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK; import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT; import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
@@ -79,6 +79,7 @@ import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.jcraft.jsch.HostKey; import com.jcraft.jsch.HostKey;
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -1868,7 +1869,7 @@ public class ReceiveCommits {
&& ctl.canForgeCommitter() && ctl.canForgeCommitter()
&& ctl.canForgeGerritServerIdentity() && ctl.canForgeGerritServerIdentity()
&& ctl.canUploadMerges() && ctl.canUploadMerges()
&& !project.isUseSignedOffBy() && !projectControl.getProjectState().isUseSignedOffBy()
&& Iterables.isEmpty(rejectCommits) && Iterables.isEmpty(rejectCommits)
&& !GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName()) && !GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())
&& !(MagicBranch.isMagicBranch(cmd.getRefName()) && !(MagicBranch.isMagicBranch(cmd.getRefName())
@@ -1954,7 +1955,7 @@ public class ReceiveCommits {
return false; return false;
} }
if (project.isUseSignedOffBy()) { if (projectControl.getProjectState().isUseSignedOffBy()) {
// If the project wants Signed-off-by / Acked-by lines, verify we // If the project wants Signed-off-by / Acked-by lines, verify we
// have them for the blamable parties involved on this change. // have them for the blamable parties involved on this change.
// //
@@ -1978,7 +1979,7 @@ public class ReceiveCommits {
final List<String> idList = c.getFooterLines(CHANGE_ID); final List<String> idList = c.getFooterLines(CHANGE_ID);
if (MagicBranch.isMagicBranch(cmd.getRefName()) || NEW_PATCHSET.matcher(cmd.getRefName()).matches()) { if (MagicBranch.isMagicBranch(cmd.getRefName()) || NEW_PATCHSET.matcher(cmd.getRefName()).matches()) {
if (idList.isEmpty()) { if (idList.isEmpty()) {
if (project.isRequireChangeID()) { if (projectControl.getProjectState().isRequireChangeID()) {
String errMsg = "missing Change-Id in commit message"; String errMsg = "missing Change-Id in commit message";
reject(cmd, errMsg); reject(cmd, errMsg);
addMessage(getFixedCommitMsgWithChangeId(errMsg, c)); addMessage(getFixedCommitMsgWithChangeId(errMsg, c));

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.project;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType; import com.google.gerrit.reviewdb.client.Project.SubmitType;
import java.util.List; import java.util.List;
@@ -27,12 +28,12 @@ public class CreateProjectArgs {
public ProjectControl newParent; public ProjectControl newParent;
public String projectDescription; public String projectDescription;
public SubmitType submitType; public SubmitType submitType;
public boolean contributorAgreements; public InheritedBoolean contributorAgreements;
public boolean signedOffBy; public InheritedBoolean signedOffBy;
public boolean permissionsOnly; public boolean permissionsOnly;
public List<String> branch; public List<String> branch;
public boolean contentMerge; public InheritedBoolean contentMerge;
public boolean changeIdRequired; public InheritedBoolean changeIdRequired;
public boolean createEmptyCommit; public boolean createEmptyCommit;
public CreateProjectArgs() { public CreateProjectArgs() {

View File

@@ -232,8 +232,7 @@ public class ProjectControl {
String pName = state.getProject().getName(); String pName = state.getProject().getName();
return new Capable("Upload denied for project '" + pName + "'"); return new Capable("Upload denied for project '" + pName + "'");
} }
Project project = state.getProject(); if (state.isUseContributorAgreements()) {
if (project.isUseContributorAgreements()) {
return verifyActiveContributorAgreement(); return verifyActiveContributorAgreement();
} }
return Capable.OK; return Capable.OK;

View File

@@ -14,13 +14,16 @@
package com.google.gerrit.server.project; package com.google.gerrit.server.project;
import com.google.common.base.Function;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.rules.PrologEnvironment; import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.rules.RulesCache; import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
@@ -295,4 +298,63 @@ public class ProjectState {
public boolean isAllProjects() { public boolean isAllProjects() {
return isAllProjects; return isAllProjects;
} }
public boolean isUseContributorAgreements() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseContributorAgreements();
}
});
}
public boolean isUseContentMerge() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseContentMerge();
}
});
}
public boolean isUseSignedOffBy() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseSignedOffBy();
}
});
}
public boolean isRequireChangeID() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getRequireChangeID();
}
});
}
private boolean getInheritedBoolean(Function<Project, InheritedBoolean> func) {
Set<Project.NameKey> seen = Sets.newHashSet();
seen.add(getProject().getNameKey());
ProjectState s = this;
do {
switch (func.apply(s.getProject())) {
case TRUE:
return true;
case FALSE:
return false;
case INHERIT:
default:
Project.NameKey parent = s.getProject().getParent(allProjectsName);
if (parent != null && seen.add(parent)) {
s = projectCache.get(parent);
} else {
s = null;
}
}
} while (s != null);
return false;
}
} }

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.ApprovalCategory;
import com.google.gerrit.reviewdb.client.ApprovalCategoryValue; import com.google.gerrit.reviewdb.client.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion; import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.SystemConfig; import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
@@ -209,7 +210,10 @@ public class SchemaCreator {
ProjectConfig config = ProjectConfig.read(md); ProjectConfig config = ProjectConfig.read(md);
Project p = config.getProject(); Project p = config.getProject();
p.setDescription("Rights inherited by all other projects"); p.setDescription("Rights inherited by all other projects");
p.setUseContributorAgreements(false); p.setRequireChangeID(InheritedBoolean.TRUE);
p.setUseContentMerge(InheritedBoolean.FALSE);
p.setUseContributorAgreements(InheritedBoolean.FALSE);
p.setUseSignedOffBy(InheritedBoolean.FALSE);
AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true); AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
AccessSection all = config.getAccessSection(AccessSection.ALL, true); AccessSection all = config.getAccessSection(AccessSection.ALL, true);

View File

@@ -33,6 +33,7 @@ import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.ApprovalCategory; import com.google.gerrit.reviewdb.client.ApprovalCategory;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.SystemConfig; import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
@@ -200,8 +201,7 @@ class Schema_53 extends SchemaVersion {
private void loadProject(ResultSet rs, Project project) throws SQLException, private void loadProject(ResultSet rs, Project project) throws SQLException,
OrmException { OrmException {
project.setDescription(rs.getString("description")); project.setDescription(rs.getString("description"));
project.setUseContributorAgreements("Y".equals(rs project.setUseContributorAgreements(asInheritedBoolean(rs, "use_contributor_agreements"));
.getString("use_contributor_agreements")));
switch (rs.getString("submit_type").charAt(0)) { switch (rs.getString("submit_type").charAt(0)) {
case 'F': case 'F':
@@ -221,12 +221,19 @@ class Schema_53 extends SchemaVersion {
+ rs.getString("submit_type") + " on project " + project.getName()); + rs.getString("submit_type") + " on project " + project.getName());
} }
project.setUseSignedOffBy("Y".equals(rs.getString("use_signed_off_by"))); project.setUseSignedOffBy(asInheritedBoolean(rs, "use_signed_off_by"));
project.setRequireChangeID("Y".equals(rs.getString("require_change_id"))); project.setRequireChangeID(asInheritedBoolean(rs, "require_change_id"));
project.setUseContentMerge("Y".equals(rs.getString("use_content_merge"))); project.setUseContentMerge(asInheritedBoolean(rs, "use_content_merge"));
project.setParentName(rs.getString("parent_name")); project.setParentName(rs.getString("parent_name"));
} }
private static InheritedBoolean asInheritedBoolean(ResultSet rs, String col)
throws SQLException {
return "Y".equals(rs.getString(col))
? Project.InheritedBoolean.TRUE
: Project.InheritedBoolean.INHERIT;
}
private void readOldRefRights(ReviewDb db) throws SQLException { private void readOldRefRights(ReviewDb db) throws SQLException {
rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>(); rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>();

View File

@@ -19,6 +19,7 @@ import com.google.gerrit.common.errors.ProjectCreationFailedException;
import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType; import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.server.project.CreateProject; import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.CreateProjectArgs; import com.google.gerrit.server.project.CreateProjectArgs;
@@ -64,17 +65,37 @@ final class CreateProjectCommand extends SshCommand {
+ "(default: MERGE_IF_NECESSARY)") + "(default: MERGE_IF_NECESSARY)")
private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY; private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY;
@Option(name = "--contributor-agreements", usage = "if contributor agreement is required")
private InheritedBoolean contributorAgreements;
@Option(name = "--signed-off-by", usage = "if signed-off-by is required")
private InheritedBoolean signedOffBy = InheritedBoolean.INHERIT;
@Option(name = "--content-merge", usage = "allow automatic conflict resolving within files")
private InheritedBoolean contentMerge = InheritedBoolean.INHERIT;
@Option(name = "--change-id", usage = "if change-id is required")
private InheritedBoolean requireChangeID = InheritedBoolean.INHERIT;
@Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required") @Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
private boolean contributorAgreements; void setUseContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.TRUE;
}
@Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required") @Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
private boolean signedOffBy; void setUseSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.TRUE;
}
@Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files") @Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files")
private boolean contentMerge; void setUseContentMerge(boolean on) {
contentMerge = InheritedBoolean.TRUE;
}
@Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required") @Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required")
private boolean requireChangeID; void setRequireChangeId(boolean on) {
requireChangeID = InheritedBoolean.TRUE;
}
@Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n" @Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
+ "(default: master)") + "(default: master)")

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.State; import com.google.gerrit.reviewdb.client.Project.State;
import com.google.gerrit.reviewdb.client.Project.SubmitType; import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
@@ -50,29 +51,57 @@ final class SetProjectCommand extends SshCommand {
+ "(default: MERGE_IF_NECESSARY)") + "(default: MERGE_IF_NECESSARY)")
private SubmitType submitType; private SubmitType submitType;
@Option(name = "--contributor-agreements", usage = "if contributor agreement is required")
private InheritedBoolean contributorAgreements;
@Option(name = "--signed-off-by", usage = "if signed-off-by is required")
private InheritedBoolean signedOffBy;
@Option(name = "--content-merge", usage = "allow automatic conflict resolving within files")
private InheritedBoolean contentMerge;
@Option(name = "--change-id", usage = "if change-id is required")
private InheritedBoolean requireChangeID;
@Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required") @Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
private Boolean contributorAgreements; void setUseContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.TRUE;
}
@Option(name = "--no-contributor-agreements", aliases = {"--nca"}, usage = "if contributor agreement is not required") @Option(name = "--no-contributor-agreements", aliases = {"--nca"}, usage = "if contributor agreement is not required")
private Boolean noContributorAgreements; void setNoContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.FALSE;
}
@Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required") @Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
private Boolean signedOffBy; void setUseSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.TRUE;
}
@Option(name = "--no-signed-off-by", aliases = {"--nso"}, usage = "if signed-off-by is not required") @Option(name = "--no-signed-off-by", aliases = {"--nso"}, usage = "if signed-off-by is not required")
private Boolean noSignedOffBy; void setNoSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.FALSE;
}
@Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files") @Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files")
private Boolean contentMerge; void setUseContentMerge(boolean on) {
contentMerge = InheritedBoolean.TRUE;
}
@Option(name = "--no-content-merge", usage = "don't allow automatic conflict resolving within files") @Option(name = "--no-content-merge", usage = "don't allow automatic conflict resolving within files")
private Boolean noContentMerge; void setNoContentMerge(boolean on) {
contentMerge = InheritedBoolean.FALSE;
}
@Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required") @Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required")
private Boolean requireChangeID; void setRequireChangeId(boolean on) {
requireChangeID = InheritedBoolean.TRUE;
}
@Option(name = "--no-change-id", aliases = {"--nid"}, usage = "if change-id is not required") @Option(name = "--no-change-id", aliases = {"--nid"}, usage = "if change-id is not required")
private Boolean noRequireChangeID; void setNoChangeId(boolean on) {
requireChangeID = InheritedBoolean.FALSE;
}
@Option(name = "--project-state", aliases = {"--ps"}, usage = "project's visibility state") @Option(name = "--project-state", aliases = {"--ps"}, usage = "project's visibility state")
private State state; private State state;
@@ -85,7 +114,6 @@ final class SetProjectCommand extends SshCommand {
@Override @Override
protected void run() throws Failure { protected void run() throws Failure {
validate();
Project ctlProject = projectControl.getProject(); Project ctlProject = projectControl.getProject();
Project.NameKey nameKey = ctlProject.getNameKey(); Project.NameKey nameKey = ctlProject.getNameKey();
String name = ctlProject.getName(); String name = ctlProject.getName();
@@ -97,38 +125,27 @@ final class SetProjectCommand extends SshCommand {
ProjectConfig config = ProjectConfig.read(md); ProjectConfig config = ProjectConfig.read(md);
Project project = config.getProject(); Project project = config.getProject();
project.setRequireChangeID(requireChangeID != null ? requireChangeID if (requireChangeID != null) {
: project.isRequireChangeID()); project.setRequireChangeID(requireChangeID);
}
project.setRequireChangeID(noRequireChangeID != null if (submitType != null) {
? !noRequireChangeID : project.isRequireChangeID()); project.setSubmitType(submitType);
}
project.setSubmitType(submitType != null ? submitType : project if (contentMerge != null) {
.getSubmitType()); project.setUseContentMerge(contentMerge);
}
project.setUseContentMerge(contentMerge != null ? contentMerge if (contributorAgreements != null) {
: project.isUseContentMerge()); project.setUseContributorAgreements(contributorAgreements);
}
project.setUseContentMerge(noContentMerge != null ? !noContentMerge if (signedOffBy != null) {
: project.isUseContentMerge()); project.setUseSignedOffBy(signedOffBy);
}
project.setUseContributorAgreements(contributorAgreements != null if (projectDescription != null) {
? contributorAgreements : project.isUseContributorAgreements()); project.setDescription(projectDescription);
}
project.setUseContributorAgreements(noContributorAgreements != null if (state != null) {
? !noContributorAgreements : project.isUseContributorAgreements()); project.setState(state);
}
project.setUseSignedOffBy(signedOffBy != null ? signedOffBy : project
.isUseSignedOffBy());
project.setUseContentMerge(noSignedOffBy != null ? !noSignedOffBy
: project.isUseContentMerge());
project.setDescription(projectDescription != null ? projectDescription
: project.getDescription());
project.setState(state != null ? state : project.getState());
md.setMessage("Project settings updated"); md.setMessage("Project settings updated");
config.commit(md); config.commit(md);
} finally { } finally {
@@ -154,18 +171,4 @@ final class SetProjectCommand extends SshCommand {
throw new UnloggedFailure(1, err.toString()); throw new UnloggedFailure(1, err.toString());
} }
} }
private void validate() throws UnloggedFailure {
checkExclusivity(contentMerge, "--use-content-merge",
noContentMerge, "--no-content-merge");
checkExclusivity(contributorAgreements, "--use-contributor-agreements",
noContributorAgreements, "--no-contributor-agreements");
checkExclusivity(signedOffBy, "--use-signed-off-by",
noSignedOffBy, "--no-signed-off-by");
checkExclusivity(requireChangeID, "--require-change-id",
noRequireChangeID, "--no-change-id");
}
} }