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