class Bundler::Source::Git
def self.from_lock(options)
def self.from_lock(options) new(options.merge("uri" => options.delete("remote"))) end
def allow_git_ops?
def allow_git_ops? @allow_remote || @allow_cached end
def base_name
def base_name File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)},''), ".git") end
def cache
def cache if cached? return if has_revision_cached? Bundler.ui.info "Updating #{uri}" in_cache do git %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"| end else Bundler.ui.info "Fetching #{uri}" FileUtils.mkdir_p(cache_path.dirname) git %|clone #{uri_escaped} "#{cache_path}" --bare --no-hardlinks| end end
def cache_path
def cache_path @cache_path ||= begin git_scope = "#{base_name}-#{uri_hash}" if Bundler.requires_sudo? Bundler.user_bundle_path.join("cache/git", git_scope) else Bundler.cache.join("git", git_scope) end end end
def cached?
def cached? cache_path.exist? end
def checkout
def checkout unless File.exist?(path.join(".git")) FileUtils.mkdir_p(path.dirname) FileUtils.rm_rf(path) git %|clone --no-checkout "#{cache_path}" "#{path}"| File.chmod((0777 & ~File.umask), path) end Dir.chdir(path) do git %|fetch --force --quiet --tags "#{cache_path}"| git "reset --hard #{revision}" if @submodules git "submodule init" git "submodule update" end end end
def eql?(o)
def eql?(o) Git === o && uri == o.uri && ref == o.ref && name == o.name && version == o.version && submodules == o.submodules end
def git(command)
def git(command) if allow_git_ops? out = %x{git #{command}} if $?.exitstatus != 0 msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed." msg << "\nIf this error persists you could try removing the cache directory '#{cache_path}'" if cached? raise GitError, msg end out else raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \ "this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \ "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}" end end
def has_revision_cached?
def has_revision_cached? return unless @revision in_cache { git %|cat-file -e #{@revision}| } true rescue GitError false end
def in_cache(&blk)
def in_cache(&blk) cache unless cached? Dir.chdir(cache_path, &blk) end
def initialize(options)
def initialize(options) super # stringify options that could be set as symbols %w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] } @uri = options["uri"] @ref = options["ref"] || options["branch"] || options["tag"] || 'master' @revision = options["revision"] @submodules = options["submodules"] @update = false end
def install(spec)
def install(spec) Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} " unless @installed Bundler.ui.debug " * Checking out revision: #{ref}" checkout if allow_git_ops? @installed = true end generate_bin(spec) end
def load_spec_files
def load_spec_files super rescue PathError, GitError raise GitError, "#{to_s} is not checked out. Please run `bundle install`" end
def name
def name File.basename(@uri, '.git') end
def path
def path @install_path ||= begin git_scope = "#{base_name}-#{shortref_for_path(revision)}" if Bundler.requires_sudo? Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope) else Bundler.install_path.join(git_scope) end end end
def revision
def revision @revision ||= begin if allow_git_ops? in_cache { git("rev-parse #{ref}").strip } else raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application" end end end
def shortref_for_display(ref)
def shortref_for_display(ref) ref[0..6] end
def shortref_for_path(ref)
def shortref_for_path(ref) ref[0..11] end
def specs
def specs if allow_git_ops? && !@update # Start by making sure the git cache is up to date cache checkout @update = true end local_specs end
def to_lock
def to_lock out = "GIT\n" out << " remote: #{@uri}\n" out << " revision: #{revision}\n" %w(ref branch tag submodules).each do |opt| out << " #{opt}: #{options[opt]}\n" if options[opt] end out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB out << " specs:\n" end
def to_s
def to_s sref = options["ref"] ? shortref_for_display(options["ref"]) : ref "#{uri} (at #{sref})" end
def unlock!
def unlock! @revision = nil end
def uri_escaped
within the URI we must end the string, escape the quote and
Escape the URI for shell commands. To support a single quote
def uri_escaped "'#{uri.gsub("'") {|s| "'\\''"}}'" end
def uri_hash
def uri_hash if uri =~ %r{^\w+://(\w+@)?} # Downcase the domain component of the URI # and strip off a trailing slash, if one is present input = URI.parse(uri).normalize.to_s.sub(%r{/$},'') else # If there is no URI scheme, assume it is an ssh/git URI input = uri end Digest::SHA1.hexdigest(input) end