diff options
author | David Turner <dturner@twosigma.com> | 2017-10-12 18:43:15 -0400 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2018-02-04 15:33:24 +0100 |
commit | be2a8ed63b74f977f0b78dc5909f94317c7b571e (patch) | |
tree | 0bc66b0e58ce3a51d4a66fc2436eec2836102b2a /org.eclipse.jgit.test | |
parent | 595f968b6d4e50f09afab791007be73f14b0e5a9 (diff) | |
download | jgit-be2a8ed63b74f977f0b78dc5909f94317c7b571e.tar.gz jgit-be2a8ed63b74f977f0b78dc5909f94317c7b571e.zip |
Basic submodule merge handling
This doesn't handle the really hard thing, which is merging spurious
conflicts inside .gitmodules files. That's OK: git.git doesn't
either. Users can resolve the conflict themselves and then commit
the merge.
Previously, jgit would crash when attempting to merge conflicting
submodule changes. Even if there was no conflict, after a merge which
adds submodules, the repository would have been missing empty
directories for newly-added submodules.
This patch fixes the crash, and adds the empty directories where
necessary. It ensures that the index is in a conflicted state when
submodule changes conflict.
Reported-by: Alexey Korobkov
Bug: 494551
Change-Id: I79db6798c2bdcc1159b5b2589b02da198dc906a1
Signed-off-by: David Turner <dturner@twosigma.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.test')
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java | 4 | ||||
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java | 151 |
2 files changed, 153 insertions, 2 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index aafda0171c..df4be4523e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java @@ -1250,9 +1250,9 @@ public class AddCommandTest extends RepositoryTestCase { try (Git git = new Git(db)) { git.add().addFilepattern("nested-repo").call(); - + // with gitlinks ignored, we treat this as a normal directory assertEquals( - "[nested-repo, mode:160000]", + "[nested-repo/README1.md, mode:100644][nested-repo/README2.md, mode:100644]", indexState(0)); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java index ab998ebfb8..9322a4734e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java @@ -46,6 +46,8 @@ 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.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; @@ -53,6 +55,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; +import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult; @@ -61,21 +64,29 @@ import org.eclipse.jgit.api.errors.CheckoutConflictException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.NoMergeBaseException; import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectStream; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; 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.storage.file.FileBasedConfig; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; @@ -1130,6 +1141,146 @@ public class ResolveMergerTest extends RepositoryTestCase { indexState(CONTENT)); } + /** + * Merging two conflicting submodules when the index does not contain any + * entry for that submodule. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkMergeConflictingSubmodulesWithoutIndex( + MergeStrategy strategy) throws Exception { + Git git = Git.wrap(db); + writeTrashFile("initial", "initial"); + git.add().addFilepattern("initial").call(); + RevCommit initial = git.commit().setMessage("initial").call(); + + writeSubmodule("one", ObjectId + .fromString("1000000000000000000000000000000000000000")); + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + RevCommit right = git.commit().setMessage("added one").call(); + + // a second commit in the submodule + + git.checkout().setStartPoint(initial).setName("left") + .setCreateBranch(true).call(); + writeSubmodule("one", ObjectId + .fromString("2000000000000000000000000000000000000000")); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + git.commit().setMessage("a different one").call(); + + MergeResult result = git.merge().setStrategy(strategy).include(right) + .call(); + + assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); + Map<String, int[][]> conflicts = result.getConflicts(); + assertEquals(1, conflicts.size()); + assertNotNull(conflicts.get("one")); + } + + /** + * Merging two non-conflicting submodules when the index does not contain + * any entry for either submodule. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkMergeNonConflictingSubmodulesWithoutIndex( + MergeStrategy strategy) throws Exception { + Git git = Git.wrap(db); + writeTrashFile("initial", "initial"); + git.add().addFilepattern("initial").call(); + + writeSubmodule("one", ObjectId + .fromString("1000000000000000000000000000000000000000")); + + // Our initial commit should include a .gitmodules with a bunch of + // comment lines, so that + // we don't have a content merge issue when we add a new submodule at + // the top and a different + // one at the bottom. This is sort of a hack, but it should allow + // add/add submodule merges + String existing = read(Constants.DOT_GIT_MODULES); + String context = "\n# context\n# more context\n# yet more context\n"; + write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + existing + context + context + context); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + RevCommit initial = git.commit().setMessage("initial").call(); + + writeSubmodule("two", ObjectId + .fromString("1000000000000000000000000000000000000000")); + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + + RevCommit right = git.commit().setMessage("added two").call(); + + git.checkout().setStartPoint(initial).setName("left") + .setCreateBranch(true).call(); + + // we need to manually create the submodule for three for the + // .gitmodules hackery + addSubmoduleToIndex("three", ObjectId + .fromString("1000000000000000000000000000000000000000")); + new File(db.getWorkTree(), "three").mkdir(); + + existing = read(Constants.DOT_GIT_MODULES); + String three = "[submodule \"three\"]\n\tpath = three\n\turl = " + + db.getDirectory().toURI() + "\n"; + write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + three + existing); + + git.add().addFilepattern(Constants.DOT_GIT_MODULES).call(); + git.commit().setMessage("a different one").call(); + + MergeResult result = git.merge().setStrategy(strategy).include(right) + .call(); + + assertNull(result.getCheckoutConflicts()); + assertNull(result.getFailingPaths()); + for (String dir : Arrays.asList("one", "two", "three")) { + assertTrue(new File(db.getWorkTree(), dir).isDirectory()); + } + } + + private void writeSubmodule(String path, ObjectId commit) + throws IOException, ConfigInvalidException { + addSubmoduleToIndex(path, commit); + new File(db.getWorkTree(), path).mkdir(); + + StoredConfig config = db.getConfig(); + config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, + ConfigConstants.CONFIG_KEY_URL, + db.getDirectory().toURI().toString()); + config.save(); + + FileBasedConfig modulesConfig = new FileBasedConfig( + new File(db.getWorkTree(), Constants.DOT_GIT_MODULES), + db.getFS()); + modulesConfig.load(); + modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, + ConfigConstants.CONFIG_KEY_PATH, path); + modulesConfig.save(); + + } + + private void addSubmoduleToIndex(String path, ObjectId commit) + throws IOException { + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new DirCacheEditor.PathEdit(path) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(commit); + } + }); + editor.commit(); + } + // Assert that every specified index entry has the same last modification // timestamp as the associated file private void checkConsistentLastModified(String... pathes) |