@@ -1242,6 +1242,91 @@ def test_commit_msg_hook_fail(self, rw_repo):
12421242 else :
12431243 raise AssertionError ("Should have caught a HookExecutionError" )
12441244
1245+ # ---- core.hooksPath tests ----
1246+
1247+ @pytest .mark .xfail (
1248+ type (_win_bash_status ) is WinBashStatus .Absent ,
1249+ reason = "Can't run a hook on Windows without bash.exe." ,
1250+ raises = HookExecutionError ,
1251+ )
1252+ @pytest .mark .xfail (
1253+ type (_win_bash_status ) is WinBashStatus .WslNoDistro ,
1254+ reason = "Currently uses the bash.exe of WSL, even with no WSL distro installed" ,
1255+ raises = HookExecutionError ,
1256+ )
1257+ @with_rw_repo ("HEAD" , bare = True )
1258+ def test_run_commit_hook_respects_absolute_hooks_path (self , rw_repo ):
1259+ """Hooks in a custom absolute core.hooksPath are found and executed."""
1260+ index = rw_repo .index
1261+ custom_hooks_dir = osp .join (rw_repo .working_dir , "custom-hooks" )
1262+ os .makedirs (custom_hooks_dir , exist_ok = True )
1263+ hp = osp .join (custom_hooks_dir , "fake-hook" )
1264+ with open (hp , "w" , encoding = "utf-8" ) as f :
1265+ f .write ("#!/bin/sh\n echo 'ran custom hook' >output.txt\n " )
1266+ os .chmod (hp , 0o755 )
1267+
1268+ rw_repo .config_writer ().set_value ("core" , "hooksPath" , custom_hooks_dir ).release ()
1269+ run_commit_hook ("fake-hook" , index )
1270+ output = Path (rw_repo .git_dir , "output.txt" ).read_text (encoding = "utf-8" )
1271+ self .assertEqual (output , "ran custom hook\n " )
1272+
1273+ @pytest .mark .xfail (
1274+ type (_win_bash_status ) is WinBashStatus .Absent ,
1275+ reason = "Can't run a hook on Windows without bash.exe." ,
1276+ raises = HookExecutionError ,
1277+ )
1278+ @pytest .mark .xfail (
1279+ type (_win_bash_status ) is WinBashStatus .WslNoDistro ,
1280+ reason = "Currently uses the bash.exe of WSL, even with no WSL distro installed" ,
1281+ raises = HookExecutionError ,
1282+ )
1283+ @with_rw_repo ("HEAD" , bare = True )
1284+ def test_run_commit_hook_respects_relative_hooks_path (self , rw_repo ):
1285+ """A relative core.hooksPath is resolved against the working tree root."""
1286+ index = rw_repo .index
1287+ custom_hooks_dir = osp .join (rw_repo .working_dir , "hooks-relative" )
1288+ os .makedirs (custom_hooks_dir , exist_ok = True )
1289+ hp = osp .join (custom_hooks_dir , "fake-hook" )
1290+ with open (hp , "w" , encoding = "utf-8" ) as f :
1291+ f .write ("#!/bin/sh\n echo 'ran relative hook' >output.txt\n " )
1292+ os .chmod (hp , 0o755 )
1293+
1294+ rw_repo .config_writer ().set_value ("core" , "hooksPath" , "hooks-relative" ).release ()
1295+ run_commit_hook ("fake-hook" , index )
1296+ output = Path (rw_repo .git_dir , "output.txt" ).read_text (encoding = "utf-8" )
1297+ self .assertEqual (output , "ran relative hook\n " )
1298+
1299+ @with_rw_repo ("HEAD" , bare = True )
1300+ def test_run_commit_hook_falls_back_to_default_without_hooks_path (self , rw_repo ):
1301+ """When core.hooksPath is not set, the default .git/hooks path is used."""
1302+ index = rw_repo .index
1303+ _make_hook (index .repo .git_dir , "fake-hook" , "echo 'ran default hook' >output.txt" )
1304+ run_commit_hook ("fake-hook" , index )
1305+ output = Path (rw_repo .git_dir , "output.txt" ).read_text (encoding = "utf-8" )
1306+ self .assertEqual (output , "ran default hook\n " )
1307+
1308+ @pytest .mark .xfail (
1309+ type (_win_bash_status ) is WinBashStatus .Absent ,
1310+ reason = "Can't run a hook on Windows without bash.exe." ,
1311+ raises = HookExecutionError ,
1312+ )
1313+ @pytest .mark .xfail (
1314+ type (_win_bash_status ) is WinBashStatus .WslNoDistro ,
1315+ reason = "Currently uses the bash.exe of WSL, even with no WSL distro installed" ,
1316+ raises = HookExecutionError ,
1317+ )
1318+ @with_rw_repo ("HEAD" , bare = True )
1319+ def test_run_commit_hook_hooks_path_skips_missing_hook (self , rw_repo ):
1320+ """If a hook does not exist in core.hooksPath, no error is raised."""
1321+ index = rw_repo .index
1322+ custom_hooks_dir = osp .join (rw_repo .working_dir , "empty-hooks" )
1323+ os .makedirs (custom_hooks_dir , exist_ok = True )
1324+ rw_repo .config_writer ().set_value ("core" , "hooksPath" , custom_hooks_dir ).release ()
1325+ # Should not raise -- the hook doesn't exist, so run_commit_hook returns silently
1326+ run_commit_hook ("nonexistent-hook" , index )
1327+
1328+ # ---- end core.hooksPath tests ----
1329+
12451330 @with_rw_repo ("HEAD" )
12461331 def test_index_add_pathlib (self , rw_repo ):
12471332 git_dir = Path (rw_repo .git_dir )
0 commit comments