andea_revanced-patches/xml_tools/utils/git.py

126 lines
3.8 KiB
Python

"""Git commands."""
import logging
from pathlib import Path
import git
logger = logging.getLogger("xml_tools")
class GitClient:
"""Handler for Git operations on a repository using GitPython."""
def __init__(self, repo_path: Path) -> None:
"""Initialize GitClient with repository path.
Args:
repo_path: Path to the Git repository.
Raises:
ValueError: If the provided path is not a valid Git repository.
"""
self.repo_path = repo_path
try:
self.repo = git.Repo(str(repo_path), search_parent_directories=True)
except git.InvalidGitRepositoryError as e:
msg = f"Invalid Git repository at path: {repo_path}"
raise ValueError(msg) from e
@staticmethod
def _handle_git_error(operation: str, exception: Exception) -> tuple[int, str, str]:
"""Handle GitPython exceptions.
Args:
operation: The name of the Git operation that failed.
exception: The exception that was raised by GitPython.
Returns:
A tuple containing:
- an error code (1),
- an empty output string,
- the exception message as error string.
"""
logger.error("Git %s failed: %s", operation, exception)
return 1, "", str(exception)
def switch_branch(self, branch_name: str) -> tuple[int, str, str]:
"""Switches to the specified branch.
Args:
branch_name: The name of the branch to switch to.
Returns:
A tuple containing:
- Return code (0 for success, 1 for failure)
- Command output (success message or empty)
- Error output (empty string on success, error message on failure)
"""
try:
self.repo.git.checkout(branch_name)
except git.GitCommandError as e:
return self._handle_git_error(f"checkout {branch_name}", e)
else:
return 0, f"Switched to branch {branch_name}", ""
def fetch(self) -> tuple[int, str, str]:
"""Fetch updates from remote.
Returns:
A tuple containing:
- Return code (0 for success, 1 for failure)
- Command output (success message or empty)
- Error output (empty string on success, error message on failure)
"""
try:
self.repo.remotes.origin.fetch()
except git.GitCommandError as e:
return self._handle_git_error("fetch", e)
else:
return 0, "Fetch successful", ""
def pull(self) -> tuple[int, str, str]:
"""Pull changes from remote.
Returns:
A tuple containing:
- Return code (0 for success, 1 for failure)
- Command output (success message or empty)
- Error output (empty string on success, error message on failure)
"""
try:
self.repo.remotes.origin.pull()
except git.GitCommandError as e:
return self._handle_git_error("pull", e)
else:
return 0, "Pull successful", ""
def sync_repository(self) -> bool:
"""Synchronize the repository with its remote.
This method performs three operations in sequence:
1. Switches to the 'dev' branch
2. Fetches updates from remote
3. Pulls changes
Returns:
True if all operations succeeded, False otherwise.
"""
operations = [
(self.switch_branch, "dev"),
(self.fetch,),
(self.pull,),
]
for operation, *args in operations:
code, _, _ = operation(*args)
if code != 0:
return False
logger.debug("Git %s successful", operation.__name__)
return True