зеркало из https://github.com/github/grit.git
updated packfile code to recognize index v2
This commit is contained in:
Родитель
791ec6b351
Коммит
81a3e0d4a8
|
@ -9,17 +9,18 @@
|
|||
# provides native ruby access to git objects and pack files
|
||||
#
|
||||
|
||||
begin
|
||||
require 'mmap'
|
||||
rescue LoadError
|
||||
|
||||
module Grit
|
||||
module GitRuby
|
||||
module Internal
|
||||
class Mmap
|
||||
def initialize(file)
|
||||
def initialize(file, version = 1)
|
||||
@file = file
|
||||
@offset = nil
|
||||
if version == 2
|
||||
@global_offset = 8
|
||||
else
|
||||
@global_offset = 0
|
||||
end
|
||||
end
|
||||
|
||||
def unmap
|
||||
|
@ -41,7 +42,7 @@ module Grit
|
|||
raise RuntimeError, "invalid index param: #{idx.class}"
|
||||
end
|
||||
if @offset != offset
|
||||
@file.seek(offset)
|
||||
@file.seek(offset + @global_offset)
|
||||
end
|
||||
@offset = offset + len ? len : 1
|
||||
if not len
|
||||
|
@ -55,5 +56,3 @@ module Grit
|
|||
end
|
||||
end
|
||||
|
||||
end # rescue LoadError
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ require 'zlib'
|
|||
require 'grit/git-ruby/internal/raw_object'
|
||||
require 'grit/git-ruby/internal/mmap'
|
||||
|
||||
PACK_SIGNATURE = "PACK"
|
||||
PACK_IDX_SIGNATURE = "\377tOc"
|
||||
|
||||
module Grit
|
||||
module GitRuby
|
||||
module Internal
|
||||
|
@ -27,30 +30,19 @@ module Grit
|
|||
SHA1Size = 20
|
||||
IdxOffsetSize = 4
|
||||
OffsetSize = 4
|
||||
CrcSize = 4
|
||||
OffsetStart = FanOutCount * IdxOffsetSize
|
||||
SHA1Start = OffsetStart + OffsetSize
|
||||
EntrySize = OffsetSize + SHA1Size
|
||||
EntrySizeV2 = SHA1Size + CrcSize + OffsetSize
|
||||
|
||||
def initialize(file)
|
||||
if file =~ /\.idx$/
|
||||
file = file[0...-3] + 'pack'
|
||||
end
|
||||
|
||||
@cache = {}
|
||||
@name = file
|
||||
|
||||
with_idx do |idx|
|
||||
@offsets = [0]
|
||||
FanOutCount.times do |i|
|
||||
pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
|
||||
if pos < @offsets[i]
|
||||
raise PackFormatError, "pack #@name has discontinuous index #{i}"
|
||||
end
|
||||
@offsets << pos
|
||||
end
|
||||
|
||||
@size = @offsets[-1]
|
||||
end
|
||||
@cache = {}
|
||||
init_pack
|
||||
end
|
||||
|
||||
def with_idx(index_file = nil)
|
||||
|
@ -60,7 +52,21 @@ module Grit
|
|||
else
|
||||
idxfile = File.open(index_file)
|
||||
end
|
||||
idx = Mmap.new(idxfile)
|
||||
|
||||
# read header
|
||||
sig = idxfile.read(4)
|
||||
ver = idxfile.read(4).unpack("N")[0]
|
||||
|
||||
if sig == PACK_IDX_SIGNATURE
|
||||
if(ver != 2)
|
||||
raise PackFormatError, "pack #@name has unknown pack file version #{ver}"
|
||||
end
|
||||
@version = 2
|
||||
else
|
||||
@version = 1
|
||||
end
|
||||
|
||||
idx = Mmap.new(idxfile, @version)
|
||||
yield idx
|
||||
idx.unmap
|
||||
idxfile.close
|
||||
|
@ -72,31 +78,6 @@ module Grit
|
|||
packfile.close
|
||||
end
|
||||
|
||||
# given an index file, list out the shas that it's packfile contains
|
||||
def self.get_shas(index_file)
|
||||
with_idx(index_file) do |idx|
|
||||
@offsets = [0]
|
||||
FanOutCount.times do |i|
|
||||
pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
|
||||
if pos < @offsets[i]
|
||||
raise PackFormatError, "pack #@name has discontinuous index #{i}"
|
||||
end
|
||||
@offsets << pos
|
||||
end
|
||||
|
||||
@size = @offsets[-1]
|
||||
shas = []
|
||||
|
||||
pos = SHA1Start
|
||||
@size.times do
|
||||
sha1 = idx[pos,SHA1Size]
|
||||
pos += EntrySize
|
||||
shas << sha1.unpack("H*").first
|
||||
end
|
||||
shas
|
||||
end
|
||||
end
|
||||
|
||||
def cache_objects
|
||||
@cache = {}
|
||||
with_packfile do |packfile|
|
||||
|
@ -117,6 +98,13 @@ module Grit
|
|||
# shouldnt be anything open now
|
||||
end
|
||||
|
||||
# given an index file, list out the shas that it's packfile contains
|
||||
def get_shas
|
||||
shas = []
|
||||
each_sha1 { |sha| shas << sha.unpack("H*")[0] }
|
||||
shas
|
||||
end
|
||||
|
||||
def [](sha1)
|
||||
if obj = @cache[sha1]
|
||||
return obj
|
||||
|
@ -128,31 +116,116 @@ module Grit
|
|||
return obj
|
||||
end
|
||||
|
||||
def init_pack
|
||||
with_idx do |idx|
|
||||
@offsets = [0]
|
||||
FanOutCount.times do |i|
|
||||
pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
|
||||
if pos < @offsets[i]
|
||||
raise PackFormatError, "pack #@name has discontinuous index #{i}"
|
||||
end
|
||||
@offsets << pos
|
||||
end
|
||||
@size = @offsets[-1]
|
||||
end
|
||||
end
|
||||
|
||||
def each_entry
|
||||
with_idx do |idx|
|
||||
pos = OffsetStart
|
||||
@size.times do
|
||||
offset = idx[pos,OffsetSize].unpack('N')[0]
|
||||
sha1 = idx[pos+OffsetSize,SHA1Size]
|
||||
pos += EntrySize
|
||||
yield sha1, offset
|
||||
if @version == 2
|
||||
data = read_data_v2(idx)
|
||||
data.each do |sha1, crc, offset|
|
||||
yield sha1, offset
|
||||
end
|
||||
else
|
||||
pos = OffsetStart
|
||||
@size.times do
|
||||
offset = idx[pos,OffsetSize].unpack('N')[0]
|
||||
sha1 = idx[pos+OffsetSize,SHA1Size]
|
||||
pos += EntrySize
|
||||
yield sha1, offset
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def read_data_v2(idx)
|
||||
data = []
|
||||
pos = OffsetStart
|
||||
@size.times do |i|
|
||||
data[i] = [idx[pos,SHA1Size], 0, 0]
|
||||
pos += SHA1Size
|
||||
end
|
||||
@size.times do |i|
|
||||
crc = idx[pos,CrcSize]
|
||||
data[i][1] = crc
|
||||
pos += CrcSize
|
||||
end
|
||||
@size.times do |i|
|
||||
puts "#{i} : #{pos}"
|
||||
offset = idx[pos,OffsetSize].unpack('N')[0]
|
||||
data[i][2] = offset
|
||||
pos += OffsetSize
|
||||
end
|
||||
data
|
||||
end
|
||||
private :read_data_v2
|
||||
|
||||
def each_sha1
|
||||
with_idx do |idx|
|
||||
# unpacking the offset is quite expensive, so
|
||||
# we avoid using #each
|
||||
pos = SHA1Start
|
||||
@size.times do
|
||||
sha1 = idx[pos,SHA1Size]
|
||||
pos += EntrySize
|
||||
yield sha1
|
||||
if @version == 2
|
||||
data = read_data_v2(idx)
|
||||
data.each do |sha1, crc, offset|
|
||||
yield sha1
|
||||
end
|
||||
else
|
||||
pos = SHA1Start
|
||||
@size.times do
|
||||
sha1 = idx[pos,SHA1Size]
|
||||
pos += EntrySize
|
||||
yield sha1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_object_in_index(idx, sha1)
|
||||
slot = sha1[0]
|
||||
return nil if !slot
|
||||
first, last = @offsets[slot,2]
|
||||
while first < last
|
||||
mid = (first + last) / 2
|
||||
if @version == 2
|
||||
midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size]
|
||||
cmp = midsha1 <=> sha1
|
||||
|
||||
if cmp < 0
|
||||
first = mid + 1
|
||||
elsif cmp > 0
|
||||
last = mid
|
||||
else
|
||||
pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
|
||||
offset = idx[pos, OffsetSize].unpack('N')[0]
|
||||
return offset
|
||||
end
|
||||
else
|
||||
midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
|
||||
cmp = midsha1 <=> sha1
|
||||
|
||||
if cmp < 0
|
||||
first = mid + 1
|
||||
elsif cmp > 0
|
||||
last = mid
|
||||
else
|
||||
pos = OffsetStart + mid * EntrySize
|
||||
offset = idx[pos,OffsetSize].unpack('N')[0]
|
||||
return offset
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def find_object(sha1)
|
||||
obj = nil
|
||||
with_idx do |idx|
|
||||
|
@ -161,29 +234,7 @@ module Grit
|
|||
obj
|
||||
end
|
||||
private :find_object
|
||||
|
||||
def find_object_in_index(idx, sha1)
|
||||
slot = sha1[0]
|
||||
return nil if !slot
|
||||
first, last = @offsets[slot,2]
|
||||
while first < last
|
||||
mid = (first + last) / 2
|
||||
midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
|
||||
cmp = midsha1 <=> sha1
|
||||
|
||||
if cmp < 0
|
||||
first = mid + 1
|
||||
elsif cmp > 0
|
||||
last = mid
|
||||
else
|
||||
pos = OffsetStart + mid * EntrySize
|
||||
offset = idx[pos,OffsetSize].unpack('N')[0]
|
||||
return offset
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def parse_object(offset)
|
||||
obj = nil
|
||||
with_packfile do |packfile|
|
||||
|
|
|
@ -281,6 +281,13 @@ module Grit
|
|||
log = truncate_arr(log, end_sha)
|
||||
end
|
||||
|
||||
# shorten the list if it's longer than max_count (had to get everything in branches)
|
||||
if options[:max_count]
|
||||
if (opt_len = options[:max_count].to_i) < log.size
|
||||
log = log[0, opt_len]
|
||||
end
|
||||
end
|
||||
|
||||
if options[:pretty] == 'raw'
|
||||
log.map {|k, v| v }.join('')
|
||||
else
|
||||
|
@ -337,7 +344,7 @@ module Grit
|
|||
opts[:path_limiter])
|
||||
add_sha = false
|
||||
end
|
||||
subarray += walk_log(psha, opts, (array.size + subarray.size + total_size))
|
||||
subarray += walk_log(psha, opts, (array.size + total_size))
|
||||
next if opts[:first_parent]
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ref: refs/heads/master
|
|
@ -0,0 +1,11 @@
|
|||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
[remote "origin"]
|
||||
url = git@github.com:schacon/grit.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
[branch "master"]
|
||||
remote = origin
|
||||
merge = refs/heads/master
|
|
@ -0,0 +1 @@
|
|||
Unnamed repository; edit this file to name it for gitweb.
|
Двоичный файл не отображается.
|
@ -0,0 +1,6 @@
|
|||
# git-ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
|
@ -0,0 +1,10 @@
|
|||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/master
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/nonpack
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/test/chacon
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/heads/test/master
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/heads/testing
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/remotes/origin/HEAD
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/remotes/origin/master
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/remotes/tom/master
|
||||
f0055fda16c18fd8b27986dbf038c735b82198d7 refs/tags/v0.7.0
|
||||
7bcc0ee821cdd133d8a53e8e7173a334fef448aa refs/tags/v0.7.0^{}
|
|
@ -0,0 +1,3 @@
|
|||
P pack-4eb8bbafbe65e4f3841581537735fc5d448759c5.pack
|
||||
P pack-c8881c2613522dc3ac69af9c7b4881a061aaec8c.pack
|
||||
|
Двоичные данные
test/dot_git_iv2/objects/pack/pack-4eb8bbafbe65e4f3841581537735fc5d448759c5.idx
Normal file
Двоичные данные
test/dot_git_iv2/objects/pack/pack-4eb8bbafbe65e4f3841581537735fc5d448759c5.idx
Normal file
Двоичный файл не отображается.
Двоичные данные
test/dot_git_iv2/objects/pack/pack-4eb8bbafbe65e4f3841581537735fc5d448759c5.pack
Normal file
Двоичные данные
test/dot_git_iv2/objects/pack/pack-4eb8bbafbe65e4f3841581537735fc5d448759c5.pack
Normal file
Двоичный файл не отображается.
Двоичные данные
test/dot_git_iv2/objects/pack/pack-c8881c2613522dc3ac69af9c7b4881a061aaec8c.idx
Normal file
Двоичные данные
test/dot_git_iv2/objects/pack/pack-c8881c2613522dc3ac69af9c7b4881a061aaec8c.idx
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
fetch-pack 3903 on agadorsparticus
|
Двоичные данные
test/dot_git_iv2/objects/pack/pack-c8881c2613522dc3ac69af9c7b4881a061aaec8c.pack
Normal file
Двоичные данные
test/dot_git_iv2/objects/pack/pack-c8881c2613522dc3ac69af9c7b4881a061aaec8c.pack
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,10 @@
|
|||
# pack-refs with: peeled
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/master
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/nonpack
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/heads/test/chacon
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/heads/test/master
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/heads/testing
|
||||
ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a refs/remotes/origin/master
|
||||
2d3acf90f35989df8f262dc50beadc4ee3ae1560 refs/remotes/tom/master
|
||||
f0055fda16c18fd8b27986dbf038c735b82198d7 refs/tags/v0.7.0
|
||||
^7bcc0ee821cdd133d8a53e8e7173a334fef448aa
|
|
@ -0,0 +1 @@
|
|||
ref: refs/remotes/origin/master
|
|
@ -9,6 +9,19 @@ class TestRubyGit < Test::Unit::TestCase
|
|||
@blob_sha = '4232d073306f01cf0b895864e5a5cfad7dd76fce'
|
||||
end
|
||||
|
||||
def test_log_merge
|
||||
c1 = '420eac97a826bfac8724b6b0eef35c20922124b7'
|
||||
c2 = '30e367cef2203eba2b341dc9050993b06fd1e108'
|
||||
out = @git.rev_list({:pretty => 'raw', :max_count => 10}, 'master')
|
||||
assert_match "commit #{c1}", out
|
||||
assert_match "commit #{c2}", out
|
||||
end
|
||||
|
||||
def test_log_max_count
|
||||
out = @git.rev_list({:max_count => 10}, 'master')
|
||||
assert_equal 10, out.split("\n").size
|
||||
end
|
||||
|
||||
def test_diff
|
||||
commit1 = '2d3acf90f35989df8f262dc50beadc4ee3ae1560'
|
||||
commit2 = '420eac97a826bfac8724b6b0eef35c20922124b7'
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
require 'pp'
|
||||
|
||||
class TestRubyGitIv2 < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@git = Grit::Repo.new(File.join(File.dirname(__FILE__), *%w[dot_git_iv2]), :is_bare => true)
|
||||
@rgit = @git.git.ruby_git
|
||||
@commit_sha = 'ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a'
|
||||
@tree_sha = 'cd7422af5a2e0fff3e94d6fb1a8fff03b2841881'
|
||||
@blob_sha = '4232d073306f01cf0b895864e5a5cfad7dd76fce'
|
||||
end
|
||||
|
||||
=begin
|
||||
def test_sha
|
||||
i = 0
|
||||
@rgit.packs[0].each_sha1 do |sha|
|
||||
i += 1
|
||||
sha = sha.unpack("H*")[0]
|
||||
#puts "#{i} : #{sha}"
|
||||
end
|
||||
end
|
||||
=end
|
||||
|
||||
def test_basic
|
||||
assert @rgit.object_exists?(@commit_sha)
|
||||
assert_equal 10, @git.commits.size
|
||||
end
|
||||
|
||||
def test_objects
|
||||
commit = @rgit.get_object_by_sha1(@commit_sha)
|
||||
assert_equal commit.author.email, 'schacon@gmail.com'
|
||||
tree = @rgit.get_object_by_sha1(@tree_sha)
|
||||
assert_equal 7, tree.entry.size
|
||||
blob = @rgit.get_object_by_sha1(@blob_sha)
|
||||
assert_match 'First public release', blob.content
|
||||
end
|
||||
|
||||
end
|
Загрузка…
Ссылка в новой задаче