Skip to content
This repository was archived by the owner on Oct 8, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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