Browse Source

Implement DIR_NO_GITLINKS

Implement the DIR_NO_GITLINKS setting with the same functionality
it provides in cGit.

Bug: 436200
Change-Id: I8304e42df2d7e8d7925f515805e075a92ff6ce28
Signed-off-by: Preben Ingvaldsen <preben@puppetlabs.com>
tags/v4.3.0.201604071810-r
Preben Ingvaldsen 8 years ago
parent
commit
403f04d8dd

+ 110
- 6
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java View File

@@ -53,6 +53,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;

import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -62,14 +63,11 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Test;
@@ -1002,6 +1000,91 @@ public class AddCommandTest extends RepositoryTestCase {
assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
}

@Test
public void testAddGitlink() throws Exception {
createNestedRepo("git-link-dir");
try (Git git = new Git(db)) {
git.add().addFilepattern("git-link-dir").call();

assertEquals(
"[git-link-dir, mode:160000]",
indexState(0));
Set<String> untrackedFiles = git.status().call().getUntracked();
assert (untrackedFiles.isEmpty());
}

}

@Test
public void testAddSubrepoWithDirNoGitlinks() throws Exception {
createNestedRepo("nested-repo");

// Set DIR_NO_GITLINKS
StoredConfig config = db.getConfig();
config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
config.save();

assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());

try (Git git = new Git(db)) {
git.add().addFilepattern("nested-repo").call();

assertEquals(
"[nested-repo/README1.md, mode:100644]" +
"[nested-repo/README2.md, mode:100644]",
indexState(0));
}

// Turn off DIR_NO_GITLINKS, ensure nested-repo is still treated as
// a normal directory
// Set DIR_NO_GITLINKS
config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, false);
config.save();

writeTrashFile("nested-repo", "README3.md", "content");

try (Git git = new Git(db)) {
git.add().addFilepattern("nested-repo").call();

assertEquals(
"[nested-repo/README1.md, mode:100644]" +
"[nested-repo/README2.md, mode:100644]" +
"[nested-repo/README3.md, mode:100644]",
indexState(0));
}
}

@Test
public void testAddGitlinkDoesNotChange() throws Exception {
createNestedRepo("nested-repo");

try (Git git = new Git(db)) {
git.add().addFilepattern("nested-repo").call();

assertEquals(
"[nested-repo, mode:160000]",
indexState(0));
}

// Set DIR_NO_GITLINKS
StoredConfig config = db.getConfig();
config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
config.save();

assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());

try (Git git = new Git(db)) {
git.add().addFilepattern("nested-repo").call();

assertEquals(
"[nested-repo, mode:160000]",
indexState(0));
}
}

private static DirCacheEntry addEntryToBuilder(String path, File file,
ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
throws IOException {
@@ -1029,4 +1112,25 @@ public class AddCommandTest extends RepositoryTestCase {
throw new IOException("could not commit");
}

private void createNestedRepo(String path) throws IOException {
File gitLinkDir = new File(db.getWorkTree(), path);
FileUtils.mkdir(gitLinkDir);

FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
nestedBuilder.setWorkTree(gitLinkDir);

Repository nestedRepo = nestedBuilder.build();
nestedRepo.create();

writeTrashFile(path, "README1.md", "content");
writeTrashFile(path, "README2.md", "content");

// Commit these changes in the subrepo
try (Git git = new Git(nestedRepo)) {
git.add().addFilepattern(".").call();
git.commit().setMessage("subrepo commit").call();
} catch (GitAPIException e) {
throw new RuntimeException(e);
}
}
}

+ 39
- 3
org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java View File

@@ -64,7 +64,12 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.lib.ConfigConstants;
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.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff;
@@ -279,6 +284,37 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
}
}

