Browse Source

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>
tags/v3.4.0.201405211411-rc1
Konrad Kügler 10 years ago
parent
commit
b84057ad62

+ 58
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java View File

@@ -46,6 +46,7 @@ import static org.junit.Assert.assertEquals;
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.CherryPickResult.CherryPickStatus;
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 @@ public class CherryPickCommandTest extends RepositoryTestCase {
.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");
}
}

+ 1
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties View File

@@ -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]

+ 41
- 9
org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java View File

@@ -56,6 +56,7 @@ import org.eclipse.jgit.api.errors.NoMessageException;
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 class CherryPickCommand extends GitCommand<CherryPickResult> {
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 class CherryPickCommand extends GitCommand<CherryPickResult> {
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 class CherryPickCommand extends GitCommand<CherryPickResult> {
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;

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java View File

@@ -147,6 +147,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotWorkWithOtherStagesThanZeroRightNow;
/***/ public String canOnlyCherryPickCommitsWithOneParent;
/***/ public String canOnlyRevertCommitsWithOneParent;
/***/ public String commitDoesNotHaveGivenParent;
/***/ public String cantFindObjectInReversePackIndexForTheSpecifiedOffset;
/***/ public String cantPassMeATree;
/***/ public String channelMustBeInRange0_255;

Loading…
Cancel
Save