diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index 630bd7dc0..d30c1911d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -66,6 +66,7 @@ import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.junit.MockSystemReader; +import org.eclipse.jgit.merge.MergeConfig; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; import org.junit.After; @@ -334,19 +335,27 @@ public void testGetFastForwardMergeoptions() throws ConfigInvalidException { assertSame(FastForwardMode.FF, c.getEnum( ConfigConstants.CONFIG_BRANCH_SECTION, "side", ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF)); + MergeConfig mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode()); c = parse("[branch \"side\"]\n\tmergeoptions = --ff-only\n"); assertSame(FastForwardMode.FF_ONLY, c.getEnum( ConfigConstants.CONFIG_BRANCH_SECTION, "side", ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF_ONLY)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode()); c = parse("[branch \"side\"]\n\tmergeoptions = --ff\n"); assertSame(FastForwardMode.FF, c.getEnum( ConfigConstants.CONFIG_BRANCH_SECTION, "side", ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode()); c = parse("[branch \"side\"]\n\tmergeoptions = --no-ff\n"); assertSame(FastForwardMode.NO_FF, c.getEnum( ConfigConstants.CONFIG_BRANCH_SECTION, "side", ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.NO_FF)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode()); } @Test @@ -368,18 +377,56 @@ public void testGetFastForwardMerge() throws ConfigInvalidException { assertSame(FastForwardMode.Merge.TRUE, c.getEnum( ConfigConstants.CONFIG_KEY_MERGE, null, ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE)); + MergeConfig mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode()); c = parse("[merge]\n\tff = only\n"); assertSame(FastForwardMode.Merge.ONLY, c.getEnum( ConfigConstants.CONFIG_KEY_MERGE, null, ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.ONLY)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode()); c = parse("[merge]\n\tff = true\n"); assertSame(FastForwardMode.Merge.TRUE, c.getEnum( ConfigConstants.CONFIG_KEY_MERGE, null, ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode()); c = parse("[merge]\n\tff = false\n"); assertSame(FastForwardMode.Merge.FALSE, c.getEnum( ConfigConstants.CONFIG_KEY_MERGE, null, ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.FALSE)); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode()); + } + + @Test + public void testCombinedMergeOptions() throws ConfigInvalidException { + Config c = new Config(null); // not set + MergeConfig mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode()); + assertTrue(mergeConfig.isCommit()); + assertFalse(mergeConfig.isSquash()); + // branch..mergeoptions should win over merge.ff + c = parse("[merge]\n\tff = false\n" + + "[branch \"side\"]\n\tmergeoptions = --ff-only\n"); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode()); + assertTrue(mergeConfig.isCommit()); + assertFalse(mergeConfig.isSquash()); + // merge.ff used for ff setting if not set via mergeoptions + c = parse("[merge]\n\tff = only\n" + + "[branch \"side\"]\n\tmergeoptions = --squash\n"); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode()); + assertTrue(mergeConfig.isCommit()); + assertTrue(mergeConfig.isSquash()); + // mergeoptions wins if it has ff options amongst other options + c = parse("[merge]\n\tff = false\n" + + "[branch \"side\"]\n\tmergeoptions = --ff-only --no-commit\n"); + mergeConfig = c.get(MergeConfig.getParser("side")); + assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode()); + assertFalse(mergeConfig.isCommit()); + assertFalse(mergeConfig.isSquash()); } @Test diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java index 78b260df7..d8e73e391 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -72,6 +72,7 @@ import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.MergeConfig; import org.eclipse.jgit.merge.MergeMessageFormatter; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.Merger; @@ -99,9 +100,9 @@ public class MergeCommand extends GitCommand { private List commits = new LinkedList(); - private boolean squash; + private Boolean squash; - private FastForwardMode fastForwardMode = FastForwardMode.FF; + private FastForwardMode fastForwardMode; /** * The modes available for fast forward merges corresponding to the @@ -195,7 +196,7 @@ public static FastForwardMode valueOf(FastForwardMode.Merge ffMode) { } } - private boolean commit = true; + private Boolean commit; /** * @param repo @@ -212,10 +213,12 @@ protected MergeCommand(Repository repo) { * * @return the result of the merge */ + @SuppressWarnings("boxing") public MergeResult call() throws GitAPIException, NoHeadException, ConcurrentRefUpdateException, CheckoutConflictException, InvalidMergeHeadsException, WrongRepositoryStateException, NoMessageException { checkCallable(); + fallBackToConfiguration(); checkParameters(); RevWalk revWalk = null; @@ -413,7 +416,7 @@ public MergeResult call() throws GitAPIException, NoHeadException, } private void checkParameters() throws InvalidMergeHeadsException { - if (squash && fastForwardMode == FastForwardMode.NO_FF) { + if (squash.booleanValue() && fastForwardMode == FastForwardMode.NO_FF) { throw new JGitInternalException( JGitText.get().cannotCombineSquashWithNoff); } @@ -427,6 +430,20 @@ private void checkParameters() throws InvalidMergeHeadsException { Integer.valueOf(commits.size()))); } + /** + * Use values from the configuation if they have not been explicitly defined + * via the setters + */ + private void fallBackToConfiguration() { + MergeConfig config = MergeConfig.getConfigForCurrentBranch(repo); + if (squash == null) + squash = Boolean.valueOf(config.isSquash()); + if (commit == null) + commit = Boolean.valueOf(config.isCommit()); + if (fastForwardMode == null) + fastForwardMode = config.getFastForwardMode(); + } + private void updateHead(StringBuilder refLogMessage, ObjectId newHeadId, ObjectId oldHeadID) throws IOException, ConcurrentRefUpdateException { @@ -511,7 +528,7 @@ public MergeCommand include(String name, AnyObjectId commit) { */ public MergeCommand setSquash(boolean squash) { checkCallable(); - this.squash = squash; + this.squash = Boolean.valueOf(squash); return this; } @@ -545,7 +562,7 @@ public MergeCommand setFastForward(FastForwardMode fastForwardMode) { * @since 3.0 */ public MergeCommand setCommit(boolean commit) { - this.commit = commit; + this.commit = Boolean.valueOf(commit); return this; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java new file mode 100644 index 000000000..9125ddfd6 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2014 Konrad Kügler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *******************************************************************************/ +package org.eclipse.jgit.merge; + +import java.io.IOException; + +import org.eclipse.jgit.api.MergeCommand.FastForwardMode; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.lib.Repository; + +/** + * Holds configuration for merging into a given branch + * + * @since 3.3 + */ +public class MergeConfig { + + /** + * @param repo + * @return merge configuration for the current branch of the repository + */ + public static MergeConfig getConfigForCurrentBranch(Repository repo) { + try { + String branch = repo.getBranch(); + if (branch != null) + return repo.getConfig().get(getParser(branch)); + } catch (IOException e) { + // ignore + } + // use defaults if branch can't be determined + return new MergeConfig(); + } + + /** + * @param branch + * short branch name to get the configuration for, as returned + * e.g. by {@link Repository#getBranch()} + * @return a parser for use with {@link Config#get(SectionParser)} + */ + public static final SectionParser getParser( + final String branch) { + return new MergeConfigSectionParser(branch); + } + + private final FastForwardMode fastForwardMode; + + private final boolean squash; + + private final boolean commit; + + private MergeConfig(String branch, Config config) { + String[] mergeOptions = getMergeOptions(branch, config); + fastForwardMode = getFastForwardMode(config, mergeOptions); + squash = isMergeConfigOptionSet("--squash", mergeOptions); //$NON-NLS-1$ + commit = !isMergeConfigOptionSet("--no-commit", mergeOptions); //$NON-NLS-1$ + } + + private MergeConfig() { + fastForwardMode = FastForwardMode.FF; + squash = false; + commit = true; + } + + /** + * @return the fast forward mode configured for this branch + */ + public FastForwardMode getFastForwardMode() { + return fastForwardMode; + } + + /** + * @return true if merges into this branch are configured to be squash + * merges, false otherwise + */ + public boolean isSquash() { + return squash; + } + + /** + * @return false if --no-commit is configured for this branch, true + * otherwise (event if --squash is configured) + */ + public boolean isCommit() { + return commit; + } + + private static FastForwardMode getFastForwardMode(Config config, + String[] mergeOptions) { + for (String option : mergeOptions) { + for (FastForwardMode mode : FastForwardMode.values()) + if (mode.matchConfigValue(option)) + return mode; + } + FastForwardMode ffmode = FastForwardMode.valueOf(config.getEnum( + ConfigConstants.CONFIG_KEY_MERGE, null, + ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE)); + return ffmode; + } + + private static boolean isMergeConfigOptionSet(String optionToLookFor, + String[] mergeOptions) { + for (String option : mergeOptions) { + if (optionToLookFor.equals(option)) + return true; + } + return false; + } + + private static String[] getMergeOptions(String branch, Config config) { + String mergeOptions = config.getString( + ConfigConstants.CONFIG_BRANCH_SECTION, branch, + ConfigConstants.CONFIG_KEY_MERGEOPTIONS); + if (mergeOptions != null) + return mergeOptions.split("\\s"); //$NON-NLS-1$ + else + return new String[0]; + } + + private static class MergeConfigSectionParser implements + SectionParser { + + private final String branch; + + public MergeConfigSectionParser(String branch) { + this.branch = branch; + } + + public MergeConfig parse(Config cfg) { + return new MergeConfig(branch, cfg); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MergeConfigSectionParser) + return branch.equals(((MergeConfigSectionParser) obj).branch); + else + return false; + } + + @Override + public int hashCode() { + return branch.hashCode(); + } + + } + +}