Skip to content

Commit

Permalink
Merge pull request #56 from betesh/implement-porcelain-grep
Browse files Browse the repository at this point in the history
Implement porcelain grep
  • Loading branch information
bartkamphorst authored May 14, 2020
2 parents 0589b42 + 6402ef6 commit b6a91fe
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
30 changes: 30 additions & 0 deletions lib/rjgit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ def self.describe(repository, ref, options = {})
end
command.call
end

# options:
# * ref
# * path_filter
# * case_insensitive
def self.grep(repository, query, options={})
case_insensitive = options[:case_insensitive]
repo = RJGit.repository_type(repository)
walk = RevWalk.new(repo)
ls_tree_options = {:recursive => true, :path_filter => options[:path_filter]}

query = case query
when Regexp then query
when String then Regexp.new(Regexp.escape(query))
else raise "A #{query.class} was passed to #{self}.grep(). Only Regexps and Strings are supported!"
end

query = Regexp.new(query.source, query.options | Regexp::IGNORECASE) if case_insensitive

ls_tree(repo, nil, options.fetch(:ref, 'HEAD'), ls_tree_options).each_with_object({}) do |item, result|
blob = Blob.new(repo, item[:mode], item[:path], walk.lookup_blob(ObjectId.from_string(item[:id])))
next if blob.binary?

rows = blob.data.split("\n")
data = rows.grep(query)
next if data.empty?

result[blob.path] = data
end
end
end

module Plumbing
Expand Down
54 changes: 54 additions & 0 deletions spec/rjgit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,60 @@
end
end

describe 'git-grep' do
before(:all) do
File.open(File.join(@temp_repo_path, @testfile), 'a') {|file| file.write("\nAppending to test file") }
Porcelain.add(@repo, @testfile)
Porcelain.commit(@repo, 'Append to testfile')
end

it "finds files matching a String" do
expect(Porcelain.grep(@repo, ' file to add')).to eq(@testfile => ["This is a new file to add."])
end

it "finds files matching a Regexp" do
expect(Porcelain.grep(@repo, / file.*add/)).to eq(@testfile => ["This is a new file to add."])
end

it "supports the path option" do
expect(Porcelain.grep(@repo, / file.*add/, :path_filter => "chapters")).to eq({})
expect(Porcelain.grep(@repo, / file.*add/, :path_filter => "test_file.txt")).to eq(@testfile => ["This is a new file to add."])
end

it 'works on nested files' do
results = Porcelain.grep(@repo, /eventually/, :path_filter => "chapters")
expect(results.keys).to contain_exactly("chapters/prematerial.txt")
expect(results.fetch("chapters/prematerial.txt")).to contain_exactly(a_string_matching(/ eventually the rubicon/))
end

it 'supports the ref option' do
expect(Porcelain.grep(@repo, 'Append', ref: @repo.commits[0].id)).to eq(@testfile => ["Appending to test file"])
expect(Porcelain.grep(@repo, 'Append', ref: @repo.commits[1].id)).to eq({})
end

it 'ignores binary files' do
expect(Porcelain.grep(@repo, /./, :path_filter => "homer-excited.png")).to eq({})
end

it 'only greps for Regexps and Strings' do
expect { Porcelain.grep(@repo, 10..20) }.to raise_error("A Range was passed to RJGit::Porcelain.grep(). Only Regexps and Strings are supported!")
end

it 'supports case-insensitivity for a Regexp' do
expect(Porcelain.grep(@repo, /\AThis is a/, :path_filter => "test_file.txt")).to eq(@testfile => ["This is a new file to add."])
expect(Porcelain.grep(@repo, /\Athis Is A/, :path_filter => "test_file.txt")).to eq({})
expect(Porcelain.grep(@repo, /\Athis Is A/, :path_filter => "test_file.txt", case_insensitive: true)).to eq(@testfile => ["This is a new file to add."])
expect(Porcelain.grep(@repo, /\Athis Is Not A/, :path_filter => "test_file.txt", case_insensitive: true)).to eq({})
end

it 'supports case-insensitivity for a String' do
expect(Porcelain.grep(@repo, "This is a", :path_filter => "test_file.txt")).to eq(@testfile => ["This is a new file to add."])
expect(Porcelain.grep(@repo, "this Is A", :path_filter => "test_file.txt")).to eq({})
expect(Porcelain.grep(@repo, "this Is A", :path_filter => "test_file.txt", case_insensitive: true)).to eq(@testfile => ["This is a new file to add."])
expect(Porcelain.grep(@repo, "this Is Not A", :path_filter => "test_file.txt", case_insensitive: true)).to eq({})
end
end

after(:all) do
@repo = nil
remove_temp_repo(@temp_repo_path)
Expand Down

0 comments on commit b6a91fe

Please sign in to comment.