Cherry-Pick: Support --mainline to pick merges
By specifying a mainline parent, a merge is cherry picked as if this parent was its only parent. If no mainline parent is given, cherry picking merges is not allowed, as before. Change-Id: I391cb73bf8f49e2df61428c17b40fae8c86a8b76 Signed-off-by: Konrad Kügler <swamblumat-eclipsebugs@yahoo.de>
This commit is contained in:
parent
2aa2b3af31
commit
b84057ad62
|
@ -46,6 +46,7 @@
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -55,11 +56,13 @@
|
|||
import org.eclipse.jgit.api.ResetCommand.ResetType;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||
import org.eclipse.jgit.api.errors.MultipleParentsNotAllowedException;
|
||||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||
import org.eclipse.jgit.lib.ConfigConstants;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.FileMode;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ReflogReader;
|
||||
import org.eclipse.jgit.lib.RepositoryState;
|
||||
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
|
||||
|
@ -336,4 +339,59 @@ private void doCherryPickAndCheckResult(final Git git,
|
|||
.startsWith("cherry-pick: "));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cherry-picking merge commit M onto T
|
||||
* <pre>
|
||||
* M
|
||||
* |\
|
||||
* C D
|
||||
* |/
|
||||
* T B
|
||||
* | /
|
||||
* A
|
||||
* </pre>
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testCherryPickMerge() throws Exception {
|
||||
Git git = new Git(db);
|
||||
|
||||
commitFile("file", "1\n2\n3\n", "master");
|
||||
commitFile("file", "1\n2\n3\n", "side");
|
||||
checkoutBranch("refs/heads/side");
|
||||
RevCommit commitD = commitFile("file", "1\n2\n3\n4\n5\n", "side2");
|
||||
commitFile("file", "a\n2\n3\n", "side");
|
||||
MergeResult mergeResult = git.merge().include(commitD).call();
|
||||
ObjectId commitM = mergeResult.getNewHead();
|
||||
checkoutBranch("refs/heads/master");
|
||||
RevCommit commitT = commitFile("another", "t", "master");
|
||||
|
||||
try {
|
||||
git.cherryPick().include(commitM).call();
|
||||
fail("merges should not be cherry-picked by default");
|
||||
} catch (MultipleParentsNotAllowedException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
git.cherryPick().include(commitM).setMainlineParentNumber(3).call();
|
||||
fail("specifying a non-existent parent should fail");
|
||||
} catch (JGitInternalException e) {
|
||||
// expected
|
||||
assertTrue(e.getMessage().endsWith(
|
||||
"does not have a parent number 3."));
|
||||
}
|
||||
|
||||
CherryPickResult result = git.cherryPick().include(commitM)
|
||||
.setMainlineParentNumber(1).call();
|
||||
assertEquals(CherryPickStatus.OK, result.getStatus());
|
||||
checkFile(new File(db.getWorkTree(), "file"), "1\n2\n3\n4\n5\n");
|
||||
|
||||
git.reset().setMode(ResetType.HARD).setRef(commitT.getName()).call();
|
||||
|
||||
CherryPickResult result2 = git.cherryPick().include(commitM)
|
||||
.setMainlineParentNumber(2).call();
|
||||
assertEquals(CherryPickStatus.OK, result2.getStatus());
|
||||
checkFile(new File(db.getWorkTree(), "file"), "a\n2\n3\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ cannotUnloadAModifiedTree=Cannot unload a modified tree.
|
|||
cannotWorkWithOtherStagesThanZeroRightNow=Cannot work with other stages than zero right now. Won't write corrupt index.
|
||||
canOnlyCherryPickCommitsWithOneParent=Cannot cherry-pick commit ''{0}'' because it has {1} parents, only commits with exactly one parent are supported.
|
||||
canOnlyRevertCommitsWithOneParent=Cannot revert commit ''{0}'' because it has {1} parents, only commits with exactly one parent are supported
|
||||
commitDoesNotHaveGivenParent=The commit ''{0}'' does not have a parent number {1}.
|
||||
cantFindObjectInReversePackIndexForTheSpecifiedOffset=Can't find object in (reverse) pack index for the specified offset {0}
|
||||
cantPassMeATree=Can't pass me a tree!
|
||||
channelMustBeInRange0_255=channel {0} must be in range [0, 255]
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
import org.eclipse.jgit.api.errors.UnmergedPathsException;
|
||||
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
|
@ -90,6 +91,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
|
|||
|
||||
private MergeStrategy strategy = MergeStrategy.RECURSIVE;
|
||||
|
||||
private Integer mainlineParentNumber;
|
||||
|
||||
/**
|
||||
* @param repo
|
||||
*/
|
||||
|
@ -139,15 +142,7 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
|
|||
RevCommit srcCommit = revWalk.parseCommit(srcObjectId);
|
||||
|
||||
// get the parent of the commit to cherry-pick
|
||||
if (srcCommit.getParentCount() != 1)
|
||||
throw new MultipleParentsNotAllowedException(
|
||||
MessageFormat.format(
|
||||
JGitText.get().canOnlyCherryPickCommitsWithOneParent,
|
||||
srcCommit.name(),
|
||||
Integer.valueOf(srcCommit.getParentCount())));
|
||||
|
||||
RevCommit srcParent = srcCommit.getParent(0);
|
||||
revWalk.parseHeaders(srcParent);
|
||||
final RevCommit srcParent = getParentCommit(srcCommit, revWalk);
|
||||
|
||||
String ourName = calculateOurName(headRef);
|
||||
String cherryPickName = srcCommit.getId().abbreviate(7).name()
|
||||
|
@ -200,6 +195,31 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
|
|||
return new CherryPickResult(newHead, cherryPickedRefs);
|
||||
}
|
||||
|
||||
private RevCommit getParentCommit(RevCommit srcCommit, RevWalk revWalk)
|
||||
throws MultipleParentsNotAllowedException, MissingObjectException,
|
||||
IOException {
|
||||
final RevCommit srcParent;
|
||||
if (mainlineParentNumber == null) {
|
||||
if (srcCommit.getParentCount() != 1)
|
||||
throw new MultipleParentsNotAllowedException(
|
||||
MessageFormat.format(
|
||||
JGitText.get().canOnlyCherryPickCommitsWithOneParent,
|
||||
srcCommit.name(),
|
||||
Integer.valueOf(srcCommit.getParentCount())));
|
||||
srcParent = srcCommit.getParent(0);
|
||||
} else {
|
||||
if (mainlineParentNumber.intValue() > srcCommit.getParentCount())
|
||||
throw new JGitInternalException(MessageFormat.format(
|
||||
JGitText.get().commitDoesNotHaveGivenParent, srcCommit,
|
||||
mainlineParentNumber));
|
||||
srcParent = srcCommit
|
||||
.getParent(mainlineParentNumber.intValue() - 1);
|
||||
}
|
||||
|
||||
revWalk.parseHeaders(srcParent);
|
||||
return srcParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param commit
|
||||
* a reference to a commit which is cherry-picked to the current
|
||||
|
@ -271,6 +291,18 @@ public CherryPickCommand setStrategy(MergeStrategy strategy) {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mainlineParentNumber
|
||||
* the (1-based) parent number to diff against. This allows
|
||||
* cherry-picking of merges.
|
||||
* @return {@code this}
|
||||
* @since 3.4
|
||||
*/
|
||||
public CherryPickCommand setMainlineParentNumber(int mainlineParentNumber) {
|
||||
this.mainlineParentNumber = Integer.valueOf(mainlineParentNumber);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String calculateOurName(Ref headRef) {
|
||||
if (ourCommitName != null)
|
||||
return ourCommitName;
|
||||
|
|
|
@ -147,6 +147,7 @@ public static JGitText get() {
|
|||
/***/ public String cannotWorkWithOtherStagesThanZeroRightNow;
|
||||
/***/ public String canOnlyCherryPickCommitsWithOneParent;
|
||||
/***/ public String canOnlyRevertCommitsWithOneParent;
|
||||
/***/ public String commitDoesNotHaveGivenParent;
|
||||
/***/ public String cantFindObjectInReversePackIndexForTheSpecifiedOffset;
|
||||
/***/ public String cantPassMeATree;
|
||||
/***/ public String channelMustBeInRange0_255;
|
||||
|
|
Loading…
Reference in New Issue