class Dependabot::Workspace::Git

def capture_failed_change_attempt(memo = nil, error = nil)

def capture_failed_change_attempt(memo = nil, error = nil)
  return nil if changed_files(ignored_mode: "matching").empty? && error.nil?
  sha = stash(memo)
  change_attempts << ChangeAttempt.new(self, id: sha, memo: memo, error: error)
end

def changed?

def changed?
  changes.any? || !changed_files(ignored_mode: "no").empty?
end

def changed_files(ignored_mode: "traditional")

def changed_files(ignored_mode: "traditional")
  run_shell_command(
    "git status --untracked-files=all --ignored=#{ignored_mode} --short .",
    fingerprint: "git status --untracked-files=all --ignored=<ignored_mode> --short ."
  ).strip
end

def clean

def clean
  run_shell_command("git clean -fx .")
end

def commit(memo = nil)

def commit(memo = nil)
  run_shell_command("git add #{path}")
  msg = memo || "workspace change"
  run_shell_command(
    %(git commit -m "#{msg}"),
    fingerprint: "git commit -m \"<msg>\"",
    allow_unsafe_shell_command: true
  )
  head_sha
end

def configure_git

def configure_git
  run_shell_command(%(git config user.name "#{USER}"), allow_unsafe_shell_command: true)
  run_shell_command(%(git config user.email "#{EMAIL}"), allow_unsafe_shell_command: true)
end

def current_commit

def current_commit
  # Avoid emitting the user's commit message to logs if Dependabot hasn't made any changes
  return "Initial SHA: #{initial_head_sha}" if changes.empty?
  # Prints out the last commit in the format "<short-ref> <commit-message>"
  run_shell_command(%(git log -1 --pretty="%h% B"), allow_unsafe_shell_command: true).strip
end

def debug(message)

def debug(message)
  Dependabot.logger.debug("[workspace] #{message}")
end

def head_sha

def head_sha
  run_shell_command("git rev-parse HEAD", stderr_to_stdout: false).strip
end

def initialize(path)

def initialize(path)
  super
  @initial_head_sha = T.let(head_sha, String)
  configure_git
end

def last_stash_sha

def last_stash_sha
  run_shell_command("git rev-parse refs/stash").strip
end

def reset(sha)

def reset(sha)
  run_shell_command(
    "git reset --hard #{sha}",
    fingerprint: "git reset --hard <sha>"
  )
end

def reset!

def reset!
  reset(initial_head_sha)
  clean
  run_shell_command("git stash clear")
  @change_attempts = []
  nil
end

def run_shell_command(*args, **kwargs)

def run_shell_command(*args, **kwargs)
  Dir.chdir(path) { T.unsafe(SharedHelpers).run_shell_command(*args, **kwargs) }
end

def stash(memo = nil)

def stash(memo = nil)
  msg = memo || "workspace change attempt"
  run_shell_command("git add --all --force .")
  run_shell_command(
    %(git stash push --all -m "#{msg}"),
    fingerprint: "git stash push --all -m \"<msg>\"",
    allow_unsafe_shell_command: true
  )
  last_stash_sha
end

def store_change(memo = nil)

def store_change(memo = nil)
  return nil if changed_files(ignored_mode: "no").empty?
  debug("store_change - before: #{current_commit}")
  sha = commit(memo)
  change_attempts << ChangeAttempt.new(self, id: sha, memo: memo)
ensure
  debug("store_change - after: #{current_commit}")
end

def to_patch

def to_patch
  run_shell_command(
    "git diff --patch #{@initial_head_sha}.. .",
    fingerprint: "git diff --path <initial_head_sha>.. ."
  )
end