| /* |
| * 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 |
| * 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 java.nio.charset.StandardCharsets.UTF_8; |
| import static org.junit.Assert.assertArrayEquals; |
| 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.CheckoutCommand; |
| import org.eclipse.jgit.api.CheckoutResult; |
| import org.eclipse.jgit.api.Git; |
| import org.eclipse.jgit.api.MergeResult.MergeStatus; |
| import org.eclipse.jgit.api.ResetCommand.ResetType; |
| import org.eclipse.jgit.api.Status; |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.NoFilepatternException; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheCheckout; |
| import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; |
| import org.eclipse.jgit.dircache.DirCacheEditor; |
| import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; |
| import org.eclipse.jgit.dircache.DirCacheEntry; |
| import org.eclipse.jgit.errors.CheckoutConflictException; |
| import org.eclipse.jgit.errors.CorruptObjectException; |
| import org.eclipse.jgit.errors.NoWorkTreeException; |
| import org.eclipse.jgit.events.ChangeRecorder; |
| import org.eclipse.jgit.events.ListenerHandle; |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.junit.TestRepository.BranchBuilder; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.treewalk.AbstractTreeIterator; |
| import org.eclipse.jgit.treewalk.FileTreeIterator; |
| import org.eclipse.jgit.treewalk.TreeWalk; |
| import org.eclipse.jgit.treewalk.WorkingTreeIterator; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.junit.Assume; |
| import org.junit.Test; |
| |
| public class DirCacheCheckoutTest extends RepositoryTestCase { |
| private DirCacheCheckout dco; |
| protected ObjectId theHead; |
| protected ObjectId theMerge; |
| private DirCache dirCache; |
| |
| private void prescanTwoTrees(ObjectId head, ObjectId merge) |
| throws IllegalStateException, IOException { |
| DirCache dc = db.lockDirCache(); |
| try { |
| dco = new DirCacheCheckout(db, head, dc, merge); |
| dco.preScanTwoTrees(); |
| } finally { |
| dc.unlock(); |
| } |
| } |
| |
| private void checkout() throws IOException { |
| DirCache dc = db.lockDirCache(); |
| try { |
| dco = new DirCacheCheckout(db, theHead, dc, theMerge); |
| dco.checkout(); |
| } finally { |
| dc.unlock(); |
| } |
| } |
| |
| private List<String> getRemoved() { |
| return dco.getRemoved(); |
| } |
| |
| private Map<String, CheckoutMetadata> getUpdated() { |
| return dco.getUpdated(); |
| } |
| |
| 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<>(); |
| 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 { |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| writeTrashFile("f", "f()"); |
| writeTrashFile("D/g", "g()"); |
| git.add().addFilepattern(".").call(); |
| git.commit().setMessage("inital").call(); |
| assertIndex(mkmap("f", "f()", "D/g", "g()")); |
| recorder.assertNoEvent(); |
| git.branchCreate().setName("topic").call(); |
| recorder.assertNoEvent(); |
| |
| writeTrashFile("f", "f()\nmaster"); |
| writeTrashFile("D/g", "g()\ng2()"); |
| writeTrashFile("E/h", "h()"); |
| git.add().addFilepattern(".").call(); |
| RevCommit master = git.commit().setMessage("master-1").call(); |
| assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()")); |
| recorder.assertNoEvent(); |
| |
| checkoutBranch("refs/heads/topic"); |
| assertIndex(mkmap("f", "f()", "D/g", "g()")); |
| recorder.assertEvent(new String[] { "f", "D/g" }, |
| new String[] { "E/h" }); |
| |
| writeTrashFile("f", "f()\nside"); |
| assertTrue(new File(db.getWorkTree(), "D/g").delete()); |
| writeTrashFile("G/i", "i()"); |
| git.add().addFilepattern(".").call(); |
| git.add().addFilepattern(".").setUpdate(true).call(); |
| RevCommit topic = git.commit().setMessage("topic-1").call(); |
| assertIndex(mkmap("f", "f()\nside", "G/i", "i()")); |
| recorder.assertNoEvent(); |
| |
| writeTrashFile("untracked", "untracked"); |
| |
| resetHard(master); |
| assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()")); |
| recorder.assertEvent(new String[] { "f", "D/g", "E/h" }, |
| new String[] { "G", "G/i" }); |
| |
| resetHard(topic); |
| assertIndex(mkmap("f", "f()\nside", "G/i", "i()")); |
| assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()", "untracked", |
| "untracked")); |
| recorder.assertEvent(new String[] { "f", "G/i" }, |
| new String[] { "D", "D/g", "E", "E/h" }); |
| |
| assertEquals(MergeStatus.CONFLICTING, git.merge().include(master) |
| .call().getMergeStatus()); |
| assertEquals( |
| "[D/g, mode:100644, stage:1][D/g, mode:100644, stage:3][E/h, mode:100644][G/i, mode:100644][f, mode:100644, stage:1][f, mode:100644, stage:2][f, mode:100644, stage:3]", |
| indexState(0)); |
| recorder.assertEvent(new String[] { "f", "D/g", "E/h" }, |
| ChangeRecorder.EMPTY); |
| |
| resetHard(master); |
| assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()")); |
| assertWorkDir(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", |
| "h()", "untracked", "untracked")); |
| recorder.assertEvent(new String[] { "f", "D/g" }, |
| new String[] { "G", "G/i" }); |
| |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| /** |
| * Reset hard from unclean condition. |
| * <p> |
| * WorkDir: Empty <br> |
| * Index: f/g <br> |
| * Merge: x |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testResetHardFromIndexEntryWithoutFileToTreeWithoutFile() |
| throws Exception { |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| writeTrashFile("x", "x"); |
| git.add().addFilepattern("x").call(); |
| RevCommit id1 = git.commit().setMessage("c1").call(); |
| |
| writeTrashFile("f/g", "f/g"); |
| git.rm().addFilepattern("x").call(); |
| recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "x" }); |
| git.add().addFilepattern("f/g").call(); |
| git.commit().setMessage("c2").call(); |
| deleteTrashFile("f/g"); |
| deleteTrashFile("f"); |
| |
| // The actual test |
| git.reset().setMode(ResetType.HARD).setRef(id1.getName()).call(); |
| assertIndex(mkmap("x", "x")); |
| recorder.assertEvent(new String[] { "x" }, ChangeRecorder.EMPTY); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| /** |
| * Test first checkout in a repo |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testInitialCheckout() throws Exception { |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db); |
| TestRepository<Repository> db_t = new TestRepository<>(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| BranchBuilder master = db_t.branch("master"); |
| master.commit().add("f", "1").message("m0").create(); |
| assertFalse(new File(db.getWorkTree(), "f").exists()); |
| git.checkout().setName("master").call(); |
| assertTrue(new File(db.getWorkTree(), "f").exists()); |
| recorder.assertEvent(new String[] { "f" }, ChangeRecorder.EMPTY); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| private DirCacheCheckout resetHard(RevCommit commit) |
| throws NoWorkTreeException, |
| CorruptObjectException, IOException { |
| DirCacheCheckout dc; |
| dc = new DirCacheCheckout(db, null, db.lockDirCache(), |
| commit.getTree()); |
| dc.setFailOnConflict(true); |
| 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(UTF_8))); |
| } |
| } |
| |
| @Test |
| public void testRules1thru3_NoIndexEntry() throws IOException { |
| ObjectId head = buildTree(mk("foo")); |
| ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE, |
| new byte[0]); |
| |
| prescanTwoTrees(head, merge); |
| |
| assertTrue(getRemoved().contains("foo")); |
| |
| prescanTwoTrees(merge, head); |
| |
| assertTrue(getUpdated().containsKey("foo")); |
| |
| merge = buildTree(mkmap("foo", "a")); |
| |
| prescanTwoTrees(head, merge); |
| |
| assertConflict("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()); |
| ObjectId id; |
| try (ObjectInserter inserter = db.newObjectInserter()) { |
| 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()); |
| } |
| |
| } |
| |
| static final class AddEdit extends PathEdit { |
| |
| private final ObjectId data; |
| |
| private final long length; |
| |
| public AddEdit(String entryPath, ObjectId data, long length) { |
| super(entryPath); |
| this.data = data; |
| this.length = length; |
| } |
| |
| @Override |
| public void apply(DirCacheEntry ent) { |
| ent.setFileMode(FileMode.REGULAR_FILE); |
| ent.setLength(length); |
| ent.setObjectId(data); |
| } |
| |
| } |
| |
| private ObjectId buildTree(HashMap<String, String> headEntries) |
| throws IOException { |
| DirCache lockDirCache = DirCache.newInCore(); |
| // assertTrue(lockDirCache.lock()); |
| DirCacheEditor editor = lockDirCache.editor(); |
| if (headEntries != null) { |
| for (java.util.Map.Entry<String, String> e : headEntries.entrySet()) { |
| AddEdit addEdit = new AddEdit(e.getKey(), |
| genSha1(e.getValue()), e.getValue().length()); |
| editor.add(addEdit); |
| } |
| } |
| editor.finish(); |
| return lockDirCache.writeTree(db.newObjectInserter()); |
| } |
| |
| ObjectId genSha1(String data) { |
| try (ObjectInserter w = db.newObjectInserter()) { |
| ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes(UTF_8)); |
| w.flush(); |
| return id; |
| } catch (IOException e) { |
| fail(e.toString()); |
| } |
| 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<>(); |
| 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<String, String> 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<String, String> 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 Keep |
| *4 D F D N N N Conflict |
| *5 D F F Y N N Y Keep |
| *5b D F F Y N N N Conflict |
| *6 D F F N N N Y Keep |
| *6b D F F N N N N Conflict |
| *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 { |
| ObjectId treeDF = buildTree(mkmap("DF", "DF")); |
| ObjectId 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 |
| doit(mk("DF/DF"), mk("DF/DF"), mk("DF")); |
| assertNoConflicts(); |
| } |
| |
| @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")); |
| assertConflict("DF/DF"); |
| |
| } |
| |
| @Test |
| public void testDirectoryFileConflicts_5() throws Exception { |
| // 5 |
| doit(mk("DF/DF"), mk("DF"), mk("DF")); |
| assertRemoved("DF/DF"); |
| assertEquals(0, dco.getConflicts().size()); |
| assertEquals(0, dco.getUpdated().size()); |
| } |
| |
| @Test |
| public void testDirectoryFileConflicts_5b() throws Exception { |
| // 5 |
| doit(mk("DF/DF"), mkmap("DF", "different"), mk("DF")); |
| assertRemoved("DF/DF"); |
| assertConflict("DF"); |
| assertEquals(0, dco.getUpdated().size()); |
| } |
| |
| @Test |
| public void testDirectoryFileConflicts_6() throws Exception { |
| // 6 |
| setupCase(mk("DF/DF"), mk("DF"), mk("DF")); |
| writeTrashFile("DF", "different"); |
| go(); |
| assertRemoved("DF/DF"); |
| assertEquals(0, dco.getConflicts().size()); |
| assertEquals(0, dco.getUpdated().size()); |
| } |
| |
| @Test |
| public void testDirectoryFileConflicts_6b() throws Exception { |
| // 6 |
| setupCase(mk("DF/DF"), mk("DF"), mkmap("DF", "different")); |
| writeTrashFile("DF", "again different"); |
| go(); |
| assertRemoved("DF/DF"); |
| assertConflict("DF"); |
| assertEquals(0, dco.getUpdated().size()); |
| } |
| |
| @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(mkmap("DF", "QP"), mkmap("DF", "QP"), mkmap("DF/DF", "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"); |
| assertEquals("foo", e.getConflictingFiles()[0]); |
| 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(); |
| fail("did not throw CheckoutConflictException"); |
| } catch (CheckoutConflictException e) { |
| assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", |
| "e/f", "e/g", "e/g3")); |
| assertConflict("e/g"); |
| assertEquals("e/g", e.getConflictingFiles()[0]); |
| } |
| } |
| |
| @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 testCheckoutChangeLinkToEmptyDir() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| |
| // Add a link to file |
| String linkName = "link"; |
| File link = writeLink(linkName, fname).toFile(); |
| git.add().addFilepattern(linkName).call(); |
| git.commit().setMessage("Added file and link").call(); |
| |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| |
| // replace link with empty directory |
| FileUtils.delete(link); |
| FileUtils.mkdir(link); |
| assertTrue("Link must be a directory now", link.isDirectory()); |
| |
| // modify file |
| writeTrashFile(fname, "b"); |
| assertWorkDir(mkmap(fname, "b", linkName, "/")); |
| recorder.assertNoEvent(); |
| |
| // revert both paths to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname) |
| .addPath(linkName).call(); |
| |
| assertWorkDir(mkmap(fname, "a", linkName, "a")); |
| recorder.assertEvent(new String[] { fname, linkName }, |
| ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeLinkToEmptyDirs() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| |
| // Add a link to file |
| String linkName = "link"; |
| File link = writeLink(linkName, fname).toFile(); |
| git.add().addFilepattern(linkName).call(); |
| git.commit().setMessage("Added file and link").call(); |
| |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| |
| // replace link with directory containing only directories, no files |
| FileUtils.delete(link); |
| FileUtils.mkdirs(new File(link, "dummyDir")); |
| assertTrue("Link must be a directory now", link.isDirectory()); |
| |
| assertFalse("Must not delete non empty directory", link.delete()); |
| |
| // modify file |
| writeTrashFile(fname, "b"); |
| assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/")); |
| recorder.assertNoEvent(); |
| |
| // revert both paths to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname) |
| .addPath(linkName).call(); |
| |
| assertWorkDir(mkmap(fname, "a", linkName, "a")); |
| recorder.assertEvent(new String[] { fname, linkName }, |
| ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| String fname = "file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| |
| // Add a link to file |
| String linkName = "link"; |
| File link = writeLink(linkName, fname).toFile(); |
| git.add().addFilepattern(linkName).call(); |
| git.commit().setMessage("Added file and link").call(); |
| |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| |
| // replace link with directory containing only directories, no files |
| FileUtils.delete(link); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir1", "file1", "c"); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir2", "file2", "d"); |
| |
| assertTrue("File must be a directory now", link.isDirectory()); |
| assertFalse("Must not delete non empty directory", link.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c", |
| linkName + "/dir2/file2", "d")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(linkName) |
| .call(); |
| |
| // expect only the one added to the index |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| recorder.assertEvent(new String[] { linkName }, |
| ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry() |
| throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| String fname = "file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| |
| // Add a link to file |
| String linkName = "link"; |
| File link = writeLink(linkName, fname).toFile(); |
| git.add().addFilepattern(linkName).call(); |
| git.commit().setMessage("Added file and link").call(); |
| |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| |
| // replace link with directory containing only directories, no files |
| FileUtils.delete(link); |
| |
| // create and add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir1", "file1", "c"); |
| git.add().addFilepattern(linkName + "/dir1/file1").call(); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir2", "file2", "d"); |
| |
| assertTrue("File must be a directory now", link.isDirectory()); |
| assertFalse("Must not delete non empty directory", link.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c", |
| linkName + "/dir2/file2", "d")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(linkName) |
| .call(); |
| |
| // original file and link |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| recorder.assertEvent(new String[] { linkName }, |
| ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeFileToEmptyDir() throws Exception { |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| File file = writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("Added file").call(); |
| |
| // replace file with empty directory |
| FileUtils.delete(file); |
| FileUtils.mkdir(file); |
| assertTrue("File must be a directory now", file.isDirectory()); |
| assertWorkDir(mkmap(fname, "/")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call(); |
| assertWorkDir(mkmap(fname, "a")); |
| recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeFileToEmptyDirs() throws Exception { |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| File file = writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("Added file").call(); |
| |
| // replace file with directory containing only directories, no files |
| FileUtils.delete(file); |
| FileUtils.mkdirs(new File(file, "dummyDir")); |
| assertTrue("File must be a directory now", file.isDirectory()); |
| assertFalse("Must not delete non empty directory", file.delete()); |
| |
| assertWorkDir(mkmap(fname + "/dummyDir", "/")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call(); |
| assertWorkDir(mkmap(fname, "a")); |
| recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeFileToNonEmptyDirs() throws Exception { |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| File file = writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("Added file").call(); |
| |
| assertWorkDir(mkmap(fname, "a")); |
| |
| // replace file with directory containing only directories, no files |
| FileUtils.delete(file); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(fname + "/dir1", "file1", "c"); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(fname + "/dir2", "file2", "d"); |
| |
| assertTrue("File must be a directory now", file.isDirectory()); |
| assertFalse("Must not delete non empty directory", file.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname + "/dir1/file1", "c", |
| fname + "/dir2/file2", "d")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call(); |
| |
| // expect only the one added to the index |
| assertWorkDir(mkmap(fname, "a")); |
| recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY); |
| |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutChangeFileToNonEmptyDirsAndNewIndexEntry() |
| throws Exception { |
| String fname = "was_file"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| File file = writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("Added file").call(); |
| |
| assertWorkDir(mkmap(fname, "a")); |
| |
| // replace file with directory containing only directories, no files |
| FileUtils.delete(file); |
| |
| // create and add a file in the new directory to the index |
| writeTrashFile(fname + "/dir", "file1", "c"); |
| git.add().addFilepattern(fname + "/dir/file1").call(); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(fname + "/dir", "file2", "d"); |
| |
| assertTrue("File must be a directory now", file.isDirectory()); |
| assertFalse("Must not delete non empty directory", file.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname + "/dir/file1", "c", fname + "/dir/file2", |
| "d")); |
| recorder.assertNoEvent(); |
| |
| // revert path to HEAD state |
| git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call(); |
| assertWorkDir(mkmap(fname, "a")); |
| recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY); |
| Status st = git.status().call(); |
| assertTrue(st.isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCheckoutOutChangesAutoCRLFfalse() throws IOException { |
| setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo")); |
| checkout(); |
| assertIndex(mkmap("foo/bar", "foo\nbar")); |
| assertWorkDir(mkmap("foo/bar", "foo\nbar")); |
| } |
| |
| @Test |
| public void testCheckoutOutChangesAutoCRLFInput() throws IOException { |
| setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo")); |
| db.getConfig().setString("core", null, "autocrlf", "input"); |
| checkout(); |
| assertIndex(mkmap("foo/bar", "foo\nbar")); |
| assertWorkDir(mkmap("foo/bar", "foo\nbar")); |
| } |
| |
| @Test |
| public void testCheckoutOutChangesAutoCRLFtrue() throws IOException { |
| setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo")); |
| db.getConfig().setString("core", null, "autocrlf", "true"); |
| checkout(); |
| assertIndex(mkmap("foo/bar", "foo\nbar")); |
| assertWorkDir(mkmap("foo/bar", "foo\r\nbar")); |
| } |
| |
| @Test |
| public void testCheckoutOutChangesAutoCRLFtrueBinary() throws IOException { |
| setupCase(mk("foo"), mkmap("foo/bar", "foo\nb\u0000ar"), mk("foo")); |
| db.getConfig().setString("core", null, "autocrlf", "true"); |
| checkout(); |
| assertIndex(mkmap("foo/bar", "foo\nb\u0000ar")); |
| assertWorkDir(mkmap("foo/bar", "foo\nb\u0000ar")); |
| } |
| |
| @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")); |
| assertEquals(Arrays.asList("foo"), getConflicts()); |
| assertTrue(new File(trash, "foo").isFile()); |
| } |
| } |
| |
| @Test |
| public void testDontOverwriteEmptyFolder() throws IOException { |
| setupCase(mk("foo"), mk("foo"), mk("foo")); |
| FileUtils.mkdir(new File(db.getWorkTree(), "d")); |
| checkout(); |
| assertWorkDir(mkmap("foo", "foo", "d", "/")); |
| } |
| |
| @Test |
| public void testOverwriteUntrackedIgnoredFile() throws IOException, |
| GitAPIException { |
| String fname="file.txt"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("create file").call(); |
| |
| // Create branch |
| git.branchCreate().setName("side").call(); |
| |
| // Modify file |
| writeTrashFile(fname, "b"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("modify file").call(); |
| recorder.assertNoEvent(); |
| |
| // Switch branches |
| git.checkout().setName("side").call(); |
| recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY); |
| git.rm().addFilepattern(fname).call(); |
| recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { fname }); |
| writeTrashFile(".gitignore", fname); |
| git.add().addFilepattern(".gitignore").call(); |
| git.commit().setMessage("delete and ignore file").call(); |
| |
| writeTrashFile(fname, "Something different"); |
| recorder.assertNoEvent(); |
| git.checkout().setName("master").call(); |
| assertWorkDir(mkmap(fname, "b")); |
| recorder.assertEvent(new String[] { fname }, |
| new String[] { ".gitignore" }); |
| assertTrue(git.status().call().isClean()); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testOverwriteUntrackedFileModeChange() |
| throws IOException, GitAPIException { |
| String fname = "file.txt"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| File file = writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| git.commit().setMessage("create file").call(); |
| assertWorkDir(mkmap(fname, "a")); |
| |
| // Create branch |
| git.branchCreate().setName("side").call(); |
| |
| // Switch branches |
| git.checkout().setName("side").call(); |
| recorder.assertNoEvent(); |
| |
| // replace file with directory containing files |
| FileUtils.delete(file); |
| |
| // create and add a file in the new directory to the index |
| writeTrashFile(fname + "/dir1", "file1", "c"); |
| git.add().addFilepattern(fname + "/dir1/file1").call(); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(fname + "/dir2", "file2", "d"); |
| |
| assertTrue("File must be a directory now", file.isDirectory()); |
| assertFalse("Must not delete non empty directory", file.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname + "/dir1/file1", "c", |
| fname + "/dir2/file2", "d")); |
| |
| try { |
| git.checkout().setName("master").call(); |
| fail("did not throw exception"); |
| } catch (Exception e) { |
| // 2 extra files are still there |
| assertWorkDir(mkmap(fname + "/dir1/file1", "c", |
| fname + "/dir2/file2", "d")); |
| } |
| recorder.assertNoEvent(); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testOverwriteUntrackedLinkModeChange() |
| throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| String fname = "file.txt"; |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add a file |
| writeTrashFile(fname, "a"); |
| git.add().addFilepattern(fname).call(); |
| |
| // Add a link to file |
| String linkName = "link"; |
| File link = writeLink(linkName, fname).toFile(); |
| git.add().addFilepattern(linkName).call(); |
| git.commit().setMessage("Added file and link").call(); |
| |
| assertWorkDir(mkmap(linkName, "a", fname, "a")); |
| |
| // Create branch |
| git.branchCreate().setName("side").call(); |
| |
| // Switch branches |
| git.checkout().setName("side").call(); |
| recorder.assertNoEvent(); |
| |
| // replace link with directory containing files |
| FileUtils.delete(link); |
| |
| // create and add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir1", "file1", "c"); |
| git.add().addFilepattern(linkName + "/dir1/file1").call(); |
| |
| // create but do not add a file in the new directory to the index |
| writeTrashFile(linkName + "/dir2", "file2", "d"); |
| |
| assertTrue("Link must be a directory now", link.isDirectory()); |
| assertFalse("Must not delete non empty directory", link.delete()); |
| |
| // 2 extra files are created |
| assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c", |
| linkName + "/dir2/file2", "d")); |
| |
| try { |
| git.checkout().setName("master").call(); |
| fail("did not throw exception"); |
| } catch (Exception e) { |
| // 2 extra files are still there |
| assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c", |
| linkName + "/dir2/file2", "d")); |
| } |
| recorder.assertNoEvent(); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testFileModeChangeWithNoContentChangeUpdate() throws Exception { |
| if (!FS.DETECTED.supportsExecute()) |
| return; |
| |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add non-executable file |
| File file = writeTrashFile("file.txt", "a"); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit1").call(); |
| assertFalse(db.getFS().canExecute(file)); |
| |
| // Create branch |
| git.branchCreate().setName("b1").call(); |
| |
| // Make file executable |
| db.getFS().setExecute(file, true); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit2").call(); |
| recorder.assertNoEvent(); |
| |
| // Verify executable and working directory is clean |
| Status status = git.status().call(); |
| assertTrue(status.getModified().isEmpty()); |
| assertTrue(status.getChanged().isEmpty()); |
| assertTrue(db.getFS().canExecute(file)); |
| |
| // Switch branches |
| git.checkout().setName("b1").call(); |
| |
| // Verify not executable and working directory is clean |
| status = git.status().call(); |
| assertTrue(status.getModified().isEmpty()); |
| assertTrue(status.getChanged().isEmpty()); |
| assertFalse(db.getFS().canExecute(file)); |
| recorder.assertEvent(new String[] { "file.txt" }, |
| ChangeRecorder.EMPTY); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testFileModeChangeAndContentChangeConflict() throws Exception { |
| if (!FS.DETECTED.supportsExecute()) |
| return; |
| |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add non-executable file |
| File file = writeTrashFile("file.txt", "a"); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit1").call(); |
| assertFalse(db.getFS().canExecute(file)); |
| |
| // Create branch |
| git.branchCreate().setName("b1").call(); |
| |
| // Make file executable |
| db.getFS().setExecute(file, true); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit2").call(); |
| |
| // Verify executable and working directory is clean |
| Status status = git.status().call(); |
| assertTrue(status.getModified().isEmpty()); |
| assertTrue(status.getChanged().isEmpty()); |
| assertTrue(db.getFS().canExecute(file)); |
| |
| writeTrashFile("file.txt", "b"); |
| |
| // Switch branches |
| CheckoutCommand checkout = git.checkout().setName("b1"); |
| try { |
| checkout.call(); |
| fail("Checkout exception not thrown"); |
| } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) { |
| CheckoutResult result = checkout.getResult(); |
| assertNotNull(result); |
| assertNotNull(result.getConflictList()); |
| assertEquals(1, result.getConflictList().size()); |
| assertTrue(result.getConflictList().contains("file.txt")); |
| } |
| recorder.assertNoEvent(); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testDirtyFileModeEqualHeadMerge() |
| throws Exception { |
| if (!FS.DETECTED.supportsExecute()) |
| return; |
| |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add non-executable file |
| File file = writeTrashFile("file.txt", "a"); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit1").call(); |
| assertFalse(db.getFS().canExecute(file)); |
| |
| // Create branch |
| git.branchCreate().setName("b1").call(); |
| |
| // Create second commit and don't touch file |
| writeTrashFile("file2.txt", ""); |
| git.add().addFilepattern("file2.txt").call(); |
| git.commit().setMessage("commit2").call(); |
| |
| // stage a mode change |
| writeTrashFile("file.txt", "a"); |
| db.getFS().setExecute(file, true); |
| git.add().addFilepattern("file.txt").call(); |
| |
| // dirty the file |
| writeTrashFile("file.txt", "b"); |
| |
| assertEquals( |
| "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]", |
| indexState(CONTENT)); |
| assertWorkDir(mkmap("file.txt", "b", "file2.txt", "")); |
| recorder.assertNoEvent(); |
| |
| // Switch branches and check that the dirty file survived in |
| // worktree and index |
| git.checkout().setName("b1").call(); |
| assertEquals("[file.txt, mode:100755, content:a]", |
| indexState(CONTENT)); |
| assertWorkDir(mkmap("file.txt", "b")); |
| recorder.assertEvent(ChangeRecorder.EMPTY, |
| new String[] { "file2.txt" }); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testDirtyFileModeEqualIndexMerge() |
| throws Exception { |
| if (!FS.DETECTED.supportsExecute()) |
| return; |
| |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add non-executable file |
| File file = writeTrashFile("file.txt", "a"); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit1").call(); |
| assertFalse(db.getFS().canExecute(file)); |
| |
| // Create branch |
| git.branchCreate().setName("b1").call(); |
| |
| // Create second commit with executable file |
| file = writeTrashFile("file.txt", "b"); |
| db.getFS().setExecute(file, true); |
| git.add().addFilepattern("file.txt").call(); |
| git.commit().setMessage("commit2").call(); |
| |
| // stage the same content as in the branch we want to switch to |
| writeTrashFile("file.txt", "a"); |
| db.getFS().setExecute(file, false); |
| git.add().addFilepattern("file.txt").call(); |
| |
| // dirty the file |
| writeTrashFile("file.txt", "c"); |
| db.getFS().setExecute(file, true); |
| |
| assertEquals("[file.txt, mode:100644, content:a]", |
| indexState(CONTENT)); |
| assertWorkDir(mkmap("file.txt", "c")); |
| recorder.assertNoEvent(); |
| |
| // Switch branches and check that the dirty file survived in |
| // worktree |
| // and index |
| git.checkout().setName("b1").call(); |
| assertEquals("[file.txt, mode:100644, content:a]", |
| indexState(CONTENT)); |
| assertWorkDir(mkmap("file.txt", "c")); |
| recorder.assertNoEvent(); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test |
| public void testFileModeChangeAndContentChangeNoConflict() throws Exception { |
| if (!FS.DETECTED.supportsExecute()) |
| return; |
| |
| ChangeRecorder recorder = new ChangeRecorder(); |
| ListenerHandle handle = null; |
| try (Git git = new Git(db)) { |
| handle = db.getListenerList() |
| .addWorkingTreeModifiedListener(recorder); |
| // Add first file |
| File file1 = writeTrashFile("file1.txt", "a"); |
| git.add().addFilepattern("file1.txt").call(); |
| git.commit().setMessage("commit1").call(); |
| assertFalse(db.getFS().canExecute(file1)); |
| |
| // Add second file |
| File file2 = writeTrashFile("file2.txt", "b"); |
| git.add().addFilepattern("file2.txt").call(); |
| git.commit().setMessage("commit2").call(); |
| assertFalse(db.getFS().canExecute(file2)); |
| recorder.assertNoEvent(); |
| |
| // Create branch from first commit |
| assertNotNull(git.checkout().setCreateBranch(true).setName("b1") |
| .setStartPoint(Constants.HEAD + "~1").call()); |
| recorder.assertEvent(ChangeRecorder.EMPTY, |
| new String[] { "file2.txt" }); |
| |
| // Change content and file mode in working directory and index |
| file1 = writeTrashFile("file1.txt", "c"); |
| db.getFS().setExecute(file1, true); |
| git.add().addFilepattern("file1.txt").call(); |
| |
| // Switch back to 'master' |
| assertNotNull(git.checkout().setName(Constants.MASTER).call()); |
| recorder.assertEvent(new String[] { "file2.txt" }, |
| ChangeRecorder.EMPTY); |
| } finally { |
| if (handle != null) { |
| handle.remove(); |
| } |
| } |
| } |
| |
| @Test(expected = CheckoutConflictException.class) |
| public void testFolderFileConflict() throws Exception { |
| RevCommit headCommit = commitFile("f/a", "initial content", "master"); |
| RevCommit checkoutCommit = commitFile("f/a", "side content", "side"); |
| FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE); |
| writeTrashFile("f", "file instead of folder"); |
| new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(), |
| checkoutCommit.getTree()).checkout(); |
| } |
| |
| @Test |
| public void testMultipleContentConflicts() throws Exception { |
| commitFile("a", "initial content", "master"); |
| RevCommit headCommit = commitFile("b", "initial content", "master"); |
| commitFile("a", "side content", "side"); |
| RevCommit checkoutCommit = commitFile("b", "side content", "side"); |
| writeTrashFile("a", "changed content"); |
| writeTrashFile("b", "changed content"); |
| |
| try { |
| new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(), |
| checkoutCommit.getTree()).checkout(); |
| fail(); |
| } catch (CheckoutConflictException expected) { |
| assertEquals(2, expected.getConflictingFiles().length); |
| assertTrue(Arrays.asList(expected.getConflictingFiles()) |
| .contains("a")); |
| assertTrue(Arrays.asList(expected.getConflictingFiles()) |
| .contains("b")); |
| assertEquals("changed content", read("a")); |
| assertEquals("changed content", read("b")); |
| } |
| } |
| |
| @Test |
| public void testFolderFileAndContentConflicts() throws Exception { |
| RevCommit headCommit = commitFile("f/a", "initial content", "master"); |
| commitFile("b", "side content", "side"); |
| RevCommit checkoutCommit = commitFile("f/a", "side content", "side"); |
| FileUtils.delete(new File(db.getWorkTree(), "f"), FileUtils.RECURSIVE); |
| writeTrashFile("f", "file instead of a folder"); |
| writeTrashFile("b", "changed content"); |
| |
| try { |
| new DirCacheCheckout(db, headCommit.getTree(), db.lockDirCache(), |
| checkoutCommit.getTree()).checkout(); |
| fail(); |
| } catch (CheckoutConflictException expected) { |
| assertEquals(2, expected.getConflictingFiles().length); |
| assertTrue(Arrays.asList(expected.getConflictingFiles()) |
| .contains("b")); |
| assertTrue(Arrays.asList(expected.getConflictingFiles()) |
| .contains("f")); |
| assertEquals("file instead of a folder", read("f")); |
| assertEquals("changed content", read("b")); |
| } |
| } |
| |
| @Test |
| public void testLongFilename() throws Exception { |
| char[] bytes = new char[253]; |
| Arrays.fill(bytes, 'f'); |
| String longFileName = new String(bytes); |
| // 1 |
| doit(mkmap(longFileName, "a"), mkmap(longFileName, "b"), |
| mkmap(longFileName, "a")); |
| writeTrashFile(longFileName, "a"); |
| checkout(); |
| assertNoConflicts(); |
| assertUpdated(longFileName); |
| } |
| |
| @Test |
| public void testIgnoredDirectory() throws Exception { |
| writeTrashFile(".gitignore", "src/ignored"); |
| writeTrashFile("src/ignored/sub/foo.txt", "1"); |
| try (Git git = new Git(db)) { |
| git.add().addFilepattern(".").call(); |
| RevCommit commit = git.commit().setMessage("adding .gitignore") |
| .call(); |
| writeTrashFile("foo.txt", "2"); |
| writeTrashFile("zzz.txt", "3"); |
| git.add().addFilepattern("foo.txt").call(); |
| git.commit().setMessage("add file").call(); |
| assertEquals("Should not have entered ignored directory", 1, |
| resetHardAndCount(commit)); |
| } |
| } |
| |
| @Test |
| public void testIgnoredDirectoryWithTrackedContent() throws Exception { |
| writeTrashFile("src/ignored/sub/foo.txt", "1"); |
| try (Git git = new Git(db)) { |
| git.add().addFilepattern(".").call(); |
| git.commit().setMessage("adding foo.txt").call(); |
| writeTrashFile(".gitignore", "src/ignored"); |
| writeTrashFile("src/ignored/sub/foo.txt", "2"); |
| writeTrashFile("src/ignored/other/bar.txt", "3"); |
| git.add().addFilepattern(".").call(); |
| RevCommit commit = git.commit().setMessage("adding .gitignore") |
| .call(); |
| writeTrashFile("foo.txt", "2"); |
| writeTrashFile("zzz.txt", "3"); |
| git.add().addFilepattern("foo.txt").call(); |
| git.commit().setMessage("add file").call(); |
| File file = writeTrashFile("src/ignored/sub/foo.txt", "3"); |
| assertEquals("Should have entered ignored directory", 3, |
| resetHardAndCount(commit)); |
| checkFile(file, "2"); |
| } |
| } |
| |
| @Test |
| public void testResetWithChangeInGitignore() throws Exception { |
| writeTrashFile(".gitignore", "src/ignored"); |
| writeTrashFile("src/ignored/sub/foo.txt", "1"); |
| try (Git git = new Git(db)) { |
| git.add().addFilepattern(".").call(); |
| RevCommit initial = git.commit().setMessage("initial").call(); |
| writeTrashFile("src/newignored/foo.txt", "2"); |
| writeTrashFile("src/.gitignore", "newignored"); |
| git.add().addFilepattern(".").call(); |
| RevCommit commit = git.commit().setMessage("newignored").call(); |
| assertEquals("Should not have entered src/newignored directory", 1, |
| resetHardAndCount(initial)); |
| assertEquals("Should have entered src/newignored directory", 2, |
| resetHardAndCount(commit)); |
| deleteTrashFile("src/.gitignore"); |
| git.rm().addFilepattern("src/.gitignore").call(); |
| RevCommit top = git.commit().setMessage("Unignore newignore") |
| .call(); |
| assertEquals("Should have entered src/newignored directory", 2, |
| resetHardAndCount(initial)); |
| assertEquals("Should have entered src/newignored directory", 2, |
| resetHardAndCount(commit)); |
| assertEquals("Should not have entered src/newignored directory", 1, |
| resetHardAndCount(top)); |
| |
| } |
| } |
| |
| @Test |
| public void testCheckoutWithEmptyIndexDoesntOverwrite() throws Exception { |
| try (Git git = new Git(db); |
| TestRepository<Repository> db_t = new TestRepository<>(db)) { |
| // prepare the commits |
| BranchBuilder master = db_t.branch("master"); |
| RevCommit mergeCommit = master.commit() |
| .add("p/x", "headContent") |
| .message("m0").create(); |
| master.commit().add("p/x", "headContent").message("m1").create(); |
| git.checkout().setName("master").call(); |
| |
| // empty index and write unsaved data in 'p' |
| git.rm().addFilepattern("p").call(); |
| writeTrashFile("p", "important data"); |
| |
| git.checkout().setName(mergeCommit.getName()).call(); |
| |
| assertEquals("", indexState(CONTENT)); |
| assertEquals("important data", read("p")); |
| } |
| } |
| |
| private static class TestFileTreeIterator extends FileTreeIterator { |
| |
| // For assertions only |
| private final int[] count; |
| |
| public TestFileTreeIterator(Repository repo, int[] count) { |
| super(repo); |
| this.count = count; |
| } |
| |
| protected TestFileTreeIterator(final WorkingTreeIterator p, |
| final File root, FS fs, FileModeStrategy fileModeStrategy, |
| int[] count) { |
| super(p, root, fs, fileModeStrategy); |
| this.count = count; |
| } |
| |
| @Override |
| protected AbstractTreeIterator enterSubtree() { |
| count[0] += 1; |
| return new TestFileTreeIterator(this, |
| ((FileEntry) current()).getFile(), fs, fileModeStrategy, |
| count); |
| } |
| } |
| |
| private int resetHardAndCount(RevCommit commit) throws Exception { |
| int[] callCount = { 0 }; |
| DirCache cache = db.lockDirCache(); |
| FileTreeIterator workingTreeIterator = new TestFileTreeIterator(db, |
| callCount); |
| try { |
| DirCacheCheckout checkout = new DirCacheCheckout(db, null, cache, |
| commit.getTree().getId(), workingTreeIterator); |
| checkout.setFailOnConflict(false); |
| checkout.checkout(); |
| } finally { |
| cache.unlock(); |
| } |
| return callCount[0]; |
| } |
| |
| public void assertWorkDir(Map<String, String> i) |
| throws CorruptObjectException, |
| IOException { |
| try (TreeWalk walk = new TreeWalk(db)) { |
| walk.setRecursive(false); |
| 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); |
| File file = new File(db.getWorkTree(), path); |
| assertTrue(file.exists()); |
| if (file.isFile()) { |
| assertNotNull("found unexpected file for path " + path |
| + " in workdir", expectedValue); |
| try (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; |
| } |
| assertArrayEquals( |
| "unexpected content for path " + path |
| + " in workDir. ", |
| buffer, i.get(path).getBytes(UTF_8)); |
| } |
| nrFiles++; |
| } else if (file.isDirectory()) { |
| String[] files = file.list(); |
| if (files != null && files.length == 0) { |
| assertEquals("found unexpected empty folder for path " |
| + path + " in workDir. ", "/", i.get(path)); |
| nrFiles++; |
| } |
| } |
| if (walk.isSubtree()) { |
| walk.enterSubtree(); |
| } |
| } |
| assertEquals("WorkDir has not the right size.", i.size(), nrFiles); |
| } |
| } |
| } |