{"thread":{"id":"18597","subject":"[JGIT PATCH 2/2] Add cherry picking and revert support to JGit","startedAt":"2009-03-28T02:53:51Z","lastAt":"2009-03-28T02:53:52Z","messageCount":2,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"109686","messageId":"1238208832-29427-1-git-send-email-spearce@spearce.org","threadId":"18597","inReplyTo":null,"subject":"[JGIT PATCH 1/2] Introduce ThreeWayMerge API","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-28T02:53:51Z","receivedAt":"2009-03-28T02:53:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"A 3-way merge (2 trees, and one common ancestor) is a specialized\ntype of merge which has a tighter restriction on its inputs.  This\nchange refactors the merge API slightly to introduce this special\ntype, so we can add 3-way specific operations on the API.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n This is a quick two patch series to teach JGit enough to enable an\n application to perform simple cherry-picks, much like we already\n support doing simple merges.\n\n I'm already using it in Gerrit Code Review to enable projects to\n have a more \"format-patch | am\" style workflow over a \"push; pull\"\n style of workflow, if they choose to do that.\n\n\n .../src/org/spearce/jgit/merge/MergeStrategy.java  |    2 +-\n .../src/org/spearce/jgit/merge/Merger.java         |    2 +-\n .../jgit/merge/StrategySimpleTwoWayInCore.java     |   11 +--\n .../spearce/jgit/merge/ThreeWayMergeStrategy.java  |   46 ++++++++++\n .../src/org/spearce/jgit/merge/ThreeWayMerger.java |   89 ++++++++++++++++++++\n 5 files changed, 141 insertions(+), 9 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMergeStrategy.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/MergeStrategy.java b/org.spearce.jgit/src/org/spearce/jgit/merge/MergeStrategy.java\nindex 66bd86c..5439e5c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/MergeStrategy.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/MergeStrategy.java\n@@ -55,7 +55,7 @@\n \tpublic static final MergeStrategy THEIRS = new StrategyOneSided(\"theirs\", 1);\n \n \t/** Simple strategy to merge paths, without simultaneous edits. */\n-\tpublic static final MergeStrategy SIMPLE_TWO_WAY_IN_CORE = StrategySimpleTwoWayInCore.INSTANCE;\n+\tpublic static final ThreeWayMergeStrategy SIMPLE_TWO_WAY_IN_CORE = StrategySimpleTwoWayInCore.INSTANCE;\n \n \tprivate static final HashMap<String, MergeStrategy> STRATEGIES = new HashMap<String, MergeStrategy>();\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java b/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\nindex 100cc38..db3e329 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\n@@ -122,7 +122,7 @@ public ObjectWriter getObjectWriter() {\n \t *             one or more sources could not be read, or outputs could not\n \t *             be written to the Repository.\n \t */\n-\tpublic final boolean merge(final AnyObjectId[] tips) throws IOException {\n+\tpublic boolean merge(final AnyObjectId[] tips) throws IOException {\n \t\tsourceObjects = new RevObject[tips.length];\n \t\tfor (int i = 0; i < tips.length; i++)\n \t\t\tsourceObjects[i] = walk.parseAny(tips[i]);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\nindex 3ebe397..9807644 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\n@@ -60,8 +60,8 @@\n  * trees will cause a merge conflict, as this strategy does not attempt to merge\n  * file contents.\n  */\n-public class StrategySimpleTwoWayInCore extends MergeStrategy {\n-\tstatic final MergeStrategy INSTANCE = new StrategySimpleTwoWayInCore();\n+public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {\n+\tstatic final ThreeWayMergeStrategy INSTANCE = new StrategySimpleTwoWayInCore();\n \n \t/** Create a new instance of the strategy. */\n \tprotected StrategySimpleTwoWayInCore() {\n@@ -74,11 +74,11 @@ public String getName() {\n \t}\n \n \t@Override\n-\tpublic Merger newMerger(final Repository db) {\n+\tpublic ThreeWayMerger newMerger(final Repository db) {\n \t\treturn new InCoreMerger(db);\n \t}\n \n-\tprivate static class InCoreMerger extends Merger {\n+\tprivate static class InCoreMerger extends ThreeWayMerger {\n \t\tprivate static final int T_BASE = 0;\n \n \t\tprivate static final int T_OURS = 1;\n@@ -101,9 +101,6 @@ InCoreMerger(final Repository local) {\n \n \t\t@Override\n \t\tprotected boolean mergeImpl() throws IOException {\n-\t\t\tif (sourceTrees.length != 2)\n-\t\t\t\treturn false;\n-\n \t\t\ttw.reset();\n \t\t\ttw.addTree(mergeBase(0, 1));\n \t\t\ttw.addTree(sourceTrees[0]);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMergeStrategy.java b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMergeStrategy.java\nnew file mode 100644\nindex 0000000..848e80b\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMergeStrategy.java\n@@ -0,0 +1,46 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.merge;\n+\n+import org.spearce.jgit.lib.Repository;\n+\n+/** A merge strategy to merge 2 trees, using a common base ancestor tree. */\n+public abstract class ThreeWayMergeStrategy extends MergeStrategy {\n+\t@Override\n+\tpublic abstract ThreeWayMerger newMerger(Repository db);\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\nnew file mode 100644\nindex 0000000..9d1621d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\n@@ -0,0 +1,89 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.merge;\n+\n+import java.io.IOException;\n+\n+import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.lib.AnyObjectId;\n+import org.spearce.jgit.lib.Repository;\n+\n+/** A merge of 2 trees, using a common base ancestor tree. */\n+public abstract class ThreeWayMerger extends Merger {\n+\t/**\n+\t * Create a new merge instance for a repository.\n+\t * \n+\t * @param local\n+\t *            the repository this merger will read and write data on.\n+\t */\n+\tprotected ThreeWayMerger(final Repository local) {\n+\t\tsuper(local);\n+\t}\n+\n+\t/**\n+\t * Merge together two tree-ish objects.\n+\t * <p>\n+\t * Any tree-ish may be supplied as inputs. Commits and/or tags pointing at\n+\t * trees or commits may be passed as input objects.\n+\t * \n+\t * @param a\n+\t *            source tree to be combined together.\n+\t * @param b\n+\t *            source tree to be combined together.\n+\t * @return true if the merge was completed without conflicts; false if the\n+\t *         merge strategy cannot handle this merge or there were conflicts\n+\t *         preventing it from automatically resolving all paths.\n+\t * @throws IncorrectObjectTypeException\n+\t *             one of the input objects is not a commit, but the strategy\n+\t *             requires it to be a commit.\n+\t * @throws IOException\n+\t *             one or more sources could not be read, or outputs could not\n+\t *             be written to the Repository.\n+\t */\n+\tpublic boolean merge(final AnyObjectId a, final AnyObjectId b)\n+\t\t\tthrows IOException {\n+\t\treturn merge(new AnyObjectId[] { a, b });\n+\t}\n+\n+\t@Override\n+\tpublic boolean merge(final AnyObjectId[] tips) throws IOException {\n+\t\tif (tips.length != 2)\n+\t\t\treturn false;\n+\t\treturn super.merge(tips);\n+\t}\n+}\n-- \n1.6.2.1.471.g682837\n"},{"id":"109685","messageId":"1238208832-29427-2-git-send-email-spearce@spearce.org","threadId":"18597","inReplyTo":"1238208832-29427-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 2/2] Add cherry picking and revert support to JGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-28T02:53:52Z","receivedAt":"2009-03-28T02:53:52Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"A cherry-pick can be performed by a 3 way merge, where the base is\nthe parent of the commit you are cherry-picking, instead of using\nthe derived base from the commit graph.\n\nBy allowing the caller of a ThreeWayMerger to set the merge base\nprior to invoking the merge method, we allow the caller to direct\nus to execute a cherry-pick, or a revert.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/merge/CherryPickTest.java |  149 ++++++++++++++++++++\n .../src/org/spearce/jgit/merge/Merger.java         |   18 +++-\n .../jgit/merge/StrategySimpleTwoWayInCore.java     |    2 +-\n .../src/org/spearce/jgit/merge/ThreeWayMerger.java |   41 ++++++\n .../spearce/jgit/treewalk/CanonicalTreeParser.java |    2 +-\n 5 files changed, 209 insertions(+), 3 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/merge/CherryPickTest.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/CherryPickTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/CherryPickTest.java\nnew file mode 100644\nindex 0000000..78a5553\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/CherryPickTest.java\n@@ -0,0 +1,149 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\n+ * Copyright (C) 2008, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.merge;\n+\n+import java.io.ByteArrayInputStream;\n+\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheBuilder;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ObjectWriter;\n+import org.spearce.jgit.lib.PersonIdent;\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+\n+public class CherryPickTest extends RepositoryTestCase {\n+\tpublic void testPick() throws Exception {\n+\t\t// B---O\n+\t\t// \\----P---T\n+\t\t//\n+\t\t// Cherry-pick \"T\" onto \"O\". This shouldn't introduce \"p-fail\", which\n+\t\t// was created by \"P\", nor should it modify \"a\", which was done by \"P\".\n+\t\t//\n+\t\tfinal DirCache treeB = DirCache.read(db);\n+\t\tfinal DirCache treeO = DirCache.read(db);\n+\t\tfinal DirCache treeP = DirCache.read(db);\n+\t\tfinal DirCache treeT = DirCache.read(db);\n+\t\t{\n+\t\t\tfinal DirCacheBuilder b = treeB.builder();\n+\t\t\tfinal DirCacheBuilder o = treeO.builder();\n+\t\t\tfinal DirCacheBuilder p = treeP.builder();\n+\t\t\tfinal DirCacheBuilder t = treeT.builder();\n+\n+\t\t\tb.add(makeEntry(\"a\", FileMode.REGULAR_FILE));\n+\n+\t\t\to.add(makeEntry(\"a\", FileMode.REGULAR_FILE));\n+\t\t\to.add(makeEntry(\"o\", FileMode.REGULAR_FILE));\n+\n+\t\t\tp.add(makeEntry(\"a\", FileMode.REGULAR_FILE, \"q\"));\n+\t\t\tp.add(makeEntry(\"p-fail\", FileMode.REGULAR_FILE));\n+\n+\t\t\tt.add(makeEntry(\"a\", FileMode.REGULAR_FILE));\n+\t\t\tt.add(makeEntry(\"t\", FileMode.REGULAR_FILE));\n+\n+\t\t\tb.finish();\n+\t\t\to.finish();\n+\t\t\tp.finish();\n+\t\t\tt.finish();\n+\t\t}\n+\n+\t\tfinal ObjectWriter ow = new ObjectWriter(db);\n+\t\tfinal ObjectId B = commit(ow, treeB, new ObjectId[] {});\n+\t\tfinal ObjectId O = commit(ow, treeO, new ObjectId[] { B });\n+\t\tfinal ObjectId P = commit(ow, treeP, new ObjectId[] { B });\n+\t\tfinal ObjectId T = commit(ow, treeT, new ObjectId[] { P });\n+\n+\t\tThreeWayMerger twm = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);\n+\t\ttwm.setBase(P);\n+\t\tboolean merge = twm.merge(new ObjectId[] { O, T });\n+\t\tassertTrue(merge);\n+\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.setRecursive(true);\n+\t\ttw.reset(twm.getResultTreeId());\n+\n+\t\tassertTrue(tw.next());\n+\t\tassertEquals(\"a\", tw.getPathString());\n+\t\tassertCorrectId(treeO, tw);\n+\n+\t\tassertTrue(tw.next());\n+\t\tassertEquals(\"o\", tw.getPathString());\n+\t\tassertCorrectId(treeO, tw);\n+\n+\t\tassertTrue(tw.next());\n+\t\tassertEquals(\"t\", tw.getPathString());\n+\t\tassertCorrectId(treeT, tw);\n+\n+\t\tassertFalse(tw.next());\n+\t}\n+\n+\tprivate void assertCorrectId(final DirCache treeT, final TreeWalk tw) {\n+\t\tassertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw\n+\t\t\t\t.getObjectId(0));\n+\t}\n+\n+\tprivate ObjectId commit(final ObjectWriter ow, final DirCache treeB,\n+\t\t\tfinal ObjectId[] parentIds) throws Exception {\n+\t\tfinal Commit c = new Commit(db);\n+\t\tc.setTreeId(treeB.writeTree(ow));\n+\t\tc.setAuthor(new PersonIdent(\"A U Thor\", \"a.u.thor\", 1L, 0));\n+\t\tc.setCommitter(c.getAuthor());\n+\t\tc.setParentIds(parentIds);\n+\t\tc.setMessage(\"Tree \" + c.getTreeId().name());\n+\t\treturn ow.writeCommit(c);\n+\t}\n+\n+\tprivate DirCacheEntry makeEntry(final String path, final FileMode mode)\n+\t\t\tthrows Exception {\n+\t\treturn makeEntry(path, mode, path);\n+\t}\n+\n+\tprivate DirCacheEntry makeEntry(final String path, final FileMode mode,\n+\t\t\tfinal String content) throws Exception {\n+\t\tfinal DirCacheEntry ent = new DirCacheEntry(path);\n+\t\tent.setFileMode(mode);\n+\t\tfinal byte[] contentBytes = Constants.encode(content);\n+\t\tent.setObjectId(new ObjectWriter(db).computeBlobSha1(\n+\t\t\t\tcontentBytes.length, new ByteArrayInputStream(contentBytes)));\n+\t\treturn ent;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java b/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\nindex db3e329..2d5d44f 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/Merger.java\n@@ -179,9 +179,25 @@ protected AbstractTreeIterator mergeBase(final int aIdx, final int bIdx)\n \t\t\t\t\t+ sourceCommits[bIdx].name() + \"found:\" + \"\\n  \"\n \t\t\t\t\t+ base.name() + \"\\n  \" + base2.name());\n \t\t}\n+\t\treturn openTree(base.getTree());\n+\t}\n+\n+\t/**\n+\t * Open an iterator over a tree.\n+\t * \n+\t * @param treeId\n+\t *            the tree to scan; must be a tree (not a treeish).\n+\t * @return an iterator for the tree.\n+\t * @throws IncorrectObjectTypeException\n+\t *             the input object is not a tree.\n+\t * @throws IOException\n+\t *             the tree object is not found or cannot be read.\n+\t */\n+\tprotected AbstractTreeIterator openTree(final AnyObjectId treeId)\n+\t\t\tthrows IncorrectObjectTypeException, IOException {\n \t\tfinal WindowCursor curs = new WindowCursor();\n \t\ttry {\n-\t\t\treturn new CanonicalTreeParser(null, db, base.getTree(), curs);\n+\t\t\treturn new CanonicalTreeParser(null, db, treeId, curs);\n \t\t} finally {\n \t\t\tcurs.release();\n \t\t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\nindex 9807644..5d4447c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java\n@@ -102,7 +102,7 @@ InCoreMerger(final Repository local) {\n \t\t@Override\n \t\tprotected boolean mergeImpl() throws IOException {\n \t\t\ttw.reset();\n-\t\t\ttw.addTree(mergeBase(0, 1));\n+\t\t\ttw.addTree(mergeBase());\n \t\t\ttw.addTree(sourceTrees[0]);\n \t\t\ttw.addTree(sourceTrees[1]);\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\nindex 9d1621d..6f041c1 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/ThreeWayMerger.java\n@@ -40,11 +40,16 @@\n import java.io.IOException;\n \n import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.errors.MissingObjectException;\n import org.spearce.jgit.lib.AnyObjectId;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.revwalk.RevTree;\n+import org.spearce.jgit.treewalk.AbstractTreeIterator;\n \n /** A merge of 2 trees, using a common base ancestor tree. */\n public abstract class ThreeWayMerger extends Merger {\n+\tprivate RevTree baseTree;\n+\n \t/**\n \t * Create a new merge instance for a repository.\n \t * \n@@ -56,6 +61,29 @@ protected ThreeWayMerger(final Repository local) {\n \t}\n \n \t/**\n+\t * Set the common ancestor tree.\n+\t * \n+\t * @param id\n+\t *            common base treeish; null to automatically compute the common\n+\t *            base from the input commits during\n+\t *            {@link #merge(AnyObjectId, AnyObjectId)}.\n+\t * @throws IncorrectObjectTypeException\n+\t *             the object is not a treeish.\n+\t * @throws MissingObjectException\n+\t *             the object does not exist.\n+\t * @throws IOException\n+\t *             the object could not be read.\n+\t */\n+\tpublic void setBase(final AnyObjectId id) throws MissingObjectException,\n+\t\t\tIncorrectObjectTypeException, IOException {\n+\t\tif (id != null) {\n+\t\t\tbaseTree = walk.parseTree(id);\n+\t\t} else {\n+\t\t\tbaseTree = null;\n+\t\t}\n+\t}\n+\n+\t/**\n \t * Merge together two tree-ish objects.\n \t * <p>\n \t * Any tree-ish may be supplied as inputs. Commits and/or tags pointing at\n@@ -86,4 +114,17 @@ public boolean merge(final AnyObjectId[] tips) throws IOException {\n \t\t\treturn false;\n \t\treturn super.merge(tips);\n \t}\n+\n+\t/**\n+\t * Create an iterator to walk the merge base.\n+\t * \n+\t * @return an iterator over the caller-specified merge base, or the natural\n+\t *         merge base of the two input commits.\n+\t * @throws IOException\n+\t */\n+\tprotected AbstractTreeIterator mergeBase() throws IOException {\n+\t\tif (baseTree != null)\n+\t\t\treturn openTree(baseTree);\n+\t\treturn mergeBase(0, 1);\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/CanonicalTreeParser.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/CanonicalTreeParser.java\nindex 7f89cff..ec1cf10 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/CanonicalTreeParser.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/CanonicalTreeParser.java\n@@ -91,7 +91,7 @@ public CanonicalTreeParser() {\n \t *             a loose object or pack file could not be read.\n \t */\n \tpublic CanonicalTreeParser(final byte[] prefix, final Repository repo,\n-\t\t\tfinal ObjectId treeId, final WindowCursor curs)\n+\t\t\tfinal AnyObjectId treeId, final WindowCursor curs)\n \t\t\tthrows IncorrectObjectTypeException, IOException {\n \t\tsuper(prefix);\n \t\treset(repo, treeId, curs);\n-- \n1.6.2.1.471.g682837\n"}]}