From ec7430466f47c5a488250e503ced136a34eb170d Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Tue, 3 Sep 2024 20:17:40 +0930 Subject: [PATCH] refactor: move git operations into seperate files --- backend/app/git/operations/commit.py | 16 +++ backend/app/git/operations/delete.py | 20 +++ backend/app/git/operations/operations.py | 34 +++++ backend/app/git/operations/pull.py | 15 ++ backend/app/git/operations/push.py | 18 +++ backend/app/git/operations/revert.py | 36 +++++ backend/app/git/operations/stage.py | 20 +++ backend/app/settings.py | 169 +++++++---------------- 8 files changed, 208 insertions(+), 120 deletions(-) create mode 100644 backend/app/git/operations/commit.py create mode 100644 backend/app/git/operations/delete.py create mode 100644 backend/app/git/operations/operations.py create mode 100644 backend/app/git/operations/pull.py create mode 100644 backend/app/git/operations/push.py create mode 100644 backend/app/git/operations/revert.py create mode 100644 backend/app/git/operations/stage.py diff --git a/backend/app/git/operations/commit.py b/backend/app/git/operations/commit.py new file mode 100644 index 0000000..a109d17 --- /dev/null +++ b/backend/app/git/operations/commit.py @@ -0,0 +1,16 @@ +# git/operations/commit.py + +import git +import logging + +logger = logging.getLogger(__name__) + +def commit_changes(repo_path, files, message): + try: + repo = git.Repo(repo_path) + repo.index.add(files) + repo.index.commit(message) + return True, "Successfully committed changes." + except Exception as e: + logger.error(f"Error committing changes: {str(e)}", exc_info=True) + return False, f"Error committing changes: {str(e)}" \ No newline at end of file diff --git a/backend/app/git/operations/delete.py b/backend/app/git/operations/delete.py new file mode 100644 index 0000000..2713a77 --- /dev/null +++ b/backend/app/git/operations/delete.py @@ -0,0 +1,20 @@ +# git/operations/delete.py + +import os +import logging + +logger = logging.getLogger(__name__) + +def delete_file(repo_path, file_path): + try: + full_file_path = os.path.join(repo_path, file_path) + + if os.path.exists(full_file_path): + os.remove(full_file_path) + message = f"File {file_path} has been deleted." + return True, message + else: + return False, "File does not exist." + except Exception as e: + logger.error(f"Error deleting file: {str(e)}", exc_info=True) + return False, f"Error deleting file: {str(e)}" \ No newline at end of file diff --git a/backend/app/git/operations/operations.py b/backend/app/git/operations/operations.py new file mode 100644 index 0000000..627e8da --- /dev/null +++ b/backend/app/git/operations/operations.py @@ -0,0 +1,34 @@ +# git/operations/operations.py + +import git +from .stage import stage_files +from .commit import commit_changes +from .push import push_changes +from .revert import revert_file, revert_all +from .delete import delete_file +from .pull import pull_branch + +class GitOperations: + def __init__(self, repo_path): + self.repo_path = repo_path + + def stage(self, files): + return stage_files(self.repo_path, files) + + def commit(self, files, message): + return commit_changes(self.repo_path, files, message) + + def push(self, files, message): + return push_changes(self.repo_path, files, message) + + def revert(self, file_path): + return revert_file(self.repo_path, file_path) + + def revert_all(self): + return revert_all(self.repo_path) + + def delete(self, file_path): + return delete_file(self.repo_path, file_path) + + def pull(self, branch_name): + return pull_branch(self.repo_path, branch_name) \ No newline at end of file diff --git a/backend/app/git/operations/pull.py b/backend/app/git/operations/pull.py new file mode 100644 index 0000000..51fad14 --- /dev/null +++ b/backend/app/git/operations/pull.py @@ -0,0 +1,15 @@ +# git/operations/pull.py + +import git +import logging + +logger = logging.getLogger(__name__) + +def pull_branch(repo_path, branch_name): + try: + repo = git.Repo(repo_path) + repo.git.pull('origin', branch_name) + return True, f"Successfully pulled changes for branch {branch_name}." + except Exception as e: + logger.error(f"Error pulling branch: {str(e)}", exc_info=True) + return False, f"Error pulling branch: {str(e)}" \ No newline at end of file diff --git a/backend/app/git/operations/push.py b/backend/app/git/operations/push.py new file mode 100644 index 0000000..d9d0455 --- /dev/null +++ b/backend/app/git/operations/push.py @@ -0,0 +1,18 @@ +# git/operations/push.py + +import git +import logging +from .commit import commit_changes + +logger = logging.getLogger(__name__) + +def push_changes(repo_path, files, message): + try: + repo = git.Repo(repo_path) + commit_changes(repo_path, files, message) + origin = repo.remote(name='origin') + origin.push() + return True, "Successfully pushed changes." + except Exception as e: + logger.error(f"Error pushing changes: {str(e)}", exc_info=True) + return False, f"Error pushing changes: {str(e)}" \ No newline at end of file diff --git a/backend/app/git/operations/revert.py b/backend/app/git/operations/revert.py new file mode 100644 index 0000000..42f79e2 --- /dev/null +++ b/backend/app/git/operations/revert.py @@ -0,0 +1,36 @@ +# git/operations/revert.py + +import git +import logging + +logger = logging.getLogger(__name__) + +def revert_file(repo_path, file_path): + try: + repo = git.Repo(repo_path) + staged_deletions = repo.index.diff("HEAD", R=True) + is_staged_for_deletion = any(d.a_path == file_path for d in staged_deletions) + + if is_staged_for_deletion: + repo.git.reset("--", file_path) + repo.git.checkout('HEAD', "--", file_path) + message = f"File {file_path} has been restored and unstaged from deletion." + else: + repo.git.restore("--", file_path) + repo.git.restore('--staged', "--", file_path) + message = f"File {file_path} has been reverted." + + return True, message + except Exception as e: + logger.error(f"Error reverting file: {str(e)}", exc_info=True) + return False, f"Error reverting file: {str(e)}" + +def revert_all(repo_path): + try: + repo = git.Repo(repo_path) + repo.git.restore('--staged', '.') + repo.git.restore('.') + return True, "All changes have been reverted to the last commit." + except Exception as e: + logger.error(f"Error reverting all changes: {str(e)}", exc_info=True) + return False, f"Error reverting all changes: {str(e)}" \ No newline at end of file diff --git a/backend/app/git/operations/stage.py b/backend/app/git/operations/stage.py new file mode 100644 index 0000000..72a59cc --- /dev/null +++ b/backend/app/git/operations/stage.py @@ -0,0 +1,20 @@ +# git/operations/stage.py + +import git +import logging + +logger = logging.getLogger(__name__) + +def stage_files(repo_path, files): + try: + repo = git.Repo(repo_path) + if not files: + repo.git.add(A=True) + message = "All changes have been staged." + else: + repo.index.add(files) + message = "Specified files have been staged." + return True, message + except Exception as e: + logger.error(f"Error staging files: {str(e)}", exc_info=True) + return False, f"Error staging files: {str(e)}" \ No newline at end of file diff --git a/backend/app/settings.py b/backend/app/settings.py index f375847..bbfac2b 100644 --- a/backend/app/settings.py +++ b/backend/app/settings.py @@ -15,6 +15,7 @@ from .git.authenticate import validate_git_token from .git.status.status import get_git_status from .git.status.diff import get_diff from .git.branches.branches import Branch_Manager +from .git.operations.operations import GitOperations from .settings_utils import load_settings, save_settings logging.basicConfig(level=logging.DEBUG) @@ -35,6 +36,7 @@ class SettingsManager: self.repo_url = self.settings.get('gitRepo') if self.settings else None self.repo_path = DB_DIR self.branch_manager = Branch_Manager(self.repo_path) + self.git_operations = GitOperations(self.repo_path) def clone_repository(self): return clone_repository(self.repo_url, self.repo_path, self.settings["gitToken"]) @@ -58,32 +60,22 @@ class SettingsManager: return self.branch_manager.get_current() def stage_files(self, files): - try: - repo = git.Repo(self.repo_path) - for file_path in files: - repo.index.add([file_path]) - return True, "Successfully staged files." - except Exception as e: - logger.error(f"Error staging files: {str(e)}", exc_info=True) - return False, f"Error staging files: {str(e)}" + return self.git_operations.stage(files) def push_files(self, files, commit_message): - try: - repo = git.Repo(self.repo_path) - # Stage the files - self.stage_files(files) + return self.git_operations.push(files, commit_message) - # Commit the staged files - repo.index.commit(commit_message) + def revert_file(self, file_path): + return self.git_operations.revert(file_path) - # Push the commit to the remote repository - origin = repo.remote(name='origin') - origin.push() + def revert_all(self): + return self.git_operations.revert_all() - return True, "Successfully committed and pushed files." - except Exception as e: - logger.error(f"Error pushing files: {str(e)}", exc_info=True) - return False, f"Error pushing files: {str(e)}" + def delete_file(self, file_path): + return self.git_operations.delete(file_path) + + def pull_branch(self, branch_name): + return self.git_operations.pull(branch_name) settings_manager = SettingsManager() @@ -198,27 +190,16 @@ def get_current_branch(): else: return jsonify({'success': False, 'error': 'Failed to get current branch'}), 400 + @bp.route('/stage', methods=['POST']) def stage_files(): files = request.json.get('files', []) - - try: - repo = git.Repo(settings_manager.repo_path) - - if not files: # If no files are specified, stage all changes - repo.git.add(A=True) # This adds all changes to staging, including deletions - message = "All changes have been staged." - else: - for file_path in files: - # Staging a deleted file requires just adding the file path. - repo.git.add(file_path) - message = "Specified files have been staged." - + success, message = settings_manager.stage_files(files) + if success: return jsonify({'success': True, 'message': message}), 200 - - except Exception as e: - logger.error(f"Error staging files: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error staging files: {str(e)}"}), 400 + else: + logger.error(f"Error staging files: {message}") + return jsonify({'success': False, 'error': message}), 400 def generate_commit_message(user_message, files): @@ -239,111 +220,59 @@ def push_files(): files = request.json.get('files', []) user_commit_message = request.json.get('commit_message', "Commit and push staged files") logger.debug(f"Received request to push files: {files}") - - try: - repo = git.Repo(settings_manager.repo_path) - - # Instead of restaging the files, we directly commit the staged changes - staged_files = repo.index.diff("HEAD") # Get the list of staged files - if not staged_files: - return jsonify({'success': False, 'error': "No staged changes to commit."}), 400 - - # Generate the structured commit message - commit_message = generate_commit_message(user_commit_message, files) - - # Commit the staged changes - repo.index.commit(commit_message) - - # Push the commit to the remote repository - origin = repo.remote(name='origin') - origin.push() - + commit_message = generate_commit_message(user_commit_message, files) + success, message = settings_manager.push_files(files, commit_message) + if success: logger.debug("Successfully committed and pushed files") - return jsonify({'success': True, 'message': "Successfully committed and pushed files."}), 200 - - except Exception as e: - logger.error(f"Error pushing files: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error pushing files: {str(e)}"}), 400 + return jsonify({'success': True, 'message': message}), 200 + else: + logger.error(f"Error pushing files: {message}") + return jsonify({'success': False, 'error': message}), 400 @bp.route('/revert', methods=['POST']) def revert_file(): file_path = request.json.get('file_path') - if not file_path: return jsonify({'success': False, 'error': "File path is required."}), 400 - - try: - repo = git.Repo(settings_manager.repo_path) - - # Check if the file is staged for deletion - staged_deletions = repo.index.diff("HEAD", R=True) - is_staged_for_deletion = any(d.a_path == file_path for d in staged_deletions) - - if is_staged_for_deletion: - # If the file is staged for deletion, we need to unstage it and restore it - repo.git.reset("--", file_path) # Unstage the deletion - repo.git.checkout('HEAD', "--", file_path) # Restore the file from HEAD - message = f"File {file_path} has been restored and unstaged from deletion." - else: - # For other changes, use the existing revert logic - repo.git.restore("--", file_path) - repo.git.restore('--staged', "--", file_path) - message = f"File {file_path} has been reverted." - + success, message = settings_manager.revert_file(file_path) + if success: return jsonify({'success': True, 'message': message}), 200 - - except Exception as e: - logger.error(f"Error reverting file: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error reverting file: {str(e)}"}), 400 + else: + logger.error(f"Error reverting file: {message}") + return jsonify({'success': False, 'error': message}), 400 @bp.route('/revert-all', methods=['POST']) def revert_all(): - try: - repo = git.Repo(settings_manager.repo_path) - - # Revert all files to the state of the last commit - repo.git.restore('--staged', '.') - repo.git.restore('.') - - return jsonify({'success': True, 'message': "All changes have been reverted to the last commit."}), 200 - - except Exception as e: - logger.error(f"Error reverting all changes: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error reverting all changes: {str(e)}"}), 400 + success, message = settings_manager.revert_all() + if success: + return jsonify({'success': True, 'message': message}), 200 + else: + logger.error(f"Error reverting all changes: {message}") + return jsonify({'success': False, 'error': message}), 400 @bp.route('/file', methods=['DELETE']) def delete_file(): file_path = request.json.get('file_path') - if not file_path: return jsonify({'success': False, 'error': "File path is required."}), 400 - - try: - full_file_path = os.path.join(settings_manager.repo_path, file_path) - - if os.path.exists(full_file_path): - os.remove(full_file_path) - message = f"File {file_path} has been deleted." - return jsonify({'success': True, 'message': message}), 200 - else: - return jsonify({'success': False, 'error': "File does not exist."}), 404 - - except Exception as e: - logger.error(f"Error deleting file: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error deleting file: {str(e)}"}), 400 + success, message = settings_manager.delete_file(file_path) + if success: + return jsonify({'success': True, 'message': message}), 200 + else: + logger.error(f"Error deleting file: {message}") + return jsonify({'success': False, 'error': message}), 400 @bp.route('/pull', methods=['POST']) def pull_branch(): branch_name = request.json.get('branch') - try: - repo = git.Repo(settings_manager.repo_path) - repo.git.pull('origin', branch_name) - return jsonify({'success': True, 'message': f'Successfully pulled changes for branch {branch_name}.'}), 200 - except Exception as e: - logger.error(f"Error pulling branch: {str(e)}", exc_info=True) - return jsonify({'success': False, 'error': f"Error pulling branch: {str(e)}"}), 400 + success, message = settings_manager.pull_branch(branch_name) + if success: + return jsonify({'success': True, 'message': message}), 200 + else: + logger.error(f"Error pulling branch: {message}") + return jsonify({'success': False, 'error': message}), 400 @bp.route('/diff', methods=['POST']) def diff_file():