class DTK::Client::GitAdapter

Constants

GitErrorPattern
TEMP_BRANCH

Attributes

git_repo[RW]

Public Class Methods

clone(repo_url, target_path, branch, opts={}) click to toggle source
# File lib/domain/git_adapter.rb, line 251
def self.clone(repo_url, target_path, branch, opts={})
  git_base = handle_git_error{Git.clone(repo_url, target_path)}

  unless branch.nil?
    if opts[:track_remote_branch]
      # This just tracks remote branch
      begin
        git_base.checkout(branch)
      rescue => e
        # TODO: see if any other kind of error
        raise DtkError.new("The branch or tag '#{branch}' does not exist on repo '#{repo_url}'")
      end
  else
      # This wil first create a remote branch;
      # TODO: this might be wrong and should be deprecated
      git_base.branch(branch).checkout
    end
  end
    git_base
end
new(repo_dir, local_branch_name = nil) click to toggle source
# File lib/domain/git_adapter.rb, line 29
      def initialize(repo_dir, local_branch_name = nil)

        if DTK::Configuration.get(:debug_grit)
          logger       = Logger.new(STDOUT)
          logger.level = Logger::INFO
          @git_repo = Git.init(repo_dir, :log => logger)
        else
          @git_repo = Git.init(repo_dir)
        end
#       If we want to log GIT interaction
#       @git_repo = Git.init(repo_dir, :log => Logger.new(STDOUT))
        @local_branch_name = local_branch_name
      end

Private Class Methods

error_msg_when_git_error(lines) click to toggle source
# File lib/domain/git_adapter.rb, line 345
def self.error_msg_when_git_error(lines)
  ret = lines.last.gsub(GitErrorPattern,'').strip()
  # TODO start putting in special cases here
  if ret =~ /adding files failed/
    if lines.first =~ /\.git/
      ret = "Cannot add files that are in a .git directory; remove any nested .git directory"
    end
  end
  ret
end
handle_git_error() { || ... } click to toggle source
# File lib/domain/git_adapter.rb, line 326
def self.handle_git_error(&block)
  ret = nil
  begin
    ret = yield
   rescue => e
    unless e.respond_to?(:message)
      raise e
    else
      err_msg = e.message
      lines = err_msg.split("\n")
      if lines.last =~ GitErrorPattern
        err_msg = error_msg_when_git_error(lines)
      end
      raise DtkError.new(err_msg)
    end
  end
  ret
end

Public Instance Methods

add_file(file_rel_path, content) click to toggle source
# File lib/domain/git_adapter.rb, line 228
def add_file(file_rel_path, content)
  content ||= String.new
  file_path = "#{@git_repo.dir}/#{file_rel_path}"
  File.open(file_path,"w"){|f|f << content}
  @git_repo.add(file_path)
end
add_remote(name, url) click to toggle source
# File lib/domain/git_adapter.rb, line 135
def add_remote(name, url)
  @git_repo.remove_remote(name) if is_there_remote?(name)

  @git_repo.add_remote(name, url)
end
changed?() click to toggle source
# File lib/domain/git_adapter.rb, line 47
def changed?
  (!(changed().empty? && untracked().empty? && deleted().empty?) || staged_commits?)
end
checkout(branch, opts = {}) click to toggle source
# File lib/domain/git_adapter.rb, line 214
def checkout(branch, opts = {})
  @git_repo.checkout(branch, opts)
end
command(*args, &block) click to toggle source
# File lib/domain/git_adapter.rb, line 43
def command(*args, &block)
  @git_repo.lib.command(*args, &block)
end
commit(commit_msg = "") click to toggle source
# File lib/domain/git_adapter.rb, line 131
def commit(commit_msg = "")
  @git_repo.commit(commit_msg)
end
current_branch() click to toggle source
# File lib/domain/git_adapter.rb, line 297
def current_branch()
  @git_repo.branches.local.find { |b| b.current }
end
delete_branch(branch, opts = {}) click to toggle source
# File lib/domain/git_adapter.rb, line 210
def delete_branch(branch, opts = {})
  @git_repo.branch(branch).delete
end
diff_remote_summary(local_branch, remote_reference) click to toggle source
# File lib/domain/git_adapter.rb, line 97
def diff_remote_summary(local_branch, remote_reference)
  branch_local_obj  = @git_repo.branches.local.find { |b| b.name == local_branch }
  branch_remote_obj = @git_repo.branches.remote.find{|r| "#{r.remote}/#{r.name}" == remote_reference }

  if branch_local_obj && branch_remote_obj
      difference = @git_repo.lib.diff_full(branch_remote_obj, branch_local_obj)
      # difference = @git_repo.diff(branch_remote_obj, branch_local_obj)
    {
      :diffs => difference
    }
  else
    raise Error.new("Error finding branches: local branch '#{local_branch}' (found: #{!branch_local_obj.nil?}), remote branch '#{remote_reference}' (found: #{!branch_remote_obj.nil?})")
  end
