fix: prevent GitStore from creating nested repos and overwriting .gitignore (#2980)
GitStore.init() now checks if the workspace is already inside a git repository before calling porcelain.init(). If so, it refuses to create a nested repo. Additionally, existing .gitignore files are preserved by appending only missing Dream-specific entries rather than overwriting. Closes #2980
This commit is contained in:
@@ -64,14 +64,35 @@ class GitStore:
|
||||
if self.is_initialized():
|
||||
return False
|
||||
|
||||
if self._is_inside_git_repo():
|
||||
logger.warning(
|
||||
"Workspace {} is already inside a git repo; "
|
||||
"skipping nested repo initialization",
|
||||
self._workspace,
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
from dulwich import porcelain
|
||||
|
||||
porcelain.init(str(self._workspace))
|
||||
|
||||
# Write .gitignore
|
||||
# Write .gitignore (merge with existing if present)
|
||||
gitignore = self._workspace / ".gitignore"
|
||||
gitignore.write_text(self._build_gitignore(), encoding="utf-8")
|
||||
dream_entries = self._build_gitignore()
|
||||
if gitignore.exists():
|
||||
existing = gitignore.read_text(encoding="utf-8")
|
||||
existing_lines = set(existing.splitlines())
|
||||
new_lines = [
|
||||
line
|
||||
for line in dream_entries.splitlines()
|
||||
if line not in existing_lines
|
||||
]
|
||||
if new_lines:
|
||||
merged = existing.rstrip("\n") + "\n" + "\n".join(new_lines) + "\n"
|
||||
gitignore.write_text(merged, encoding="utf-8")
|
||||
else:
|
||||
gitignore.write_text(dream_entries, encoding="utf-8")
|
||||
|
||||
# Ensure tracked files exist (touch them if missing) so the initial
|
||||
# commit has something to track.
|
||||
@@ -155,6 +176,19 @@ class GitStore:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _is_inside_git_repo(self) -> bool:
|
||||
"""Check if self._workspace is already inside a git repository.
|
||||
|
||||
Walks up from self._workspace to the filesystem root, returning True
|
||||
if any parent directory contains a .git directory.
|
||||
"""
|
||||
current = self._workspace.resolve()
|
||||
while current != current.parent:
|
||||
if (current / ".git").is_dir():
|
||||
return True
|
||||
current = current.parent
|
||||
return False
|
||||
|
||||
def _build_gitignore(self) -> str:
|
||||
"""Generate .gitignore content from tracked files."""
|
||||
dirs: set[str] = set()
|
||||
|
||||
Reference in New Issue
Block a user