|
11 | 11 |
|
12 | 12 | from git import Actor, Commit, Git, Reference, Repo, SymbolicReference, _backend |
13 | 13 | from git.exc import GitCommandError, InvalidGitRepositoryError |
| 14 | +from git.repo.fun import find_submodule_git_dir, find_worktree_git_dir, is_git_dir |
14 | 15 | from gitdb import IStream |
15 | 16 |
|
16 | 17 | gix = pytest.importorskip("gix") |
@@ -122,6 +123,18 @@ def test_opening_and_discovery_use_gix_with_explicit_fallbacks(repo, tmp_path): |
122 | 123 | bare_linked_native = gix.open(bare_linked) |
123 | 124 | assert bare_linked_native.is_bare() |
124 | 125 | assert bare_linked_native.workdir() == bare_linked |
| 126 | + gitfile = tmp_path / "gitfile" |
| 127 | + gitfile.write_text("gitdir: repo/.git\n") |
| 128 | + candidates = [repo.git_dir, bare.git_dir, linked / ".git", linked_admin, gitfile] |
| 129 | + with patch.object(_backend, "gix", None): |
| 130 | + expected = [find_submodule_git_dir(path) for path in candidates] |
| 131 | + with patch.object(Git, "execute", side_effect=AssertionError("unexpected CLI call")): |
| 132 | + assert [find_submodule_git_dir(path) for path in candidates] == expected |
| 133 | + assert find_worktree_git_dir(gitfile) == expected[-1] |
| 134 | + assert is_git_dir(repo.git_dir) |
| 135 | + with patch.object(Git, "execute", autospec=True, side_effect=Git.execute) as cli: |
| 136 | + assert find_submodule_git_dir(nested) is None |
| 137 | + assert sum("--resolve-git-dir" in call.args[1] for call in cli.call_args_list) == 1 |
125 | 138 | for path, search, expected_bare, rejected_candidates in [ |
126 | 139 | (root, False, False, 0), |
127 | 140 | (repo.git_dir, False, False, 0), |
@@ -165,6 +178,45 @@ def test_discovery_asks_gix_before_falling_back(tmp_path): |
165 | 178 | native_open.assert_called_once() |
166 | 179 |
|
167 | 180 |
|
| 181 | +@pytest.mark.parametrize("target", ["repo/.git", "repo/../repo/.git", "alias/.git"]) |
| 182 | +def test_gitfile_reopens_canonical_native_repository(repo, tmp_path, target): |
| 183 | + if target == "alias/.git": |
| 184 | + try: |
| 185 | + (tmp_path / "alias").symlink_to(repo.working_dir, target_is_directory=True) |
| 186 | + except OSError as exc: |
| 187 | + pytest.skip(f"symlinks unavailable: {exc}") |
| 188 | + gitfile = tmp_path / "gitfile" |
| 189 | + gitfile.write_text(f"gitdir: {target}\n") |
| 190 | + with patch.object(_backend, "gix", None), Repo(gitfile) as control: |
| 191 | + expected = (control.git_dir, control.working_tree_dir, control.bare) |
| 192 | + expected_git_path = repo.git.rev_parse("--resolve-git-dir", str(gitfile)) |
| 193 | + with patch.object(Git, "execute", side_effect=AssertionError("unexpected CLI call")): |
| 194 | + with patch.object(gix, "open_opts", wraps=gix.open_opts) as native_open: |
| 195 | + native = _backend.discover_repository(str(gitfile), {}) |
| 196 | + assert (str(native.git_dir()), str(native.workdir()), native.is_bare()) == expected |
| 197 | + assert native_open.call_count == 2 |
| 198 | + assert native_open.call_args.args[0] == expected[0] |
| 199 | + assert native_open.call_args.args[1] is native_open.call_args_list[0].args[1] |
| 200 | + assert find_submodule_git_dir(gitfile) == expected_git_path |
| 201 | + assert find_worktree_git_dir(gitfile) == expected_git_path |
| 202 | + with patch.object(Git, "execute", autospec=True, side_effect=Git.execute) as cli: |
| 203 | + with Repo(gitfile) as opened: |
| 204 | + assert (opened.git_dir, str(opened.working_tree_dir), opened.bare) == expected |
| 205 | + assert isinstance(opened._gix_repository, gix.Repository) |
| 206 | + assert all("version" in call.args[1] or "--show-ref-format" in call.args[1] for call in cli.call_args_list) |
| 207 | + |
| 208 | + |
| 209 | +def test_gitfile_reopen_failure_selects_fallback(repo, tmp_path): |
| 210 | + gitfile = tmp_path / "gitfile" |
| 211 | + gitfile.write_text("gitdir: repo/.git\n") |
| 212 | + outcomes = [gix.open(repo.git_dir), gix.Error("canonical reopen failed")] |
| 213 | + before = _backend.statistics().get(("Repo.open", "native"), 0) |
| 214 | + with patch.object(gix, "open_opts", side_effect=outcomes) as native_open: |
| 215 | + assert _backend.discover_repository(str(gitfile), {}) is NotImplemented |
| 216 | + assert native_open.call_count == 2 |
| 217 | + assert _backend.statistics().get(("Repo.open", "native"), 0) == before |
| 218 | + |
| 219 | + |
168 | 220 | def test_native_objects_trees_commits_and_index_reads(repo): |
169 | 221 | actor = Actor("Example", "example@example.invalid") |
170 | 222 | paths = ["file", "dir/with space", "dir/unicode-é", "--option"] |
|
0 commit comments