mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
repos: find submodule git directories the way git does
A submodule git directory which borrows another (commondir) has no objects of its own, and was skipped instead of refused, because "has objects/" decided what a git directory was. The walk now follows git's layout: under .git/modules every directory holding HEAD is a git directory (whatever the submodule's name, even `refs`), which must not borrow, must have its own objects and a regular config, and is sanitized; nested submodules are found in its own `modules`, and its refs and logs are never mistaken for git directories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9e8947767b
commit
a1d4c42eee
2 changed files with 60 additions and 6 deletions
|
|
@ -443,12 +443,26 @@ def sanitize_git_config(directory: pathlib.Path):
|
|||
if path.is_symlink():
|
||||
# git would follow it out of the repo.
|
||||
raise ValueError(f'{path} is a link')
|
||||
for submodule_git_dir in sorted({i.parent for i in modules.rglob('HEAD')}):
|
||||
if not (submodule_git_dir / 'objects').is_dir():
|
||||
continue # e.g. refs/remotes/origin/HEAD, a ref, not a git directory.
|
||||
if borrowed := _borrowing_git_file(submodule_git_dir):
|
||||
raise ValueError(f'{submodule_git_dir} uses another git directory ({borrowed})')
|
||||
_sanitize_config_file(submodule_git_dir / 'config', worktree_root=root)
|
||||
_sanitize_submodule_git_dirs(modules, root)
|
||||
|
||||
|
||||
def _sanitize_submodule_git_dirs(modules: pathlib.Path, root: pathlib.Path):
|
||||
"""Check and sanitize every git directory in a `modules` directory, as git finds them: a directory holding HEAD
|
||||
is a submodule's git directory (a submodule's name may contain `/`), and its nested submodules are in its own
|
||||
`modules`. Its refs and logs (which also hold files named HEAD) are not git directories."""
|
||||
for child in sorted(modules.iterdir()):
|
||||
if not child.is_dir():
|
||||
continue
|
||||
if not (child / 'HEAD').exists():
|
||||
_sanitize_submodule_git_dirs(child, root)
|
||||
continue
|
||||
if borrowed := _borrowing_git_file(child):
|
||||
raise ValueError(f'{child} uses another git directory ({borrowed})')
|
||||
if not (child / 'objects').is_dir():
|
||||
raise ValueError(f'{child} has no objects of its own')
|
||||
_sanitize_config_file(child / 'config', worktree_root=root)
|
||||
if (child / 'modules').is_dir():
|
||||
_sanitize_submodule_git_dirs(child / 'modules', root)
|
||||
|
||||
|
||||
def find_readme(directory: pathlib.Path) -> Optional[str]:
|
||||
|
|
|
|||
|
|
@ -308,3 +308,43 @@ def test_submodule_git_directories_pass(tmp_path):
|
|||
(git_dir / 'config').write_text('[core]\n\tbare = false\n')
|
||||
lib.sanitize_git_config(repo)
|
||||
assert 'proxy' not in (module / 'config').read_text()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ['library', 'refs', 'vendor/library'])
|
||||
def test_submodule_git_directory_without_objects_is_refused(tmp_path, name):
|
||||
"""A git directory which borrows another (commondir) has no objects of its own; it is refused, not skipped,
|
||||
whatever the submodule is named."""
|
||||
repo = tmp_path / 'repo'
|
||||
git_dir = repo / '.git'
|
||||
module = git_dir / 'modules' / name
|
||||
module.mkdir(parents=True)
|
||||
(git_dir / 'config').write_text('[core]\n\tbare = false\n')
|
||||
(module / 'HEAD').write_text('ref: refs/heads/main\n')
|
||||
(module / 'commondir').write_text(f'{tmp_path}\n')
|
||||
with pytest.raises(ValueError):
|
||||
lib.sanitize_git_config(repo)
|
||||
|
||||
|
||||
def test_nested_submodule_git_directories(tmp_path):
|
||||
"""A nested submodule's git directory is checked too; refs and logs named HEAD are not git directories."""
|
||||
repo = tmp_path / 'repo'
|
||||
git_dir = repo / '.git'
|
||||
(git_dir).mkdir(parents=True)
|
||||
(git_dir / 'config').write_text('[core]\n\tbare = false\n')
|
||||
outer = git_dir / 'modules/library'
|
||||
inner = outer / 'modules/nested'
|
||||
for gitdir in (outer, inner):
|
||||
(gitdir / 'objects').mkdir(parents=True)
|
||||
(gitdir / 'HEAD').write_text('ref: refs/heads/main\n')
|
||||
(gitdir / 'config').write_text('[core]\n\tbare = false\n[http]\n\tproxy = evil\n')
|
||||
(gitdir / 'refs/remotes/origin').mkdir(parents=True)
|
||||
(gitdir / 'refs/remotes/origin/HEAD').write_text('ref: refs/remotes/origin/main\n')
|
||||
(gitdir / 'logs').mkdir()
|
||||
(gitdir / 'logs/HEAD').write_text('0000 1111 t <t@t> 0 +0000\tclone\n')
|
||||
lib.sanitize_git_config(repo)
|
||||
assert 'proxy' not in (outer / 'config').read_text()
|
||||
assert 'proxy' not in (inner / 'config').read_text()
|
||||
|
||||
(inner / 'commondir').write_text(f'{tmp_path}\n')
|
||||
with pytest.raises(ValueError):
|
||||
lib.sanitize_git_config(repo)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue