From 3cc21c6951ae89b2b4d92f01aa32196421026d2d Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Sat, 7 Sep 2024 15:18:12 +0930 Subject: [PATCH] feat: new button to push local branches, no longer auto pushes new branches to remote --- backend/app/git/__init__.py | 15 ++ backend/app/git/branches/push.py | 27 ++++ frontend/src/api/api.js | 9 ++ .../settings/SettingsBranchModal.jsx | 130 +++++++++++------- 4 files changed, 129 insertions(+), 52 deletions(-) create mode 100644 backend/app/git/branches/push.py diff --git a/backend/app/git/__init__.py b/backend/app/git/__init__.py index 233624e..3f7b9dc 100644 --- a/backend/app/git/__init__.py +++ b/backend/app/git/__init__.py @@ -10,6 +10,7 @@ from ..settings_utils import save_settings import logging logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) bp = Blueprint('git', __name__, url_prefix='/git') @@ -109,6 +110,20 @@ def delete_branch(branch_name): else: logger.error(f"Failed to delete branch: {result}") return jsonify({'success': False, 'error': result}), 400 + +@bp.route('/branch/push', methods=['POST']) +def push_branch(): + data = request.json + logger.debug(f"Received request to push branch: {data}") + branch_name = data.get('branch') + if not branch_name: + return jsonify({"success": False, "error": "Branch name is required"}), 400 + + success, result = branch_manager.push(branch_name) + if success: + return jsonify({"success": True, "data": result}), 200 + else: + return jsonify({"success": False, "error": result["error"]}), 500 @bp.route('/push', methods=['POST']) def push_files(): diff --git a/backend/app/git/branches/push.py b/backend/app/git/branches/push.py new file mode 100644 index 0000000..8d925d0 --- /dev/null +++ b/backend/app/git/branches/push.py @@ -0,0 +1,27 @@ +import git +import logging + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + +def push_branch_to_remote(repo_path, branch_name): + try: + logger.debug(f"Attempting to push branch {branch_name} to remote") + repo = git.Repo(repo_path) + + # Check if the branch exists locally + if branch_name not in repo.heads: + return False, f"Branch '{branch_name}' does not exist locally." + + # Push the branch to remote and set the upstream branch + origin = repo.remote(name='origin') + origin.push(refspec=f"{branch_name}:{branch_name}", set_upstream=True) + + logger.debug(f"Successfully pushed branch to remote: {branch_name}") + return True, {"message": f"Pushed branch to remote: {branch_name}"} + except git.GitCommandError as e: + logger.error(f"Git command error pushing branch to remote: {str(e)}", exc_info=True) + return False, {"error": f"Error pushing branch to remote: {str(e)}"} + except Exception as e: + logger.error(f"Error pushing branch to remote: {str(e)}", exc_info=True) + return False, {"error": f"Error pushing branch to remote: {str(e)}"} diff --git a/frontend/src/api/api.js b/frontend/src/api/api.js index 2032baa..fae092e 100644 --- a/frontend/src/api/api.js +++ b/frontend/src/api/api.js @@ -297,3 +297,12 @@ export const unlinkRepo = async (removeFiles = false) => { } }; +export const pushBranchToRemote = async (branchName) => { + try { + const response = await axios.post(`${API_BASE_URL}/git/branch/push`, { branch: branchName }); + return response.data; + } catch (error) { + console.error('Error pushing branch to remote:', error); + throw error; + } +}; \ No newline at end of file diff --git a/frontend/src/components/settings/SettingsBranchModal.jsx b/frontend/src/components/settings/SettingsBranchModal.jsx index 36b6a94..51ab471 100644 --- a/frontend/src/components/settings/SettingsBranchModal.jsx +++ b/frontend/src/components/settings/SettingsBranchModal.jsx @@ -5,6 +5,7 @@ import { checkoutBranch, createBranch, deleteBranch, + pushBranchToRemote, } from "../../api/api"; import { ExternalLink, @@ -12,6 +13,7 @@ import { GitBranchPlus, ArrowRightCircle, Loader, + CloudUpload, } from "lucide-react"; import Tooltip from "../ui/Tooltip"; import Alert from "../ui/Alert"; @@ -58,33 +60,23 @@ const SettingsBranchModal = ({ setLoadingAction(""); }; - const handleCheckout = async (branchName, isNewBranch = false) => { + const handleCheckout = async (branchName) => { setLoadingAction(`checkout-${branchName}`); try { const response = await checkoutBranch(branchName); if (response.success) { - // Refresh branches after successful checkout await fetchBranches(); - // Notify parent component to update the status - onBranchChange(); // <-- Call the callback to update status in the parent component - if (!isNewBranch) { - Alert.success("Branch checked out successfully"); - } - onClose(); // Close the modal + onBranchChange(); + Alert.success("Branch checked out successfully"); + onClose(); } else { - Alert.error(response.error); // Use the Alert component for error + Alert.error(response.error); } } catch (error) { - if ( - error.response && - error.response.status === 400 && - error.response.data.error - ) { - Alert.error(error.response.data.error); // Show alert with specific backend error message + if (error.response && error.response.status === 400 && error.response.data.error) { + Alert.error(error.response.data.error); } else { - Alert.error( - "An unexpected error occurred while checking out the branch." - ); + Alert.error("An unexpected error occurred while checking out the branch."); console.error("Error checking out branch:", error); } } finally { @@ -98,24 +90,19 @@ const SettingsBranchModal = ({ try { const response = await createBranch(newBranchName, branchOffMode); if (response.success) { - // Checkout the new branch without showing the checkout alert - await handleCheckout(newBranchName, true); - Alert.success("Branch created and checked out successfully"); + await fetchBranches(); + onBranchChange(); + Alert.success("Branch created successfully"); + resetForm(); } else { - Alert.error(response.error); // Handle known errors from the backend + Alert.error(response.error); } } catch (error) { - if ( - error.response && - error.response.status === 400 && - error.response.data.error - ) { - Alert.error(error.response.data.error); // Specific error from the backend + if (error.response && error.response.status === 400 && error.response.data.error) { + Alert.error(error.response.data.error); } else { - console.error("Error branching off:", error); // Log unexpected errors - Alert.error( - "An unexpected error occurred while creating the branch. Please try again." - ); + console.error("Error branching off:", error); + Alert.error("An unexpected error occurred while creating the branch. Please try again."); } } finally { setLoadingAction(""); @@ -155,12 +142,12 @@ const SettingsBranchModal = ({ try { const response = await deleteBranch(branchToDelete); if (response.success) { - onBranchChange(); // <-- Call the callback to update status in the parent component - await fetchBranches(); // Refresh the list after deletion + onBranchChange(); + await fetchBranches(); Alert.success(`Branch '${branchToDelete}' deleted successfully`); setBranchToDelete(null); } else { - Alert.error(response.error); // Use the Alert component for error + Alert.error(response.error); } } catch (error) { Alert.error("An unexpected error occurred while deleting the branch."); @@ -170,6 +157,24 @@ const SettingsBranchModal = ({ } }; + const handlePushToRemote = async (branchName) => { + setLoadingAction(`push-${branchName}`); + try { + const response = await pushBranchToRemote(branchName); + if (response.success) { + Alert.success(`Branch '${branchName}' pushed to remote successfully`); + await fetchBranches(); + } else { + Alert.error(response.error); + } + } catch (error) { + Alert.error("An unexpected error occurred while pushing the branch to remote."); + console.error("Error pushing branch to remote:", error); + } finally { + setLoadingAction(""); + } + }; + return (
@@ -204,6 +209,13 @@ const SettingsBranchModal = ({ > {branch.name || "Unknown Branch"} + + {branch.isLocal && !branch.isRemote + ? "(Local)" + : !branch.isLocal && branch.isRemote + ? "(Remote)" + : "(Local & Remote)"} +
{branch.name !== currentBranch && ( @@ -234,22 +246,36 @@ const SettingsBranchModal = ({ )} - {branch.name !== currentBranch && - branch.name.toLowerCase() !== "main" && ( - - - - )} + {branch.isLocal && !branch.isRemote && ( + + + + )} + {branch.isLocal && branch.name !== currentBranch && branch.name.toLowerCase() !== "main" && ( + + + + )}