From 1576f61234bce078de4d60d2fe1f4b7c013451bc Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Fri, 3 Jul 2026 17:14:53 +0200 Subject: [PATCH] feat(git): make fetch command asynchronous for improved performance The fetch command has been updated to run asynchronously, allowing for better performance and responsiveness in the application. This change ensures that the command does not block the main thread, enhancing the user experience when interacting with repositories. - Fetch command now uses async/await for non-blocking execution - Added a grace period to prevent immediate background fetch after switching repositories - Improved handling of authentication errors during fetch operations --- src-tauri/src/git.rs | 48 ++++++++++++++++++++++++-------------------- src/App.svelte | 8 ++++---- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src-tauri/src/git.rs b/src-tauri/src/git.rs index 4ae9397..2e2486c 100644 --- a/src-tauri/src/git.rs +++ b/src-tauri/src/git.rs @@ -718,34 +718,38 @@ pub fn pull( } #[tauri::command] -pub fn fetch( +pub async fn fetch( path: String, username: Option, password: Option, ) -> Result { - let repo = resolve_repo(&path)?; - let fetch_args = ["fetch"]; - let output = match (username.as_deref(), password.as_deref()) { - (Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => { - run_git_authenticated_output(&repo, fetch_args, u, p)? + tauri::async_runtime::spawn_blocking(move || -> Result { + let repo = resolve_repo(&path)?; + let fetch_args = ["fetch"]; + let output = match (username.as_deref(), password.as_deref()) { + (Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => { + run_git_authenticated_output(&repo, fetch_args, u, p)? + } + _ => git_command() + .arg("-C") + .arg(&repo) + .args(fetch_args) + .output() + .map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?, + }; + + if output.status.success() { + return status_for_repo(&repo); } - _ => git_command() - .arg("-C") - .arg(&repo) - .args(fetch_args) - .output() - .map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?, - }; - if output.status.success() { - return status_for_repo(&repo); - } - - let details = command_output_details(&output); - if is_auth_error(&details) { - return Err(format!("AUTH_FAILED:{details}")); - } - Err(format!("Git command failed: {details}")) + let details = command_output_details(&output); + if is_auth_error(&details) { + return Err(format!("AUTH_FAILED:{details}")); + } + Err(format!("Git command failed: {details}")) + }) + .await + .map_err(|err| format!("Could not fetch repository: {err}"))? } #[tauri::command] diff --git a/src/App.svelte b/src/App.svelte index bfe317f..206f403 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -189,8 +189,10 @@ const AUTO_REFRESH_INTERVAL = 4000; let autoRefreshTimer: ReturnType | undefined; const BACKGROUND_FETCH_INTERVAL = 180_000; + const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000; let backgroundFetchTimer: ReturnType | undefined; let backgroundFetchInFlight = false; + let lastRepoSwitchAt = 0; let updateToastOpen = false; let updateToastState: UpdateToastState = "available"; let pendingUpdate: Update | null = null; @@ -264,6 +266,7 @@ // Push buttons instead, not as a background popup. async function backgroundFetchTick() { if (!autoRefreshEnabled || activeView !== "repository" || !activeRepoPath || isBusy || backgroundFetchInFlight) return; + if (Date.now() - lastRepoSwitchAt < BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS) return; backgroundFetchInFlight = true; try { await fetchRemote(activeRepoPath); @@ -869,11 +872,8 @@ await refreshBranchList(activeRepoPath, bundle.branches); await refreshCommitHistory(activeRepoPath, bundle.commits); await refreshExplorerFiles(activeRepoPath, bundle.files); + lastRepoSwitchAt = Date.now(); }); - // Silent background fetch on open, same as the periodic tick — no "Fetching" indicator, - // just brings ahead/behind (and the taskbar badge) up to date without blocking the - // repo-open flow. - void backgroundFetchTick(); } async function chooseRepositoryFolder() {