class FlashFlow::Git

Constants

ATTRIBUTES
UNMERGED_STATUSES

Attributes

working_branch[R]

Public Class Methods

new(config, logger=nil) click to toggle source
# File lib/flash_flow/git.rb, line 13
def initialize(config, logger=nil)
  @cmd_runner = CmdRunner.new(logger: logger)

  config['release_branch'] ||= config['master_branch']
  config['remote'] ||= config['merge_remote'] # For backwards compatibility

  ATTRIBUTES.each do |attr|
    unless config.has_key?(attr.to_s)
      raise RuntimeError.new("git configuration missing. Required config parameters: #{ATTRIBUTES}")
    end

    instance_variable_set("@#{attr}", config[attr.to_s])
  end

  @working_branch = current_branch
end

Public Instance Methods

add_and_commit(files, message, opts={}) click to toggle source
# File lib/flash_flow/git.rb, line 56
def add_and_commit(files, message, opts={})
  files = [files].flatten
  run("add #{'-f ' if opts[:add] && opts[:add][:force]}#{files.join(' ')}")
  run("commit -m '#{message}'")
end
ahead_of_master?(branch) click to toggle source
# File lib/flash_flow/git.rb, line 236
def ahead_of_master?(branch)
  branch_exists?(branch) && !master_branch_contains?(get_sha(branch))
end
branch_contains?(branch, ref) click to toggle source
# File lib/flash_flow/git.rb, line 66
def branch_contains?(branch, ref)
  run("branch -a --contains #{ref}", log: CmdRunner::LOG_CMD)
  last_stdout.split("\n").detect { |str| str[2..-1] == branch }
end
branch_exists?(branch) click to toggle source
# File lib/flash_flow/git.rb, line 231
def branch_exists?(branch)
  run("rev-parse --verify #{branch}")
  last_success?
end
commit_rerere(current_rereres) click to toggle source
# File lib/flash_flow/git.rb, line 92
def commit_rerere(current_rereres)
  return unless use_rerere
  @cmd_runner.run('mkdir rr-cache')
  @cmd_runner.run('rm -rf rr-cache/*')
  current_rereres.each do |rerere|
    @cmd_runner.run("cp -R .git/rr-cache/#{rerere} rr-cache/")
  end

  run('add rr-cache/')
  run("commit -m 'Update rr-cache'")
end
conflicted_files() click to toggle source
# File lib/flash_flow/git.rb, line 156
def conflicted_files
  run("diff --name-only --diff-filter=U")
  last_stdout.split("\n")
end
copy_temp_to_branch(branch, squash_message = nil) click to toggle source
# File lib/flash_flow/git.rb, line 184
def copy_temp_to_branch(branch, squash_message = nil)
  run("checkout #{temp_merge_branch}")
  run("merge --strategy=ours --no-edit #{branch}")
  run("checkout #{branch}")
  run("merge #{temp_merge_branch}")

  squash_commits(branch, squash_message) if squash_message
end
current_branch() click to toggle source
# File lib/flash_flow/git.rb, line 161
def current_branch
  run("rev-parse --abbrev-ref HEAD")
  last_stdout.strip
end
delete_temp_merge_branch() click to toggle source
# File lib/flash_flow/git.rb, line 193
def delete_temp_merge_branch
  in_branch(master_branch) do
    run("branch -d #{temp_merge_branch}")
  end
end
get_sha(branch, opts={}) click to toggle source
# File lib/flash_flow/git.rb, line 222
def get_sha(branch, opts={})
  if opts[:short]
    run("rev-parse --short #{branch}")
  else
    run("rev-parse #{branch}")
  end
  last_stdout.strip if last_success?
end
in_branch(branch) { || ... } click to toggle source
# File lib/flash_flow/git.rb, line 207
def in_branch(branch)
  begin
    starting_branch = current_branch
    run("checkout #{branch}")

    yield
  ensure
    run("checkout #{starting_branch}")
  end
end
in_dir() { || ... } click to toggle source
# File lib/flash_flow/git.rb, line 30
def in_dir
  Dir.chdir(@cmd_runner.dir) do
    yield
  end
end
in_merge_branch(&block) click to toggle source
# File lib/flash_flow/git.rb, line 203
def in_merge_branch(&block)
  in_branch(merge_branch, &block)
end
in_original_merge_branch() { || ... } click to toggle source
# File lib/flash_flow/git.rb, line 75
def in_original_merge_branch
  in_branch("#{remote}/#{merge_branch}") { yield }
end
in_temp_merge_branch(&block) click to toggle source
# File lib/flash_flow/git.rb, line 199
def in_temp_merge_branch(&block)
  in_branch(temp_merge_branch, &block)
end
initialize_rerere(copy_from_dir=nil) click to toggle source
# File lib/flash_flow/git.rb, line 84
def initialize_rerere(copy_from_dir=nil)
  return unless use_rerere

  @cmd_runner.run('mkdir .git/rr-cache')
  @cmd_runner.run('cp -R rr-cache/* .git/rr-cache/')
  @cmd_runner.run("cp -R #{File.join(copy_from_dir, '.git/rr-cache/*')} .git/rr-cache/") if copy_from_dir
end
last_command() click to toggle source
# File lib/flash_flow/git.rb, line 40
def last_command
  @cmd_runner.last_command
