diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2021-05-12 08:59:07 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2021-05-12 08:59:07 +0200 |
commit | 1aa3cf7f416658a4fafd592732ba70f5a1aee19e (patch) | |
tree | 275b1176d04d102832641292a765d1a8f3e1b3a2 /org.eclipse.jgit/src/org/eclipse/jgit/api | |
parent | b6d4844b5d60f63a46f2b8b42189f70de653b385 (diff) | |
parent | fe3034d5b90a8a1f2592a40a670705fbd7305158 (diff) | |
download | jgit-1aa3cf7f416658a4fafd592732ba70f5a1aee19e.tar.gz jgit-1aa3cf7f416658a4fafd592732ba70f5a1aee19e.zip |
Merge branch 'master' into next
* master: (34 commits)
Remove texts which were added by mistake in 00386272
Fix formatting which was broken in 00386272
LockFile: create OutputStream only when needed
Add a cgit interoperability test for LockFile
Add TemporaryBuffer.toString(int limit)
LockFile: create OutputStream only when needed
Prepare 5.12.0-SNAPSHOT builds
JGit v5.12.0.202105051250-m2
Update jetty to 9.4.40.v20210413
[releng] Update eclipse-jarsigner-plugin to 1.3.1
Implement ours/theirs content conflict resolution
ssh: ensure list is modifiable before using Iterator.remove().
Update orbit to S20210406213021 and add 4.20-staging target
Fix typo in test method name
Allow file mode conflicts in virtual base commit on recursive merge.
sshd: don't lock the known_hosts files on reading
Allow info messages in UsernamePasswordCredentialsProvider
ssh config: do environment variable replacement
sshd: implement server-sig-algs SSH extension (client side)
Upgrade ecj to 3.25.0
...
Change-Id: Ibc39a9c4e431d15b67ab4a307241f47a7f3740a9
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/api')
6 files changed, 302 insertions, 134 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java index 5d0154c6dc..7922f9e729 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others + * Copyright (C) 2010, 2021 Christian Halstrick <christian.halstrick@sap.com> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -13,6 +13,7 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.GitAPIException; @@ -35,9 +36,12 @@ import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.ContentMergeStrategy; import org.eclipse.jgit.merge.MergeMessageFormatter; import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.merge.Merger; import org.eclipse.jgit.merge.ResolveMerger; +import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.FileTreeIterator; @@ -61,6 +65,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { private MergeStrategy strategy = MergeStrategy.RECURSIVE; + private ContentMergeStrategy contentStrategy; + private Integer mainlineParentNumber; private boolean noCommit = false; @@ -121,16 +127,30 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { String cherryPickName = srcCommit.getId().abbreviate(7).name() + " " + srcCommit.getShortMessage(); //$NON-NLS-1$ - ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo); - merger.setWorkingTreeIterator(new FileTreeIterator(repo)); - merger.setBase(srcParent.getTree()); - merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$ - cherryPickName }); - if (merger.merge(newHead, srcCommit)) { - if (!merger.getModifiedFiles().isEmpty()) { + Merger merger = strategy.newMerger(repo); + merger.setProgressMonitor(monitor); + boolean noProblems; + 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", ourName, cherryPickName }); //$NON-NLS-1$ + resolveMerger + .setWorkingTreeIterator(new FileTreeIterator(repo)); + resolveMerger.setBase(srcParent.getTree()); + noProblems = merger.merge(newHead, srcCommit); + failingPaths = resolveMerger.getFailingPaths(); + unmergedPaths = resolveMerger.getUnmergedPaths(); + if (!resolveMerger.getModifiedFiles().isEmpty()) { repo.fireEvent(new WorkingTreeModifiedEvent( - merger.getModifiedFiles(), null)); + resolveMerger.getModifiedFiles(), null)); } + } else { + noProblems = merger.merge(newHead, srcCommit); + } + if (noProblems) { if (AnyObjectId.isEqual(newHead.getTree().getId(), merger.getResultTreeId())) { continue; @@ -153,24 +173,26 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { } cherryPickedRefs.add(src); } else { - if (merger.failed()) { - return new CherryPickResult(merger.getFailingPaths()); + if (failingPaths != null && !failingPaths.isEmpty()) { + return new CherryPickResult(failingPaths); } // there are merge conflicts - String message = new MergeMessageFormatter() + String message; + if (unmergedPaths != null) { + message = new MergeMessageFormatter() .formatWithConflicts(srcCommit.getFullMessage(), - merger.getUnmergedPaths()); + unmergedPaths); + } else { + message = srcCommit.getFullMessage(); + } if (!noCommit) { repo.writeCherryPickHead(srcCommit.getId()); } repo.writeMergeCommitMsg(message); - repo.fireEvent(new WorkingTreeModifiedEvent( - merger.getModifiedFiles(), null)); - return CherryPickResult.CONFLICT; } } @@ -291,6 +313,22 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { } /** + * 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 CherryPickCommand setContentMergeStrategy( + ContentMergeStrategy strategy) { + this.contentStrategy = strategy; + return this; + } + + /** * Set the (1-based) parent number to diff against * * @param mainlineParentNumber diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 041276c0f8..37f1d482aa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -20,6 +20,7 @@ import java.util.LinkedList; import java.util.List; import org.eclipse.jgit.api.errors.AbortedByHookException; +import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.EmptyCommitException; import org.eclipse.jgit.api.errors.GitAPIException; @@ -36,6 +37,8 @@ import org.eclipse.jgit.dircache.DirCacheBuildIterator; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.UnmergedPathException; import org.eclipse.jgit.hooks.CommitMsgHook; import org.eclipse.jgit.hooks.Hooks; @@ -67,6 +70,8 @@ import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.util.ChangeIdUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A class used to execute a {@code Commit} command. It has setters for all @@ -78,6 +83,9 @@ import org.eclipse.jgit.util.ChangeIdUtil; * >Git documentation about Commit</a> */ public class CommitCommand extends GitCommand<RevCommit> { + private static final Logger log = LoggerFactory + .getLogger(CommitCommand.class); + private PersonIdent author; private PersonIdent committer; @@ -173,8 +181,7 @@ public class CommitCommand extends GitCommand<RevCommit> { if (all && !repo.isBare()) { try (Git git = new Git(repo)) { - git.add() - .addFilepattern(".") //$NON-NLS-1$ + git.add().addFilepattern(".") //$NON-NLS-1$ .setUpdate(true).call(); } catch (NoFilepatternException e) { // should really not happen @@ -212,7 +219,7 @@ public class CommitCommand extends GitCommand<RevCommit> { .setCommitMessage(message).call(); } - // lock the index + RevCommit revCommit; DirCache index = repo.lockDirCache(); try (ObjectInserter odi = repo.newObjectInserter()) { if (!only.isEmpty()) @@ -226,100 +233,37 @@ public class CommitCommand extends GitCommand<RevCommit> { if (insertChangeId) insertChangeId(indexTreeId); - // Check for empty commits - if (headId != null && !allowEmpty.booleanValue()) { - RevCommit headCommit = rw.parseCommit(headId); - headCommit.getTree(); - if (indexTreeId.equals(headCommit.getTree())) { - throw new EmptyCommitException( - JGitText.get().emptyCommit); - } - } + checkIfEmpty(rw, headId, indexTreeId); // Create a Commit object, populate it and write it CommitBuilder commit = new CommitBuilder(); commit.setCommitter(committer); commit.setAuthor(author); commit.setMessage(message); - commit.setParentIds(parents); commit.setTreeId(indexTreeId); if (signCommit.booleanValue()) { - if (gpgSigner == null) { - throw new ServiceUnavailableException( - JGitText.get().signingServiceUnavailable); - } - if (gpgSigner instanceof GpgObjectSigner) { - ((GpgObjectSigner) gpgSigner).signObject(commit, - signingKey, committer, credentialsProvider, - gpgConfig); - } else { - if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) { - throw new UnsupportedSigningFormatException(JGitText - .get().onlyOpenPgpSupportedForSigning); - } - gpgSigner.sign(commit, signingKey, committer, - credentialsProvider); - } + sign(commit); } ObjectId commitId = odi.insert(commit); odi.flush(); + revCommit = rw.parseCommit(commitId); - RevCommit revCommit = rw.parseCommit(commitId); - RefUpdate ru = repo.updateRef(Constants.HEAD); - ru.setNewObjectId(commitId); - if (!useDefaultReflogMessage) { - ru.setRefLogMessage(reflogComment, false); - } else { - String prefix = amend ? "commit (amend): " //$NON-NLS-1$ - : parents.isEmpty() ? "commit (initial): " //$NON-NLS-1$ - : "commit: "; //$NON-NLS-1$ - ru.setRefLogMessage(prefix + revCommit.getShortMessage(), - false); - } - if (headId != null) - ru.setExpectedOldObjectId(headId); - else - ru.setExpectedOldObjectId(ObjectId.zeroId()); - Result rc = ru.forceUpdate(); - switch (rc) { - case NEW: - case FORCED: - case FAST_FORWARD: { - setCallable(false); - if (state == RepositoryState.MERGING_RESOLVED - || isMergeDuringRebase(state)) { - // Commit was successful. Now delete the files - // used for merge commits - repo.writeMergeCommitMsg(null); - repo.writeMergeHeads(null); - } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) { - repo.writeMergeCommitMsg(null); - repo.writeCherryPickHead(null); - } else if (state == RepositoryState.REVERTING_RESOLVED) { - repo.writeMergeCommitMsg(null); - repo.writeRevertHead(null); - } - Hooks.postCommit(repo, - hookOutRedirect.get(PostCommitHook.NAME), - hookErrRedirect.get(PostCommitHook.NAME)).call(); - - return revCommit; - } - case REJECTED: - case LOCK_FAILURE: - throw new ConcurrentRefUpdateException( - JGitText.get().couldNotLockHEAD, ru.getRef(), rc); - default: - throw new JGitInternalException(MessageFormat.format( - JGitText.get().updatingRefFailed, Constants.HEAD, - commitId.toString(), rc)); - } + updateRef(state, headId, revCommit, commitId); } finally { index.unlock(); } + try { + Hooks.postCommit(repo, hookOutRedirect.get(PostCommitHook.NAME), + hookErrRedirect.get(PostCommitHook.NAME)).call(); + } catch (Exception e) { + log.error(MessageFormat.format( + JGitText.get().postCommitHookFailed, e.getMessage()), + e); + } + return revCommit; } catch (UnmergedPathException e) { throw new UnmergedPathsException(e); } catch (IOException e) { @@ -328,6 +272,89 @@ public class CommitCommand extends GitCommand<RevCommit> { } } + private void checkIfEmpty(RevWalk rw, ObjectId headId, ObjectId indexTreeId) + throws EmptyCommitException, MissingObjectException, + IncorrectObjectTypeException, IOException { + if (headId != null && !allowEmpty.booleanValue()) { + RevCommit headCommit = rw.parseCommit(headId); + headCommit.getTree(); + if (indexTreeId.equals(headCommit.getTree())) { + throw new EmptyCommitException(JGitText.get().emptyCommit); + } + } + } + + private void sign(CommitBuilder commit) throws ServiceUnavailableException, + CanceledException, UnsupportedSigningFormatException { + if (gpgSigner == null) { + throw new ServiceUnavailableException( + JGitText.get().signingServiceUnavailable); + } + if (gpgSigner instanceof GpgObjectSigner) { + ((GpgObjectSigner) gpgSigner).signObject(commit, + signingKey, committer, credentialsProvider, + gpgConfig); + } else { + if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) { + throw new UnsupportedSigningFormatException(JGitText + .get().onlyOpenPgpSupportedForSigning); + } + gpgSigner.sign(commit, signingKey, committer, + credentialsProvider); + } + } + + private void updateRef(RepositoryState state, ObjectId headId, + RevCommit revCommit, ObjectId commitId) + throws ConcurrentRefUpdateException, IOException { + RefUpdate ru = repo.updateRef(Constants.HEAD); + ru.setNewObjectId(commitId); + if (!useDefaultReflogMessage) { + ru.setRefLogMessage(reflogComment, false); + } else { + String prefix = amend ? "commit (amend): " //$NON-NLS-1$ + : parents.isEmpty() ? "commit (initial): " //$NON-NLS-1$ + : "commit: "; //$NON-NLS-1$ + ru.setRefLogMessage(prefix + revCommit.getShortMessage(), + false); + } + if (headId != null) { + ru.setExpectedOldObjectId(headId); + } else { + ru.setExpectedOldObjectId(ObjectId.zeroId()); + } + Result rc = ru.forceUpdate(); + switch (rc) { + case NEW: + case FORCED: + case FAST_FORWARD: { + setCallable(false); + if (state == RepositoryState.MERGING_RESOLVED + || isMergeDuringRebase(state)) { + // Commit was successful. Now delete the files + // used for merge commits + repo.writeMergeCommitMsg(null); + repo.writeMergeHeads(null); + } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) { + repo.writeMergeCommitMsg(null); + repo.writeCherryPickHead(null); + } else if (state == RepositoryState.REVERTING_RESOLVED) { + repo.writeMergeCommitMsg(null); + repo.writeRevertHead(null); + } + break; + } + case REJECTED: + case LOCK_FAILURE: + throw new ConcurrentRefUpdateException( + JGitText.get().couldNotLockHEAD, ru.getRef(), rc); + default: + throw new JGitInternalException(MessageFormat.format( + JGitText.get().updatingRefFailed, Constants.HEAD, + commitId.toString(), rc)); + } + } + private void insertChangeId(ObjectId treeId) { ObjectId firstParentId = null; if (!parents.isEmpty()) 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 d88f4ec561..c611f915ae 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -1,7 +1,7 @@ /* * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> - * Copyright (C) 2010-2014, Stefan Lay <stefan.lay@sap.com> - * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others + * 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 v. 1.0 which is available at @@ -45,6 +45,7 @@ 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; @@ -71,6 +72,8 @@ public class MergeCommand extends GitCommand<MergeResult> { private MergeStrategy mergeStrategy = MergeStrategy.RECURSIVE; + private ContentMergeStrategy contentStrategy; + private List<Ref> commits = new LinkedList<>(); private Boolean squash; @@ -320,6 +323,7 @@ public class MergeCommand extends GitCommand<MergeResult> { 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)); @@ -473,6 +477,22 @@ 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 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java index 449250890c..281ecfd011 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -1,7 +1,7 @@ /* * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> - * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others + * 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 v. 1.0 which is available at @@ -43,6 +43,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode; +import org.eclipse.jgit.merge.ContentMergeStrategy; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; @@ -69,6 +70,8 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { private MergeStrategy strategy = MergeStrategy.RECURSIVE; + private ContentMergeStrategy contentStrategy; + private TagOpt tagOption; private FastForwardMode fastForwardMode; @@ -275,8 +278,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { JGitText.get().pullTaskName)); // we check the updates to see which of the updated branches - // corresponds - // to the remote branch name + // corresponds to the remote branch name AnyObjectId commitToMerge; if (isRemote) { Ref r = null; @@ -354,8 +356,11 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { } RebaseCommand rebase = new RebaseCommand(repo); RebaseResult rebaseRes = rebase.setUpstream(commitToMerge) - .setUpstreamName(upstreamName).setProgressMonitor(monitor) - .setOperation(Operation.BEGIN).setStrategy(strategy) + .setProgressMonitor(monitor) + .setUpstreamName(upstreamName) + .setOperation(Operation.BEGIN) + .setStrategy(strategy) + .setContentMergeStrategy(contentStrategy) .setPreserveMerges( pullRebaseMode == BranchRebaseMode.PRESERVE) .call(); @@ -363,7 +368,9 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { } else { MergeCommand merge = new MergeCommand(repo); MergeResult mergeRes = merge.include(upstreamName, commitToMerge) - .setStrategy(strategy).setProgressMonitor(monitor) + .setProgressMonitor(monitor) + .setStrategy(strategy) + .setContentMergeStrategy(contentStrategy) .setFastForward(getFastForwardMode()).call(); monitor.update(1); result = new PullResult(fetchRes, remote, mergeRes); @@ -442,6 +449,21 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> { } /** + * 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 PullCommand setContentMergeStrategy(ContentMergeStrategy strategy) { + this.contentStrategy = strategy; + return this; + } + + /** * Set the specification of annotated tag behavior during fetch * * @param tagOpt diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 836175dcea..a26ffc2e66 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler@sap.com> - * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others + * 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 v. 1.0 which is available at @@ -65,6 +65,7 @@ import org.eclipse.jgit.lib.Ref; 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.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevSort; @@ -212,6 +213,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { private MergeStrategy strategy = MergeStrategy.RECURSIVE; + private ContentMergeStrategy contentStrategy; + private boolean preserveMerges = false; /** @@ -501,8 +504,11 @@ public class RebaseCommand extends GitCommand<RebaseResult> { String ourCommitName = getOurCommitName(); try (Git git = new Git(repo)) { CherryPickResult cherryPickResult = git.cherryPick() - .include(commitToPick).setOurCommitName(ourCommitName) - .setReflogPrefix(REFLOG_PREFIX).setStrategy(strategy) + .include(commitToPick) + .setOurCommitName(ourCommitName) + .setReflogPrefix(REFLOG_PREFIX) + .setStrategy(strategy) + .setContentMergeStrategy(contentStrategy) .call(); switch (cherryPickResult.getStatus()) { case FAILED: @@ -556,7 +562,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { .include(commitToPick) .setOurCommitName(ourCommitName) .setReflogPrefix(REFLOG_PREFIX) - .setStrategy(strategy); + .setStrategy(strategy) + .setContentMergeStrategy(contentStrategy); if (isMerge) { pickCommand.setMainlineParentNumber(1); // We write a MERGE_HEAD and later commit explicitly @@ -592,6 +599,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { MergeCommand merge = git.merge() .setFastForward(MergeCommand.FastForwardMode.NO_FF) .setProgressMonitor(monitor) + .setStrategy(strategy) + .setContentMergeStrategy(contentStrategy) .setCommit(false); for (int i = 1; i < commitToPick.getParentCount(); i++) merge.include(newParents.get(i)); @@ -1137,7 +1146,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } private List<RevCommit> calculatePickList(RevCommit headCommit) - throws GitAPIException, NoHeadException, IOException { + throws IOException { List<RevCommit> cherryPickList = new ArrayList<>(); try (RevWalk r = new RevWalk(repo)) { r.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true); @@ -1587,6 +1596,21 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } /** + * 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 RebaseCommand setContentMergeStrategy(ContentMergeStrategy strategy) { + this.contentStrategy = strategy; + return this; + } + + /** * Whether to preserve merges during rebase * * @param preserve diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java index 56b3992fcd..1004d3e50f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2017 GitHub Inc. and others + * Copyright (C) 2012, 2021 GitHub Inc. and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -38,7 +38,9 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; +import org.eclipse.jgit.merge.ContentMergeStrategy; import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.merge.Merger; import org.eclipse.jgit.merge.ResolveMerger; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; @@ -71,6 +73,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> { private MergeStrategy strategy = MergeStrategy.RECURSIVE; + private ContentMergeStrategy contentStrategy; + /** * Create command to apply the changes of a stashed commit * @@ -166,16 +170,25 @@ public class StashApplyCommand extends GitCommand<ObjectId> { if (restoreUntracked && stashCommit.getParentCount() == 3) untrackedCommit = revWalk.parseCommit(stashCommit.getParent(2)); - ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo); - merger.setCommitNames(new String[] { "stashed HEAD", "HEAD", //$NON-NLS-1$ //$NON-NLS-2$ - "stash" }); //$NON-NLS-1$ - merger.setBase(stashHeadCommit); - merger.setWorkingTreeIterator(new FileTreeIterator(repo)); - boolean mergeSucceeded = merger.merge(headCommit, stashCommit); - List<String> modifiedByMerge = merger.getModifiedFiles(); - if (!modifiedByMerge.isEmpty()) { - repo.fireEvent( - new WorkingTreeModifiedEvent(modifiedByMerge, null)); + Merger merger = strategy.newMerger(repo); + boolean mergeSucceeded; + if (merger instanceof ResolveMerger) { + ResolveMerger resolveMerger = (ResolveMerger) merger; + resolveMerger + .setCommitNames(new String[] { "stashed HEAD", "HEAD", //$NON-NLS-1$ //$NON-NLS-2$ + "stash" }); //$NON-NLS-1$ + resolveMerger.setBase(stashHeadCommit); + resolveMerger + .setWorkingTreeIterator(new FileTreeIterator(repo)); + resolveMerger.setContentMergeStrategy(contentStrategy); + mergeSucceeded = resolveMerger.merge(headCommit, stashCommit); + List<String> modifiedByMerge = resolveMerger.getModifiedFiles(); + if (!modifiedByMerge.isEmpty()) { + repo.fireEvent(new WorkingTreeModifiedEvent(modifiedByMerge, + null)); + } + } else { + mergeSucceeded = merger.merge(headCommit, stashCommit); } if (mergeSucceeded) { DirCache dc = repo.lockDirCache(); @@ -184,11 +197,14 @@ public class StashApplyCommand extends GitCommand<ObjectId> { dco.setFailOnConflict(true); dco.checkout(); // Ignoring failed deletes.... if (restoreIndex) { - ResolveMerger ixMerger = (ResolveMerger) strategy - .newMerger(repo, true); - ixMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$ - "HEAD", "stashed index" }); //$NON-NLS-1$//$NON-NLS-2$ - ixMerger.setBase(stashHeadCommit); + Merger ixMerger = strategy.newMerger(repo, true); + if (ixMerger instanceof ResolveMerger) { + ResolveMerger resolveMerger = (ResolveMerger) ixMerger; + resolveMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$ + "HEAD", "stashed index" }); //$NON-NLS-1$//$NON-NLS-2$ + resolveMerger.setBase(stashHeadCommit); + resolveMerger.setContentMergeStrategy(contentStrategy); + } boolean ok = ixMerger.merge(headCommit, stashIndexCommit); if (ok) { resetIndex(revWalk @@ -200,16 +216,20 @@ public class StashApplyCommand extends GitCommand<ObjectId> { } if (untrackedCommit != null) { - ResolveMerger untrackedMerger = (ResolveMerger) strategy - .newMerger(repo, true); - untrackedMerger.setCommitNames(new String[] { - "null", "HEAD", "untracked files" }); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ - // There is no common base for HEAD & untracked files - // because the commit for untracked files has no parent. If - // we use stashHeadCommit as common base (as in the other - // merges) we potentially report conflicts for files - // which are not even member of untracked files commit - untrackedMerger.setBase(null); + Merger untrackedMerger = strategy.newMerger(repo, true); + if (untrackedMerger instanceof ResolveMerger) { + ResolveMerger resolveMerger = (ResolveMerger) untrackedMerger; + resolveMerger.setCommitNames(new String[] { "null", "HEAD", //$NON-NLS-1$//$NON-NLS-2$ + "untracked files" }); //$NON-NLS-1$ + // There is no common base for HEAD & untracked files + // because the commit for untracked files has no parent. + // If we use stashHeadCommit as common base (as in the + // other merges) we potentially report conflicts for + // files which are not even member of untracked files + // commit. + resolveMerger.setBase(null); + resolveMerger.setContentMergeStrategy(contentStrategy); + } boolean ok = untrackedMerger.merge(headCommit, untrackedCommit); if (ok) { @@ -279,6 +299,23 @@ public class StashApplyCommand extends GitCommand<ObjectId> { } /** + * 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 StashApplyCommand setContentMergeStrategy( + ContentMergeStrategy strategy) { + checkCallable(); + this.contentStrategy = strategy; + return this; + } + + /** * Whether the command should restore untracked files * * @param applyUntracked |