""" Extract Patch - Extract patch for a single chromium file. """ from typing import Tuple, Optional from ...common.context import Context from ...common.utils import log_info, log_warning from .utils import ( run_git_command, parse_diff_output, write_patch_file, create_deletion_marker, validate_commit_exists, FileOperation, GitError, ) def extract_single_file_patch( build_ctx: Context, chromium_path: str, base: str, force: bool = False, ) -> Tuple[bool, Optional[str]]: """Extract patch for a single chromium file. Extracts the diff from base commit to current working directory (including unstaged changes) for the specified file. Args: build_ctx: Build context chromium_path: Path to file in chromium (e.g., chrome/common/foo.h) base: Base commit to diff against force: If True, overwrite existing patch without prompting Returns: Tuple of (success: bool, error_message: Optional[str]) """ if not validate_commit_exists(base, build_ctx.chromium_src): return False, f"Base commit not found: {base}" log_info(f"Extracting patch for: {chromium_path}") log_info(f" Base: {base[:12]}") # Get diff from base to working directory for this file diff_cmd = ["git", "diff", base, "--", chromium_path] result = run_git_command(diff_cmd, cwd=build_ctx.chromium_src) if result.returncode != 0: return False, f"Failed to get diff: {result.stderr}" if not result.stdout.strip(): # No diff - check if file exists in base vs working directory base_exists = ( run_git_command( ["git", "cat-file", "-e", f"{base}:{chromium_path}"], cwd=build_ctx.chromium_src, ).returncode == 0 ) working_file = build_ctx.chromium_src / chromium_path working_exists = working_file.exists() if not base_exists and not working_exists: return False, f"File does not exist in base or working directory: {chromium_path}" if base_exists and working_exists: return False, f"No changes found for: {chromium_path}" if not base_exists and working_exists: # New file - get full content as diff diff_cmd = ["git", "diff", "--no-index", "/dev/null", chromium_path] result = run_git_command(diff_cmd, cwd=build_ctx.chromium_src) # --no-index returns 1 when files differ, which is expected if not result.stdout.strip(): return False, f"Failed to generate diff for new file: {chromium_path}" # Parse the diff file_patches = parse_diff_output(result.stdout) if not file_patches: return False, f"Failed to parse diff for: {chromium_path}" if chromium_path not in file_patches: # The file might be in the patches under a different key if len(file_patches) == 1: patch = list(file_patches.values())[0] else: return False, f"Unexpected diff output for: {chromium_path}" else: patch = file_patches[chromium_path] # Check for existing patch patch_path = build_ctx.get_patch_path_for_file(chromium_path) if patch_path.exists() and not force: import click if not click.confirm(f"Patch already exists: {chromium_path}. Overwrite?", default=False): log_info("Extraction cancelled") return False, "Cancelled by user" # Handle different operations if patch.operation == FileOperation.DELETE: if create_deletion_marker(build_ctx, chromium_path): return True, None return False, f"Failed to create deletion marker for: {chromium_path}" if patch.is_binary: return False, f"Binary files not supported: {chromium_path}" if not patch.patch_content: return False, f"No patch content for: {chromium_path}" # Write the patch if write_patch_file(build_ctx, chromium_path, patch.patch_content): return True, None return False, f"Failed to write patch for: {chromium_path}"