git_ls 0.2.0 → 0.3.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 +3 -0
- data/Gemfile.lock +1 -1
- data/lib/git_ls.rb +123 -4
- data/lib/git_ls/version.rb +1 -1
- metadata +1 -2
- data/lib/git_ls/parser.rb +0 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e317eb1dd5319b6b4fc46d2c928d2870b953ab830a1eab915b74676770e69f35
|
4
|
+
data.tar.gz: 9782182d8e15fba682c15018c9c0ffd15ac3f748dbb92e6bd6510b256b4f4404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5aedff95a701fe5554afd2ff627e2d2f3a6a02fb0bac8603b314fef038e9fdacf61a917ac942fe678c1161123e8a4860fce8d272292f7497df304de0838404f
|
7
|
+
data.tar.gz: 2c6a4b71a6c5d8485230bdb3f6800546118fb1cb07326813ef6de6b269c1be896124be6898ae580292a49a860145c4375e3da5ae537c07f57da26b37ad049905
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/lib/git_ls.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'git_ls/parser'
|
4
|
-
|
5
|
-
# Entry point for gem.
|
6
3
|
# Usage:
|
7
4
|
# GitLS.files -> Array of strings as files.
|
8
5
|
# This will be identical output to git ls-files
|
@@ -11,10 +8,132 @@ module GitLS
|
|
11
8
|
|
12
9
|
class << self
|
13
10
|
def files(path = ::Dir.pwd)
|
11
|
+
read(path, false)
|
12
|
+
end
|
13
|
+
|
14
|
+
def headers(path = ::Dir.pwd)
|
15
|
+
read(path, true)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def read(path, return_headers_only)
|
14
21
|
path = ::File.join(path, '.git/index') if ::File.directory?(path)
|
15
|
-
::
|
22
|
+
file = ::File.new(path)
|
23
|
+
# 4-byte signature:
|
24
|
+
# The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache")
|
25
|
+
# 4-byte version number:
|
26
|
+
# The current supported versions are 2, 3 and 4.
|
27
|
+
# 32-bit number of index entries.
|
28
|
+
sig, git_index_version, length = file.read(12).unpack('A4NN')
|
29
|
+
raise ::GitLS::Error, 'not a git dir or .git/index file' unless sig == 'DIRC'
|
30
|
+
|
31
|
+
return { git_index_version: git_index_version, length: length } if return_headers_only
|
32
|
+
|
33
|
+
files = Array.new(length)
|
34
|
+
case git_index_version
|
35
|
+
when 2 then files_2(files, file)
|
36
|
+
when 3 then files_3(files, file)
|
37
|
+
when 4 then files_4(files, file)
|
38
|
+
else raise ::GitLS::Error, 'Unrecognized git index version'
|
39
|
+
end
|
40
|
+
files
|
16
41
|
rescue Errno::ENOENT => e
|
17
42
|
raise GitLS::Error, "Not a git directory: #{e.message}"
|
43
|
+
ensure
|
44
|
+
# :nocov:
|
45
|
+
# coverage tracking for branches in ensure blocks is weird
|
46
|
+
file&.close
|
47
|
+
# :nocov:
|
48
|
+
files
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def files_2(files, file)
|
54
|
+
files.map! do
|
55
|
+
file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
|
56
|
+
length = (file.getbyte & 0b0000_1111) * 256 + file.getbyte # find the 12 byte length
|
57
|
+
if length < 0xFFF
|
58
|
+
path = file.read(length)
|
59
|
+
# :nocov:
|
60
|
+
else
|
61
|
+
# i can't test this i just get ENAMETOOLONG a lot
|
62
|
+
path = file.readline("\0").chop
|
63
|
+
file.pos -= 1
|
64
|
+
# :nocov:
|
65
|
+
end
|
66
|
+
file.pos += 8 - ((length - 2) % 8) # 1-8 bytes padding of nuls
|
67
|
+
path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def files_3(files, file)
|
72
|
+
files.map! do
|
73
|
+
file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
|
74
|
+
|
75
|
+
flags = file.getbyte * 256 + file.getbyte
|
76
|
+
extended_flag = (flags & 0b0100_0000_0000_0000).positive?
|
77
|
+
file.pos += 2 if extended_flag
|
78
|
+
|
79
|
+
length = flags & 0b0000_1111_1111_1111
|
80
|
+
if length < 0xFFF
|
81
|
+
path = file.read(length)
|
82
|
+
# :nocov:
|
83
|
+
else
|
84
|
+
# i can't test this i just get ENAMETOOLONG a lot
|
85
|
+
path = file.readline("\0").chop
|
86
|
+
file.pos -= 1
|
87
|
+
# :nocov:
|
88
|
+
end
|
89
|
+
|
90
|
+
file.pos += 8 - ((path.bytesize - (extended_flag ? 0 : 2)) % 8) # 1-8 bytes padding of nuls
|
91
|
+
path
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def files_4(files, file)
|
96
|
+
prev_entry_path = ""
|
97
|
+
files.map! do
|
98
|
+
file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
|
99
|
+
flags = file.getbyte * 256 + file.getbyte
|
100
|
+
file.pos += 2 if (flags & 0b0100_0000_0000_0000).positive?
|
101
|
+
|
102
|
+
length = flags & 0b0000_1111_1111_1111
|
103
|
+
|
104
|
+
# documentation for this number from
|
105
|
+
# https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format
|
106
|
+
# offset encoding:
|
107
|
+
# n bytes with MSB set in all but the last one.
|
108
|
+
# The offset is then the number constructed by
|
109
|
+
# concatenating the lower 7 bit of each byte, and
|
110
|
+
# for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
|
111
|
+
# to the result.
|
112
|
+
read_offset = 0
|
113
|
+
prev_read_offset = file.getbyte
|
114
|
+
n = 1
|
115
|
+
while (prev_read_offset & 0b1000_0000).positive?
|
116
|
+
read_offset += (prev_read_offset - 0b1000_0000)
|
117
|
+
read_offset += 2**(7 * n)
|
118
|
+
n += 1
|
119
|
+
prev_read_offset = file.getbyte
|
120
|
+
end
|
121
|
+
read_offset += prev_read_offset
|
122
|
+
|
123
|
+
initial_part_length = prev_entry_path.bytesize - read_offset
|
124
|
+
|
125
|
+
if length < 0xFFF
|
126
|
+
rest = file.read(length - initial_part_length)
|
127
|
+
file.pos += 1 # the NUL
|
128
|
+
# :nocov:
|
129
|
+
else
|
130
|
+
# i can't test this i just get ENAMETOOLONG a lot
|
131
|
+
rest = file.readline("\0").chop
|
132
|
+
# :nocov:
|
133
|
+
end
|
134
|
+
|
135
|
+
prev_entry_path = prev_entry_path.byteslice(0, initial_part_length) + rest
|
136
|
+
end
|
18
137
|
end
|
19
138
|
end
|
20
139
|
end
|
data/lib/git_ls/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_ls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dana Sherson
|
@@ -71,7 +71,6 @@ files:
|
|
71
71
|
- Rakefile
|
72
72
|
- git_index.gemspec
|
73
73
|
- lib/git_ls.rb
|
74
|
-
- lib/git_ls/parser.rb
|
75
74
|
- lib/git_ls/version.rb
|
76
75
|
homepage: https://github.com/robotdana/git_ls
|
77
76
|
licenses:
|
data/lib/git_ls/parser.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GitLS
|
4
|
-
# Parse a .git/index file
|
5
|
-
# Format documented here: https://git-scm.com/docs/index-format
|
6
|
-
class Parser
|
7
|
-
HEADER = 'A4' + # 4-byte signature:
|
8
|
-
# The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache")
|
9
|
-
'N' + # 4-byte version number:
|
10
|
-
# The current supported versions are 2, 3 and 4.
|
11
|
-
'N' # 32-bit number of index entries.
|
12
|
-
|
13
|
-
def initialize(file)
|
14
|
-
@file = file
|
15
|
-
raise ::GitLS::Error, 'not a git dir or .git/index file' unless valid?
|
16
|
-
end
|
17
|
-
|
18
|
-
def files
|
19
|
-
headers
|
20
|
-
|
21
|
-
@files = Array.new(length)
|
22
|
-
case git_index_version
|
23
|
-
when 2 then files_2
|
24
|
-
when 3 then files_3
|
25
|
-
when 4 then files_4
|
26
|
-
else raise ::GitLS::Error, 'Unrecognized git version'
|
27
|
-
end
|
28
|
-
@files
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def files_2
|
34
|
-
@files.map! do
|
35
|
-
@file.pos += 62 # skip 62 bytes (40 bytes of stat, 20 bytes of sha, 2 bytes flags)
|
36
|
-
ret = @file.readline("\0").chop
|
37
|
-
@file.pos += 7 - ((ret.bytesize - 2) % 8) # 1-8 bytes padding of nuls
|
38
|
-
ret
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def files_3
|
43
|
-
@files.map! do
|
44
|
-
@file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
|
45
|
-
flag = @file.getbyte
|
46
|
-
extended_flag_offset = if (flag & 0b0100_0000).positive?
|
47
|
-
3 # skip next half of flag + extended flags
|
48
|
-
else
|
49
|
-
1 # skip next half of flag
|
50
|
-
end
|
51
|
-
@file.pos += extended_flag_offset
|
52
|
-
|
53
|
-
ret = @file.readline("\0").chop
|
54
|
-
@file.pos += 7 - ((ret.bytesize + extended_flag_offset - 3) % 8) # 1-8 bytes padding of nuls
|
55
|
-
ret
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def files_4
|
60
|
-
prev_entry_path = ""
|
61
|
-
@files.map! do
|
62
|
-
@file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
|
63
|
-
flag = @file.getbyte
|
64
|
-
@file.pos += if (flag & 0b0100_0000).positive?
|
65
|
-
3 # skip next half of flag + extended flags
|
66
|
-
else
|
67
|
-
1 # skip next half of flag
|
68
|
-
end
|
69
|
-
|
70
|
-
# flags = @file.read(2) # 2 bytes flags
|
71
|
-
# # skip extend flags if extended flags bit set
|
72
|
-
# @file.pos += 2 if flags && (flags.unpack1('n') & 0b0100_0000_0000_0000).positive?
|
73
|
-
|
74
|
-
# documentation for this number from
|
75
|
-
# https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format
|
76
|
-
# offset encoding:
|
77
|
-
# n bytes with MSB set in all but the last one.
|
78
|
-
# The offset is then the number constructed by
|
79
|
-
# concatenating the lower 7 bit of each byte, and
|
80
|
-
# for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
|
81
|
-
# to the result.
|
82
|
-
read_offset = 0
|
83
|
-
prev_read_offset = @file.getbyte
|
84
|
-
n = 1
|
85
|
-
while (prev_read_offset & 0b1000_0000).positive?
|
86
|
-
read_offset += (prev_read_offset - 0b1000_0000)
|
87
|
-
read_offset += 2**(7 * n)
|
88
|
-
n += 1
|
89
|
-
prev_read_offset = @file.getbyte
|
90
|
-
end
|
91
|
-
read_offset += prev_read_offset
|
92
|
-
|
93
|
-
prev_entry_path = prev_entry_path.byteslice(0, prev_entry_path.bytesize - read_offset) + @file.readline("\0").chop
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def headers
|
98
|
-
@headers ||= @file.read(12).unpack(::GitLS::Parser::HEADER)
|
99
|
-
end
|
100
|
-
|
101
|
-
def valid?
|
102
|
-
headers[0] == 'DIRC'
|
103
|
-
end
|
104
|
-
|
105
|
-
def git_index_version
|
106
|
-
@git_version = headers[1]
|
107
|
-
end
|
108
|
-
|
109
|
-
def length
|
110
|
-
headers[2]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|