Files
profilarr/backend/app/git/status/status.py
Sam Chau ca84a1c95b feature: merge conflict detection and resolution (#6)
- pulls now correctly identify merge conflicts and enter a merge state
- user resolves each file individually
- commit resolve merge state
- allows users to keep custom changes and pull in updates
- improve commit message component
- seperated commit / add functionality
2025-02-05 16:09:58 +10:30

111 lines
3.9 KiB
Python

# git/status/status.py
import git
from git.exc import GitCommandError, InvalidGitRepositoryError
import logging
from .incoming_changes import get_incoming_changes
from .outgoing_changes import get_outgoing_changes
from .merge_conflicts import get_merge_conflicts
from .utils import determine_type
import os
import yaml
logger = logging.getLogger(__name__)
def get_commits_ahead(repo, branch):
return list(repo.iter_commits(f'origin/{branch}..{branch}'))
def get_commits_behind(repo, branch):
return list(repo.iter_commits(f'{branch}..origin/{branch}'))
def get_unpushed_changes(repo, branch):
"""Get detailed info about files modified in unpushed commits"""
try:
# Get the file paths
unpushed_files = repo.git.diff(f'origin/{branch}..{branch}',
'--name-only').split('\n')
unpushed_files = [f for f in unpushed_files if f]
detailed_changes = []
for file_path in unpushed_files:
try:
# Get the current content of the file to extract name
with open(os.path.join(repo.working_dir, file_path), 'r') as f:
content = yaml.safe_load(f.read())
detailed_changes.append({
'type':
determine_type(file_path),
'name':
content.get('name', os.path.basename(file_path)),
'file_path':
file_path
})
except Exception as e:
logger.warning(
f"Could not get details for {file_path}: {str(e)}")
# Fallback to basic info if we can't read the file
detailed_changes.append({
'type': determine_type(file_path),
'name': os.path.basename(file_path),
'file_path': file_path
})
return detailed_changes
except Exception as e:
logger.error(f"Error getting unpushed changes: {str(e)}")
return []
def get_git_status(repo_path):
try:
logger.debug(f"Attempting to get status for repo at {repo_path}")
repo = git.Repo(repo_path)
branch = repo.active_branch.name
remote_branch_exists = f"origin/{branch}" in [
ref.name for ref in repo.remotes.origin.refs
]
# Check for merge state
is_merging = os.path.exists(os.path.join(repo.git_dir, 'MERGE_HEAD'))
# Get merge conflicts if we're in a merge state
merge_conflicts = get_merge_conflicts(repo) if is_merging else []
# Get all changes first
outgoing_changes = get_outgoing_changes(repo)
logger.debug(f"Outgoing changes detected: {outgoing_changes}")
if remote_branch_exists:
repo.remotes.origin.fetch()
commits_behind = get_commits_behind(repo, branch)
commits_ahead = get_commits_ahead(repo, branch)
incoming_changes = get_incoming_changes(repo, branch)
unpushed_files = get_unpushed_changes(
repo, branch) if commits_ahead else []
else:
commits_behind = []
commits_ahead = []
incoming_changes = []
unpushed_files = []
status = {
"branch": branch,
"remote_branch_exists": remote_branch_exists,
"outgoing_changes": outgoing_changes,
"commits_behind": len(commits_behind),
"commits_ahead": len(commits_ahead),
"incoming_changes": incoming_changes,
"has_unpushed_commits": len(commits_ahead) > 0,
"unpushed_files": unpushed_files,
"is_merging": is_merging,
"merge_conflicts": merge_conflicts,
"has_conflicts": bool(merge_conflicts)
}
return True, status
except Exception as e:
logger.error(f"Error in get_git_status: {str(e)}", exc_info=True)
return False, str(e)