All that's really required to run a merge operation is a single ObjectInserter, from which we can construct a RevWalk, plus a Config that declares a diff algorithm. Provide some factory methods that don't take Repository. Change-Id: Ib884dce2528424b5bcbbbbfc043baec1886b9bbdtags/v4.8.0.201705170830-rc1
*/ | */ | ||||
package org.eclipse.jgit.merge; | package org.eclipse.jgit.merge; | ||||
import static java.nio.charset.StandardCharsets.UTF_8; | |||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; | |||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.Arrays; | |||||
import org.eclipse.jgit.api.Git; | import org.eclipse.jgit.api.Git; | ||||
import org.eclipse.jgit.api.MergeResult; | import org.eclipse.jgit.api.MergeResult; | ||||
import org.eclipse.jgit.errors.NoMergeBaseException; | import org.eclipse.jgit.errors.NoMergeBaseException; | ||||
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; | import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; | ||||
import org.eclipse.jgit.junit.RepositoryTestCase; | import org.eclipse.jgit.junit.RepositoryTestCase; | ||||
import org.eclipse.jgit.junit.TestRepository; | |||||
import org.eclipse.jgit.lib.ObjectId; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; | import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.revwalk.RevObject; | |||||
import org.eclipse.jgit.revwalk.RevTree; | |||||
import org.eclipse.jgit.revwalk.RevWalk; | |||||
import org.eclipse.jgit.treewalk.FileTreeIterator; | import org.eclipse.jgit.treewalk.FileTreeIterator; | ||||
import org.eclipse.jgit.util.FS; | import org.eclipse.jgit.util.FS; | ||||
import org.eclipse.jgit.util.FileUtils; | import org.eclipse.jgit.util.FileUtils; | ||||
/** | /** | ||||
* Merging two equal subtrees with an incore merger should lead to a merged | * Merging two equal subtrees with an incore merger should lead to a merged | ||||
* state (The 'Gerrit' use case). | |||||
* state. | |||||
* | * | ||||
* @param strategy | * @param strategy | ||||
* @throws Exception | * @throws Exception | ||||
assertTrue(noProblems); | assertTrue(noProblems); | ||||
} | } | ||||
/** | |||||
* Merging two equal subtrees with an incore merger should lead to a merged | |||||
* state, without using a Repository (the 'Gerrit' use case). | |||||
* | |||||
* @param strategy | |||||
* @throws Exception | |||||
*/ | |||||
@Theory | |||||
public void checkMergeEqualTreesInCore_noRepo(MergeStrategy strategy) | |||||
throws Exception { | |||||
Git git = Git.wrap(db); | |||||
writeTrashFile("d/1", "orig"); | |||||
git.add().addFilepattern("d/1").call(); | |||||
RevCommit first = git.commit().setMessage("added d/1").call(); | |||||
writeTrashFile("d/1", "modified"); | |||||
RevCommit masterCommit = git.commit().setAll(true) | |||||
.setMessage("modified d/1 on master").call(); | |||||
git.checkout().setCreateBranch(true).setStartPoint(first) | |||||
.setName("side").call(); | |||||
writeTrashFile("d/1", "modified"); | |||||
RevCommit sideCommit = git.commit().setAll(true) | |||||
.setMessage("modified d/1 on side").call(); | |||||
git.rm().addFilepattern("d/1").call(); | |||||
git.rm().addFilepattern("d").call(); | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
ThreeWayMerger resolveMerger = | |||||
(ThreeWayMerger) strategy.newMerger(ins, db.getConfig()); | |||||
boolean noProblems = resolveMerger.merge(masterCommit, sideCommit); | |||||
assertTrue(noProblems); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Merging two equal subtrees when the index and HEAD does not contain any | * Merging two equal subtrees when the index and HEAD does not contain any | ||||
* file in that subtree should lead to a merged state. | * file in that subtree should lead to a merged state. | ||||
assertEquals(expected, read("file")); | assertEquals(expected, read("file")); | ||||
} | } | ||||
@Theory | |||||
public void checkContentMergeNoConflict_noRepo(MergeStrategy strategy) | |||||
throws Exception { | |||||
Git git = Git.wrap(db); | |||||
writeTrashFile("file", "1\n2\n3"); | |||||
git.add().addFilepattern("file").call(); | |||||
RevCommit first = git.commit().setMessage("added file").call(); | |||||
writeTrashFile("file", "1master\n2\n3"); | |||||
RevCommit masterCommit = git.commit().setAll(true) | |||||
.setMessage("modified file on master").call(); | |||||
git.checkout().setCreateBranch(true).setStartPoint(first) | |||||
.setName("side").call(); | |||||
writeTrashFile("file", "1\n2\n3side"); | |||||
RevCommit sideCommit = git.commit().setAll(true) | |||||
.setMessage("modified file on side").call(); | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
ResolveMerger merger = | |||||
(ResolveMerger) strategy.newMerger(ins, db.getConfig()); | |||||
boolean noProblems = merger.merge(masterCommit, sideCommit); | |||||
assertTrue(noProblems); | |||||
assertEquals("1master\n2\n3side", | |||||
readBlob(merger.getResultTreeId(), "file")); | |||||
} | |||||
} | |||||
@Theory | @Theory | ||||
public void checkContentMergeConflict(MergeStrategy strategy) | public void checkContentMergeConflict(MergeStrategy strategy) | ||||
throws Exception { | throws Exception { | ||||
assertEquals(expected, read("file")); | assertEquals(expected, read("file")); | ||||
} | } | ||||
@Theory | |||||
public void checkContentMergeConflict_noTree(MergeStrategy strategy) | |||||
throws Exception { | |||||
Git git = Git.wrap(db); | |||||
writeTrashFile("file", "1\n2\n3"); | |||||
git.add().addFilepattern("file").call(); | |||||
RevCommit first = git.commit().setMessage("added file").call(); | |||||
writeTrashFile("file", "1master\n2\n3"); | |||||
RevCommit masterCommit = git.commit().setAll(true) | |||||
.setMessage("modified file on master").call(); | |||||
git.checkout().setCreateBranch(true).setStartPoint(first) | |||||
.setName("side").call(); | |||||
writeTrashFile("file", "1side\n2\n3"); | |||||
RevCommit sideCommit = git.commit().setAll(true) | |||||
.setMessage("modified file on side").call(); | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
ResolveMerger merger = | |||||
(ResolveMerger) strategy.newMerger(ins, db.getConfig()); | |||||
boolean noProblems = merger.merge(masterCommit, sideCommit); | |||||
assertFalse(noProblems); | |||||
assertEquals(Arrays.asList("file"), merger.getUnmergedPaths()); | |||||
MergeFormatter fmt = new MergeFormatter(); | |||||
merger.getMergeResults().get("file"); | |||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { | |||||
fmt.formatMerge(out, merger.getMergeResults().get("file"), | |||||
"BASE", "OURS", "THEIRS", UTF_8.name()); | |||||
String expected = "<<<<<<< OURS\n" | |||||
+ "1master\n" | |||||
+ "=======\n" | |||||
+ "1side\n" | |||||
+ ">>>>>>> THEIRS\n" | |||||
+ "2\n" | |||||
+ "3"; | |||||
assertEquals(expected, new String(out.toByteArray(), UTF_8)); | |||||
} | |||||
} | |||||
} | |||||
/** | /** | ||||
* Merging after criss-cross merges. In this case we merge together two | * Merging after criss-cross merges. In this case we merge together two | ||||
* commits which have two equally good common ancestors | * commits which have two equally good common ancestors | ||||
curMod >= lastMod); | curMod >= lastMod); | ||||
} | } | ||||
} | } | ||||
private String readBlob(ObjectId treeish, String path) throws Exception { | |||||
TestRepository<?> tr = new TestRepository<>(db); | |||||
RevWalk rw = tr.getRevWalk(); | |||||
RevTree tree = rw.parseTree(treeish); | |||||
RevObject obj = tr.get(tree, path); | |||||
if (obj == null) { | |||||
return null; | |||||
} | |||||
return new String(rw.getObjectReader().open(obj, OBJ_BLOB).getBytes(), UTF_8); | |||||
} | |||||
} | } |
assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId()); | assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId()); | ||||
} | } | ||||
@Test | |||||
public void testOurs_noRepo() throws IOException { | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
Merger ourMerger = MergeStrategy.OURS.newMerger(ins, db.getConfig()); | |||||
boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") }); | |||||
assertTrue(merge); | |||||
assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId()); | |||||
} | |||||
} | |||||
@Test | @Test | ||||
public void testTheirs() throws IOException { | public void testTheirs() throws IOException { | ||||
Merger ourMerger = MergeStrategy.THEIRS.newMerger(db); | Merger ourMerger = MergeStrategy.THEIRS.newMerger(db); | ||||
assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId()); | assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId()); | ||||
} | } | ||||
@Test | |||||
public void testTheirs_noRepo() throws IOException { | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
Merger ourMerger = MergeStrategy.THEIRS.newMerger(db); | |||||
boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") }); | |||||
assertTrue(merge); | |||||
assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId()); | |||||
} | |||||
} | |||||
@Test | @Test | ||||
public void testTrivialTwoWay() throws IOException { | public void testTrivialTwoWay() throws IOException { | ||||
Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); | Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); | ||||
assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId()); | assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId()); | ||||
} | } | ||||
@Test | |||||
public void testTrivialTwoWay_noRepo() throws IOException { | |||||
try (ObjectInserter ins = db.newObjectInserter()) { | |||||
Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(ins, db.getConfig()); | |||||
boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") }); | |||||
assertTrue(merge); | |||||
assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId()); | |||||
} | |||||
} | |||||
@Test | @Test | ||||
public void testTrivialTwoWay_conflict() throws IOException { | public void testTrivialTwoWay_conflict() throws IOException { | ||||
Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); | Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); |
renamesRejoiningModifies=Rejoining modified file pairs | renamesRejoiningModifies=Rejoining modified file pairs | ||||
repositoryAlreadyExists=Repository already exists: {0} | repositoryAlreadyExists=Repository already exists: {0} | ||||
repositoryConfigFileInvalid=Repository config file {0} invalid {1} | repositoryConfigFileInvalid=Repository config file {0} invalid {1} | ||||
repositoryIsRequired=repository is required | |||||
repositoryNotFound=repository not found: {0} | repositoryNotFound=repository not found: {0} | ||||
repositoryState_applyMailbox=Apply mailbox | repositoryState_applyMailbox=Apply mailbox | ||||
repositoryState_bare=Bare | repositoryState_bare=Bare |
/***/ public String renamesRejoiningModifies; | /***/ public String renamesRejoiningModifies; | ||||
/***/ public String repositoryAlreadyExists; | /***/ public String repositoryAlreadyExists; | ||||
/***/ public String repositoryConfigFileInvalid; | /***/ public String repositoryConfigFileInvalid; | ||||
/***/ public String repositoryIsRequired; | |||||
/***/ public String repositoryNotFound; | /***/ public String repositoryNotFound; | ||||
/***/ public String repositoryState_applyMailbox; | /***/ public String repositoryState_applyMailbox; | ||||
/***/ public String repositoryState_bare; | /***/ public String repositoryState_bare; |
import java.util.HashMap; | import java.util.HashMap; | ||||
import org.eclipse.jgit.internal.JGitText; | import org.eclipse.jgit.internal.JGitText; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
/** | /** | ||||
* @return the new merge instance which implements this strategy. | * @return the new merge instance which implements this strategy. | ||||
*/ | */ | ||||
public abstract Merger newMerger(Repository db, boolean inCore); | public abstract Merger newMerger(Repository db, boolean inCore); | ||||
/** | |||||
* Create a new merge instance. | |||||
* <p> | |||||
* The merge will happen in memory, working folder will not be modified, in | |||||
* case of a non-trivial merge that requires manual resolution, the merger | |||||
* will fail. | |||||
* | |||||
* @param inserter | |||||
* inserter to write results back to. | |||||
* @param config | |||||
* repo config for reading diff algorithm settings. | |||||
* @return the new merge instance which implements this strategy. | |||||
* @since 4.8 | |||||
*/ | |||||
public abstract Merger newMerger(ObjectInserter inserter, Config config); | |||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import org.eclipse.jgit.annotations.Nullable; | |||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | import org.eclipse.jgit.errors.IncorrectObjectTypeException; | ||||
import org.eclipse.jgit.errors.NoMergeBaseException; | import org.eclipse.jgit.errors.NoMergeBaseException; | ||||
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; | import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; | ||||
* Instance of a specific {@link MergeStrategy} for a single {@link Repository}. | * Instance of a specific {@link MergeStrategy} for a single {@link Repository}. | ||||
*/ | */ | ||||
public abstract class Merger { | public abstract class Merger { | ||||
/** The repository this merger operates on. */ | |||||
/** | |||||
* The repository this merger operates on. | |||||
* <p> | |||||
* Null if and only if the merger was constructed with {@link | |||||
* #Merger(ObjectInserter)}. Callers that want to assume the repo is not null | |||||
* (e.g. because of a previous check that the merger is not in-core) may use | |||||
* {@link #nonNullRepo()}. | |||||
*/ | |||||
@Nullable | |||||
protected final Repository db; | protected final Repository db; | ||||
/** Reader to support {@link #walk} and other object loading. */ | /** Reader to support {@link #walk} and other object loading. */ | ||||
* the repository this merger will read and write data on. | * the repository this merger will read and write data on. | ||||
*/ | */ | ||||
protected Merger(final Repository local) { | protected Merger(final Repository local) { | ||||
if (local == null) { | |||||
throw new NullPointerException(JGitText.get().repositoryIsRequired); | |||||
} | |||||
db = local; | db = local; | ||||
inserter = db.newObjectInserter(); | |||||
inserter = local.newObjectInserter(); | |||||
reader = inserter.newReader(); | reader = inserter.newReader(); | ||||
walk = new RevWalk(reader); | walk = new RevWalk(reader); | ||||
} | } | ||||
/** | |||||
* Create a new in-core merge instance from an inserter. | |||||
* | |||||
* @param oi | |||||
* the inserter to write objects to. Will be closed at the | |||||
* conclusion of {@code merge}, unless {@code flush} is false. | |||||
* @since 4.8 | |||||
*/ | |||||
protected Merger(ObjectInserter oi) { | |||||
db = null; | |||||
inserter = oi; | |||||
reader = oi.newReader(); | |||||
walk = new RevWalk(reader); | |||||
} | |||||
/** | /** | ||||
* @return the repository this merger operates on. | * @return the repository this merger operates on. | ||||
*/ | */ | ||||
@Nullable | |||||
public Repository getRepository() { | public Repository getRepository() { | ||||
return db; | return db; | ||||
} | } | ||||
/** @return an object writer to create objects in {@link #getRepository()}. */ | |||||
/** | |||||
* @return non-null repository instance | |||||
* @throws NullPointerException | |||||
* if the merger was constructed without a repository. | |||||
* @since 4.8 | |||||
*/ | |||||
protected Repository nonNullRepo() { | |||||
if (db == null) { | |||||
throw new NullPointerException(JGitText.get().repositoryIsRequired); | |||||
} | |||||
return db; | |||||
} | |||||
/** | |||||
* @return an object writer to create objects, writing objects to {@link | |||||
* #getRepository()} (if a repository was provided). | |||||
*/ | |||||
public ObjectInserter getObjectInserter() { | public ObjectInserter getObjectInserter() { | ||||
return inserter; | return inserter; | ||||
} | } | ||||
* | * | ||||
* @param oi | * @param oi | ||||
* the inserter instance to use. Must be associated with the | * the inserter instance to use. Must be associated with the | ||||
* repository instance returned by {@link #getRepository()}. | |||||
* repository instance returned by {@link #getRepository()} (if a | |||||
* repository was provided). Will be closed at the conclusion of | |||||
* {@code merge}, unless {@code flush} is false. | |||||
*/ | */ | ||||
public void setObjectInserter(ObjectInserter oi) { | public void setObjectInserter(ObjectInserter oi) { | ||||
walk.close(); | walk.close(); | ||||
* | * | ||||
* @since 3.5 | * @since 3.5 | ||||
* @param flush | * @param flush | ||||
* whether to flush the underlying object inserter when finished to | |||||
* store any content-merged blobs and virtual merged bases; if | |||||
* false, callers are responsible for flushing. | |||||
* whether to flush and close the underlying object inserter when | |||||
* finished to store any content-merged blobs and virtual merged | |||||
* bases; if false, callers are responsible for flushing. | |||||
* @param tips | * @param tips | ||||
* source trees to be combined together. The merge base is not | * source trees to be combined together. The merge base is not | ||||
* included in this set. | * included in this set. |
import org.eclipse.jgit.errors.NoMergeBaseException; | import org.eclipse.jgit.errors.NoMergeBaseException; | ||||
import org.eclipse.jgit.internal.JGitText; | import org.eclipse.jgit.internal.JGitText; | ||||
import org.eclipse.jgit.lib.CommitBuilder; | import org.eclipse.jgit.lib.CommitBuilder; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.PersonIdent; | import org.eclipse.jgit.lib.PersonIdent; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
this(local, false); | this(local, false); | ||||
} | } | ||||
/** | |||||
* Normal recursive merge, implies inCore. | |||||
* | |||||
* @param inserter | |||||
* @param config | |||||
* @since 4.8 | |||||
*/ | |||||
protected RecursiveMerger(ObjectInserter inserter, Config config) { | |||||
super(inserter, config); | |||||
} | |||||
/** | /** | ||||
* Get a single base commit for two given commits. If the two source commits | * Get a single base commit for two given commits. If the two source commits | ||||
* have more than one base commit recursively merge the base commits | * have more than one base commit recursively merge the base commits |
*/ | */ | ||||
package org.eclipse.jgit.merge; | package org.eclipse.jgit.merge; | ||||
import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM; | |||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION; | |||||
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM; | |||||
import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; | import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; | ||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; | import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; | ||||
import org.eclipse.jgit.errors.IndexWriteException; | import org.eclipse.jgit.errors.IndexWriteException; | ||||
import org.eclipse.jgit.errors.MissingObjectException; | import org.eclipse.jgit.errors.MissingObjectException; | ||||
import org.eclipse.jgit.errors.NoWorkTreeException; | import org.eclipse.jgit.errors.NoWorkTreeException; | ||||
import org.eclipse.jgit.lib.ConfigConstants; | |||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.FileMode; | import org.eclipse.jgit.lib.FileMode; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.ObjectReader; | import org.eclipse.jgit.lib.ObjectReader; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.revwalk.RevTree; | import org.eclipse.jgit.revwalk.RevTree; | ||||
*/ | */ | ||||
protected MergeAlgorithm mergeAlgorithm; | protected MergeAlgorithm mergeAlgorithm; | ||||
private static MergeAlgorithm getMergeAlgorithm(Config config) { | |||||
SupportedAlgorithm diffAlg = config.getEnum( | |||||
CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM, | |||||
HISTOGRAM); | |||||
return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg)); | |||||
} | |||||
private static String[] defaultCommitNames() { | |||||
return new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |||||
} | |||||
/** | /** | ||||
* @param local | * @param local | ||||
* @param inCore | * @param inCore | ||||
*/ | */ | ||||
protected ResolveMerger(Repository local, boolean inCore) { | protected ResolveMerger(Repository local, boolean inCore) { | ||||
super(local); | super(local); | ||||
SupportedAlgorithm diffAlg = local.getConfig().getEnum( | |||||
ConfigConstants.CONFIG_DIFF_SECTION, null, | |||||
ConfigConstants.CONFIG_KEY_ALGORITHM, | |||||
SupportedAlgorithm.HISTOGRAM); | |||||
mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg)); | |||||
commitNames = new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |||||
mergeAlgorithm = getMergeAlgorithm(local.getConfig()); | |||||
commitNames = defaultCommitNames(); | |||||
this.inCore = inCore; | this.inCore = inCore; | ||||
if (inCore) { | if (inCore) { | ||||
this(local, false); | this(local, false); | ||||
} | } | ||||
/** | |||||
* @param inserter | |||||
* @param config | |||||
* @since 4.8 | |||||
*/ | |||||
protected ResolveMerger(ObjectInserter inserter, Config config) { | |||||
super(inserter); | |||||
mergeAlgorithm = getMergeAlgorithm(config); | |||||
commitNames = defaultCommitNames(); | |||||
inCore = true; | |||||
implicitDirCache = false; | |||||
dircache = DirCache.newInCore(); | |||||
} | |||||
@Override | @Override | ||||
protected boolean mergeImpl() throws IOException { | protected boolean mergeImpl() throws IOException { | ||||
if (implicitDirCache) | if (implicitDirCache) | ||||
dircache = getRepository().lockDirCache(); | |||||
dircache = nonNullRepo().lockDirCache(); | |||||
try { | try { | ||||
return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1], | return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1], | ||||
// of a non-empty directory, for which delete() would fail. | // of a non-empty directory, for which delete() would fail. | ||||
for (int i = toBeDeleted.size() - 1; i >= 0; i--) { | for (int i = toBeDeleted.size() - 1; i >= 0; i--) { | ||||
String fileName = toBeDeleted.get(i); | String fileName = toBeDeleted.get(i); | ||||
File f = new File(db.getWorkTree(), fileName); | |||||
File f = new File(nonNullRepo().getWorkTree(), fileName); | |||||
if (!f.delete()) | if (!f.delete()) | ||||
if (!f.isDirectory()) | if (!f.isDirectory()) | ||||
failingPaths.put(fileName, | failingPaths.put(fileName, | ||||
return; | return; | ||||
} | } | ||||
DirCache dc = db.readDirCache(); | |||||
DirCache dc = nonNullRepo().readDirCache(); | |||||
Iterator<String> mpathsIt=modifiedFiles.iterator(); | Iterator<String> mpathsIt=modifiedFiles.iterator(); | ||||
while(mpathsIt.hasNext()) { | while(mpathsIt.hasNext()) { | ||||
String mpath=mpathsIt.next(); | String mpath=mpathsIt.next(); | ||||
*/ | */ | ||||
private File writeMergedFile(MergeResult<RawText> result) | private File writeMergedFile(MergeResult<RawText> result) | ||||
throws FileNotFoundException, IOException { | throws FileNotFoundException, IOException { | ||||
File workTree = db.getWorkTree(); | |||||
FS fs = db.getFS(); | |||||
File workTree = nonNullRepo().getWorkTree(); | |||||
FS fs = nonNullRepo().getFS(); | |||||
File of = new File(workTree, tw.getPathString()); | File of = new File(workTree, tw.getPathString()); | ||||
File parentFolder = of.getParentFile(); | File parentFolder = of.getParentFile(); | ||||
if (!fs.exists(parentFolder)) | if (!fs.exists(parentFolder)) | ||||
private ObjectId insertMergeResult(MergeResult<RawText> result) | private ObjectId insertMergeResult(MergeResult<RawText> result) | ||||
throws IOException { | throws IOException { | ||||
TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile( | TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile( | ||||
db.getDirectory(), 10 << 20); | |||||
db != null ? nonNullRepo().getDirectory() : null, 10 << 20); | |||||
try { | try { | ||||
new MergeFormatter().formatMerge(buf, result, | new MergeFormatter().formatMerge(buf, result, | ||||
Arrays.asList(commitNames), CHARACTER_ENCODING); | Arrays.asList(commitNames), CHARACTER_ENCODING); |
import java.io.IOException; | import java.io.IOException; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
/** | /** | ||||
return new OneSide(db, treeIndex); | return new OneSide(db, treeIndex); | ||||
} | } | ||||
@Override | |||||
public Merger newMerger(final ObjectInserter inserter, final Config config) { | |||||
return new OneSide(inserter, treeIndex); | |||||
} | |||||
static class OneSide extends Merger { | static class OneSide extends Merger { | ||||
private final int treeIndex; | private final int treeIndex; | ||||
treeIndex = index; | treeIndex = index; | ||||
} | } | ||||
protected OneSide(final ObjectInserter inserter, final int index) { | |||||
super(inserter); | |||||
treeIndex = index; | |||||
} | |||||
@Override | @Override | ||||
protected boolean mergeImpl() throws IOException { | protected boolean mergeImpl() throws IOException { | ||||
return treeIndex < sourceTrees.length; | return treeIndex < sourceTrees.length; |
package org.eclipse.jgit.merge; | package org.eclipse.jgit.merge; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
/** | /** | ||||
return new RecursiveMerger(db, inCore); | return new RecursiveMerger(db, inCore); | ||||
} | } | ||||
@Override | |||||
public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) { | |||||
return new RecursiveMerger(inserter, config); | |||||
} | |||||
@Override | @Override | ||||
public String getName() { | public String getName() { | ||||
return "recursive"; //$NON-NLS-1$ | return "recursive"; //$NON-NLS-1$ |
*/ | */ | ||||
package org.eclipse.jgit.merge; | package org.eclipse.jgit.merge; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
/** | /** | ||||
return new ResolveMerger(db, inCore); | return new ResolveMerger(db, inCore); | ||||
} | } | ||||
@Override | |||||
public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) { | |||||
return new ResolveMerger(inserter, config); | |||||
} | |||||
@Override | @Override | ||||
public String getName() { | public String getName() { | ||||
return "resolve"; //$NON-NLS-1$ | return "resolve"; //$NON-NLS-1$ | ||||
} | } | ||||
} | |||||
} |
import org.eclipse.jgit.dircache.DirCacheBuilder; | import org.eclipse.jgit.dircache.DirCacheBuilder; | ||||
import org.eclipse.jgit.dircache.DirCacheEntry; | import org.eclipse.jgit.dircache.DirCacheEntry; | ||||
import org.eclipse.jgit.errors.UnmergedPathException; | import org.eclipse.jgit.errors.UnmergedPathException; | ||||
import org.eclipse.jgit.lib.Config; | |||||
import org.eclipse.jgit.lib.FileMode; | import org.eclipse.jgit.lib.FileMode; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | import org.eclipse.jgit.lib.ObjectInserter; | ||||
return newMerger(db); | return newMerger(db); | ||||
} | } | ||||
@Override | |||||
public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) { | |||||
return new InCoreMerger(inserter); | |||||
} | |||||
private static class InCoreMerger extends ThreeWayMerger { | private static class InCoreMerger extends ThreeWayMerger { | ||||
private static final int T_BASE = 0; | private static final int T_BASE = 0; | ||||
cache = DirCache.newInCore(); | cache = DirCache.newInCore(); | ||||
} | } | ||||
InCoreMerger(final ObjectInserter inserter) { | |||||
super(inserter); | |||||
tw = new NameConflictTreeWalk(null, reader); | |||||
cache = DirCache.newInCore(); | |||||
} | |||||
@Override | @Override | ||||
protected boolean mergeImpl() throws IOException { | protected boolean mergeImpl() throws IOException { | ||||
tw.addTree(mergeBase()); | tw.addTree(mergeBase()); |
import org.eclipse.jgit.errors.MissingObjectException; | import org.eclipse.jgit.errors.MissingObjectException; | ||||
import org.eclipse.jgit.lib.AnyObjectId; | import org.eclipse.jgit.lib.AnyObjectId; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.revwalk.RevTree; | import org.eclipse.jgit.revwalk.RevTree; | ||||
this(local); | this(local); | ||||
} | } | ||||
/** | |||||
* Create a new in-core merge instance from an inserter. | |||||
* | |||||
* @param inserter | |||||
* the inserter to write objects to. | |||||
* @since 4.8 | |||||
*/ | |||||
protected ThreeWayMerger(ObjectInserter inserter) { | |||||
super(inserter); | |||||
} | |||||
/** | /** | ||||
* Set the common ancestor tree. | * Set the common ancestor tree. | ||||
* | * |