Skip to content

Commit 7df710f

Browse files
siebertmschacon
authored andcommitted
Add the possibility to read blob contents in chunks via IO#popen
1 parent ae106e2 commit 7df710f

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

lib/git/lib.rb

+14-6
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ def process_commit_data(data, sha = nil)
166166
end
167167
end
168168

169-
def object_contents(sha)
170-
command('cat-file', ['-p', sha])
169+
def object_contents(sha, &block)
170+
command('cat-file', ['-p', sha], &block)
171171
end
172172

173173
def ls_tree(sha)
@@ -596,20 +596,20 @@ def command_lines(cmd, opts = [], chdir = true)
596596
command(cmd, opts, chdir).split("\n")
597597
end
598598

599-
def command(cmd, opts = [], chdir = true)
599+
def command(cmd, opts = [], chdir = true, &block)
600600
ENV['GIT_DIR'] = @git_dir if (@git_dir != ENV['GIT_DIR'])
601601
ENV['GIT_INDEX_FILE'] = @git_index_file if (@git_index_file != ENV['GIT_INDEX_FILE'])
602602
ENV['GIT_WORK_TREE'] = @git_work_dir if (@git_work_dir != ENV['GIT_WORK_TREE'])
603603
path = @git_work_dir || @git_dir || @path
604604

605605
opts = opts.to_a.join(' ')
606-
git_cmd = "git #{cmd} #{opts}"
606+
git_cmd = "git #{cmd} #{opts} 2>&1"
607607

608608
out = nil
609609
if chdir && (Dir.getwd != path)
610-
Dir.chdir(path) { out = `#{git_cmd} 2>&1`.chomp }
610+
Dir.chdir(path) { out = run_command(git_cmd, &block) }
611611
else
612-
out = `#{git_cmd} 2>&1`.chomp
612+
out = run_command(git_cmd, &block)
613613
end
614614

615615
if @logger
@@ -626,5 +626,13 @@ def command(cmd, opts = [], chdir = true)
626626
out
627627
end
628628

629+
def run_command(git_cmd, &block)
630+
if block_given?
631+
IO.popen(git_cmd, &block)
632+
else
633+
`#{git_cmd}`.chomp
634+
end
635+
end
636+
629637
end
630638
end

lib/git/object.rb

+11-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@ def size
2828
@size || @size = @base.lib.object_size(@objectish)
2929
end
3030

31-
# caches the contents of this call in memory
32-
def contents
33-
@contents || @contents = @base.lib.object_contents(@objectish)
31+
# get the object's contents
32+
# if no block is given, the contents are cached in memory and returned as a string
33+
# if a block is given, it yields an IO object (via IO::popen) which could be used to
34+
# read a large file in chunks. use this for large files so that they are not held
35+
# in memory
36+
def contents(&block)
37+
if block_given?
38+
@base.lib.object_contents(@objectish, &block)
39+
else
40+
@contents || @contents = @base.lib.object_contents(@objectish)
41+
end
3442
end
3543

3644
def contents_array

tests/units/test_lib.rb

+27
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,33 @@ def test_object_contents
8585
assert_equal(blob, @lib.object_contents('v2.5:example.txt')) #blob
8686

8787
end
88+
89+
def test_object_contents_with_block
90+
commit = "tree 94c827875e2cadb8bc8d4cdd900f19aa9e8634c7\n"
91+
commit += "parent 546bec6f8872efa41d5d97a369f669165ecda0de\n"
92+
commit += "author scott Chacon <schacon@agadorsparticus.corp.reactrix.com> 1194561188 -0800\n"
93+
commit += "committer scott Chacon <schacon@agadorsparticus.corp.reactrix.com> 1194561188 -0800\n"
94+
commit += "\ntest"
95+
96+
@lib.object_contents('1cc8667014381') do |f|
97+
assert_equal(commit, f.read.chomp)
98+
end
99+
100+
# commit
101+
102+
tree = "040000 tree 6b790ddc5eab30f18cabdd0513e8f8dac0d2d3ed\tex_dir\n"
103+
tree += "100644 blob 3aac4b445017a8fc07502670ec2dbf744213dd48\texample.txt"
104+
105+
@lib.object_contents('1cc8667014381^{tree}') do |f|
106+
assert_equal(tree, f.read.chomp) #tree
107+
end
108+
109+
blob = "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n2"
110+
111+
@lib.object_contents('v2.5:example.txt') do |f|
112+
assert_equal(blob, f.read.chomp) #blob
113+
end
114+
end
88115

89116
# returns Git::Branch object array
90117
def test_branches_all

tests/units/test_object.rb

+9
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ def test_blob_contents
102102
o = @git.gblob('v2.6:example.txt')
103103
assert_equal('replace with new text', o.contents)
104104
assert_equal('replace with new text', o.contents) # this should be cached
105+
106+
# make sure the block is called
107+
block_called = false
108+
o.contents do |f|
109+
block_called = true
110+
assert_equal('replace with new text', f.read.chomp)
111+
end
112+
113+
assert(block_called)
105114
end
106115

107116
def test_revparse

0 commit comments

Comments
 (0)