Skip to content

Commit 0901035

Browse files
Byroncodex
andcommitted
test: isolate repository fixtures from checkout history
Took a quick look, pretty zoomed out, but think it's a desirable change as finally the test-suite is independent of the input repository or state. <!-- agent --> Make PR and post-merge test results independent of checkout depth, refs, and history. The shared test repository and dependency clone sources previously came from the source checkout, requiring historical objects and a bootstrap script even though setup already used `gix.testtools`. Generate small repositories from scratch through `gix.testtools`, with named root, release, merge, conflict, rename, date, and submodule states. Adapt legacy assertions to those scenarios while retaining merge, serialization, traversal, index, remote, and nested-submodule coverage. Make dirty-submodule removal checks explicit for staged, unstaged, and untracked data, including dry runs. Remove the source-history environment override, dependency factory, checkout lock cleanup, and bootstrap script. The main CI suites now use shallow checkouts and remove source `.git` metadata before pytest. Add a regression that initializes the shared fixture from a source copy without Git metadata, and document the isolation requirement. Hash raw parser samples in cache arguments to avoid archive filename limits on cold runs. Assisted-by: GPT 6.0 Astra Co-authored-by: GPT 6.0 Astra <codex@openai.com>
1 parent 9bfb816 commit 0901035

29 files changed

Lines changed: 424 additions & 491 deletions

‎.github/workflows/alpine-test.yml‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,13 @@ jobs:
3434

3535
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3636
with:
37-
fetch-depth: 0
3837
persist-credentials: false
3938

4039
- name: Set workspace ownership
4140
run: |
4241
chown -R runner:docker -- "$GITHUB_WORKSPACE"
4342
shell: sh -exo pipefail {0} # Run this as root, not the "runner" user.
4443

45-
- name: Prepare this repo for tests
46-
run: |
47-
./init-tests-after-clone.sh
48-
4944
- name: Set git user identity and command aliases for the tests
5045
run: |
5146
git config --global user.email "travis@ci.com"
@@ -95,6 +90,9 @@ jobs:
9590
python --version
9691
python -c 'import os, sys; print(f"{sys.platform=}, {os.name=}")'
9792
93+
- name: Remove source Git metadata to verify fixture isolation
94+
run: .venv/bin/python -c "from pathlib import Path; from git.util import rmtree; rmtree('.git'); assert not Path('.git').exists()"
95+
9896
- name: Test with pytest
9997
run: |
10098
. .venv/bin/activate

‎.github/workflows/cygwin-test.yml‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ jobs:
3030

3131
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3232
with:
33-
fetch-depth: 0
3433
persist-credentials: false
3534

3635
- name: Install Cygwin
@@ -61,10 +60,6 @@ jobs:
6160
git config --global --add safe.directory "$(pwd)/.git"
6261
git config --global core.autocrlf false
6362
64-
- name: Prepare this repo for tests
65-
run: |
66-
./init-tests-after-clone.sh
67-
6863
- name: Set git user identity and command aliases for the tests
6964
run: |
7065
git config --global user.email "travis@ci.com"

‎.github/workflows/pythonpackage.yml‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ jobs:
6161
steps:
6262
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
6363
with:
64-
fetch-depth: 0
6564
persist-credentials: false
6665

6766
- name: Set up Python ${{ matrix.python-version }}
@@ -115,10 +114,6 @@ jobs:
115114
brew install git
116115
echo "$(brew --prefix git)/bin" >> "$GITHUB_PATH"
117116
118-
- name: Prepare this repo for tests
119-
run: |
120-
./init-tests-after-clone.sh
121-
122117
- name: Set git user identity and command aliases for the tests
123118
run: |
124119
git config --global user.email "travis@ci.com"
@@ -219,6 +214,9 @@ jobs:
219214
run: |
220215
basedpyright --warnings
221216
217+
- name: Remove source Git metadata to verify fixture isolation
218+
run: python -c "from pathlib import Path; from git.util import rmtree; rmtree('.git'); assert not Path('.git').exists()"
219+
222220
- name: Test with pytest
223221
run: |
224222
if [[ "$RUNNER_OS" == "Windows" && -z "${GIT_PYTHON_GIT_EXECUTABLE+x}" ]]; then