@Test
public void testTreewalkEnterSubtree() throws Exception {
try (Git git = new Git(db)) {
writeTrashFile("b/c", "b/c");
writeTrashFile("z/.git", "gitdir: /tmp/somewhere");
git.add().addFilepattern(".").call();
git.rm().addFilepattern("a,").addFilepattern("a,b")
.addFilepattern("a0b").call();
assertEquals("[a/b, mode:100644][b/c, mode:100644][z, mode:160000]",
indexState(0));
FileUtils.delete(new File(db.getWorkTree(), "b"),
FileUtils.RECURSIVE);

TreeWalk tw = new TreeWalk(db);
tw.addTree(new DirCacheIterator(db.readDirCache()));
tw.addTree(new FileTreeIterator(db));
assertTrue(tw.next());
assertEquals("a", tw.getPathString());
tw.enterSubtree();
tw.next();
assertEquals("a/b", tw.getPathString());
tw.next();
assertEquals("b", tw.getPathString());
tw.enterSubtree();
tw.next();
assertEquals("b/c", tw.getPathString());
assertNotNull(tw.getTree(0, AbstractTreeIterator.class));
assertNotNull(tw.getTree(EmptyTreeIterator.class));
}
}

@Test
public void testIsModifiedSymlinkAsFile() throws Exception {
writeTrashFile("symlink", "content");
@@ -345,7 +381,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
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);
@@ -362,7 +398,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
walk.addTree(indexIter);
walk.addTree(workTreeIter);
walk.setFilter(PathFilter.create(path));
assertTrue(walk.next());
assertTrue(indexIter.idEqual(workTreeIter));
}

+ 5
- 1
org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java View File

@@ -45,6 +45,7 @@ package org.eclipse.jgit.api;

import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.eclipse.jgit.lib.FileMode.GITLINK;
import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;

import java.io.IOException;
@@ -201,7 +202,10 @@ public class AddCommand extends GitCommand<DirCache> {
continue;
}

