class Danger::GitRepo

Attributes

diff[RW]
folder[RW]
log[RW]

Public Instance Methods

diff_for_folder(folder, from: "master", to: "HEAD", lookup_top_level: false) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 9
def diff_for_folder(folder, from: "master", to: "HEAD", lookup_top_level: false)
  self.folder = folder
  git_top_level = find_git_top_level_if_needed!(folder, lookup_top_level)

  repo = Git.open(git_top_level)

  ensure_commitish_exists!(from)
  ensure_commitish_exists!(to)

  merge_base = find_merge_base(repo, from, to)
  commits_in_branch_count = commits_in_branch_count(from, to)

  self.diff = repo.diff(merge_base, to)
  self.log = repo.log(commits_in_branch_count).between(from, to)
end
ensure_commitish_exists!(commitish) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 73
def ensure_commitish_exists!(commitish)
  return ensure_commitish_exists_on_branch!(commitish, commitish) if commit_is_ref?(commitish)
  return if commit_exists?(commitish)

  git_in_depth_fetch
  raise_if_we_cannot_find_the_commit(commitish) if commit_not_exists?(commitish)
end
ensure_commitish_exists_on_branch!(branch, commitish) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 81
def ensure_commitish_exists_on_branch!(branch, commitish)
  return if commit_exists?(commitish)

  depth = 0
  success =
    (3..6).any? do |factor|
      depth += Math.exp(factor).to_i

      git_fetch_branch_to_depth(branch, depth)
      commit_exists?(commitish)
    end

  return if success

  git_in_depth_fetch
  raise_if_we_cannot_find_the_commit(commitish) if commit_not_exists?(commitish)
end
exec(string) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 51
def exec(string)
  require "open3"
  Dir.chdir(self.folder || ".") do
    git_command = string.split(" ").dup.unshift("git")
    Open3.popen2(default_env, *git_command) do |_stdin, stdout, _wait_thr|
      stdout.read.rstrip
    end
  end
end
head_commit() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 61
def head_commit
  exec("rev-parse HEAD")
end
origins() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 69
def origins
  exec("remote show origin -n").lines.grep(/Fetch URL/)[0].split(": ", 2)[1].chomp
end
renamed_files() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 25
def renamed_files
  # Get raw diff with --find-renames --diff-filter
  # We need to pass --find-renames cause
  # older versions of git don't use this flag as default
  diff = exec(
    "diff #{self.diff.from} #{self.diff.to} --find-renames --diff-filter=R"
  ).lines.map { |line| line.tr("\n", "") }

  before_name_regexp = /^rename from (.*)$/
  after_name_regexp = /^rename to (.*)$/

  # Extract old and new paths via regexp
  diff.each_with_index.map do |line, index|
    before_match = line.match(before_name_regexp)
    next unless before_match

    after_match = diff.fetch(index + 1, "").match(after_name_regexp)
    next unless after_match

    {
      before: before_match.captures.first,
      after: after_match.captures.first
    }
  end.compact
end
tags() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 65
def tags
  exec("tag")
end

Private Instance Methods

commit_exists?(sha1) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 117
def commit_exists?(sha1)
  !commit_not_exists?(sha1)
end
commit_is_ref?(commit) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 167
def commit_is_ref?(commit)
  /[a-f0-9]{5,40}/ !~ commit
end
commit_not_exists?(sha1) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 121
def commit_not_exists?(sha1)
  exec("rev-parse --quiet --verify #{sha1}^{commit}").empty?
end
commits_in_branch_count(from, to) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 163
def commits_in_branch_count(from, to)
  exec("rev-list #{from}..#{to} --count").to_i
end
compare_path(path1, path2) click to toggle source

Compare given paths as realpath. Return true if both are same. ‘git rev-parse –show-toplevel` returns a path resolving symlink. In rspec, given path can be a temporary directory’s path created under a symlinked directory ‘/var`.

# File lib/danger/scm_source/git_repo.rb, line 187
def compare_path(path1, path2)
  Pathname.new(path1).realdirpath == Pathname.new(path2).realdirpath
end
default_env() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 109
def default_env
  { "LANG" => "en_US.UTF-8" }
end
find_git_top_level_if_needed!(folder, lookup_top_level) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 171
def find_git_top_level_if_needed!(folder, lookup_top_level)
  git_top_level = Dir.chdir(folder) { exec("rev-parse --show-toplevel") }
  return git_top_level if lookup_top_level

  # To keep backward compatibility, `GitRepo#diff_for_folder` expects folder
  # to be top level git directory or requires `true` to `lookup_top_level`.
  # https://github.com/danger/danger/pull/1178
  return folder if compare_path(git_top_level, folder)

  message = "#{folder} is not the top level git directory. Pass correct path or `true` to `lookup_top_level` option for `diff_for_folder`"
  raise ArgumentError, message
end
find_merge_base(repo, from, to) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 125
def find_merge_base(repo, from, to)
  possible_merge_base = possible_merge_base(repo, from, to)
  return possible_merge_base if possible_merge_base

  possible_merge_base = find_merge_base_with_incremental_fetch(repo, from, to)
  return possible_merge_base if possible_merge_base

  git_in_depth_fetch
  possible_merge_base = possible_merge_base(repo, from, to)

  raise "Cannot find a merge base between #{from} and #{to}. If you are using shallow clone/fetch, try increasing the --depth" unless possible_merge_base

  possible_merge_base
end
find_merge_base_with_incremental_fetch(repo, from, to) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 140
def find_merge_base_with_incremental_fetch(repo, from, to)
  from_is_ref = commit_is_ref?(from)
  to_is_ref = commit_is_ref?(to)

  return unless from_is_ref || to_is_ref

  depth = 0
  (3..6).each do |factor|
    depth += Math.exp(factor).to_i

    git_fetch_branch_to_depth(from, depth) if from_is_ref
    git_fetch_branch_to_depth(to, depth) if to_is_ref
    merge_base = possible_merge_base(repo, from, to)
    return merge_base if merge_base
  end

  nil
end
git_fetch_branch_to_depth(branch, depth) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 105
def git_fetch_branch_to_depth(branch, depth)
  exec("fetch --depth=#{depth} --prune origin +refs/heads/#{branch}:refs/remotes/origin/#{branch}")
end
git_in_depth_fetch() click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 101
def git_in_depth_fetch
  exec("fetch --depth 1000000")
end
possible_merge_base(repo, from, to) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 159
def possible_merge_base(repo, from, to)
  [repo.merge_base(from, to)].find { |base| commit_exists?(base) }
end
raise_if_we_cannot_find_the_commit(commitish) click to toggle source
# File lib/danger/scm_source/git_repo.rb, line 113
def raise_if_we_cannot_find_the_commit(commitish)
  raise "Commit #{commitish[0..7]} doesn't exist. Are you running `danger local/pr` against the correct repository? Also this usually happens when you rebase/reset and force-pushed."
end