git_ls 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|