Skip to content

Commit de2662b

Browse files
committed
feat: init self repairing
1 parent 7ad8f3f commit de2662b

2 files changed

Lines changed: 79 additions & 5 deletions

File tree

lib/git_work/commands/init.ex

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule GitWork.Commands.Init do
33
Convert an existing normal git repository into the worktree-based layout.
44
"""
55

6-
alias GitWork.Git
6+
alias GitWork.{Git, Project}
77

88
def help do
99
"""
@@ -36,7 +36,7 @@ defmodule GitWork.Commands.Init do
3636

3737
cond do
3838
File.dir?(bare_dir) ->
39-
{:error, "already initialized (found .bare/)"}
39+
do_repair(dir, git_dir, bare_dir)
4040

4141
not File.dir?(git_dir) ->
4242
{:error, "not a git repository (no .git/ directory)"}
@@ -58,6 +58,60 @@ defmodule GitWork.Commands.Init do
5858
end
5959
end
6060

61+
defp do_repair(dir, git_dir, bare_dir) do
62+
with :ok <- ensure_gitdir_pointer(dir, git_dir),
63+
:ok <- configure_bare(bare_dir),
64+
{:ok, branch} <- Project.head_branch(dir),
65+
:ok <- ensure_worktree_dir(dir, branch) do
66+
{:ok, Project.worktree_path(dir, branch)}
67+
end
68+
end
69+
70+
defp ensure_gitdir_pointer(dir, git_dir) do
71+
cond do
72+
File.dir?(git_dir) ->
73+
{:error, "found .git directory; not a git-work root"}
74+
75+
File.regular?(git_dir) ->
76+
case File.read(git_dir) do
77+
{:ok, "gitdir: ./.bare\n"} ->
78+
:ok
79+
80+
{:ok, _} ->
81+
write_gitdir_pointer(dir)
82+
83+
{:error, reason} ->
84+
{:error, "failed to read .git pointer: #{reason}"}
85+
end
86+
87+
true ->
88+
write_gitdir_pointer(dir)
89+
end
90+
end
91+
92+
defp ensure_worktree_dir(dir, branch) do
93+
worktree_dir = Project.worktree_path(dir, branch)
94+
95+
if File.dir?(worktree_dir) do
96+
:ok
97+
else
98+
bare_dir = Project.bare_path(dir)
99+
100+
case Git.cmd(["worktree", "add", worktree_dir, branch], cd: bare_dir) do
101+
{:ok, _} ->
102+
:ok
103+
104+
{:error, _msg} ->
105+
_ = Git.cmd(["worktree", "prune"], cd: bare_dir)
106+
107+
case Git.cmd(["worktree", "add", worktree_dir, branch], cd: bare_dir) do
108+
{:ok, _} -> :ok
109+
{:error, msg2} -> {:error, "failed to recreate worktree: #{msg2}"}
110+
end
111+
end
112+
end
113+
end
114+
61115
defp do_init_steps(dir, branch, stashed?, git_dir, bare_dir) do
62116
with :ok <- move_git_to_bare(git_dir, bare_dir),
63117
:ok <- configure_bare(bare_dir),

test/git_work/commands/init_test.exs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,29 @@ defmodule GitWork.Commands.InitTest do
6060

6161
assert {:ok, _} = Init.run([])
6262

63-
# Second run should fail
64-
assert {:error, msg} = Init.run([])
65-
assert msg =~ "already initialized"
63+
# Second run should repair and succeed
64+
assert {:ok, path} = Init.run([])
65+
assert path == Path.join(repo, "main")
66+
67+
# core.bare should be true in .bare
68+
{value, 0} =
69+
System.cmd("git", ["config", "--bool", "core.bare"], cd: Path.join(repo, ".bare"))
70+
71+
assert String.trim(value) == "true"
72+
end
73+
74+
test "recreates missing HEAD worktree on rerun", %{tmp: tmp} do
75+
repo = GitWork.TestHelper.create_normal_repo(tmp)
76+
77+
File.cd!(repo)
78+
79+
assert {:ok, main_path} = Init.run([])
80+
File.rm_rf!(main_path)
81+
82+
assert {:ok, new_path} = Init.run([])
83+
assert new_path == main_path
84+
assert File.dir?(new_path)
85+
assert File.regular?(Path.join(new_path, "README.md"))
6686
end
6787

6888
test "handles dirty working tree with stash", %{tmp: tmp} do

0 commit comments

Comments
 (0)