This repository has been archived by the owner on Oct 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
defmodule Xgit.Repository.OnDisk do | ||
@moduledoc ~S""" | ||
Implementation of `Xgit.Repository` that stores content on the local file system. | ||
**IMPORTANT NOTE:** This is intended as a reference implementation largely | ||
for testing purposes and may not necessarily handle all of the edge cases that | ||
the traditional `git` command-line interface will handle. | ||
That said, it does intentionally use the same `.git` folder format as command-line | ||
git so that results may be compared for similar operations. | ||
""" | ||
|
||
@doc ~S""" | ||
Creates a new, empty git repository on the local file system. | ||
Analogous to [`git init`](https://git-scm.com/docs/git-init). | ||
## Options | ||
* `:work_dir` (required): Top-level working directory. A `.git` directory is | ||
created inside this directory. | ||
## Return Value | ||
`:ok` | ||
## Errors | ||
Will raise `ArgumentError` if options are incomplete or incorrect. | ||
Will raise `File.Error` or similar if unable to create the directory. | ||
""" | ||
# @spec init([work_dir: String.t]) :: :ok | ||
defdelegate init(opts), to: Xgit.Repository.OnDisk.Init | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
defmodule Xgit.Repository.OnDisk.Init do | ||
@moduledoc false | ||
# Implements Xgit.Repository.OnDisk.init/1. | ||
|
||
def init(opts) when is_list(opts) do | ||
work_dir = Keyword.get(opts, :work_dir) | ||
|
||
unless is_binary(work_dir) do | ||
raise ArgumentError, "Xgit.Repository.OnDisk.init/1: :work_dir must be a file path" | ||
end | ||
|
||
work_dir | ||
|> assert_not_exists!() | ||
|> create_empty_repo!() | ||
|
||
:ok | ||
end | ||
|
||
defp assert_not_exists!(path) do | ||
if File.exists?(path) do | ||
raise ArgumentError, | ||
"Xgit.Repository.OnDisk.init/1: :work_dir must be a directory that doesn't already exist" | ||
else | ||
path | ||
end | ||
end | ||
|
||
defp create_empty_repo!(path) do | ||
File.mkdir_p!(path) | ||
|
||
path | ||
|> Path.join(".git") | ||
|> create_git_dir!() | ||
end | ||
|
||
defp create_git_dir!(git_dir) do | ||
create_branches_dir!(git_dir) | ||
create_config!(git_dir) | ||
create_description!(git_dir) | ||
create_head!(git_dir) | ||
create_hooks_dir!(git_dir) | ||
create_info_dir!(git_dir) | ||
create_objects_dir!(git_dir) | ||
create_refs_dir!(git_dir) | ||
end | ||
|
||
defp create_branches_dir!(git_dir) do | ||
git_dir | ||
|> Path.join("branches") | ||
|> File.mkdir_p!() | ||
end | ||
|
||
defp create_config!(git_dir) do | ||
git_dir | ||
|> Path.join("config") | ||
|> File.write!(~s""" | ||
[core] | ||
\trepositoryformatversion = 0 | ||
\tfilemode = true | ||
\tbare = false | ||
\tlogallrefupdates = true | ||
""") | ||
end | ||
|
||
defp create_description!(git_dir) do | ||
git_dir | ||
|> Path.join("description") | ||
|> File.write!("Unnamed repository; edit this file 'description' to name the repository.\n") | ||
end | ||
|
||
defp create_head!(git_dir) do | ||
git_dir | ||
|> Path.join("HEAD") | ||
|> File.write!("ref: refs/heads/master\n") | ||
end | ||
|
||
defp create_hooks_dir!(git_dir) do | ||
git_dir | ||
|> Path.join("hooks") | ||
|> File.mkdir_p!() | ||
|
||
# NOTE: Intentionally not including the sample files. | ||
end | ||
|
||
defp create_info_dir!(git_dir) do | ||
info_dir = Path.join(git_dir, "info") | ||
File.mkdir_p!(info_dir) | ||
|
||
info_dir | ||
|> Path.join("exclude") | ||
|> File.write!(~S""" | ||
# git ls-files --others --exclude-from=.git/info/exclude | ||
# Lines that start with '#' are comments. | ||
# For a project mostly in C, the following would be a good set of | ||
# exclude patterns (uncomment them if you want to use them): | ||
# *.[oa] | ||
# *~ | ||
.DS_Store | ||
""") | ||
end | ||
|
||
defp create_objects_dir!(git_dir) do | ||
git_dir | ||
|> Path.join("objects/info") | ||
|> File.mkdir_p!() | ||
|
||
git_dir | ||
|> Path.join("objects/pack") | ||
|> File.mkdir_p!() | ||
end | ||
|
||
defp create_refs_dir!(git_dir) do | ||
refs_dir = Path.join(git_dir, "refs") | ||
File.mkdir_p!(refs_dir) | ||
|
||
refs_dir | ||
|> Path.join("heads") | ||
|> File.mkdir_p!() | ||
|
||
refs_dir | ||
|> Path.join("tags") | ||
|> File.mkdir_p!() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
defmodule Xgit.GitInitTestCase do | ||
@moduledoc false | ||
# Creates a temporary directory containing an | ||
# initialized, but otherwise empty git repo and | ||
# adjacent space for an Xgit-created git repo. | ||
|
||
use ExUnit.CaseTemplate | ||
|
||
setup do | ||
Temp.track!() | ||
tmp = Temp.mkdir!() | ||
ref = Path.join(tmp, "ref") | ||
xgit = Path.join(tmp, "xgit") | ||
|
||
git_init_and_standardize(ref) | ||
|
||
{:ok, ref: ref, xgit: xgit} | ||
end | ||
|
||
defp git_init_and_standardize(git_dir) do | ||
git_dir | ||
|> git_init() | ||
|> remove_sample_hooks() | ||
|> rewrite_config() | ||
|> rewrite_info_exclude() | ||
end | ||
|
||
defp git_init(git_dir) do | ||
{_, 0} = System.cmd("git", ["init", git_dir]) | ||
git_dir | ||
end | ||
|
||
defp remove_sample_hooks(git_dir) do | ||
hooks_dir = Path.join(git_dir, ".git/hooks") | ||
|
||
hooks_dir | ||
|> File.ls!() | ||
|> Enum.filter(&String.ends_with?(&1, ".sample")) | ||
|> Enum.each(&File.rm!(Path.join(hooks_dir, &1))) | ||
|
||
git_dir | ||
end | ||
|
||
defp rewrite_config(git_dir) do | ||
git_dir | ||
|> Path.join(".git/config") | ||
|> File.write!(~s""" | ||
[core] | ||
\trepositoryformatversion = 0 | ||
\tfilemode = true | ||
\tbare = false | ||
\tlogallrefupdates = true | ||
""") | ||
|
||
git_dir | ||
end | ||
|
||
defp rewrite_info_exclude(git_dir) do | ||
git_dir | ||
|> Path.join(".git/info/exclude") | ||
|> File.write!(~s""" | ||
# git ls-files --others --exclude-from=.git/info/exclude | ||
# Lines that start with '#' are comments. | ||
# For a project mostly in C, the following would be a good set of | ||
# exclude patterns (uncomment them if you want to use them): | ||
# *.[oa] | ||
# *~ | ||
.DS_Store | ||
""") | ||
|
||
git_dir | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
defmodule Xgit.Repository.OnDisk.InitTest do | ||
use Xgit.GitInitTestCase, async: true | ||
|
||
alias Xgit.Repository.OnDisk | ||
|
||
import FolderDiff | ||
|
||
describe "init/1" do | ||
test "happy path matches command-line git", %{ref: ref, xgit: xgit} do | ||
assert :ok = OnDisk.init(work_dir: xgit) | ||
assert_folders_are_equal(ref, xgit) | ||
end | ||
|
||
test "error: no work_dir" do | ||
assert_raise ArgumentError, fn -> | ||
OnDisk.init([]) | ||
end | ||
end | ||
|
||
test "error: work dir exists already", %{xgit: xgit} do | ||
File.mkdir_p!(xgit) | ||
|
||
assert_raise ArgumentError, fn -> | ||
OnDisk.init(work_dir: xgit) | ||
end | ||
end | ||
end | ||
end |