end
diff_summary(local_branch, remote_reference) click to toggle source
# File lib/domain/git_adapter.rb, line 79
def diff_summary(local_branch, remote_reference)
  branch_local_obj  = @git_repo.branches.local.find { |b| b.name == local_branch }
  branch_remote_obj = @git_repo.branches.remote.find{|r| "#{r.remote}/#{r.name}" == remote_reference }

  if branch_local_obj && branch_remote_obj

    difference = @git_repo.diff(branch_local_obj, branch_remote_obj)

    files_modified = difference.stats[:files] ? difference.stats[:files].keys.collect { |file| { :path => file }} : []
    {
      :files_modified => files_modified,
      :are_there_changes => !files_modified.empty?
    }
  else
    raise Error.new("Error finding branches: local branch '#{local_branch}' (found: #{!branch_local_obj.nil?}), remote branch '#{remote_reference}' (found: #{!branch_remote_obj.nil?})")
  end
end
fetch(remote = 'origin') click to toggle source
# File lib/domain/git_adapter.rb, line 141
def fetch(remote = 'origin')
  @git_repo.fetch(remote)
end
find_remote_sha(ref) click to toggle source
# File lib/domain/git_adapter.rb, line 163
def find_remote_sha(ref)
  remote = @git_repo.branches.remote.find{|r| "#{r.remote}/#{r.name}" == ref}
  remote.gcommit.sha
end
head_commit_sha() click to toggle source
# File lib/domain/git_adapter.rb, line 159
def head_commit_sha()
  ret_local_branch.gcommit.sha
end
local_branch_name() click to toggle source
# File lib/domain/git_adapter.rb, line 280
def local_branch_name
  ret_local_branch.name
end
local_summary() click to toggle source
# File lib/domain/git_adapter.rb, line 112
def local_summary()

  {
    :files_added => (untracked() + added()).collect { |file| { :path => file }},
    :files_modified => changed().collect { |file| { :path => file }},
    :files_deleted => deleted().collect { |file| { :path => file }},
    :are_there_changes => something_changed?
  }
end
merge(remote_branch_ref) click to toggle source
# File lib/domain/git_adapter.rb, line 247
def merge(remote_branch_ref)
  @git_repo.merge(remote_branch_ref)
end
merge_relationship(type, ref, opts={}) click to toggle source
# File lib/domain/git_adapter.rb, line 168
def merge_relationship(type, ref, opts={})
  ref_remote, ref_branch = ref.split('/')
  # fetch remote branch
  fetch(ref_remote) if opts[:fetch_if_needed]


  git_reference = case type
    when :remote_branch
      @git_repo.branches.remote.find { |r| "#{r.remote}/#{r.name}" == ref }
    when :local_branch
      @git_repo.branches.find { |b| b.name == ref }
    else
      raise Error.new("Illegal type parameter (#{type}) passed to merge_relationship")
  end

  local_sha = ret_local_branch.gcommit.sha

  opts[:ret_commit_shas][:local_sha] = local_sha if opts[:ret_commit_shas]

  unless git_reference
    return :no_remote_ref if type.eql?(:remote_branch)

    raise Error.new("Cannot find git ref '#{ref}'")
  end

  git_reference_sha = git_reference.gcommit.sha
  opts[:ret_commit_shas][:other_sha] = git_reference_sha if opts[:ret_commit_shas]

  # shas can be different but content the same
  if git_reference_sha.eql?(local_sha) || !any_differences?(local_sha, git_reference_sha)
    :equal
  else
    if rev_list_contains?(local_sha, git_reference_sha)
      :local_ahead
    elsif rev_list_contains?(git_reference_sha, local_sha)
      :local_behind
    else
      :branchpoint
    end
  end
end
merge_theirs(remote_branch_ref) click to toggle source
# File lib/domain/git_adapter.rb, line 303
def merge_theirs(remote_branch_ref)
  branch = local_branch_name

  # Git is not agile enoguh to work with following commands so we are using native commands to achive this
  Dir.chdir(repo_dir) do
    OsUtil.suspend_output do
      puts `git checkout -b #{TEMP_BRANCH} #{remote_branch_ref}`
      puts `git merge #{branch} -s ours`
      puts `git checkout #{branch}`
      puts `git reset #{TEMP_BRANCH} --hard`
      puts `git branch -D #{TEMP_BRANCH}`
    end
  end
end
new_version() click to toggle source
# File lib/domain/git_adapter.rb, line 122
def new_version()
  return local_summary()
end
print_status() click to toggle source
pull_remote_to_local(remote_branch, local_branch, remote='origin') click to toggle source
# File lib/domain/git_adapter.rb, line 235
def pull_remote_to_local(remote_branch, local_branch, remote='origin')
  # special case; if no branches and local_branch differs from master
  # creates master plus local_branch
  # special_case must be calculated before pull
  special_case = current_branch().nil? and local_branch != 'master'
  @git_repo.pull(remote,"#{remote_branch}:#{local_branch}")
  if special_case
    @git_repo.branch(local_branch).checkout
    @git_repo.branch('master').delete
  end
