Skip to content

Commit

Permalink
Fix bug of git shallow cloning
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhongping committed Feb 11, 2022
1 parent eed7e8f commit b897624
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 20 deletions.
62 changes: 42 additions & 20 deletions lib/cocoapods-downloader/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,35 @@ def checkout_options
end

def self.preprocess_options(options)
return options if options[:commit] || options[:tag]
return options unless options[:branch]

command = ['ls-remote',
options[:git],
options[:branch]]
output = Git.execute_command('git', command)
match = commit_from_ls_remote output, options[:branch]

return options if match.nil?

options[:commit] = match
options.delete(:branch)

commit = commit_from_remote_ref(options[:git], options[:branch])
return options if commit.nil?
options[:commit] = commit
options
end

# Matches a commit from branch or tag by git remote url.
#
# @note When there is a branch and tag with the same name, it will match
# the branch, since `refs/heads` is sorted before `refs/tags`.
#
# @param [String] url
# The remote url of the git repository.
#
# @param [String] ref
# The desired branch or tag to match a commit to.
#
# @return [String] commit hash string, or nil if no match found
def self.commit_from_remote_ref(url, ref)
return nil if url.nil? || ref.nil?
command = ['ls-remote', url, ref]
output = Git.execute_command('git', command)
match = commit_from_ls_remote(output, ref)
match
end

