diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java index 0b12058c8..064100839 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java @@ -45,19 +45,26 @@ package org.eclipse.jgit.treewalk; 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 java.io.File; import java.security.MessageDigest; import org.eclipse.jgit.api.Git; +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.dircache.DirCacheIterator; +import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.RawParseUtils; import org.junit.Before; @@ -208,6 +215,170 @@ public class FileTreeIteratorTest extends RepositoryTestCase { assertFalse(fti.isModified(dce, false)); } + @Test + public void submoduleHeadMatchesIndex() throws Exception { + Git git = new Git(db); + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call(); + + TreeWalk walk = new TreeWalk(db); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } + + @Test + public void submoduleWithNoGitDirectory() throws Exception { + Git git = new Git(db); + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + File submoduleRoot = new File(db.getWorkTree(), path); + assertTrue(submoduleRoot.mkdir()); + assertTrue(new File(submoduleRoot, Constants.DOT_GIT).mkdir()); + + TreeWalk walk = new TreeWalk(db); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertFalse(indexIter.idEqual(workTreeIter)); + assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + } + + @Test + public void submoduleWithNoHead() throws Exception { + Git git = new Git(db); + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + assertNotNull(Git.init().setDirectory(new File(db.getWorkTree(), path)) + .call().getRepository()); + + TreeWalk walk = new TreeWalk(db); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertFalse(indexIter.idEqual(workTreeIter)); + assertEquals(ObjectId.zeroId(), workTreeIter.getEntryObjectId()); + } + + @Test + public void submoduleDirectoryIterator() throws Exception { + Git git = new Git(db); + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call(); + + TreeWalk walk = new TreeWalk(db); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db.getWorkTree(), + db.getFS(), db.getConfig().get(WorkingTreeOptions.KEY)); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } + + @Test + public void submoduleNestedWithHeadMatchingIndex() throws Exception { + Git git = new Git(db); + writeTrashFile("file.txt", "content"); + git.add().addFilepattern("file.txt").call(); + final RevCommit id = git.commit().setMessage("create file").call(); + final String path = "sub/dir1/dir2"; + DirCache cache = db.lockDirCache(); + DirCacheEditor editor = cache.editor(); + editor.add(new PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.GITLINK); + ent.setObjectId(id); + } + }); + editor.commit(); + + Git.cloneRepository().setURI(db.getDirectory().toURI().toString()) + .setDirectory(new File(db.getWorkTree(), path)).call(); + + TreeWalk walk = new TreeWalk(db); + DirCacheIterator indexIter = new DirCacheIterator(db.readDirCache()); + FileTreeIterator workTreeIter = new FileTreeIterator(db); + walk.addTree(indexIter); + walk.addTree(workTreeIter); + walk.setFilter(PathFilter.create(path)); + + assertTrue(walk.next()); + assertTrue(indexIter.idEqual(workTreeIter)); + } + private static String nameOf(final AbstractTreeIterator i) { return RawParseUtils.decode(Constants.CHARSET, i.path, 0, i.pathLen); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index c683308e9..c7ae7757a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -223,4 +223,11 @@ public class FileTreeIterator extends WorkingTreeIterator { public File getEntryFile() { return ((FileEntry) current()).getFile(); } + + @Override + protected byte[] idSubmodule(final Entry e) { + if (repository == null) + return idSubmodule(getDirectory(), e); + return super.idSubmodule(e); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index d067e9099..8db0516de 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -67,12 +67,15 @@ import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.ignore.IgnoreNode; import org.eclipse.jgit.ignore.IgnoreRule; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; @@ -122,6 +125,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** If there is a .gitignore file present, the parsed rules from it. */ private IgnoreNode ignoreNode; + /** Repository that is the root level being iterated over */ + protected Repository repository; + /** * Create a new iterator with no parent. * @@ -177,6 +183,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * the repository. */ protected void initRootIterator(Repository repo) { + repository = repo; Entry entry; if (ignoreNode instanceof PerDirectoryIgnoreNode) entry = ((PerDirectoryIgnoreNode) ignoreNode).entry; @@ -239,13 +246,66 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { // return zeroid; case FileMode.TYPE_GITLINK: - // TODO: Support obtaining current HEAD SHA-1 from nested repository - // - return zeroid; + contentIdFromPtr = ptr; + return contentId = idSubmodule(entries[ptr]); } return zeroid; } + /** + * Get submodule id for given entry. + * + * @param e + * @return non-null submodule id + */ + protected byte[] idSubmodule(Entry e) { + if (repository == null) + return zeroid; + File directory; + try { + directory = repository.getWorkTree(); + } catch (NoWorkTreeException nwte) { + return zeroid; + } + return idSubmodule(directory, e); + } + + /** + * Get submodule id using the repository at the location of the entry + * relative to the directory. + * + * @param directory + * @param e + * @return non-null submodule id + */ + protected byte[] idSubmodule(File directory, Entry e) { + final String gitDirPath = e.getName() + "/" + Constants.DOT_GIT; + final File submoduleGitDir = new File(directory, gitDirPath); + if (!submoduleGitDir.isDirectory()) + return zeroid; + final Repository submoduleRepo; + try { + FS fs = repository != null ? repository.getFS() : FS.DETECTED; + submoduleRepo = new RepositoryBuilder().setGitDir(submoduleGitDir) + .setMustExist(true).setFS(fs).build(); + } catch (IOException exception) { + return zeroid; + } + final ObjectId head; + try { + head = submoduleRepo.resolve(Constants.HEAD); + } catch (IOException exception) { + return zeroid; + } finally { + submoduleRepo.close(); + } + if (head == null) + return zeroid; + final byte[] id = new byte[Constants.OBJECT_ID_LENGTH]; + head.copyRawTo(id, 0); + return id; + } + private static final byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };