/*
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and
- * other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008-2011, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008-2011, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010-2011, Christian Halstrick <christian.halstrick@sap.com>
+ * 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
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.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 {
}
}
- @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());
}
}
- @Override
- public List<String> getRemoved() {
+ private List<String> getRemoved() {
return dco.getRemoved();
}
- @Override
- public Map<String, ObjectId> getUpdated() {
+ private Map<String, ObjectId> getUpdated() {
return dco.getUpdated();
}
- @Override
- public List<String> getConflicts() {
+ private List<String> getConflicts() {
return dco.getConflicts();
}
+ private static HashMap<String, String> mk(String a) {
+ return mkmap(a, a);
+ }
+
+ private static HashMap<String, String> mkmap(String... args) {
+ if ((args.length % 2) > 0)
+ throw new IllegalArgumentException("needs to be pairs");
+
+ HashMap<String, String> map = new HashMap<String, String>();
+ 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 {
assertTrue(dc.checkout());
return dc;
}
+
+ private void assertIndex(HashMap<String, String> 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<String, String> headEntries, HashMap<String, String> mergeEntries, HashMap<String, String> indexEntries) throws IOException {
+ theHead = buildTree(headEntries);
+ theMerge = buildTree(mergeEntries);
+ buildIndex(indexEntries);
+ }
+
+ private void buildIndex(HashMap<String, String> 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<String,String> 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<String, String> 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<String, String> 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<String, String> idxMap;
+
+ idxMap = new HashMap<String, String>();
+ 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<String, String>();
+ idxMap.put("foo", "foo");
+ setupCase(null, idxMap, idxMap);
+ go();
+
+ assertAllEmpty();
+
+ // rules 8 and 9
+ HashMap<String, String> mergeMap;
+ mergeMap = new HashMap<String, String>();
+
+ mergeMap.put("foo", "merge");
+ setupCase(null, mergeMap, idxMap);
+ go();
+
+ assertTrue(getUpdated().isEmpty());
+ assertTrue(getRemoved().isEmpty());
+ assertTrue(getConflicts().contains("foo"));
+
+ // rule 10
+
+ HashMap<String, String> headMap = new HashMap<String, String>();
+ 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<String, String> h, HashMap<String, String> m, HashMap<String, String> 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<String, String> 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);
+ }
}
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();
}
@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");
}
@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
}
/**
- * 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
+++ /dev/null
-/*
- * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * 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<String, String> headEntries,
- HashMap<String, String> mergeEntries,
- HashMap<String, String> indexEntries) throws IOException {
- theHead = buildTree(headEntries);
- theMerge = buildTree(mergeEntries);
- buildIndex(indexEntries);
- }
-
- private void buildIndex(HashMap<String, String> indexEntries) throws IOException {
- GitIndex index = new GitIndex(db);
-
- if (indexEntries != null) {
- for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
- index.add(trash, writeTrashFile(e.getKey(), e.getValue())).forceRecheck();
- }
- }
-
- index.write();
- db.getIndex().read();
- }
-
- private Tree buildTree(HashMap<String, String> 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<String, String> 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<String, String> idxMap;
-
- idxMap = new HashMap<String, String>();
- 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<String, String>();
- idxMap.put("foo", "foo");
- setupCase(null, idxMap, idxMap);
- go();
-
- assertAllEmpty();
-
- // rules 8 and 9
- HashMap<String, String> mergeMap;
- mergeMap = new HashMap<String, String>();
-
- mergeMap.put("foo", "merge");
- setupCase(null, mergeMap, idxMap);
- go();
-
- assertTrue(getUpdated().isEmpty());
- assertTrue(getRemoved().isEmpty());
- assertTrue(getConflicts().contains("foo"));
-
- // rule 10
-
- HashMap<String, String> headMap = new HashMap<String, String>();
- 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<String, String> h, HashMap<String, String> m,
- HashMap<String, String> i) throws IOException {
- setupCase(h, m, i);
- go();
- }
-
- protected static HashMap<String, String> mk(String a) {
- return mkmap(a, a);
- }
-
- protected static HashMap<String, String> mkmap(String... args) {
- if ((args.length % 2) > 0)
- throw new IllegalArgumentException("needs to be pairs");
-
- HashMap<String, String> map = new HashMap<String, String>();
- 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<String, String> 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<String, String> 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<String> getRemoved();
- public abstract Map<String, ObjectId> getUpdated();
- public abstract List<String> getConflicts();
-}
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
* @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())
*
* @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