]> source.dussan.org Git - jgit.git/commitdiff
TestRepository: Add a cherryPick method 22/44122/4
authorDave Borowitz <dborowitz@google.com>
Wed, 18 Mar 2015 17:46:41 +0000 (10:46 -0700)
committerDave Borowitz <dborowitz@google.com>
Mon, 23 Mar 2015 20:03:31 +0000 (13:03 -0700)
CherryPickCommand only works on a non-bare repository, as it must
modify the working tree and index in case of a merge conflict. In
tests, being able to recover from a merge conflict is less important,
as the caller should be able to control the full contents of files in
advance of the cherry-pick.

Change-Id: Ic332e44df1308b9336e884666b08c1f6db64513d

org.eclipse.jgit.junit/META-INF/MANIFEST.MF
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java

index b20464e397eae5fbc2aac38be204a23f908c06cf..327a697f6644da6fea9583029cc93f0349efd3dd 100644 (file)
@@ -14,6 +14,7 @@ Import-Package: org.eclipse.jgit.api;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.internal.storage.file;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.internal.storage.pack;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.lib;version="[4.0.0,4.1.0)",
+ org.eclipse.jgit.merge;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.revwalk;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.storage.file;version="[4.0.0,4.1.0)",
  org.eclipse.jgit.treewalk;version="[4.0.0,4.1.0)",
index ce33ec70949af49997d2969793b26e79c6eb6258..925a6b0216dc62cad37c4bff4830ff70923b802c 100644 (file)
@@ -52,11 +52,13 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.security.MessageDigest;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.TimeZone;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.dircache.DirCache;
@@ -88,6 +90,8 @@ import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefWriter;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.TagBuilder;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.merge.ThreeWayMerger;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -187,6 +191,11 @@ public class TestRepository<R extends Repository> {
                return new Date(now);
        }
 
