diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java | 339 |
1 files changed, 262 insertions, 77 deletions
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 8d2cb1d8cd..0cac374844 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -2,56 +2,27 @@ * Copyright (C) 2008, Google Inc. * Copyright (C) 2007-2010, Robin Rosenberg <robin.rosenberg@dewire.com> * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> - * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com> and others * - * 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 + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://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. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.treewalk; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.time.Instant; +import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; @@ -63,12 +34,14 @@ import org.eclipse.jgit.util.FS; * Working directory iterator for standard Java IO. * <p> * This iterator uses the standard <code>java.io</code> package to read the - * specified working directory as part of a {@link TreeWalk}. + * specified working directory as part of a + * {@link org.eclipse.jgit.treewalk.TreeWalk}. */ public class FileTreeIterator extends WorkingTreeIterator { + /** - * the starting directory. This directory should correspond to the root of - * the repository. + * the starting directory of this Iterator. All entries are located directly + * in this directory. */ protected final File directory; @@ -79,14 +52,41 @@ public class FileTreeIterator extends WorkingTreeIterator { protected final FS fs; /** + * the strategy used to compute the FileMode for a FileEntry. Can be used to + * control things such as whether to recurse into a directory or create a + * gitlink. + * + * @since 4.3 + */ + protected final FileModeStrategy fileModeStrategy; + + /** * Create a new iterator to traverse the work tree and its children. * * @param repo * the repository whose working tree will be scanned. */ public FileTreeIterator(Repository repo) { + this(repo, + repo.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks() ? + NoGitlinksStrategy.INSTANCE : + DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new iterator to traverse the work tree and its children. + * + * @param repo + * the repository whose working tree will be scanned. + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a FileEntry; + * controls gitlinks etc. + * @since 4.3 + */ + public FileTreeIterator(Repository repo, FileModeStrategy fileModeStrategy) { this(repo.getWorkTree(), repo.getFS(), - repo.getConfig().get(WorkingTreeOptions.KEY)); + repo.getConfig().get(WorkingTreeOptions.KEY), + fileModeStrategy); initRootIterator(repo); } @@ -102,10 +102,32 @@ public class FileTreeIterator extends WorkingTreeIterator { * @param options * working tree options to be used */ - public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options) { + public FileTreeIterator(File root, FS fs, WorkingTreeOptions options) { + this(root, fs, options, DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new iterator to traverse the given directory and its children. + * + * @param root + * the starting directory. This directory should correspond to + * the root of the repository. + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @param options + * working tree options to be used + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a FileEntry; + * controls gitlinks etc. + * @since 4.3 + */ + public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options, + FileModeStrategy fileModeStrategy) { super(options); directory = root; this.fs = fs; + this.fileModeStrategy = fileModeStrategy; init(entries()); } @@ -114,41 +136,165 @@ public class FileTreeIterator extends WorkingTreeIterator { * * @param p * the parent iterator we were created from. + * @param root + * the subdirectory. This should be a directory contained within + * the parent directory. * @param fs * the file system abstraction which will be necessary to perform * certain file system operations. + * @since 4.3 + */ + protected FileTreeIterator(final FileTreeIterator p, final File root, + FS fs) { + this(p, root, fs, p.fileModeStrategy); + } + + /** + * Create a new iterator to traverse a subdirectory, given the specified + * FileModeStrategy. + * + * @param p + * the parent iterator we were created from. * @param root * the subdirectory. This should be a directory contained within - * the parent directory. + * the parent directory + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a given + * FileEntry. + * @since 4.3 */ protected FileTreeIterator(final WorkingTreeIterator p, final File root, - FS fs) { + FS fs, FileModeStrategy fileModeStrategy) { super(p); directory = root; this.fs = fs; + this.fileModeStrategy = fileModeStrategy; init(entries()); } @Override - public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) + public AbstractTreeIterator createSubtreeIterator(ObjectReader reader) throws IncorrectObjectTypeException, IOException { - return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs); + if (!walksIgnoredDirectories() && isEntryIgnored()) { + DirCacheIterator iterator = getDirCacheIterator(); + if (iterator == null) { + return new EmptyTreeIterator(this); + } + // Only enter if we have an associated DirCacheIterator that is + // at the same entry (which indicates there is some already + // tracked file underneath this directory). Otherwise the + // directory is indeed ignored and can be skipped entirely. + } + return enterSubtree(); + } + + + /** + * Create a new iterator for the current entry's subtree. + * <p> + * The parent reference of the iterator must be <code>this</code>, otherwise + * the caller would not be able to exit out of the subtree iterator + * correctly and return to continue walking <code>this</code>. + * + * @return a new iterator that walks over the current subtree. + * @since 5.0 + */ + protected AbstractTreeIterator enterSubtree() { + return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs, + fileModeStrategy); } private Entry[] entries() { - final File[] all = directory.listFiles(); - if (all == null) - return EOF; - final Entry[] r = new Entry[all.length]; - for (int i = 0; i < r.length; i++) - r[i] = new FileEntry(all[i], fs); - return r; + return fs.list(directory, fileModeStrategy); + } + + /** + * An interface representing the methods used to determine the FileMode for + * a FileEntry. + * + * @since 4.3 + */ + public interface FileModeStrategy { + /** + * Compute the FileMode for a given File, based on its attributes. + * + * @param f + * the file to return a FileMode for + * @param attributes + * the attributes of a file + * @return a FileMode indicating whether the file is a regular file, a + * directory, a gitlink, etc. + */ + FileMode getMode(File f, FS.Attributes attributes); } /** + * A default implementation of a FileModeStrategy; defaults to treating + * nested .git directories as gitlinks, etc. + * + * @since 4.3 + */ + public static class DefaultFileModeStrategy implements FileModeStrategy { + /** + * a singleton instance of the default FileModeStrategy + */ + public static final DefaultFileModeStrategy INSTANCE = + new DefaultFileModeStrategy(); + + @Override + public FileMode getMode(File f, FS.Attributes attributes) { + if (attributes.isSymbolicLink()) { + return FileMode.SYMLINK; + } else if (attributes.isDirectory()) { + if (new File(f, Constants.DOT_GIT).exists()) { + return FileMode.GITLINK; + } + return FileMode.TREE; + } else if (attributes.isExecutable()) { + return FileMode.EXECUTABLE_FILE; + } else { + return FileMode.REGULAR_FILE; + } + } + } + + /** + * 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 + */ + public static class NoGitlinksStrategy implements FileModeStrategy { + + /** + * a singleton instance of the default FileModeStrategy + */ + public static final 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 */ - static public class FileEntry extends Entry { + public static class FileEntry extends Entry { private final FileMode mode; private FS.Attributes attributes; @@ -164,20 +310,50 @@ public class FileTreeIterator extends WorkingTreeIterator { * file system */ public FileEntry(File f, FS fs) { + this(f, fs, DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new file entry given the specified FileModeStrategy + * + * @param f + * file + * @param fs + * file system + * @param fileModeStrategy + * the strategy to use when determining the FileMode of a + * file; controls gitlinks etc. + * + * @since 4.3 + */ + public FileEntry(File f, FS fs, FileModeStrategy fileModeStrategy) { this.fs = fs; f = fs.normalize(f); attributes = fs.getAttributes(f); - if (attributes.isSymbolicLink()) - mode = FileMode.SYMLINK; - else if (attributes.isDirectory()) { - if (new File(f, Constants.DOT_GIT).exists()) - mode = FileMode.GITLINK; - else - mode = FileMode.TREE; - } else if (attributes.isExecutable()) - mode = FileMode.EXECUTABLE_FILE; - else - mode = FileMode.REGULAR_FILE; + mode = fileModeStrategy.getMode(f, attributes); + } + + /** + * Create a new file entry given the specified FileModeStrategy + * + * @param f + * file + * @param fs + * file system + * @param attributes + * of the file + * @param fileModeStrategy + * the strategy to use when determining the FileMode of a + * file; controls gitlinks etc. + * + * @since 5.0 + */ + public FileEntry(File f, FS fs, FS.Attributes attributes, + FileModeStrategy fileModeStrategy) { + this.fs = fs; + this.attributes = attributes; + f = fs.normalize(f); + mode = fileModeStrategy.getMode(f, attributes); } @Override @@ -195,19 +371,21 @@ public class FileTreeIterator extends WorkingTreeIterator { return attributes.getLength(); } + /** + * @since 5.1.9 + */ @Override - public long getLastModified() { - return attributes.getLastModifiedTime(); + public Instant getLastModifiedInstant() { + return attributes.getLastModifiedInstant(); } @Override public InputStream openInputStream() throws IOException { - if (fs.isSymLink(getFile())) + if (attributes.isSymbolicLink()) { return new ByteArrayInputStream(fs.readSymLink(getFile()) - .getBytes( - Constants.CHARACTER_ENCODING)); - else - return new FileInputStream(getFile()); + .getBytes(UTF_8)); + } + return new FileInputStream(getFile()); } /** @@ -221,6 +399,8 @@ public class FileTreeIterator extends WorkingTreeIterator { } /** + * <p>Getter for the field <code>directory</code>.</p> + * * @return The root directory of this iterator */ public File getDirectory() { @@ -228,6 +408,8 @@ public class FileTreeIterator extends WorkingTreeIterator { } /** + * Get the location of the working file. + * * @return The location of the working file. This is the same as {@code new * File(getDirectory(), getEntryPath())} but may be faster by * reusing an internal File instance. @@ -237,9 +419,12 @@ public class FileTreeIterator extends WorkingTreeIterator { } @Override - protected byte[] idSubmodule(final Entry e) { - if (repository == null) - return idSubmodule(getDirectory(), e); - return super.idSubmodule(e); + protected byte[] idSubmodule(Entry e) { + return idSubmodule(getDirectory(), e); + } + + @Override + protected String readSymlinkTarget(Entry entry) throws IOException { + return fs.readSymLink(getEntryFile()); } } |