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() {