From bf97b2cbe0c8cea4da9bb6fc5ba1e90ff2c016af Mon Sep 17 00:00:00 2001 From: daiqingshuang Date: Thu, 22 May 2025 18:36:55 +0800 Subject: [PATCH] =?UTF-8?q?TODO=20=E5=BC=82=E6=AD=A5=E5=92=8C=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- src/branch_selector.cpp | 23 +++++++++---- src/branch_selector.h | 14 +++++++- src/git_repository.cpp | 66 ++++++++++++++++-------------------- src/git_repository.h | 13 ++++---- src/main.cpp | 5 +++ src/main_frame.cpp | 74 +++++++++++++++++++---------------------- src/main_frame.h | 1 - src/task_manager.cpp | 1 + src/task_manager.h | 49 +++++++++++++++++++++++++++ 10 files changed, 155 insertions(+), 93 deletions(-) create mode 100644 src/task_manager.cpp create mode 100644 src/task_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 379ea27..579e6cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16...3.31) project(branch_switcher) include(cmake/project_cpp_standard.cmake) -set_cpp_standard(20) +set_cpp_standard(23) # 如果需要,手动指定 wxWidgets 路径 # set(wxWidgets_ROOT_DIR "D:/Projects/vcpkg/installed/x64-mingw-dynamic/lib") diff --git a/src/branch_selector.cpp b/src/branch_selector.cpp index edc136c..e79db27 100644 --- a/src/branch_selector.cpp +++ b/src/branch_selector.cpp @@ -1,5 +1,6 @@ #include "branch_selector.h" #include "config_manager.h" +#include "task_manager.h" wxBEGIN_EVENT_TABLE(BranchSelector, wxPanel) EVT_COMBOBOX(wxID_ANY, BranchSelector::OnBranchSelected) @@ -13,19 +14,24 @@ BranchSelector::BranchSelector(wxWindow* parent, std::shared_ptrGetName()) : label; - wxStaticText* label_ctrl = new wxStaticText(this, wxID_ANY, display_label); + label_ctrl = new wxStaticText(this, wxID_ANY, display_label); // 创建下拉列表 branch_combo_ = new wxComboBox(this, wxID_ANY); // 填充分支列表 const auto& branches = repo->GetAllBranches(); - for (const auto& branch : branches) { + for (const auto& branch : branches.value()) { branch_combo_->Append(branch); } + auto result = repo->GetCurrentBranchName(); // 选择当前分支 - branch_combo_->SetStringSelection(repo->GetCurrentBranchName()); + if (!result.has_value()) { + ProcessResult(result); + return; + } + branch_combo_->SetStringSelection(result.value()); // 布局 hbox->Add(label_ctrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); @@ -77,13 +83,18 @@ bool BranchSelector::ApplyBranchChange() { } // 切换分支 - bool success = repo_->SwitchBranch(selected_branch); - if (!success) { + auto result = repo_->SwitchBranch(selected_branch); + if (!result.has_value()) { return false; } // 拉取更新 - return repo_->Pull(); + TaskManager::Get().PushGitTask([this]{ return repo_->Pull(); }, + [this](auto in_result){ + ProcessResult(in_result); + }); + repo_->Pull(); + return true; } void BranchSelector::OnBranchSelected(wxCommandEvent& event) { diff --git a/src/branch_selector.h b/src/branch_selector.h index d9cbfd3..ba995c1 100644 --- a/src/branch_selector.h +++ b/src/branch_selector.h @@ -13,13 +13,25 @@ public: std::string GetSelectedBranchName() const; bool ApplyBranchChange(); - bool HardReset() const { return repo_->HardReset(); } + bool HardReset() const { return repo_->HardReset().has_value(); } std::shared_ptr GetRepo() const { return repo_; } + + template + void ProcessResult(const std::expected& in_expected) { + if (!in_expected.has_value()) { + label_ctrl->SetForegroundColour(*wxRED); + label_ctrl->SetToolTip(in_expected.error()); + } else { + label_ctrl->SetForegroundColour(*wxBLACK); + label_ctrl->SetToolTip(wxEmptyString); + } + } private: void OnBranchSelected(wxCommandEvent& event); wxComboBox* branch_combo_ = nullptr; + wxStaticText* label_ctrl = nullptr; std::shared_ptr repo_; wxString current_group_; diff --git a/src/git_repository.cpp b/src/git_repository.cpp index 6045caf..92bdf59 100644 --- a/src/git_repository.cpp +++ b/src/git_repository.cpp @@ -4,7 +4,7 @@ #include "git_command.h" -std::string ExecGitCommand(const std::string& cmd, const std::filesystem::path& path) { +std::expected ExecGitCommand(const std::string& cmd, const std::filesystem::path& path) { std::string fullCmd = "git " + cmd; std::string result; @@ -19,7 +19,7 @@ std::string ExecGitCommand(const std::string& cmd, const std::filesystem::path& #endif if (!pipe) { - return "执行命令失败"; + return std::unexpected("执行命令失败"); } char buffer[128]; @@ -34,6 +34,10 @@ std::string ExecGitCommand(const std::string& cmd, const std::filesystem::path& #endif _chdir(currentPath.string().c_str()); + if (result != "Already up to date.\n") + return std::unexpected(result); + if (result.find("error") != std::string::npos) + return std::unexpected(result); return result; } @@ -42,40 +46,27 @@ git::Repository::Repository(const std::filesystem::path& in_path) { name_ = path_.filename().string(); } -bool git::Repository::SwitchBranch(const std::string& in_branch) { - std::string cmd = "checkout " + in_branch; - auto result = ExecGitCommand(cmd, path_); - if (result.find("error") != std::string::npos) { - return false; - } - return true; +std::expected git::Repository::SwitchBranch(const std::string& in_branch) { + const std::string cmd = "checkout " + in_branch; + return ExecGitCommand(cmd, path_); } -bool git::Repository::HardReset() { - auto result = ExecGitCommand("reset --hard", path_); - return result.find("error") == std::string::npos; +std::expected git::Repository::HardReset() { + return ExecGitCommand("reset --hard", path_); } -bool git::Repository::Pull() { - std::string cmd = "pull"; - auto result = ExecGitCommand(cmd, path_); - if (result != "Already up to date.\n") - return false; - if (result.find("error") != std::string::npos) { - return false; - } - return true; +std::expected git::Repository::Pull() { + return ExecGitCommand("pull", path_); } -std::vector> git::Repository::GetSubmodules() const { +std::expected>, std::string> git::Repository::GetSubmodules() const { std::vector> submodules; std::string cmd = "submodule"; auto result = ExecGitCommand(cmd, path_); - if (result.find("error") != std::string::npos) { - return submodules; - } + if (!result.has_value()) + return std::unexpected(result.error()); - std::istringstream iss(result); + std::istringstream iss(result.value()); std::string line; while (std::getline(iss, line)) { // 截取第一个空格到第二个空格之间的内容 @@ -91,25 +82,24 @@ std::vector> git::Repository::GetSubmodules() c return submodules; } -std::string git::Repository::GetCurrentBranchName() const { - std::string cmd = "rev-parse --abbrev-ref HEAD"; +std::expected git::Repository::GetCurrentBranchName() const { + const std::string cmd = "rev-parse --abbrev-ref HEAD"; auto result = ExecGitCommand(cmd, path_); - if (result.find("error") != std::string::npos) { - return ""; - } - result.replace(result.find('\n'), 1, ""); // Remove trailing newline + if (!result.has_value()) + return result; + auto& str = result.value(); + str.replace(str.find('\n'), 1, ""); // Remove trailing newline return result; } -std::vector git::Repository::GetAllBranches() const { +std::expected, std::string> git::Repository::GetAllBranches() const { std::vector branches; - std::string cmd = "branch"; + const std::string cmd = "branch"; auto result = ExecGitCommand(cmd, path_); - if (result.find("error") != std::string::npos) { - return branches; - } + if (!result.has_value()) + return std::unexpected(result.error()); - std::istringstream iss(result); + std::istringstream iss(result.value()); std::string line; while (std::getline(iss, line)) { if (!line.empty()) { diff --git a/src/git_repository.h b/src/git_repository.h index 257d751..5a3cb68 100644 --- a/src/git_repository.h +++ b/src/git_repository.h @@ -3,19 +3,20 @@ #include #include #include +#include namespace git { class Repository { public: explicit Repository(const std::filesystem::path& in_path); - bool SwitchBranch(const std::string& in_branch); - bool HardReset(); - bool Pull(); + std::expected SwitchBranch(const std::string& in_branch); + std::expected HardReset(); + std::expected Pull(); - [[nodiscard]] std::vector> GetSubmodules() const; - [[nodiscard]] std::string GetCurrentBranchName() const; - [[nodiscard]] std::vector GetAllBranches() const; + [[nodiscard]] std::expected>, std::string> GetSubmodules() const; + [[nodiscard]] std::expected GetCurrentBranchName() const; + [[nodiscard]] std::expected, std::string> GetAllBranches() const; [[nodiscard]] auto GetName() const { return name_; } [[nodiscard]] bool IsValid() const; diff --git a/src/main.cpp b/src/main.cpp index a51a9de..3f10da8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "config_manager.h" #include "main_frame.h" #include "git_command.h" +#include "task_manager.h" class GitBranchApp : public wxApp { public: @@ -31,6 +32,10 @@ public: return true; } + virtual void OnEventLoopEnter(wxEventLoopBase*) override { + TaskManager::Get().ProcessComplete(); + } + int OnExit() override { // 保存配置 ConfigManager::Instance().Save(); diff --git a/src/main_frame.cpp b/src/main_frame.cpp index 8aa8b2a..94e51eb 100644 --- a/src/main_frame.cpp +++ b/src/main_frame.cpp @@ -47,7 +47,7 @@ void MainFrame::InitializeUI() { // 创建子模块分支选择器 auto submodules = main_repo_->GetSubmodules(); - for (const auto& submodule : submodules) { + for (const auto& submodule : submodules.value()) { branch_selectors_.push_back(new BranchSelector(this, submodule)); } @@ -58,9 +58,6 @@ void MainFrame::InitializeUI() { // 创建应用按钮 wxButton* apply_button = new wxButton(this, wxID_APPLY, "Apply"); - // 创建日志控件 - log_ctrl_ = new ColoredLogCtrl(this); - // 添加到主布局 main_sizer->Add(group_combo_, 0, wxEXPAND | wxALL, 5); @@ -70,7 +67,6 @@ void MainFrame::InitializeUI() { main_sizer->Add(hard_reset_checkbox_, 0, wxALL, 5); main_sizer->Add(apply_button, 0, wxALL, 5); - main_sizer->Add(log_ctrl_, 1, wxEXPAND | wxALL, 5); SetSizerAndFit(main_sizer); SetMinSize(wxSize(600, 400)); @@ -80,22 +76,24 @@ void MainFrame::PopulateBranchGroups() { // 获取所有分支组 std::vector branch_groups; + auto result = main_repo_->GetAllBranches(); // 获取主仓库的所有分支 - for (const auto& branch : main_repo_->GetAllBranches()) { + for (const auto& branch : result.value()) { branch_groups.push_back(branch); } // 获取子模块的所有分支 auto submodules = main_repo_->GetSubmodules(); - for (const auto& submodule : submodules) { - for (const auto& branch : submodule->GetAllBranches()) { + for (const auto& submodule : submodules.value()) { + auto r = submodule->GetAllBranches(); + for (const auto& branch : r.value()) { branch_groups.push_back(branch); } } // 去重 - std::sort(branch_groups.begin(), branch_groups.end()); - branch_groups.erase(std::unique(branch_groups.begin(), branch_groups.end()), branch_groups.end()); + std::ranges::sort(branch_groups); + branch_groups.erase(std::ranges::unique(branch_groups).begin(), branch_groups.end()); // 创建分支组选择器 group_combo_ = new wxComboBox(this, wxID_ANY); @@ -107,46 +105,42 @@ void MainFrame::PopulateBranchGroups() { } void MainFrame::OnApply(wxCommandEvent& event) { - log_ctrl_->LogInfo("Applying branch changes..."); - bool any_failures = false; for (auto* selector : branch_selectors_) { std::string repo_name = selector->GetRepo()->GetName(); std::string branch_name = selector->GetSelectedBranchName(); - log_ctrl_->LogInfo(wxString::Format("Switching %s to branch: %s", repo_name, branch_name)); + // log_ctrl_->LogInfo(wxString::Format("Switching %s to branch: %s", repo_name, branch_name)); - if (is_hard_reset_) { - if (selector->HardReset()) { - log_ctrl_->LogSuccess(wxString::Format("Successfully updated %s to %s", repo_name, branch_name)); - } else { - log_ctrl_->LogError(wxString::Format("Failed to update %s to %s", repo_name, branch_name)); - } - } - - if (selector->ApplyBranchChange()) { - log_ctrl_->LogSuccess(wxString::Format("Successfully updated %s to %s", repo_name, branch_name)); - } else { - log_ctrl_->LogError(wxString::Format("Failed to update %s to %s", repo_name, branch_name)); - any_failures = true; - } + // if (is_hard_reset_) { + // if (selector->HardReset()) { + // log_ctrl_->LogSuccess(wxString::Format("Successfully updated %s to %s", repo_name, branch_name)); + // } else { + // log_ctrl_->LogError(wxString::Format("Failed to update %s to %s", repo_name, branch_name)); + // } + // } + // + // if (selector->ApplyBranchChange()) { + // log_ctrl_->LogSuccess(wxString::Format("Successfully updated %s to %s", repo_name, branch_name)); + // } else { + // log_ctrl_->LogError(wxString::Format("Failed to update %s to %s", repo_name, branch_name)); + // any_failures = true; + // } } - if (any_failures) { - log_ctrl_->LogWarning("Some operations failed. See errors above."); - } else { - log_ctrl_->LogSuccess("All operations completed successfully!"); - } - - log_ctrl_->LogInfo("==============完成=============="); + // if (any_failures) { + // log_ctrl_->LogWarning("Some operations failed. See errors above."); + // } else { + // log_ctrl_->LogSuccess("All operations completed successfully!"); + // } } void MainFrame::OnGroupSelect(wxCommandEvent& event) { wxString selected_group = group_combo_->GetStringSelection(); if (selected_group.IsEmpty()) return; - log_ctrl_->LogInfo(wxString::Format("Selecting branch group: %s", selected_group)); + // log_ctrl_->LogInfo(wxString::Format("Selecting branch group: %s", selected_group)); for (auto* selector : branch_selectors_) { selector->SelectGroupBranch(selected_group); @@ -160,9 +154,9 @@ void MainFrame::OnHardResetChanged(wxCommandEvent& event) { is_hard_reset_ = event.IsChecked(); ConfigManager::Instance().SetHardReset(is_hard_reset_); - if (is_hard_reset_) { - log_ctrl_->LogWarning("Hard reset mode enabled. This will discard local changes!"); - } else { - log_ctrl_->LogInfo("Hard reset mode disabled."); - } + // if (is_hard_reset_) { + // log_ctrl_->LogWarning("Hard reset mode enabled. This will discard local changes!"); + // } else { + // log_ctrl_->LogInfo("Hard reset mode disabled."); + // } } diff --git a/src/main_frame.h b/src/main_frame.h index d4db823..3b55593 100644 --- a/src/main_frame.h +++ b/src/main_frame.h @@ -24,7 +24,6 @@ private: std::vector branch_selectors_; wxComboBox* group_combo_ = nullptr; wxCheckBox* hard_reset_checkbox_ = nullptr; - ColoredLogCtrl* log_ctrl_ = nullptr; std::shared_ptr main_repo_; bool is_hard_reset_ = false; diff --git a/src/task_manager.cpp b/src/task_manager.cpp new file mode 100644 index 0000000..a857e44 --- /dev/null +++ b/src/task_manager.cpp @@ -0,0 +1 @@ +#include "task_manager.h" diff --git a/src/task_manager.h b/src/task_manager.h new file mode 100644 index 0000000..d7aeedd --- /dev/null +++ b/src/task_manager.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include + +class TaskManager { +public: + static auto& Get() { + static TaskManager instance; + return instance; + } + + void PushGitTask(auto&& task, auto&& on_complete) { + tasks_.emplace_back(std::thread([this, task, on_complete] { + const auto& result = task(); + std::lock_guard lock(mutex_); + on_complete_.push_back([result, on_complete] { + on_complete(result); + }); + })); + } + void PushGitTask(auto&& task) { + tasks_.emplace_back(std::thread([this, task] { + task(); + })); + } + + void ProcessComplete() { + std::lock_guard lock(mutex_); + for (auto& task : tasks_) { + if (task.joinable()) { + task.join(); + } + } + + for (const auto& complete : on_complete_) { + if (complete) { + complete(); + } + } + on_complete_.clear(); + } +private: + TaskManager() = default; + std::vector tasks_; + + std::vector> on_complete_; + std::mutex mutex_; +};