Skip to content

Commit 49ca50e

Browse files
committed
Use git to resolve the correct hooks path
1 parent ac9dda6 commit 49ca50e

File tree

4 files changed

+83
-46
lines changed

4 files changed

+83
-46
lines changed

lib/git.ex

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,24 @@
11
defmodule GitHooks.Git do
22
@moduledoc false
33

4-
alias Mix.Project
5-
64
@doc false
75
@spec resolve_git_path() :: any
86
def resolve_git_path do
97
:git_hooks
108
|> Application.get_env(:git_path)
119
|> case do
1210
nil ->
13-
path = Path.join(Project.deps_path(), "/../.git")
14-
15-
if File.dir?(path) do
16-
path
17-
else
18-
resolve_git_submodule_path(path)
19-
end
11+
{path, 0} = System.cmd("git", ["rev-parse", "--git-path", "hooks"])
12+
String.replace(path, "\n", "")
2013

2114
custom_path ->
2215
custom_path
2316
end
2417
end
2518

26-
@spec resolve_git_submodule_path(String.t()) :: any
27-
defp resolve_git_submodule_path(git_path) do
28-
with {:ok, contents} <- File.read(git_path),
29-
%{"dir" => submodule_dir} <- Regex.named_captures(~r/^gitdir:\s+(?<dir>.*)$/, contents) do
30-
Project.deps_path()
31-
|> Path.join("/../" <> submodule_dir)
32-
else
33-
_error ->
34-
raise "Error resolving git submodule path '#{git_path}'"
35-
end
19+
@spec git_hooks_path_for(path :: String.t()) :: String.t()
20+
def git_hooks_path_for(path) do
21+
__MODULE__.resolve_git_path()
22+
|> Path.join("/#{path}")
3623
end
3724
end

lib/git/path.ex

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
defmodule GitHooks.Git.Path do
2+
@moduledoc false
3+
4+
alias GitHooks.Git
5+
6+
@doc false
7+
@spec resolve_git_hooks_path() :: any
8+
def resolve_git_hooks_path do
9+
:git_hooks
10+
|> Application.get_env(:git_path)
11+
|> case do
12+
nil -> resolve_git_hooks_path_based_on_git_version()
13+
custom_path -> custom_path
14+
end
15+
end
16+
17+
@spec git_hooks_path_for(path :: String.t()) :: String.t()
18+
def git_hooks_path_for(path) do
19+
__MODULE__.resolve_git_hooks_path()
20+
|> Path.join("/#{path}")
21+
|> String.replace(~r/\/+/, "/")
22+
end
23+
24+
#
25+
# Private functions
26+
#
27+
28+
# https://stackoverflow.com/questions/10848191/git-submodule-commit-hooks
29+
#
30+
# Git 2.10+
31+
# `git rev-parse --git-path hooks`
32+
# Pre Git 2.10+
33+
# `git rev-parse --git-dir /hooks`
34+
#
35+
# This will support as well changes on the default /hooks path
36+
# git config core.hooksPath .githooks/
37+
38+
@spec resolve_git_hooks_path_based_on_git_version() :: String.t()
39+
defp resolve_git_hooks_path_based_on_git_version() do
40+
Git.git_version()
41+
|> Version.compare(Version.parse!("2.10.0"))
42+
|> case do
43+
:lt ->
44+
{path, 0} = System.cmd("git", ["rev-parse", "--git-dir", "hooks"])
45+
String.replace(path, "\n", "")
46+
47+
_gt_or_eq ->
48+
{path, 0} = System.cmd("git", ["rev-parse", "--git-path", "hooks"])
49+
String.replace(path, "\n", "")
50+
end
51+
end
52+
end

lib/mix/tasks/git_hooks/install.ex

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Mix.Tasks.GitHooks.Install do
2121
use Mix.Task
2222

2323
alias GitHooks.Config
24-
alias GitHooks.Git
24+
alias GitHooks.Git.Path, as: GitPath
2525
alias GitHooks.Printer
2626

2727
@impl true
@@ -55,9 +55,7 @@ defmodule Mix.Tasks.GitHooks.Install do
5555

5656
case File.read(template_file) do
5757
{:ok, body} ->
58-
target_file_path =
59-
Git.resolve_git_path()
60-
|> Path.join("/hooks/#{git_hook_atom_as_kebab_string}")
58+
target_file_path = GitPath.git_hooks_path_for(git_hook_atom_as_kebab_string)
6159

