fast_ignore 0.15.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|