git_ls 0.4.0 → 0.5.1

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: 4ef6ee0fb0ab70df2190d65de7389011c84036fff2993ebf65d90913746f7ff3
4
- data.tar.gz: 25c8eb1f6d58e830311b531f3aedb5f5302801bec42aded9e3df8d8e06f938a1
3
+ metadata.gz: 723546fbde74cd69c51631aafbb7fee4b4dbe12915f969855dcc6d1b577834bf
4
+ data.tar.gz: 3b9d5b1c58209e1a799864a71a875b059eef1e315c53e56351c44026f4c07931
5
5
  SHA512:
6
- metadata.gz: 31f293dba3526adcad121f36dd7afe09ca114a559581566e25ea6f60b071c566f29840aa2be46b61f6c3291f3da896b81f9b199e341ea535bb25695ae67381e8
7
- data.tar.gz: 79401fdfd2f6ac2497f3554ceff5d27b9e690f31e2395dbf84a429f4ae8b2c257cd3c6a4cff35f3f5768f662566951edb1e28f59b8efe4abeb22f4ca9569b922
6
+ metadata.gz: a4d9fb5d7c4f84057007dbc9052c011f165e191c0c7e4bfc533cf9319ce61f8103d65b936abc884e44fd1ecae666d665c160f9ab39a2f35ec1f115a871a8fd9c
7
+ data.tar.gz: 71a87a2b4946e2a499dba2c673507951a95eb4fb8a499f3737d037ed428121314d2c5e7fef0356bc8ea7070f4877e69f6b0ed13bd4cffaecacd8a0b757b26d56
@@ -0,0 +1,82 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ rspec:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2, 3.3, 3.4, ruby-head, jruby, jruby-head]
17
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
25
+ - run: bundle exec rspec
26
+
27
+ coverage:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: ruby/setup-ruby@v1
32
+ with:
33
+ ruby-version: 3.4
34
+ bundler-cache: true
35
+ - run: COVERAGE=1 bundle exec rspec
36
+
37
+ - uses: actions/upload-artifact@v4
38
+ with:
39
+ name: coverage-report
40
+ path: 'coverage/*'
41
+ retention-days: 1
42
+ if: ${{ failure() }}
43
+
44
+ rubocop:
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ - uses: ruby/setup-ruby@v1
49
+ with:
50
+ ruby-version: 3.4
51
+ bundler-cache: true
52
+ - run: bundle exec rubocop
53
+
54
+ spellr:
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+ - uses: ruby/setup-ruby@v1
59
+ with:
60
+ ruby-version: 3.4
61
+ bundler-cache: true
62
+ - run: bundle exec spellr
63
+
64
+ leftovers:
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ - uses: ruby/setup-ruby@v1
69
+ with:
70
+ ruby-version: 3.4
71
+ bundler-cache: true
72
+ - run: bundle exec leftovers
73
+
74
+ build:
75
+ runs-on: ubuntu-latest
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+ - uses: ruby/setup-ruby@v1
79
+ with:
80
+ ruby-version: 3.4
81
+ bundler-cache: true
82
+ - run: bundle exec rake build
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  /.rspec_status
12
12
  /.ruby-version
13
13
  Gemfile.lock
14
+ /sorbet/rbi/hidden-definitions/errors.txt
data/.leftovers.yml CHANGED
@@ -1,2 +1,3 @@
1
1
  exclude_paths:
2
2
  - vendor
3
+ - sorbet
data/.rubocop.yml CHANGED
@@ -1,4 +1,6 @@
1
- require: rubocop-rspec
1
+ require:
2
+ - rubocop-rspec
3
+ - rubocop-rake
2
4
 
3
5
  # Reference:
4
6
  # https://rubocop.readthedocs.io/en/latest/
@@ -113,11 +115,12 @@ Metrics:
113
115
  CountComments: false
114
116
 
115
117
  Metrics/BlockLength:
116
- ExcludedMethods:
118
+ AllowedMethods:
117
119
  - configure
118
120
  - describe
119
121
  - context
120
122
  - shared_examples
123
+ - benchmark
121
124
 
122
125
  Metrics/CyclomaticComplexity:
123
126
  Enabled: false
