fix: name the dirty profiles profile_sync refuses

The refusal said the directory carried uncommitted changes without naming the profile, among about 375. It now lists the files git status reports, and says that --force writes over them.
This commit is contained in:
Abdessamad Derraz committed 2026-11-05 19:27:12 +01:00
1 parent fb9b5592a5
commit a3c853316a
2 files changed
+33 -5

No files matched your search

+15 -5
View File
@@ -4154,16 +4154,22 @@ def _refuse_dirty_tree(emulators_dir: str, names: list[str]) -> None:
print(f"{e}. Pass --force to write anyway.", file=sys.stderr)
raise SystemExit(1) from e
if dirty:
# Another session may own the change, so the refusal names the files:
# --force writes over them.
one = len(dirty) == 1
print(
f"{emulators_dir} carries uncommitted changes to the selected "
"profiles. Commit them first or pass --force.",
f"{', '.join(dirty)} {'has' if one else 'have'} uncommitted changes: "
f"commit {'it' if one else 'them'} or pass --force, which writes over "
f"{'it' if one else 'them'}.",
file=sys.stderr,
)
raise SystemExit(1)
def emulators_dir_is_dirty(emulators_dir: str, names: list[str] | None = None) -> bool:
"""True when the profile directory carries uncommitted changes.
def emulators_dir_is_dirty(emulators_dir: str, names: list[str] | None = None) -> list[str]:
"""The files of the profile directory that carry uncommitted changes.
Paths read as the status prints them, from the repository root.
``names`` narrows the question to those profiles. Git is asked from
inside the directory, so a profile tree that lives in another
@@ -4181,7 +4187,11 @@ def emulators_dir_is_dirty(emulators_dir: str, names: list[str] | None = None) -
raise RuntimeError(
f"cannot read the git status of {emulators_dir}: {result.stderr.strip()}"
)
return bool(result.stdout.strip())
return sorted(
line[3:].split(" -> ")[-1].strip('"')
for line in result.stdout.splitlines()
if line.strip()
)
def build_parser() -> argparse.ArgumentParser:
+18
View File
@@ -5303,6 +5303,24 @@ class TestEmulatorsDirStatus(unittest.TestCase):
self.assertTrue(profile_sync.emulators_dir_is_dirty(root, ["a"]))
self.assertFalse(profile_sync.emulators_dir_is_dirty(root, ["b"]))
def test_the_refusal_names_the_changed_profile(self):
"""One profile changed by another session among ~375 refused the
whole run without saying which."""
with tempfile.TemporaryDirectory() as root:
self._git("init", "-q", cwd=root)
for name in "abc":
Path(root, f"{name}.yml").write_text(f"emulator: {name}\n")
self._git("add", "a.yml", "b.yml", "c.yml", cwd=root)
self._git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm", "p", cwd=root)
Path(root, "b.yml").write_text("emulator: b2\n")
self.assertEqual(profile_sync.emulators_dir_is_dirty(root, ["a", "b", "c"]),
["b.yml"])
err = io.StringIO()
with contextlib.redirect_stderr(err), self.assertRaises(SystemExit):
profile_sync._refuse_dirty_tree(root, ["a", "b", "c"])
self.assertIn("b.yml has uncommitted changes", err.getvalue())
self.assertNotIn("a.yml", err.getvalue())
def test_a_tree_outside_any_repository_is_not_clean(self):
with tempfile.TemporaryDirectory() as root, mock.patch.dict(
os.environ, {"GIT_CEILING_DIRECTORIES": str(Path(root).parent)}