# Matches a commit from the branches reported by git ls-remote.
#
# @note When there is a branch and tag with the same name, it will match
Expand All @@ -52,7 +65,7 @@ def self.preprocess_options(options)
#
def self.commit_from_ls_remote(output, branch_name)
return nil if branch_name.nil?
encoded_branch_name = branch_name.dup.force_encoding(Encoding::ASCII_8BIT)
encoded_branch_name = branch_name.dup.force_encoding(Encoding::UTF_8)
match = %r{([a-z0-9]*)\trefs\/(heads|tags)\/#{Regexp.quote(encoded_branch_name)}}.match(output)
match[1] unless match.nil?
end
Expand Down Expand Up @@ -129,17 +142,26 @@ def update_submodules
#
def clone_arguments(force_head, shallow_clone)
command = ['clone', url, target_path, '--template=']

if shallow_clone && !options[:commit]
command += %w(--single-branch --depth 1)
if force_head
if shallow_clone && !options[:commit]
command += %w(--single-branch --depth 1)
end
return command
end

unless force_head
if tag_or_branch = options[:tag] || options[:branch]
command += ['--branch', tag_or_branch]
if shallow_clone
if (Git.options & options.keys).empty?
command += %w(--single-branch --depth 1)
elsif ref = options[:branch] || options[:tag]
command += %w(--single-branch)
commit = Git.commit_from_remote_ref(url, ref)
if !options[:commit] || (commit == options[:commit])
command += %w(--depth 1)
end
end
end

if tag_or_branch = options[:tag] || options[:branch]
command += ['--branch', tag_or_branch]
end
command
end

Expand Down
109 changes: 109 additions & 0 deletions spec/git_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ def ensure_only_one_ref(folder)
end
end

def ensure_only_one_branch(folder)
Dir.chdir(folder) do
`git branch| wc -l`.strip.should == '1'
end
end

def ensure_only_one_tag(folder)
Dir.chdir(folder) do
`git tag| wc -l`.strip.should == '1'
end
end

before do
FileUtils.rm_rf('/tmp/git-submodule-repo')
FileUtils.cp_r(fixture('git-submodule-repo'), '/tmp/')
Expand Down Expand Up @@ -156,6 +168,59 @@ def ensure_only_one_ref(folder)
`git rev-list HEAD`.chomp.should.include '407e385'
end
end

# If 'preprocess_options' remove the options[:branch], it will fail here.
it 'clones a specific branch and preprocess options' do
options = { :git => fixture_url('git-repo'), :branch => 'topic_branch' }
options = Downloader.preprocess_options(options)
downloader = Downloader.for_target(tmp_folder, options)
downloader.download

tmp_folder('README').read.strip.should == 'topic_branch'
ensure_only_one_ref(tmp_folder)
end

it 'clones a specific branch and pointing commit' do
options = { :git => fixture_url('git-repo'), :branch => 'topic_branch', :commit => '4bd5b396b46445d897fd2eab4a56e868b9eb5fc4' } # rubocop:disable Metrics/LineLength
downloader = Downloader.for_target(tmp_folder, options)
downloader.download

tmp_folder('README').read.strip.should == 'topic_branch'
ensure_only_one_ref(tmp_folder)
end

it 'clones a specific tag and pointing commit' do
options = { :git => fixture_url('git-repo'), :tag => 'v1.0', :commit => '407e385d69d49a691eb6853f643317c61e417c83' } # rubocop:disable Metrics/LineLength
downloader = Downloader.for_target(tmp_folder, options)
downloader.download

tmp_folder('README').read.strip.should == 'v1.0'
ensure_only_one_ref(tmp_folder)
end

it 'clones a specific branch and non-pointing commit' do
options = { :git => fixture_url('git-repo'), :branch => 'topic_branch', :commit => '7ad3a6ccca379b6bd7e4ff9477448670d799c661' } # rubocop:disable Metrics/LineLength
downloader = Downloader.for_target(tmp_folder, options)
downloader.download
Dir.chdir(tmp_folder) do
`git rev-list HEAD`.chomp.should.include '7ad3a6ccca379b6bd7e4ff9477448670d799c661'
`git checkout topic_branch`
end
tmp_folder('README').read.strip.should == 'topic_branch'
ensure_only_one_branch(tmp_folder)
end

it 'clones a specific tag and non-pointing commit' do
options = { :git => fixture_url('git-repo'), :tag => 'v1.0', :commit => '7ad3a6ccca379b6bd7e4ff9477448670d799c661' } # rubocop:disable Metrics/LineLength
downloader = Downloader.for_target(tmp_folder, options)
downloader.download
Dir.chdir(tmp_folder) do
`git rev-list HEAD`.chomp.should.include '7ad3a6ccca379b6bd7e4ff9477448670d799c661'
`git checkout v1.0`
end
tmp_folder('README').read.strip.should == 'v1.0'
ensure_only_one_tag(tmp_folder)
end
end

describe 'Robustness' do
Expand Down Expand Up @@ -269,6 +334,15 @@ def ensure_only_one_ref(folder)
Git.send(:commit_from_ls_remote, nil, 'test_branch').should.nil?
Git.send(:commit_from_ls_remote, "123\trefs/heads/abc", nil).should.nil?
end

# If use Encoding::ASCII, it will fail here.
it 'finds commit for a branch with forced encoding' do
test_commit = '8edcd2fc53176ccb5fa7327f33b7dd733dbd3a06'
test_branch = '中文分支'
test_output = "#{test_commit}\trefs/heads/#{test_branch}"
result = Git.send(:commit_from_ls_remote, test_output, test_branch)
result.should == test_commit
end
end

describe '::preprocess_options' do
Expand All @@ -289,6 +363,41 @@ def ensure_only_one_ref(folder)
new_options = Downloader.preprocess_options(options)
new_options[:branch].should == 'aaaa'
end

# If 'preprocess_options' remove the `return options if options[:commit]`, it will fail here.
it 'don`t override commit' do
options = { :git => fixture_url('git-repo'), :branch => 'master', :commit => '7ad3a6c' }
new_options = Downloader.preprocess_options(options)
new_options[:commit].should.include '7ad3a6c'
end
end

describe ':commit_from_remote_ref' do
it 'match a commit for a branch' do
commit = Git.send(:commit_from_remote_ref, fixture_url('git-repo'), 'topic_branch')
commit.should == '4bd5b396b46445d897fd2eab4a56e868b9eb5fc4'
end

it 'match a commit for a tag' do
commit = Git.send(:commit_from_remote_ref, fixture_url('git-repo'), 'v1.0')
commit.should == '407e385d69d49a691eb6853f643317c61e417c83'
end

it 'match a commit for invalid branch' do
commit = Git.send(:commit_from_remote_ref, fixture_url('git-repo'), 'abcde')
commit.should.nil?
end

it 'match a commit for invalid tag' do
commit = Git.send(:commit_from_remote_ref, fixture_url('git-repo'), 'v1.abcde')
commit.should.nil?
end

it 'handles nil inputs' do
Git.send(:commit_from_remote_ref, nil, 'test_branch').should.nil?
Git.send(:commit_from_remote_ref, fixture_url('git-repo'), nil).should.nil?
Git.send(:commit_from_remote_ref, nil, nil).should.nil?
end
end
end
end
Expand Down

0 comments on commit b897624

Please sign in to comment.