]> source.dussan.org Git - jgit.git/commitdiff
Support cherry-picking a root commit 07/201607/1
authorThomas Wolf <twolf@apache.org>
Sun, 23 Apr 2023 19:31:40 +0000 (21:31 +0200)
committerThomas Wolf <twolf@apache.org>
Sat, 29 Apr 2023 11:24:32 +0000 (13:24 +0200)
Handle the case of the commit to be picked not having any parents.

Since JGit implements cherry-pick as a 3-way-merge between the commit
to be picked and the target commit, using the parent of the picked
commit as merge base, this is super simple: just don't set a base tree.
The merger will not find any merge base and will supply an empty tree
iterator for the base.

Bug: 581832
Change-Id: I88985f1b1723db5b35ce58bf228bc48d23d6fca3
Signed-off-by: Thomas Wolf <twolf@apache.org>
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java

index 0d38197d9ab475b9457c1e8b93223288bb10485c..301d6be66250a3e1e2b2eee81e480bc182c30bd9 100644 (file)
@@ -100,41 +100,73 @@ public class CherryPickCommandTest extends RepositoryTestCase {
                }
        }
 
-    @Test
-    public void testSequentialCherryPick() throws IOException, JGitInternalException,
-            GitAPIException {
-        try (Git git = new Git(db)) {
-               writeTrashFile("a", "first line\nsec. line\nthird line\n");
-               git.add().addFilepattern("a").call();
-               RevCommit firstCommit = git.commit().setMessage("create a").call();
-
-               writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
-               git.add().addFilepattern("a").call();
-               RevCommit enlargingA = git.commit().setMessage("enlarged a").call();
-
-               writeTrashFile("a",
-                       "first line\nsecond line\nthird line\nfourth line\n");
-               git.add().addFilepattern("a").call();
-               RevCommit fixingA = git.commit().setMessage("fixed a").call();
-
-               git.branchCreate().setName("side").setStartPoint(firstCommit).call();
-               checkoutBranch("refs/heads/side");
-
-               writeTrashFile("b", "nothing to do with a");
-               git.add().addFilepattern("b").call();
-               git.commit().setMessage("create b").call();
-
-               CherryPickResult result = git.cherryPick().include(enlargingA).include(fixingA).call();
-               assertEquals(CherryPickResult.CherryPickStatus.OK, result.getStatus());
-
-               Iterator<RevCommit> history = git.log().call().iterator();
-               assertEquals("fixed a", history.next().getFullMessage());
-               assertEquals("enlarged a", history.next().getFullMessage());
-               assertEquals("create b", history.next().getFullMessage());
-               assertEquals("create a", history.next().getFullMessage());
-               assertFalse(history.hasNext());
-        }
-    }
+       @Test
+       public void testSequentialCherryPick()
+                       throws IOException, JGitInternalException, GitAPIException {
+               try (Git git = new Git(db)) {
+                       writeTrashFile("a", "first line\nsec. line\nthird line\n");
+                       git.add().addFilepattern("a").call();
+                       RevCommit firstCommit = git.commit().setMessage("create a").call();
+
+                       writeTrashFile("a",
+                                       "first line\nsec. line\nthird line\nfourth line\n");
+                       git.add().addFilepattern("a").call();
+                       RevCommit enlargingA = git.commit().setMessage("enlarged a").call();
+
+                       writeTrashFile("a",
+                                       "first line\nsecond line\nthird line\nfourth line\n");
+                       git.add().addFilepattern("a").call();
+                       RevCommit fixingA = git.commit().setMessage("fixed a").call();
+
+                       git.branchCreate().setName("side").setStartPoint(firstCommit)
+                                       .call();
+                       checkoutBranch("refs/heads/side");
+
+                       writeTrashFile("b", "nothing to do with a");
+                       git.add().addFilepattern("b").call();
+                       git.commit().setMessage("create b").call();
+
+                       CherryPickResult result = git.cherryPick().include(enlargingA)
+                                       .include(fixingA).call();
+                       assertEquals(CherryPickResult.CherryPickStatus.OK,
+                                       result.getStatus());
+
+                       Iterator<RevCommit> history = git.log().call().iterator();
+                       assertEquals("fixed a", history.next().getFullMessage());
+                       assertEquals("enlarged a", history.next().getFullMessage());
+                       assertEquals("create b", history.next().getFullMessage());
+                       assertEquals("create a", history.next().getFullMessage());
+                       assertFalse(history.hasNext());
+               }
+       }
+
+       @Test
+       public void testRootCherryPick()
+                       throws IOException, JGitInternalException, GitAPIException {
+               try (Git git = new Git(db)) {
+                       writeTrashFile("a", "a");
+                       writeTrashFile("b", "b");
+                       git.add().addFilepattern("a").addFilepattern("b").call();
+                       RevCommit firstCommit = git.commit().setMessage("Create a and b")
+                                       .call();
+
+                       git.checkout().setOrphan(true).setName("orphan").call();
+                       git.rm().addFilepattern("a").addFilepattern("b").call();
+                       writeTrashFile("a", "a");
+                       git.add().addFilepattern("a").call();
+                       git.commit().setMessage("Orphan a").call();
+
+                       CherryPickResult result = git.cherryPick().include(firstCommit)
+                                       .call();
+                       assertEquals(CherryPickResult.CherryPickStatus.OK,
+                                       result.getStatus());
+
+                       Iterator<RevCommit> history = git.log().call().iterator();
+                       assertEquals("Create a and b", history.next().getFullMessage());
+                       assertEquals("Orphan a", history.next().getFullMessage());
+                       assertFalse(history.hasNext());
+               }
+       }
 
        @Test
        public void testCherryPickDirtyIndex() throws Exception {
index ceba89d1662e2320a5ae64b85f0f166a95619afe..5f8c2b728a77771e882e2ecaf62438372f888e19 100644 (file)
@@ -142,7 +142,9 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
                                                        new String[] { "BASE", ourName, cherryPickName }); //$NON-NLS-1$
                                        resolveMerger
                                                        .setWorkingTreeIterator(new FileTreeIterator(repo));
-                                       resolveMerger.setBase(srcParent.getTree());
+                                       if (srcParent != null) {
+                                               resolveMerger.setBase(srcParent.getTree());
+                                       }
                                        noProblems = merger.merge(newHead, srcCommit);
                                        failingPaths = resolveMerger.getFailingPaths();
                                        unmergedPaths = resolveMerger.getUnmergedPaths();
@@ -217,12 +219,16 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
                        IOException {
                final RevCommit srcParent;
                if (mainlineParentNumber == null) {
-                       if (srcCommit.getParentCount() != 1)
+                       int nOfParents = srcCommit.getParentCount();
+                       if (nOfParents == 0) {
+                               return null;
+                       } else if (nOfParents != 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()) {