@@ -125,6 +128,9 @@ Metrics/CyclomaticComplexity:
125
128
  Metrics/PerceivedComplexity:
126
129
  Enabled: false
127
130
 
131
+ Metrics/ClassLength:
132
+ Enabled: false
133
+
128
134
  RSpec:
129
135
  Enabled: true
130
136
  Include:
@@ -140,7 +146,7 @@ RSpec/ExampleLength:
140
146
  RSpec/ExpectActual:
141
147
  Enabled: false
142
148
 
143
- RSpec/FilePath:
149
+ RSpec/SpecFilePathFormat:
144
150
  Enabled: false
145
151
 
146
152
  # Multiple expectations are useful
@@ -224,3 +230,20 @@ Style/HashTransformValues:
224
230
 
225
231
  Style/CommentedKeyword:
226
232
  Enabled: false
233
+
234
+ Style/CaseLikeIf:
235
+ Enabled: false
236
+
237
+ Style/NumericPredicate:
238
+ Enabled: false
239
+
240
+ Style/Semicolon:
241
+ Exclude:
242
+ - bin/benchmark
243
+
244
+ Naming/VariableNumber:
245
+ Enabled: false
246
+
247
+ Lint/Void:
248
+ Exclude:
249
+ - bin/benchmark
data/.simplecov CHANGED
@@ -8,4 +8,8 @@ SimpleCov.start do
8
8
  else
9
9
  minimum_coverage 100
10
10
  end
11
+ self.formatter = SimpleCov::Formatter::MultiFormatter.new([
12
+ SimpleCov::Formatter::HTMLFormatter,
13
+ SimpleCov::Formatter::Console
14
+ ])
11
15
  end
data/.spellr.yml CHANGED
@@ -1,2 +1,3 @@
1
1
  excludes:
2
2
  - vendor
3
+ - sorbet
@@ -1,4 +1,5 @@
1
1
  baz
2
+ bitshift
2
3
  changelog
3
4
  codebase
4
5
  cov
@@ -20,6 +21,7 @@ params
20
21
  pwd
21
22
  quotepath
22
23
  rdoc
24
+ readme
23
25
  rspec
24
26
  rubo
25
27
  rubocop
@@ -27,7 +29,11 @@ rvm
27
29
  sharedindex
28
30
  sherson
29
31
  simplecov
32
+ srb
30
33
  sudo
31
34
  tmp
35
+ txt
32
36
  usr
37
+ utf
38
+ warmup
33
39
  yardoc
@@ -1,2 +1,4 @@
1
1
  ewah
2
+ ips
2
3
  untr
4
+ webpack
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 0.5.1
2
+ - Test with new ruby versions, meaningless performance tweak using bitshift instead of exponent in one place.
3
+
4
+ # 0.5.0
5
+ - Major Performance Improvements
6
+
1
7
  # 0.4.0
2
8
  - Handles split index files
3
9
 
data/Gemfile CHANGED
@@ -5,5 +5,20 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in git_index.gemspec
6
6
  gemspec
7
7
 
8
- gem 'rake', '>= 12.3.3'
9
- gem 'rspec', '~> 3.0'
8
+ group(:development) do
9
+ gem 'benchmark', require: false
10
+ gem 'benchmark-ips', require: false
11
+ gem 'bundler', '>= 2', require: false
12
+ gem 'fast_ignore', '>= 0.15.1', require: false
13
+ gem 'leftovers', '>= 0.4.0', require: false
14
+ gem 'pry', require: false
15
+ gem 'rake', '>= 12.3.3', require: false
16
+ gem 'rspec', require: false
17
+ gem 'rubocop', '>= 0.93.1', require: false
18
+ gem 'rubocop-performance', '>= 1.8.1', require: false
19
+ gem 'rubocop-rake', require: false
20
+ gem 'rubocop-rspec', '>= 1.44.1', require: false
21
+ gem 'simplecov', '>= 0.18.5', require: false
22
+ gem 'simplecov-console', require: false
23
+ gem 'spellr', require: false, platforms: :ruby
24
+ end
data/README.md CHANGED
@@ -2,10 +2,9 @@
2
2
 
