|
3 | 3 |
|
4 | 4 | import contextlib |
5 | 5 | import gc |
| 6 | +from functools import lru_cache |
6 | 7 | import os |
7 | 8 | import os.path as osp |
8 | 9 | from pathlib import Path |
@@ -157,22 +158,49 @@ def test_submodule_cli_lifecycle(tmp_path, monkeypatch, object_format, ref_forma |
157 | 158 | assert not parent.submodules |
158 | 159 |
|
159 | 160 |
|
| 161 | +@pytest.fixture(scope="module") |
| 162 | +def movable_submodule_baseline(tmp_path_factory): |
| 163 | + """Prepare each logical-name layout once; only copies are handed to tests.""" |
| 164 | + |
| 165 | + @lru_cache(maxsize=None) |
| 166 | + def prepare(name): |
| 167 | + root = tmp_path_factory.mktemp("movable-submodule") |
| 168 | + with git.Repo.init(root / "source") as source, git.Repo.init(root / "parent") as parent: |
| 169 | + source.git.symbolic_ref("HEAD", "refs/heads/master") |
| 170 | + (root / "source/file").write_text("content", encoding="utf-8") |
| 171 | + source.index.add(["file"]) |
| 172 | + source.index.commit("Create source") |
| 173 | + with _patch_git_config("protocol.file.allow", "always"): |
| 174 | + submodule = parent.create_submodule(name, "module", source.working_tree_dir) |
| 175 | + parent.index.commit("Create submodule") |
| 176 | + submodule.module().close() |
| 177 | + return root / "parent" |
| 178 | + |
| 179 | + return prepare |
| 180 | + |
| 181 | + |
160 | 182 | @pytest.fixture |
161 | | -def movable_submodule(tmp_path, request): |
162 | | - """Create a committed local submodule whose logical name stays fixed when moved.""" |
163 | | - with git.Repo.init(tmp_path / "source") as source, git.Repo.init(tmp_path / "parent") as parent: |
164 | | - source.git.symbolic_ref("HEAD", "refs/heads/master") |
165 | | - (tmp_path / "source" / "file").write_text("content", encoding="utf-8") |
166 | | - source.index.add(["file"]) |
167 | | - source.index.commit("Create source") |
168 | | - with _patch_git_config("protocol.file.allow", "always"): |
169 | | - submodule = parent.create_submodule( |
170 | | - getattr(request, "param", "logical-name"), "module", source.working_tree_dir |
171 | | - ) |
172 | | - parent.index.commit("Create submodule") |
173 | | - # Release clone handles before Windows moves the checkout. |
174 | | - submodule.module().close() |
175 | | - yield submodule |
| 183 | +def movable_submodule(tmp_path, request, movable_submodule_baseline): |
| 184 | + """Give each test independent writable metadata and a fresh Python wrapper.""" |
| 185 | + baseline = movable_submodule_baseline(getattr(request, "param", "logical-name")) |
| 186 | + shutil.copytree(baseline, tmp_path / "parent", symlinks=True) |
| 187 | + with git.Repo(tmp_path / "parent") as parent: |
| 188 | + yield parent.submodules[0] |
| 189 | + |
| 190 | + |
| 191 | +def test_movable_submodule_copies_are_isolated(movable_submodule, movable_submodule_baseline): |
| 192 | + sm = movable_submodule |
| 193 | + baseline = movable_submodule_baseline(sm.name) |
| 194 | + for relative in (".git/index", ".git/config", ".git/modules/logical-name/config", "module/file"): |
| 195 | + copy = Path(sm.repo.working_tree_dir, relative) |
| 196 | + original = baseline / relative |
| 197 | + assert copy.read_bytes() == original.read_bytes() |
| 198 | + assert not copy.samefile(original) |
| 199 | + Path(sm.abspath, "file").write_text("changed", encoding="utf-8") |
| 200 | + sm.repo.create_head("copy-only") |
| 201 | + assert (baseline / "module/file").read_text(encoding="utf-8") == "content" |
| 202 | + with git.Repo(baseline) as original: |
| 203 | + assert "copy-only" not in original.heads |
176 | 204 |
|
177 | 205 |
|
178 | 206 | def _move_snapshot(submodule): |
|
0 commit comments