diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2012-02-16 00:44:36 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2012-02-16 00:44:47 +0100 |
commit | 42b0fb467916f17ea62c147e2a7387b193d765fd (patch) | |
tree | 8d9f33665b53c3d21317fc0b9db5e09dfe3eab09 | |
parent | de48250da5a424ffbf26db0a4b8bf0d83f39f76e (diff) | |
parent | f70b24a60176c3bd35c1f67e9ea039078ea9a5ad (diff) | |
download | jgit-42b0fb467916f17ea62c147e2a7387b193d765fd.tar.gz jgit-42b0fb467916f17ea62c147e2a7387b193d765fd.zip |
Merge branch 'stable-1.3'
* stable-1.3:
Prepare post 1.3.0.201202151440-r build
JGit 1.3.0.201202151440-r
Generate conflicts and index updates on file mode changes
Change-Id: Ie99780ef5cdea7b3ea1ea076282fe0a25f14f469
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java | 203 | ||||
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java | 41 |
2 files changed, 235 insertions, 9 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index fafd745b5a..efb686da59 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -55,22 +55,27 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jgit.api.CheckoutCommand; +import org.eclipse.jgit.api.CheckoutResult; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.dircache.DirCacheEditor; -import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; +import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.errors.CheckoutConflictException; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.FS; import org.junit.Test; public class DirCacheCheckoutTest extends RepositoryTestCase { @@ -939,6 +944,202 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { } } + @Test + public void testFileModeChangeWithNoContentChangeUpdate() throws Exception { + if (!FS.DETECTED.supportsExecute()) + return; + + Git git = Git.wrap(db); + + // Add non-executable file + File file = writeTrashFile("file.txt", "a"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit1").call(); + assertFalse(db.getFS().canExecute(file)); + + // Create branch + git.branchCreate().setName("b1").call(); + + // Make file executable + db.getFS().setExecute(file, true); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit2").call(); + + // Verify executable and working directory is clean + Status status = git.status().call(); + assertTrue(status.getModified().isEmpty()); + assertTrue(status.getChanged().isEmpty()); + assertTrue(db.getFS().canExecute(file)); + + // Switch branches + git.checkout().setName("b1").call(); + + // Verify not executable and working directory is clean + status = git.status().call(); + assertTrue(status.getModified().isEmpty()); + assertTrue(status.getChanged().isEmpty()); + assertFalse(db.getFS().canExecute(file)); + } + + @Test + public void testFileModeChangeAndContentChangeConflict() throws Exception { + if (!FS.DETECTED.supportsExecute()) + return; + + Git git = Git.wrap(db); + + // Add non-executable file + File file = writeTrashFile("file.txt", "a"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit1").call(); + assertFalse(db.getFS().canExecute(file)); + + // Create branch + git.branchCreate().setName("b1").call(); + + // Make file executable + db.getFS().setExecute(file, true); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit2").call(); + + // Verify executable and working directory is clean + Status status = git.status().call(); + assertTrue(status.getModified().isEmpty()); + assertTrue(status.getChanged().isEmpty()); + assertTrue(db.getFS().canExecute(file)); + + writeTrashFile("file.txt", "b"); + + // Switch branches + CheckoutCommand checkout = git.checkout().setName("b1"); + try { + checkout.call(); + fail("Checkout exception not thrown"); + } catch (JGitInternalException e) { + CheckoutResult result = checkout.getResult(); + assertNotNull(result); + assertNotNull(result.getConflictList()); + assertEquals(1, result.getConflictList().size()); + assertTrue(result.getConflictList().contains("file.txt")); + } + } + + @Test + public void testDirtyFileModeEqualHeadMerge() + throws Exception { + if (!FS.DETECTED.supportsExecute()) + return; + + Git git = Git.wrap(db); + + // Add non-executable file + File file = writeTrashFile("file.txt", "a"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit1").call(); + assertFalse(db.getFS().canExecute(file)); + + // Create branch + git.branchCreate().setName("b1").call(); + + // Create second commit and don't touch file + writeTrashFile("file2.txt", ""); + git.add().addFilepattern("file2.txt").call(); + git.commit().setMessage("commit2").call(); + + // stage a mode change + writeTrashFile("file.txt", "a"); + db.getFS().setExecute(file, true); + git.add().addFilepattern("file.txt").call(); + + // dirty the file + writeTrashFile("file.txt", "b"); + + assertEquals( + "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]", + indexState(CONTENT)); + assertWorkDir(mkmap("file.txt", "b", "file2.txt", "")); + + // Switch branches and check that the dirty file survived in worktree + // and index + git.checkout().setName("b1").call(); + assertEquals("[file.txt, mode:100755, content:a]", indexState(CONTENT)); + assertWorkDir(mkmap("file.txt", "b")); + } + + @Test + public void testDirtyFileModeEqualIndexMerge() + throws Exception { + if (!FS.DETECTED.supportsExecute()) + return; + + Git git = Git.wrap(db); + + // Add non-executable file + File file = writeTrashFile("file.txt", "a"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit1").call(); + assertFalse(db.getFS().canExecute(file)); + + // Create branch + git.branchCreate().setName("b1").call(); + + // Create second commit with executable file + file = writeTrashFile("file.txt", "b"); + db.getFS().setExecute(file, true); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("commit2").call(); + + // stage the same content as in the branch we want to switch to + writeTrashFile("file.txt", "a"); + db.getFS().setExecute(file, false); + git.add().addFilepattern("file.txt").call(); + + // dirty the file + writeTrashFile("file.txt", "c"); + db.getFS().setExecute(file, true); + + assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT)); + assertWorkDir(mkmap("file.txt", "c")); + + // Switch branches and check that the dirty file survived in worktree + // and index + git.checkout().setName("b1").call(); + assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT)); + assertWorkDir(mkmap("file.txt", "c")); + } + + @Test + public void testFileModeChangeAndContentChangeNoConflict() throws Exception { + if (!FS.DETECTED.supportsExecute()) + return; + + Git git = Git.wrap(db); + + // Add first file + File file1 = writeTrashFile("file1.txt", "a"); + git.add().addFilepattern("file1.txt").call(); + git.commit().setMessage("commit1").call(); + assertFalse(db.getFS().canExecute(file1)); + + // Add second file + File file2 = writeTrashFile("file2.txt", "b"); + git.add().addFilepattern("file2.txt").call(); + git.commit().setMessage("commit2").call(); + assertFalse(db.getFS().canExecute(file2)); + + // Create branch from first commit + assertNotNull(git.checkout().setCreateBranch(true).setName("b1") + .setStartPoint(Constants.HEAD + "~1").call()); + + // Change content and file mode in working directory and index + file1 = writeTrashFile("file1.txt", "c"); + db.getFS().setExecute(file1, true); + git.add().addFilepattern("file1.txt").call(); + + // Switch back to 'master' + assertNotNull(git.checkout().setName(Constants.MASTER).call()); + } + public void assertWorkDir(HashMap<String, String> i) throws CorruptObjectException, IOException { TreeWalk walk = new TreeWalk(db); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 9b397e88c9..58b691228d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -473,6 +473,23 @@ public class DirCacheCheckout { } /** + * Compares whether two pairs of ObjectId and FileMode are equal. + * + * @param id1 + * @param mode1 + * @param id2 + * @param mode2 + * @return <code>true</code> if FileModes and ObjectIds are equal. + * <code>false</code> otherwise + */ + private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2, + FileMode mode2) { + if (!mode1.equals(mode2)) + return false; + return id1 != null ? id1.equals(id2) : id2 == null; + } + + /** * Here the main work is done. This method is called for each existing path * in head, index and merge. This method decides what to do with the * corresponding index entry: keep it, update it, remove it or mark a @@ -508,6 +525,9 @@ public class DirCacheCheckout { ObjectId iId = (i == null ? null : i.getEntryObjectId()); ObjectId mId = (m == null ? null : m.getEntryObjectId()); ObjectId hId = (h == null ? null : h.getEntryObjectId()); + FileMode iMode = (i == null ? null : i.getEntryFileMode()); + FileMode mMode = (m == null ? null : m.getEntryFileMode()); + FileMode hMode = (h == null ? null : h.getEntryFileMode()); /** * <pre> @@ -596,7 +616,7 @@ public class DirCacheCheckout { case 0xFDD: // 10 11 // TODO: make use of tree extension as soon as available in jgit // we would like to do something like - // if (!iId.equals(mId)) + // if (!equalIdAndMode(iId, iMode, mId, mMode) // conflict(name, i.getDirCacheEntry(), h, m); // But since we don't know the id of a tree in the index we do // nothing here and wait that conflicts between index and merge @@ -610,7 +630,7 @@ public class DirCacheCheckout { conflict(name, (i != null) ? i.getDirCacheEntry() : null, h, m); break; case 0xFDF: // 7 8 9 - if (hId.equals(mId)) { + if (equalIdAndMode(hId, hMode, mId, mMode)) { if (isModified(name)) conflict(name, i.getDirCacheEntry(), h, m); // 8 else @@ -625,7 +645,7 @@ public class DirCacheCheckout { keep(i.getDirCacheEntry()); break; case 0xFFD: // 12 13 14 - if (hId.equals(iId)) { + if (equalIdAndMode(hId, hMode, iId, iMode)) { dce = i.getDirCacheEntry(); if (f == null || f.isModified(dce, true)) conflict(name, dce, h, m); @@ -662,7 +682,9 @@ public class DirCacheCheckout { if (!FileMode.GITLINK.equals(m.getEntryFileMode())) { // a dirty worktree: the index is empty but we have a // workingtree-file - if (mId == null || !mId.equals(f.getEntryObjectId())) { + if (mId == null + || !equalIdAndMode(mId, mMode, + f.getEntryObjectId(), f.getEntryFileMode())) { conflict(name, null, h, m); return; } @@ -703,7 +725,7 @@ public class DirCacheCheckout { * </pre> */ - if (m == null || mId.equals(iId)) { + if (m == null || equalIdAndMode(mId, mMode, iId, iMode)) { if (m==null && walk.isDirectoryFileConflict()) { if (dce != null && (f == null || f.isModified(dce, true))) @@ -732,7 +754,7 @@ public class DirCacheCheckout { // be removed from the index, but not deleted from disk. remove(name); } else { - if (hId.equals(iId)) { + if (equalIdAndMode(hId, hMode, iId, iMode)) { if (f == null || f.isModified(dce, true)) conflict(name, dce, h, m); else @@ -741,9 +763,12 @@ public class DirCacheCheckout { conflict(name, dce, h, m); } } else { - if (!hId.equals(mId) && !hId.equals(iId) && !mId.equals(iId)) + if (!equalIdAndMode(hId, hMode, mId, mMode) + && !equalIdAndMode(hId, hMode, iId, iMode) + && !equalIdAndMode(mId, mMode, iId, iMode)) conflict(name, dce, h, m); - else if (hId.equals(iId) && !mId.equals(iId)) { + else if (equalIdAndMode(hId, hMode, iId, iMode) + && !equalIdAndMode(mId, mMode, iId, iMode)) { // For submodules just update the index with the new SHA-1 if (dce != null && FileMode.GITLINK.equals(dce.getFileMode())) { |