Option to reject implicit merges when pushing changes for review
An implicit merge is a case where by submitting an open change one also
merges a branch into the target branch. Typically, this happens when a
change is done on top of master and, by mistake, pushed to stable
branch. Merging this change would also implicitly merge master into
stable.
Example 1:
o < change pushed for stable
|
o < master
|
o < stable
Submitting this change will implicitly merge master into stable:
o < change pushed for stable, stable
|
o < master
|
o
Example 2:
o < change pushed for stable
|
master > o o < stable
\ /
o
Submitting this change will implicitly merge master into stable:
o < stable
/|
/ |
o < change pushed for stable
| |
master > o o
\ /
o
A new project property receive.rejectImplicitMerges controls whether an
implicit merge will be rejected. When an implicit merge is detected
Gerrit will print error(s) to the user:
remote: ERROR: Implicit Merge of 39adddb Commit message subject
remote: ERROR: Implicit Merge of ...
and will reject the push.
Bug: issue 1107
Change-Id: I0b14c64bebe28ea5579fc11f6beedacf5982e5aa
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 5da617f..7121265 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -173,6 +173,22 @@
Default is `INHERIT`, which means that this property is inherited from
the parent project.
+[[receive.rejectImplicitMerges]]receive.rejectImplicitMerges::
++
+Controls whether a check for implicit merges will be performed when changes are
+pushed for review. An implicit merge is a case where merging an open change
+would implicitly merge another branch into the target branch. Typically, this
+happens when a change is done on master and, by mistake, pushed to a stable branch
+for review. When submitting such change, master would be implicitly merged into
+stable without anyone noticing that. When this option is set to 'true' Gerrit
+will reject the push if an implicit merge is detected.
++
+This check is only done for non-merge commits, merge commits are not subject of
+the implicit merge check.
++
+Default is `INHERIT`, which means that this property is inherited from
+the parent project.
+
[[submit-section]]
=== Submit section
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 2dc203d..457a287 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -733,6 +733,7 @@
"create_new_change_for_all_not_in_target": "INHERIT",
"enable_signed_push": "INHERIT",
"require_signed_push": "INHERIT",
+ "reject_implicit_merges": "INHERIT",
"require_change_id": "TRUE",
"max_object_size_limit": "10m",
"submit_type": "REBASE_IF_NECESSARY",
@@ -786,6 +787,11 @@
"configured_value": "INHERIT",
"inherited_value": false
},
+ "reject_implicit_merges": {
+ "value": false,
+ "configured_value": "INHERIT",
+ "inherited_value": false
+ },
"max_object_size_limit": {
"value": "10m",
"configured_value": "10m",
@@ -2305,6 +2311,9 @@
|`require_signed_push`|optional, not set if signed push is disabled|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
signed push validation is required on the project.
+|`reject_implicit_merges`|optional|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+implicit merges should be rejected on changes pushed to the project.
|`max_object_size_limit` ||
The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
limit] of this project as a link:#max-object-size-limit-info[
@@ -2373,6 +2382,11 @@
directly to a branch or tag. +
Can be `TRUE`, `FALSE` or `INHERIT`. +
If not set, this setting is not updated.
+|`reject_implicit_merges` |optional|
+Whether a check for implicit merges will be performed when changes
+are pushed for review. +
+Can be `TRUE`, `FALSE` or `INHERIT`. +
+If not set, this setting is not updated.
|`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[
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
index c892877..d79e573 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -374,6 +374,11 @@
.contains(expectedMessage.toLowerCase());
}
+ public String getMessage() {
+ RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
+ return message(refUpdate);
+ }
+
private String message(RemoteRefUpdate refUpdate) {
StringBuilder b = new StringBuilder();
if (refUpdate.getMessage() != null) {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
new file mode 100644
index 0000000..e7097f0
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.git;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.pushHead;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.server.git.ProjectConfig;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+public class ImplicitMergeCheckIT extends AbstractDaemonTest {
+
+ @Test
+ public void implicitMergeViaFastForward() throws Exception {
+ setRejectImplicitMerges();
+
+ pushHead(testRepo, "refs/heads/stable", false);
+ PushOneCommit.Result m = push("refs/heads/master", "0", "file", "0");
+ PushOneCommit.Result c = push("refs/for/stable", "1", "file", "1");
+
+ c.assertMessage(implicitMergeOf(m.getCommit()));
+ c.assertErrorStatus();
+ }
+
+ @Test
+ public void implicitMergeViaRealMerge() throws Exception {
+ setRejectImplicitMerges();
+
+ ObjectId base = repo().exactRef("HEAD").getObjectId();
+ push("refs/heads/stable", "0", "f", "0");
+ testRepo.reset(base);
+ PushOneCommit.Result m = push("refs/heads/master", "1", "f", "1");
+ PushOneCommit.Result c = push("refs/for/stable", "2", "f", "2");
+
+ c.assertMessage(implicitMergeOf(m.getCommit()));
+ c.assertErrorStatus();
+ }
+
+ @Test
+ public void implicitMergeCheckOff() throws Exception {
+ ObjectId base = repo().exactRef("HEAD").getObjectId();
+ push("refs/heads/stable", "0", "f", "0");
+ testRepo.reset(base);
+ PushOneCommit.Result m = push("refs/heads/master", "1", "f", "1");
+ PushOneCommit.Result c = push("refs/for/stable", "2", "f", "2");
+
+ assertThat(c.getMessage().toLowerCase()).doesNotContain(
+ implicitMergeOf(m.getCommit()));
+ }
+
+ @Test
+ public void notImplicitMerge_noWarning() throws Exception {
+ setRejectImplicitMerges();
+
+ ObjectId base = repo().exactRef("HEAD").getObjectId();
+ push("refs/heads/stable", "0", "f", "0");
+ testRepo.reset(base);
+ PushOneCommit.Result m = push("refs/heads/master", "1", "f", "1");
+ PushOneCommit.Result c = push("refs/for/master", "2", "f", "2");
+
+ assertThat(c.getMessage().toLowerCase()).doesNotContain(
+ implicitMergeOf(m.getCommit()));
+ }
+
+ private static String implicitMergeOf(ObjectId commit) {
+ return "implicit merge of " + commit.abbreviate(7).name();
+ }
+
+ private void setRejectImplicitMerges() throws Exception {
+ ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+ cfg.getProject().setRejectImplicitMerges(InheritableBoolean.TRUE);
+ saveProjectConfig(project, cfg);
+ }
+
+ private PushOneCommit.Result push(String ref, String subject,
+ String fileName, String content) throws Exception {
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo,
+ subject, fileName, content);
+ return push.to(ref);
+ }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
index d42bf7b..81c999bc 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
@@ -31,6 +31,7 @@
public InheritedBooleanInfo requireChangeId;
public InheritedBooleanInfo enableSignedPush;
public InheritedBooleanInfo requireSignedPush;
+ public InheritedBooleanInfo rejectImplicitMerges;
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
public SubmitType submitType;
public ProjectState state;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInput.java
index 71aa24a..8ab13f6 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInput.java
@@ -29,6 +29,7 @@
public InheritableBoolean requireChangeId;
public InheritableBoolean enableSignedPush;
public InheritableBoolean requireSignedPush;
+ public InheritableBoolean rejectImplicitMerges;
public String maxObjectSizeLimit;
public SubmitType submitType;
public ProjectState state;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index 511be5f..984c5a3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -45,6 +45,7 @@
String enableSignedPush();
String requireSignedPush();
String requireChangeID();
+ String rejectImplicitMerges();
String headingMaxObjectSizeLimit();
String headingGroupOptions();
String isVisibleToAll();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 3896cad..2fe5978 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -27,6 +27,7 @@
enableSignedPush = Enable signed push
requireSignedPush = Require signed push
requireChangeID = Require <code>Change-Id</code> in commit message
+rejectImplicitMerges = Reject implicit merges when changes are pushed for review
headingMaxObjectSizeLimit = Maximum Git object size limit
headingGroupOptions = Group Options
isVisibleToAll = Make group visible to all registered users.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index c948a8e..e1cfa90 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -85,6 +85,7 @@
private ListBox newChangeForAllNotInTarget;
private ListBox enableSignedPush;
private ListBox requireSignedPush;
+ private ListBox rejectImplicitMerges;
private NpTextBox maxObjectSizeLimit;
private Label effectiveMaxObjectSizeLimit;
private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
@@ -184,6 +185,7 @@
contributorAgreements.setEnabled(isOwner);
signedOffBy.setEnabled(isOwner);
requireChangeID.setEnabled(isOwner);
+ rejectImplicitMerges.setEnabled(isOwner);
maxObjectSizeLimit.setEnabled(isOwner);
if (pluginConfigWidgets != null) {
@@ -253,6 +255,10 @@
grid.add(Util.C.requireSignedPush(), requireSignedPush);
}
+ rejectImplicitMerges = newInheritedBooleanBox();
+ saveEnabler.listenTo(rejectImplicitMerges);
+ grid.addHtml(Util.C.rejectImplicitMerges(), rejectImplicitMerges);
+
maxObjectSizeLimit = new NpTextBox();
saveEnabler.listenTo(maxObjectSizeLimit);
effectiveMaxObjectSizeLimit = new Label();
@@ -383,6 +389,7 @@
setBool(enableSignedPush, result.enableSignedPush());
setBool(requireSignedPush, result.requireSignedPush());
}
+ setBool(rejectImplicitMerges, result.rejectImplicitMerges());
setSubmitType(result.submitType());
setState(result.state());
maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
@@ -659,7 +666,7 @@
ProjectApi.setConfig(getProjectKey(), descTxt.getText().trim(),
getBool(contributorAgreements), getBool(contentMerge),
getBool(signedOffBy), getBool(newChangeForAllNotInTarget), getBool(requireChangeID),
- esp, rsp,
+ esp, rsp, getBool(rejectImplicitMerges),
maxObjectSizeLimit.getText().trim(),
SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())),
ProjectState.valueOf(state.getValue(state.getSelectedIndex())),
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
index 96b25c5..9751b3b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -56,6 +56,9 @@
public final native InheritedBooleanInfo requireSignedPush()
/*-{ return this.require_signed_push; }-*/;
+ public final native InheritedBooleanInfo rejectImplicitMerges()
+ /*-{ return this.reject_implicit_merges; }-*/;
+
public final SubmitType submitType() {
return SubmitType.valueOf(submitTypeRaw());
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index 8e29e4c..10932bc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -118,6 +118,7 @@
InheritableBoolean requireChangeId,
InheritableBoolean enableSignedPush,
InheritableBoolean requireSignedPush,
+ InheritableBoolean rejectImplicitMerges,
String maxObjectSizeLimit,
SubmitType submitType, ProjectState state,
Map<String, Map<String, ConfigParameterValue>> pluginConfigValues,
@@ -135,6 +136,7 @@
if (requireSignedPush != null) {
in.setRequireSignedPush(requireSignedPush);
}
+ in.setRejectImplicitMerges(rejectImplicitMerges);
in.setMaxObjectSizeLimit(maxObjectSizeLimit);
in.setSubmitType(submitType);
in.setState(state);
@@ -267,6 +269,12 @@
private native void setRequireSignedPushRaw(String v)
/*-{ if(v)this.require_signed_push=v; }-*/;
+ final void setRejectImplicitMerges(InheritableBoolean v) {
+ setRejectImplicitMergesRaw(v.name());
+ }
+ private native void setRejectImplicitMergesRaw(String v)
+ /*-{ if(v)this.reject_implicit_merges=v; }-*/;
+
final native void setMaxObjectSizeLimit(String l)
/*-{ if(l)this.max_object_size_limit=l; }-*/;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
index 86e6894..74ebb25 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
@@ -99,6 +99,8 @@
protected InheritableBoolean enableSignedPush;
protected InheritableBoolean requireSignedPush;
+ protected InheritableBoolean rejectImplicitMerges;
+
protected Project() {
}
@@ -151,6 +153,10 @@
return maxObjectSizeLimit;
}
+ public InheritableBoolean getRejectImplicitMerges() {
+ return rejectImplicitMerges;
+ }
+
public void setUseContributorAgreements(final InheritableBoolean u) {
useContributorAgreements = u;
}
@@ -196,6 +202,10 @@
maxObjectSizeLimit = limit;
}
+ public void setRejectImplicitMerges(InheritableBoolean check) {
+ rejectImplicitMerges = check;
+ }
+
public SubmitType getSubmitType() {
return submitType;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index 64d9a9c..1721ae2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -125,6 +125,7 @@
private static final String KEY_CHECK_RECEIVED_OBJECTS = "checkReceivedObjects";
private static final String KEY_ENABLE_SIGNED_PUSH = "enableSignedPush";
private static final String KEY_REQUIRE_SIGNED_PUSH = "requireSignedPush";
+ private static final String KEY_REJECT_IMPLICIT_MERGES = "rejectImplicitMerges";
private static final String SUBMIT = "submit";
private static final String KEY_ACTION = "action";
@@ -491,6 +492,8 @@
p.setRequireSignedPush(getEnum(rc, RECEIVE, null,
KEY_REQUIRE_SIGNED_PUSH, InheritableBoolean.INHERIT));
p.setMaxObjectSizeLimit(rc.getString(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT));
+ p.setRejectImplicitMerges(getEnum(rc, RECEIVE, null,
+ KEY_REJECT_IMPLICIT_MERGES, InheritableBoolean.INHERIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
p.setUseContentMerge(getEnum(rc, SUBMIT, null, KEY_MERGE_CONTENT, InheritableBoolean.INHERIT));
@@ -940,6 +943,8 @@
p.getEnableSignedPush(), InheritableBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_PUSH,
p.getRequireSignedPush(), InheritableBoolean.INHERIT);
+ set(rc, RECEIVE, null, KEY_REJECT_IMPLICIT_MERGES,
+ p.getRejectImplicitMerges(), InheritableBoolean.INHERIT);
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.getUseContentMerge(), InheritableBoolean.INHERIT);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 2f73360..7fadae0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -1608,8 +1608,8 @@
rp.getRevWalk().sort(RevSort.TOPO);
rp.getRevWalk().sort(RevSort.REVERSE, true);
try {
- rp.getRevWalk().markStart(
- rp.getRevWalk().parseCommit(magicBranch.cmd.getNewId()));
+ RevCommit start = rp.getRevWalk().parseCommit(magicBranch.cmd.getNewId());
+ rp.getRevWalk().markStart(start);
if (magicBranch.baseCommit != null) {
logDebug("Marking {} base commits uninteresting",
magicBranch.baseCommit.size());
@@ -1635,6 +1635,15 @@
receiveConfig.getEffectiveMaxBatchChangesLimit(user);
int total = 0;
int alreadyTracked = 0;
+ boolean rejectImplicitMerges = start.getParentCount() == 1
+ && projectCache.get(project.getNameKey()).isRejectImplicitMerges();
+ Set<RevCommit> mergedParents;
+ if (rejectImplicitMerges) {
+ mergedParents = new HashSet<>();
+ } else {
+ mergedParents = null;
+ }
+
for (;;) {
RevCommit c = rp.getRevWalk().next();
if (c == null) {
@@ -1644,6 +1653,14 @@
String name = c.name();
groupCollector.visit(c);
Collection<Ref> existingRefs = existing.get(c);
+
+ if (rejectImplicitMerges) {
+ for (RevCommit p : c.getParents()) {
+ mergedParents.add(p);
+ }
+ mergedParents.remove(c);
+ }
+
if (!existingRefs.isEmpty()) { // Commit is already tracked.
alreadyTracked++;
// Corner cases where an existing commit might need a new group:
@@ -1717,6 +1734,10 @@
+ " lookups", total, alreadyTracked, newChanges.size(),
pending.size());
+ if (rejectImplicitMerges) {
+ rejectImplicitMerges(mergedParents);
+ }
+
for (Iterator<ChangeLookup> itr = pending.iterator(); itr.hasNext();) {
ChangeLookup p = itr.next();
if (newChangeIds.contains(p.changeKey)) {
@@ -1833,6 +1854,38 @@
}
}
+ private void rejectImplicitMerges(Set<RevCommit> mergedParents)
+ throws MissingObjectException, IncorrectObjectTypeException, IOException {
+ if (!mergedParents.isEmpty()) {
+ Ref targetRef = allRefs.get(magicBranch.ctl.getRefName());
+ if (targetRef != null) {
+ RevWalk rw = rp.getRevWalk();
+ RevCommit tip = rw.parseCommit(targetRef.getObjectId());
+ boolean containsImplicitMerges = true;
+ for (RevCommit p : mergedParents) {
+ containsImplicitMerges &= !rw.isMergedInto(p, tip);
+ }
+
+ if (containsImplicitMerges) {
+ rw.reset();
+ for (RevCommit p : mergedParents) {
+ rw.markStart(p);
+ }
+ rw.markUninteresting(tip);
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ rw.parseBody(c);
+ messages.add(new CommitValidationMessage(
+ "ERROR: Implicit Merge of " + c.abbreviate(7).name()
+ + " " + c.getShortMessage(), false));
+
+ }
+ reject(magicBranch.cmd, "implicit merges detected");
+ }
+ }
+ }
+ }
+
private void markHeadsAsUninteresting(RevWalk rw, @Nullable String forRef) {
int i = 0;
for (Ref ref : allRefs.values()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
index 570c2d2..a7ba217 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
@@ -39,7 +39,6 @@
import java.util.TreeMap;
public class ConfigInfoImpl extends ConfigInfo {
-
public ConfigInfoImpl(boolean serverEnableSignedPush,
ProjectControl control,
TransferConfig config,
@@ -60,6 +59,7 @@
new InheritedBooleanInfo();
InheritedBooleanInfo enableSignedPush = new InheritedBooleanInfo();
InheritedBooleanInfo requireSignedPush = new InheritedBooleanInfo();
+ InheritedBooleanInfo rejectImplicitMerges = new InheritedBooleanInfo();
useContributorAgreements.value = projectState.isUseContributorAgreements();
useSignedOffBy.value = projectState.isUseSignedOffBy();
@@ -77,6 +77,7 @@
p.getCreateNewChangeForAllNotInTarget();
enableSignedPush.configuredValue = p.getEnableSignedPush();
requireSignedPush.configuredValue = p.getRequireSignedPush();
+ rejectImplicitMerges.configuredValue = p.getRejectImplicitMerges();
ProjectState parentState = Iterables.getFirst(projectState
.parents(), null);
@@ -90,12 +91,14 @@
parentState.isCreateNewChangeForAllNotInTarget();
enableSignedPush.inheritedValue = projectState.isEnableSignedPush();
requireSignedPush.inheritedValue = projectState.isRequireSignedPush();
+ rejectImplicitMerges.inheritedValue = projectState.isRejectImplicitMerges();
}
this.useContributorAgreements = useContributorAgreements;
this.useSignedOffBy = useSignedOffBy;
this.useContentMerge = useContentMerge;
this.requireChangeId = requireChangeId;
+ this.rejectImplicitMerges = rejectImplicitMerges;
this.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
if (serverEnableSignedPush) {
this.enableSignedPush = enableSignedPush;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 9e5c35f..68d236e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -440,6 +440,15 @@
});
}
+ public boolean isRejectImplicitMerges() {
+ return getInheritableBoolean(new Function<Project, InheritableBoolean>() {
+ @Override
+ public InheritableBoolean apply(Project input) {
+ return input.getRejectImplicitMerges();
+ }
+ });
+ }
+
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = new LinkedHashMap<>();
for (ProjectState s : treeInOrder()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index b91818b..19b5b26 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -145,6 +145,10 @@
}
}
+ if (input.rejectImplicitMerges != null) {
+ p.setRejectImplicitMerges(input.rejectImplicitMerges);
+ }
+
if (input.maxObjectSizeLimit != null) {
p.setMaxObjectSizeLimit(input.maxObjectSizeLimit);
}