class Bundler::Source::Git::GitProxy

The GitProxy is responsible to interact with git repositories. All actions required by the Git source is encapsulated in this object.

Attributes

branch[RW]
explicit_ref[RW]
path[RW]
ref[RW]
revision[W]
tag[RW]
uri[RW]

Public Class Methods

new(path, uri, options = {}, revision = nil, git = nil) click to toggle source
# File bundler/source/git/git_proxy.rb, line 52
def initialize(path, uri, options = {}, revision = nil, git = nil)
  @path     = path
  @uri      = uri
  @branch   = options["branch"]
  @tag      = options["tag"]
  @ref      = options["ref"]
  @explicit_ref = branch || tag || ref
  @revision = revision
  @git      = git
  @commit_ref = nil
end

Public Instance Methods

checkout() click to toggle source
# File bundler/source/git/git_proxy.rb, line 89
def checkout
  return if has_revision_cached?

  Bundler.ui.info "Fetching #{credential_filtered_uri}"

  extra_fetch_needed = clone_needs_extra_fetch?
  unshallow_needed = clone_needs_unshallow?
  return unless extra_fetch_needed || unshallow_needed

  git_remote_fetch(unshallow_needed ? ["--unshallow"] : depth_args)
end
contains?(commit) click to toggle source
# File bundler/source/git/git_proxy.rb, line 74
def contains?(commit)
  allowed_with_path do
    result, status = git_null("branch", "--contains", commit, :dir => path)
    status.success? && result =~ /^\* (.*)$/
  end
end
copy_to(destination, submodules = false) click to toggle source
# File bundler/source/git/git_proxy.rb, line 101
def copy_to(destination, submodules = false)
  unless File.exist?(destination.join(".git"))
    begin
      SharedHelpers.filesystem_access(destination.dirname) do |p|
        FileUtils.mkdir_p(p)
      end
      SharedHelpers.filesystem_access(destination) do |p|
        FileUtils.rm_rf(p)
      end
      git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
      File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination)
    rescue Errno::EEXIST => e
      file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1]
      raise GitError, "Bundler could not install a gem because it needs to " \
        "create a directory, but a file exists - #{file_path}. Please delete " \
        "this file and try again."
    end
  end

  git "fetch", "--force", "--quiet", *extra_fetch_args, :dir => destination if @commit_ref

  git "reset", "--hard", @revision, :dir => destination

  if submodules
    git_retry "submodule", "update", "--init", "--recursive", :dir => destination
  elsif Gem::Version.create(version) >= Gem::Version.create("2.9.0")
    inner_command = "git -C $toplevel submodule deinit --force $sm_path"
    git_retry "submodule", "foreach", "--quiet", inner_command, :dir => destination
  end