3
3
  Parses the .git/index file like `git ls-files` does.
4
4
 
5
- - for small repos (as in, anything smaller than rails),
6
- it can be faster than doing the system call to git
7
- - still takes less than half a second for very large repos e.g. the linux repo
5
+ - faster than doing the system call to git
8
6
  - doesn't require git to be installed
7
+ - tested against ruby 2.4 - 3.4.1 and jruby
9
8
 
10
9
  ## Installation
11
10
 
@@ -23,23 +22,30 @@ Or install it yourself as:
23
22
 
24
23
  $ gem install git_ls
25
24
 
25
+ And require
26
+ ```ruby
27
+ require 'git_ls'
28
+ ```
29
+
26
30
  ## Usage
27
31
 
28
- `GitLS.files` returns an array of filenames, equivalent to `` `git ls-files -z`.split("\0") ``
32
+ `GitLS.files` reads the `.git/index` file to return an array of file paths, equivalent to `` `git ls-files`.split("\n") ``, but faster, and without requiring git being installed.
33
+
29
34
  `GitLS.files("path/to/repo")` if the repo is not $PWD.
30
35
 
36
+ Strictly speaking it's equivalent to `` `git ls-files -c core.quotepath=off -z`.split("\0") ``, handling file paths with spaces and non-ascii characters, and returning file paths as UTF-8 strings.
37
+
31
38
  ## Development
32
39
 
33
- Have a look in the bin dir for some useful tools.
34
- To install this gem onto your local machine, run `bundle exec rake install`.
40
+ - Have a look in the bin dir for some useful tools.
41
+ - To install this gem onto your local machine, run `bundle exec rake install`.
42
+ - Run `rake` to run all tests & linters.
35
43
 
36
44
  ## Contributing
37
45
 
38
46
  Bug reports and pull requests are welcome on GitHub at https://github.com/robotdana/git_ls.
39
47
  If you're comfortable, please attach `.git/index` (and `.git/sharedindex.<sha>` if applicable) and the output of `git ls-files` where it doesn't match.
40
48
 
41
-
42
49
  ## License
43
50
 
44
51
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
45
- # git_ls
data/Rakefile CHANGED
@@ -1,18 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bundler/setup'
3
4
  require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
- require 'rubocop/rake_task'
6
- require 'spellr/rake_task'
7
- require 'leftovers/rake_task'
8
5
 
9
- RuboCop::RakeTask.new
6
+ require 'rspec/core/rake_task'
10
7
  RSpec::Core::RakeTask.new(:spec)
11
- Spellr::RakeTask.generate_task
12
- Leftovers::RakeTask.generate_task
8
+ linters = %i{spec}
9
+
10
+ if RUBY_PLATFORM != 'java'
11
+ require 'rubocop/rake_task'
12
+ RuboCop::RakeTask.new
13
+ linters << :rubocop
13
14
 
14
- if RUBY_PLATFORM == 'java'
15
- task default: %i{spec build}
16
- else
17
- task default: %i{spec rubocop spellr leftovers build}
15
+ require 'spellr/rake_task'
16
+ Spellr::RakeTask.generate_task
17
+ linters << :spellr
18
+
19
+ require 'leftovers/rake_task'
20
+ Leftovers::RakeTask.generate_task
21
+ linters << :leftovers
18
22
  end
23
+
24
+ linters << :build
25
+
26
+ desc 'Run all linters'
27
+ task default: linters
data/git_index.gemspec CHANGED
@@ -26,14 +26,5 @@ Gem::Specification.new do |spec|
26
26
  end
27
27
 
28
28
  spec.require_paths = ['lib']
29
-
30
- spec.add_development_dependency 'bundler', '>= 2'
31
- spec.add_development_dependency 'leftovers'
32
- spec.add_development_dependency 'pry', '> 0'
33
- spec.add_development_dependency 'rake', '>= 12.3.3'
34
- spec.add_development_dependency 'rubocop'
35
- spec.add_development_dependency 'rubocop-performance'
36
- spec.add_development_dependency 'rubocop-rspec'
37
- spec.add_development_dependency 'simplecov', '~> 0.18.5'
38
- spec.add_development_dependency 'spellr'
29
+ spec.metadata['rubygems_mfa_required'] = 'true'
39
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitLS
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.1'
5
5
  end
