|
16 | 16 | import ddt |
17 | 17 | import pytest |
18 | 18 |
|
19 | | -from git import NULL_TREE, BlobFilter, Diff, Git, IndexFile, Object, Tree |
| 19 | +from git import NULL_TREE, BlobFilter, Diff, Git, IndexFile, Object, Repo, Tree |
20 | 20 | from git.diff import NULL_TREE_SHA |
21 | 21 | from git.exc import ( |
22 | 22 | CheckoutError, |
@@ -1189,6 +1189,86 @@ def test_pre_commit_hook_outside_worktree(self): |
1189 | 1189 | output = Path(repo.working_dir, "outside-hook-output.txt").read_text(encoding="utf-8") |
1190 | 1190 | self.assertEqual(output, "ran outside hook\n") |
1191 | 1191 |
|
| 1192 | + def test_pre_commit_hook_not_run_in_repository_git_refuses(self): |
| 1193 | + for layout in ("regular", "bare", "linked", "separate", "configured"): |
| 1194 | + # Linked worktree registrations contain absolute paths. |
| 1195 | + with self.subTest(layout=layout), fixture_repo( |
| 1196 | + "hook-ownership.sh", |
| 1197 | + layout, |
| 1198 | + write=True, |
| 1199 | + execute=layout == "linked", |
| 1200 | + subdir="metadata" if layout == "configured" else "repo", |
| 1201 | + ) as repo: |
| 1202 | + output = Path(repo.working_dir, "output.txt") |
| 1203 | + discovery = repo.git_dir if layout == "configured" else repo.working_dir |
| 1204 | + baseline_output = Path(discovery, "output.txt") |
| 1205 | + baseline = Git(discovery) |
| 1206 | + # Reset any configured exemptions, as in Git's t0033-safe-directory.sh. |
| 1207 | + with mock.patch.dict( |
| 1208 | + os.environ, |
| 1209 | + { |
| 1210 | + "GIT_TEST_ASSUME_DIFFERENT_OWNER": "1", |
| 1211 | + "GIT_CONFIG_COUNT": "1", |
| 1212 | + "GIT_CONFIG_KEY_0": "safe.directory", |
| 1213 | + "GIT_CONFIG_VALUE_0": "", |
| 1214 | + }, |
| 1215 | + ): |
| 1216 | + if layout == "regular": |
| 1217 | + with self.assertRaisesRegex(GitCommandError, "dubious ownership"): |
| 1218 | + baseline.commit("--allow-empty", "--no-gpg-sign", "-m", "Git refuses this repository") |
| 1219 | + self.assertFalse(output.exists()) |
| 1220 | + with self.assertRaisesRegex(GitCommandError, "dubious ownership"): |
| 1221 | + repo.index.commit("Git refuses this repository") |
| 1222 | + self.assertFalse(output.exists()) |
| 1223 | + with self.assertRaises(GitCommandError): |
| 1224 | + baseline.hook("run", "pre-commit") |
| 1225 | + self.assertFalse(baseline_output.exists()) |
| 1226 | + with self.assertRaisesRegex(GitCommandError, "dubious ownership"): |
| 1227 | + run_commit_hook("pre-commit", repo.index) |
| 1228 | + self.assertFalse(output.exists()) |
| 1229 | + |
| 1230 | + for environment in ( |
| 1231 | + {"GIT_CONFIG_VALUE_0": str(discovery)}, |
| 1232 | + {"GIT_DIR": str(repo.git_dir), "GIT_WORK_TREE": str(repo.working_dir)}, |
| 1233 | + ): |
| 1234 | + with baseline.custom_environment(**environment), repo.git.custom_environment(**environment): |
| 1235 | + baseline.hook("run", "pre-commit") |
| 1236 | + expected = baseline_output.read_text(encoding="utf-8") |
| 1237 | + baseline_output.unlink() |
| 1238 | + run_commit_hook("pre-commit", repo.index) |
| 1239 | + self.assertEqual(output.read_text(encoding="utf-8"), expected) |
| 1240 | + output.unlink() |
| 1241 | + |
| 1242 | + def test_commit_hook_preserves_explicit_repository_selection(self): |
| 1243 | + for selection in ("one-shot", "persistent", "environment", "git-dir-option"): |
| 1244 | + with self.subTest(selection=selection), fixture_repo("hook-ownership.sh", "regular", write=True) as repo: |
| 1245 | + with Repo(repo.git_dir) as metadata: |
| 1246 | + index = metadata.index |
| 1247 | + baseline = Git(metadata.working_dir) |
| 1248 | + metadata.git.version_info |
| 1249 | + environment = {"GIT_DIR": ".git"} if selection == "environment" else {} |
| 1250 | + with baseline.custom_environment(**environment), metadata.git.custom_environment(**environment): |
| 1251 | + for command in (baseline, metadata.git): |
| 1252 | + if selection == "one-shot": |
| 1253 | + command(C=".git") |
| 1254 | + elif selection == "persistent": |
| 1255 | + command.set_persistent_git_options(C=".git") |
| 1256 | + elif selection == "git-dir-option": |
| 1257 | + command(git_dir=".git") |
| 1258 | + output = Path(repo.git_dir if selection in ("one-shot", "persistent") else repo.working_dir) |
| 1259 | + output /= "output.txt" |
| 1260 | + baseline.hook("run", "pre-commit") |
| 1261 | + expected = output.read_text(encoding="utf-8") |
| 1262 | + output.unlink() |
| 1263 | + run_commit_hook("pre-commit", index) |
| 1264 | + self.assertEqual(output.read_text(encoding="utf-8"), expected) |
| 1265 | + |
| 1266 | + metadata.git.set_persistent_git_options() |
| 1267 | + metadata.git(C="missing") |
| 1268 | + with self.assertRaises(GitCommandError): |
| 1269 | + run_commit_hook("pre-commit", index) |
| 1270 | + self.assertEqual(metadata.git.rev_parse("--git-dir"), str(metadata.git_dir)) |
| 1271 | + |
1192 | 1272 | def test_pre_commit_hook_fail(self): |
1193 | 1273 | with fixture_repo("index.sh", "hook-fail", write=True) as repo: |
1194 | 1274 | with self.assertRaises(HookExecutionError) as caught: |
|
0 commit comments