Skip to content
This repository has been archived by the owner on Oct 8, 2020. It is now read-only.

Commit

Permalink
Merge b1e44d0 into 687827b
Browse files Browse the repository at this point in the history
  • Loading branch information
scouten committed Jul 24, 2019
2 parents 687827b + b1e44d0 commit c5fe3a4
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 13 deletions.
65 changes: 52 additions & 13 deletions lib/xgit/plumbing/hash_object.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule Xgit.Plumbing.HashObject do
alias Xgit.Core.ObjectId
alias Xgit.Core.ObjectType
alias Xgit.Core.ValidateObject
alias Xgit.Repository

@doc ~S"""
Computes an object ID and optionally writes that into the repository's object store.
Expand All @@ -31,6 +32,19 @@ defmodule Xgit.Plumbing.HashObject do
* Default: `true`
* This is the inverse of the [`--literally` option on `git hash-object`](https://git-scm.com/docs/git-hash-object#Documentation/git-hash-object.txt---literally).
`:repo`: where the content should be stored
* Type: `Xgit.Repository` (PID)
* Default: `nil`
`:write?`: `true` to write the object into the repository
* Type: boolean
* Default: `false`
* This option is meaningless if `:repo` is not specified.
* See [`-w` option on `git hash-object`](https://git-scm.com/docs/git-hash-object#Documentation/git-hash-object.txt--w).
**TO DO:** There is no support, at present, for filters as defined in a
`.gitattributes` file. See [issue #18](https://github.com/elixir-git/xgit/issues/18).
## Return Value
`{:ok, object_id}` if the object could be validated and assigned an ID.
Expand All @@ -39,6 +53,18 @@ defmodule Xgit.Plumbing.HashObject do
@spec run(content :: ContentSource.t(), type: ObjectType.t() | nil) ::
{:ok, ObjectID.t()} | {:error, reason :: String.t()}
def run(content, opts \\ []) when not is_nil(content) and is_list(opts) do
%{type: type, validate?: validate?, repo: repo, write?: write?} = validate_options(opts)

%Object{content: content, type: type}
|> apply_filters(repo)
|> annotate_with_size()
|> assign_object_id()
|> validate_content(validate?)
|> maybe_write_to_repo(repo, write?)
|> result(opts)
end

defp validate_options(opts) do
type = Keyword.get(opts, :type, :blob)

unless ObjectType.valid?(type) do
Expand All @@ -52,16 +78,25 @@ defmodule Xgit.Plumbing.HashObject do
"Xgit.Plumbing.HashObject.run/2: validate? #{inspect(validate?)} is invalid"
end

repo = nil
# Keyword.get(opts, :repository)
repo = Keyword.get(opts, :repo)

%Object{content: content, type: type}
|> apply_filters(repo)
|> annotate_with_size()
|> assign_object_id()
|> validate_content(validate?)
|> maybe_write_to_repo(opts)
|> result(opts)
unless repo == nil or Repository.valid?(repo) do
raise ArgumentError, "Xgit.Plumbing.HashObject.run/2: repo #{inspect(repo)} is invalid"
end

write? = Keyword.get(opts, :write?, false)

unless is_boolean(write?) do
raise ArgumentError,
"Xgit.Plumbing.HashObject.run/2: write? #{inspect(write?)} is invalid"
end

if write? and repo == nil do
raise ArgumentError,
"Xgit.Plumbing.HashObject.run/2: write?: true requires a repo to be specified"
end

%{type: type, validate?: validate?, repo: repo, write?: write?}
end

defp apply_filters(object, _repository) do
Expand Down Expand Up @@ -98,12 +133,16 @@ defmodule Xgit.Plumbing.HashObject do
defp assign_object_id(%Object{content: content, type: type} = object),
do: %{object | id: ObjectId.calculate_id(content, type)}

defp maybe_write_to_repo({:ok, object}, _opts) do
# TO DO: Pass the object through to repo to write it to disk.
{:ok, object}
defp maybe_write_to_repo({:ok, object}, _repo, false = _write?), do: {:ok, object}

defp maybe_write_to_repo({:ok, object}, repo, true = _write?) do
case Repository.put_loose_object(repo, object) do
:ok -> {:ok, object}
{:error, reason} -> {:error, reason}
end
end

defp maybe_write_to_repo({:error, reason}, _opts), do: {:error, reason}
defp maybe_write_to_repo({:error, reason}, _repo, _write?), do: {:error, reason}

defp result({:ok, %Object{id: id}}, _opts), do: {:ok, id}
defp result({:error, reason}, _opts), do: {:error, reason}
Expand Down
4 changes: 4 additions & 0 deletions test/support/git_init_test_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ defmodule Xgit.GitInitTestCase do
use ExUnit.CaseTemplate

setup do
setup_git_repo()
end

def setup_git_repo do
Temp.track!()
tmp = Temp.mkdir!()
ref = Path.join(tmp, "ref")
Expand Down
49 changes: 49 additions & 0 deletions test/xgit/plumbing/hash_object_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ defmodule Xgit.Plumbing.HashObjectTest do
use ExUnit.Case, async: true

alias Xgit.Core.FileContentSource
alias Xgit.GitInitTestCase
alias Xgit.Plumbing.HashObject
alias Xgit.Repository.OnDisk

import FolderDiff

describe "run/2" do
test "happy path: deriving SHA hash with no repo" do
Expand Down Expand Up @@ -32,6 +36,27 @@ defmodule Xgit.Plumbing.HashObjectTest do
|> HashObject.run()
end

test "happy path: write to repo matches command-line git (small file)" do
{:ok, ref: ref, xgit: xgit} = GitInitTestCase.setup_git_repo()
# Only invoking GitInitTestCase manually here because other tests in this
# module don't need it. Setting up a repo that we don't use in the other
# tests would be wasteful.

Temp.track!()
path = Temp.path!()
File.write!(path, "test content\n")

{_output, 0} = System.cmd("git", ["hash-object", "-w", path], cd: ref)

assert :ok = OnDisk.create(xgit)
assert {:ok, repo} = OnDisk.start_link(work_dir: xgit)

assert {:ok, "d670460b4b4aece5915caf5c68d12f560a9fe3e4"} =
HashObject.run("test content\n", repo: repo, write?: true)

assert_folders_are_equal(ref, xgit)
end

test "happy path: validate content (content is valid)" do
Temp.track!()
path = Temp.path!()
Expand Down Expand Up @@ -103,5 +128,29 @@ defmodule Xgit.Plumbing.HashObjectTest do
HashObject.run("test content\n", validate?: "yes")
end
end

test "error: :repo invalid" do
assert_raise ArgumentError,
~s(Xgit.Plumbing.HashObject.run/2: repo "/path/to/repo" is invalid),
fn ->
HashObject.run("test content\n", repo: "/path/to/repo")
end
end

test "error: :write? invalid" do
assert_raise ArgumentError,
~s(Xgit.Plumbing.HashObject.run/2: write? "yes" is invalid),
fn ->
HashObject.run("test content\n", write?: "yes")
end
end

test "error: :write? without repo" do
assert_raise ArgumentError,
~s(Xgit.Plumbing.HashObject.run/2: write?: true requires a repo to be specified),
fn ->
HashObject.run("test content\n", write?: true)
end
end
end
end

0 comments on commit c5fe3a4

Please sign in to comment.