data/lib/git_ls.rb CHANGED
@@ -3,105 +3,105 @@
3
3
  # Usage:
4
4
  # GitLS.files -> Array of strings as files.
5
5
  # This will be identical output to git ls-files
6
+ require 'stringio'
7
+ require_relative 'git_ls/version'
8
+
6
9
  module GitLS # rubocop:disable Metrics/ModuleLength
7
10
  class Error < StandardError; end
8
11
 
9
12
  class << self
10
- def files(path = ::Dir.pwd)
11
- read(path, false)
12
- end
13
+ def files(path = nil)
14
+ path = path ? ::File.join(path, '.git/index') : '.git/index'
13
15
 
14
- def headers(path = ::Dir.pwd)
15
- read(path, true)
16
+ read(path, false)
16
17
  end
17
18
 
18
19
  private
19
20
 
20
- def read(path, return_headers_only) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
21
- path = ::File.join(path, '.git/index') if ::File.directory?(path)
22
- file = ::File.new(path)
21
+ def read(path, _return_headers_only) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
22
+ begin
23
+ # reading the whole file into memory is faster than lots of ::File#read
24
+ # the biggest it's going to be is 10s of megabytes, well within ram.
25
+ file = ::StringIO.new(::File.read(path, mode: 'rb'))
26
+ rescue ::Errno::ENOENT => e
27
+ raise ::GitLS::Error, "Not a git directory: #{e.message}"
28
+ end
29
+
23
30
  buf = ::String.new
24
31
  # 4-byte signature:
25
32
  # The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache")
26
33
  # 4-byte version number:
27
34
  # The current supported versions are 2, 3 and 4.
28
35
  # 32-bit number of index entries.
29
- sig, git_index_version, length = file.read(12, buf).unpack('a4NN')
30
- raise ::GitLS::Error, ".git/index file not found at #{path}" unless sig == 'DIRC'
36
+ file.read(4, buf)
37
+ sig = buf
38
+ raise ::GitLS::Error, ".git/index file not found at '#{path}'" unless sig == 'DIRC'
39
+
40
+ file.read(4, buf)
41
+ git_index_version = buf.unpack1('N')
31
42
 
32
- return { git_index_version: git_index_version, length: length } if return_headers_only
43
+ file.read(4, buf)
44
+ entries = buf.unpack1('N')
33
45
 
34
- files = ::Array.new(length)
35
- case git_index_version
46
+ files = ::Array.new(entries)
47
+ files = case git_index_version
36
48
  when 2 then files_2(files, file)
37
49
  when 3 then files_3(files, file)
38
50
  when 4 then files_4(files, file)
39
- else raise ::GitLS::Error, 'Unrecognized git index version'
51
+ else raise ::GitLS::Error, "Unrecognized git index version '#{git_index_version}'"
40
52
  end
41
53
 
42
- extensions(files, file, buf)
43
- files
44
- rescue ::Errno::ENOENT => e
45
- raise ::GitLS::Error, "Not a git directory: #{e.message}"
46
- ensure
47
- # :nocov:
48
- # coverage tracking for branches in ensure blocks is weird
49
- file&.close
50
- # :nocov:
51
- files
54
+ read_extensions(files, file, path, buf)
52
55
  end
53
56
 
54
- def extensions(files, file, buf)
55
- case file.read(4, buf)
56
- when 'link' then link_extension(files, file, buf)
57
- when /[A-Z]{4}/ then ignored_extension(files, file, buf)
57
+ def read_extensions(files, file, path, buf) # rubocop:disable Metrics/MethodLength
58
+ extension = file.read(4, buf)
59
+ if extension == 'link'
60
+ read_link_extension(files, file, path, buf)
61
+ elsif extension.match?(/\A[A-Z]{4}\z/)
62
+ size = file.read(4, buf).unpack1('N')
63
+ file.seek(size, 1)
64
+ read_extensions(files, file, path, buf)
58
65
  else
59
- return if (file.pos += 16) && file.eof?
66
+ return files if file.seek(16, 1) && file.eof?
60
67
 
