# =================== AIPass ==================== # Name: commit_handler.py # Description: Commit handler with scoped staging # Version: 1.0.0 # Created: 2026-05-12 # Modified: 2026-05-12 # ============================================= """Commit handler with scoped staging.""" from __future__ import annotations import subprocess from pathlib import Path from aipass.prax import logger from aipass.drone.apps.handlers.json import json_handler from aipass.drone.apps.handlers.git.lock_handler import find_repo_root def stage_branch_dir(branch_dir: Path, repo_root: Path | None = None) -> dict: """Stage all changes under branch_dir. Shared utility used by both commit_handler and pr_handler. """ if repo_root is None: repo_root = find_repo_root() try: rel_dir = branch_dir.resolve().relative_to(repo_root.resolve()) except ValueError: logger.warning( "stage_branch_dir: branch_dir %s not relative to repo root %s", branch_dir, repo_root, ) rel_dir = branch_dir add_result = subprocess.run( ["git", "add", str(rel_dir) + "/"], capture_output=True, text=True, cwd=str(repo_root), ) if add_result.returncode != 0: return { "success": False, "rel_dir": rel_dir, "message": f"Failed to stage files: {add_result.stderr.strip()}", } return {"success": True, "rel_dir": rel_dir, "message": f"Staged files under {rel_dir}"} def commit_changes( message: str, branch_dir: Path | None = None, all_files: bool = False, ) -> dict: """Commit staged changes or all changes under branch_dir.""" repo_root = find_repo_root() if all_files and branch_dir: stage_result = stage_branch_dir(branch_dir, repo_root) if not stage_result["success"]: return {"stdout": "", "stderr": stage_result["message"], "exit_code": 1} diff_check = subprocess.run( ["git", "diff", "--cached", "--quiet"], capture_output=True, text=True, cwd=str(repo_root), ) if diff_check.returncode == 0: return { "stdout": "", "stderr": "Nothing to commit: no changes staged", "exit_code": 1, } try: cmd = ["git", "commit", "-m", message] if branch_dir: try: rel_dir = branch_dir.resolve().relative_to(repo_root.resolve()) except ValueError as exc: logger.warning("commit_changes: branch_dir not relative to repo root: %s", exc) rel_dir = branch_dir cmd.extend(["--", str(rel_dir) + "/"]) result = subprocess.run( cmd, capture_output=True, text=True, cwd=str(repo_root), ) except (OSError, subprocess.SubprocessError) as exc: logger.error("git commit failed: %s", exc) return {"stdout": "", "stderr": f"git commit failed: {exc}", "exit_code": 1} json_handler.log_operation( "commit_changes", {"message": message, "all_files": all_files, "exit_code": result.returncode}, ) return { "stdout": result.stdout.strip(), "stderr": result.stderr.strip(), "exit_code": result.returncode, }