if (f.getEntryRawMode() == TYPE_TREE) {
if ((f.getEntryRawMode() == TYPE_TREE
&& f.getIndexFileMode(c) != FileMode.GITLINK) ||
(f.getEntryRawMode() == TYPE_GITLINK
&& f.getIndexFileMode(c) == FileMode.TREE)) {
// Index entry exists and is symlink, gitlink or file,
// otherwise the tree would have been entered above.
// Replace the index entry by diving into tree of files.

+ 6
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java View File

@@ -235,6 +235,12 @@ public class ConfigConstants {
*/
public static final String CONFIG_KEY_HIDEDOTFILES = "hidedotfiles";

/**
* The "dirnogitlinks" key
* @since 4.3
*/
public static final String CONFIG_KEY_DIRNOGITLINKS = "dirNoGitLinks";

/** The "precomposeunicode" key */
public static final String CONFIG_KEY_PRECOMPOSEUNICODE = "precomposeunicode";


+ 9
- 0
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java View File

@@ -729,4 +729,13 @@ public abstract class AbstractTreeIterator {
public String toString() {
return getClass().getSimpleName() + "[" + getEntryPathString() + "]"; //$NON-NLS-1$
}

/**
* @return whether or not this Iterator is iterating through the Work Tree
*
* @since 4.3
*/
public boolean isWorkTree() {
return false;
}
}

+ 33
- 1
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java View File

@@ -94,7 +94,10 @@ public class FileTreeIterator extends WorkingTreeIterator {
* the repository whose working tree will be scanned.
*/
public FileTreeIterator(Repository repo) {
this(repo, DefaultFileModeStrategy.INSTANCE);
this(repo,
repo.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks() ?
NoGitlinksStrategy.INSTANCE :
DefaultFileModeStrategy.INSTANCE);
}

/**
@@ -291,6 +294,35 @@ public class FileTreeIterator extends WorkingTreeIterator {
}
}

/**
* A FileModeStrategy that implements native git's DIR_NO_GITLINKS
* behavior. This is the same as the default FileModeStrategy, except
* all directories will be treated as directories regardless of whether
* or not they contain a .git directory or file.
*
* @since 4.3
*/
static public class NoGitlinksStrategy implements FileModeStrategy {

/**
* a singleton instance of the default FileModeStrategy
*/
public final static NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy();

@Override
public FileMode getMode(File f, FS.Attributes attributes) {
if (attributes.isSymbolicLink()) {
return FileMode.SYMLINK;
} else if (attributes.isDirectory()) {
return FileMode.TREE;
} else if (attributes.isExecutable()) {
return FileMode.EXECUTABLE_FILE;
} else {
return FileMode.REGULAR_FILE;
}
}
}


/**
* Wrapper for a standard Java IO file

+ 6
- 1
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java View File

@@ -1187,7 +1187,12 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
for (int i = 0; i < trees.length; i++) {
final AbstractTreeIterator t = trees[i];
final AbstractTreeIterator n;
if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
// If we find a GITLINK when attempting to enter a subtree, then the
// GITLINK must exist as a TREE in the index, meaning the working tree
// entry should be treated as a TREE
if (t.matches == ch && !t.eof() &&
(FileMode.TREE.equals(t.mode)
|| (FileMode.GITLINK.equals(t.mode) && t.isWorkTree())))
n = t.createSubtreeIterator(reader, idBuffer);
else
n = t.createEmptyTreeIterator();

+ 27
- 8
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java View File

@@ -264,7 +264,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
// the cached index information for the path.
//
DirCacheIterator i = state.walk.getTree(state.dirCacheTree,
DirCacheIterator.class);
DirCacheIterator.class);
if (i != null) {
DirCacheEntry ent = i.getDirCacheEntry();
if (ent != null && compareMetadata(ent) == MetadataDiff.EQUAL) {
@@ -289,6 +289,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return zeroid;
}

@Override
public boolean isWorkTree() {
return true;
}

/**
* Get submodule id for given entry.
*
@@ -916,17 +921,31 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
*/
public FileMode getIndexFileMode(final DirCacheIterator indexIter) {
final FileMode wtMode = getEntryFileMode();
if (indexIter == null)
return wtMode;
if (getOptions().isFileMode())
if (indexIter == null) {
return wtMode;
}
final FileMode iMode = indexIter.getEntryFileMode();
if (FileMode.REGULAR_FILE == wtMode
&& FileMode.EXECUTABLE_FILE == iMode)
if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) {
return wtMode;
}
if (!getOptions().isFileMode()) {
if (FileMode.REGULAR_FILE == wtMode
&& FileMode.EXECUTABLE_FILE == iMode) {
return iMode;
}
if (FileMode.EXECUTABLE_FILE == wtMode
&& FileMode.REGULAR_FILE == iMode) {
return iMode;
}
}
if (FileMode.GITLINK == iMode
&& FileMode.TREE == wtMode) {
return iMode;
if (FileMode.EXECUTABLE_FILE == wtMode
&& FileMode.REGULAR_FILE == iMode)
}
if (FileMode.TREE == iMode
&& FileMode.GITLINK == wtMode) {
return iMode;
}
return wtMode;
}


+ 13
- 0
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java View File

@@ -73,6 +73,8 @@ public class WorkingTreeOptions {

private final HideDotFiles hideDotFiles;

private final boolean dirNoGitLinks;

private WorkingTreeOptions(final Config rc) {
fileMode = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_KEY_FILEMODE, true);
@@ -87,6 +89,9 @@ public class WorkingTreeOptions {
hideDotFiles = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_HIDEDOTFILES,
HideDotFiles.DOTGITONLY);
dirNoGitLinks = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_DIRNOGITLINKS,
false);
}

/** @return true if the execute bit on working files should be trusted. */
@@ -131,4 +136,12 @@ public class WorkingTreeOptions {
public HideDotFiles getHideDotFiles() {
return hideDotFiles;
}

/**
* @return whether or not we treat nested repos as directories.
* If true, folders containing .git entries will not be
* treated as gitlinks.
* @since 4.3
*/
public boolean isDirNoGitLinks() { return dirNoGitLinks; }
}

Loading…
Cancel
Save