end
current_branch() click to toggle source
# File bundler/source/git/git_proxy.rb, line 68
def current_branch
  @current_branch ||= allowed_with_path do
    git("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
  end
end
full_version() click to toggle source
# File bundler/source/git/git_proxy.rb, line 85
def full_version
  @full_version ||= git("--version").sub(/git version\s*/, "").strip
end
revision() click to toggle source
# File bundler/source/git/git_proxy.rb, line 64
def revision
  @revision ||= allowed_with_path { find_local_revision }
end
version() click to toggle source
# File bundler/source/git/git_proxy.rb, line 81
def version
  @version ||= full_version.match(/((\.?\d+)+).*/)[1]
end

Private Instance Methods

allow?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 300
def allow?
  allowed = @git ? @git.allow_git_ops? : true

  raise GitNotInstalledError.new if allowed && !Bundler.git_present?

  allowed
end
allowed_with_path() { || ... } click to toggle source
# File bundler/source/git/git_proxy.rb, line 313
def allowed_with_path
  return with_path { yield } if allow?
  raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
end
capture(cmd, dir, ignore_err: false) click to toggle source
# File bundler/source/git/git_proxy.rb, line 325
def capture(cmd, dir, ignore_err: false)
  SharedHelpers.with_clean_git_env do
    require "open3"
    out, err, status = Open3.capture3(*capture3_args_for(cmd, dir))

    filtered_out = URICredentialsFilter.credential_filtered_string(out, uri)
    return [filtered_out, status] if ignore_err

    filtered_err = URICredentialsFilter.credential_filtered_string(err, uri)
    [filtered_out, filtered_err, status]
  end
end
capture3_args_for(cmd, dir) click to toggle source
# File bundler/source/git/git_proxy.rb, line 338
def capture3_args_for(cmd, dir)
  return ["git", *cmd] unless dir

  if Bundler.feature_flag.bundler_3_mode? || supports_minus_c?
    ["git", "-C", dir.to_s, *cmd]
  else
    ["git", *cmd, { :chdir => dir.to_s }]
  end
end
check_allowed(command) click to toggle source
# File bundler/source/git/git_proxy.rb, line 318
def check_allowed(command)
  require "shellwords"
  command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
  raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
  command_with_no_credentials
end
clone_needs_extra_fetch?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 150
def clone_needs_extra_fetch?
  return true if path.exist?

  SharedHelpers.filesystem_access(path.dirname) do |p|
    FileUtils.mkdir_p(p)
  end
  git_retry "clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s

  extra_ref
end
clone_needs_unshallow?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 161
def clone_needs_unshallow?
  return false unless path.join("shallow").exist?
  return true if full_clone?

  @revision && @revision != head_revision
end
configured_uri() click to toggle source

Adds credentials to the URI

# File bundler/source/git/git_proxy.rb, line 282
def configured_uri
  if /https?:/.match?(uri)
    remote = Bundler::URI(uri)
    config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
    remote.userinfo ||= config_auth
    remote.to_s
  elsif File.exist?(uri)
    "file://#{uri}"
  else
    uri.to_s
  end
end
credential_filtered_uri() click to toggle source

Removes credentials from the URI

# File bundler/source/git/git_proxy.rb, line 296
def credential_filtered_uri
  URICredentialsFilter.credential_filtered_uri(uri)
end
depth() click to toggle source
# File bundler/source/git/git_proxy.rb, line 175
def depth
  return @depth if defined?(@depth)

  @depth = if !supports_fetching_unreachable_refs?
    nil
  elsif not_pinned? || pinned_to_full_sha?
    1
  elsif ref.include?("~")
    parsed_depth = ref.split("~").last
    parsed_depth.to_i + 1
  end
end
depth_args() click to toggle source
# File bundler/source/git/git_proxy.rb, line 359
def depth_args
  return [] if full_clone?

  ["--depth", depth.to_s]
end
extra_clone_args() click to toggle source
# File bundler/source/git/git_proxy.rb, line 348
def extra_clone_args
  args = depth_args
  return [] if args.empty?

  args += ["--single-branch"]
  args.unshift("--no-tags") if supports_cloning_with_no_tags?

  args += ["--branch", branch || tag] if branch || tag
  args
end
extra_fetch_args() click to toggle source
# File bundler/source/git/git_proxy.rb, line 365
def extra_fetch_args
  extra_args = [path.to_s, *depth_args]
  extra_args.push(@commit_ref)
  extra_args
end
extra_ref() click to toggle source
# File bundler/source/git/git_proxy.rb, line 168
def extra_ref
  return false if not_pinned?
  return true unless full_clone?

  ref.start_with?("refs/")
end
find_local_revision() click to toggle source
# File bundler/source/git/git_proxy.rb, line 261
def find_local_revision
  return head_revision if explicit_ref.nil?

  find_revision_for(explicit_ref)
end
find_revision_for(reference) click to toggle source
# File bundler/source/git/git_proxy.rb, line 271
def find_revision_for(reference)
  verify(reference)
rescue GitCommandError => e
  raise MissingGitRevisionError.new(e.command, path, reference, credential_filtered_uri)
end
full_clone?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 371
def full_clone?
  depth.nil?
end
fully_qualified_ref() click to toggle source
# File bundler/source/git/git_proxy.rb, line 209
def fully_qualified_ref
  if branch
    "refs/heads/#{branch}"
  elsif tag
    "refs/tags/#{tag}"
  elsif ref.nil?
    "refs/heads/#{current_branch}"
  end
end
git(*command, dir: nil) click to toggle source
# File bundler/source/git/git_proxy.rb, line 241
def git(*command, dir: nil)
  command_with_no_credentials = check_allowed(command)

  out, err, status = capture(command, dir)

  raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, err) unless status.success?

  Bundler.ui.warn err unless err.empty?

  out