end
push(remote_branch_ref, opts={}) click to toggle source
# File lib/domain/git_adapter.rb, line 218
def push(remote_branch_ref, opts={})
  remote, remote_branch = remote_branch_ref.split('/')
  push_with_remote(remote, remote_branch, opts)
end
push_with_remote(remote, remote_branch, opts={}) click to toggle source
# File lib/domain/git_adapter.rb, line 223
def push_with_remote(remote, remote_branch, opts={})
  branch_for_push = "#{local_branch_name}:refs/heads/#{remote_branch||local_branch_name}"
  @git_repo.push(remote, branch_for_push, opts)
end
repo_dir() click to toggle source
# File lib/domain/git_adapter.rb, line 272
def repo_dir
  @git_repo.dir.path
end
repo_exists?() click to toggle source
# File lib/domain/git_adapter.rb, line 276
def repo_exists?
  File.exists?(repo_dir)
end
reset_hard(current_branch_sha) click to toggle source
# File lib/domain/git_adapter.rb, line 318
def reset_hard(current_branch_sha)
  @git_repo.reset_hard(current_branch_sha)
end
ret_local_branch() click to toggle source
# File lib/domain/git_adapter.rb, line 284
def ret_local_branch
  # This build in assumption that just one local branch
  unless ret = current_branch()
    raise Error.new("Unexpected that current_branch() is nil")
  end
  if @local_branch_name
    unless ret.name == @local_branch_name
      raise Error.new("Unexpected that @local_branch_name (#{@local_branch_name}) does not equal current branch (#{current_branch()})")
    end
  end
  ret
end
rev_list(commit_sha) click to toggle source
# File lib/domain/git_adapter.rb, line 145
def rev_list(commit_sha)
  git_command('rev-list', commit_sha)
end
rev_list_contains?(container_sha, index_sha) click to toggle source
# File lib/domain/git_adapter.rb, line 154
def rev_list_contains?(container_sha, index_sha)
  results = rev_list(container_sha)
  !results.split("\n").grep(index_sha).empty?
end
stage_and_commit(commit_msg = "") click to toggle source
# File lib/domain/git_adapter.rb, line 126
def stage_and_commit(commit_msg = "")
  stage_changes()
  commit(commit_msg)
end
stage_changes() click to toggle source
# File lib/domain/git_adapter.rb, line 51
def stage_changes()
  handle_git_error do
    @git_repo.add(untracked())
    @git_repo.add(added())
    @git_repo.add(changed())
  end
  deleted().each do |file|
    begin
      @git_repo.remove(file)
    rescue
      # ignore this error means file has already been staged
      # we cannot support status of file, in 1.8.7 so this is
      # solution for that
    end
  end
end
staged_commits?() click to toggle source
# File lib/domain/git_adapter.rb, line 149
def staged_commits?()
  response = git_command('diff','--cached')
  !response.empty?
end

Private Instance Methods

added() click to toggle source
# File lib/domain/git_adapter.rb, line 374
def added
  status.is_a?(Hash) ? status.added().keys : status.added().collect { |file| file.first }
end
any_differences?(sha1, sha2) click to toggle source
# File lib/domain/git_adapter.rb, line 390
def any_differences?(sha1, sha2)
  @git_repo.diff(sha1, sha2).size > 0
end
changed() click to toggle source

Method bellow show different behavior when working with 1.8.7 so based on Hash response we know it it is: Hash => 1.9.3 + Array => 1.8.7

# File lib/domain/git_adapter.rb, line 362
def changed
  status.is_a?(Hash) ? status.changed().keys : status.changed().collect { |file| file.first }
end
deleted() click to toggle source
# File lib/domain/git_adapter.rb, line 370
def deleted
  status.is_a?(Hash) ? status.deleted().keys : status.deleted().collect { |file| file.first }
end
git_command(cmd, opts=[]) click to toggle source
# File lib/domain/git_adapter.rb, line 394
def git_command(cmd, opts=[])
  ENV['GIT_DIR'] = "#{@git_repo.dir.path}/.git"
  ENV['GIT_INDEX_FILE'] = @git_repo.index.path

  path = @git_repo.dir.path

  opts = [opts].flatten.join(' ')

  response = `git #{cmd} #{opts}`.chomp

  ENV.delete('GIT_DIR')
  ENV.delete('GIT_INDEX_FILE')

  return response
end
handle_git_error(&block) click to toggle source
# File lib/domain/git_adapter.rb, line 323
def handle_git_error(&block)
  self.class.handle_git_error(&block)
end
is_there_remote?(remote_name) click to toggle source
# File lib/domain/git_adapter.rb, line 386
def is_there_remote?(remote_name)
  @git_repo.remotes.find { |r| r.name == remote_name }
end
something_changed?() click to toggle source
# File lib/domain/git_adapter.rb, line 378
def something_changed?
  ![changed, untracked, deleted, added].flatten.empty?
end
status() click to toggle source
# File lib/domain/git_adapter.rb, line 382
def status
  @git_repo.status
end
untracked() click to toggle source
# File lib/domain/git_adapter.rb, line 366
def untracked
  status.is_a?(Hash) ? status.untracked().keys : status.untracked().collect { |file| file.first }
end