fast_ignore 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9992ae13ee95ee05f55cceda56e5bd1c4210d5817be17d9116193787ac658d38
4
- data.tar.gz: '093e221a737476e92c0c6cf1b6632abb4da103ea01ee3d0f66acf067011f092c'
3
+ metadata.gz: 88d6249cc842976d1b5c0e984816cfc260cc3edcbe4774ea4580e934bedcf925
4
+ data.tar.gz: 3ab13efc019bee9b5cadcb45f485021132f07787bce73d66bb9ef63b76bc51ca
5
5
  SHA512:
6
- metadata.gz: 78db62b2bfa57b7b509b6f326f3b4f3f7371336bf30caa46c2d1b3ccc4086e2bbdb7abf3cc843700d4a93ff82f368a829e0c05da0ceae52f0e8ab2af4eaa32bd
7
- data.tar.gz: c0df1d280a71bb7140076d39774a96728f2a1c8e29b756216211467a05eb93f7a3001c4472dc47155d0b57ec40d4d65f83c0f4f813014c01f55f7b9e03ffac67
6
+ metadata.gz: 853124ae1ddf1fc599e8a422580ee84ff705907a70c7ece17db72502a4468ca0d5479f918c175f91dc1915c7cccb5ad4cd7eaa956adf2d7b0a5e2fa024821c3d
7
+ data.tar.gz: b90c827caa6c24c3491237ec5fb538b828b2daea879a4d94d6bc3ac4368807c7040bc0d6ea6405b8e9712483d00fddfb6d43e1a970786f374144a4d6165f59ee
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.16.0
2
+ - 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.
3
+ - Add ruby 3 to the test matrix
4
+
1
5
  # v0.15.2
2
6
  - Updated methods with multiple `_` arguments to have different names to make sorbet happy
3
7
 
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.4-3.0.0.preview1 & jruby
18
+ - Supports ruby 2.4-3.0.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))
@@ -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
@@ -15,13 +15,13 @@ class FastIgnore
15
15
 
16
16
  def gitconfig_gitignore_path(config_path)
17
17
  return unless config_path
18
- return unless ::File.exist?(config_path)
18
+ return unless ::File.readable?(config_path)
19
19
 
20
- ignore_path = ::File.readlines(config_path).find { |l| l.sub!(/\A\s*excludesfile\s*=/, '') }
20
+ ignore_path = ::FastIgnore::GitconfigParser.parse(config_path)
21
21
  return unless ignore_path
22
22
 
23
23
  ignore_path.strip!
24
- return ignore_path if ignore_path.empty? # don't expand path in this case
24
+ return '' if ignore_path.empty? # don't expand path in this case
25
25
 
26
26
  ::File.expand_path(ignore_path)
27
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.15.2'
4
+ VERSION = '0.16.0'
5
5
  end
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:
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.15.2
4
+ version: 0.16.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: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2021-12-09 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,9 @@ 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'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: rubocop-rspec
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +128,20 @@ dependencies:
122
128
  - - "~>"
123
129
  - !ruby/object:Gem::Version
124
130
  version: 0.18.5
131
+ - !ruby/object:Gem::Dependency
132
+ name: simplecov-console
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
125
145
  - !ruby/object:Gem::Dependency
126
146
  name: spellr
127
147
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +156,7 @@ dependencies:
136
156
  - - ">="
137
157
  - !ruby/object:Gem::Version
138
158
  version: 0.8.3
139
- description:
159
+ description:
140
160
  email:
141
161
  - robot@dana.sh
142
162
  executables: []
@@ -149,6 +169,7 @@ files:
149
169
  - lib/fast_ignore.rb
150
170
  - lib/fast_ignore/backports.rb
151
171
  - lib/fast_ignore/file_root.rb
172
+ - lib/fast_ignore/gitconfig_parser.rb
152
173
  - lib/fast_ignore/gitignore_include_rule_builder.rb
153
174
  - lib/fast_ignore/gitignore_rule_builder.rb
154
175
  - lib/fast_ignore/gitignore_rule_regexp_builder.rb
@@ -168,7 +189,7 @@ metadata:
168
189
  homepage_uri: https://github.com/robotdana/fast_ignore
169
190
  source_code_uri: https://github.com/robotdana/fast_ignore
170
191
  changelog_uri: https://github.com/robotdana/fast_ignore/blob/main/CHANGELOG.md
171
- post_install_message:
192
+ post_install_message:
172
193
  rdoc_options: []
173
194
  require_paths:
174
195
  - lib
@@ -183,8 +204,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
204
  - !ruby/object:Gem::Version
184
205
  version: '0'
185
206
  requirements: []
186
- rubygems_version: 3.1.2
187
- signing_key:
207
+ rubygems_version: 3.2.15
208
+ signing_key:
188
209
  specification_version: 4
189
210
  summary: Parse gitignore files, quickly
190
211
  test_files: []