fast_ignore 0.11.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +159 -80
- data/lib/fast_ignore.rb +46 -18
- data/lib/fast_ignore/backports.rb +3 -7
- data/lib/fast_ignore/file_root.rb +39 -0
- data/lib/fast_ignore/gitignore_include_rule_builder.rb +102 -0
- data/lib/fast_ignore/gitignore_rule_builder.rb +182 -0
- data/lib/fast_ignore/gitignore_rule_regexp_builder.rb +76 -0
- data/lib/fast_ignore/gitignore_rule_scanner.rb +73 -0
- data/lib/fast_ignore/global_gitignore.rb +50 -0
- data/lib/fast_ignore/rule.rb +28 -12
- data/lib/fast_ignore/rule_builder.rb +18 -80
- data/lib/fast_ignore/rule_set.rb +38 -13
- data/lib/fast_ignore/rule_sets.rb +113 -0
- data/lib/fast_ignore/shebang_rule.rb +34 -14
- data/lib/fast_ignore/unmatchable_rule.rb +41 -0
- data/lib/fast_ignore/version.rb +1 -1
- metadata +10 -21
- data/.gitignore +0 -6
- data/.leftovers.yml +0 -5
- data/.rspec +0 -3
- data/.rubocop.yml +0 -216
- data/.simplecov +0 -10
- data/.spellr.yml +0 -5
- data/.spellr_wordlists/english.txt +0 -56
- data/.spellr_wordlists/ruby.txt +0 -1
- data/.spellr_wordlists/shell.txt +0 -3
- data/.travis.yml +0 -10
- data/Gemfile +0 -9
- data/Rakefile +0 -14
- data/bin/console +0 -8
- data/bin/ls +0 -6
- data/bin/setup +0 -8
- data/bin/time +0 -21
- data/fast_ignore.gemspec +0 -41
- data/lib/fast_ignore/fn_match_to_re.rb +0 -96
- data/lib/fast_ignore/rule_set_builder.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f96030c8670a5d709139689212b3a78a0eb0b5266cbd0057954863ae89e11745
|
4
|
+
data.tar.gz: a2815ec68f6c367a4450efce8799aa359adeee1ad1aaa971b589cd8612ddbb61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c22c6c79a6605473156a041d5ddb5b81f943605dc4360dd8577acd56718c928c106e044169b13bc73c7e884ffcc5516c7a6b5d6134fee9aaa4aa73d0178b791
|
7
|
+
data.tar.gz: 1d165e44b50d9b1c301c5cad8ff3c443bfb2f9134be09e28a4e90539c960a269083485f6ff30bc6df6e2ec82347630187ebf0082cd2d06ba7416e9c70f37ea6f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
# v0.15.0
|
2
|
+
- fixed a handful of character class edge cases to match git behavior
|
3
|
+
- mostly ranges with - or / as one end of the range
|
4
|
+
- major refactoring of the regexp builder that shouldn't have any behaviour implications but should make development easier (e.g. seeing those unhandled edge cases).
|
5
|
+
- improved speed of repos with many sub-gitignore files
|
6
|
+
- mentioned submodules & sparse checkout in the readme as yet another thing git does that this project doesn't because submodule details are hidden in the git index.
|
7
|
+
|
8
|
+
# v0.14.0
|
9
|
+
- significant performance improvements ~50% faster
|
10
|
+
- add `FastIgnore#to_proc` for no good reason
|
11
|
+
|
12
|
+
# v0.13.0
|
13
|
+
- Attempt to improve documentation structure
|
14
|
+
- Remove `gitignore: true` raising `Errno::ENOENT` if root:/.gitignore didn't exist. I can't think of a use. Now `gitignore: true` is just the default behaviour.
|
15
|
+
- Don't ignore `.git` if `gitignore: false`.
|
16
|
+
|
17
|
+
# v0.12.1
|
18
|
+
- Reads all relevant git config files when finding a global .gitignore
|
19
|
+
|
20
|
+
# v0.12.0
|
21
|
+
- Reads all relevant gitignore files (nested .gitignore files, global .gitignore referred to in .gitconfig, and .git/info/exclude)
|
22
|
+
|
1
23
|
# v0.11.0
|
2
24
|
- major performance improvement (use regexp rather than fnmatch)
|
3
25
|
- optionally pass directory: and content: into allowed? if these are already loaded.
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# FastIgnore
|
2
2
|
|
3
|
-
[![travis](https://travis-ci.
|
3
|
+
[![travis](https://travis-ci.com/robotdana/fast_ignore.svg?branch=master)](https://travis-ci.com/robotdana/fast_ignore)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/fast_ignore.svg)](https://rubygems.org/gems/fast_ignore)
|
4
5
|
|
5
6
|
This started as a way to quickly and natively ruby-ly parse gitignore files and find matching files.
|
6
7
|
It's now gained an equivalent includes file functionality, ARGV awareness, and some shebang matching, while still being extremely fast, to be a one-stop file-list for your linter.
|
@@ -14,12 +15,15 @@ FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
|
|
14
15
|
## Features
|
15
16
|
|
16
17
|
- Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
|
17
|
-
- Supports ruby 2.4
|
18
|
-
- supports all gitignore rule patterns
|
18
|
+
- Supports ruby 2.4-2.7 & jruby
|
19
|
+
- supports all [gitignore rule patterns](https://git-scm.com/docs/gitignore#_pattern_format)
|
19
20
|
- doesn't require git to be installed
|
20
|
-
- supports a gitignore-esque "include" patterns. (`include_rules
|
21
|
-
- supports an expansion of include patterns,
|
22
|
-
- supports matching by shebang rather than filename for extensionless files: `#!:`
|
21
|
+
- supports a gitignore-esque "include" patterns. ([`include_rules:`](#include_rules)/[`include_files:`](#include_files))
|
22
|
+
- supports an expansion of include patterns, expanding and anchoring paths ([`argv_rules:`](#argv_rules))
|
23
|
+
- supports [matching by shebang](#shebang_rules) rather than filename for extensionless files: `#!:`
|
24
|
+
- reads .gitignore in all subdirectories
|
25
|
+
- reads .git/info/excludes
|
26
|
+
- reads the global gitignore file mentioned in your git config
|
23
27
|
|
24
28
|
## Installation
|
25
29
|
|
@@ -46,22 +50,26 @@ FastIgnore.new.each { |file| puts "#{file} is not ignored by the .gitignore file
|
|
46
50
|
|
47
51
|
### `#each`, `#map` etc
|
48
52
|
|
49
|
-
|
53
|
+
This yields paths that are _not_ ignored by the gitignore, i.e. the paths that would be returned by `git ls-files`.
|
54
|
+
|
55
|
+
A FastIgnore instance is an Enumerable and responds to all Enumerable methods:
|
50
56
|
|
51
57
|
```ruby
|
52
58
|
FastIgnore.new.to_a
|
53
59
|
FastIgnore.new.map { |file| file.upcase }
|
54
60
|
```
|
55
61
|
|
56
|
-
Like other enumerables, `FastIgnore#each` can return an enumerator
|
62
|
+
Like other enumerables, `FastIgnore#each` can return an enumerator:
|
57
63
|
|
58
64
|
```ruby
|
59
65
|
FastIgnore.new.each.with_index { |file, index| puts "#{file}#{index}" }
|
60
66
|
```
|
61
67
|
|
68
|
+
**Warning: Do not change directory (e.g. `Dir.chdir`) in the block.**
|
69
|
+
|
62
70
|
### `#allowed?`
|
63
71
|
|
64
|
-
To check if a single
|
72
|
+
To check if a single path is allowed, use
|
65
73
|
```ruby
|
66
74
|
FastIgnore.new.allowed?('relative/path')
|
67
75
|
FastIgnore.new.allowed?('./relative/path')
|
@@ -69,7 +77,9 @@ FastIgnore.new.allowed?('/absolute/path')
|
|
69
77
|
FastIgnore.new.allowed?('~/home/path')
|
70
78
|
```
|
71
79
|
|
72
|
-
|
80
|
+
Relative paths will be considered relative to the [`root:`](#root) directory, not the current directory.
|
81
|
+
|
82
|
+
This is aliased as `===` so you can use a FastIgnore instance in case statements.
|
73
83
|
```ruby
|
74
84
|
case my_path
|
75
85
|
when FastIgnore.new
|
@@ -77,18 +87,34 @@ when FastIgnore.new
|
|
77
87
|
end
|
78
88
|
```
|
79
89
|
|
80
|
-
It's recommended to
|
90
|
+
It's recommended to save the FastIgnore instance to a variable to avoid having to read and parse the gitignore file and gitconfig files repeatedly.
|
91
|
+
|
92
|
+
See [Optimising allowed](#optimising_allowed) for ways to make this even faster
|
93
|
+
|
94
|
+
**Note: A file must exist at that path and not be a directory for it to be considered allowed.**
|
95
|
+
Essentially it can be thought of as `` `git ls-files`.include?(path) `` but much faster.
|
96
|
+
This excludes all directories and all possible path names that don't exist.
|
97
|
+
|
81
98
|
|
82
99
|
### `relative: true`
|
83
|
-
|
100
|
+
|
101
|
+
**Default: false**
|
102
|
+
|
103
|
+
When `relative: false`: FastIgnore#each will yield full paths.
|
104
|
+
When `relative: true`: FastIgnore#each will yield paths relative to the [`root:`](#root) directory
|
84
105
|
|
85
106
|
```ruby
|
86
107
|
FastIgnore.new(relative: true).to_a
|
87
108
|
```
|
88
109
|
|
89
110
|
### `follow_symlinks: true`
|
90
|
-
|
91
|
-
|
111
|
+
|
112
|
+
**Default: false**
|
113
|
+
|
114
|
+
When `follow_symlinks: false`: FastIgnore#each will match git's behaviour and not follow symbolic links.
|
115
|
+
When `follow_symlinks: true`: FastIgnore#each will check if a symlink points to a directory, and files in linked directories must also match rules using the symlink path as the directory location, not the real directory location.
|
116
|
+
|
117
|
+
**This doesn't use the real path for matching or yield or return it.**
|
92
118
|
|
93
119
|
```ruby
|
94
120
|
FastIgnore.new(follow_symlinks: true).to_a
|
@@ -96,12 +122,16 @@ FastIgnore.new(follow_symlinks: true).to_a
|
|
96
122
|
|
97
123
|
### `root:`
|
98
124
|
|
99
|
-
|
125
|
+
**Default: Dir.pwd ($PWD, the current working directory)**
|
126
|
+
|
100
127
|
This directory is used for:
|
101
|
-
-
|
102
|
-
-
|
103
|
-
-
|
104
|
-
-
|
128
|
+
- the location of `.git/core/exclude`
|
129
|
+
- the ancestor of all non-global [automatically loaded `.gitignore` files](#gitignore_false)
|
130
|
+
- the root directory for array rules ([`ignore_rules:`](#ignore_rules), [`include_rules:`](#include_rules), [`argv_rules:`](#argv_rules)) containing `/`
|
131
|
+
- the path that [`relative:`](#relative_true) is relative to
|
132
|
+
- the ancestor of all paths yielded by [`#each`](#each_map_etc)
|
133
|
+
- the path that [`#allowed?`](#allowed) considers relative paths relative to
|
134
|
+
- the ancestor of all [`include_files:`](#include_files) and [`ignore_files:`](#ignore_files)
|
105
135
|
|
106
136
|
To use a different directory:
|
107
137
|
```ruby
|
@@ -109,36 +139,49 @@ FastIgnore.new(root: '/absolute/path/to/root').to_a
|
|
109
139
|
FastIgnore.new(root: '../relative/path/to/root').to_a
|
110
140
|
```
|
111
141
|
|
142
|
+
A relative root will be found relative to the current working directory when the FastIgnore instance is initialized, and that will be the last time the current working directory is relevant.
|
143
|
+
|
144
|
+
**Note: Changes to the current working directory (e.g. with `Dir.chdir`), after initialising a FastIgnore instance, will _not_ affect the FastIgnore instance. `root:` will always be what it was when the instance was initialized, even as a default value.**
|
145
|
+
|
112
146
|
### `gitignore:`
|
113
147
|
|
114
|
-
|
115
|
-
To not do this use
|
116
|
-
```ruby
|
117
|
-
FastIgnore.new(gitignore: false).to_a
|
118
|
-
```
|
148
|
+
**Default: true**
|
119
149
|
|
120
|
-
|
121
|
-
|
122
|
-
FastIgnore.new(gitignore: true).to_a
|
123
|
-
```
|
150
|
+
When `gitignore: true`: the .gitignore file in the [`root:`](#root) directory is loaded, plus any .gitignore files in its subdirectories, the global git ignore file as described in git config, and .git/info/exclude. `.git` directories are also excluded to match the behaviour of `git ls-files`.
|
151
|
+
When `gitignore: false`: no ignore files or git config files are automatically read, and `.git` will not be automatically excluded.
|
124
152
|
|
125
|
-
If the gitignore file is somewhere else
|
126
153
|
```ruby
|
127
|
-
FastIgnore.new(
|
154
|
+
FastIgnore.new(gitignore: false).to_a
|
128
155
|
```
|
129
|
-
Note that the location of the .gitignore file will affect rules beginning with `/` or ending in `/**`
|
130
156
|
|
131
157
|
### `ignore_files:`
|
158
|
+
|
159
|
+
**This is a list of files in the gitignore format to parse and match paths against, not a list of files to ignore** If you want an array of files use [`ignore_rules:`](#ignore_rules)
|
160
|
+
|
161
|
+
Additional gitignore-style files, either as a path or an array of paths.
|
162
|
+
|
132
163
|
You can specify other gitignore-style files to ignore as well.
|
133
164
|
Missing files will raise an `Errno::ENOENT` error.
|
134
165
|
|
166
|
+
Relative paths are relative to the [`root:`](#root) directory.
|
167
|
+
Absolute paths also need to be within the [`root:`](#root) directory.
|
168
|
+
|
169
|
+
|
135
170
|
```ruby
|
136
|
-
FastIgnore.new(ignore_files: '/
|
171
|
+
FastIgnore.new(ignore_files: 'relative/path/to/my/ignore/file').to_a
|
137
172
|
FastIgnore.new(ignore_files: ['/absolute/path/to/my/ignore/file', '/and/another']).to_a
|
138
173
|
```
|
139
174
|
|
175
|
+
Note: the location of the files will affect rules beginning with or containing `/`.
|
176
|
+
|
177
|
+
To avoid raising `Errno::ENOENT` when the file doesn't exist:
|
178
|
+
```ruby
|
179
|
+
FastIgnore.new(ignore_files: ['/ignore/file'].select { |f| File.exist?(f) }).to_a
|
180
|
+
```
|
181
|
+
|
140
182
|
### `ignore_rules:`
|
141
|
-
|
183
|
+
|
184
|
+
This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
|
142
185
|
|
143
186
|
```ruby
|
144
187
|
FastIgnore.new(ignore_rules: '.DS_Store').to_a
|
@@ -146,84 +189,110 @@ FastIgnore.new(ignore_rules: ['.git', '.gitkeep']).to_a
|
|
146
189
|
FastIgnore.new(ignore_rules: ".git\n.gitkeep").to_a
|
147
190
|
```
|
148
191
|
|
149
|
-
|
192
|
+
These rules use the [`root:`](#root) argument to resolve rules containing `/`.
|
150
193
|
|
151
|
-
|
194
|
+
### `include_files:`
|
152
195
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
196
|
+
**This is an array of files in the gitignore format to parse and match paths against, not a list of files to include.** If you want an array of files use [`include_rules:`](#include_rules).
|
197
|
+
|
198
|
+
Building on the gitignore format, FastIgnore also accepts rules to include matching paths (rather than ignoring them).
|
199
|
+
A rule matching a directory will include all descendants of that directory.
|
200
|
+
|
201
|
+
These rules can be provided in files either as absolute or relative paths, or an array of paths.
|
202
|
+
Relative paths are relative to the [`root:`](#root) directory.
|
203
|
+
Absolute paths also need to be within the [`root:`](#root) directory.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
FastIgnore.new(include_files: 'my_include_file').to_a
|
207
|
+
FastIgnore.new(include_files: ['/absolute/include/file', './relative/include/file']).to_a
|
208
|
+
```
|
209
|
+
|
210
|
+
Missing files will raise an `Errno::ENOENT` error.
|
211
|
+
|
212
|
+
To avoid raising `Errno::ENOENT` when the file doesn't exist:
|
213
|
+
```ruby
|
214
|
+
FastIgnore.new(include_files: ['include/file'].select { |f| File.exist?(f) }).to_a
|
165
215
|
```
|
166
216
|
|
167
|
-
|
217
|
+
**Note: All paths checked must not be excluded by any ignore files AND each included by include file separately AND the [`include_rules:`](#include_rules) AND the [`argv_rules:`](#argv_rules). see [Combinations](#combinations) for solutions to using OR.**
|
218
|
+
|
219
|
+
### `include_rules:`
|
220
|
+
|
221
|
+
Building on the gitignore format, FastIgnore also accepts rules to include matching paths (rather than ignoring them).
|
222
|
+
A rule matching a directory will include all descendants of that directory.
|
223
|
+
|
224
|
+
This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
|
168
225
|
```ruby
|
169
|
-
FastIgnore.new(include_files: '/absolute/path/to/my/include/file', gitignore: false).to_a
|
170
226
|
FastIgnore.new(include_rules: %w{my*rule /and/another !rule}, gitignore: false).to_a
|
171
227
|
```
|
172
228
|
|
173
|
-
|
229
|
+
Rules use the [`root:`](#root) argument to resolve rules containing `/`.
|
230
|
+
|
231
|
+
**Note: All paths checked must not be excluded by any ignore files AND each included by [include file](#include_files) separately AND the `include_rules:` AND the [`argv_rules:`](#argv_rules). see [Combinations](#combinations) for solutions to using OR.**
|
232
|
+
|
233
|
+
### `argv_rules:`
|
234
|
+
This is like [`include_rules:`](#include_rules) with additional features meant for dealing with humans and `ARGV` values.
|
235
|
+
|
236
|
+
It expands rules that are absolute paths, and paths beginning with `~`, `../` and `./` (with and without `!`).
|
237
|
+
This means rules beginning with `/` are absolute. Not relative to [`root:`](#root).
|
238
|
+
|
239
|
+
Additionally it assumes all rules are relative to the [`root:`](#root) directory (after resolving absolute paths) unless they begin with `*` (or `!*`).
|
240
|
+
|
241
|
+
This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
|
174
242
|
|
175
243
|
```ruby
|
176
244
|
FastIgnore.new(argv_rules: ['./a/pasted/path', '/or/a/path/from/stdin', 'an/argument', '*.txt']).to_a
|
177
245
|
```
|
178
246
|
|
179
|
-
|
180
|
-
It assumes all rules are anchored unless they begin with `*` or `!*`.
|
247
|
+
**Warning: it will *not* expand e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`.**
|
181
248
|
|
182
|
-
Note:
|
249
|
+
**Note: All paths checked must not be excluded by any ignore files AND each included by [include file](#include_files) separately AND the [`include_rules:`](#include_rules) AND the `argv_rules:`. see [Combinations](#combinations) for solutions to using OR.**
|
183
250
|
|
184
251
|
### shebang rules
|
185
252
|
|
186
|
-
Sometimes you need to match files by their shebang rather than their path or filename
|
187
|
-
|
188
|
-
To match extensionless files by shebang/hashbang/etc:
|
253
|
+
Sometimes you need to match files by their shebang/hashbang/etc rather than their path or filename
|
189
254
|
|
190
|
-
|
255
|
+
Rules beginning with `#!:` will match whole words in the shebang line of extensionless files.
|
191
256
|
e.g.
|
192
257
|
```gitignore
|
193
258
|
#!:ruby
|
194
259
|
```
|
195
260
|
will match shebang lines: `#!/usr/bin/env ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
|
261
|
+
|
196
262
|
e.g.
|
197
263
|
```gitignore
|
198
264
|
#!:bin/ruby
|
199
265
|
```
|
200
266
|
will match `#!/bin/ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
|
201
|
-
|
267
|
+
Only exact substring matches are available, There's no special handling of * or / or etc.
|
202
268
|
|
269
|
+
These rules can be supplied any way regular rules are, whether in a .gitignore file or files mentioned in [`include_files:`](#include_files) or [`ignore_files:`](#ignore_files) or [`include_rules:`](#include_rules) or [`ignore_rules:`](#ignore_rules) or [`argv_rules:`](#argv_rules)
|
203
270
|
```ruby
|
204
271
|
FastIgnore.new(include_rules: ['*.rb', '#!:ruby']).to_a
|
205
272
|
FastIgnore.new(ignore_rules: ['*.sh', '#!:sh', '#!:bash', '#!:zsh']).to_a
|
206
273
|
```
|
207
274
|
|
275
|
+
**Note: git considers rules like this as a comment and will ignore them.**
|
276
|
+
|
208
277
|
## Combinations
|
209
278
|
|
210
|
-
In the simplest case a file must be allowed by each ignore file, each include file, and each array of rules. That is, they are combined using AND
|
279
|
+
In the simplest case a file must be allowed by each ignore file, each include file, and each array of rules. That is, they are combined using `AND`.
|
211
280
|
|
212
281
|
To combine files using `OR`, that is, a file may be matched by either file it doesn't have to be referred to in both:
|
213
|
-
provide the files as strings to `include_rules:` or `ignore_rules:`
|
282
|
+
provide the files as strings to [`include_rules:`](#include_rules) or [`ignore_rules:`](#ignore_rules)
|
214
283
|
```ruby
|
215
284
|
FastIgnore.new(include_rules: [File.read('/my/path'), File.read('/another/path')])).to_a
|
216
285
|
```
|
217
|
-
This does unfortunately lose the file path as the root for
|
218
|
-
If that's important, combine the files in the file system and use `include_files:` or `ignore_files:` as normal.
|
286
|
+
This does unfortunately lose the file path as the root for rules containing `/`.
|
287
|
+
If that's important, combine the files in the file system and use [`include_files:`](#include_files) or [`ignore_files:`](#ignore_files) as normal.
|
219
288
|
|
220
|
-
To use the additional ARGV handling
|
289
|
+
To use the additional `ARGV` handling of [`argv_rules:`](#argv_rules) on a file, read the file into the array.
|
221
290
|
|
222
291
|
```ruby
|
223
292
|
FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
|
224
293
|
```
|
225
294
|
|
226
|
-
This does unfortunately lose the file path as the root
|
295
|
+
This does unfortunately lose the file path as the root `/` and there is no workaround except setting the [`root:`](#root) for the whole FastIgnore instance.
|
227
296
|
|
228
297
|
### optimising #allowed?
|
229
298
|
|
@@ -233,29 +302,39 @@ FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/
|
|
233
302
|
```
|
234
303
|
This is not required, and if FastIgnore does have to go to the filesystem for this information it's well optimised to only read what is necessary.
|
235
304
|
|
236
|
-
|
237
|
-
|
238
|
-
- Doesn't take into account project excludes in `.git/info/exclude`
|
239
|
-
- Doesn't take into account globally ignored files in `git config core.excludesFile`.
|
240
|
-
- Doesn't know what to do if you change the current working directory inside the `FastIgnore#each` block.
|
305
|
+
## Limitations
|
306
|
+
- Doesn't know what to do if you change the current working directory inside the [`FastIgnore#each`](#each_map_etc) block.
|
241
307
|
So don't do that.
|
242
308
|
|
243
|
-
(It does handle changing the current working directory between `FastIgnore#allowed?` calls
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
309
|
+
(It does handle changing the current working directory between [`FastIgnore#allowed?`](#allowed) calls)
|
310
|
+
- FastIgnore always matches patterns case-insensitively. (git varies by filesystem).
|
311
|
+
- FastIgnore always outputs paths as literal UTF-8 characters. (git depends on your core.quotepath setting but by default outputs non ascii paths with octal escapes surrounded by quotes).
|
312
|
+
- Because git looks at its own index objects and FastIgnore looks at the file system there may be some differences between FastIgnore and `git ls-files`. To avoid these differences you may want to use the [`git_ls`](https://github.com/robotdana/git_ls) gem instead
|
313
|
+
- Tracked files that were committed before the matching ignore rule was committed will be returned by `git ls-files`, but not by FastIgnore.
|
314
|
+
- Untracked files will be returned by FastIgnore, but not by `git ls-files`
|
315
|
+
- Deleted files whose deletions haven't been committed will be returned by `git ls-files`, but not by FastIgnore
|
316
|
+
- On a case insensitive file system, with files that differ only by case, `git ls-files` will include all case variations, while FastIgnore will only include whichever variation git placed in the file system.
|
317
|
+
- FastIgnore is unaware of submodules and just treats them like regular directories. For example: `git ls-files --recurse-submodules` won't use the parent repo's gitignore on a submodule, while FastIgnore doesn't know it's a submodule and will.
|
318
|
+
- FastIgnore will only return the files actually on the file system when using `git sparse-checkout`.
|
254
319
|
|
255
320
|
## Contributing
|
256
321
|
|
257
322
|
Bug reports and pull requests are welcome on GitHub at https://github.com/robotdana/fast_ignore.
|
258
323
|
|
324
|
+
Some tools that may help:
|
325
|
+
|
326
|
+
- `bin/setup`: install development dependencies
|
327
|
+
- `bundle exec rspec`: run all tests
|
328
|
+
- `bundle exec rake`: run all tests and linters
|
329
|
+
- `bin/console`: open a `pry` console with everything required for experimenting
|
330
|
+
- `bin/ls [argv_rules]`: the equivalent of `git ls-files`
|
331
|
+
- `bin/prof/ls [argv_rules]`: ruby-prof report for `bin/ls`
|
332
|
+
- `bin/prof/parse [argv_rules]`: ruby-prof report for parsing root and global gitignore files and any arguments.
|
333
|
+
- `bin/time [argv_rules]`: the average time for 30 runs of `bin/ls`<br>
|
334
|
+
This repo is too small to stress bin/time more than 0.01s, switch to a large repo and find the average time before and after changes.
|
335
|
+
- `bin/compare`: compare the speed and output of FastIgnore and `git ls-files`.
|
336
|
+
(suppressing differences that are because of known [limitations](#limitations))
|
337
|
+
|
259
338
|
## License
|
260
339
|
|
261
340
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/fast_ignore.rb
CHANGED
@@ -2,12 +2,20 @@
|
|
2
2
|
|
3
3
|
require_relative './fast_ignore/backports'
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
require 'set'
|
6
|
+
require 'strscan'
|
7
|
+
require_relative './fast_ignore/rule_sets'
|
7
8
|
require_relative './fast_ignore/rule_set'
|
9
|
+
require_relative './fast_ignore/global_gitignore'
|
10
|
+
require_relative './fast_ignore/rule_builder'
|
11
|
+
require_relative './fast_ignore/gitignore_rule_builder'
|
12
|
+
require_relative './fast_ignore/gitignore_include_rule_builder'
|
13
|
+
require_relative './fast_ignore/gitignore_rule_regexp_builder'
|
14
|
+
require_relative './fast_ignore/gitignore_rule_scanner'
|
15
|
+
require_relative './fast_ignore/file_root'
|
8
16
|
require_relative './fast_ignore/rule'
|
17
|
+
require_relative './fast_ignore/unmatchable_rule'
|
9
18
|
require_relative './fast_ignore/shebang_rule'
|
10
|
-
require_relative './fast_ignore/fn_match_to_re'
|
11
19
|
|
12
20
|
class FastIgnore
|
13
21
|
class Error < StandardError; end
|
@@ -19,11 +27,13 @@ class FastIgnore
|
|
19
27
|
using ::FastIgnore::Backports::DirEachChild if defined?(::FastIgnore::Backports::DirEachChild)
|
20
28
|
# :nocov:
|
21
29
|
|
22
|
-
def initialize(relative: false, root: nil, follow_symlinks: false, **rule_set_builder_args)
|
30
|
+
def initialize(relative: false, root: nil, gitignore: :auto, follow_symlinks: false, **rule_set_builder_args)
|
23
31
|
@relative = relative
|
24
|
-
@
|
32
|
+
@follow_symlinks_method = ::File.method(follow_symlinks ? :stat : :lstat)
|
33
|
+
@gitignore_enabled = gitignore
|
34
|
+
@loaded_gitignore_files = ::Set[''] if gitignore
|
25
35
|
@root = "#{::File.expand_path(root.to_s, Dir.pwd)}/"
|
26
|
-
@rule_sets = ::FastIgnore::
|
36
|
+
@rule_sets = ::FastIgnore::RuleSets.new(root: @root, gitignore: gitignore, **rule_set_builder_args)
|
27
37
|
|
28
38
|
freeze
|
29
39
|
end
|
@@ -31,7 +41,7 @@ class FastIgnore
|
|
31
41
|
def each(&block)
|
32
42
|
return enum_for(:each) unless block_given?
|
33
43
|
|
34
|
-
dir_pwd = Dir.pwd
|
44
|
+
dir_pwd = ::Dir.pwd
|
35
45
|
root_from_pwd = @root.start_with?(dir_pwd) ? ".#{@root.delete_prefix(dir_pwd)}" : @root
|
36
46
|
|
37
47
|
each_recursive(root_from_pwd, '', &block)
|
@@ -40,35 +50,53 @@ class FastIgnore
|
|
40
50
|
def allowed?(path, directory: nil, content: nil)
|
41
51
|
full_path = ::File.expand_path(path, @root)
|
42
52
|
return false unless full_path.start_with?(@root)
|
43
|
-
return false if directory.nil? ?
|
53
|
+
return false if directory.nil? ? @follow_symlinks_method.call(full_path).directory? : directory
|
44
54
|
|
45
55
|
relative_path = full_path.delete_prefix(@root)
|
56
|
+
load_gitignore_recursive(relative_path) if @gitignore_enabled
|
57
|
+
|
46
58
|
filename = ::File.basename(relative_path)
|
47
59
|
|
48
|
-
@rule_sets.
|
60
|
+
@rule_sets.allowed_recursive?(relative_path, full_path, filename, content)
|
49
61
|
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
50
62
|
false
|
51
63
|
end
|
52
64
|
alias_method :===, :allowed?
|
53
65
|
|
66
|
+
def to_proc
|
67
|
+
method(:allowed?).to_proc
|
68
|
+
end
|
69
|
+
|
54
70
|
private
|
55
71
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
::File.lstat(path).directory?
|
72
|
+
def load_gitignore_recursive(path)
|
73
|
+
paths = []
|
74
|
+
while (path = ::File.dirname(path)) != '.'
|
75
|
+
paths << path
|
61
76
|
end
|
77
|
+
|
78
|
+
paths.reverse_each(&method(:load_gitignore))
|
62
79
|
end
|
63
80
|
|
64
|
-
def
|
65
|
-
|
81
|
+
def load_gitignore(parent_path, check_exists: true)
|
82
|
+
return if @loaded_gitignore_files.include?(parent_path)
|
83
|
+
|
84
|
+
@rule_sets.append_subdir_gitignore(relative_path: parent_path + '.gitignore', check_exists: check_exists)
|
85
|
+
|
86
|
+
@loaded_gitignore_files << parent_path
|
87
|
+
end
|
88
|
+
|
89
|
+
def each_recursive(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
90
|
+
children = ::Dir.children(parent_full_path)
|
91
|
+
load_gitignore(parent_relative_path, check_exists: false) if @gitignore_enabled && children.include?('.gitignore')
|
92
|
+
|
93
|
+
children.each do |filename|
|
66
94
|
begin
|
67
95
|
full_path = parent_full_path + filename
|
68
96
|
relative_path = parent_relative_path + filename
|
69
|
-
dir =
|
97
|
+
dir = @follow_symlinks_method.call(full_path).directory?
|
70
98
|
|
71
|
-
next unless @rule_sets.
|
99
|
+
next unless @rule_sets.allowed_unrecursive?(relative_path, dir, full_path, filename)
|
72
100
|
|
73
101
|
if dir
|
74
102
|
each_recursive(full_path + '/', relative_path + '/', &block)
|