Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e2110f8
tests/conftest.py: the session turns git's automatic maintenance off,…
Oct 16, 2026
accc7d2
tests/conftest.py: the note on the session's maintenance settings nam…
Oct 16, 2026
4149cbb
tests/conftest.py and four test files: one helper builds the `url.<fi…
Oct 16, 2026
8cfc8eb
tests/conftest.py and tests/test_no_auto_maintenance.py: the session'…
Oct 16, 2026
979bb39
tests/test_no_auto_maintenance.py: the two new global-file tests shar…
Oct 16, 2026
5c06c11
Merge #713's fix round (979bb394, origin/fix/645-fixture-copy-mainten…
Oct 16, 2026
deb029c
tests/test_clone_just_pushed.py and tests/conftest.py: `read_through_…
Oct 16, 2026
a9f36ce
Merge branch 'main' into fix/645-fixture-copy-maintenance-race
brettheap Oct 17, 2026
b6e1559
tests/test_doctor_legs_members_next.py and 24 other files: tests appe…
Oct 17, 2026
2664c17
Merge #713's final head a9f36ce6 (landed as 741679e)
Oct 17, 2026
8368d1f
Merge main 741679e7.. after #713 landed as 741679e
Oct 17, 2026
2908e46
Merge #748's final head 8368d1f8 and main fa04d2cd into fix/758-git-c…
Oct 17, 2026
eaa72d9
tests/test_no_auto_maintenance.py and two other files: the scan sees …
Oct 17, 2026
4a553e1
Merge main fc867207 into fix/758-git-command-scope, and tests/test_no…
Oct 17, 2026
b9ad118
tests/test_family_head_lost_gitmodules.py and eight other files: `on_…
Oct 17, 2026
fafba9e
Merge #818's refresh 4a553e17 (main fc867207) into fix/835-config-cou…
Oct 17, 2026
79b2eac
tests/test_doctor_paste_through_git.py and seven other files: two com…
Oct 17, 2026
73b6d2b
Merge #818's squash aec3b38 after it landed, taking the branch's side…
Oct 17, 2026
5b27685
Merge branch 'main' into fix/835-config-count-appends
brettheap Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ def submodule_update_through_git(root: Path, remotes: Path,
# suite copies is made, or pushed to, that way today.
# `tests/test_no_auto_maintenance.py` holds both scopes to account, and fails a
# test file that sets `GIT_CONFIG_COUNT` to a number of its own instead of
# appending (#758).
# appending (#758), or works one out by hand (#835). The one place that does
# is `appended_to_command_scope`, below.

#: The two settings, in the order they are appended and written.
NO_AUTO_MAINTENANCE = (("gc.auto", "0"), ("maintenance.auto", "false"))
Expand Down Expand Up @@ -295,6 +296,47 @@ def appended_to_command_scope(environ: Mapping[str, str],
return added


#: The two entries a Windows job holds, under keys no config of a machine this
#: runs on sets.
JOB_ENTRIES = [("test.heldone", "one"), ("test.heldtwo", "two")]


def put_job_and_session_entries(monkeypatch) -> None:
"""The environment holds, for the rest of the test, what the Windows job
sets (`JOB_ENTRIES`) and what the session fixture appends after them (the
two settings of `NO_AUTO_MAINTENANCE`), as a test that reads the
environment at its call finds it. `GIT_CONFIG_PARAMETERS` is taken away
first: git lists its entries after the count's, and a run started under
`git -c` holds some."""
monkeypatch.delenv("GIT_CONFIG_PARAMETERS", raising=False)
held = appended_to_command_scope({}, JOB_ENTRIES)
for name, value in {**held, **appended_to_command_scope(
held, NO_AUTO_MAINTENANCE)}.items():
monkeypatch.setenv(name, value)


def command_line_entries(listing: str) -> list:
"""`key=value` of each entry in the command scope of a `git config
--show-origin --list` listing, in git's order."""
return [line.split("\t", 1)[1] for line in listing.splitlines()
if line.startswith("command line:")]


def command_scope_at(environ: Mapping[str, str], cwd: Path) -> list:
"""The entries git reads from its command scope under `environ` laid over
this process's own (`command_line_entries` of `git config --show-origin
--list`, run in `cwd` and read, never written). `GIT_CONFIG_PARAMETERS`
is taken away, as `put_job_and_session_entries` does."""
laid = {name: value for name, value in os.environ.items()
if name != "GIT_CONFIG_PARAMETERS"}
shown = subprocess.run(
["git", "config", "--show-origin", "--list"], cwd=str(cwd),
env={**laid, **environ}, capture_output=True, text=True,
encoding="utf-8", errors="replace", check=False)
assert shown.returncode == 0, shown.stderr + shown.stdout
return command_line_entries(shown.stdout)


def home_is_known() -> bool:
"""Whether git can expand a `~/` path: `HOME` is set, or this is Windows,
where Git for Windows finds a home of its own."""
Expand Down
14 changes: 9 additions & 5 deletions tests/test_adopt_reencoded_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,15 +423,19 @@ def env_steering(tmp_path: Path, steer: dict) -> subprocess.CompletedProcess:


@pytest.mark.parametrize("steer", [
pytest.param({"GIT_ATTR_SOURCE": "HEAD"}, id="git-attr-source-head"),
pytest.param({"GIT_ATTR_SOURCE": EMPTY_TREE},
pytest.param(lambda: {"GIT_ATTR_SOURCE": "HEAD"},
id="git-attr-source-head"),
pytest.param(lambda: {"GIT_ATTR_SOURCE": EMPTY_TREE},
id="git-attr-source-the-empty-tree"),
pytest.param(command_scope("attr.tree", EMPTY_TREE),
pytest.param(lambda: command_scope("attr.tree", EMPTY_TREE),
id="attr-tree-in-the-command-scope")])
def test_no_tree_this_machine_names_steers_the_question(tmp_path, steer):
"""The throwaway repository has no commit for `HEAD` to name, and the
empty tree would make git read no rule: neither is inherited."""
result = env_steering(tmp_path, steer)
empty tree would make git read no rule: neither is inherited. `steer`
is built here, at the test: `command_scope` appends to the entries the
environment holds WHEN IT IS CALLED, and at collection those are not the
session's (#835)."""
result = env_steering(tmp_path, steer())
assert "REFUSED" not in result.stderr, result.stderr
assert f"FINDING {REENCODED_FILES}: {KEPT_GROUP}" in result.stderr, \
result.stderr
Expand Down
12 changes: 5 additions & 7 deletions tests/test_clone_just_pushed.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,13 @@ def test_config_and_env_reach_the_one_command_and_nothing_is_stored(
config=["-c", "clone.defaultRemoteName=by-config"])
assert_store_read_through_git(by_config, "the clone made with `config`")
assert git("remote", cwd=by_config).stdout.split() == ["by-config"]
# APPENDED to what the environment holds, as `command_scope` does in
# `test_adopt_ignored_shape_files.py`: the Windows job sets two entries
# of its own there, and `GIT_CONFIG_COUNT=1` would drop them.
held = int(os.environ.get("GIT_CONFIG_COUNT") or 0)
# APPENDED to what the environment holds (`appended_to_command_scope`):
# the Windows job sets two entries of its own, and `GIT_CONFIG_COUNT=1`
# would drop them.
by_env = cloned_just_pushed(
remote, tmp_path / "by-env", recurse=False,
env={"GIT_CONFIG_COUNT": str(held + 1),
f"GIT_CONFIG_KEY_{held}": "clone.defaultRemoteName",
f"GIT_CONFIG_VALUE_{held}": "by-env"})
env=appended_to_command_scope(
os.environ, [("clone.defaultRemoteName", "by-env")]))
assert_store_read_through_git(by_env, "the clone made with `env`")
assert git("remote", cwd=by_env).stdout.split() == ["by-env"]
stored = git("config", "--local", "--list", cwd=by_env).stdout
Expand Down
65 changes: 65 additions & 0 deletions tests/test_command_scopes_built_at_the_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: Apache-2.0
"""A command scope a parameter hands a test is built at the test (#835, #818's
second review).

`appended_to_command_scope(os.environ, ...)` appends to the entries the
environment holds WHEN IT IS CALLED. A `pytest.param(command_scope(...))` is
called at collection, before the session fixture (`no_auto_maintenance`) has
put its `gc.auto=0` and `maintenance.auto=false` in the environment. Laid over
the environment at the test, that count drops the two: on a Linux run git read
only `user.name=Ada Lovelace`, or only `attr.tree=...`. The session's global
file still kept maintenance off, so no test failed.

Two parameters were built so (`test_adopt_reencoded_files.py`'s `attr.tree`
and `test_family_init_asks_every_value.py`'s `user.name`). Each is a `lambda`
now, called by its test, as `test_adopt_nested_attributes.py` already did. The
parameters are read off the tests' own marks and asked of git, which reads
the command scope and writes none.
"""

from __future__ import annotations

import pytest

from conftest import NO_AUTO_MAINTENANCE, command_scope_at
import test_adopt_reencoded_files as reencoded
import test_family_init_asks_every_value as family_init
from test_adopt_nested_attributes import EMPTY_TREE

#: The parametrized tests that hand a test a command scope: the module, the
#: test, its parameter, and the entry each param that names one holds.
SITES = {
"attr-tree": (reencoded,
"test_no_tree_this_machine_names_steers_the_question",
"steer",
{"attr-tree-in-the-command-scope":
f"attr.tree={EMPTY_TREE}"}),
"user-name": (family_init,
"test_each_exit_of_the_variables_remedy_goes_on_to_a_holder",
"env", {"unset-it": "user.name=Ada Lovelace"}),
}


def params_of(module, name: str, argument: str) -> list:
"""The `pytest.param`s a test's `parametrize` of `argument` holds."""
marks = [mark for mark in getattr(module, name).pytestmark
if mark.name == "parametrize" and mark.args[0] == argument]
assert len(marks) == 1, marks
return marks[0].args[1]


@pytest.mark.parametrize("site", SITES)
def test_each_param_is_built_at_the_test_beside_the_sessions_settings(
site, tmp_path):
module, name, argument, entries = SITES[site]
seen = {}
for param in params_of(module, name, argument):
value = param.values[0]
seen[param.id] = command_scope_at(
value() if callable(value) else value, tmp_path)
assert set(entries) <= set(seen), sorted(seen)
for param_id, read in seen.items():
for key, value in NO_AUTO_MAINTENANCE:
assert f"{key}={value}" in read, (param_id, read)
for param_id, entry in entries.items():
assert entry in seen[param_id], (param_id, seen[param_id])
Loading
Loading