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

TODO: actually cache git 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

restart.
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