mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 02:41:11 +01:00
- 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
116 lines
3.9 KiB
Python
116 lines
3.9 KiB
Python
import os
|
|
import yaml
|
|
import logging
|
|
from git import GitCommandError
|
|
from .utils import determine_type
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Define the possible states
|
|
UNRESOLVED = "UNRESOLVED" # File is still in conflict, hasn't been resolved and not added
|
|
RESOLVED = "RESOLVED" # File is no longer in conflict, been resolved and has been added
|
|
|
|
|
|
def get_merge_conflicts(repo):
|
|
"""Get all merge conflicts in the repository."""
|
|
try:
|
|
if not os.path.exists(os.path.join(repo.git_dir, 'MERGE_HEAD')):
|
|
logger.debug("No MERGE_HEAD found - not in merge state")
|
|
return []
|
|
|
|
conflicts = []
|
|
status = repo.git.status('--porcelain', '-z').split('\0')
|
|
|
|
logger.debug(f"Raw status output: {[s for s in status if s]}")
|
|
|
|
for item in status:
|
|
if not item or len(item) < 4:
|
|
continue
|
|
|
|
x, y, file_path = item[0], item[1], item[3:]
|
|
logger.debug(
|
|
f"Processing status item - X: {x}, Y: {y}, Path: {file_path}")
|
|
|
|
if 'U' in (x, y) or (x == 'D' and y == 'D'):
|
|
conflict = process_conflict_file(repo, file_path)
|
|
if conflict:
|
|
conflicts.append(conflict)
|
|
|
|
logger.debug(f"Found {len(conflicts)} conflicts")
|
|
return conflicts
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting merge conflicts: {str(e)}", exc_info=True)
|
|
return []
|
|
|
|
|
|
def process_conflict_file(repo, file_path):
|
|
"""Process a single conflict file and return its conflict information."""
|
|
try:
|
|
logger.debug(f"Processing conflict file: {file_path}")
|
|
|
|
# Get current and incoming versions
|
|
ours_data = get_version_data(repo, 'HEAD', file_path)
|
|
theirs_data = get_version_data(repo, 'MERGE_HEAD', file_path)
|
|
|
|
if not ours_data or not theirs_data:
|
|
logger.warning(
|
|
f"Missing data for {file_path} - Ours: {bool(ours_data)}, Theirs: {bool(theirs_data)}"
|
|
)
|
|
return None
|
|
|
|
conflict_details = {'conflicting_parameters': []}
|
|
|
|
# Find conflicting fields
|
|
for key in set(ours_data.keys()) | set(theirs_data.keys()):
|
|
if key == 'date_modified':
|
|
continue
|
|
|
|
ours_value = ours_data.get(key)
|
|
theirs_value = theirs_data.get(key)
|
|
|
|
if ours_value != theirs_value:
|
|
logger.debug(
|
|
f"Found conflict in {key} - Local: {ours_value}, Incoming: {theirs_value}"
|
|
)
|
|
conflict_details['conflicting_parameters'].append({
|
|
'parameter':
|
|
key,
|
|
'local_value':
|
|
ours_value,
|
|
'incoming_value':
|
|
theirs_value
|
|
})
|
|
|
|
# Check if file still has unmerged (UU) status
|
|
status_output = repo.git.status('--porcelain', file_path)
|
|
logger.debug(f"Status output for {file_path}: {status_output}")
|
|
status = UNRESOLVED if status_output.startswith('UU') else RESOLVED
|
|
|
|
result = {
|
|
'file_path': file_path,
|
|
'type': determine_type(file_path),
|
|
'name': ours_data.get('name'),
|
|
'status': status,
|
|
'conflict_details': conflict_details
|
|
}
|
|
|
|
logger.debug(f"Processed conflict result: {result}")
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error processing conflict file {file_path}: {str(e)}",
|
|
exc_info=True)
|
|
return None
|
|
|
|
|
|
def get_version_data(repo, ref, file_path):
|
|
"""Get YAML data from a specific version of a file."""
|
|
try:
|
|
content = repo.git.show(f'{ref}:{file_path}')
|
|
return yaml.safe_load(content) if content else None
|
|
except GitCommandError as e:
|
|
logger.error(
|
|
f"Error getting version data for {ref}:{file_path}: {str(e)}")
|
|
return None
|