61
- raise ::GitLS::Error, "Unrecognized .git/index extension #{buf.inspect}"
68
+ raise ::GitLS::Error, "Unrecognized .git/index extension #{extension.inspect}"
62
69
  end
63
70
  end
64
71
 
65
- def ignored_extension(files, file, buf)
66
- size = file.read(4, buf).unpack1('N')
67
- file.pos += size
68
- extensions(files, file, buf)
69
- end
70
-
71
- def link_extension(files, file, buf) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
72
- file.pos += 4 # size = file.read(4, buf).unpack1('N')
72
+ def read_link_extension(files, file, path, buf) # rubocop:disable Metrics/MethodLength
73
+ file.seek(4, 1) # skip size
73
74
 
74
75
  sha = file.read(20, buf)
75
76
 
76
- new_files = files.dup
77
-
78
- files.replace files("#{::File.dirname(file.path)}/sharedindex.#{sha.unpack1('H*')}")
77
+ split_files = read("#{::File.dirname(path)}/sharedindex.#{sha.unpack1('H*')}", false)
79
78
 
80
79
  ewah_each_value(file, buf) do |pos|
81
- files[pos] = nil
80
+ split_files[pos] = nil
82
81
  end
83
82
 
84
83
  ewah_each_value(file, buf) do |pos|
85
- replacement_file = new_files.shift
84
+ replacement_file = files.shift
86
85
  # the documentation *implies* that this *may* get a new filename
87
86
  # i can't get it to happen though
88
87
  # :nocov:
89
- files[pos] = replacement_file unless replacement_file.empty?
88
+ split_files[pos] = replacement_file unless replacement_file.empty?
90
89
  # :nocov:
91
90
  end
92
91
 
93
- files.compact!
94
- files.concat(new_files)
95
- files.sort!
92
+ split_files.compact!
93
+ split_files.concat(files)
94
+ split_files.sort!
96
95
 
97
- extensions(files, file, buf)
96
+ read_extensions(split_files, file, path, buf)
98
97
  end
99
98
 
100
99
  # format is defined here:
101
100
  # https://git-scm.com/docs/bitmap-format#_appendix_a_serialization_format_for_an_ewah_bitmap
102
101
  def ewah_each_value(file, buf) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
103
102
  uncompressed_pos = 0
104
- file.pos += 4 # uncompressed_bits_count = file.read(4, buf).unpack1('N')
103
+
104
+ file.seek(4, 1) # skip 4 byte uncompressed_bits_count.
105
105
  compressed_bytes = file.read(4, buf).unpack1('N') * 8
106
106
 
107
107
  final_file_pos = file.pos + compressed_bytes
@@ -110,10 +110,10 @@ module GitLS # rubocop:disable Metrics/ModuleLength
110
110
  run_length_word = file.read(8, buf).unpack1('Q>')
111
111
  # 1st bit
112
112
  run_bit = run_length_word & 1
113
- # the next 32 bits, masked, multiplied by 64 (which is shifted by 6 places)
114
- run_length = ((run_length_word >> 1) & 0xFFFF_FFFF) << 6
113
+ # the next 32 bits, masked, multiplied by 64
114
+ run_length = ((run_length_word / 0b1_0) & 0xFFFF_FFFF) * 64
115
115
  # the next 31 bits
116
- literal_length = (run_length_word >> 33)
116
+ literal_length = (run_length_word / 0b100000000_00000000_00000000_00000000_0)
117
117
 
118
118
  if run_bit == 1
119
119
  run_length.times do
@@ -124,9 +124,12 @@ module GitLS # rubocop:disable Metrics/ModuleLength
124
124
  uncompressed_pos += run_length
125
125
  end
126
126
 
127
- literal_length.times do
128
- word = file.read(8, buf).unpack1('B*').reverse
129
- word.each_char do |char|
127
+ next unless literal_length > 0
128
+
129
+ file.read(8 * literal_length, buf)
130
+ words = buf.unpack('B64' * literal_length)
131
+ words.each do |word|
132
+ word.each_char.reverse_each do |char|
130
133
  yield(uncompressed_pos) if char == '1'
131
134
 
132
135
  uncompressed_pos += 1
