diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index 903875103..3baa4d65f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -37,12 +37,22 @@ */ package org.eclipse.jgit.lib; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeResult.MergeStatus; +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.errors.CorruptObjectException; +import org.eclipse.jgit.errors.NoWorkTreeException; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; public class DirCacheCheckoutTest extends ReadTreeTest { private DirCacheCheckout dco; @@ -83,4 +93,78 @@ public class DirCacheCheckoutTest extends ReadTreeTest { public List getConflicts() { return dco.getConflicts(); } + + public void testResetHard() throws IOException, NoFilepatternException, + GitAPIException { + Git git = new Git(db); + writeTrashFile("f", "f()"); + writeTrashFile("D/g", "g()"); + git.add().addFilepattern(".").call(); + git.commit().setMessage("inital").call(); + assertIndex(mkmap("f", "f()", "D/g", "g()")); + + git.branchCreate().setName("topic").call(); + + 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()")); + + checkoutBranch("refs/heads/topic"); + assertIndex(mkmap("f", "f()", "D/g", "g()")); + + 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()")); + + resetHard(master); + assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()")); + resetHard(topic); + assertIndex(mkmap("f", "f()\nside", "G/i", "i()")); + assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()")); + + assertEquals(MergeStatus.CONFLICTING, git.merge().include(master) + .call().getMergeStatus()); + assertEquals( + "[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)); + + 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()")); + } + + 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 checkoutBranch(String branchName) + throws IllegalStateException, IOException { + RevWalk walk = new RevWalk(db); + RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD)); + RevCommit branch = walk.parseCommit(db.resolve(branchName)); + DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), + db.lockDirCache(), branch.getTree()); + dco.setFailOnConflict(true); + assertTrue(dco.checkout()); + walk.release(); + // update the HEAD + RefUpdate refUpdate = db.updateRef(Constants.HEAD); + assertEquals(Result.FORCED, refUpdate.link(branchName)); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index d2629728e..07f97d9b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -314,9 +314,9 @@ public class DirCacheCheckout { WorkingTreeIterator f) { if (m != null) { if (i == null || f == null || !m.idEqual(i) - || (i.getDirCacheEntry() != null && f.isModified(i - .getDirCacheEntry(), true, config_filemode(), repo - .getFS()))) { + || (i.getDirCacheEntry() != null && (f.isModified( + i.getDirCacheEntry(), true, config_filemode(), + repo.getFS()) || i.getDirCacheEntry().getStage() != 0))) { update(m.getEntryPathString(), m.getEntryObjectId(), m.getEntryFileMode()); } else @@ -334,7 +334,7 @@ public class DirCacheCheckout { conflicts.remove(i.getEntryPathString()); } } - } else + } else if (i.getDirCacheEntry().getStage() == 0) keep(i.getDirCacheEntry()); } } @@ -372,10 +372,14 @@ public class DirCacheCheckout { File file=null; String last = ""; - for (String r : removed) { + // when deleting files process them in the opposite order as they have + // been reported. This ensures the files are deleted before we delete + // their parent folders + for (int i = removed.size() - 1; i >= 0; i--) { + String r = removed.get(i); file = new File(repo.getWorkTree(), r); - if (!file.delete()) - toBeDeleted.add(r); + if (!file.delete() && file.exists()) + toBeDeleted.add(r); else { if (!isSamePrefix(r, last)) removeEmptyParents(file);