end
last_stdout() click to toggle source
# File lib/flash_flow/git.rb, line 36
def last_stdout
  @cmd_runner.last_stdout
end
last_success?() click to toggle source
# File lib/flash_flow/git.rb, line 44
def last_success?
  @cmd_runner.last_success?
end
master_branch_contains?(sha) click to toggle source
# File lib/flash_flow/git.rb, line 71
def master_branch_contains?(sha)
  branch_contains?("remotes/#{remote}/#{master_branch}", sha)
end
merge(branch) click to toggle source
# File lib/flash_flow/git.rb, line 62
def merge(branch)
  run("merge #{branch}")
end
most_recent_commit() click to toggle source
# File lib/flash_flow/git.rb, line 166
def most_recent_commit
  run("show -s --format=%cd head")
end
push(branch, force=false) click to toggle source
# File lib/flash_flow/git.rb, line 180
def push(branch, force=false)
  run("push #{'-f' if force} #{remote} #{branch}:#{branch}")
end
read_file_from_merge_branch(filename) click to toggle source
# File lib/flash_flow/git.rb, line 79
def read_file_from_merge_branch(filename)
  run("show #{remote}/#{merge_branch}:#{filename}", log: CmdRunner::LOG_CMD)
  last_stdout
end
rerere_resolve!() click to toggle source
# File lib/flash_flow/git.rb, line 104
def rerere_resolve!
  return false unless use_rerere

  if unresolved_conflicts.empty?
    merging_files = staged_and_working_dir_files.select { |s| UNMERGED_STATUSES.include?(s[0..1]) }.map { |s| s[3..-1] }
    conflicts = conflicted_files

    run("add #{merging_files.join(" ")}")
    run('commit --no-edit')

    resolutions(conflicts)
  else
    false
  end
end
reset_temp_merge_branch() click to toggle source
# File lib/flash_flow/git.rb, line 170
def reset_temp_merge_branch
  in_branch(master_branch) do
    run("fetch #{remote}")
    run("branch -D #{temp_merge_branch}")
    run("checkout -b #{temp_merge_branch}")
    run("reset --hard #{remote}/#{master_branch}")
    run("clean -x -f -d")
  end
end
resolution_candidates(file) click to toggle source

git rerere doesn't give you a deterministic way to determine which resolution was used

# File lib/flash_flow/git.rb, line 137
def resolution_candidates(file)
  @cmd_runner.run("diff -q --from-file #{file} .git/rr-cache/*/postimage*", log: CmdRunner::LOG_CMD)
  different_files = split_diff_lines(@cmd_runner.last_stdout)

  @cmd_runner.run('ls -la .git/rr-cache/*/postimage*', log: CmdRunner::LOG_CMD)
  all_files = split_diff_lines(@cmd_runner.last_stdout)

  all_files - different_files
end
resolutions(files) click to toggle source
# File lib/flash_flow/git.rb, line 128
def resolutions(files)
  {}.tap do |hash|
    files.map do |file|
      hash[file] = resolution_candidates(file)
    end.flatten
  end
end
run(cmd, opts={}) click to toggle source
# File lib/flash_flow/git.rb, line 48
def run(cmd, opts={})
  @cmd_runner.run("git #{cmd}", opts)
end
split_diff_lines(arr) click to toggle source
# File lib/flash_flow/git.rb, line 147
def split_diff_lines(arr)
  arr.split("\n").map { |s| s.split(".git/rr-cache/").last.split("/postimage").first }
end
staged_and_working_dir_files() click to toggle source
# File lib/flash_flow/git.rb, line 151
def staged_and_working_dir_files
  run("status --porcelain")
  last_stdout.split("\n").reject { |line| line[0..1] == '??' }
end
temp_merge_branch() click to toggle source
# File lib/flash_flow/git.rb, line 218
def temp_merge_branch
  "flash_flow-#{merge_branch}"
end
unresolved_conflicts() click to toggle source
# File lib/flash_flow/git.rb, line 120
def unresolved_conflicts
  in_dir do
    conflicted_files.map do |file|
      File.open(file) { |f| f.grep(/>>>>/) }.empty? ? nil : file
    end.compact
  end
end
version() click to toggle source
# File lib/flash_flow/git.rb, line 240
def version
  run('--version')
  semver_regex = Regexp.new('.*(\d+\.\d+\.\d+).*')
  running_version = last_stdout.strip
  if semver = semver_regex.match(running_version)
    semver[1]
  end
end
working_dir() click to toggle source
# File lib/flash_flow/git.rb, line 52
def working_dir
  @cmd_runner.dir
end

Private Instance Methods

squash_commits(branch, commit_message) click to toggle source
# File lib/flash_flow/git.rb, line 251
def squash_commits(branch, commit_message)
  unless branch_exists?("#{remote}/#{branch}")
    run("push #{remote} #{master_branch}:#{branch}")
  end

  # Get all the files that differ between existing acceptance and new acceptance
  run("diff --name-only #{remote}/#{branch} #{branch}")
  files = last_stdout.split("\n")
  run("reset #{remote}/#{branch}")

  run("add -f #{files.map { |f| "\"#{Shellwords.escape(f)}\"" }.join(" ")}")

  run("commit -m '#{commit_message}'")
end