lib/git_ls/parser.rb in git_ls-0.1.0 vs lib/git_ls/parser.rb in git_ls-0.2.0
- old
+ new
@@ -17,14 +17,14 @@
def files
headers
@files = Array.new(length)
- case git_version
+ case git_index_version
when 2 then files_2
when 3 then files_3
- when 4 then raise ::GitLS::Error, "4 is complicated git version, it's not done yet"
+ when 4 then files_4
else raise ::GitLS::Error, 'Unrecognized git version'
end
@files
end
@@ -40,28 +40,70 @@
end
def files_3
@files.map! do
@file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
- flags = @file.read(2) # 2 bytes flags
- # skip extend flags if extended flags bit set
- @file.pos += 2 if flags && (flags.unpack1('n') & 0b0100_0000_0000_0000).positive?
+ flag = @file.getbyte
+ extended_flag_offset = if (flag & 0b0100_0000).positive?
+ 3 # skip next half of flag + extended flags
+ else
+ 1 # skip next half of flag
+ end
+ @file.pos += extended_flag_offset
ret = @file.readline("\0").chop
- @file.pos += 7 - ((ret.bytesize - 2) % 8) # 1-8 bytes padding of nuls
+ @file.pos += 7 - ((ret.bytesize + extended_flag_offset - 3) % 8) # 1-8 bytes padding of nuls
ret
end
end
+ def files_4
+ prev_entry_path = ""
+ @files.map! do
+ @file.pos += 60 # skip 60 bytes (40 bytes of stat, 20 bytes of sha)
+ flag = @file.getbyte
+ @file.pos += if (flag & 0b0100_0000).positive?
+ 3 # skip next half of flag + extended flags
+ else
+ 1 # skip next half of flag
+ end
+
+ # flags = @file.read(2) # 2 bytes flags
+ # # skip extend flags if extended flags bit set
+ # @file.pos += 2 if flags && (flags.unpack1('n') & 0b0100_0000_0000_0000).positive?
+
+ # documentation for this number from
+ # https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format
+ # offset encoding:
+ # n bytes with MSB set in all but the last one.
+ # The offset is then the number constructed by
+ # concatenating the lower 7 bit of each byte, and
+ # for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
+ # to the result.
+ read_offset = 0
+ prev_read_offset = @file.getbyte
+ n = 1
+ while (prev_read_offset & 0b1000_0000).positive?
+ read_offset += (prev_read_offset - 0b1000_0000)
+ read_offset += 2**(7 * n)
+ n += 1
+ prev_read_offset = @file.getbyte
+ end
+ read_offset += prev_read_offset
+
+ prev_entry_path = prev_entry_path.byteslice(0, prev_entry_path.bytesize - read_offset) + @file.readline("\0").chop
+ end
+ end
+
def headers
@headers ||= @file.read(12).unpack(::GitLS::Parser::HEADER)
end
def valid?
headers[0] == 'DIRC'
end
- def git_version
+ def git_index_version
@git_version = headers[1]
end
def length
headers[2]