updated packfile code to recognize index v2

This commit is contained in:
Scott Chacon 2008-08-06 11:45:51 -07:00
Родитель 791ec6b351
Коммит 81a3e0d4a8
20 изменённых файлов: 240 добавлений и 87 удалений

Просмотреть файл

@ -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

1
test/dot_git_iv2/HEAD Normal file
Просмотреть файл

@ -0,0 +1 @@
ref: refs/heads/master

11
test/dot_git_iv2/config Normal file
Просмотреть файл

@ -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.

Двоичные данные
test/dot_git_iv2/index Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1 @@
fetch-pack 3903 on agadorsparticus

Двоичный файл не отображается.

Просмотреть файл

@ -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'

39
test/test_rubygit_iv2.rb Normal file
Просмотреть файл

@ -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