fast_ignore 0.15.2 → 0.17.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 +16 -0
- data/README.md +66 -15
- data/lib/fast_ignore/gitconfig_parser.rb +207 -0
- data/lib/fast_ignore/global_gitignore.rb +55 -17
- data/lib/fast_ignore/rule_set.rb +3 -3
- data/lib/fast_ignore/rule_sets.rb +3 -3
- data/lib/fast_ignore/shebang_rule.rb +2 -5
- data/lib/fast_ignore/version.rb +1 -1
- data/lib/fast_ignore.rb +43 -32
- metadata +43 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78cb85cea0888d67c91de09bd54ad644510167c993dc355d187593003d341393
|
4
|
+
data.tar.gz: fb1fda9c1675ee28f4a6ff23191f95c2dd6e497ca2a70c4791dc5dfb85baecd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10531a1d819b74fcc442ce7591be124430d3c55c55afbccb79ba295dd89c0e85e4a1a0a75b11996557057fa2f5dbad71e9284789143395d229e3abbbbd42ba3c
|
7
|
+
data.tar.gz: d0b07a2757c3b962a59973581234ec9e346717d24cdbd5443073c0fd14f840fb287419d63eeb31d4aa52d3fff2e1c0842cc119ed52e836cd55ed73bcd29a3fc7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# v0.17.0
|
2
|
+
- allow overriding `exists:` in `allowed?`
|
3
|
+
- allow setting `include_directories: true` in `allowed?`
|
4
|
+
- subsequent calls to `allowed?` with the same path but different `directory:` or `content:` arguments won't potentially mess up the cache
|
5
|
+
- slight performance improvements of the shebang rule matcher loading the first line
|
6
|
+
- drop support for ruby 2.4
|
7
|
+
- add ruby 3.1 to the test matrix
|
8
|
+
|
9
|
+
# v0.16.1
|
10
|
+
- respect GIT_CONFIG_SYSTEM, GIT_CONFIG_NOSYSTEM and GIT_CONFIG_GLOBAL env vars the same way git does
|
11
|
+
- make the tests more resilient to whatever global config is going on.
|
12
|
+
|
13
|
+
# v0.16.0
|
14
|
+
- Entirely rewrite the way that git config files are read. previously it was just a regexp. now we actually parse git config files according to the same rules as git.
|
15
|
+
- Add ruby 3 to the test matrix
|
16
|
+
|
1
17
|
# v0.15.2
|
2
18
|
- Updated methods with multiple `_` arguments to have different names to make sorbet happy
|
3
19
|
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
|
|
15
15
|
## Features
|
16
16
|
|
17
17
|
- Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
|
18
|
-
- Supports ruby 2.
|
18
|
+
- Supports ruby 2.5-3.1.x & jruby
|
19
19
|
- supports all [gitignore rule patterns](https://git-scm.com/docs/gitignore#_pattern_format)
|
20
20
|
- doesn't require git to be installed
|
21
21
|
- supports a gitignore-esque "include" patterns. ([`include_rules:`](#include_rules)/[`include_files:`](#include_files))
|
@@ -81,20 +81,79 @@ Relative paths will be considered relative to the [`root:`](#root) directory, no
|
|
81
81
|
|
82
82
|
This is aliased as `===` so you can use a FastIgnore instance in case statements.
|
83
83
|
```ruby
|
84
|
+
@path_matcher ||= FastIgnore.new
|
85
|
+
|
84
86
|
case my_path
|
85
|
-
when
|
87
|
+
when @path_matcher
|
86
88
|
puts(my_path)
|
87
89
|
end
|
88
90
|
```
|
89
91
|
|
90
92
|
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
93
|
|
92
|
-
|
94
|
+
#### directory: true/false/nil
|
95
|
+
|
96
|
+
If your code already knows the path to test is/not a directory or wants to lie about whether it is/is not a directory, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
|
97
|
+
|
98
|
+
```
|
99
|
+
FastIgnore.new.allowed?('relative/path', directory: false) # matches `path` as a file
|
100
|
+
FastIgnore.new.allowed?('relative/path', directory: true) # matches `path` as a directory
|
101
|
+
FastIgnore.new.allowed?('relative/path', directory: nil) # matches path as whatever it is on the filesystem
|
102
|
+
FastIgnore.new.allowed?('relative/path) # or as a file if it doesn't exist on the file system
|
103
|
+
```
|
104
|
+
|
105
|
+
#### content: true/false/nil
|
106
|
+
|
107
|
+
default: `nil`
|
108
|
+
|
109
|
+
If your code already knows the path to test is has a particular text content or wants to lie about the content, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
|
110
|
+
|
111
|
+
```
|
112
|
+
FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env ruby\n\nputs 'hello'") # matches ruby shebang
|
113
|
+
FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env bash\n\necho 'hello'") # matches bash shebang
|
114
|
+
FastIgnore.new.allowed?('relative/path', content: nil) # matches path as whatever content is on the filesystem
|
115
|
+
FastIgnore.new.allowed?('relative/path) # or as an empty file if it doesn't actually exist
|
116
|
+
```
|
117
|
+
|
118
|
+
#### content: true/false/nil
|
119
|
+
|
120
|
+
default: `nil`
|
121
|
+
|
122
|
+
If your code already knows the path to test is has a particular text content or wants to lie about the content, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
|
123
|
+
|
124
|
+
```
|
125
|
+
FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env ruby\n\nputs 'hello'") # matches ruby shebang
|
126
|
+
FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env bash\n\necho 'hello'") # matches bash shebang
|
127
|
+
FastIgnore.new.allowed?('relative/path', content: nil) # matches path as whatever content is on the filesystem
|
128
|
+
FastIgnore.new.allowed?('relative/path) # or as an empty file if it doesn't actually exist
|
129
|
+
```
|
93
130
|
|
94
|
-
|
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.
|
131
|
+
#### exist: true/false/nil
|
97
132
|
|
133
|
+
default: `nil`
|
134
|
+
|
135
|
+
If your code already knows the path to test exists or wants to lie about its existence, you can pass `exists: true` or `exists: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `exists: nil` or nothing)
|
136
|
+
|
137
|
+
```
|
138
|
+
FastIgnore.new.allowed?('relative/path', exists: true) # will check the path regardless of whether it actually truly exists
|
139
|
+
FastIgnore.new.allowed?('relative/path', exists: false) # will always return false
|
140
|
+
FastIgnore.new.allowed?('relative/path', exists: nil) # asks the filesystem
|
141
|
+
FastIgnore.new.allowed?('relative/path) # asks the filesystem
|
142
|
+
```
|
143
|
+
|
144
|
+
#### include_directories: true/false
|
145
|
+
|
146
|
+
default: `false`
|
147
|
+
|
148
|
+
By default a file must not be a directory for it to be considered allowed. This is intended to match the behaviour of `git ls-files` which only lists files.
|
149
|
+
|
150
|
+
To match directories you can pass `include_directories: true` to `allowed?`
|
151
|
+
|
152
|
+
```
|
153
|
+
FastIgnore.new.allowed?('relative/path', include_directories: true) # will test the path even if it's a directory
|
154
|
+
FastIgnore.new.allowed?('relative/path', include_directories: false) # will always return false if the path is a directory
|
155
|
+
FastIgnore.new.allowed?('relative/path) # will always return false if the path is a directory
|
156
|
+
```
|
98
157
|
|
99
158
|
### `relative: true`
|
100
159
|
|
@@ -293,15 +352,6 @@ FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
|
|
293
352
|
```
|
294
353
|
|
295
354
|
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.
|
296
|
-
|
297
|
-
### optimising #allowed?
|
298
|
-
|
299
|
-
To avoid unnecessary calls to the filesystem, if your code already knows whether or not it's a directory, or if you're checking shebangs and you have already read the content of the file: use
|
300
|
-
```ruby
|
301
|
-
FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/ruby\n\nputs 'ok'\n")
|
302
|
-
```
|
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.
|
304
|
-
|
305
355
|
## Limitations
|
306
356
|
- Doesn't know what to do if you change the current working directory inside the [`FastIgnore#each`](#each_map_etc) block.
|
307
357
|
So don't do that.
|
@@ -309,6 +359,7 @@ This is not required, and if FastIgnore does have to go to the filesystem for th
|
|
309
359
|
(It does handle changing the current working directory between [`FastIgnore#allowed?`](#allowed) calls)
|
310
360
|
- FastIgnore always matches patterns case-insensitively. (git varies by filesystem).
|
311
361
|
- 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).
|
362
|
+
- git has a system-wide config file installed at `$(prefix)/etc/gitconfig`, where `prefix` is defined for git at install time. FastIgnore assumes that it will always be `/usr/local/etc/gitconfig`. if it's important your system config file is looked at, as that's where you have the core.excludesfile defined, use git's built-in way to override this by adding `export GIT_CONFIG_SYSTEM='/the/actual/location'` to your shell profile.
|
312
363
|
- 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
364
|
- Tracked files that were committed before the matching ignore rule was committed will be returned by `git ls-files`, but not by FastIgnore.
|
314
365
|
- Untracked files will be returned by FastIgnore, but not by `git ls-files`
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
class FastIgnore
|
5
|
+
class GitconfigParseError < FastIgnore::Error; end
|
6
|
+
|
7
|
+
class GitconfigParser # rubocop:disable Metrics/ClassLength
|
8
|
+
def self.parse(file, root: Dir.pwd, nesting: 1)
|
9
|
+
new(file, root: root, nesting: nesting).parse
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(path, root: Dir.pwd, nesting: 1)
|
13
|
+
@path = path
|
14
|
+
@root = root
|
15
|
+
@nesting = nesting
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse
|
19
|
+
raise ::FastIgnore::GitconfigParseError if nesting >= 10
|
20
|
+
|
21
|
+
read_file(path)
|
22
|
+
return unless value
|
23
|
+
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :nesting
|
30
|
+
attr_reader :path
|
31
|
+
attr_reader :root
|
32
|
+
attr_accessor :value
|
33
|
+
attr_accessor :within_quotes
|
34
|
+
attr_accessor :section
|
35
|
+
|
36
|
+
def read_file(path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
37
|
+
return unless ::File.readable?(path)
|
38
|
+
|
39
|
+
file = StringScanner.new(::File.read(path))
|
40
|
+
|
41
|
+
until file.eos?
|
42
|
+
if file.skip(/(\s+|[#;].*\n)/)
|
43
|
+
# skip
|
44
|
+
elsif file.skip(/\[core\]/i)
|
45
|
+
self.section = :core
|
46
|
+
elsif file.skip(/\[include\]/i)
|
47
|
+
self.section = :include
|
48
|
+
elsif file.skip(/\[(?i:includeif) +"/)
|
49
|
+
self.section = include_if(file) ? :include : :not_include
|
50
|
+
elsif file.skip(/\[[\w.]+( "([^\0\\"]|\\(\\{2})*"|\\{2}*)+")?\]/)
|
51
|
+
self.section = :other
|
52
|
+
elsif section == :core && file.skip(/excludesfile\s*=(\s|\\\n)*/i)
|
53
|
+
self.value = scan_value(file)
|
54
|
+
elsif section == :include && file.skip(/path\s*=(\s|\\\n)*/)
|
55
|
+
include_path = scan_value(file)
|
56
|
+
|
57
|
+
value = ::FastIgnore::GitconfigParser.parse(
|
58
|
+
::File.expand_path(include_path, ::File.dirname(path)),
|
59
|
+
root: root,
|
60
|
+
nesting: nesting + 1
|
61
|
+
)
|
62
|
+
self.value = value if value
|
63
|
+
self.section = :include
|
64
|
+
elsif file.skip(/[a-zA-Z0-9]\w*\s*([#;].*)?\n/)
|
65
|
+
nil
|
66
|
+
elsif file.skip(/[a-zA-Z0-9]\w*\s*=(\s|\\\n)*/)
|
67
|
+
skip_value(file)
|
68
|
+
else
|
69
|
+
raise ::FastIgnore::GitconfigParseError
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def scan_condition_value(file)
|
75
|
+
if file.scan(/([^\0\\\n"]|\\(\\{2})*"|\\{2}*)+(?="\])/)
|
76
|
+
value = file.matched
|
77
|
+
file.skip(/"\]/)
|
78
|
+
value
|
79
|
+
else
|
80
|
+
raise ::FastIgnore::GitconfigParseError
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def skip_condition_value(file)
|
85
|
+
raise ::FastIgnore::GitconfigParseError unless file.skip(/([^\0\\\n"]|\\(\\{2})*"|\\{2}*)+"\]/)
|
86
|
+
end
|
87
|
+
|
88
|
+
def include_if(file)
|
89
|
+
if file.skip(/onbranch:/)
|
90
|
+
on_branch?(scan_condition_value(file))
|
91
|
+
elsif file.skip(/gitdir:/)
|
92
|
+
gitdir?(scan_condition_value(file), path: path)
|
93
|
+
elsif file.skip(%r{gitdir/i:})
|
94
|
+
gitdir?(scan_condition_value(file), case_insensitive: true, path: path)
|
95
|
+
else
|
96
|
+
skip_condition_value(file)
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_branch?(branch_pattern)
|
102
|
+
branch_pattern += '**' if branch_pattern.end_with?('/')
|
103
|
+
current_branch = ::File.readable?("#{root}/.git/HEAD") && ::File.read("#{root}/.git/HEAD").sub!(
|
104
|
+
%r{\Aref: refs/heads/}, ''
|
105
|
+
)
|
106
|
+
return unless current_branch
|
107
|
+
|
108
|
+
# goddamit git what does 'a pattern with standard globbing wildcards' mean
|
109
|
+
::File.fnmatch(branch_pattern, current_branch, ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH)
|
110
|
+
end
|
111
|
+
|
112
|
+
def gitdir?(gitdir, path:, case_insensitive: false)
|
113
|
+
gitdir += '**' if gitdir.end_with?('/')
|
114
|
+
gitdir.sub!(%r{\A~/}, ENV['HOME'] + '/')
|
115
|
+
gitdir.sub!(/\A\./, path + '/')
|
116
|
+
gitdir = "**/#{gitdir}" unless gitdir.start_with?('/')
|
117
|
+
options = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH
|
118
|
+
options |= ::File::FNM_CASEFOLD if case_insensitive
|
119
|
+
::File.fnmatch(gitdir, ::File.join(root, '.git'), options)
|
120
|
+
end
|
121
|
+
|
122
|
+
def scan_value(file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
123
|
+
value = +''
|
124
|
+
until file.eos?
|
125
|
+
if file.skip(/\\\n/)
|
126
|
+
# continue
|
127
|
+
elsif file.skip(/\\\\/)
|
128
|
+
value << '\\'
|
129
|
+
elsif file.skip(/\\n/)
|
130
|
+
value << "\n"
|
131
|
+
elsif file.skip(/\\t/)
|
132
|
+
value << "\t"
|
133
|
+
elsif file.skip(/\\b/)
|
134
|
+
value.chop!
|
135
|
+
elsif file.skip(/\\"/)
|
136
|
+
value << '"'
|
137
|
+
elsif file.skip(/\\/)
|
138
|
+
raise ::FastIgnore::GitconfigParseError
|
139
|
+
elsif within_quotes
|
140
|
+
if file.skip(/"/)
|
141
|
+
self.within_quotes = false
|
142
|
+
elsif file.scan(/[^"\\\n]+/)
|
143
|
+
value << file.matched
|
144
|
+
elsif file.skip(/\n/)
|
145
|
+
raise ::FastIgnore::GitconfigParseError
|
146
|
+
# :nocov:
|
147
|
+
else
|
148
|
+
raise "Unmatched #{file.rest}"
|
149
|
+
# :nocov:
|
150
|
+
end
|
151
|
+
elsif file.skip(/"/)
|
152
|
+
self.within_quotes = true
|
153
|
+
elsif file.scan(/[^;#"\s\\]+/)
|
154
|
+
value << file.matched
|
155
|
+
elsif file.skip(/\s*[;#\n]/)
|
156
|
+
break
|
157
|
+
elsif file.scan(/\s+/) # rubocop:disable Lint/DuplicateBranch
|
158
|
+
value << file.matched
|
159
|
+
# :nocov:
|
160
|
+
else
|
161
|
+
raise "Unmatched #{file.rest}"
|
162
|
+
# :nocov:
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
raise ::FastIgnore::GitconfigParseError if within_quotes
|
167
|
+
|
168
|
+
value
|
169
|
+
end
|
170
|
+
|
171
|
+
def skip_value(file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
172
|
+
until file.eos?
|
173
|
+
if file.skip(/\\(?:\n|\\|n|t|b|")/)
|
174
|
+
nil
|
175
|
+
elsif file.skip(/\\/)
|
176
|
+
raise ::FastIgnore::GitconfigParseError
|
177
|
+
elsif within_quotes
|
178
|
+
if file.skip(/"/)
|
179
|
+
self.within_quotes = false
|
180
|
+
elsif file.skip(/[^"\\\n]+/)
|
181
|
+
nil
|
182
|
+
elsif file.scan(/\n/)
|
183
|
+
raise ::FastIgnore::GitconfigParseError
|
184
|
+
# :nocov:
|
185
|
+
else
|
186
|
+
raise "Unmatched #{file.rest}"
|
187
|
+
# :nocov:
|
188
|
+
end
|
189
|
+
elsif file.skip(/"/)
|
190
|
+
self.within_quotes = true
|
191
|
+
elsif file.skip(/[^;#"\s\\]+/) # rubocop:disable Lint/DuplicateBranch
|
192
|
+
nil
|
193
|
+
elsif file.skip(/\s*[;#\n]/)
|
194
|
+
break
|
195
|
+
elsif file.skip(/\s+/) # rubocop:disable Lint/DuplicateBranch
|
196
|
+
nil
|
197
|
+
# :nocov:
|
198
|
+
else
|
199
|
+
raise "Unmatched #{file.rest}"
|
200
|
+
# :nocov:
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
raise ::FastIgnore::GitconfigParseError if within_quotes
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -4,46 +4,84 @@ class FastIgnore
|
|
4
4
|
module GlobalGitignore
|
5
5
|
class << self
|
6
6
|
def path(root:)
|
7
|
-
|
8
|
-
gitconfig_gitignore_path(::File.expand_path('~/.gitconfig')) ||
|
9
|
-
gitconfig_gitignore_path(xdg_config_path) ||
|
10
|
-
gitconfig_gitignore_path('/etc/gitconfig') ||
|
7
|
+
ignore_path = gitconfigs_gitignore_path(root) ||
|
11
8
|
default_global_gitignore_path
|
9
|
+
|
10
|
+
ignore_path unless ignore_path.empty?
|
12
11
|
end
|
13
12
|
|
14
13
|
private
|
15
14
|
|
15
|
+
def gitconfigs_gitignore_path(root)
|
16
|
+
gitconfig_gitignore_path(repo_config_path(root)) ||
|
17
|
+
gitconfig_gitignore_path(global_config_path) ||
|
18
|
+
gitconfig_gitignore_path(default_user_config_path) ||
|
19
|
+
gitconfig_gitignore_path(system_config_path)
|
20
|
+
rescue ::FastIgnore::GitconfigParseError
|
21
|
+
''
|
22
|
+
end
|
23
|
+
|
16
24
|
def gitconfig_gitignore_path(config_path)
|
17
25
|
return unless config_path
|
18
|
-
return unless ::File.
|
26
|
+
return unless ::File.readable?(config_path)
|
19
27
|
|
20
|
-
ignore_path = ::
|
28
|
+
ignore_path = ::FastIgnore::GitconfigParser.parse(config_path)
|
21
29
|
return unless ignore_path
|
22
30
|
|
23
31
|
ignore_path.strip!
|
24
|
-
return
|
32
|
+
return '' if ignore_path.empty? # don't expand path in this case
|
25
33
|
|
26
34
|
::File.expand_path(ignore_path)
|
27
35
|
end
|
28
36
|
|
29
|
-
def
|
30
|
-
|
37
|
+
def default_user_config_path
|
38
|
+
return if env('GIT_CONFIG_GLOBAL')
|
39
|
+
|
40
|
+
::File.expand_path('git/config', default_config_home)
|
31
41
|
end
|
32
42
|
|
33
43
|
def default_global_gitignore_path
|
34
|
-
|
35
|
-
|
44
|
+
::File.expand_path('git/ignore', default_config_home)
|
45
|
+
end
|
46
|
+
|
47
|
+
def repo_config_path(root)
|
48
|
+
::File.expand_path('.git/config', root)
|
49
|
+
end
|
50
|
+
|
51
|
+
def global_config_path
|
52
|
+
::File.expand_path(env('GIT_CONFIG_GLOBAL', '~/.gitconfig'))
|
53
|
+
end
|
54
|
+
|
55
|
+
def system_config_path
|
56
|
+
return if env?('GIT_CONFIG_NOSYSTEM')
|
57
|
+
|
58
|
+
::File.expand_path(env('GIT_CONFIG_SYSTEM', '/usr/local/etc/gitconfig'))
|
59
|
+
end
|
60
|
+
|
61
|
+
def default_config_home
|
62
|
+
env('XDG_CONFIG_HOME', '~/.config')
|
63
|
+
end
|
64
|
+
|
65
|
+
def env(env_var, default = nil)
|
66
|
+
value = ::ENV[env_var]
|
67
|
+
|
68
|
+
if value && (not value.empty?)
|
69
|
+
value
|
36
70
|
else
|
37
|
-
|
71
|
+
default
|
38
72
|
end
|
39
73
|
end
|
40
74
|
|
41
|
-
def
|
42
|
-
::ENV[
|
43
|
-
end
|
75
|
+
def env?(env_var)
|
76
|
+
value = ::ENV[env_var]
|
44
77
|
|
45
|
-
|
46
|
-
|
78
|
+
if value&.match?(/\A(yes|on|true|1)\z/i)
|
79
|
+
true
|
80
|
+
elsif !value || value.match?(/\A(no|off|false|0|)\z/i)
|
81
|
+
false
|
82
|
+
else
|
83
|
+
raise ::FastIgnore::GitconfigParseError
|
84
|
+
end
|
47
85
|
end
|
48
86
|
end
|
49
87
|
end
|
data/lib/fast_ignore/rule_set.rb
CHANGED
@@ -11,7 +11,7 @@ class FastIgnore
|
|
11
11
|
@file_rules = (squash ? squash_rules(rules.reject(&:dir_only?)) : rules.reject(&:dir_only?)).freeze
|
12
12
|
@has_shebang_rules = rules.any?(&:shebang?)
|
13
13
|
|
14
|
-
@allowed_recursive = { '.' => true }
|
14
|
+
@allowed_recursive = { ['.', true, nil] => true }
|
15
15
|
@allow = allow
|
16
16
|
@gitignore = gitignore
|
17
17
|
|
@@ -27,8 +27,8 @@ class FastIgnore
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
|
30
|
-
@allowed_recursive.fetch(relative_path) do
|
31
|
-
@allowed_recursive[
|
30
|
+
@allowed_recursive.fetch([relative_path, dir, content]) do |key|
|
31
|
+
@allowed_recursive[key] =
|
32
32
|
allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
|
33
33
|
allowed_unrecursive?(relative_path, dir, full_path, filename, content)
|
34
34
|
end
|
@@ -27,8 +27,8 @@ class FastIgnore
|
|
27
27
|
@array.freeze if @gitignore_rule_set
|
28
28
|
end
|
29
29
|
|
30
|
-
def allowed_recursive?(relative_path, full_path, filename, content)
|
31
|
-
@array.all? { |r| r.allowed_recursive?(relative_path,
|
30
|
+
def allowed_recursive?(relative_path, dir, full_path, filename, content)
|
31
|
+
@array.all? { |r| r.allowed_recursive?(relative_path, dir, full_path, filename, content) }
|
32
32
|
end
|
33
33
|
|
34
34
|
def allowed_unrecursive?(relative_path, dir, full_path, filename)
|
@@ -73,7 +73,7 @@ class FastIgnore
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def build_from_root_gitignore_file(path)
|
76
|
-
return unless ::File.exist?(path)
|
76
|
+
return unless path && ::File.exist?(path)
|
77
77
|
|
78
78
|
build_rule_set(::File.readlines(path), false, gitignore: true)
|
79
79
|
end
|
@@ -61,12 +61,9 @@ class FastIgnore
|
|
61
61
|
|
62
62
|
def first_line(path) # rubocop:disable Metrics/MethodLength
|
63
63
|
file = ::File.new(path)
|
64
|
-
first_line =
|
64
|
+
first_line = file.sysread(64)
|
65
65
|
if first_line.start_with?('#!')
|
66
|
-
|
67
|
-
new_fragment = file.sysread(64)
|
68
|
-
first_line += new_fragment
|
69
|
-
end
|
66
|
+
first_line += file.readline unless first_line.match?(/\n/)
|
70
67
|
else
|
71
68
|
file.close
|
72
69
|
return
|
data/lib/fast_ignore/version.rb
CHANGED
data/lib/fast_ignore.rb
CHANGED
@@ -4,22 +4,24 @@ require_relative './fast_ignore/backports'
|
|
4
4
|
|
5
5
|
require 'set'
|
6
6
|
require 'strscan'
|
7
|
-
require_relative './fast_ignore/rule_sets'
|
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'
|
16
|
-
require_relative './fast_ignore/rule'
|
17
|
-
require_relative './fast_ignore/unmatchable_rule'
|
18
|
-
require_relative './fast_ignore/shebang_rule'
|
19
7
|
|
20
8
|
class FastIgnore
|
21
9
|
class Error < StandardError; end
|
22
10
|
|
11
|
+
require_relative './fast_ignore/rule_sets'
|
12
|
+
require_relative './fast_ignore/rule_set'
|
13
|
+
require_relative './fast_ignore/global_gitignore'
|
14
|
+
require_relative './fast_ignore/rule_builder'
|
15
|
+
require_relative './fast_ignore/gitignore_rule_builder'
|
16
|
+
require_relative './fast_ignore/gitignore_include_rule_builder'
|
17
|
+
require_relative './fast_ignore/gitignore_rule_regexp_builder'
|
18
|
+
require_relative './fast_ignore/gitignore_rule_scanner'
|
19
|
+
require_relative './fast_ignore/file_root'
|
20
|
+
require_relative './fast_ignore/rule'
|
21
|
+
require_relative './fast_ignore/unmatchable_rule'
|
22
|
+
require_relative './fast_ignore/shebang_rule'
|
23
|
+
require_relative './fast_ignore/gitconfig_parser'
|
24
|
+
|
23
25
|
include ::Enumerable
|
24
26
|
|
25
27
|
# :nocov:
|
@@ -47,19 +49,30 @@ class FastIgnore
|
|
47
49
|
each_recursive(root_from_pwd, '', &block)
|
48
50
|
end
|
49
51
|
|
50
|
-
def allowed?(path, directory: nil, content: nil)
|
52
|
+
def allowed?(path, directory: nil, content: nil, exists: nil, include_directories: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
51
53
|
full_path = ::File.expand_path(path, @root)
|
52
54
|
return false unless full_path.start_with?(@root)
|
53
|
-
|
55
|
+
|
56
|
+
begin
|
57
|
+
directory = directory.nil? ? @follow_symlinks_method.call(full_path).directory? : directory
|
58
|
+
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
59
|
+
exists = false if exists.nil?
|
60
|
+
directory = false
|
61
|
+
end
|
62
|
+
|
63
|
+
return false if !include_directories && directory
|
64
|
+
|
65
|
+
exists = exists.nil? ? ::File.exist?(full_path) : exists
|
66
|
+
|
67
|
+
return false unless exists
|
54
68
|
|
55
69
|
relative_path = full_path.delete_prefix(@root)
|
56
70
|
load_gitignore_recursive(relative_path) if @gitignore_enabled
|
57
71
|
|
58
72
|
filename = ::File.basename(relative_path)
|
73
|
+
content = content.slice(/.*/) if content # we only care about the first line
|
59
74
|
|
60
|
-
@rule_sets.allowed_recursive?(relative_path, full_path, filename, content)
|
61
|
-
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
62
|
-
false
|
75
|
+
@rule_sets.allowed_recursive?(relative_path, directory, full_path, filename, content)
|
63
76
|
end
|
64
77
|
alias_method :===, :allowed?
|
65
78
|
|
@@ -75,7 +88,7 @@ class FastIgnore
|
|
75
88
|
paths << path
|
76
89
|
end
|
77
90
|
|
78
|
-
paths.reverse_each(
|
91
|
+
paths.reverse_each { |p| load_gitignore(p) }
|
79
92
|
end
|
80
93
|
|
81
94
|
def load_gitignore(parent_path, check_exists: true)
|
@@ -91,21 +104,19 @@ class FastIgnore
|
|
91
104
|
load_gitignore(parent_relative_path, check_exists: false) if @gitignore_enabled && children.include?('.gitignore')
|
92
105
|
|
93
106
|
children.each do |filename|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
yield(@relative ? relative_path : @root + relative_path)
|
105
|
-
end
|
106
|
-
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
107
|
-
nil
|
107
|
+
full_path = parent_full_path + filename
|
108
|
+
relative_path = parent_relative_path + filename
|
109
|
+
dir = @follow_symlinks_method.call(full_path).directory?
|
110
|
+
|
111
|
+
next unless @rule_sets.allowed_unrecursive?(relative_path, dir, full_path, filename)
|
112
|
+
|
113
|
+
if dir
|
114
|
+
each_recursive(full_path + '/', relative_path + '/', &block)
|
115
|
+
else
|
116
|
+
yield(@relative ? relative_path : @root + relative_path)
|
108
117
|
end
|
118
|
+
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
119
|
+
nil
|
109
120
|
end
|
110
121
|
end
|
111
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_ignore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.17.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dana Sherson
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -87,6 +87,9 @@ dependencies:
|
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 0.93.1
|
90
|
+
- - "<"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '1.12'
|
90
93
|
type: :development
|
91
94
|
prerelease: false
|
92
95
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -94,6 +97,23 @@ dependencies:
|
|
94
97
|
- - ">="
|
95
98
|
- !ruby/object:Gem::Version
|
96
99
|
version: 0.93.1
|
100
|
+
- - "<"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.12'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rubocop-rake
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
97
117
|
- !ruby/object:Gem::Dependency
|
98
118
|
name: rubocop-rspec
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +142,20 @@ dependencies:
|
|
122
142
|
- - "~>"
|
123
143
|
- !ruby/object:Gem::Version
|
124
144
|
version: 0.18.5
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: simplecov-console
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
125
159
|
- !ruby/object:Gem::Dependency
|
126
160
|
name: spellr
|
127
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,7 +170,7 @@ dependencies:
|
|
136
170
|
- - ">="
|
137
171
|
- !ruby/object:Gem::Version
|
138
172
|
version: 0.8.3
|
139
|
-
description:
|
173
|
+
description:
|
140
174
|
email:
|
141
175
|
- robot@dana.sh
|
142
176
|
executables: []
|
@@ -149,6 +183,7 @@ files:
|
|
149
183
|
- lib/fast_ignore.rb
|
150
184
|
- lib/fast_ignore/backports.rb
|
151
185
|
- lib/fast_ignore/file_root.rb
|
186
|
+
- lib/fast_ignore/gitconfig_parser.rb
|
152
187
|
- lib/fast_ignore/gitignore_include_rule_builder.rb
|
153
188
|
- lib/fast_ignore/gitignore_rule_builder.rb
|
154
189
|
- lib/fast_ignore/gitignore_rule_regexp_builder.rb
|
@@ -168,7 +203,7 @@ metadata:
|
|
168
203
|
homepage_uri: https://github.com/robotdana/fast_ignore
|
169
204
|
source_code_uri: https://github.com/robotdana/fast_ignore
|
170
205
|
changelog_uri: https://github.com/robotdana/fast_ignore/blob/main/CHANGELOG.md
|
171
|
-
post_install_message:
|
206
|
+
post_install_message:
|
172
207
|
rdoc_options: []
|
173
208
|
require_paths:
|
174
209
|
- lib
|
@@ -176,15 +211,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
211
|
requirements:
|
177
212
|
- - ">="
|
178
213
|
- !ruby/object:Gem::Version
|
179
|
-
version: 2.
|
214
|
+
version: 2.5.0
|
180
215
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
216
|
requirements:
|
182
217
|
- - ">="
|
183
218
|
- !ruby/object:Gem::Version
|
184
219
|
version: '0'
|
185
220
|
requirements: []
|
186
|
-
rubygems_version: 3.
|
187
|
-
signing_key:
|
221
|
+
rubygems_version: 3.2.15
|
222
|
+
signing_key:
|
188
223
|
specification_version: 4
|
189
224
|
summary: Parse gitignore files, quickly
|
190
225
|
test_files: []
|