@@ -134,59 +137,64 @@ module GitLS # rubocop:disable Metrics/ModuleLength
134
137
  end
135
138
  end
136
139
 
137
- file.pos += 4 # bitmap metadata for adding to bitmaps
140
+ file.seek(4, 1) # bitmap metadata for adding to bitmaps
138
141
  end
139
142
 
140
143
  def files_2(files, file) # rubocop:disable Metrics/MethodLength
141
144
  files.map! do
142
- file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
143
- length = ((file.getbyte & 0b0000_1111) << 8) + file.getbyte # find the 12 byte length
145
+ file.seek(60, 1) # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
146
+
147
+ length = ((file.getbyte & 0xF) * 256) + file.getbyte # find the 12 byte length
144
148
  if length < 0xFFF
145
149
  path = file.read(length)
146
150
  # :nocov:
147
151
  else
148
152
  # i can't test this i just get ENAMETOOLONG a lot
149
- path = file.readline("\0").chop
150
- file.pos -= 1
153
+ # I'm not sure it's even possible to get to this path, PATH_MAX is 4096 bytes on linux, 1024 on mac
154
+ # and length is a 12 byte number: 4096 max.
155
+ path = file.readline("\0").chop!
156
+ file.seek(-1, 1)
151
157
  # :nocov:
152
158
  end
153
- file.pos += 8 - ((length - 2) % 8) # 1-8 bytes padding of nuls
159
+ file.seek(8 - ((length - 2) % 8), 1) # 1-8 bytes padding of nuls
160
+ path.force_encoding(Encoding::UTF_8)
154
161
  path
155
162
  end
156
163
  end
157
164
 
