From 83c172f0f7d1fb5ceb14fd58704ae18dc59a1e33 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Wed, 19 Oct 2011 23:14:41 +0200 Subject: [PATCH] Deprecate GitIndex more by using only DirCache internally. This includes merging ReadTreeTest into DirCacheCheckoutTest and converting IndexDiffTest to use DirCache only. The GitIndex specific T0007GitIndex test remains. GitIndex is deprecated. Let us speed up its demise by focusing the DirCacheCheckout tests to using DirCache instead. This also add explicit deprecation comments to methods that depend on GitIndex in Repository and TreeEntry. The latter is deprecated in itself. Change-Id: Id89262f7fbfee07871f444378f196ded444f2783 Signed-off-by: Robin Rosenberg --- .../jgit/lib/DirCacheCheckoutTest.java | 762 ++++++++++++++++- .../org/eclipse/jgit/lib/IndexDiffTest.java | 88 +- .../org/eclipse/jgit/lib/ReadTreeTest.java | 802 ------------------ .../file/RepositorySetupWorkDirTest.java | 2 +- .../src/org/eclipse/jgit/lib/Repository.java | 1 + .../src/org/eclipse/jgit/lib/TreeEntry.java | 1 + 6 files changed, 810 insertions(+), 846 deletions(-) delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java 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 8c3f6f37c..3001880d2 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 @@ -1,6 +1,9 @@ /* - * Copyright (C) 2010, Christian Halstrick and - * other copyright owners as documented in the project's IP log. + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2008-2011, Shawn O. Pearce + * Copyright (C) 2008-2011, Robin Rosenberg + * Copyright (C) 2010-2011, Christian Halstrick + * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v1.0 which accompanies this @@ -38,10 +41,16 @@ package org.eclipse.jgit.lib; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,15 +61,23 @@ import org.eclipse.jgit.api.errors.GitAPIException; 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.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.junit.Test; -public class DirCacheCheckoutTest extends ReadTreeTest { +public class DirCacheCheckoutTest extends RepositoryTestCase { private DirCacheCheckout dco; - @Override - public void prescanTwoTrees(Tree head, Tree merge) + protected Tree theHead; + protected Tree theMerge; + private DirCache dirCache; + + private void prescanTwoTrees(Tree head, Tree merge) throws IllegalStateException, IOException { DirCache dc = db.lockDirCache(); try { @@ -71,8 +88,7 @@ public class DirCacheCheckoutTest extends ReadTreeTest { } } - @Override - public void checkout() throws IOException { + private void checkout() throws IOException { DirCache dc = db.lockDirCache(); try { dco = new DirCacheCheckout(db, theHead.getId(), dc, theMerge.getId()); @@ -82,21 +98,34 @@ public class DirCacheCheckoutTest extends ReadTreeTest { } } - @Override - public List getRemoved() { + private List getRemoved() { return dco.getRemoved(); } - @Override - public Map getUpdated() { + private Map getUpdated() { return dco.getUpdated(); } - @Override - public List getConflicts() { + private List getConflicts() { return dco.getConflicts(); } + private static HashMap mk(String a) { + return mkmap(a, a); + } + + private static HashMap mkmap(String... args) { + if ((args.length % 2) > 0) + throw new IllegalArgumentException("needs to be pairs"); + + HashMap map = new HashMap(); + for (int i = 0; i < args.length; i += 2) { + map.put(args[i], args[i + 1]); + } + + return map; + } + @Test public void testResetHard() throws IOException, NoFilepatternException, GitAPIException { @@ -187,4 +216,711 @@ public class DirCacheCheckoutTest extends ReadTreeTest { assertTrue(dc.checkout()); return dc; } + + private void assertIndex(HashMap i) + throws CorruptObjectException, IOException { + String expectedValue; + String path; + DirCache read = DirCache.read(db.getIndexFile(), db.getFS()); + + assertEquals("Index has not the right size.", i.size(), + read.getEntryCount()); + for (int j = 0; j < read.getEntryCount(); j++) { + path = read.getEntry(j).getPathString(); + expectedValue = i.get(path); + assertNotNull("found unexpected entry for path " + path + + " in index", expectedValue); + assertTrue("unexpected content for path " + path + + " in index. Expected: <" + expectedValue + ">", + Arrays.equals(db.open(read.getEntry(j).getObjectId()) + .getCachedBytes(), i.get(path).getBytes())); + } + } + + @Test + public void testRules1thru3_NoIndexEntry() throws IOException { + Tree head = new Tree(db); + head = buildTree(mk("foo")); + ObjectId objectId = head.findBlobMember("foo").getId(); + Tree merge = new Tree(db); + + prescanTwoTrees(head, merge); + + assertTrue(getRemoved().contains("foo")); + + prescanTwoTrees(merge, head); + + assertEquals(objectId, getUpdated().get("foo")); + + merge = buildTree(mkmap("foo", "a")); + ObjectId anotherId = merge.findBlobMember("foo").getId(); + + prescanTwoTrees(head, merge); + + assertEquals(anotherId, getUpdated().get("foo")); + } + + void setupCase(HashMap headEntries, HashMap mergeEntries, HashMap indexEntries) throws IOException { + theHead = buildTree(headEntries); + theMerge = buildTree(mergeEntries); + buildIndex(indexEntries); + } + + private void buildIndex(HashMap indexEntries) throws IOException { + dirCache = new DirCache(db.getIndexFile(), db.getFS()); + if (indexEntries != null) { + assertTrue(dirCache.lock()); + DirCacheEditor editor = dirCache.editor(); + for (java.util.Map.Entry e : indexEntries.entrySet()) { + writeTrashFile(e.getKey(), e.getValue()); + ObjectInserter inserter = db.newObjectInserter(); + final ObjectId id = inserter.insert(Constants.OBJ_BLOB, + Constants.encode(e.getValue())); + editor.add(new DirCacheEditor.DeletePath(e.getKey())); + editor.add(new DirCacheEditor.PathEdit(e.getKey()) { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.REGULAR_FILE); + ent.setObjectId(id); + ent.setUpdateNeeded(false); + } + }); + } + assertTrue(editor.commit()); + } + + } + + private Tree buildTree(HashMap headEntries) throws IOException { + Tree tree = new Tree(db); + if (headEntries == null) + return tree; + FileTreeEntry fileEntry; + Tree parent; + ObjectInserter oi = db.newObjectInserter(); + try { + for (java.util.Map.Entry e : headEntries.entrySet()) { + fileEntry = tree.addFile(e.getKey()); + fileEntry.setId(genSha1(e.getValue())); + parent = fileEntry.getParent(); + while (parent != null) { + parent.setId(oi.insert(Constants.OBJ_TREE, parent.format())); + parent = parent.getParent(); + } + } + oi.flush(); + } finally { + oi.release(); + } + return tree; + } + + ObjectId genSha1(String data) { + ObjectInserter w = db.newObjectInserter(); + try { + ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes()); + w.flush(); + return id; + } catch (IOException e) { + fail(e.toString()); + } finally { + w.release(); + } + return null; + } + + protected void go() throws IllegalStateException, IOException { + prescanTwoTrees(theHead, theMerge); + } + + @Test + public void testRules4thru13_IndexEntryNotInHead() throws IOException { + // rules 4 and 5 + HashMap idxMap; + + idxMap = new HashMap(); + idxMap.put("foo", "foo"); + setupCase(null, null, idxMap); + go(); + + assertTrue(getUpdated().isEmpty()); + assertTrue(getRemoved().isEmpty()); + assertTrue(getConflicts().isEmpty()); + + // rules 6 and 7 + idxMap = new HashMap(); + idxMap.put("foo", "foo"); + setupCase(null, idxMap, idxMap); + go(); + + assertAllEmpty(); + + // rules 8 and 9 + HashMap mergeMap; + mergeMap = new HashMap(); + + mergeMap.put("foo", "merge"); + setupCase(null, mergeMap, idxMap); + go(); + + assertTrue(getUpdated().isEmpty()); + assertTrue(getRemoved().isEmpty()); + assertTrue(getConflicts().contains("foo")); + + // rule 10 + + HashMap headMap = new HashMap(); + headMap.put("foo", "foo"); + setupCase(headMap, null, idxMap); + go(); + + assertTrue(getRemoved().contains("foo")); + assertTrue(getUpdated().isEmpty()); + assertTrue(getConflicts().isEmpty()); + + // rule 11 + setupCase(headMap, null, idxMap); + assertTrue(new File(trash, "foo").delete()); + writeTrashFile("foo", "bar"); + db.readDirCache().getEntry(0).setUpdateNeeded(true); + go(); + + assertTrue(getRemoved().isEmpty()); + assertTrue(getUpdated().isEmpty()); + assertTrue(getConflicts().contains("foo")); + + // rule 12 & 13 + headMap.put("foo", "head"); + setupCase(headMap, null, idxMap); + go(); + + assertTrue(getRemoved().isEmpty()); + assertTrue(getUpdated().isEmpty()); + assertTrue(getConflicts().contains("foo")); + + // rules 14 & 15 + setupCase(headMap, headMap, idxMap); + go(); + + assertAllEmpty(); + + // rules 16 & 17 + setupCase(headMap, mergeMap, idxMap); go(); + assertTrue(getConflicts().contains("foo")); + + // rules 18 & 19 + setupCase(headMap, idxMap, idxMap); go(); + assertAllEmpty(); + + // rule 20 + setupCase(idxMap, mergeMap, idxMap); go(); + assertTrue(getUpdated().containsKey("foo")); + + // rules 21 + setupCase(idxMap, mergeMap, idxMap); + assertTrue(new File(trash, "foo").delete()); + writeTrashFile("foo", "bar"); + db.readDirCache().getEntry(0).setUpdateNeeded(true); + go(); + assertTrue(getConflicts().contains("foo")); + } + + private void assertAllEmpty() { + assertTrue(getRemoved().isEmpty()); + assertTrue(getUpdated().isEmpty()); + assertTrue(getConflicts().isEmpty()); + } + + /*- + * Directory/File Conflict cases: + * It's entirely possible that in practice a number of these may be equivalent + * to the cases described in git-read-tree.txt. As long as it does the right thing, + * that's all I care about. These are basically reverse-engineered from + * what git currently does. If there are tests for these in git, it's kind of + * hard to track them all down... + * + * H I M Clean H==M H==I I==M Result + * ------------------------------------------------------------------ + *1 D D F Y N Y N Update + *2 D D F N N Y N Conflict + *3 D F D Y N N Update + *4 D F D N N N Update + *5 D F F Y N N Y Keep + *6 D F F N N N Y Keep + *7 F D F Y Y N N Update + *8 F D F N Y N N Conflict + *9 F D F Y N N N Update + *10 F D D N N Y Keep + *11 F D D N N N Conflict + *12 F F D Y N Y N Update + *13 F F D N N Y N Conflict + *14 F F D N N N Conflict + *15 0 F D N N N Conflict + *16 0 D F Y N N N Update + *17 0 D F N N N Conflict + *18 F 0 D Update + *19 D 0 F Update + */ + @Test + public void testDirectoryFileSimple() throws IOException { + Tree treeDF = buildTree(mkmap("DF", "DF")); + Tree treeDFDF = buildTree(mkmap("DF/DF", "DF/DF")); + buildIndex(mkmap("DF", "DF")); + + prescanTwoTrees(treeDF, treeDFDF); + + assertTrue(getRemoved().contains("DF")); + assertTrue(getUpdated().containsKey("DF/DF")); + + recursiveDelete(new File(trash, "DF")); + buildIndex(mkmap("DF/DF", "DF/DF")); + + prescanTwoTrees(treeDFDF, treeDF); + assertTrue(getRemoved().contains("DF/DF")); + assertTrue(getUpdated().containsKey("DF")); + } + + @Test + public void testDirectoryFileConflicts_1() throws Exception { + // 1 + doit(mk("DF/DF"), mk("DF"), mk("DF/DF")); + assertNoConflicts(); + assertUpdated("DF"); + assertRemoved("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_2() throws Exception { + // 2 + setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF")); + writeTrashFile("DF/DF", "different"); + go(); + assertConflict("DF/DF"); + + } + + @Test + public void testDirectoryFileConflicts_3() throws Exception { + // 3 - the first to break! + doit(mk("DF/DF"), mk("DF/DF"), mk("DF")); + assertUpdated("DF/DF"); + assertRemoved("DF"); + } + + @Test + public void testDirectoryFileConflicts_4() throws Exception { + // 4 (basically same as 3, just with H and M different) + doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF")); + assertUpdated("DF/DF"); + assertRemoved("DF"); + + } + + @Test + public void testDirectoryFileConflicts_5() throws Exception { + // 5 + doit(mk("DF/DF"), mk("DF"), mk("DF")); + assertRemoved("DF/DF"); + + } + + @Test + public void testDirectoryFileConflicts_6() throws Exception { + // 6 + setupCase(mk("DF/DF"), mk("DF"), mk("DF")); + writeTrashFile("DF", "different"); + go(); + assertRemoved("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_7() throws Exception { + // 7 + doit(mk("DF"), mk("DF"), mk("DF/DF")); + assertUpdated("DF"); + assertRemoved("DF/DF"); + + cleanUpDF(); + setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); + go(); + assertRemoved("DF/DF/DF/DF/DF"); + assertUpdated("DF/DF"); + + cleanUpDF(); + setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); + writeTrashFile("DF/DF/DF/DF/DF", "diff"); + go(); + assertConflict("DF/DF/DF/DF/DF"); + + // assertUpdated("DF/DF"); + // Why do we expect an update on DF/DF. H==M, + // H&M are files and index contains a dir, index + // is dirty: that case is not in the table but + // we cannot update DF/DF to a file, this would + // require that we delete DF/DF/DF/DF/DF in workdir + // throwing away unsaved contents. + // This test would fail in DirCacheCheckoutTests. + } + + @Test + public void testDirectoryFileConflicts_8() throws Exception { + // 8 + setupCase(mk("DF"), mk("DF"), mk("DF/DF")); + recursiveDelete(new File(db.getWorkTree(), "DF")); + writeTrashFile("DF", "xy"); + go(); + assertConflict("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_9() throws Exception { + // 9 + doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF")); + assertRemoved("DF/DF"); + assertUpdated("DF"); + } + + @Test + public void testDirectoryFileConflicts_10() throws Exception { + // 10 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mk("DF/DF")); + assertNoConflicts(); + } + + @Test + public void testDirectoryFileConflicts_11() throws Exception { + // 11 + doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf")); + assertConflict("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_12() throws Exception { + // 12 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mk("DF")); + assertRemoved("DF"); + assertUpdated("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_13() throws Exception { + // 13 + cleanUpDF(); + setupCase(mk("DF"), mk("DF/DF"), mk("DF")); + writeTrashFile("DF", "asdfsdf"); + go(); + assertConflict("DF"); + assertUpdated("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_14() throws Exception { + // 14 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo")); + assertConflict("DF"); + assertUpdated("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_15() throws Exception { + // 15 + doit(mkmap(), mk("DF/DF"), mk("DF")); + + // This test would fail in DirCacheCheckoutTests. I think this test is wrong, + // it should check for conflicts according to rule 15 + // assertRemoved("DF"); + + assertUpdated("DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_15b() throws Exception { + // 15, take 2, just to check multi-leveled + doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF")); + + // I think this test is wrong, it should + // check for conflicts according to rule 15 + // This test would fail in DirCacheCheckouts + // assertRemoved("DF"); + + assertUpdated("DF/DF/DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_16() throws Exception { + // 16 + cleanUpDF(); + doit(mkmap(), mk("DF"), mk("DF/DF/DF")); + assertRemoved("DF/DF/DF"); + assertUpdated("DF"); + } + + @Test + public void testDirectoryFileConflicts_17() throws Exception { + // 17 + cleanUpDF(); + setupCase(mkmap(), mk("DF"), mk("DF/DF/DF")); + writeTrashFile("DF/DF/DF", "asdf"); + go(); + assertConflict("DF/DF/DF"); + + // Why do we expect an update on DF. If we really update + // DF and update also the working tree we would have to + // overwrite a dirty file in the work-tree DF/DF/DF + // This test would fail in DirCacheCheckout + // assertUpdated("DF"); + } + + @Test + public void testDirectoryFileConflicts_18() throws Exception { + // 18 + cleanUpDF(); + doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null); + assertRemoved("DF/DF"); + assertUpdated("DF/DF/DF/DF"); + } + + @Test + public void testDirectoryFileConflicts_19() throws Exception { + // 19 + cleanUpDF(); + doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null); + assertRemoved("DF/DF/DF/DF"); + assertUpdated("DF/DF/DF"); + } + + protected void cleanUpDF() throws Exception { + tearDown(); + setUp(); + recursiveDelete(new File(trash, "DF")); + } + + protected void assertConflict(String s) { + assertTrue(getConflicts().contains(s)); + } + + protected void assertUpdated(String s) { + assertTrue(getUpdated().containsKey(s)); + } + + protected void assertRemoved(String s) { + assertTrue(getRemoved().contains(s)); + } + + protected void assertNoConflicts() { + assertTrue(getConflicts().isEmpty()); + } + + protected void doit(HashMap h, HashMap m, HashMap i) + throws IOException { + setupCase(h, m, i); + go(); + } + + @Test + public void testUntrackedConflicts() throws IOException { + setupCase(null, mk("foo"), null); + writeTrashFile("foo", "foo"); + go(); + + // test that we don't overwrite untracked files when there is a HEAD + recursiveDelete(new File(trash, "foo")); + setupCase(mk("other"), mkmap("other", "other", "foo", "foo"), + mk("other")); + writeTrashFile("foo", "bar"); + try { + checkout(); + fail("didn't get the expected exception"); + } catch (CheckoutConflictException e) { + assertConflict("foo"); + assertWorkDir(mkmap("foo", "bar", "other", "other")); + assertIndex(mk("other")); + } + + // test that we don't overwrite untracked files when there is no HEAD + recursiveDelete(new File(trash, "other")); + recursiveDelete(new File(trash, "foo")); + setupCase(null, mk("foo"), null); + writeTrashFile("foo", "bar"); + try { + checkout(); + fail("didn't get the expected exception"); + } catch (CheckoutConflictException e) { + assertConflict("foo"); + assertWorkDir(mkmap("foo", "bar")); + assertIndex(mkmap("other", "other")); + } + + // TODO: Why should we expect conflicts here? + // H and M are empty and according to rule #5 of + // the carry-over rules a dirty index is no reason + // for a conflict. (I also feel it should be a + // conflict because we are going to overwrite + // unsaved content in the working tree + // This test would fail in DirCacheCheckoutTest + // assertConflict("foo"); + + recursiveDelete(new File(trash, "foo")); + recursiveDelete(new File(trash, "other")); + setupCase(null, mk("foo"), null); + writeTrashFile("foo/bar/baz", ""); + writeTrashFile("foo/blahblah", ""); + go(); + + assertConflict("foo"); + assertConflict("foo/bar/baz"); + assertConflict("foo/blahblah"); + + recursiveDelete(new File(trash, "foo")); + + setupCase(mkmap("foo/bar", "", "foo/baz", ""), + mk("foo"), mkmap("foo/bar", "", "foo/baz", "")); + assertTrue(new File(trash, "foo/bar").exists()); + go(); + + assertNoConflicts(); + } + + @Test + public void testCloseNameConflictsX0() throws IOException { + setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "b.b/b.b","b.b/b.bs"), mkmap("a/a", "a/a-c") ); + checkout(); + assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); + assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); + go(); + assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); + assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); + assertNoConflicts(); + } + + @Test + public void testCloseNameConflicts1() throws IOException { + setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "a.a/a.a","a.a/a.a"), mkmap("a/a", "a/a-c") ); + checkout(); + assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); + assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); + go(); + assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); + assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); + assertNoConflicts(); + } + + @Test + public void testCheckoutHierarchy() throws IOException { + setupCase( + mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", + "e/g"), + mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", + "e/g2"), + mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", + "e/g3")); + try { + checkout(); + } catch (CheckoutConflictException e) { + assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", + "e/f", "e/g", "e/g3")); + assertConflict("e/g"); + } + } + + @Test + public void testCheckoutOutChanges() throws IOException { + setupCase(mk("foo"), mk("foo/bar"), mk("foo")); + checkout(); + assertIndex(mk("foo/bar")); + assertWorkDir(mk("foo/bar")); + + assertFalse(new File(trash, "foo").isFile()); + assertTrue(new File(trash, "foo/bar").isFile()); + recursiveDelete(new File(trash, "foo")); + + assertWorkDir(mkmap()); + + setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar")); + checkout(); + + assertIndex(mk("foo")); + assertWorkDir(mk("foo")); + + assertFalse(new File(trash, "foo/bar").isFile()); + assertTrue(new File(trash, "foo").isFile()); + + setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar")); + + assertIndex(mkmap("foo", "bar")); + assertWorkDir(mkmap("foo", "bar")); + + try { + checkout(); + fail("did not throw exception"); + } catch (CheckoutConflictException e) { + assertIndex(mkmap("foo", "bar")); + assertWorkDir(mkmap("foo", "bar")); + } + } + + @Test + public void testCheckoutUncachedChanges() throws IOException { + setupCase(mk("foo"), mk("foo"), mk("foo")); + writeTrashFile("foo", "otherData"); + checkout(); + assertIndex(mk("foo")); + assertWorkDir(mkmap("foo", "otherData")); + assertTrue(new File(trash, "foo").isFile()); + } + + @Test + public void testDontOverwriteDirtyFile() throws IOException { + setupCase(mk("foo"), mk("other"), mk("foo")); + writeTrashFile("foo", "different"); + try { + checkout(); + fail("Didn't got the expected conflict"); + } catch (CheckoutConflictException e) { + assertIndex(mk("foo")); + assertWorkDir(mkmap("foo", "different")); + assertTrue(getConflicts().equals(Arrays.asList("foo"))); + assertTrue(new File(trash, "foo").isFile()); + } + } + + public void assertWorkDir(HashMap i) throws CorruptObjectException, + IOException { + TreeWalk walk = new TreeWalk(db); + walk.setRecursive(true); + walk.addTree(new FileTreeIterator(db)); + String expectedValue; + String path; + int nrFiles = 0; + FileTreeIterator ft; + while (walk.next()) { + ft = walk.getTree(0, FileTreeIterator.class); + path = ft.getEntryPathString(); + expectedValue = i.get(path); + assertNotNull("found unexpected file for path " + path + + " in workdir", expectedValue); + File file = new File(db.getWorkTree(), path); + assertTrue(file.exists()); + if (file.isFile()) { + FileInputStream is = new FileInputStream(file); + byte[] buffer = new byte[(int) file.length()]; + int offset = 0; + int numRead = 0; + while (offset < buffer.length + && (numRead = is.read(buffer, offset, buffer.length + - offset)) >= 0) { + offset += numRead; + } + is.close(); + assertTrue("unexpected content for path " + path + + " in workDir. Expected: <" + expectedValue + ">", + Arrays.equals(buffer, i.get(path).getBytes())); + nrFiles++; + } + } + assertEquals("WorkDir has not the right size.", i.size(), nrFiles); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java index 77f123854..e350fea32 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java @@ -49,32 +49,53 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.TreeSet; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult; import org.eclipse.jgit.api.MergeResult.MergeStatus; +import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.util.IO; import org.junit.Test; public class IndexDiffTest extends RepositoryTestCase { + + static PathEdit add(final Repository db, final File workdir, + final String path) throws FileNotFoundException, IOException { + ObjectInserter inserter = db.newObjectInserter(); + final File f = new File(workdir, path); + final ObjectId id = inserter.insert(Constants.OBJ_BLOB, + IO.readFully(f)); + return new PathEdit(path) { + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.REGULAR_FILE); + ent.setLength(f.length()); + ent.setObjectId(id); + } + }; + } + @Test public void testAdded() throws IOException { - GitIndex index = new GitIndex(db); writeTrashFile("file1", "file1"); writeTrashFile("dir/subfile", "dir/subfile"); Tree tree = new Tree(db); tree.setId(insertTree(tree)); - index.add(trash, new File(trash, "file1")); - index.add(trash, new File(trash, "dir/subfile")); - index.write(); + DirCache index = db.lockDirCache(); + DirCacheEditor editor = index.editor(); + editor.add(add(db, trash, "file1")); + editor.add(add(db, trash, "dir/subfile")); + editor.commit(); FileTreeIterator iterator = new FileTreeIterator(db); IndexDiff diff = new IndexDiff(db, tree.getId(), iterator); diff.diff(); @@ -113,13 +134,13 @@ public class IndexDiffTest extends RepositoryTestCase { } @Test - public void testModified() throws IOException { - GitIndex index = new GitIndex(db); + public void testModified() throws IOException, NoFilepatternException { + writeTrashFile("file2", "file2"); + writeTrashFile("dir/file3", "dir/file3"); - index.add(trash, writeTrashFile("file2", "file2")); - index.add(trash, writeTrashFile("dir/file3", "dir/file3")); - index.write(); + Git git = new Git(db); + git.add().addFilepattern("file2").addFilepattern("dir/file3").call(); writeTrashFile("dir/file3", "changed"); @@ -261,14 +282,17 @@ public class IndexDiffTest extends RepositoryTestCase { } @Test - public void testUnchangedSimple() throws IOException { - GitIndex index = new GitIndex(db); - - index.add(trash, writeTrashFile("a.b", "a.b")); - index.add(trash, writeTrashFile("a.c", "a.c")); - index.add(trash, writeTrashFile("a=c", "a=c")); - index.add(trash, writeTrashFile("a=d", "a=d")); - index.write(); + public void testUnchangedSimple() throws IOException, + NoFilepatternException { + writeTrashFile("a.b", "a.b"); + writeTrashFile("a.c", "a.c"); + writeTrashFile("a=c", "a=c"); + writeTrashFile("a=d", "a=d"); + Git git = new Git(db); + git.add().addFilepattern("a.b").call(); + git.add().addFilepattern("a.c").call(); + git.add().addFilepattern("a=c").call(); + git.add().addFilepattern("a=d").call(); Tree tree = new Tree(db); // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin @@ -290,23 +314,27 @@ public class IndexDiffTest extends RepositoryTestCase { } /** - * This test has both files and directories that involve - * the tricky ordering used by Git. + * This test has both files and directories that involve the tricky ordering + * used by Git. * * @throws IOException + * @throws NoFilepatternException */ @Test - public void testUnchangedComplex() throws IOException { - GitIndex index = new GitIndex(db); - - index.add(trash, writeTrashFile("a.b", "a.b")); - index.add(trash, writeTrashFile("a.c", "a.c")); - index.add(trash, writeTrashFile("a/b.b/b", "a/b.b/b")); - index.add(trash, writeTrashFile("a/b", "a/b")); - index.add(trash, writeTrashFile("a/c", "a/c")); - index.add(trash, writeTrashFile("a=c", "a=c")); - index.add(trash, writeTrashFile("a=d", "a=d")); - index.write(); + public void testUnchangedComplex() throws IOException, + NoFilepatternException { + Git git = new Git(db); + writeTrashFile("a.b", "a.b"); + writeTrashFile("a.c", "a.c"); + writeTrashFile("a/b.b/b", "a/b.b/b"); + writeTrashFile("a/b", "a/b"); + writeTrashFile("a/c", "a/c"); + writeTrashFile("a=c", "a=c"); + writeTrashFile("a=d", "a=d"); + git.add().addFilepattern("a.b").addFilepattern("a.c") + .addFilepattern("a/b.b/b").addFilepattern("a/b") + .addFilepattern("a/c").addFilepattern("a=c") + .addFilepattern("a=d").call(); Tree tree = new Tree(db); // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java deleted file mode 100644 index f76822ea5..000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java +++ /dev/null @@ -1,802 +0,0 @@ -/* - * Copyright (C) 2007, Dave Watson - * Copyright (C) 2008-2009, Robin Rosenberg - * Copyright (C) 2008, Shawn O. Pearce - * Copyright (C) 2010, Christian Halstrick - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.eclipse.jgit.lib; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.jgit.errors.CheckoutConflictException; -import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.treewalk.FileTreeIterator; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.junit.Test; - -public abstract class ReadTreeTest extends RepositoryTestCase { - protected Tree theHead; - protected Tree theMerge; - - // Each of these rules are from the read-tree manpage - // go there to see what they mean. - // Rule 0 is left out for obvious reasons :) - @Test - public void testRules1thru3_NoIndexEntry() throws IOException { - Tree head = new Tree(db); - head = buildTree(mk("foo")); - ObjectId objectId = head.findBlobMember("foo").getId(); - Tree merge = new Tree(db); - - prescanTwoTrees(head, merge); - - assertTrue(getRemoved().contains("foo")); - - prescanTwoTrees(merge, head); - - assertEquals(objectId, getUpdated().get("foo")); - - merge = buildTree(mkmap("foo", "a")); - ObjectId anotherId = merge.findBlobMember("foo").getId(); - - prescanTwoTrees(head, merge); - - assertEquals(anotherId, getUpdated().get("foo")); - } - - void setupCase(HashMap headEntries, - HashMap mergeEntries, - HashMap indexEntries) throws IOException { - theHead = buildTree(headEntries); - theMerge = buildTree(mergeEntries); - buildIndex(indexEntries); - } - - private void buildIndex(HashMap indexEntries) throws IOException { - GitIndex index = new GitIndex(db); - - if (indexEntries != null) { - for (java.util.Map.Entry e : indexEntries.entrySet()) { - index.add(trash, writeTrashFile(e.getKey(), e.getValue())).forceRecheck(); - } - } - - index.write(); - db.getIndex().read(); - } - - private Tree buildTree(HashMap headEntries) throws IOException { - Tree tree = new Tree(db); - if (headEntries == null) - return tree; - FileTreeEntry fileEntry; - Tree parent; - ObjectInserter oi = db.newObjectInserter(); - try { - for (java.util.Map.Entry e : headEntries.entrySet()) { - fileEntry = tree.addFile(e.getKey()); - fileEntry.setId(genSha1(e.getValue())); - parent = fileEntry.getParent(); - while (parent != null) { - parent.setId(oi.insert(Constants.OBJ_TREE, parent.format())); - parent = parent.getParent(); - } - } - oi.flush(); - } finally { - oi.release(); - } - return tree; - } - - ObjectId genSha1(String data) { - ObjectInserter w = db.newObjectInserter(); - try { - ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes()); - w.flush(); - return id; - } catch (IOException e) { - fail(e.toString()); - } finally { - w.release(); - } - return null; - } - - protected void go() throws IllegalStateException, IOException { - prescanTwoTrees(theHead, theMerge); - } - - // for these rules, they all have clean yes/no options - // but it doesn't matter if the entry is clean or not - // so we can just ignore the state in the filesystem entirely - @Test - public void testRules4thru13_IndexEntryNotInHead() throws IOException { - // rules 4 and 5 - HashMap idxMap; - - idxMap = new HashMap(); - idxMap.put("foo", "foo"); - setupCase(null, null, idxMap); - go(); - - assertTrue(getUpdated().isEmpty()); - assertTrue(getRemoved().isEmpty()); - assertTrue(getConflicts().isEmpty()); - - // rules 6 and 7 - idxMap = new HashMap(); - idxMap.put("foo", "foo"); - setupCase(null, idxMap, idxMap); - go(); - - assertAllEmpty(); - - // rules 8 and 9 - HashMap mergeMap; - mergeMap = new HashMap(); - - mergeMap.put("foo", "merge"); - setupCase(null, mergeMap, idxMap); - go(); - - assertTrue(getUpdated().isEmpty()); - assertTrue(getRemoved().isEmpty()); - assertTrue(getConflicts().contains("foo")); - - // rule 10 - - HashMap headMap = new HashMap(); - headMap.put("foo", "foo"); - setupCase(headMap, null, idxMap); - go(); - - assertTrue(getRemoved().contains("foo")); - assertTrue(getUpdated().isEmpty()); - assertTrue(getConflicts().isEmpty()); - - // rule 11 - setupCase(headMap, null, idxMap); - assertTrue(new File(trash, "foo").delete()); - writeTrashFile("foo", "bar"); - db.getIndex().getMembers()[0].forceRecheck(); - go(); - - assertTrue(getRemoved().isEmpty()); - assertTrue(getUpdated().isEmpty()); - assertTrue(getConflicts().contains("foo")); - - // rule 12 & 13 - headMap.put("foo", "head"); - setupCase(headMap, null, idxMap); - go(); - - assertTrue(getRemoved().isEmpty()); - assertTrue(getUpdated().isEmpty()); - assertTrue(getConflicts().contains("foo")); - - // rules 14 & 15 - setupCase(headMap, headMap, idxMap); - go(); - - assertAllEmpty(); - - // rules 16 & 17 - setupCase(headMap, mergeMap, idxMap); go(); - assertTrue(getConflicts().contains("foo")); - - // rules 18 & 19 - setupCase(headMap, idxMap, idxMap); go(); - assertAllEmpty(); - - // rule 20 - setupCase(idxMap, mergeMap, idxMap); go(); - assertTrue(getUpdated().containsKey("foo")); - - // rules 21 - setupCase(idxMap, mergeMap, idxMap); - assertTrue(new File(trash, "foo").delete()); - writeTrashFile("foo", "bar"); - db.getIndex().getMembers()[0].forceRecheck(); - go(); - assertTrue(getConflicts().contains("foo")); - } - - private void assertAllEmpty() { - assertTrue(getRemoved().isEmpty()); - assertTrue(getUpdated().isEmpty()); - assertTrue(getConflicts().isEmpty()); - } - - @Test - public void testDirectoryFileSimple() throws IOException { - Tree treeDF = buildTree(mkmap("DF", "DF")); - Tree treeDFDF = buildTree(mkmap("DF/DF", "DF/DF")); - buildIndex(mkmap("DF", "DF")); - - prescanTwoTrees(treeDF, treeDFDF); - - assertTrue(getRemoved().contains("DF")); - assertTrue(getUpdated().containsKey("DF/DF")); - - recursiveDelete(new File(trash, "DF")); - buildIndex(mkmap("DF/DF", "DF/DF")); - - prescanTwoTrees(treeDFDF, treeDF); - assertTrue(getRemoved().contains("DF/DF")); - assertTrue(getUpdated().containsKey("DF")); - } - - /* - * Directory/File Conflict cases: - * It's entirely possible that in practice a number of these may be equivalent - * to the cases described in git-read-tree.txt. As long as it does the right thing, - * that's all I care about. These are basically reverse-engineered from - * what git currently does. If there are tests for these in git, it's kind of - * hard to track them all down... - * - * H I M Clean H==M H==I I==M Result - * ------------------------------------------------------------------ - *1 D D F Y N Y N Update - *2 D D F N N Y N Conflict - *3 D F D Y N N Update - *4 D F D N N N Update - *5 D F F Y N N Y Keep - *6 D F F N N N Y Keep - *7 F D F Y Y N N Update - *8 F D F N Y N N Conflict - *9 F D F Y N N N Update - *10 F D D N N Y Keep - *11 F D D N N N Conflict - *12 F F D Y N Y N Update - *13 F F D N N Y N Conflict - *14 F F D N N N Conflict - *15 0 F D N N N Conflict - *16 0 D F Y N N N Update - *17 0 D F N N N Conflict - *18 F 0 D Update - *19 D 0 F Update - */ - - @Test - public void testDirectoryFileConflicts_1() throws Exception { - // 1 - doit(mk("DF/DF"), mk("DF"), mk("DF/DF")); - assertNoConflicts(); - assertUpdated("DF"); - assertRemoved("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_2() throws Exception { - // 2 - setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF")); - writeTrashFile("DF/DF", "different"); - go(); - assertConflict("DF/DF"); - - } - - @Test - public void testDirectoryFileConflicts_3() throws Exception { - // 3 - the first to break! - doit(mk("DF/DF"), mk("DF/DF"), mk("DF")); - assertUpdated("DF/DF"); - assertRemoved("DF"); - } - - @Test - public void testDirectoryFileConflicts_4() throws Exception { - // 4 (basically same as 3, just with H and M different) - doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF")); - assertUpdated("DF/DF"); - assertRemoved("DF"); - - } - - @Test - public void testDirectoryFileConflicts_5() throws Exception { - // 5 - doit(mk("DF/DF"), mk("DF"), mk("DF")); - assertRemoved("DF/DF"); - - } - - @Test - public void testDirectoryFileConflicts_6() throws Exception { - // 6 - setupCase(mk("DF/DF"), mk("DF"), mk("DF")); - writeTrashFile("DF", "different"); - go(); - assertRemoved("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_7() throws Exception { - // 7 - doit(mk("DF"), mk("DF"), mk("DF/DF")); - assertUpdated("DF"); - assertRemoved("DF/DF"); - - cleanUpDF(); - setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); - go(); - assertRemoved("DF/DF/DF/DF/DF"); - assertUpdated("DF/DF"); - - cleanUpDF(); - setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); - writeTrashFile("DF/DF/DF/DF/DF", "diff"); - go(); - assertConflict("DF/DF/DF/DF/DF"); - - // assertUpdated("DF/DF"); - // Why do we expect an update on DF/DF. H==M, - // H&M are files and index contains a dir, index - // is dirty: that case is not in the table but - // we cannot update DF/DF to a file, this would - // require that we delete DF/DF/DF/DF/DF in workdir - // throwing away unsaved contents. - // This test would fail in DirCacheCheckoutTests. - } - - @Test - public void testDirectoryFileConflicts_8() throws Exception { - // 8 - setupCase(mk("DF"), mk("DF"), mk("DF/DF")); - recursiveDelete(new File(db.getWorkTree(), "DF")); - writeTrashFile("DF", "xy"); - go(); - assertConflict("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_9() throws Exception { - // 9 - doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF")); - assertRemoved("DF/DF"); - assertUpdated("DF"); - } - - @Test - public void testDirectoryFileConflicts_10() throws Exception { - // 10 - cleanUpDF(); - doit(mk("DF"), mk("DF/DF"), mk("DF/DF")); - assertNoConflicts(); - } - - @Test - public void testDirectoryFileConflicts_11() throws Exception { - // 11 - doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf")); - assertConflict("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_12() throws Exception { - // 12 - cleanUpDF(); - doit(mk("DF"), mk("DF/DF"), mk("DF")); - assertRemoved("DF"); - assertUpdated("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_13() throws Exception { - // 13 - cleanUpDF(); - setupCase(mk("DF"), mk("DF/DF"), mk("DF")); - writeTrashFile("DF", "asdfsdf"); - go(); - assertConflict("DF"); - assertUpdated("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_14() throws Exception { - // 14 - cleanUpDF(); - doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo")); - assertConflict("DF"); - assertUpdated("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_15() throws Exception { - // 15 - doit(mkmap(), mk("DF/DF"), mk("DF")); - - // This test would fail in DirCacheCheckoutTests. I think this test is wrong, - // it should check for conflicts according to rule 15 - // assertRemoved("DF"); - - assertUpdated("DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_15b() throws Exception { - // 15, take 2, just to check multi-leveled - doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF")); - - // I think this test is wrong, it should - // check for conflicts according to rule 15 - // This test would fail in DirCacheCheckouts - // assertRemoved("DF"); - - assertUpdated("DF/DF/DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_16() throws Exception { - // 16 - cleanUpDF(); - doit(mkmap(), mk("DF"), mk("DF/DF/DF")); - assertRemoved("DF/DF/DF"); - assertUpdated("DF"); - } - - @Test - public void testDirectoryFileConflicts_17() throws Exception { - // 17 - cleanUpDF(); - setupCase(mkmap(), mk("DF"), mk("DF/DF/DF")); - writeTrashFile("DF/DF/DF", "asdf"); - go(); - assertConflict("DF/DF/DF"); - - // Why do we expect an update on DF. If we really update - // DF and update also the working tree we would have to - // overwrite a dirty file in the work-tree DF/DF/DF - // This test would fail in DirCacheCheckout - // assertUpdated("DF"); - } - - @Test - public void testDirectoryFileConflicts_18() throws Exception { - // 18 - cleanUpDF(); - doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null); - assertRemoved("DF/DF"); - assertUpdated("DF/DF/DF/DF"); - } - - @Test - public void testDirectoryFileConflicts_19() throws Exception { - // 19 - cleanUpDF(); - doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null); - assertRemoved("DF/DF/DF/DF"); - assertUpdated("DF/DF/DF"); - } - - protected void cleanUpDF() throws Exception { - tearDown(); - setUp(); - recursiveDelete(new File(trash, "DF")); - } - - protected void assertConflict(String s) { - assertTrue(getConflicts().contains(s)); - } - - protected void assertUpdated(String s) { - assertTrue(getUpdated().containsKey(s)); - } - - protected void assertRemoved(String s) { - assertTrue(getRemoved().contains(s)); - } - - protected void assertNoConflicts() { - assertTrue(getConflicts().isEmpty()); - } - - protected void doit(HashMap h, HashMap m, - HashMap i) throws IOException { - setupCase(h, m, i); - go(); - } - - protected static HashMap mk(String a) { - return mkmap(a, a); - } - - protected static HashMap mkmap(String... args) { - if ((args.length % 2) > 0) - throw new IllegalArgumentException("needs to be pairs"); - - HashMap map = new HashMap(); - for (int i = 0; i < args.length; i += 2) { - map.put(args[i], args[i+1]); - } - - return map; - } - - @Test - public void testUntrackedConflicts() throws IOException { - setupCase(null, mk("foo"), null); - writeTrashFile("foo", "foo"); - go(); - - // test that we don't overwrite untracked files when there is a HEAD - recursiveDelete(new File(trash, "foo")); - setupCase(mk("other"), mkmap("other", "other", "foo", "foo"), - mk("other")); - writeTrashFile("foo", "bar"); - try { - checkout(); - fail("didn't get the expected exception"); - } catch (CheckoutConflictException e) { - assertConflict("foo"); - assertWorkDir(mkmap("foo", "bar", "other", "other")); - assertIndex(mk("other")); - } - - // test that we don't overwrite untracked files when there is no HEAD - recursiveDelete(new File(trash, "other")); - recursiveDelete(new File(trash, "foo")); - setupCase(null, mk("foo"), null); - writeTrashFile("foo", "bar"); - try { - checkout(); - fail("didn't get the expected exception"); - } catch (CheckoutConflictException e) { - assertConflict("foo"); - assertWorkDir(mkmap("foo", "bar")); - assertIndex(mkmap()); - } - - // TODO: Why should we expect conflicts here? - // H and M are emtpy and according to rule #5 of - // the carry-over rules a dirty index is no reason - // for a conflict. (I also feel it should be a - // conflict because we are going to overwrite - // unsaved content in the working tree - // This test would fail in DirCacheCheckoutTest - // assertConflict("foo"); - - recursiveDelete(new File(trash, "foo")); - recursiveDelete(new File(trash, "other")); - setupCase(null, mk("foo"), null); - writeTrashFile("foo/bar/baz", ""); - writeTrashFile("foo/blahblah", ""); - go(); - - assertConflict("foo"); - assertConflict("foo/bar/baz"); - assertConflict("foo/blahblah"); - - recursiveDelete(new File(trash, "foo")); - - setupCase(mkmap("foo/bar", "", "foo/baz", ""), - mk("foo"), mkmap("foo/bar", "", "foo/baz", "")); - assertTrue(new File(trash, "foo/bar").exists()); - go(); - - assertNoConflicts(); - } - - @Test - public void testCloseNameConflictsX0() throws IOException { - setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "b.b/b.b","b.b/b.bs"), mkmap("a/a", "a/a-c") ); - checkout(); - assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); - assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); - go(); - assertIndex(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); - assertWorkDir(mkmap("a/a", "a/a", "b.b/b.b", "b.b/b.bs")); - assertNoConflicts(); - } - - @Test - public void testCloseNameConflicts1() throws IOException { - setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "a.a/a.a","a.a/a.a"), mkmap("a/a", "a/a-c") ); - checkout(); - assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); - assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); - go(); - assertIndex(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); - assertWorkDir(mkmap("a/a", "a/a", "a.a/a.a", "a.a/a.a")); - assertNoConflicts(); - } - - @Test - public void testCheckoutHierarchy() throws IOException { - setupCase( - mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", - "e/g"), - mkmap("a", "a2", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", - "e/g2"), - mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", - "e/g3")); - try { - checkout(); - } catch (CheckoutConflictException e) { - assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", - "e/f", "e/g", "e/g3")); - assertConflict("e/g"); - } - } - - @Test - public void testCheckoutOutChanges() throws IOException { - setupCase(mk("foo"), mk("foo/bar"), mk("foo")); - checkout(); - assertIndex(mk("foo/bar")); - assertWorkDir(mk("foo/bar")); - - assertFalse(new File(trash, "foo").isFile()); - assertTrue(new File(trash, "foo/bar").isFile()); - recursiveDelete(new File(trash, "foo")); - - assertWorkDir(mkmap()); - - setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar")); - checkout(); - - assertIndex(mk("foo")); - assertWorkDir(mk("foo")); - - assertFalse(new File(trash, "foo/bar").isFile()); - assertTrue(new File(trash, "foo").isFile()); - - setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar")); - - assertIndex(mkmap("foo", "bar")); - assertWorkDir(mkmap("foo", "bar")); - - try { - checkout(); - fail("did not throw exception"); - } catch (CheckoutConflictException e) { - assertIndex(mkmap("foo", "bar")); - assertWorkDir(mkmap("foo", "bar")); - } - } - - @Test - public void testCheckoutUncachedChanges() throws IOException { - setupCase(mk("foo"), mk("foo"), mk("foo")); - writeTrashFile("foo", "otherData"); - checkout(); - assertIndex(mk("foo")); - assertWorkDir(mkmap("foo", "otherData")); - assertTrue(new File(trash, "foo").isFile()); - } - - @Test - public void testDontOverwriteDirtyFile() throws IOException { - setupCase(mk("foo"), mk("other"), mk("foo")); - writeTrashFile("foo", "different"); - try { - checkout(); - fail("Didn't got the expected conflict"); - } catch (CheckoutConflictException e) { - assertIndex(mk("foo")); - assertWorkDir(mkmap("foo", "different")); - assertTrue(getConflicts().equals(Arrays.asList("foo"))); - assertTrue(new File(trash, "foo").isFile()); - } - } - - public void assertWorkDir(HashMap i) - throws CorruptObjectException, IOException { - TreeWalk walk = new TreeWalk(db); - walk.setRecursive(true); - walk.addTree(new FileTreeIterator(db)); - String expectedValue; - String path; - int nrFiles = 0; - FileTreeIterator ft; - while (walk.next()) { - ft = walk.getTree(0, FileTreeIterator.class); - path = ft.getEntryPathString(); - expectedValue = i.get(path); - assertNotNull("found unexpected file for path " - + path + " in workdir", expectedValue); - File file = new File(db.getWorkTree(), path); - assertTrue(file.exists()); - if (file.isFile()) { - FileInputStream is = new FileInputStream(file); - byte[] buffer = new byte[(int) file.length()]; - int offset = 0; - int numRead = 0; - while (offset < buffer.length - && (numRead = is.read(buffer, offset, buffer.length - - offset)) >= 0) { - offset += numRead; - } - is.close(); - assertTrue("unexpected content for path " + path - + " in workDir. Expected: <" + expectedValue + ">", - Arrays.equals(buffer, i.get(path).getBytes())); - nrFiles++; - } - } - assertEquals("WorkDir has not the right size.", i.size(), nrFiles); - } - - - public void assertIndex(HashMap i) - throws CorruptObjectException, IOException { - String expectedValue; - String path; - GitIndex theIndex=db.getIndex(); - // Without an explicit refresh we might miss index updates. If the index - // is updated multiple times inside a FileSystemTimer tick db.getIndex will - // not reload the index and return a cached (stale) index. - theIndex.read(); - assertEquals("Index has not the right size.", i.size(), - theIndex.getMembers().length); - for (int j = 0; j < theIndex.getMembers().length; j++) { - path = theIndex.getMembers()[j].getName(); - expectedValue = i.get(path); - assertNotNull("found unexpected entry for path " + path - + " in index", expectedValue); - assertTrue("unexpected content for path " + path - + " in index. Expected: <" + expectedValue + ">", - Arrays.equals( - db.open(theIndex.getMembers()[j].getObjectId()) - .getCachedBytes(), i.get(path).getBytes())); - } - } - - public abstract void prescanTwoTrees(Tree head, Tree merge) throws IllegalStateException, IOException; - public abstract void checkout() throws IOException; - public abstract List getRemoved(); - public abstract Map getUpdated(); - public abstract List getConflicts(); -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java index 1781c3d62..8affb01f2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java @@ -165,7 +165,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testExceptionThrown_BareRepoGetIndex() throws Exception { File gitDir = getFile("workdir"); try { - new FileRepository(gitDir).getIndex(); + new FileRepository(gitDir).readDirCache(); fail("Expected NoWorkTreeException missing"); } catch (NoWorkTreeException e) { // expected diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 4806ab922..b1c4c78e6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -816,6 +816,7 @@ public abstract class Repository { * @throws NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. + * @deprecated Use {@link #readDirCache()} instead */ public GitIndex getIndex() throws IOException, NoWorkTreeException { if (isBare()) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java index 38c33f311..6f47db675 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java @@ -241,6 +241,7 @@ public abstract class TreeEntry implements Comparable { * * @param i * @return '/' for Tree entries and NUL for non-treeish objects + * @deprecated since it depends on deprecated GitIndex, and internal */ final public static int lastChar(Entry i) { // FIXME, gitlink etc. Currently Trees cannot appear in the