TestRepository: Add a cherryPick method
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
This commit is contained in:
parent
dd453f4185
commit
f1a15f7eb0
|
@ -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)",
|
||||
|
|
|
@ -52,11 +52,13 @@
|
|||
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.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 Date getClock() {
|
|||
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 void reset(String name) throws Exception {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
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 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 void commitToUnbornHead() throws Exception {
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue