Fixed checkouts when HEAD is ignored

In the case where DirCacheCheckout was used to checkout a tree
without taking HEAD into account (e.g. during a clone or hard reset)
we didn't handle conflicts correctly. E.g. if there are conflicts
(entries with stage != 0) in the index and we tried to hard reset
we have been processing the conflicting pathes multiple times (once
for every stage). With this fix we will update the index with the
entry from the "merge" state (the state we want checkout) when we
detect existing conflicts.

Change-Id: Iffbddccaa588cf0d1460a5e44dabaf540d996e26
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
This commit is contained in:
Christian Halstrick 2010-11-12 23:31:55 +01:00 committed by Chris Aniszczyk
parent 7bce473c92
commit da1ea27fa2
2 changed files with 95 additions and 7 deletions

View File

@ -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 Map<String, ObjectId> getUpdated() {
public List<String> 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));
}
}

View File

@ -314,9 +314,9 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
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 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
conflicts.remove(i.getEntryPathString());
}
}
} else
} else if (i.getDirCacheEntry().getStage() == 0)
keep(i.getDirCacheEntry());
}
}
@ -372,10 +372,14 @@ public boolean checkout() throws IOException {
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);