perf(git): move refresh and actions off the UI thread
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -71,6 +72,9 @@ std::array<char, 256> g_inline_branch_name{};
|
||||
std::string g_inline_branch_target;
|
||||
std::string g_requested_branch_checkout;
|
||||
std::string g_running_branch_checkout;
|
||||
std::string g_merge_branch_source;
|
||||
std::string g_merge_branch_target;
|
||||
bool g_merge_branch_popup = false;
|
||||
std::string g_pending_commit_jump;
|
||||
RepositoryView* g_expanded_refs_repository = nullptr;
|
||||
int g_expanded_refs_commit = -1;
|
||||
@@ -121,6 +125,36 @@ using RefreshClock = std::chrono::steady_clock;
|
||||
constexpr auto active_refresh_interval = std::chrono::seconds(2);
|
||||
constexpr auto background_refresh_interval = std::chrono::seconds(5);
|
||||
|
||||
enum class GitAsyncOperation { reload, capture, pull, push, checkout_branch, push_branch, fetch, stash, pop_stash };
|
||||
|
||||
struct GitAsyncRequest {
|
||||
GitAsyncOperation operation = GitAsyncOperation::reload;
|
||||
RepositoryView* tab = nullptr;
|
||||
std::string repo_path;
|
||||
std::vector<std::string> arguments;
|
||||
std::string branch;
|
||||
std::string remote;
|
||||
std::string success_notice;
|
||||
std::string focus_commit;
|
||||
std::string preserve_selected_hash;
|
||||
int pull_mode = 1;
|
||||
bool surface_errors = false;
|
||||
};
|
||||
|
||||
struct GitAsyncResult {
|
||||
GitAsyncOperation operation = GitAsyncOperation::reload;
|
||||
RepositoryView* tab = nullptr;
|
||||
std::string repo_path;
|
||||
bool success = false;
|
||||
std::string notice;
|
||||
std::string focus_commit;
|
||||
std::string preserve_selected_hash;
|
||||
bool surface_errors = false;
|
||||
std::unique_ptr<RepositoryView> snapshot;
|
||||
};
|
||||
|
||||
std::future<GitAsyncResult> g_git_async_future;
|
||||
|
||||
|
||||
float ui(float value) { return value * g_ui_scale; }
|
||||
RepositoryView& repo() { return *g_tabs.at(g_active_tab); }
|
||||
@@ -187,25 +221,191 @@ bool copy_to_clipboard(std::string_view text, const char* description) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void transfer_repository_state(RepositoryView& source, RepositoryView& target) {
|
||||
if (&source == &target) return;
|
||||
target.close();
|
||||
target.repo = source.repo;
|
||||
source.repo = nullptr;
|
||||
target.commit_walk = source.commit_walk;
|
||||
source.commit_walk = nullptr;
|
||||
target.history_exhausted = source.history_exhausted;
|
||||
target.credentials_checked = source.credentials_checked;
|
||||
target.path = std::move(source.path);
|
||||
target.name = std::move(source.name);
|
||||
target.branch = std::move(source.branch);
|
||||
target.local_branches = std::move(source.local_branches);
|
||||
target.remote_branches = std::move(source.remote_branches);
|
||||
target.local_branch_divergence = std::move(source.local_branch_divergence);
|
||||
target.remote_branch_divergence = std::move(source.remote_branch_divergence);
|
||||
target.remotes = std::move(source.remotes);
|
||||
target.worktrees = std::move(source.worktrees);
|
||||
target.worktree_branches = std::move(source.worktree_branches);
|
||||
target.submodules = std::move(source.submodules);
|
||||
target.submodule_statuses = std::move(source.submodule_statuses);
|
||||
target.commits = std::move(source.commits);
|
||||
target.working_files = std::move(source.working_files);
|
||||
target.selected_commit = source.selected_commit;
|
||||
target.scroll_to_commit = source.scroll_to_commit;
|
||||
target.last_background_refresh = source.last_background_refresh;
|
||||
target.pending_background_refresh = source.pending_background_refresh;
|
||||
target.undo_action = std::move(source.undo_action);
|
||||
target.redo_action = std::move(source.redo_action);
|
||||
}
|
||||
|
||||
std::string selected_commit_hash(const RepositoryView& repository) {
|
||||
if (repository.selected_commit < 0 || repository.selected_commit >= static_cast<int>(repository.commits.size()))
|
||||
return {};
|
||||
return oid_string(repository.commits[static_cast<size_t>(repository.selected_commit)].oid);
|
||||
}
|
||||
|
||||
void restore_selected_commit(RepositoryView& repository, const std::string& hash) {
|
||||
if (hash.empty()) return;
|
||||
git_oid target{};
|
||||
if (git_oid_fromstr(&target, hash.c_str()) != 0) return;
|
||||
for (size_t index = 0; index < repository.commits.size(); ++index) {
|
||||
if (git_oid_equal(&repository.commits[index].oid, &target) != 0) {
|
||||
repository.selected_commit = static_cast<int>(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryView* find_repository_tab(RepositoryView* tab) {
|
||||
const auto iterator = std::find_if(g_tabs.begin(), g_tabs.end(), [tab](const auto& item) {
|
||||
return item.get() == tab;
|
||||
});
|
||||
return iterator == g_tabs.end() ? nullptr : iterator->get();
|
||||
}
|
||||
|
||||
void mark_repository_refreshed(RepositoryView& repository) {
|
||||
repository.last_background_refresh = RefreshClock::now();
|
||||
repository.pending_background_refresh = false;
|
||||
}
|
||||
|
||||
bool reload_repository_background(RepositoryView& repository, bool surface_errors = false) {
|
||||
if (!g_git_manager || !repository.repo || repository.path.empty()) return false;
|
||||
std::string error;
|
||||
repository.pending_background_refresh = false;
|
||||
repository.last_background_refresh = RefreshClock::now();
|
||||
if (!g_git_manager->reload(repository, error)) {
|
||||
if (surface_errors && !error.empty()) g_notice = error;
|
||||
GitAsyncResult execute_git_async_request(const GitAsyncRequest& request) {
|
||||
GitAsyncResult result;
|
||||
result.operation = request.operation;
|
||||
result.tab = request.tab;
|
||||
result.repo_path = request.repo_path;
|
||||
result.focus_commit = request.focus_commit;
|
||||
result.preserve_selected_hash = request.preserve_selected_hash;
|
||||
result.surface_errors = request.surface_errors;
|
||||
|
||||
GitManager manager;
|
||||
RepositoryView repository;
|
||||
if (!manager.openRepository(repository, request.repo_path, result.notice)) return result;
|
||||
|
||||
bool action_ok = true;
|
||||
switch (request.operation) {
|
||||
case GitAsyncOperation::reload:
|
||||
break;
|
||||
case GitAsyncOperation::capture: {
|
||||
std::string output;
|
||||
action_ok = manager.captureGit(repository, request.arguments, output, result.notice) &&
|
||||
manager.reload(repository, result.notice);
|
||||
if (action_ok) result.notice = request.success_notice;
|
||||
break;
|
||||
}
|
||||
case GitAsyncOperation::pull:
|
||||
action_ok = manager.pull(repository, request.pull_mode, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::push:
|
||||
action_ok = manager.push(repository, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::checkout_branch:
|
||||
action_ok = manager.checkoutBranch(repository, request.branch, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::push_branch:
|
||||
action_ok = manager.pushBranch(repository, request.branch, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::fetch:
|
||||
action_ok = manager.fetch(repository, request.remote, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::stash:
|
||||
action_ok = manager.stash(repository, result.notice);
|
||||
break;
|
||||
case GitAsyncOperation::pop_stash:
|
||||
action_ok = manager.popStash(repository, result.notice);
|
||||
break;
|
||||
}
|
||||
if (!action_ok) return result;
|
||||
|
||||
auto snapshot = std::make_unique<RepositoryView>();
|
||||
transfer_repository_state(repository, *snapshot);
|
||||
result.snapshot = std::move(snapshot);
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void apply_git_async_result(GitAsyncResult result) {
|
||||
if (result.operation == GitAsyncOperation::pull || result.operation == GitAsyncOperation::push)
|
||||
g_running_toolbar_action = ToolbarActionRequest::none;
|
||||
if (result.operation == GitAsyncOperation::checkout_branch)
|
||||
g_running_branch_checkout.clear();
|
||||
RepositoryView* target = find_repository_tab(result.tab);
|
||||
if (!target || target->path != result.repo_path) {
|
||||
if ((result.success || result.surface_errors) && !result.notice.empty()) g_notice = result.notice;
|
||||
return;
|
||||
}
|
||||
if (result.success && result.snapshot) {
|
||||
transfer_repository_state(*result.snapshot, *target);
|
||||
if (!result.focus_commit.empty()) {
|
||||
if (target == &repo()) g_pending_commit_jump = result.focus_commit;
|
||||
else restore_selected_commit(*target, result.focus_commit);
|
||||
} else {
|
||||
restore_selected_commit(*target, result.preserve_selected_hash);
|
||||
}
|
||||
mark_repository_refreshed(*target);
|
||||
}
|
||||
if ((result.success || result.surface_errors) && !result.notice.empty()) g_notice = result.notice;
|
||||
}
|
||||
|
||||
bool git_async_busy() {
|
||||
return g_git_async_future.valid() &&
|
||||
g_git_async_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready;
|
||||
}
|
||||
|
||||
void poll_git_async_result() {
|
||||
if (!g_git_async_future.valid()) return;
|
||||
if (g_git_async_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) return;
|
||||
apply_git_async_result(g_git_async_future.get());
|
||||
}
|
||||
|
||||
bool start_git_async_request(GitAsyncRequest request, const char* busy_notice = "Git operation already running") {
|
||||
poll_git_async_result();
|
||||
if (git_async_busy()) {
|
||||
if (busy_notice && *busy_notice) g_notice = busy_notice;
|
||||
return false;
|
||||
}
|
||||
if (!request.tab || request.repo_path.empty()) return false;
|
||||
g_git_async_future = std::async(std::launch::async, [request = std::move(request)]() {
|
||||
return execute_git_async_request(request);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reload_repository_background(RepositoryView& repository, bool surface_errors = false) {
|
||||
if (!g_git_manager || !repository.repo || repository.path.empty()) return false;
|
||||
GitAsyncRequest request;
|
||||
request.operation = GitAsyncOperation::reload;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.surface_errors = surface_errors;
|
||||
request.preserve_selected_hash = selected_commit_hash(repository);
|
||||
const bool started = start_git_async_request(std::move(request), surface_errors
|
||||
? "A Git operation is already running"
|
||||
: "");
|
||||
if (started) {
|
||||
repository.pending_background_refresh = false;
|
||||
repository.last_background_refresh = RefreshClock::now();
|
||||
}
|
||||
return started;
|
||||
}
|
||||
|
||||
void process_repository_background_refreshes() {
|
||||
poll_git_async_result();
|
||||
if (!g_git_manager || g_tabs.empty()) return;
|
||||
if (git_async_busy()) return;
|
||||
if (g_running_toolbar_action != ToolbarActionRequest::none) return;
|
||||
if (!g_running_branch_checkout.empty() || !g_requested_branch_checkout.empty()) return;
|
||||
const RefreshClock::time_point now = RefreshClock::now();
|
||||
@@ -216,19 +416,24 @@ void process_repository_background_refreshes() {
|
||||
const bool due = repository.pending_background_refresh ||
|
||||
repository.last_background_refresh.time_since_epoch().count() == 0 ||
|
||||
now - repository.last_background_refresh >= interval;
|
||||
if (due) reload_repository_background(repository, index == g_active_tab);
|
||||
if (due) {
|
||||
reload_repository_background(repository, index == g_active_tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool run_repository_action(const std::vector<std::string>& arguments, const std::string& success_notice,
|
||||
const std::string& focus_commit = {}) {
|
||||
std::string output;
|
||||
if (!g_git_manager->captureGit(repo(), arguments, output, g_notice)) return false;
|
||||
if (!g_git_manager->reload(repo(), g_notice)) return false;
|
||||
mark_repository_refreshed(repo());
|
||||
if (!focus_commit.empty()) g_pending_commit_jump = focus_commit;
|
||||
g_notice = success_notice;
|
||||
return true;
|
||||
GitAsyncRequest request;
|
||||
request.operation = GitAsyncOperation::capture;
|
||||
request.tab = &repo();
|
||||
request.repo_path = repo().path;
|
||||
request.arguments = arguments;
|
||||
request.success_notice = success_notice;
|
||||
request.focus_commit = focus_commit;
|
||||
request.preserve_selected_hash = selected_commit_hash(repo());
|
||||
return start_git_async_request(std::move(request));
|
||||
}
|
||||
|
||||
void mark_action_refresh(RepositoryView& repository) {
|
||||
@@ -236,6 +441,61 @@ void mark_action_refresh(RepositoryView& repository) {
|
||||
repository.pending_background_refresh = true;
|
||||
}
|
||||
|
||||
bool start_branch_checkout_async(RepositoryView& repository, const std::string& branch) {
|
||||
GitAsyncRequest request;
|
||||
request.operation = GitAsyncOperation::checkout_branch;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.branch = branch;
|
||||
return start_git_async_request(std::move(request));
|
||||
}
|
||||
|
||||
bool start_push_branch_async(RepositoryView& repository, const std::string& branch) {
|
||||
GitAsyncRequest request;
|
||||
request.operation = GitAsyncOperation::push_branch;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.branch = branch;
|
||||
request.preserve_selected_hash = selected_commit_hash(repository);
|
||||
return start_git_async_request(std::move(request));
|
||||
}
|
||||
|
||||
bool start_fetch_async(RepositoryView& repository, const std::string& remote, bool surface_errors = true) {
|
||||
GitAsyncRequest request;
|
||||
request.operation = GitAsyncOperation::fetch;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.remote = remote;
|
||||
request.surface_errors = surface_errors;
|
||||
request.preserve_selected_hash = selected_commit_hash(repository);
|
||||
return start_git_async_request(std::move(request), "A Git operation is already running");
|
||||
}
|
||||
|
||||
bool start_toolbar_action_async(RepositoryView& repository, ToolbarActionRequest action) {
|
||||
GitAsyncRequest request;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.preserve_selected_hash = selected_commit_hash(repository);
|
||||
if (action == ToolbarActionRequest::pull) {
|
||||
request.operation = GitAsyncOperation::pull;
|
||||
request.pull_mode = g_user_data->pullMode();
|
||||
} else if (action == ToolbarActionRequest::push) {
|
||||
request.operation = GitAsyncOperation::push;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return start_git_async_request(std::move(request));
|
||||
}
|
||||
|
||||
bool start_stash_async(RepositoryView& repository, bool pop) {
|
||||
GitAsyncRequest request;
|
||||
request.operation = pop ? GitAsyncOperation::pop_stash : GitAsyncOperation::stash;
|
||||
request.tab = &repository;
|
||||
request.repo_path = repository.path;
|
||||
request.preserve_selected_hash = selected_commit_hash(repository);
|
||||
return start_git_async_request(std::move(request));
|
||||
}
|
||||
|
||||
void draw_dotted_bezier(ImDrawList* draw, const ImVec2& start, const ImVec2& control_start,
|
||||
const ImVec2& control_end, const ImVec2& end, ImU32 color, float radius, float spacing) {
|
||||
const float estimated_length = std::hypot(end.x - start.x, end.y - start.y) +
|
||||
@@ -288,6 +548,12 @@ void cancel_inline_branch() {
|
||||
g_focus_inline_branch = false;
|
||||
}
|
||||
|
||||
void cancel_merge_branch() {
|
||||
g_merge_branch_source.clear();
|
||||
g_merge_branch_target.clear();
|
||||
g_merge_branch_popup = false;
|
||||
}
|
||||
|
||||
void begin_inline_branch(int commit_index) {
|
||||
if (commit_index < 0 || commit_index >= static_cast<int>(repo().commits.size())) {
|
||||
g_notice = "Create an initial commit before creating a branch";
|
||||
@@ -314,6 +580,13 @@ void clear_sidebar_filter() {
|
||||
g_filter.fill('\0');
|
||||
}
|
||||
|
||||
void request_branch_merge(const std::string& source_branch, const std::string& target_branch) {
|
||||
if (source_branch.empty() || target_branch.empty() || source_branch == target_branch) return;
|
||||
g_merge_branch_source = source_branch;
|
||||
g_merge_branch_target = target_branch;
|
||||
g_merge_branch_popup = true;
|
||||
}
|
||||
|
||||
void request_branch_checkout(const std::string& branch) {
|
||||
g_requested_branch_checkout = branch;
|
||||
}
|
||||
@@ -336,6 +609,7 @@ void reset_repository_view() {
|
||||
g_expanded_refs_repository = nullptr;
|
||||
g_expanded_refs_commit = -1;
|
||||
cancel_inline_branch();
|
||||
cancel_merge_branch();
|
||||
g_requested_branch_checkout.clear();
|
||||
g_running_branch_checkout.clear();
|
||||
g_reset_repository_view = true;
|
||||
@@ -362,6 +636,7 @@ void close_tab(size_t index) {
|
||||
if (index >= g_tabs.size()) return;
|
||||
const bool closing_active_tab = index == g_active_tab;
|
||||
if (g_inline_branch_repository == g_tabs[index].get()) cancel_inline_branch();
|
||||
cancel_merge_branch();
|
||||
if (g_user_data && !g_tabs[index]->path.empty()) g_user_data->addRecentlyClosed(g_tabs[index]->path);
|
||||
g_tabs.erase(g_tabs.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
if (g_tabs.empty()) create_new_tab();
|
||||
@@ -843,21 +1118,45 @@ void branch_context(const std::string& branch, bool remote) {
|
||||
request_branch_checkout(branch);
|
||||
clear_sidebar_filter();
|
||||
}
|
||||
if (!remote && ImGui::MenuItem(ICON_TB_ARROW_RIGHT_ARROW_LEFT " Merge into current branch")) {
|
||||
request_branch_merge(branch, repo().branch);
|
||||
clear_sidebar_filter();
|
||||
}
|
||||
if (!remote && ImGui::MenuItem(ICON_TB_UPLOAD " Push branch")) {
|
||||
if (g_git_manager->pushBranch(repo(), branch, g_notice))
|
||||
mark_action_refresh(repo());
|
||||
start_push_branch_async(repo(), branch);
|
||||
clear_sidebar_filter();
|
||||
}
|
||||
if (remote && ImGui::MenuItem(ICON_TB_DOWNLOAD " Fetch")) {
|
||||
const size_t slash = branch.find('/');
|
||||
if (g_git_manager->fetch(repo(), slash == std::string::npos ? branch : branch.substr(0, slash), g_notice))
|
||||
mark_action_refresh(repo());
|
||||
start_fetch_async(repo(), slash == std::string::npos ? branch : branch.substr(0, slash));
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem(ICON_TB_COPY " Copy branch name")) copy_to_clipboard(branch, "branch name");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
void branch_drag_source(const std::string& branch, bool remote) {
|
||||
if (!ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) return;
|
||||
ImGui::SetDragDropPayload("GITREE_BRANCH", branch.c_str(), branch.size() + 1);
|
||||
ImGui::TextUnformatted(remote ? ICON_TB_CLOUD " Remote branch" : ICON_TB_CODE_BRANCH " Local branch");
|
||||
ImGui::TextDisabled("%s", branch.c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
void branch_drag_target(const std::string& branch) {
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
|
||||
ImGui::GetDragDropPayload() != nullptr) {
|
||||
ImGui::SetTooltip("%s Drop to merge into %s", ICON_TB_ARROW_RIGHT_ARROW_LEFT, branch.c_str());
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
}
|
||||
if (!ImGui::BeginDragDropTarget()) return;
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("GITREE_BRANCH")) {
|
||||
const char* source = static_cast<const char*>(payload->Data);
|
||||
if (source && source[0] != '\0') request_branch_merge(source, branch);
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
std::string branch_divergence_text(const std::string& branch, bool remote) {
|
||||
const auto& divergences = remote
|
||||
? repo().remote_branch_divergence
|
||||
@@ -893,6 +1192,8 @@ void draw_branch_nodes(const BranchNode& parent, const char* branch_icon, const
|
||||
sidebar_item_row(branch_icon, name, "branch" + id,
|
||||
nullptr, 0, branch_icon_color, divergence,
|
||||
!remote && node.full_name == repo().branch);
|
||||
branch_drag_source(node.full_name, remote);
|
||||
if (!remote) branch_drag_target(node.full_name);
|
||||
branch_context(node.full_name, remote);
|
||||
}
|
||||
draw_branch_nodes(node, branch_icon, root_group_icon, remote, id, depth + 1);
|
||||
@@ -902,6 +1203,8 @@ void draw_branch_nodes(const BranchNode& parent, const char* branch_icon, const
|
||||
const std::string divergence = branch_divergence_text(node.full_name, remote);
|
||||
sidebar_item_row(branch_icon, name, id, nullptr, 0, branch_icon_color, divergence,
|
||||
!remote && node.full_name == repo().branch);
|
||||
branch_drag_source(node.full_name, remote);
|
||||
if (!remote) branch_drag_target(node.full_name);
|
||||
branch_context(node.full_name, remote);
|
||||
}
|
||||
}
|
||||
@@ -2692,6 +2995,45 @@ void draw_licenses_popup() {
|
||||
|
||||
void draw_git_action_popups() {
|
||||
static bool checkout_new_branch = true;
|
||||
if (g_merge_branch_popup) {
|
||||
g_merge_branch_popup = false;
|
||||
ImGui::OpenPopup("Merge branches");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Merge branches", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextUnformatted(ICON_TB_ARROW_RIGHT_ARROW_LEFT " Merge branches");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
ImGui::TextUnformatted(ICON_TB_CODE_BRANCH " Source branch");
|
||||
ImGui::TextDisabled("%s", g_merge_branch_source.c_str());
|
||||
ImGui::TextUnformatted(ICON_TB_CODE_BRANCH " Target branch");
|
||||
ImGui::TextDisabled("%s", g_merge_branch_target.c_str());
|
||||
if (g_merge_branch_target != repo().branch)
|
||||
ImGui::TextWrapped("%s The target branch will be checked out first, then the merge will run on that branch.",
|
||||
ICON_TB_TRIANGLE_EXCLAMATION);
|
||||
else
|
||||
ImGui::TextWrapped("This will merge the source branch into the currently checked out branch.");
|
||||
ImGui::Spacing();
|
||||
ImGui::BeginDisabled(g_merge_branch_source.empty() || g_merge_branch_target.empty() ||
|
||||
g_merge_branch_source == g_merge_branch_target);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.35f, 0.22f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.18f, 0.42f, 0.27f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.20f, 0.48f, 0.30f, 1.0f));
|
||||
if (ImGui::Button(ICON_TB_ARROW_RIGHT_ARROW_LEFT " Merge branch", {ui(150.0f), 0}) &&
|
||||
g_git_manager->mergeBranch(repo(), g_merge_branch_source, g_merge_branch_target, g_notice)) {
|
||||
mark_action_refresh(repo());
|
||||
cancel_merge_branch();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_TB_XMARK " Cancel", {ui(90.0f), 0})) {
|
||||
cancel_merge_branch();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (g_branch_create_popup) {
|
||||
g_git_name.fill('\0');
|
||||
checkout_new_branch = true;
|
||||
@@ -3184,9 +3526,8 @@ void draw_app() {
|
||||
}
|
||||
if (ImGui::BeginMenu("Edit")) { ImGui::MenuItem("Preferences", nullptr, false, false); ImGui::EndMenu(); }
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
if ((ImGui::MenuItem("Refresh", "F5") || refresh_requested) &&
|
||||
g_git_manager->reload(repo(), g_notice))
|
||||
mark_repository_refreshed(repo());
|
||||
if (ImGui::MenuItem("Refresh", "F5") || refresh_requested)
|
||||
reload_repository_background(repo(), true);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
@@ -3436,13 +3777,11 @@ void draw_app() {
|
||||
}
|
||||
ImGui::SameLine(0, action_spacing);
|
||||
if (toolbar_action("stash", "Stash", ICON_TB_BOX_ARCHIVE, "Stash changes", true, false, 58)) {
|
||||
if (g_git_manager->stash(repo(), g_notice))
|
||||
mark_action_refresh(repo());
|
||||
start_stash_async(repo(), false);
|
||||
}
|
||||
ImGui::SameLine(0, action_spacing);
|
||||
if (toolbar_action("pop", "Pop", ICON_TB_BOX_OPEN, "Pop latest stash", true, false, 54)) {
|
||||
if (g_git_manager->popStash(repo(), g_notice))
|
||||
mark_action_refresh(repo());
|
||||
start_stash_async(repo(), true);
|
||||
}
|
||||
draw_open_in_button();
|
||||
ImGui::EndChild();
|
||||
@@ -3504,20 +3843,13 @@ void draw_app() {
|
||||
draw_popups();
|
||||
ImGui::End();
|
||||
|
||||
if (toolbar_action_to_execute == ToolbarActionRequest::pull) {
|
||||
if (g_git_manager->pull(repo(), g_user_data->pullMode(), g_notice))
|
||||
mark_action_refresh(repo());
|
||||
} else if (toolbar_action_to_execute == ToolbarActionRequest::push) {
|
||||
if (g_git_manager->push(repo(), g_notice))
|
||||
mark_action_refresh(repo());
|
||||
}
|
||||
if (toolbar_action_to_execute != ToolbarActionRequest::none)
|
||||
g_running_toolbar_action = ToolbarActionRequest::none;
|
||||
if (toolbar_action_to_execute != ToolbarActionRequest::none &&
|
||||
start_toolbar_action_async(repo(), toolbar_action_to_execute))
|
||||
g_running_toolbar_action = toolbar_action_to_execute;
|
||||
|
||||
if (!branch_checkout_to_execute.empty()) {
|
||||
if (g_git_manager->checkoutBranch(repo(), branch_checkout_to_execute, g_notice))
|
||||
mark_action_refresh(repo());
|
||||
g_running_branch_checkout.clear();
|
||||
if (!g_running_branch_checkout.empty() && start_branch_checkout_async(repo(), branch_checkout_to_execute))
|
||||
g_running_branch_checkout = branch_checkout_to_execute;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user