diff options
author | Konrad Kügler <swamblumat-eclipsebugs@yahoo.de> | 2014-05-16 18:42:44 +0200 |
---|---|---|
committer | Konrad Kügler <swamblumat-eclipsebugs@yahoo.de> | 2014-05-17 19:18:07 +0200 |
commit | b84057ad629f4dfc0a94b6a273570a53ba5a9d0a (patch) | |
tree | 9159244012ff1e727c676ca1b29ab694d700825e | |
parent | 2aa2b3af31e95b90c20794c727fc2e48b5f448e9 (diff) | |
download | jgit-b84057ad629f4dfc0a94b6a273570a53ba5a9d0a.tar.gz jgit-b84057ad629f4dfc0a94b6a273570a53ba5a9d0a.zip |
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>
4 files changed, 101 insertions, 9 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java index 2668c116a0..df73435803 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java @@ -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"); + } } diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index b3049b07f4..7b073217a5 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -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] 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 b3415b3505..cf50160da5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 576ba0f493..39f203ca8b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -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; |