View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3    * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
4    * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
5    * Copyright (C) 2012-2013, Robin Rosenberg
6    * and other copyright owners as documented in the project's IP log.
7    *
8    * This program and the accompanying materials are made available
9    * under the terms of the Eclipse Distribution License v1.0 which
10   * accompanies this distribution, is reproduced below, and is
11   * available at http://www.eclipse.org/org/documents/edl-v10.php
12   *
13   * All rights reserved.
14   *
15   * Redistribution and use in source and binary forms, with or
16   * without modification, are permitted provided that the following
17   * conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright
20   *   notice, this list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above
23   *   copyright notice, this list of conditions and the following
24   *   disclaimer in the documentation and/or other materials provided
25   *   with the distribution.
26   *
27   * - Neither the name of the Eclipse Foundation, Inc. nor the
28   *   names of its contributors may be used to endorse or promote
29   *   products derived from this software without specific prior
30   *   written permission.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package org.eclipse.jgit.treewalk;
48  
49  import static java.nio.charset.StandardCharsets.UTF_8;
50  
51  import java.io.ByteArrayInputStream;
52  import java.io.File;
53  import java.io.FileInputStream;
54  import java.io.FileNotFoundException;
55  import java.io.IOException;
56  import java.io.InputStream;
57  import java.nio.ByteBuffer;
58  import java.nio.CharBuffer;
59  import java.nio.charset.CharacterCodingException;
60  import java.nio.charset.CharsetEncoder;
61  import java.text.MessageFormat;
62  import java.util.Arrays;
63  import java.util.Collections;
64  import java.util.Comparator;
65  import java.util.HashMap;
66  import java.util.Map;
67  
68  import org.eclipse.jgit.api.errors.FilterFailedException;
69  import org.eclipse.jgit.attributes.AttributesNode;
70  import org.eclipse.jgit.attributes.AttributesRule;
71  import org.eclipse.jgit.attributes.FilterCommand;
72  import org.eclipse.jgit.attributes.FilterCommandRegistry;
73  import org.eclipse.jgit.diff.RawText;
74  import org.eclipse.jgit.dircache.DirCacheEntry;
75  import org.eclipse.jgit.dircache.DirCacheIterator;
76  import org.eclipse.jgit.errors.CorruptObjectException;
77  import org.eclipse.jgit.errors.MissingObjectException;
78  import org.eclipse.jgit.errors.NoWorkTreeException;
79  import org.eclipse.jgit.ignore.FastIgnoreRule;
80  import org.eclipse.jgit.ignore.IgnoreNode;
81  import org.eclipse.jgit.internal.JGitText;
82  import org.eclipse.jgit.lib.Constants;
83  import org.eclipse.jgit.lib.CoreConfig;
84  import org.eclipse.jgit.lib.CoreConfig.CheckStat;
85  import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
86  import org.eclipse.jgit.lib.CoreConfig.SymLinks;
87  import org.eclipse.jgit.lib.FileMode;
88  import org.eclipse.jgit.lib.ObjectId;
89  import org.eclipse.jgit.lib.ObjectLoader;
90  import org.eclipse.jgit.lib.ObjectReader;
91  import org.eclipse.jgit.lib.Repository;
92  import org.eclipse.jgit.submodule.SubmoduleWalk;
93  import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
94  import org.eclipse.jgit.util.FS;
95  import org.eclipse.jgit.util.FS.ExecutionResult;
96  import org.eclipse.jgit.util.Holder;
97  import org.eclipse.jgit.util.IO;
98  import org.eclipse.jgit.util.Paths;
99  import org.eclipse.jgit.util.RawParseUtils;
100 import org.eclipse.jgit.util.TemporaryBuffer;
101 import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
102 import org.eclipse.jgit.util.io.AutoLFInputStream;
103 import org.eclipse.jgit.util.io.EolStreamTypeUtil;
104 import org.eclipse.jgit.util.sha1.SHA1;
105 
106 /**
107  * Walks a working directory tree as part of a
108  * {@link org.eclipse.jgit.treewalk.TreeWalk}.
109  * <p>
110  * Most applications will want to use the standard implementation of this
111  * iterator, {@link org.eclipse.jgit.treewalk.FileTreeIterator}, as that does
112  * all IO through the standard <code>java.io</code> package. Plugins for a Java
113  * based IDE may however wish to create their own implementations of this class
114  * to allow traversal of the IDE's project space, as well as benefit from any
115  * caching the IDE may have.
116  *
117  * @see FileTreeIterator
118  */
119 public abstract class WorkingTreeIterator extends AbstractTreeIterator {
120 	private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
121 
122 	/** An empty entry array, suitable for {@link #init(Entry[])}. */
123 	protected static final Entry[] EOF = {};
124 
125 	/** Size we perform file IO in if we have to read and hash a file. */
126 	static final int BUFFER_SIZE = 2048;
127 
128 	/**
129 	 * Maximum size of files which may be read fully into memory for performance
130 	 * reasons.
131 	 */
132 	private static final long MAXIMUM_FILE_SIZE_TO_READ_FULLY = 65536;
133 
134 	/** Inherited state of this iterator, describing working tree, etc. */
135 	private final IteratorState state;
136 
137 	/** The {@link #idBuffer()} for the current entry. */
138 	private byte[] contentId;
139 
140 	/** Index within {@link #entries} that {@link #contentId} came from. */
141 	private int contentIdFromPtr;
142 
143 	/** List of entries obtained from the subclass. */
144 	private Entry[] entries;
145 
146 	/** Total number of entries in {@link #entries} that are valid. */
147 	private int entryCnt;
148 
149 	/** Current position within {@link #entries}. */
150 	private int ptr;
151 
152 	/** If there is a .gitignore file present, the parsed rules from it. */
153 	private IgnoreNode ignoreNode;
154 
155 	/**
156 	 * cached clean filter command. Use a Ref in order to distinguish between
157 	 * the ref not cached yet and the value null
158 	 */
159 	private Holder<String> cleanFilterCommandHolder;
160 
161 	/**
162 	 * cached eol stream type. Use a Ref in order to distinguish between the ref
163 	 * not cached yet and the value null
164 	 */
165 	private Holder<EolStreamType> eolStreamTypeHolder;
166 
167 	/** Repository that is the root level being iterated over */
168 	protected Repository repository;
169 
170 	/** Cached canonical length, initialized from {@link #idBuffer()} */
171 	private long canonLen = -1;
172 
173 	/** The offset of the content id in {@link #idBuffer()} */
174 	private int contentIdOffset;
175 
176 	/**
177 	 * Create a new iterator with no parent.
178 	 *
179 	 * @param options
180 	 *            working tree options to be used
181 	 */
182 	protected WorkingTreeIterator(WorkingTreeOptions options) {
183 		super();
184 		state = new IteratorState(options);
185 	}
186 
187 	/**
188 	 * Create a new iterator with no parent and a prefix.
189 	 * <p>
190 	 * The prefix path supplied is inserted in front of all paths generated by
191 	 * this iterator. It is intended to be used when an iterator is being
192 	 * created for a subsection of an overall repository and needs to be
193 	 * combined with other iterators that are created to run over the entire
194 	 * repository namespace.
195 	 *
196 	 * @param prefix
197 	 *            position of this iterator in the repository tree. The value
198 	 *            may be null or the empty string to indicate the prefix is the
199 	 *            root of the repository. A trailing slash ('/') is
200 	 *            automatically appended if the prefix does not end in '/'.
201 	 * @param options
202 	 *            working tree options to be used
203 	 */
204 	protected WorkingTreeIterator(final String prefix,
205 			WorkingTreeOptions options) {
206 		super(prefix);
207 		state = new IteratorState(options);
208 	}
209 
210 	/**
211 	 * Create an iterator for a subtree of an existing iterator.
212 	 *
213 	 * @param p
214 	 *            parent tree iterator.
215 	 */
216 	protected WorkingTreeIterator(WorkingTreeIterator p) {
217 		super(p);
218 		state = p.state;
219 		repository = p.repository;
220 	}
221 
222 	/**
223 	 * Initialize this iterator for the root level of a repository.
224 	 * <p>
225 	 * This method should only be invoked after calling {@link #init(Entry[])},
226 	 * and only for the root iterator.
227 	 *
228 	 * @param repo
229 	 *            the repository.
230 	 */
231 	protected void initRootIterator(Repository repo) {
232 		repository = repo;
233 		Entry entry;
234 		if (ignoreNode instanceof PerDirectoryIgnoreNode)
235 			entry = ((PerDirectoryIgnoreNode) ignoreNode).entry;
236 		else
237 			entry = null;
238 		ignoreNode = new RootIgnoreNode(entry, repo);
239 	}
240 
241 	/**
242 	 * Define the matching {@link org.eclipse.jgit.dircache.DirCacheIterator},
243 	 * to optimize ObjectIds.
244 	 *
245 	 * Once the DirCacheIterator has been set this iterator must only be
246 	 * advanced by the TreeWalk that is supplied, as it assumes that itself and
247 	 * the corresponding DirCacheIterator are positioned on the same file path
248 	 * whenever {@link #idBuffer()} is invoked.
249 	 *
250 	 * @param walk
251 	 *            the walk that will be advancing this iterator.
252 	 * @param treeId
253 	 *            index of the matching
254 	 *            {@link org.eclipse.jgit.dircache.DirCacheIterator}.
255 	 */
256 	public void setDirCacheIterator(TreeWalk walk, int treeId) {
257 		state.walk = walk;
258 		state.dirCacheTree = treeId;
259 	}
260 
261 	/**
262 	 * Retrieves the {@link DirCacheIterator} at the current entry if
263 	 * {@link #setDirCacheIterator(TreeWalk, int)} was called.
264 	 *
265 	 * @return the DirCacheIterator, or {@code null} if not set or not at the
266 	 *         current entry
267 	 * @since 5.0
268 	 */
269 	protected DirCacheIterator getDirCacheIterator() {
270 		if (state.dirCacheTree >= 0 && state.walk != null) {
271 			return state.walk.getTree(state.dirCacheTree,
272 					DirCacheIterator.class);
273 		}
274 		return null;
275 	}
276 
277 	/**
278 	 * Defines whether this {@link WorkingTreeIterator} walks ignored
279 	 * directories.
280 	 *
281 	 * @param includeIgnored
282 	 *            {@code false} to skip ignored directories, if possible;
283 	 *            {@code true} to always include them in the walk
284 	 * @since 5.0
285 	 */
286 	public void setWalkIgnoredDirectories(boolean includeIgnored) {
287 		state.walkIgnored = includeIgnored;
288 	}
289 
290 	/**
291 	 * Tells whether this {@link WorkingTreeIterator} walks ignored directories.
292 	 *
293 	 * @return {@code true} if it does, {@code false} otherwise
294 	 * @since 5.0
295 	 */
296 	public boolean walksIgnoredDirectories() {
297 		return state.walkIgnored;
298 	}
299 
300 	/** {@inheritDoc} */
301 	@Override
302 	public boolean hasId() {
303 		if (contentIdFromPtr == ptr)
304 			return true;
305 		return (mode & FileMode.TYPE_MASK) == FileMode.TYPE_FILE;
306 	}
307 
308 	/** {@inheritDoc} */
309 	@Override
310 	public byte[] idBuffer() {
311 		if (contentIdFromPtr == ptr)
312 			return contentId;
313 
314 		if (state.walk != null) {
315 			// If there is a matching DirCacheIterator, we can reuse
316 			// its idBuffer, but only if we appear to be clean against
317 			// the cached index information for the path.
318 			DirCacheIterator i = state.walk.getTree(state.dirCacheTree,
319 							DirCacheIterator.class);
320 			if (i != null) {
321 				DirCacheEntry ent = i.getDirCacheEntry();
322 				if (ent != null && compareMetadata(ent) == MetadataDiff.EQUAL
323 						&& ((ent.getFileMode().getBits()
324 								& FileMode.TYPE_MASK) != FileMode.TYPE_GITLINK)) {
325 					contentIdOffset = i.idOffset();
326 					contentIdFromPtr = ptr;
327 					return contentId = i.idBuffer();
328 				}
329 				contentIdOffset = 0;
330 			} else {
331 				contentIdOffset = 0;
332 			}
333 		}
334 		switch (mode & FileMode.TYPE_MASK) {
335 		case FileMode.TYPE_SYMLINK:
336 		case FileMode.TYPE_FILE:
337 			contentIdFromPtr = ptr;
338 			return contentId = idBufferBlob(entries[ptr]);
339 		case FileMode.TYPE_GITLINK:
340 			contentIdFromPtr = ptr;
341 			return contentId = idSubmodule(entries[ptr]);
342 		}
343 		return zeroid;
344 	}
345 
346 	/** {@inheritDoc} */
347 	@Override
348 	public boolean isWorkTree() {
349 		return true;
350 	}
351 
352 	/**
353 	 * Get submodule id for given entry.
354 	 *
355 	 * @param e
356 	 *            a {@link org.eclipse.jgit.treewalk.WorkingTreeIterator.Entry}
357 	 *            object.
358 	 * @return non-null submodule id
359 	 */
360 	protected byte[] idSubmodule(Entry e) {
361 		if (repository == null)
362 			return zeroid;
363 		File directory;
364 		try {
365 			directory = repository.getWorkTree();
366 		} catch (NoWorkTreeException nwte) {
367 			return zeroid;
368 		}
369 		return idSubmodule(directory, e);
370 	}
371 
372 	/**
373 	 * Get submodule id using the repository at the location of the entry
374 	 * relative to the directory.
375 	 *
376 	 * @param directory
377 	 *            a {@link java.io.File} object.
378 	 * @param e
379 	 *            a {@link org.eclipse.jgit.treewalk.WorkingTreeIterator.Entry}
380 	 *            object.
381 	 * @return non-null submodule id
382 	 */
383 	protected byte[] idSubmodule(File directory, Entry e) {
384 		try (Repository submoduleRepo = SubmoduleWalk.getSubmoduleRepository(
385 				directory, e.getName(),
386 				repository != null ? repository.getFS() : FS.DETECTED)) {
387 			if (submoduleRepo == null) {
388 				return zeroid;
389 			}
390 			ObjectId head = submoduleRepo.resolve(Constants.HEAD);
391 			if (head == null) {
392 				return zeroid;
393 			}
394 			byte[] id = new byte[Constants.OBJECT_ID_LENGTH];
395 			head.copyRawTo(id, 0);
396 			return id;
397 		} catch (IOException exception) {
398 			return zeroid;
399 		}
400 	}
401 
402 	private static final byte[] digits = { '0', '1', '2', '3', '4', '5', '6',
403 			'7', '8', '9' };
404 
405 	private static final byte[] hblob = Constants
406 			.encodedTypeString(Constants.OBJ_BLOB);
407 
408 	private byte[] idBufferBlob(Entry e) {
409 		try {
410 			final InputStream is = e.openInputStream();
411 			if (is == null)
412 				return zeroid;
413 			try {
414 				state.initializeReadBuffer();
415 
416 				final long len = e.getLength();
417 				InputStream filteredIs = possiblyFilteredInputStream(e, is, len,
418 						OperationType.CHECKIN_OP);
419 				return computeHash(filteredIs, canonLen);
420 			} finally {
421 				safeClose(is);
422 			}
423 		} catch (IOException err) {
424 			// Can't read the file? Don't report the failure either.
425 			return zeroid;
426 		}
427 	}
428 
429 	private InputStream possiblyFilteredInputStream(final Entry e,
430 			final InputStream is, final long len) throws IOException {
431 		return possiblyFilteredInputStream(e, is, len, null);
432 
433 	}
434 
435 	private InputStream possiblyFilteredInputStream(final Entry e,
436 			final InputStream is, final long len, OperationType opType)
437 			throws IOException {
438 		if (getCleanFilterCommand() == null
439 				&& getEolStreamType(opType) == EolStreamType.DIRECT) {
440 			canonLen = len;
441 			return is;
442 		}
443 
444 		if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) {
445 			ByteBuffer rawbuf = IO.readWholeStream(is, (int) len);
446 			rawbuf = filterClean(rawbuf.array(), rawbuf.limit(), opType);
447 			canonLen = rawbuf.limit();
448 			return new ByteArrayInputStream(rawbuf.array(), 0, (int) canonLen);
449 		}
450 
451 		if (getCleanFilterCommand() == null && isBinary(e)) {
452 				canonLen = len;
453 				return is;
454 			}
455 
456 		final InputStream lenIs = filterClean(e.openInputStream(),
457 				opType);
458 		try {
459 			canonLen = computeLength(lenIs);
460 		} finally {
461 			safeClose(lenIs);
462 		}
463 		return filterClean(is, opType);
464 	}
465 
466 	private static void safeClose(InputStream in) {
467 		try {
468 			in.close();
469 		} catch (IOException err2) {
470 			// Suppress any error related to closing an input
471 			// stream. We don't care, we should not have any
472 			// outstanding data to flush or anything like that.
473 		}
474 	}
475 
476 	private static boolean isBinary(Entry entry) throws IOException {
477 		InputStream in = entry.openInputStream();
478 		try {
479 			return RawText.isBinary(in);
480 		} finally {
481 			safeClose(in);
482 		}
483 	}
484 
485 	private ByteBuffer filterClean(byte[] src, int n, OperationType opType)
486 			throws IOException {
487 		InputStream in = new ByteArrayInputStream(src);
488 		try {
489 			return IO.readWholeStream(filterClean(in, opType), n);
490 		} finally {
491 			safeClose(in);
492 		}
493 	}
494 
495 	private InputStream filterClean(InputStream in) throws IOException {
496 		return filterClean(in, null);
497 	}
498 
499 	private InputStream filterClean(InputStream in, OperationType opType)
500 			throws IOException {
501 		in = handleAutoCRLF(in, opType);
502 		String filterCommand = getCleanFilterCommand();
503 		if (filterCommand != null) {
504 			if (FilterCommandRegistry.isRegistered(filterCommand)) {
505 				LocalFile buffer = new TemporaryBuffer.LocalFile(null);
506 				FilterCommand command = FilterCommandRegistry
507 						.createFilterCommand(filterCommand, repository, in,
508 								buffer);
509 				while (command.run() != -1) {
510 					// loop as long as command.run() tells there is work to do
511 				}
512 				return buffer.openInputStreamWithAutoDestroy();
513 			}
514 			FS fs = repository.getFS();
515 			ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
516 					new String[0]);
517 			filterProcessBuilder.directory(repository.getWorkTree());
518 			filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
519 					repository.getDirectory().getAbsolutePath());
520 			ExecutionResult result;
521 			try {
522 				result = fs.execute(filterProcessBuilder, in);
523 			} catch (IOException | InterruptedException e) {
524 				throw new IOException(new FilterFailedException(e,
525 						filterCommand, getEntryPathString()));
526 			}
527 			int rc = result.getRc();
528 			if (rc != 0) {
529 				throw new IOException(new FilterFailedException(rc,
530 						filterCommand, getEntryPathString(),
531 						result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
532 						RawParseUtils.decode(result.getStderr()
533 								.toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
534 			}
535 			return result.getStdout().openInputStreamWithAutoDestroy();
536 		}
537 		return in;
538 	}
539 
540 	private InputStream handleAutoCRLF(InputStream in, OperationType opType)
541 			throws IOException {
542 		return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType(opType));
543 	}
544 
545 	/**
546 	 * Returns the working tree options used by this iterator.
547 	 *
548 	 * @return working tree options
549 	 */
550 	public WorkingTreeOptions getOptions() {
551 		return state.options;
552 	}
553 
554 	/** {@inheritDoc} */
555 	@Override
556 	public int idOffset() {
557 		return contentIdOffset;
558 	}
559 
560 	/** {@inheritDoc} */
561 	@Override
562 	public void reset() {
563 		if (!first()) {
564 			ptr = 0;
565 			if (!eof())
566 				parseEntry();
567 		}
568 	}
569 
570 	/** {@inheritDoc} */
571 	@Override
572 	public boolean first() {
573 		return ptr == 0;
574 	}
575 
576 	/** {@inheritDoc} */
577 	@Override
578 	public boolean eof() {
579 		return ptr == entryCnt;
580 	}
581 
582 	/** {@inheritDoc} */
583 	@Override
584 	public void next(int delta) throws CorruptObjectException {
585 		ptr += delta;
586 		if (!eof()) {
587 			parseEntry();
588 		}
589 	}
590 
591 	/** {@inheritDoc} */
592 	@Override
593 	public void back(int delta) throws CorruptObjectException {
594 		ptr -= delta;
595 		parseEntry();
596 	}
597 
598 	private void parseEntry() {
599 		final Entry e = entries[ptr];
600 		mode = e.getMode().getBits();
601 
602 		final int nameLen = e.encodedNameLen;
603 		ensurePathCapacity(pathOffset + nameLen, pathOffset);
604 		System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
605 		pathLen = pathOffset + nameLen;
606 		canonLen = -1;
607 		cleanFilterCommandHolder = null;
608 		eolStreamTypeHolder = null;
609 	}
610 
611 	/**
612 	 * Get the raw byte length of this entry.
613 	 *
614 	 * @return size of this file, in bytes.
615 	 */
616 	public long getEntryLength() {
617 		return current().getLength();
618 	}
619 
620 	/**
621 	 * Get the filtered input length of this entry
622 	 *
623 	 * @return size of the content, in bytes
624 	 * @throws java.io.IOException
625 	 */
626 	public long getEntryContentLength() throws IOException {
627 		if (canonLen == -1) {
628 			long rawLen = getEntryLength();
629 			if (rawLen == 0)
630 				canonLen = 0;
631 			InputStream is = current().openInputStream();
632 			try {
633 				// canonLen gets updated here
634 				possiblyFilteredInputStream(current(), is, current()
635 						.getLength());
636 			} finally {
637 				safeClose(is);
638 			}
639 		}
640 		return canonLen;
641 	}
642 
643 	/**
644 	 * Get the last modified time of this entry.
645 	 *
646 	 * @return last modified time of this file, in milliseconds since the epoch
647 	 *         (Jan 1, 1970 UTC).
648 	 */
649 	public long getEntryLastModified() {
650 		return current().getLastModified();
651 	}
652 
653 	/**
654 	 * Obtain an input stream to read the file content.
655 	 * <p>
656 	 * Efficient implementations are not required. The caller will usually
657 	 * obtain the stream only once per entry, if at all.
658 	 * <p>
659 	 * The input stream should not use buffering if the implementation can avoid
660 	 * it. The caller will buffer as necessary to perform efficient block IO
661 	 * operations.
662 	 * <p>
663 	 * The caller will close the stream once complete.
664 	 *
665 	 * @return a stream to read from the file.
666 	 * @throws java.io.IOException
667 	 *             the file could not be opened for reading.
668 	 */
669 	public InputStream openEntryStream() throws IOException {
670 		InputStream rawis = current().openInputStream();
671 		if (getCleanFilterCommand() == null
672 				&& getEolStreamType() == EolStreamType.DIRECT)
673 			return rawis;
674 		else
675 			return filterClean(rawis);
676 	}
677 
678 	/**
679 	 * Determine if the current entry path is ignored by an ignore rule.
680 	 *
681 	 * @return true if the entry was ignored by an ignore rule file.
682 	 * @throws java.io.IOException
683 	 *             a relevant ignore rule file exists but cannot be read.
684 	 */
685 	public boolean isEntryIgnored() throws IOException {
686 		return isEntryIgnored(pathLen);
687 	}
688 
689 	/**
690 	 * Determine if the entry path is ignored by an ignore rule.
691 	 *
692 	 * @param pLen
693 	 *            the length of the path in the path buffer.
694 	 * @return true if the entry is ignored by an ignore rule.
695 	 * @throws java.io.IOException
696 	 *             a relevant ignore rule file exists but cannot be read.
697 	 */
698 	protected boolean isEntryIgnored(int pLen) throws IOException {
699 		return isEntryIgnored(pLen, mode);
700 	}
701 
702 	/**
703 	 * Determine if the entry path is ignored by an ignore rule.
704 	 *
705 	 * @param pLen
706 	 *            the length of the path in the path buffer.
707 	 * @param fileMode
708 	 *            the original iterator file mode
709 	 * @return true if the entry is ignored by an ignore rule.
710 	 * @throws IOException
711 	 *             a relevant ignore rule file exists but cannot be read.
712 	 */
713 	private boolean isEntryIgnored(int pLen, int fileMode)
714 			throws IOException {
715 		// The ignore code wants path to start with a '/' if possible.
716 		// If we have the '/' in our path buffer because we are inside
717 		// a sub-directory include it in the range we convert to string.
718 		//
719 		final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset;
720 		String pathRel = TreeWalk.pathOf(this.path, pOff, pLen);
721 		String parentRel = getParentPath(pathRel);
722 
723 		// CGit is processing .gitignore files by starting at the root of the
724 		// repository and then recursing into subdirectories. With this
725 		// approach, top-level ignored directories will be processed first which
726 		// allows to skip entire subtrees and further .gitignore-file processing
727 		// within these subtrees.
728 		//
729 		// We will follow the same approach by marking directories as "ignored"
730 		// here. This allows to have a simplified FastIgnore.checkIgnore()
731 		// implementation (both in terms of code and computational complexity):
732 		//
733 		// Without the "ignored" flag, we would have to apply the ignore-check
734 		// to a path and all of its parents always(!), to determine whether a
735 		// path is ignored directly or by one of its parent directories; with
736 		// the "ignored" flag, we know at this point that the parent directory
737 		// is definitely not ignored, thus the path can only become ignored if
738 		// there is a rule matching the path itself.
739 		if (isDirectoryIgnored(parentRel)) {
740 			return true;
741 		}
742 
743 		IgnoreNode rules = getIgnoreNode();
744 		final Boolean ignored = rules != null
745 				? rules.checkIgnored(pathRel, FileMode.TREE.equals(fileMode))
746 				: null;
747 		if (ignored != null) {
748 			return ignored.booleanValue();
749 		}
750 		return parent instanceof WorkingTreeIterator
751 				&& ((WorkingTreeIterator) parent).isEntryIgnored(pLen,
752 						fileMode);
753 	}
754 
755 	private IgnoreNode getIgnoreNode() throws IOException {
756 		if (ignoreNode instanceof PerDirectoryIgnoreNode)
757 			ignoreNode = ((PerDirectoryIgnoreNode) ignoreNode).load();
758 		return ignoreNode;
759 	}
760 
761 	/**
762 	 * Retrieves the {@link org.eclipse.jgit.attributes.AttributesNode} for the
763 	 * current entry.
764 	 *
765 	 * @return the {@link org.eclipse.jgit.attributes.AttributesNode} for the
766 	 *         current entry.
767 	 * @throws IOException
768 	 */
769 	public AttributesNode getEntryAttributesNode() throws IOException {
770 		if (attributesNode instanceof PerDirectoryAttributesNode)
771 			attributesNode = ((PerDirectoryAttributesNode) attributesNode)
772 					.load();
773 		return attributesNode;
774 	}
775 
776 	private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
777 		@Override
778 		public int compare(Entry a, Entry b) {
779 			return Paths.compare(
780 					a.encodedName, 0, a.encodedNameLen, a.getMode().getBits(),
781 					b.encodedName, 0, b.encodedNameLen, b.getMode().getBits());
782 		}
783 	};
784 
785 	/**
786 	 * Constructor helper.
787 	 *
788 	 * @param list
789 	 *            files in the subtree of the work tree this iterator operates
790 	 *            on
791 	 */
792 	protected void init(Entry[] list) {
793 		// Filter out nulls, . and .. as these are not valid tree entries,
794 		// also cache the encoded forms of the path names for efficient use
795 		// later on during sorting and iteration.
796 		//
797 		entries = list;
798 		int i, o;
799 
800 		final CharsetEncoder nameEncoder = state.nameEncoder;
801 		for (i = 0, o = 0; i < entries.length; i++) {
802 			final Entry e = entries[i];
803 			if (e == null)
804 				continue;
805 			final String name = e.getName();
806 			if (".".equals(name) || "..".equals(name)) //$NON-NLS-1$ //$NON-NLS-2$
807 				continue;
808 			if (Constants.DOT_GIT.equals(name))
809 				continue;
810 			if (Constants.DOT_GIT_IGNORE.equals(name))
811 				ignoreNode = new PerDirectoryIgnoreNode(e);
812 			if (Constants.DOT_GIT_ATTRIBUTES.equals(name))
813 				attributesNode = new PerDirectoryAttributesNode(e);
814 			if (i != o)
815 				entries[o] = e;
816 			e.encodeName(nameEncoder);
817 			o++;
818 		}
819 		entryCnt = o;
820 		Arrays.sort(entries, 0, entryCnt, ENTRY_CMP);
821 
822 		contentIdFromPtr = -1;
823 		ptr = 0;
824 		if (!eof())
825 			parseEntry();
826 		else if (pathLen == 0) // see bug 445363
827 			pathLen = pathOffset;
828 	}
829 
830 	/**
831 	 * Obtain the current entry from this iterator.
832 	 *
833 	 * @return the currently selected entry.
834 	 */
835 	protected Entry current() {
836 		return entries[ptr];
837 	}
838 
839 	/**
840 	 * The result of a metadata-comparison between the current entry and a
841 	 * {@link DirCacheEntry}
842 	 */
843 	public enum MetadataDiff {
844 		/**
845 		 * The entries are equal by metaData (mode, length,
846 		 * modification-timestamp) or the <code>assumeValid</code> attribute of
847 		 * the index entry is set
848 		 */
849 		EQUAL,
850 
851 		/**
852 		 * The entries are not equal by metaData (mode, length) or the
853 		 * <code>isUpdateNeeded</code> attribute of the index entry is set
854 		 */
855 		DIFFER_BY_METADATA,
856 
857 		/** index entry is smudged - can't use that entry for comparison */
858 		SMUDGED,
859 
860 		/**
861 		 * The entries are equal by metaData (mode, length) but differ by
862 		 * modification-timestamp.
863 		 */
864 		DIFFER_BY_TIMESTAMP
865 	}
866 
867 	/**
868 	 * Is the file mode of the current entry different than the given raw mode?
869 	 *
870 	 * @param rawMode
871 	 *            an int.
872 	 * @return true if different, false otherwise
873 	 */
874 	public boolean isModeDifferent(int rawMode) {
875 		// Determine difference in mode-bits of file and index-entry. In the
876 		// bitwise presentation of modeDiff we'll have a '1' when the two modes
877 		// differ at this position.
878 		int modeDiff = getEntryRawMode() ^ rawMode;
879 
880 		if (modeDiff == 0)
881 			return false;
882 
883 		// Do not rely on filemode differences in case of symbolic links
884 		if (getOptions().getSymLinks() == SymLinks.FALSE)
885 			if (FileMode.SYMLINK.equals(rawMode))
886 				return false;
887 
888 		// Ignore the executable file bits if WorkingTreeOptions tell me to
889 		// do so. Ignoring is done by setting the bits representing a
890 		// EXECUTABLE_FILE to '0' in modeDiff
891 		if (!state.options.isFileMode())
892 			modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits();
893 		return modeDiff != 0;
894 	}
895 
896 	/**
897 	 * Compare the metadata (mode, length, modification-timestamp) of the
898 	 * current entry and a {@link org.eclipse.jgit.dircache.DirCacheEntry}
899 	 *
900 	 * @param entry
901 	 *            the {@link org.eclipse.jgit.dircache.DirCacheEntry} to compare
902 	 *            with
903 	 * @return a
904 	 *         {@link org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff}
905 	 *         which tells whether and how the entries metadata differ
906 	 */
907 	public MetadataDiff compareMetadata(DirCacheEntry entry) {
908 		if (entry.isAssumeValid())
909 			return MetadataDiff.EQUAL;
910 
911 		if (entry.isUpdateNeeded())
912 			return MetadataDiff.DIFFER_BY_METADATA;
913 
914 		if (isModeDifferent(entry.getRawMode()))
915 			return MetadataDiff.DIFFER_BY_METADATA;
916 
917 		// Don't check for length or lastmodified on folders
918 		int type = mode & FileMode.TYPE_MASK;
919 		if (type == FileMode.TYPE_TREE || type == FileMode.TYPE_GITLINK)
920 			return MetadataDiff.EQUAL;
921 
922 		if (!entry.isSmudged() && entry.getLength() != (int) getEntryLength())
923 			return MetadataDiff.DIFFER_BY_METADATA;
924 
925 		// Git under windows only stores seconds so we round the timestamp
926 		// Java gives us if it looks like the timestamp in index is seconds
927 		// only. Otherwise we compare the timestamp at millisecond precision,
928 		// unless core.checkstat is set to "minimal", in which case we only
929 		// compare the whole second part.
930 		long cacheLastModified = entry.getLastModified();
931 		long fileLastModified = getEntryLastModified();
932 		long lastModifiedMillis = fileLastModified % 1000;
933 		long cacheMillis = cacheLastModified % 1000;
934 		if (getOptions().getCheckStat() == CheckStat.MINIMAL) {
935 			fileLastModified = fileLastModified - lastModifiedMillis;
936 			cacheLastModified = cacheLastModified - cacheMillis;
937 		} else if (cacheMillis == 0)
938 			fileLastModified = fileLastModified - lastModifiedMillis;
939 		// Some Java version on Linux return whole seconds only even when
940 		// the file systems supports more precision.
941 		else if (lastModifiedMillis == 0)
942 			cacheLastModified = cacheLastModified - cacheMillis;
943 
944 		if (fileLastModified != cacheLastModified)
945 			return MetadataDiff.DIFFER_BY_TIMESTAMP;
946 		else if (!entry.isSmudged())
947 			// The file is clean when you look at timestamps.
948 			return MetadataDiff.EQUAL;
949 		else
950 			return MetadataDiff.SMUDGED;
951 	}
952 
953 	/**
954 	 * Checks whether this entry differs from a given entry from the
955 	 * {@link org.eclipse.jgit.dircache.DirCache}.
956 	 *
957 	 * File status information is used and if status is same we consider the
958 	 * file identical to the state in the working directory. Native git uses
959 	 * more stat fields than we have accessible in Java.
960 	 *
961 	 * @param entry
962 	 *            the entry from the dircache we want to compare against
963 	 * @param forceContentCheck
964 	 *            True if the actual file content should be checked if
965 	 *            modification time differs.
966 	 * @param reader
967 	 *            access to repository objects if necessary. Should not be null.
968 	 * @return true if content is most likely different.
969 	 * @throws java.io.IOException
970 	 * @since 3.3
971 	 */
972 	public boolean isModified(DirCacheEntry entry, boolean forceContentCheck,
973 			ObjectReader reader) throws IOException {
974 		if (entry == null)
975 			return !FileMode.MISSING.equals(getEntryFileMode());
976 		MetadataDiff diff = compareMetadata(entry);
977 		switch (diff) {
978 		case DIFFER_BY_TIMESTAMP:
979 			if (forceContentCheck)
980 				// But we are told to look at content even though timestamps
981 				// tell us about modification
982 				return contentCheck(entry, reader);
983 			else
984 				// We are told to assume a modification if timestamps differs
985 				return true;
986 		case SMUDGED:
987 			// The file is clean by timestamps but the entry was smudged.
988 			// Lets do a content check
989 			return contentCheck(entry, reader);
990 		case EQUAL:
991 			if (mode == FileMode.SYMLINK.getBits()) {
992 				return contentCheck(entry, reader);
993 			}
994 			return false;
995 		case DIFFER_BY_METADATA:
996 			if (mode == FileMode.TREE.getBits()
997 					&& entry.getFileMode().equals(FileMode.GITLINK)) {
998 				byte[] idBuffer = idBuffer();
999 				int idOffset = idOffset();
1000 				if (entry.getObjectId().compareTo(idBuffer, idOffset) == 0) {
1001 					return true;
1002 				} else if (ObjectId.zeroId().compareTo(idBuffer,
1003 						idOffset) == 0) {
1004 					return new File(repository.getWorkTree(),
1005 							entry.getPathString()).list().length > 0;
1006 				}
1007 				return false;
1008 			} else if (mode == FileMode.SYMLINK.getBits())
1009 				return contentCheck(entry, reader);
1010 			return true;
1011 		default:
1012 			throw new IllegalStateException(MessageFormat.format(
1013 					JGitText.get().unexpectedCompareResult, diff.name()));
1014 		}
1015 	}
1016 
1017 	/**
1018 	 * Get the file mode to use for the current entry when it is to be updated
1019 	 * in the index.
1020 	 *
1021 	 * @param indexIter
1022 	 *            {@link org.eclipse.jgit.dircache.DirCacheIterator} positioned
1023 	 *            at the same entry as this iterator or null if no
1024 	 *            {@link org.eclipse.jgit.dircache.DirCacheIterator} is
1025 	 *            available at this iterator's current entry
1026 	 * @return index file mode
1027 	 */
1028 	public FileMode getIndexFileMode(DirCacheIterator indexIter) {
1029 		final FileMode wtMode = getEntryFileMode();
1030 		if (indexIter == null) {
1031 			return wtMode;
1032 		}
1033 		final FileMode iMode = indexIter.getEntryFileMode();
1034 		if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) {
1035 			return wtMode;
1036 		}
1037 		if (!getOptions().isFileMode()) {
1038 			if (FileMode.REGULAR_FILE == wtMode
1039 					&& FileMode.EXECUTABLE_FILE == iMode) {
1040 				return iMode;
1041 			}
1042 			if (FileMode.EXECUTABLE_FILE == wtMode
1043 					&& FileMode.REGULAR_FILE == iMode) {
1044 				return iMode;
1045 			}
1046 		}
1047 		if (FileMode.GITLINK == iMode
1048 				&& FileMode.TREE == wtMode) {
1049 			return iMode;
1050 		}
1051 		if (FileMode.TREE == iMode
1052 				&& FileMode.GITLINK == wtMode) {
1053 			return iMode;
1054 		}
1055 		return wtMode;
1056 	}
1057 
1058 	/**
1059 	 * Compares the entries content with the content in the filesystem.
1060 	 * Unsmudges the entry when it is detected that it is clean.
1061 	 *
1062 	 * @param entry
1063 	 *            the entry to be checked
1064 	 * @param reader
1065 	 *            acccess to repository data if necessary
1066 	 * @return <code>true</code> if the content doesn't match,
1067 	 *         <code>false</code> if it matches
1068 	 * @throws IOException
1069 	 */
1070 	private boolean contentCheck(DirCacheEntry entry, ObjectReader reader)
1071 			throws IOException {
1072 		if (getEntryObjectId().equals(entry.getObjectId())) {
1073 			// Content has not changed
1074 
1075 			// We know the entry can't be racily clean because it's still clean.
1076 			// Therefore we unsmudge the entry!
1077 			// If by any chance we now unsmudge although we are still in the
1078 			// same time-slot as the last modification to the index file the
1079 			// next index write operation will smudge again.
1080 			// Caution: we are unsmudging just by setting the length of the
1081 			// in-memory entry object. It's the callers task to detect that we
1082 			// have modified the entry and to persist the modified index.
1083 			entry.setLength((int) getEntryLength());
1084 
1085 			return false;
1086 		} else {
1087 			if (mode == FileMode.SYMLINK.getBits()) {
1088 				return !new File(readSymlinkTarget(current())).equals(
1089 						new File(readContentAsNormalizedString(entry, reader)));
1090 			}
1091 			// Content differs: that's a real change, perhaps
1092 			if (reader == null) // deprecated use, do no further checks
1093 				return true;
1094 
1095 			switch (getEolStreamType()) {
1096 			case DIRECT:
1097 				return true;
1098 			default:
1099 				try {
1100 					ObjectLoader loader = reader.open(entry.getObjectId());
1101 					if (loader == null)
1102 						return true;
1103 
1104 					// We need to compute the length, but only if it is not
1105 					// a binary stream.
1106 					long dcInLen;
1107 					try (InputStream dcIn = new AutoLFInputStream(
1108 							loader.openStream(), true,
1109 							true /* abort if binary */)) {
1110 						dcInLen = computeLength(dcIn);
1111 					} catch (AutoLFInputStream.IsBinaryException e) {
1112 						return true;
1113 					}
1114 
1115 					try (InputStream dcIn = new AutoLFInputStream(
1116 							loader.openStream(), true)) {
1117 						byte[] autoCrLfHash = computeHash(dcIn, dcInLen);
1118 						boolean changed = getEntryObjectId()
1119 								.compareTo(autoCrLfHash, 0) != 0;
1120 						return changed;
1121 					}
1122 				} catch (IOException e) {
1123 					return true;
1124 				}
1125 			}
1126 		}
1127 	}
1128 
1129 	private static String readContentAsNormalizedString(DirCacheEntry entry,
1130 			ObjectReader reader) throws MissingObjectException, IOException {
1131 		ObjectLoader open = reader.open(entry.getObjectId());
1132 		byte[] cachedBytes = open.getCachedBytes();
1133 		return FS.detect().normalize(RawParseUtils.decode(cachedBytes));
1134 	}
1135 
1136 	/**
1137 	 * Reads the target of a symlink as a string. This default implementation
1138 	 * fully reads the entry's input stream and converts it to a normalized
1139 	 * string. Subclasses may override to provide more specialized
1140 	 * implementations.
1141 	 *
1142 	 * @param entry
1143 	 *            to read
1144 	 * @return the entry's content as a normalized string
1145 	 * @throws java.io.IOException
1146 	 *             if the entry cannot be read or does not denote a symlink
1147 	 * @since 4.6
1148 	 */
1149 	protected String readSymlinkTarget(Entry entry) throws IOException {
1150 		if (!entry.getMode().equals(FileMode.SYMLINK)) {
1151 			throw new java.nio.file.NotLinkException(entry.getName());
1152 		}
1153 		long length = entry.getLength();
1154 		byte[] content = new byte[(int) length];
1155 		try (InputStream is = entry.openInputStream()) {
1156 			int bytesRead = IO.readFully(is, content, 0);
1157 			return FS.detect()
1158 					.normalize(RawParseUtils.decode(content, 0, bytesRead));
1159 		}
1160 	}
1161 
1162 	private static long computeLength(InputStream in) throws IOException {
1163 		// Since we only care about the length, use skip. The stream
1164 		// may be able to more efficiently wade through its data.
1165 		//
1166 		long length = 0;
1167 		for (;;) {
1168 			long n = in.skip(1 << 20);
1169 			if (n <= 0)
1170 				break;
1171 			length += n;
1172 		}
1173 		return length;
1174 	}
1175 
1176 	private byte[] computeHash(InputStream in, long length) throws IOException {
1177 		SHA1 contentDigest = SHA1.newInstance();
1178 		final byte[] contentReadBuffer = state.contentReadBuffer;
1179 
1180 		contentDigest.update(hblob);
1181 		contentDigest.update((byte) ' ');
1182 
1183 		long sz = length;
1184 		if (sz == 0) {
1185 			contentDigest.update((byte) '0');
1186 		} else {
1187 			final int bufn = contentReadBuffer.length;
1188 			int p = bufn;
1189 			do {
1190 				contentReadBuffer[--p] = digits[(int) (sz % 10)];
1191 				sz /= 10;
1192 			} while (sz > 0);
1193 			contentDigest.update(contentReadBuffer, p, bufn - p);
1194 		}
1195 		contentDigest.update((byte) 0);
1196 
1197 		for (;;) {
1198 			final int r = in.read(contentReadBuffer);
1199 			if (r <= 0)
1200 				break;
1201 			contentDigest.update(contentReadBuffer, 0, r);
1202 			sz += r;
1203 		}
1204 		if (sz != length)
1205 			return zeroid;
1206 		return contentDigest.digest();
1207 	}
1208 
1209 	/**
1210 	 * A single entry within a working directory tree.
1211 	 *
1212 	 * @since 5.0
1213 	 */
1214 	public static abstract class Entry {
1215 		byte[] encodedName;
1216 
1217 		int encodedNameLen;
1218 
1219 		void encodeName(CharsetEncoder enc) {
1220 			final ByteBuffer b;
1221 			try {
1222 				b = enc.encode(CharBuffer.wrap(getName()));
1223 			} catch (CharacterCodingException e) {
1224 				// This should so never happen.
1225 				throw new RuntimeException(MessageFormat.format(
1226 						JGitText.get().unencodeableFile, getName()));
1227 			}
1228 
1229 			encodedNameLen = b.limit();
1230 			if (b.hasArray() && b.arrayOffset() == 0)
1231 				encodedName = b.array();
1232 			else
1233 				b.get(encodedName = new byte[encodedNameLen]);
1234 		}
1235 
1236 		@Override
1237 		public String toString() {
1238 			return getMode().toString() + " " + getName(); //$NON-NLS-1$
1239 		}
1240 
1241 		/**
1242 		 * Get the type of this entry.
1243 		 * <p>
1244 		 * <b>Note: Efficient implementation required.</b>
1245 		 * <p>
1246 		 * The implementation of this method must be efficient. If a subclass
1247 		 * needs to compute the value they should cache the reference within an
1248 		 * instance member instead.
1249 		 *
1250 		 * @return a file mode constant from {@link FileMode}.
1251 		 */
1252 		public abstract FileMode getMode();
1253 
1254 		/**
1255 		 * Get the byte length of this entry.
1256 		 * <p>
1257 		 * <b>Note: Efficient implementation required.</b>
1258 		 * <p>
1259 		 * The implementation of this method must be efficient. If a subclass
1260 		 * needs to compute the value they should cache the reference within an
1261 		 * instance member instead.
1262 		 *
1263 		 * @return size of this file, in bytes.
1264 		 */
1265 		public abstract long getLength();
1266 
1267 		/**
1268 		 * Get the last modified time of this entry.
1269 		 * <p>
1270 		 * <b>Note: Efficient implementation required.</b>
1271 		 * <p>
1272 		 * The implementation of this method must be efficient. If a subclass
1273 		 * needs to compute the value they should cache the reference within an
1274 		 * instance member instead.
1275 		 *
1276 		 * @return time since the epoch (in ms) of the last change.
1277 		 */
1278 		public abstract long getLastModified();
1279 
1280 		/**
1281 		 * Get the name of this entry within its directory.
1282 		 * <p>
1283 		 * Efficient implementations are not required. The caller will obtain
1284 		 * the name only once and cache it once obtained.
1285 		 *
1286 		 * @return name of the entry.
1287 		 */
1288 		public abstract String getName();
1289 
1290 		/**
1291 		 * Obtain an input stream to read the file content.
1292 		 * <p>
1293 		 * Efficient implementations are not required. The caller will usually
1294 		 * obtain the stream only once per entry, if at all.
1295 		 * <p>
1296 		 * The input stream should not use buffering if the implementation can
1297 		 * avoid it. The caller will buffer as necessary to perform efficient
1298 		 * block IO operations.
1299 		 * <p>
1300 		 * The caller will close the stream once complete.
1301 		 *
1302 		 * @return a stream to read from the file.
1303 		 * @throws IOException
1304 		 *             the file could not be opened for reading.
1305 		 */
1306 		public abstract InputStream openInputStream() throws IOException;
1307 	}
1308 
1309 	/** Magic type indicating we know rules exist, but they aren't loaded. */
1310 	private static class PerDirectoryIgnoreNode extends IgnoreNode {
1311 		final Entry entry;
1312 
1313 		PerDirectoryIgnoreNode(Entry entry) {
1314 			super(Collections.<FastIgnoreRule> emptyList());
1315 			this.entry = entry;
1316 		}
1317 
1318 		IgnoreNode load() throws IOException {
1319 			IgnoreNode r = new IgnoreNode();
1320 			try (InputStream in = entry.openInputStream()) {
1321 				r.parse(in);
1322 			}
1323 			return r.getRules().isEmpty() ? null : r;
1324 		}
1325 	}
1326 
1327 	/** Magic type indicating there may be rules for the top level. */
1328 	private static class RootIgnoreNode extends PerDirectoryIgnoreNode {
1329 		final Repository repository;
1330 
1331 		RootIgnoreNode(Entry entry, Repository repository) {
1332 			super(entry);
1333 			this.repository = repository;
1334 		}
1335 
1336 		@Override
1337 		IgnoreNode load() throws IOException {
1338 			IgnoreNode r;
1339 			if (entry != null) {
1340 				r = super.load();
1341 				if (r == null)
1342 					r = new IgnoreNode();
1343 			} else {
1344 				r = new IgnoreNode();
1345 			}
1346 
1347 			FS fs = repository.getFS();
1348 			String path = repository.getConfig().get(CoreConfig.KEY)
1349 					.getExcludesFile();
1350 			if (path != null) {
1351 				File excludesfile;
1352 				if (path.startsWith("~/")) //$NON-NLS-1$
1353 					excludesfile = fs.resolve(fs.userHome(), path.substring(2));
1354 				else
1355 					excludesfile = fs.resolve(null, path);
1356 				loadRulesFromFile(r, excludesfile);
1357 			}
1358 
1359 			File exclude = fs.resolve(repository.getDirectory(),
1360 					Constants.INFO_EXCLUDE);
1361 			loadRulesFromFile(r, exclude);
1362 
1363 			return r.getRules().isEmpty() ? null : r;
1364 		}
1365 
1366 		private static void loadRulesFromFile(IgnoreNode r, File exclude)
1367 				throws FileNotFoundException, IOException {
1368 			if (FS.DETECTED.exists(exclude)) {
1369 				try (FileInputStream in = new FileInputStream(exclude)) {
1370 					r.parse(in);
1371 				}
1372 			}
1373 		}
1374 	}
1375 
1376 	/** Magic type indicating we know rules exist, but they aren't loaded. */
1377 	private static class PerDirectoryAttributesNode extends AttributesNode {
1378 		final Entry entry;
1379 
1380 		PerDirectoryAttributesNode(Entry entry) {
1381 			super(Collections.<AttributesRule> emptyList());
1382 			this.entry = entry;
1383 		}
1384 
1385 		AttributesNode load() throws IOException {
1386 			AttributesNode r = new AttributesNode();
1387 			try (InputStream in = entry.openInputStream()) {
1388 				r.parse(in);
1389 			}
1390 			return r.getRules().isEmpty() ? null : r;
1391 		}
1392 	}
1393 
1394 
1395 	private static final class IteratorState {
1396 		/** Options used to process the working tree. */
1397 		final WorkingTreeOptions options;
1398 
1399 		/** File name character encoder. */
1400 		final CharsetEncoder nameEncoder;
1401 
1402 		/** Buffer used to perform {@link #contentId} computations. */
1403 		byte[] contentReadBuffer;
1404 
1405 		/** TreeWalk with a (supposedly) matching DirCacheIterator. */
1406 		TreeWalk walk;
1407 
1408 		/** Position of the matching {@link DirCacheIterator}. */
1409 		int dirCacheTree = -1;
1410 
1411 		/** Whether the iterator shall walk ignored directories. */
1412 		boolean walkIgnored = false;
1413 
1414 		final Map<String, Boolean> directoryToIgnored = new HashMap<>();
1415 
1416 		IteratorState(WorkingTreeOptions options) {
1417 			this.options = options;
1418 			this.nameEncoder = UTF_8.newEncoder();
1419 		}
1420 
1421 		void initializeReadBuffer() {
1422 			if (contentReadBuffer == null) {
1423 				contentReadBuffer = new byte[BUFFER_SIZE];
1424 			}
1425 		}
1426 	}
1427 
1428 	/**
1429 	 * Get the clean filter command for the current entry.
1430 	 *
1431 	 * @return the clean filter command for the current entry or
1432 	 *         <code>null</code> if no such command is defined
1433 	 * @throws java.io.IOException
1434 	 * @since 4.2
1435 	 */
1436 	public String getCleanFilterCommand() throws IOException {
1437 		if (cleanFilterCommandHolder == null) {
1438 			String cmd = null;
1439 			if (state.walk != null) {
1440 				cmd = state.walk
1441 						.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
1442 			}
1443 			cleanFilterCommandHolder = new Holder<>(cmd);
1444 		}
1445 		return cleanFilterCommandHolder.get();
1446 	}
1447 
1448 	/**
1449 	 * Get the eol stream type for the current entry.
1450 	 *
1451 	 * @return the eol stream type for the current entry or <code>null</code> if
1452 	 *         it cannot be determined. When state or state.walk is null or the
1453 	 *         {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on a
1454 	 *         {@link org.eclipse.jgit.lib.Repository} then null is returned.
1455 	 * @throws java.io.IOException
1456 	 * @since 4.3
1457 	 */
1458 	public EolStreamType getEolStreamType() throws IOException {
1459 		return getEolStreamType(null);
1460 	}
1461 
1462 	/**
1463 	 * @param opType
1464 	 *            The operationtype (checkin/checkout) which should be used
1465 	 * @return the eol stream type for the current entry or <code>null</code> if
1466 	 *         it cannot be determined. When state or state.walk is null or the
1467 	 *         {@link TreeWalk} is not based on a {@link Repository} then null
1468 	 *         is returned.
1469 	 * @throws IOException
1470 	 */
1471 	private EolStreamType getEolStreamType(OperationType opType)
1472 			throws IOException {
1473 		if (eolStreamTypeHolder == null) {
1474 			EolStreamType type=null;
1475 			if (state.walk != null) {
1476 				type = state.walk.getEolStreamType(opType);
1477 			} else {
1478 				switch (getOptions().getAutoCRLF()) {
1479 				case FALSE:
1480 					type = EolStreamType.DIRECT;
1481 					break;
1482 				case TRUE:
1483 				case INPUT:
1484 					type = EolStreamType.AUTO_LF;
1485 					break;
1486 				}
1487 			}
1488 			eolStreamTypeHolder = new Holder<>(type);
1489 		}
1490 		return eolStreamTypeHolder.get();
1491 	}
1492 
1493 	private boolean isDirectoryIgnored(String pathRel) throws IOException {
1494 		final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset;
1495 		final String base = TreeWalk.pathOf(this.path, 0, pOff);
1496 		final String pathAbs = concatPath(base, pathRel);
1497 		return isDirectoryIgnored(pathRel, pathAbs);
1498 	}
1499 
1500 	private boolean isDirectoryIgnored(String pathRel, String pathAbs)
1501 			throws IOException {
1502 		assert pathRel.length() == 0 || (pathRel.charAt(0) != '/'
1503 				&& pathRel.charAt(pathRel.length() - 1) != '/');
1504 		assert pathAbs.length() == 0 || (pathAbs.charAt(0) != '/'
1505 				&& pathAbs.charAt(pathAbs.length() - 1) != '/');
1506 		assert pathAbs.endsWith(pathRel);
1507 
1508 		Boolean ignored = state.directoryToIgnored.get(pathAbs);
1509 		if (ignored != null) {
1510 			return ignored.booleanValue();
1511 		}
1512 
1513 		final String parentRel = getParentPath(pathRel);
1514 		if (parentRel != null && isDirectoryIgnored(parentRel)) {
1515 			state.directoryToIgnored.put(pathAbs, Boolean.TRUE);
1516 			return true;
1517 		}
1518 
1519 		final IgnoreNode node = getIgnoreNode();
1520 		for (String p = pathRel; node != null
1521 				&& !"".equals(p); p = getParentPath(p)) { //$NON-NLS-1$
1522 			ignored = node.checkIgnored(p, true);
1523 			if (ignored != null) {
1524 				state.directoryToIgnored.put(pathAbs, ignored);
1525 				return ignored.booleanValue();
1526 			}
1527 		}
1528 
1529 		if (!(this.parent instanceof WorkingTreeIterator)) {
1530 			state.directoryToIgnored.put(pathAbs, Boolean.FALSE);
1531 			return false;
1532 		}
1533 
1534 		final WorkingTreeIterator wtParent = (WorkingTreeIterator) this.parent;
1535 		final String parentRelPath = concatPath(
1536 				TreeWalk.pathOf(this.path, wtParent.pathOffset, pathOffset - 1),
1537 				pathRel);
1538 		assert concatPath(TreeWalk.pathOf(wtParent.path, 0,
1539 				Math.max(0, wtParent.pathOffset - 1)), parentRelPath)
1540 						.equals(pathAbs);
1541 		return wtParent.isDirectoryIgnored(parentRelPath, pathAbs);
1542 	}
1543 
1544 	private static String getParentPath(String path) {
1545 		final int slashIndex = path.lastIndexOf('/', path.length() - 2);
1546 		if (slashIndex > 0) {
1547 			return path.substring(path.charAt(0) == '/' ? 1 : 0, slashIndex);
1548 		}
1549 		return path.length() > 0 ? "" : null; //$NON-NLS-1$
1550 	}
1551 
1552 	private static String concatPath(String p1, String p2) {
1553 		return p1 + (p1.length() > 0 && p2.length() > 0 ? "/" : "") + p2; //$NON-NLS-1$ //$NON-NLS-2$
1554 	}
1555 }