+       /** @return timezone used for default identities. */
+       public TimeZone getTimeZone() {
+               return defaultCommitter.getTimeZone();
+       }
+
        /**
         * Adjust the current time that will used by the next commit.
         *
@@ -615,6 +624,59 @@ public class TestRepository<R extends Repository> {
                }
        }
 
+       /**
+        * Cherry-pick a commit onto HEAD.
+        * <p>
+        * This differs from {@code git cherry-pick} in that it works in a bare
+        * repository. As a result, any merge failure results in an exception, as
+        * there is no way to recover.
+        *
+        * @param id
+        *            commit-ish to cherry-pick.
+        * @return newly created commit, or null if no work was done due to the
+        *         resulting tree being identical.
+        * @throws Exception
+        */
+       public RevCommit cherryPick(AnyObjectId id) throws Exception {
+               RevCommit commit = pool.parseCommit(id);
+               pool.parseBody(commit);
+               if (commit.getParentCount() != 1)
+                       throw new IOException(String.format(
+                                       "Expected 1 parent for %s, found: %s",
+                                       id.name(), Arrays.asList(commit.getParents())));
+               RevCommit parent = commit.getParent(0);
+               pool.parseHeaders(parent);
+
+               Ref headRef = db.getRef(Constants.HEAD);
+               if (headRef == null)
+                       throw new IOException("Missing HEAD");
+               RevCommit head = pool.parseCommit(headRef.getObjectId());
+
+               ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
+               merger.setBase(parent.getTree());
+               if (merger.merge(head, commit)) {
+                       if (AnyObjectId.equals(head.getTree(), merger.getResultTreeId()))
+                               return null;
+                       tick(1);
+                       org.eclipse.jgit.lib.CommitBuilder b =
+                                       new org.eclipse.jgit.lib.CommitBuilder();
+                       b.setParentId(head);
+                       b.setTreeId(merger.getResultTreeId());
+                       b.setAuthor(commit.getAuthorIdent());
+                       b.setCommitter(new PersonIdent(defaultCommitter, new Date(now)));
+                       b.setMessage(commit.getFullMessage());
+                       ObjectId result;
+                       try (ObjectInserter ins = inserter) {
+                               result = ins.insert(b);
+                               ins.flush();
+                       }
+                       update(Constants.HEAD, result);
+                       return pool.parseCommit(result);
+               } else {
+                       throw new IOException("Merge conflict");
+               }
+       }
+
        /**
         * Update the dumb client server info files.
         *
index cefc779a27516387aa548f4cf963873992871589..fbb9eecdff6198527e20b90736a3056104b28c24 100644 (file)
@@ -46,6 +46,7 @@ package org.eclipse.jgit.junit;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -55,9 +56,9 @@ import java.util.regex.Pattern;
 
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -296,6 +297,83 @@ public class TestRepositoryTest {
                assertEquals("refs/heads/master", ref.getTarget().getName());
        }
 
+       @Test
+       public void cherryPick() throws Exception {
+               repo.updateRef("HEAD").link("refs/heads/master");
+               RevCommit head = tr.branch("master").commit()
+                               .add("foo", "foo contents\n")
+                               .create();
+               rw.parseBody(head);
+               RevCommit toPick = tr.commit()
+                               .parent(tr.commit().create()) // Can't cherry-pick root.
+                               .author(new PersonIdent("Cherrypick Author", "cpa@example.com",
+                                               tr.getClock(), tr.getTimeZone()))
+                               .author(new PersonIdent("Cherrypick Committer", "cpc@example.com",
+                                               tr.getClock(), tr.getTimeZone()))
+                               .message("message to cherry-pick")
+                               .add("bar", "bar contents\n")
+                               .create();
+               RevCommit result = tr.cherryPick(toPick);
+               rw.parseBody(result);
+
+               Ref headRef = tr.getRepository().getRef("HEAD");
+               assertEquals(result, headRef.getObjectId());
+               assertTrue(headRef.isSymbolic());
+               assertEquals("refs/heads/master", headRef.getLeaf().getName());
+
+               assertEquals(1, result.getParentCount());
+               assertEquals(head, result.getParent(0));
+               assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
+
+               // Committer name/email matches default, and time was incremented.
+               assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
+                               new PersonIdent(result.getCommitterIdent(), new Date(0)));
+               assertTrue(toPick.getCommitTime() < result.getCommitTime());
+
+               assertEquals("message to cherry-pick", result.getFullMessage());
+               assertEquals("foo contents\n", blobAsString(result, "foo"));
+               assertEquals("bar contents\n", blobAsString(result, "bar"));
+       }
+
+       @Test
+       public void cherryPickWithContentMerge() throws Exception {
+               RevCommit base = tr.branch("HEAD").commit()
+                               .add("foo", "foo contents\n\n")
+                               .create();
+               tr.branch("HEAD").commit()
+                               .add("foo", "foo contents\n\nlast line\n")
+                               .create();
+               RevCommit toPick = tr.commit()
+                               .message("message to cherry-pick")
+                               .parent(base)
+                               .add("foo", "changed foo contents\n\n")
+                               .create();
+               RevCommit result = tr.cherryPick(toPick);
+               rw.parseBody(result);
+
+               assertEquals("message to cherry-pick", result.getFullMessage());
+               assertEquals("changed foo contents\n\nlast line\n",
+                               blobAsString(result, "foo"));
+       }
+
+       @Test
+       public void cherryPickWithIdenticalContents() throws Exception {
+               RevCommit base = tr.branch("HEAD").commit().add("foo", "foo contents\n")
+                               .create();
+               RevCommit head = tr.branch("HEAD").commit()
+                               .parent(base)
+                               .add("bar", "bar contents\n")
+                               .create();
+               RevCommit toPick = tr.commit()
+                               .parent(base)
+                               .message("message to cherry-pick")
+                               .add("bar", "bar contents\n")
+                               .create();
+               assertNotEquals(head, toPick);
+               assertNull(tr.cherryPick(toPick));
+               assertEquals(head, repo.getRef("HEAD").getObjectId());
+       }
+
        private String blobAsString(AnyObjectId treeish, String path)
                        throws Exception {
                RevObject obj = tr.get(rw.parseTree(treeish), path);