end
git_null(*command, dir: nil) click to toggle source
# File bundler/source/git/git_proxy.rb, line 227
def git_null(*command, dir: nil)
  check_allowed(command)

  capture(command, dir, :ignore_err => true)
end
git_remote_fetch(args) click to toggle source
# File bundler/source/git/git_proxy.rb, line 134
def git_remote_fetch(args)
  command = ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
  command_with_no_credentials = check_allowed(command)

  Bundler::Retry.new("`#{command_with_no_credentials}` at #{path}", [MissingGitRevisionError]).attempts do
    out, err, status = capture(command, path)
    return out if status.success?

    if err.include?("couldn't find remote ref")
      raise MissingGitRevisionError.new(command_with_no_credentials, path, explicit_ref, credential_filtered_uri)
    else
      raise GitCommandError.new(command_with_no_credentials, path, err)
    end
  end
end
git_retry(*command, dir: nil) click to toggle source
# File bundler/source/git/git_proxy.rb, line 233
def git_retry(*command, dir: nil)
  command_with_no_credentials = check_allowed(command)

  Bundler::Retry.new("`#{command_with_no_credentials}` at #{dir || SharedHelpers.pwd}").attempts do
    git(*command, :dir => dir)
  end
end
has_revision_cached?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 253
def has_revision_cached?
  return unless @revision && path.exist?
  git("cat-file", "-e", @revision, :dir => path)
  true
rescue GitError
  false
end
head_revision() click to toggle source
# File bundler/source/git/git_proxy.rb, line 267
def head_revision
  verify("HEAD")
end
not_pinned?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 219
def not_pinned?
  branch || tag || ref.nil?
end
pinned_to_full_sha?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 223
def pinned_to_full_sha?
  ref =~ /\A\h{40}\z/
end
refspec() click to toggle source
# File bundler/source/git/git_proxy.rb, line 188
def refspec
  commit = pinned_to_full_sha? ? ref : @revision

  if commit
    @commit_ref = "refs/#{commit}-sha"
    return "#{commit}:#{@commit_ref}"
  end

  reference = fully_qualified_ref

  reference ||= if ref.include?("~")
    ref.split("~").first
  elsif ref.start_with?("refs/")
    ref
  else
    "refs/*"
  end

  "#{reference}:#{reference}"
end
supports_cloning_with_no_tags?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 383
def supports_cloning_with_no_tags?
  @supports_cloning_with_no_tags ||= Gem::Version.new(version) >= Gem::Version.new("2.14.0-rc0")
end
supports_fetching_unreachable_refs?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 379
def supports_fetching_unreachable_refs?
  @supports_fetching_unreachable_refs ||= Gem::Version.new(version) >= Gem::Version.new("2.5.0")
end
supports_minus_c?() click to toggle source
# File bundler/source/git/git_proxy.rb, line 375
def supports_minus_c?
  @supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
end
verify(reference) click to toggle source
# File bundler/source/git/git_proxy.rb, line 277
def verify(reference)
  git("rev-parse", "--verify", reference, :dir => path).strip
end
with_path(&blk) click to toggle source
# File bundler/source/git/git_proxy.rb, line 308
def with_path(&blk)
  checkout unless path.exist?
  blk.call
end