fix(git): filter gitignored files before staging
- Add git_check_ignored() to detect ignored files using `git check-ignore --stdin` - Update git_add_specific() to filter out ignored files before `git add` - Add stdin support to _run_git_command() for efficient batch operations - Prevents "paths are ignored" errors when staging __pycache__, .pyc files - Fixes "All commit groups failed" errors in @ml/auto-commit-service and @egirl/egirl-platform Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a6a7e96889
commit
7349aa2bea
5 changed files with 61 additions and 3 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -34,12 +34,19 @@ async def _run_git_command(
|
|||
*args: str,
|
||||
cwd: Path,
|
||||
check: bool = True,
|
||||
stdin: bytes | None = None,
|
||||
) -> tuple[str, str, int]:
|
||||
"""Run a git command asynchronously.
|
||||
|
||||
Uses asyncio subprocess with argument list (safe, no shell injection).
|
||||
This is equivalent to Node.js execFile - arguments are passed directly
|
||||
to the process without shell interpretation.
|
||||
|
||||
Args:
|
||||
*args: Git command arguments
|
||||
cwd: Working directory for the command
|
||||
check: Raise GitError on non-zero exit code
|
||||
stdin: Optional stdin data to send to the process
|
||||
"""
|
||||
# asyncio.create_subprocess_exec is safe - no shell, args passed directly
|
||||
create_process = asyncio.create_subprocess_exec
|
||||
|
|
@ -49,8 +56,9 @@ async def _run_git_command(
|
|||
cwd=str(cwd),
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE if stdin is not None else None,
|
||||
)
|
||||
stdout, stderr = await proc.communicate()
|
||||
stdout, stderr = await proc.communicate(input=stdin)
|
||||
stdout_str = stdout.decode().strip()
|
||||
stderr_str = stderr.decode().strip()
|
||||
returncode = proc.returncode or 0
|
||||
|
|
@ -157,8 +165,46 @@ async def git_add_all(repo_path: Path) -> None:
|
|||
await _run_git_command("add", "-A", cwd=repo_path)
|
||||
|
||||
|
||||
async def git_check_ignored(repo_path: Path, files: list[str]) -> list[str]:
|
||||
"""Check which files are ignored by .gitignore.
|
||||
|
||||
Args:
|
||||
repo_path: Path to the repository
|
||||
files: List of file paths to check
|
||||
|
||||
Returns:
|
||||
List of files that are NOT ignored (safe to stage)
|
||||
"""
|
||||
if not files:
|
||||
return []
|
||||
|
||||
try:
|
||||
# Use git check-ignore to filter out ignored files
|
||||
# --stdin allows us to check multiple files efficiently
|
||||
# -v flag would show matches, but we want non-matches
|
||||
# Exit code 0 = files are ignored, 1 = files are NOT ignored
|
||||
file_list = "\n".join(files)
|
||||
stdout, stderr, returncode = await _run_git_command(
|
||||
"check-ignore", "--stdin",
|
||||
cwd=repo_path,
|
||||
check=False,
|
||||
stdin=file_list.encode()
|
||||
)
|
||||
|
||||
# Files in stdout are ignored - we want to exclude these
|
||||
ignored_files = set(stdout.strip().split("\n")) if stdout.strip() else set()
|
||||
|
||||
# Return only non-ignored files
|
||||
return [f for f in files if f not in ignored_files]
|
||||
|
||||
except GitError:
|
||||
# If check-ignore fails, return all files (safer than blocking commits)
|
||||
logger.warning(f"git check-ignore failed in {repo_path}, proceeding without filter")
|
||||
return files
|
||||
|
||||
|
||||
async def git_add_specific(repo_path: Path, files: list[str]) -> None:
|
||||
"""Stage specific files only.
|
||||
"""Stage specific files only, filtering out gitignored files.
|
||||
|
||||
Args:
|
||||
repo_path: Path to the repository
|
||||
|
|
@ -166,8 +212,20 @@ async def git_add_specific(repo_path: Path, files: list[str]) -> None:
|
|||
"""
|
||||
if not files:
|
||||
return
|
||||
|
||||
# Filter out gitignored files before staging
|
||||
stageable_files = await git_check_ignored(repo_path, files)
|
||||
|
||||
if not stageable_files:
|
||||
logger.warning(f"All {len(files)} files are gitignored, nothing to stage")
|
||||
return
|
||||
|
||||
if len(stageable_files) < len(files):
|
||||
ignored_count = len(files) - len(stageable_files)
|
||||
logger.debug(f"Filtered out {ignored_count} gitignored files, staging {len(stageable_files)}")
|
||||
|
||||
# Add files in a single command for efficiency
|
||||
await _run_git_command("add", "--", *files, cwd=repo_path)
|
||||
await _run_git_command("add", "--", *stageable_files, cwd=repo_path)
|
||||
|
||||
|
||||
async def git_commit(repo_path: Path, message: str) -> CommitResult:
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Reference in a new issue