6260
target_file_body =
6361
body
@@ -85,13 +83,9 @@ defmodule Mix.Tasks.GitHooks.Install do
8583

8684
@spec backup_current_hook(String.t(), Keyword.t()) :: {:error, atom} | {:ok, non_neg_integer()}
8785
defp backup_current_hook(git_hook_to_backup, opts) do
88-
source_file_path =
89-
Git.resolve_git_path()
90-
|> Path.join("/hooks/#{git_hook_to_backup}")
86+
source_file_path = GitPath.git_hooks_path_for(git_hook_to_backup)
9187

92-
target_file_path =
93-
Git.resolve_git_path()
94-
|> Path.join("/hooks/#{git_hook_to_backup}.pre_git_hooks_backup")
88+
target_file_path = GitPath.git_hooks_path_for("/#{git_hook_to_backup}.pre_git_hooks_backup")
9589

9690
unless opts[:quiet] || !Config.verbose?() do
9791
Printer.info("Backing up git hook file `#{source_file_path}` to `#{target_file_path}`")
@@ -104,8 +98,8 @@ defmodule Mix.Tasks.GitHooks.Install do
10498
defp track_configured_hooks do
10599
git_hooks = Config.git_hooks() |> Enum.join(" ")
106100

107-
Git.resolve_git_path()
108-
|> Path.join("/hooks/git_hooks.db")
101+
"/git_hooks.db"
102+
|> GitPath.git_hooks_path_for()
109103
|> write_backup(git_hooks)
110104
end
111105

@@ -123,8 +117,8 @@ defmodule Mix.Tasks.GitHooks.Install do
123117

124118
@spec ensure_hooks_folder_exists() :: any
125119
defp ensure_hooks_folder_exists do
126-
Git.resolve_git_path()
127-
|> Path.join("/hooks")
120+
"/"
121+
|> GitPath.git_hooks_path_for()
128122
|> File.mkdir_p()
129123
end
130124

@@ -134,8 +128,8 @@ defmodule Mix.Tasks.GitHooks.Install do
134128
Config.git_hooks()
135129
|> Enum.map(&Atom.to_string/1)
136130

137-
Git.resolve_git_path()
138-
|> Path.join("/git_hooks.db")
131+
"/git_hooks.db"
132+
|> GitPath.git_hooks_path_for()
139133
|> File.read()
140134
|> case do
141135
{:ok, file} ->
@@ -149,8 +143,8 @@ defmodule Mix.Tasks.GitHooks.Install do
149143
"Remove old git hook `#{git_hook_atom_as_kebab_string}` and restore backup"
150144
)
151145

152-
Git.resolve_git_path()
153-
|> Path.join("/hooks/#{git_hook_atom_as_kebab_string}")
146+
git_hook_atom_as_kebab_string
147+
|> GitPath.git_hooks_path_for()
154148
|> File.rm()
155149

156150
restore_backup(git_hook_atom_as_kebab_string)
@@ -165,16 +159,9 @@ defmodule Mix.Tasks.GitHooks.Install do
165159
@spec restore_backup(String.t()) :: any
166160
defp restore_backup(git_hook_atom_as_kebab_string) do
167161
backup_path =
168-
Path.join(
169-
Git.resolve_git_path(),
170-
"/hooks/#{git_hook_atom_as_kebab_string}.pre_git_hooks_backup"
171-
)
162+
GitPath.git_hooks_path_for("/#{git_hook_atom_as_kebab_string}.pre_git_hooks_backup")
172163

173-
restore_path =
174-
Path.join(
175-
Git.resolve_git_path(),
176-
"/#{git_hook_atom_as_kebab_string}"
177-
)
164+
restore_path = GitPath.git_hooks_path_for(git_hook_atom_as_kebab_string)
178165

179166
case File.rename(backup_path, restore_path) do
180167
:ok -> :ok

test/git/path_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule GitHooks.Git.PathTest do
2+
use ExUnit.Case
3+
4+
alias GitHooks.Git.Path
5+
6+
describe "git_hooks_path_for/1" do
7+
test "appends the path to the hooks folder" do
8+
assert Path.git_hooks_path_for("/testing") == ".git/hooks/testing"
9+
end
10+
end
11+
end

0 commit comments

Comments
 (0)