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

Commit

Permalink
Refactor Xgit.Plumbing.WriteTree to use WorkingTree.write_tree/2. (#171)
Browse files Browse the repository at this point in the history
* Refactor Xgit.Plumbing.WriteTree to use WorkingTree.write_tree/2.

This avoids the cost of passing the DirCache struct (which is potentially large) over the process boundary.

* More test coverage.
  • Loading branch information
scouten authored Sep 22, 2019
1 parent 2e4781b commit 6e88c21
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 46 deletions.
50 changes: 4 additions & 46 deletions lib/xgit/plumbing/write_tree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ defmodule Xgit.Plumbing.WriteTree do

alias Xgit.Core.DirCache
alias Xgit.Core.FilePath
alias Xgit.Core.Object
alias Xgit.Core.ObjectId
alias Xgit.Plumbing.Util.WorkingTreeOpt
alias Xgit.Repository
Expand All @@ -23,8 +22,7 @@ defmodule Xgit.Plumbing.WriteTree do
@type reason ::
:invalid_repository
| :bare
| :incomplete_merge
| :objects_missing
| WorkingTree.write_tree_reason()
| DirCache.to_tree_objects_reason()
| ParseIndexFile.from_iodevice_reason()
| Repository.put_loose_object_reason()
Expand Down Expand Up @@ -58,35 +56,22 @@ defmodule Xgit.Plumbing.WriteTree do
`{:error, :bare}` if `repository` doesn't have a working tree.
`{:error, :incomplete_merge}` if any entry in the index file is not fully merged.
`{:error, :objects_missing}` if any of the objects referenced by the index
are not present in the object store. (Exception: If `missing_ok?` is `true`,
then this condition will be ignored.)
Reason codes may also come from the following functions:
* `Xgit.Core.DirCache.to_tree_objects/2`
* `Xgit.Repository.put_loose_object/2`
* `Xgit.Repository.WorkingTree.write_tree/2`
* `Xgit.Repository.WorkingTree.ParseIndexFile.from_iodevice/1`
"""
@spec run(repository :: Repository.t(), missing_ok?: boolean, prefix: FilePath.t()) ::
{:ok, object_id :: ObjectId.t()}
| {:error, reason :: reason}
def run(repository, opts \\ []) when is_pid(repository) do
with {:ok, working_tree} <- WorkingTreeOpt.get(repository),
{missing_ok?, prefix} <- validate_options(opts),
{:ok, %DirCache{entries: entries} = dir_cache} <- WorkingTree.dir_cache(working_tree),
{:merged?, true} <- {:merged?, DirCache.fully_merged?(dir_cache)},
{:has_all_objects?, true} <-
{:has_all_objects?, has_all_objects?(repository, entries, missing_ok?)},
{:ok, objects, %Object{id: object_id}} <- DirCache.to_tree_objects(dir_cache, prefix),
:ok <- write_all_objects(repository, objects) do
cover {:ok, object_id}
_ <- validate_options(opts) do
cover WorkingTree.write_tree(working_tree, opts)
else
{:error, reason} -> cover {:error, reason}
{:merged?, false} -> cover {:error, :incomplete_merge}
{:has_all_objects?, false} -> cover {:error, :objects_missing}
end
end

Expand All @@ -107,31 +92,4 @@ defmodule Xgit.Plumbing.WriteTree do

{missing_ok?, prefix}
end

defp has_all_objects?(repository, entries, missing_ok?)

defp has_all_objects?(_repository, _entries, true), do: cover(true)

defp has_all_objects?(repository, entries, false) do
entries
|> Enum.chunk_every(100)
|> Enum.all?(fn entries_chunk ->
Repository.has_all_object_ids?(
repository,
Enum.map(entries_chunk, fn %{object_id: id} -> id end)
)
end)
end

defp write_all_objects(repository, objects)

defp write_all_objects(_repository, []), do: :ok

defp write_all_objects(repository, [object | tail]) do
case Repository.put_loose_object(repository, object) do
:ok -> write_all_objects(repository, tail)
{:error, :object_exists} -> write_all_objects(repository, tail)
{:error, reason} -> {:error, reason}
end
end
end
22 changes: 22 additions & 0 deletions test/xgit/plumbing/write_tree_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,12 @@ defmodule Xgit.Plumbing.WriteTreeTest do
WriteTree.run(repo, missing_ok?: true, prefix: 'no/such/prefix')
end

test "error: invalid repo" do
{:ok, not_repo} = GenServer.start_link(NotValid, nil)

assert {:error, :invalid_repository} = WriteTree.run(not_repo, missing_ok?: true)
end

test "error: invalid dir cache" do
{:ok, ref: _ref, xgit: xgit} = GitInitTestCase.setup_git_repo()

Expand Down Expand Up @@ -451,6 +457,22 @@ defmodule Xgit.Plumbing.WriteTreeTest do
assert {:error, :incomplete_merge} = WriteTree.run(repo, missing_ok?: true)
end

test "error: can't write tree object" do
{:ok, ref: _ref, xgit: xgit} = GitInitTestCase.setup_git_repo()

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

working_tree = Repository.default_working_tree(repo)
:ok = WorkingTree.update_dir_cache(working_tree, [@valid_entry], [])

objects_path = Path.join([xgit, ".git", "objects"])
File.rm_rf!(objects_path)
File.write!(objects_path, "not a directory")

assert {:error, :cant_create_file} = WriteTree.run(repo, missing_ok?: true)
end

test "error: :prefix invalid" do
{:ok, ref: _ref, xgit: xgit} = GitInitTestCase.setup_git_repo()

Expand Down

0 comments on commit 6e88c21

Please sign in to comment.