TODO 异步和错误处理
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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_ptr<git::Repository
|
||||
|
||||
// 如果没有提供标签,使用存储库名称
|
||||
wxString display_label = label.IsEmpty() ? wxString(repo->GetName()) : 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) {
|
||||
|
||||
@@ -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<git::Repository> GetRepo() const { return repo_; }
|
||||
|
||||
template<typename Value, typename ErrMsg>
|
||||
void ProcessResult(const std::expected<Value, ErrMsg>& 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<git::Repository> repo_;
|
||||
wxString current_group_;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "git_command.h"
|
||||
|
||||
std::string ExecGitCommand(const std::string& cmd, const std::filesystem::path& path) {
|
||||
std::expected<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> git::Repository::Pull() {
|
||||
return ExecGitCommand("pull", path_);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<git::Repository>> git::Repository::GetSubmodules() const {
|
||||
std::expected<std::vector<std::shared_ptr<git::Repository>>, std::string> git::Repository::GetSubmodules() const {
|
||||
std::vector<std::shared_ptr<git::Repository>> 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<std::shared_ptr<git::Repository>> git::Repository::GetSubmodules() c
|
||||
return submodules;
|
||||
}
|
||||
|
||||
std::string git::Repository::GetCurrentBranchName() const {
|
||||
std::string cmd = "rev-parse --abbrev-ref HEAD";
|
||||
std::expected<std::string, std::string> 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<std::string> git::Repository::GetAllBranches() const {
|
||||
std::expected<std::vector<std::string>, std::string> git::Repository::GetAllBranches() const {
|
||||
std::vector<std::string> 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()) {
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <expected>
|
||||
|
||||
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<std::string, std::string> SwitchBranch(const std::string& in_branch);
|
||||
std::expected<std::string, std::string> HardReset();
|
||||
std::expected<std::string, std::string> Pull();
|
||||
|
||||
[[nodiscard]] std::vector<std::shared_ptr<Repository>> GetSubmodules() const;
|
||||
[[nodiscard]] std::string GetCurrentBranchName() const;
|
||||
[[nodiscard]] std::vector<std::string> GetAllBranches() const;
|
||||
[[nodiscard]] std::expected<std::vector<std::shared_ptr<Repository>>, std::string> GetSubmodules() const;
|
||||
[[nodiscard]] std::expected<std::string, std::string> GetCurrentBranchName() const;
|
||||
[[nodiscard]] std::expected<std::vector<std::string>, std::string> GetAllBranches() const;
|
||||
[[nodiscard]] auto GetName() const { return name_; }
|
||||
|
||||
[[nodiscard]] bool IsValid() const;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<std::string> 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 (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 (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.");
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ private:
|
||||
std::vector<BranchSelector*> branch_selectors_;
|
||||
wxComboBox* group_combo_ = nullptr;
|
||||
wxCheckBox* hard_reset_checkbox_ = nullptr;
|
||||
ColoredLogCtrl* log_ctrl_ = nullptr;
|
||||
|
||||
std::shared_ptr<git::Repository> main_repo_;
|
||||
bool is_hard_reset_ = false;
|
||||
|
||||
1
src/task_manager.cpp
Normal file
1
src/task_manager.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "task_manager.h"
|
||||
49
src/task_manager.h
Normal file
49
src/task_manager.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
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<std::thread> tasks_;
|
||||
|
||||
std::vector<std::function<void()>> on_complete_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
Reference in New Issue
Block a user