mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-28 13:30:56 +01:00
feature: Commit Log Viewer (#7)
* style: slightly decrease font / button size for repo container * feat: view commit modal to view local commit details * fix: allow staging and comitting deleted files * feat: handle modify-delete edge case - local side deleted, remote modified - let user pick between restore, keep deleted - special handling for editing * feat: handle empty state for commits modal
This commit is contained in:
@@ -1,16 +1,125 @@
|
||||
# git/operations/commit.py
|
||||
import git
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_git_status(status_output):
|
||||
"""
|
||||
Parse git status --porcelain output into a structured format.
|
||||
|
||||
Returns dict with staged and unstaged changes, identifying status of each file.
|
||||
"""
|
||||
changes = {}
|
||||
for line in status_output:
|
||||
if not line:
|
||||
continue
|
||||
|
||||
index_status = line[0] # First character: staged status
|
||||
worktree_status = line[1] # Second character: unstaged status
|
||||
file_path = line[3:]
|
||||
|
||||
changes[file_path] = {
|
||||
'staged': index_status != ' ',
|
||||
'staged_status': index_status,
|
||||
'unstaged_status': worktree_status
|
||||
}
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def commit_changes(repo_path, files, message):
|
||||
"""
|
||||
Commit changes to git repository, optimizing staging operations.
|
||||
Only re-stages files if their current staging status is incorrect.
|
||||
|
||||
Args:
|
||||
repo_path: Path to git repository
|
||||
files: List of files to commit, or None/empty for all staged changes
|
||||
message: Commit message
|
||||
|
||||
Returns:
|
||||
tuple: (success: bool, message: str)
|
||||
"""
|
||||
try:
|
||||
repo = git.Repo(repo_path)
|
||||
repo.index.add(files)
|
||||
repo.index.commit(message)
|
||||
return True, "Successfully committed changes."
|
||||
|
||||
# If no specific files provided, commit all staged changes
|
||||
if not files:
|
||||
commit = repo.index.commit(message)
|
||||
return True, "Successfully committed all staged changes."
|
||||
|
||||
# Get current status of the repository
|
||||
status_output = repo.git.status('--porcelain').splitlines()
|
||||
status = parse_git_status(status_output)
|
||||
|
||||
# Track files that need staging operations
|
||||
to_add = []
|
||||
to_remove = []
|
||||
already_staged = []
|
||||
|
||||
for file_path in files:
|
||||
if file_path in status:
|
||||
file_status = status[file_path]
|
||||
|
||||
# File is already properly staged
|
||||
if file_status['staged']:
|
||||
if file_status['staged_status'] == 'D':
|
||||
already_staged.append(('deleted', file_path))
|
||||
else:
|
||||
already_staged.append(('modified', file_path))
|
||||
continue
|
||||
|
||||
# File needs to be staged
|
||||
if file_status['unstaged_status'] == 'D':
|
||||
to_remove.append(file_path)
|
||||
else:
|
||||
to_add.append(file_path)
|
||||
else:
|
||||
logger.warning(f"File not found in git status: {file_path}")
|
||||
|
||||
# Perform necessary staging operations
|
||||
if to_add:
|
||||
logger.debug(f"Staging modified files: {to_add}")
|
||||
repo.index.add(to_add)
|
||||
|
||||
if to_remove:
|
||||
logger.debug(f"Staging deleted files: {to_remove}")
|
||||
repo.index.remove(to_remove, working_tree=True)
|
||||
|
||||
# Commit the changes
|
||||
commit = repo.index.commit(message)
|
||||
|
||||
# Build detailed success message
|
||||
staged_counts = {
|
||||
'added/modified': len(to_add),
|
||||
'deleted': len(to_remove),
|
||||
'already_staged': len(already_staged)
|
||||
}
|
||||
|
||||
message_parts = []
|
||||
if staged_counts['added/modified']:
|
||||
message_parts.append(
|
||||
f"{staged_counts['added/modified']} files staged")
|
||||
if staged_counts['deleted']:
|
||||
message_parts.append(
|
||||
f"{staged_counts['deleted']} deletions staged")
|
||||
if staged_counts['already_staged']:
|
||||
message_parts.append(
|
||||
f"{staged_counts['already_staged']} files already staged")
|
||||
|
||||
if message_parts:
|
||||
details = " and ".join(message_parts)
|
||||
return True, f"Successfully committed changes ({details})"
|
||||
else:
|
||||
return True, "Successfully committed changes (no files needed staging)"
|
||||
|
||||
except git.exc.GitCommandError as e:
|
||||
logger.error(f"Git command error committing changes: {str(e)}",
|
||||
exc_info=True)
|
||||
return False, f"Error committing changes: {str(e)}"
|
||||
except Exception as e:
|
||||
logger.error(f"Error committing changes: {str(e)}", exc_info=True)
|
||||
return False, f"Error committing changes: {str(e)}"
|
||||
|
||||
Reference in New Issue
Block a user