fast_ignore 0.15.2 → 0.16.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 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: []