aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java241
1 files changed, 156 insertions, 85 deletions
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 d2075a70f2..7064f5a57a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -1,56 +1,26 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010-2014, Stefan Lay <stefan.lay@sap.com>
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2010, 2014, Stefan Lay <stefan.lay@sap.com>
+ * Copyright (C) 2016, 2021 Laurent Delaigue <laurent.delaigue@obeo.fr> and others
*
- * 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
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.api;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
@@ -61,17 +31,22 @@ import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitConfig;
import org.eclipse.jgit.lib.Config.ConfigEnum;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.ContentMergeStrategy;
import org.eclipse.jgit.merge.MergeConfig;
import org.eclipse.jgit.merge.MergeMessageFormatter;
import org.eclipse.jgit.merge.MergeStrategy;
@@ -98,7 +73,9 @@ public class MergeCommand extends GitCommand<MergeResult> {
private MergeStrategy mergeStrategy = MergeStrategy.RECURSIVE;
- private List<Ref> commits = new LinkedList<Ref>();
+ private ContentMergeStrategy contentStrategy;
+
+ private List<Ref> commits = new ArrayList<>();
private Boolean squash;
@@ -106,6 +83,24 @@ public class MergeCommand extends GitCommand<MergeResult> {
private String message;
+ private boolean insertChangeId;
+
+ private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
+ /**
+ * Values for the "merge.conflictStyle" git config.
+ *
+ * @since 5.12
+ */
+ public enum ConflictStyle {
+
+ /** "merge" style: only ours/theirs. This is the default. */
+ MERGE,
+
+ /** "diff3" style: ours/base/theirs. */
+ DIFF3
+ }
+
/**
* The modes available for fast forward merges corresponding to the
* <code>--ff</code>, <code>--no-ff</code> and <code>--ff-only</code>
@@ -128,10 +123,12 @@ public class MergeCommand extends GitCommand<MergeResult> {
*/
FF_ONLY;
+ @Override
public String toConfigValue() {
- return "--" + name().toLowerCase().replace('_', '-'); //$NON-NLS-1$
+ return "--" + name().toLowerCase(Locale.ROOT).replace('_', '-'); //$NON-NLS-1$
}
+ @Override
public boolean matchConfigValue(String in) {
if (StringUtils.isEmptyOrNull(in))
return false;
@@ -201,20 +198,24 @@ public class MergeCommand extends GitCommand<MergeResult> {
private Boolean commit;
/**
+ * Constructor for MergeCommand.
+ *
* @param repo
+ * the {@link org.eclipse.jgit.lib.Repository}
*/
protected MergeCommand(Repository repo) {
super(repo);
}
/**
- * Executes the {@code Merge} command with all the options and parameters
+ * {@inheritDoc}
+ * <p>
+ * Execute the {@code Merge} command with all the options and parameters
* collected by the setter methods (e.g. {@link #include(Ref)}) of this
* class. Each instance of this class should only be used for one invocation
* of the command. Don't call this method twice on an instance.
- *
- * @return the result of the merge
*/
+ @Override
@SuppressWarnings("boxing")
public MergeResult call() throws GitAPIException, NoHeadException,
ConcurrentRefUpdateException, CheckoutConflictException,
@@ -223,17 +224,15 @@ public class MergeCommand extends GitCommand<MergeResult> {
fallBackToConfiguration();
checkParameters();
- RevWalk revWalk = null;
DirCacheCheckout dco = null;
- try {
- Ref head = repo.getRef(Constants.HEAD);
+ try (RevWalk revWalk = new RevWalk(repo)) {
+ Ref head = repo.exactRef(Constants.HEAD);
if (head == null)
throw new NoHeadException(
JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
StringBuilder refLogMessage = new StringBuilder("merge "); //$NON-NLS-1$
// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
- revWalk = new RevWalk(repo);
// we know for now there is only one commit
Ref ref = commits.get(0);
@@ -241,7 +240,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
refLogMessage.append(ref.getName());
// handle annotated tags
- ref = repo.peel(ref);
+ ref = repo.getRefDatabase().peel(ref);
ObjectId objectId = ref.getPeeledObjectId();
if (objectId == null)
objectId = ref.getObjectId();
@@ -254,6 +253,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
dco = new DirCacheCheckout(repo,
repo.lockDirCache(), srcCommit.getTree());
dco.setFailOnConflict(true);
+ dco.setProgressMonitor(monitor);
dco.checkout();
RefUpdate refUpdate = repo
.updateRef(head.getTarget().getName());
@@ -284,6 +284,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
dco = new DirCacheCheckout(repo,
headCommit.getTree(), repo.lockDirCache(),
srcCommit.getTree());
+ dco.setProgressMonitor(monitor);
dco.setFailOnConflict(true);
dco.checkout();
String msg = null;
@@ -330,12 +331,14 @@ public class MergeCommand extends GitCommand<MergeResult> {
repo.writeSquashCommitMsg(squashMessage);
}
Merger merger = mergeStrategy.newMerger(repo);
+ merger.setProgressMonitor(monitor);
boolean noProblems;
Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
Map<String, MergeFailureReason> failingPaths = null;
List<String> unmergedPaths = null;
if (merger instanceof ResolveMerger) {
ResolveMerger resolveMerger = (ResolveMerger) merger;
+ resolveMerger.setContentMergeStrategy(contentStrategy);
resolveMerger.setCommitNames(new String[] {
"BASE", "HEAD", ref.getName() }); //$NON-NLS-1$ //$NON-NLS-2$
resolveMerger.setWorkingTreeIterator(new FileTreeIterator(repo));
@@ -344,6 +347,10 @@ public class MergeCommand extends GitCommand<MergeResult> {
.getMergeResults();
failingPaths = resolveMerger.getFailingPaths();
unmergedPaths = resolveMerger.getUnmergedPaths();
+ if (!resolveMerger.getModifiedFiles().isEmpty()) {
+ repo.fireEvent(new WorkingTreeModifiedEvent(
+ resolveMerger.getModifiedFiles(), null));
+ }
} else
noProblems = merger.merge(headCommit, srcCommit);
refLogMessage.append(": Merge made by "); //$NON-NLS-1$
@@ -357,6 +364,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
headCommit.getTree(), repo.lockDirCache(),
merger.getResultTreeId());
dco.setFailOnConflict(true);
+ dco.setProgressMonitor(monitor);
dco.checkout();
String msg = null;
@@ -372,9 +380,11 @@ public class MergeCommand extends GitCommand<MergeResult> {
try (Git git = new Git(getRepository())) {
newHeadId = git.commit()
.setReflogComment(refLogMessage.toString())
+ .setInsertChangeId(insertChangeId)
.call().getId();
}
mergeStatus = MergeStatus.MERGED;
+ getRepository().autoGC(monitor);
}
if (commit && squash) {
msg = JGitText.get().squashCommitNotUpdatingHEAD;
@@ -385,27 +395,27 @@ public class MergeCommand extends GitCommand<MergeResult> {
new ObjectId[] { headCommit.getId(),
srcCommit.getId() }, mergeStatus,
mergeStrategy, null, msg);
- } else {
- if (failingPaths != null) {
- repo.writeMergeCommitMsg(null);
- repo.writeMergeHeads(null);
- return new MergeResult(null, merger.getBaseCommitId(),
- new ObjectId[] {
- headCommit.getId(), srcCommit.getId() },
- MergeStatus.FAILED, mergeStrategy,
- lowLevelResults, failingPaths, null);
- } else {
- String mergeMessageWithConflicts = new MergeMessageFormatter()
- .formatWithConflicts(mergeMessage,
- unmergedPaths);
- repo.writeMergeCommitMsg(mergeMessageWithConflicts);
- return new MergeResult(null, merger.getBaseCommitId(),
- new ObjectId[] { headCommit.getId(),
- srcCommit.getId() },
- MergeStatus.CONFLICTING, mergeStrategy,
- lowLevelResults, null);
- }
}
+ if (failingPaths != null) {
+ repo.writeMergeCommitMsg(null);
+ repo.writeMergeHeads(null);
+ return new MergeResult(null, merger.getBaseCommitId(),
+ new ObjectId[] { headCommit.getId(),
+ srcCommit.getId() },
+ MergeStatus.FAILED, mergeStrategy, lowLevelResults,
+ failingPaths, null);
+ }
+ CommitConfig cfg = repo.getConfig().get(CommitConfig.KEY);
+ char commentChar = cfg.getCommentChar(message);
+ String mergeMessageWithConflicts = new MergeMessageFormatter()
+ .formatWithConflicts(mergeMessage, unmergedPaths,
+ commentChar);
+ repo.writeMergeCommitMsg(mergeMessageWithConflicts);
+ return new MergeResult(null, merger.getBaseCommitId(),
+ new ObjectId[] { headCommit.getId(),
+ srcCommit.getId() },
+ MergeStatus.CONFLICTING, mergeStrategy, lowLevelResults,
+ null);
}
} catch (org.eclipse.jgit.errors.CheckoutConflictException e) {
List<String> conflicts = (dco == null) ? Collections
@@ -416,9 +426,6 @@ public class MergeCommand extends GitCommand<MergeResult> {
MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfMergeCommand,
e), e);
- } finally {
- if (revWalk != null)
- revWalk.close();
}
}
@@ -438,8 +445,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
}
/**
- * Use values from the configuation if they have not been explicitly defined
- * via the setters
+ * Use values from the configuration if they have not been explicitly
+ * defined via the setters
*/
private void fallBackToConfiguration() {
MergeConfig config = MergeConfig.getConfigForCurrentBranch(repo);
@@ -475,9 +482,10 @@ public class MergeCommand extends GitCommand<MergeResult> {
}
/**
+ * Set merge strategy
*
* @param mergeStrategy
- * the {@link MergeStrategy} to be used
+ * the {@link org.eclipse.jgit.merge.MergeStrategy} to be used
* @return {@code this}
*/
public MergeCommand setStrategy(MergeStrategy mergeStrategy) {
@@ -487,6 +495,24 @@ public class MergeCommand extends GitCommand<MergeResult> {
}
/**
+ * Sets the content merge strategy to use if the
+ * {@link #setStrategy(MergeStrategy) merge strategy} is "resolve" or
+ * "recursive".
+ *
+ * @param strategy
+ * the {@link ContentMergeStrategy} to be used
+ * @return {@code this}
+ * @since 5.12
+ */
+ public MergeCommand setContentMergeStrategy(ContentMergeStrategy strategy) {
+ checkCallable();
+ this.contentStrategy = strategy;
+ return this;
+ }
+
+ /**
+ * Reference to a commit to be merged with the current head
+ *
* @param aCommit
* a reference to a commit which is merged with the current head
* @return {@code this}
@@ -498,6 +524,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
}
/**
+ * Id of a commit which is to be merged with the current head
+ *
* @param aCommit
* the Id of a commit which is merged with the current head
* @return {@code this}
@@ -507,8 +535,10 @@ public class MergeCommand extends GitCommand<MergeResult> {
}
/**
+ * Include a commit
+ *
* @param name
- * a name given to the commit
+ * a name of a {@code Ref} pointing to the commit
* @param aCommit
* the Id of a commit which is merged with the current head
* @return {@code this}
@@ -524,9 +554,10 @@ public class MergeCommand extends GitCommand<MergeResult> {
* HEAD. Otherwise, perform the merge and commit the result.
* <p>
* In case the merge was successful but this flag was set to
- * <code>true</code> a {@link MergeResult} with status
- * {@link MergeStatus#MERGED_SQUASHED} or
- * {@link MergeStatus#FAST_FORWARD_SQUASHED} is returned.
+ * <code>true</code> a {@link org.eclipse.jgit.api.MergeResult} with status
+ * {@link org.eclipse.jgit.api.MergeResult.MergeStatus#MERGED_SQUASHED} or
+ * {@link org.eclipse.jgit.api.MergeResult.MergeStatus#FAST_FORWARD_SQUASHED}
+ * is returned.
*
* @param squash
* whether to squash commits or not
@@ -543,12 +574,15 @@ public class MergeCommand extends GitCommand<MergeResult> {
* Sets the fast forward mode.
*
* @param fastForwardMode
- * corresponds to the --ff/--no-ff/--ff-only options. --ff is the
- * default option.
+ * corresponds to the --ff/--no-ff/--ff-only options. If
+ * {@code null} use the value of the {@code merge.ff} option
+ * configured in git config. If this option is not configured
+ * --ff is the built-in default.
* @return {@code this}
* @since 2.2
*/
- public MergeCommand setFastForward(FastForwardMode fastForwardMode) {
+ public MergeCommand setFastForward(
+ @Nullable FastForwardMode fastForwardMode) {
checkCallable();
this.fastForwardMode = fastForwardMode;
return this;
@@ -562,9 +596,11 @@ public class MergeCommand extends GitCommand<MergeResult> {
* <code>true</code> if this command should commit (this is the
* default behavior). <code>false</code> if this command should
* not commit. In case the merge was successful but this flag was
- * set to <code>false</code> a {@link MergeResult} with type
- * {@link MergeResult} with status
- * {@link MergeStatus#MERGED_NOT_COMMITTED} is returned
+ * set to <code>false</code> a
+ * {@link org.eclipse.jgit.api.MergeResult} with type
+ * {@link org.eclipse.jgit.api.MergeResult} with status
+ * {@link org.eclipse.jgit.api.MergeResult.MergeStatus#MERGED_NOT_COMMITTED}
+ * is returned
* @return {@code this}
* @since 3.0
*/
@@ -586,4 +622,39 @@ public class MergeCommand extends GitCommand<MergeResult> {
this.message = message;
return this;
}
+
+ /**
+ * If set to true a change id will be inserted into the commit message
+ *
+ * An existing change id is not replaced. An initial change id (I000...)
+ * will be replaced by the change id.
+ *
+ * @param insertChangeId
+ * whether to insert a change id
+ * @return {@code this}
+ * @since 5.0
+ */
+ public MergeCommand setInsertChangeId(boolean insertChangeId) {
+ checkCallable();
+ this.insertChangeId = insertChangeId;
+ return this;
+ }
+
+ /**
+ * The progress monitor associated with the diff operation. By default, this
+ * is set to <code>NullProgressMonitor</code>
+ *
+ * @see NullProgressMonitor
+ * @param monitor
+ * A progress monitor
+ * @return this instance
+ * @since 4.2
+ */
+ public MergeCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
+ this.monitor = monitor;
+ return this;
+ }
}