2.4 KiB · text History 2cafbbf
defmodule GitGud.WorkflowGitHelpers do
  @moduledoc """
  Commits and refs on a test repository's disk, for the dispatch tests:
  real objects written with plumbing, no working tree.
  """

  alias GitGud.Repositories.Repository
  alias GitGud.Repositories.Workers.CommitBackfill

  @doc "A commit holding exactly `files` (path => contents) on top of `parents`."
  def commit!(%Repository{disk_path: path}, files, parents, message \\ "c") do
    index = Path.join(System.tmp_dir!(), "gitidx-#{System.unique_integer([:positive])}")
    env = [{"GIT_INDEX_FILE", index}]

    try do
      for {file, contents} <- files do
        blob = git!(path, ["hash-object", "-w", "--stdin"], contents)
        git!(path, ["update-index", "--add", "--cacheinfo", "100644,#{blob},#{file}"], nil, env)
      end

      tree = git!(path, ["write-tree"], nil, env)
      git!(path, ["commit-tree", tree | Enum.flat_map(parents, &["-p", &1])] ++ ["-m", message])
    after
      File.rm(index)
    end
  end

  def git!(path, args, input \\ nil, env \\ []) do
    {out, 0} =
      case input do
        nil ->
          System.cmd("git", args, cd: path, stderr_to_stdout: true, env: env)

        _ ->
          file = Path.join(System.tmp_dir!(), "gitin-#{System.unique_integer([:positive])}")
          File.write!(file, input)

          try do
            command = Enum.map_join(["git" | args] ++ ["<", file], " ", &quote_arg/1)
            System.cmd("sh", ["-c", command], cd: path, stderr_to_stdout: true, env: env)
          after
            File.rm(file)
          end
      end

    String.trim(out)
  end

  defp quote_arg("<"), do: "<"
  defp quote_arg(arg), do: "'" <> String.replace(arg, "'", "'\\''") <> "'"

  def sha!(hex), do: elem(GitGud.Git.from_hex(hex), 1)

  @zero String.duplicate("0", 40)

  @doc """
  Point `ref` at `new_hex` (nil deletes it) and run what the post-receive
  hook enqueues for that update. `old_hex` nil means a new ref.
  """
  def push_ref!(%Repository{} = repo, ref, old_hex, new_hex, pusher_id \\ nil) do
    if new_hex,
      do: git!(repo.disk_path, ["update-ref", ref, new_hex]),
      else: git!(repo.disk_path, ["update-ref", "-d", ref])

    :ok =
      CommitBackfill.perform(%Oban.Job{
        args: %{
          "repository_id" => repo.id,
          "ref" => ref,
          "old_sha_hex" => old_hex || @zero,
          "new_sha_hex" => new_hex || @zero,
          "pusher_id" => pusher_id,
          "job_token" => false
        }
      })
  end
end