‎CONTRIBUTING.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
The following is a short step-by-step rundown of what one typically would do to contribute.
44

55
- [Fork this project](https://github.com/gitpython-developers/GitPython/fork) on GitHub.
6-
- For setting up the environment to run the self tests, please run `init-tests-after-clone.sh`.
6+
- Install the `test` extra and select a backend before running `pytest`. Test
7+
repositories are generated automatically; no source Git history is required.
78
- Please try to **write a test that fails unless the contribution is present.**
89
- Try to avoid massive commits and prefer to take small steps, with one commit for each.
910
- Feel free to add yourself to AUTHORS file.
@@ -64,8 +65,10 @@ Put initial repository and filesystem states in `tests/fixtures/*.sh` and use
6465
`test.lib.fixtures` to run them through `gix.testtools`. Read-only tests share
6566
cached output; mutating tests use writable copies. Use `execute=True` only when
6667
the fixture cannot be copied, and explain the reason at its call site. Keep
67-
operations under test in Python. Immutable parser byte samples remain in
68-
`test/fixtures`; generated caches and archives are ignored.
68+
operations under test in Python. Generate histories from scratch and refer to
69+
named fixture revisions; do not clone the source checkout, fetch project history,
70+
or depend on its refs, object IDs, index, or working-tree state. Immutable parser
71+
byte samples remain in `test/fixtures`; generated caches and archives are ignored.
6972

7073
Use `test.cleanup.cleanup_directory` for cleanup outside the shared fixture
7174
owners. Close repository handles before removing their files. Cleanup is

‎README.md‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,8 @@ To clone the [the GitHub repository](https://github.com/gitpython-developers/Git
118118
```sh
119119
git clone https://github.com/gitpython-developers/GitPython
120120
cd GitPython
121-
./init-tests-after-clone.sh
122121
```
123122

124-
On Windows, `./init-tests-after-clone.sh` can be run in a Git Bash shell.
125-
126123
If you are cloning [your own fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then replace the above `git clone` command with one that gives the URL of your fork. Or use this [`gh`](https://cli.github.com/) command (assuming you have `gh` and your fork is called `GitPython`):
127124

128125
```sh
@@ -175,9 +172,8 @@ See [Issue #525](https://github.com/gitpython-developers/GitPython/issues/525).
175172

176173
### RUNNING TESTS
177174

178-
_Important_: Right after cloning this repository, please be sure to have executed
179-
the `./init-tests-after-clone.sh` script in the repository root. Otherwise
180-
you will encounter test failures.
175+
Test repositories are generated from scratch by fixture scripts. Tests work from
176+
a shallow clone or a source copy without `.git`; no checkout preparation is needed.
181177

182178
#### Install test dependencies
183179

‎doc/gix-backend.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ installation/import smoke coverage, including the Cygwin Python 3.9 job.
214214
Running tests also requires Bash and Git 2.52+; `gix` here is GixPython's module.
215215

216216
Initial repository and filesystem states live in `tests/fixtures/*.sh`.
217+
Histories are generated from scratch, including the shared sample repository and
218+
its nested dependency repositories. Tests use named revisions such as `root`,
219+
`release`, and `merge`, and require no source checkout history or Git metadata.
220+
CI removes the source `.git` directory before running the full suite.
217221
`test/lib/fixtures.py` calls `gix.testtools` with an explicit lifetime:
218222

219223
- `read_only_fixture()` and the default `fixture_repo()` reuse cached output.

‎init-tests-after-clone.sh‎

Lines changed: 0 additions & 76 deletions
This file was deleted.

‎test/conftest.py‎

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55
from pathlib import Path
66

77
import pytest
8-
from gix import testtools
98

109
from git import _backend
11-
from test.lib import GIT_REPO, TestBase
12-
from test.lib.fixtures import read_only_fixture
1310

1411

1512
def pytest_addoption(parser):
@@ -99,29 +96,3 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
9996
with open(path, "w", encoding="utf-8") as stream:
10097
json.dump(records, stream, indent=2)
10198
stream.write("\n")
102-
103-
104-
@pytest.fixture(scope="session")
105-
def dependency_repo_factory():
106-
"""Prepare each immutable historical clone source only when a test needs it."""
107-
source = Path(GIT_REPO).resolve()
108-
revision = testtools.git(source, "rev-parse HEAD").strip()
109-
paths = {}
110-
111-
def get(name):
112-
if name not in paths:
113-
paths[name] = str(read_only_fixture("dependencies.sh", source, revision, name) / "repo")
114-
return paths[name]
115-
116-
return get
117-
118-
119-
@pytest.fixture(scope="class", autouse=True)
120-
def historical_dependency_sources(request):
121-
if request.cls is not None and issubclass(request.cls, TestBase):
122-
factory = request.getfixturevalue("dependency_repo_factory")
123-
request.cls._dependency_repo_factory = staticmethod(factory)
124-
yield
125-
del request.cls._dependency_repo_factory
126-
else:
127-
yield

‎test/fixtures/diff_initial‎

Lines changed: 0 additions & 8 deletions
This file was deleted.

‎test/lib/helper.py‎

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"TestCase",
1717
"SkipTest",
1818
"skipIf",
19-
"GIT_REPO",
2019
"GIT_DAEMON_PORT",
2120
"xfail_if_raises",
2221
"symlinks_supported",
@@ -31,7 +30,6 @@
3130
import logging
3231
import os
3332
import os.path as osp
34-
from pathlib import Path
3533
from stat import S_ISLNK, ST_MODE
3634
import socket
3735
import textwrap
@@ -41,7 +39,6 @@
4139
from typing import Union, Type, Tuple
4240

4341
import pytest
44-
from gix import testtools
4542

4643
from git.util import cwd
4744
from test.cleanup import TemporaryDirectory
@@ -53,7 +50,6 @@
5350

5451
ospd = osp.dirname
5552

56-
GIT_REPO = os.environ.get("GIT_PYTHON_TEST_GIT_REPO_BASE", ospd(ospd(ospd(__file__))))
5753
GIT_DAEMON_PORT = os.environ.get("GIT_PYTHON_TEST_GIT_DAEMON_PORT", "19418")
5854

5955
_logger = logging.getLogger(__name__)
@@ -373,22 +369,21 @@ class TestBase(TestCase):
373369
374370
self.rorepo # 'ro' stands for read-only
375371
376-
The rorepo is a scripted clone of your project's git repo. If you refer to specific
377-
shas for your objects, be sure you choose some that are part of the immutable
378-
portion of the project history (so that tests don't fail for others).
372+
The rorepo is generated by sample-repositories.sh. Its named revisions describe
373+
the test scenarios and do not depend on the project's checkout or history.
379374
"""
380375

381376
def _small_repo_url(self):
382377
""":return: A path to a small, clonable repository"""
383378
from git.cmd import Git
384379

385-
return Git.polish_url(self._dependency_repo_factory("smmap"))
380+
return Git.polish_url(str(read_only_fixture("sample-repositories.sh") / "smmap"))
386381

387382
def _gitdb_repo_url(self):
388-
""":return: A local gitdb repository reconstructed from merged history"""
383+
""":return: A generated repository with a nested submodule"""
389384
from git.cmd import Git
390385

391-
return Git.polish_url(self._dependency_repo_factory("gitdb"))
386+
return Git.polish_url(str(read_only_fixture("sample-repositories.sh") / "gitdb"))
392387

393388
@classmethod
394389
def setUpClass(cls):
@@ -399,9 +394,7 @@ def setUpClass(cls):
399394
from git import Repo
400395

401396
gc.collect()
402-
source = Path(GIT_REPO).resolve()
403-
revision = testtools.git(source, "rev-parse HEAD").strip()
404-
cls.rorepo = Repo(read_only_fixture("history.sh", source, revision, revision, "baseline") / "repo")
397+
cls.rorepo = Repo(read_only_fixture("sample-repositories.sh") / "repo")
405398

406399
@classmethod
407400
def tearDownClass(cls):

0 commit comments

Comments
 (0)