You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FileTreeIterator.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*
  2. * Copyright (C) 2008, Google Inc.
  3. * Copyright (C) 2007-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5. * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com> and others
  6. *
  7. * This program and the accompanying materials are made available under the
  8. * terms of the Eclipse Distribution License v. 1.0 which is available at
  9. * https://www.eclipse.org/org/documents/edl-v10.php.
  10. *
  11. * SPDX-License-Identifier: BSD-3-Clause
  12. */
  13. package org.eclipse.jgit.treewalk;
  14. import static java.nio.charset.StandardCharsets.UTF_8;
  15. import java.io.ByteArrayInputStream;
  16. import java.io.File;
  17. import java.io.FileInputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.time.Instant;
  21. import org.eclipse.jgit.dircache.DirCacheIterator;
  22. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  23. import org.eclipse.jgit.lib.Constants;
  24. import org.eclipse.jgit.lib.FileMode;
  25. import org.eclipse.jgit.lib.ObjectReader;
  26. import org.eclipse.jgit.lib.Repository;
  27. import org.eclipse.jgit.util.FS;
  28. /**
  29. * Working directory iterator for standard Java IO.
  30. * <p>
  31. * This iterator uses the standard <code>java.io</code> package to read the
  32. * specified working directory as part of a
  33. * {@link org.eclipse.jgit.treewalk.TreeWalk}.
  34. */
  35. public class FileTreeIterator extends WorkingTreeIterator {
  36. /**
  37. * the starting directory of this Iterator. All entries are located directly
  38. * in this directory.
  39. */
  40. protected final File directory;
  41. /**
  42. * the file system abstraction which will be necessary to perform certain
  43. * file system operations.
  44. */
  45. protected final FS fs;
  46. /**
  47. * the strategy used to compute the FileMode for a FileEntry. Can be used to
  48. * control things such as whether to recurse into a directory or create a
  49. * gitlink.
  50. *
  51. * @since 4.3
  52. */
  53. protected final FileModeStrategy fileModeStrategy;
  54. /**
  55. * Create a new iterator to traverse the work tree and its children.
  56. *
  57. * @param repo
  58. * the repository whose working tree will be scanned.
  59. */
  60. public FileTreeIterator(Repository repo) {
  61. this(repo,
  62. repo.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks() ?
  63. NoGitlinksStrategy.INSTANCE :
  64. DefaultFileModeStrategy.INSTANCE);
  65. }
  66. /**
  67. * Create a new iterator to traverse the work tree and its children.
  68. *
  69. * @param repo
  70. * the repository whose working tree will be scanned.
  71. * @param fileModeStrategy
  72. * the strategy to use to determine the FileMode for a FileEntry;
  73. * controls gitlinks etc.
  74. * @since 4.3
  75. */
  76. public FileTreeIterator(Repository repo, FileModeStrategy fileModeStrategy) {
  77. this(repo.getWorkTree(), repo.getFS(),
  78. repo.getConfig().get(WorkingTreeOptions.KEY),
  79. fileModeStrategy);
  80. initRootIterator(repo);
  81. }
  82. /**
  83. * Create a new iterator to traverse the given directory and its children.
  84. *
  85. * @param root
  86. * the starting directory. This directory should correspond to
  87. * the root of the repository.
  88. * @param fs
  89. * the file system abstraction which will be necessary to perform
  90. * certain file system operations.
  91. * @param options
  92. * working tree options to be used
  93. */
  94. public FileTreeIterator(File root, FS fs, WorkingTreeOptions options) {
  95. this(root, fs, options, DefaultFileModeStrategy.INSTANCE);
  96. }
  97. /**
  98. * Create a new iterator to traverse the given directory and its children.
  99. *
  100. * @param root
  101. * the starting directory. This directory should correspond to
  102. * the root of the repository.
  103. * @param fs
  104. * the file system abstraction which will be necessary to perform
  105. * certain file system operations.
  106. * @param options
  107. * working tree options to be used
  108. * @param fileModeStrategy
  109. * the strategy to use to determine the FileMode for a FileEntry;
  110. * controls gitlinks etc.
  111. * @since 4.3
  112. */
  113. public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options,
  114. FileModeStrategy fileModeStrategy) {
  115. super(options);
  116. directory = root;
  117. this.fs = fs;
  118. this.fileModeStrategy = fileModeStrategy;
  119. init(entries());
  120. }
  121. /**
  122. * Create a new iterator to traverse a subdirectory.
  123. *
  124. * @param p
  125. * the parent iterator we were created from.
  126. * @param root
  127. * the subdirectory. This should be a directory contained within
  128. * the parent directory.
  129. * @param fs
  130. * the file system abstraction which will be necessary to perform
  131. * certain file system operations.
  132. * @since 4.3
  133. */
  134. protected FileTreeIterator(final FileTreeIterator p, final File root,
  135. FS fs) {
  136. this(p, root, fs, p.fileModeStrategy);
  137. }
  138. /**
  139. * Create a new iterator to traverse a subdirectory, given the specified
  140. * FileModeStrategy.
  141. *
  142. * @param p
  143. * the parent iterator we were created from.
  144. * @param root
  145. * the subdirectory. This should be a directory contained within
  146. * the parent directory
  147. * @param fs
  148. * the file system abstraction which will be necessary to perform
  149. * certain file system operations.
  150. * @param fileModeStrategy
  151. * the strategy to use to determine the FileMode for a given
  152. * FileEntry.
  153. * @since 4.3
  154. */
  155. protected FileTreeIterator(final WorkingTreeIterator p, final File root,
  156. FS fs, FileModeStrategy fileModeStrategy) {
  157. super(p);
  158. directory = root;
  159. this.fs = fs;
  160. this.fileModeStrategy = fileModeStrategy;
  161. init(entries());
  162. }
  163. /** {@inheritDoc} */
  164. @Override
  165. public AbstractTreeIterator createSubtreeIterator(ObjectReader reader)
  166. throws IncorrectObjectTypeException, IOException {
  167. if (!walksIgnoredDirectories() && isEntryIgnored()) {
  168. DirCacheIterator iterator = getDirCacheIterator();
  169. if (iterator == null) {
  170. return new EmptyTreeIterator(this);
  171. }
  172. // Only enter if we have an associated DirCacheIterator that is
  173. // at the same entry (which indicates there is some already
  174. // tracked file underneath this directory). Otherwise the
  175. // directory is indeed ignored and can be skipped entirely.
  176. }
  177. return enterSubtree();
  178. }
  179. /**
  180. * Create a new iterator for the current entry's subtree.
  181. * <p>
  182. * The parent reference of the iterator must be <code>this</code>, otherwise
  183. * the caller would not be able to exit out of the subtree iterator
  184. * correctly and return to continue walking <code>this</code>.
  185. *
  186. * @return a new iterator that walks over the current subtree.
  187. * @since 5.0
  188. */
  189. protected AbstractTreeIterator enterSubtree() {
  190. return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs,
  191. fileModeStrategy);
  192. }
  193. private Entry[] entries() {
  194. return fs.list(directory, fileModeStrategy);
  195. }
  196. /**
  197. * An interface representing the methods used to determine the FileMode for
  198. * a FileEntry.
  199. *
  200. * @since 4.3
  201. */
  202. public interface FileModeStrategy {
  203. /**
  204. * Compute the FileMode for a given File, based on its attributes.
  205. *
  206. * @param f
  207. * the file to return a FileMode for
  208. * @param attributes
  209. * the attributes of a file
  210. * @return a FileMode indicating whether the file is a regular file, a
  211. * directory, a gitlink, etc.
  212. */
  213. FileMode getMode(File f, FS.Attributes attributes);
  214. }
  215. /**
  216. * A default implementation of a FileModeStrategy; defaults to treating
  217. * nested .git directories as gitlinks, etc.
  218. *
  219. * @since 4.3
  220. */
  221. public static class DefaultFileModeStrategy implements FileModeStrategy {
  222. /**
  223. * a singleton instance of the default FileModeStrategy
  224. */
  225. public static final DefaultFileModeStrategy INSTANCE =
  226. new DefaultFileModeStrategy();
  227. @Override
  228. public FileMode getMode(File f, FS.Attributes attributes) {
  229. if (attributes.isSymbolicLink()) {
  230. return FileMode.SYMLINK;
  231. } else if (attributes.isDirectory()) {
  232. if (new File(f, Constants.DOT_GIT).exists()) {
  233. return FileMode.GITLINK;
  234. }
  235. return FileMode.TREE;
  236. } else if (attributes.isExecutable()) {
  237. return FileMode.EXECUTABLE_FILE;
  238. } else {
  239. return FileMode.REGULAR_FILE;
  240. }
  241. }
  242. }
  243. /**
  244. * A FileModeStrategy that implements native git's DIR_NO_GITLINKS
  245. * behavior. This is the same as the default FileModeStrategy, except
  246. * all directories will be treated as directories regardless of whether
  247. * or not they contain a .git directory or file.
  248. *
  249. * @since 4.3
  250. */
  251. public static class NoGitlinksStrategy implements FileModeStrategy {
  252. /**
  253. * a singleton instance of the default FileModeStrategy
  254. */
  255. public static final NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy();
  256. @Override
  257. public FileMode getMode(File f, FS.Attributes attributes) {
  258. if (attributes.isSymbolicLink()) {
  259. return FileMode.SYMLINK;
  260. } else if (attributes.isDirectory()) {
  261. return FileMode.TREE;
  262. } else if (attributes.isExecutable()) {
  263. return FileMode.EXECUTABLE_FILE;
  264. } else {
  265. return FileMode.REGULAR_FILE;
  266. }
  267. }
  268. }
  269. /**
  270. * Wrapper for a standard Java IO file
  271. */
  272. public static class FileEntry extends Entry {
  273. private final FileMode mode;
  274. private FS.Attributes attributes;
  275. private FS fs;
  276. /**
  277. * Create a new file entry.
  278. *
  279. * @param f
  280. * file
  281. * @param fs
  282. * file system
  283. */
  284. public FileEntry(File f, FS fs) {
  285. this(f, fs, DefaultFileModeStrategy.INSTANCE);
  286. }
  287. /**
  288. * Create a new file entry given the specified FileModeStrategy
  289. *
  290. * @param f
  291. * file
  292. * @param fs
  293. * file system
  294. * @param fileModeStrategy
  295. * the strategy to use when determining the FileMode of a
  296. * file; controls gitlinks etc.
  297. *
  298. * @since 4.3
  299. */
  300. public FileEntry(File f, FS fs, FileModeStrategy fileModeStrategy) {
  301. this.fs = fs;
  302. f = fs.normalize(f);
  303. attributes = fs.getAttributes(f);
  304. mode = fileModeStrategy.getMode(f, attributes);
  305. }
  306. /**
  307. * Create a new file entry given the specified FileModeStrategy
  308. *
  309. * @param f
  310. * file
  311. * @param fs
  312. * file system
  313. * @param attributes
  314. * of the file
  315. * @param fileModeStrategy
  316. * the strategy to use when determining the FileMode of a
  317. * file; controls gitlinks etc.
  318. *
  319. * @since 5.0
  320. */
  321. public FileEntry(File f, FS fs, FS.Attributes attributes,
  322. FileModeStrategy fileModeStrategy) {
  323. this.fs = fs;
  324. this.attributes = attributes;
  325. f = fs.normalize(f);
  326. mode = fileModeStrategy.getMode(f, attributes);
  327. }
  328. @Override
  329. public FileMode getMode() {
  330. return mode;
  331. }
  332. @Override
  333. public String getName() {
  334. return attributes.getName();
  335. }
  336. @Override
  337. public long getLength() {
  338. return attributes.getLength();
  339. }
  340. @Override
  341. @Deprecated
  342. public long getLastModified() {
  343. return attributes.getLastModifiedInstant().toEpochMilli();
  344. }
  345. /**
  346. * @since 5.1.9
  347. */
  348. @Override
  349. public Instant getLastModifiedInstant() {
  350. return attributes.getLastModifiedInstant();
  351. }
  352. @Override
  353. public InputStream openInputStream() throws IOException {
  354. if (attributes.isSymbolicLink()) {
  355. return new ByteArrayInputStream(fs.readSymLink(getFile())
  356. .getBytes(UTF_8));
  357. }
  358. return new FileInputStream(getFile());
  359. }
  360. /**
  361. * Get the underlying file of this entry.
  362. *
  363. * @return the underlying file of this entry
  364. */
  365. public File getFile() {
  366. return attributes.getFile();
  367. }
  368. }
  369. /**
  370. * <p>Getter for the field <code>directory</code>.</p>
  371. *
  372. * @return The root directory of this iterator
  373. */
  374. public File getDirectory() {
  375. return directory;
  376. }
  377. /**
  378. * Get the location of the working file.
  379. *
  380. * @return The location of the working file. This is the same as {@code new
  381. * File(getDirectory(), getEntryPath())} but may be faster by
  382. * reusing an internal File instance.
  383. */
  384. public File getEntryFile() {
  385. return ((FileEntry) current()).getFile();
  386. }
  387. /** {@inheritDoc} */
  388. @Override
  389. protected byte[] idSubmodule(Entry e) {
  390. return idSubmodule(getDirectory(), e);
  391. }
  392. /** {@inheritDoc} */
  393. @Override
  394. protected String readSymlinkTarget(Entry entry) throws IOException {
  395. return fs.readSymLink(getEntryFile());
  396. }
  397. }