158
165
  def files_3(files, file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
159
166
  files.map! do
160
- file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
167
+ file.seek(60, 1) # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
168
+ flags = file.getbyte
169
+ extended_flag = (flags & 0b0100_0000) > 0
170
+ length = ((flags & 0xF) * 256) + file.getbyte # find the 12 byte length
171
+ file.seek(2, 1) if extended_flag
161
172
 
162
- flags = file.getbyte * 256 + file.getbyte
163
- extended_flag = (flags & 0b0100_0000_0000_0000).positive?
164
- file.pos += 2 if extended_flag
165
-
166
- length = flags & 0b0000_1111_1111_1111
167
173
  if length < 0xFFF
168
174
  path = file.read(length)
169
175
  # :nocov:
170
176
  else
171
177
  # i can't test this i just get ENAMETOOLONG a lot
172
- path = file.readline("\0").chop
173
- file.pos -= 1
178
+ # I'm not sure it's even possible to get to this path, PATH_MAX is 4096 bytes on linux, 1024 on mac
179
+ # and length is a 12 byte number: 4096 max.
180
+ path = file.readline("\0").chop!
181
+ file.seek(-1, 1)
174
182
  # :nocov:
175
183
  end
176
-
177
- file.pos += 8 - ((path.bytesize - (extended_flag ? 0 : 2)) % 8) # 1-8 bytes padding of nuls
184
+ file.seek(8 - ((path.bytesize - (extended_flag ? 0 : 2)) % 8), 1) # 1-8 bytes padding of nuls
185
+ path.force_encoding(Encoding::UTF_8)
178
186
  path
179
187
  end
180
188
  end
181
189
 
182
190
  def files_4(files, file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
183
191
  prev_entry_path = ''
184
- files.map! do
185
- file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
186
- flags = file.getbyte * 256 + file.getbyte
187
- file.pos += 2 if (flags & 0b0100_0000_0000_0000).positive?
188
-
189
- length = flags & 0b0000_1111_1111_1111
192
+ files.map! do # rubocop:disable Metrics/BlockLength
193
+ file.seek(60, 1) # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
194
+ flags = file.getbyte
195
+ extended_flag = (flags & 0b0100_0000) > 0
196
+ length = ((flags & 0xF) * 256) + file.getbyte # find the 12 byte length
197
+ file.seek(2, 1) if extended_flag
190
198
 
191
199
  # documentation for this number from
192
200
  # https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format
@@ -199,9 +207,9 @@ module GitLS # rubocop:disable Metrics/ModuleLength
199
207
  read_offset = 0
200
208
  prev_read_offset = file.getbyte
201
209
  n = 1
202
- while (prev_read_offset & 0b1000_0000).positive?
203
- read_offset += (prev_read_offset - 0b1000_0000)
204
- read_offset += 2**(7 * n)
210
+ while (prev_read_offset & 0b1000_0000) > 0
211
+ read_offset += (prev_read_offset & 0b0111_1111)
212
+ read_offset += 1 << (7 * n)
205
213
  n += 1
206
214
  prev_read_offset = file.getbyte
207
215
  end
@@ -210,16 +218,21 @@ module GitLS # rubocop:disable Metrics/ModuleLength
210
218
  initial_part_length = prev_entry_path.bytesize - read_offset
211
219
 
212
220
  if length < 0xFFF
213
- rest = file.read(length - initial_part_length)
214
- file.pos += 1 # the NUL
221
+ rest = +''
222
+ file.read(length - initial_part_length, rest)
223
+ file.seek(1, 1) # the NUL
215
224
  # :nocov:
216
225
  else
217
226
  # i can't test this i just get ENAMETOOLONG a lot
218
- rest = file.readline("\0").chop
227
+ # I'm not sure it's even possible to get to this path, PATH_MAX is 4096 bytes on linux, 1024 on mac
228
+ # and length is a 12 byte number: 4096 max.
229
+ rest = file.readline("\0").chop!
230
+ file.seek(-1, 1)
219
231
  # :nocov:
220
232
  end
221
233
 
222
- prev_entry_path = prev_entry_path.byteslice(0, initial_part_length) + rest
234
+ prev_entry_path = +"#{prev_entry_path.byteslice(0, initial_part_length)}#{rest}"
235
+ prev_entry_path.force_encoding(::Encoding::UTF_8)
223
236
  end
224
237
  end
225
238
  end
metadata CHANGED
@@ -1,148 +1,21 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_ls
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dana Sherson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2020-09-05 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '2'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '2'
27
- - !ruby/object:Gem::Dependency
28
- name: leftovers
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: pry
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">"
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">"
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 12.3.3
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 12.3.3
69
- - !ruby/object:Gem::Dependency
70
- name: rubocop
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rubocop-performance
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: rubocop-rspec
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: simplecov
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: 0.18.5
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: 0.18.5
125
- - !ruby/object:Gem::Dependency
126
- name: spellr
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- description:
10
+ date: 2025-01-28 00:00:00.000000000 Z
11
+ dependencies: []
140
12
  email:
141
13
  - robot@dana.sh
142
14
  executables: []
143
15
  extensions: []
144
16
  extra_rdoc_files: []
145
17
  files:
18
+ - ".github/workflows/ci.yml"
146
19
  - ".gitignore"
147
20
  - ".leftovers.yml"
148
21
  - ".rspec"
@@ -152,7 +25,6 @@ files:
152
25
  - ".spellr_wordlists/english.txt"
153
26
  - ".spellr_wordlists/ruby.txt"
154
27
  - ".spellr_wordlists/shell.txt"
155
- - ".travis.yml"
156
28
  - CHANGELOG.md
157
29
  - Gemfile
158
30
  - LICENSE.txt
@@ -168,7 +40,7 @@ metadata:
168
40
  homepage_uri: https://github.com/robotdana/git_ls
169
41
  source_code_uri: https://github.com/robotdana/git_ls
170
42
  changelog_uri: https://github.com/robotdana/git_ls/blob/main/CHANGELOG.md
171
- post_install_message:
43
+ rubygems_mfa_required: 'true'
172
44
  rdoc_options: []
173
45
  require_paths:
174
46
  - lib
@@ -183,8 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
55
  - !ruby/object:Gem::Version
184
56
  version: '0'
185
57
  requirements: []
186
- rubygems_version: 3.0.3
187
- signing_key:
58
+ rubygems_version: 3.6.2
188
59
  specification_version: 4
189
60
  summary: Read a .git/index file and list the files
190
61
  test_files: []
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7
6
- - 2.6
7
- - 2.5
8
- - 2.4
9
- - jruby
10
- before_install: sudo apt-get install git && gem install bundler -v 2.1.4