Skip to content

Instantly share code, notes, and snippets.

@apeiros
Created February 7, 2013 20:34
Show Gist options
  • Save apeiros/4733935 to your computer and use it in GitHub Desktop.
Save apeiros/4733935 to your computer and use it in GitHub Desktop.
Efficiently read arbitrary directories of an arbitrary git revision from a git repo into memory, without affecting the working directory.
# BEWARE! This code is largely untested and not ready for production!
Result = Struct.new(:entries, :raw_error, :status)
class Entry
HeaderUnpackFormat = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155".freeze
EmptyString = "".freeze
OctalFields = [1,2,3,4,5,6,10,13,14]
def self.from_stream(io)
header = io.read(512)
return nil unless header
fields = header.unpack(HeaderUnpackFormat)
OctalFields.each do |octal_field|
fields[octal_field] = fields[octal_field].oct
end
new(fields, io.read(fields[4]))
end
attr_reader :name
attr_reader :mode
attr_reader :uid
attr_reader :gid
attr_reader :size
attr_reader :mtime
attr_reader :checksum
attr_reader :typeflag
attr_reader :linkname
attr_reader :magic
attr_reader :version
attr_reader :uname
attr_reader :gname
attr_reader :devmajor
attr_reader :devminor
attr_reader :prefix
attr_reader :data
def initialize(fields, data)
@name = fields[0]
@mode = fields[1]
@uid = fields[2]
@gid = fields[3]
@size = fields[4]
@mtime = fields[5]
@checksum = fields[6]
@typeflag = fields[7]
@linkname = fields[8]
@magic = fields[9]
@version = fields[10]
@uname = fields[11]
@gname = fields[12]
@devmajor = fields[13]
@devminor = fields[14]
@prefix = fields[15]
@data = data
end
def skip
(512 - (size % 512)) % 512
end
def full_name
if @prefix != EmptyString
File.join(@prefix, @name)
else
@name
end
end
end
def git_archive_entries(revision, *paths)
sor,sow = IO.pipe
ser,sew = IO.pipe
pid = Process.spawn('git', 'archive', '--format=tar', revision, *paths, out: sow, err: sew)
sew.close
sow.close
entries = []
while entry=Entry.from_stream(sor)
entries << entry
sor.read(entry.skip)
end
stderr = ser.read
_, status = Process.wait2(pid)
Result.new(entries, stderr, status)
end
revision = 'version-2.8.0-beta'
paths = ['config/locales']
result = git_archive_entries(revision, *paths)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment