feat(git): make fetch command asynchronous for improved performance #11

Merged
Christoph merged 1 commits from bug/blocking_ui into master 2026-07-03 15:15:51 +00:00
2 changed files with 30 additions and 26 deletions
+26 -22
View File
@@ -718,34 +718,38 @@ pub fn pull(
} }
#[tauri::command] #[tauri::command]
pub fn fetch( pub async fn fetch(
path: String, path: String,
username: Option<String>, username: Option<String>,
password: Option<String>, password: Option<String>,
) -> Result<GitStatus, String> { ) -> Result<GitStatus, String> {
let repo = resolve_repo(&path)?; tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
let fetch_args = ["fetch"]; let repo = resolve_repo(&path)?;
let output = match (username.as_deref(), password.as_deref()) { let fetch_args = ["fetch"];
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => { let output = match (username.as_deref(), password.as_deref()) {
run_git_authenticated_output(&repo, fetch_args, u, p)? (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() { let details = command_output_details(&output);
return status_for_repo(&repo); if is_auth_error(&details) {
} return Err(format!("AUTH_FAILED:{details}"));
}
let details = command_output_details(&output); Err(format!("Git command failed: {details}"))
if is_auth_error(&details) { })
return Err(format!("AUTH_FAILED:{details}")); .await
} .map_err(|err| format!("Could not fetch repository: {err}"))?
Err(format!("Git command failed: {details}"))
} }
#[tauri::command] #[tauri::command]
+4 -4
View File
@@ -189,8 +189,10 @@
const AUTO_REFRESH_INTERVAL = 4000; const AUTO_REFRESH_INTERVAL = 4000;
let autoRefreshTimer: ReturnType<typeof setInterval> | undefined; let autoRefreshTimer: ReturnType<typeof setInterval> | undefined;
const BACKGROUND_FETCH_INTERVAL = 180_000; const BACKGROUND_FETCH_INTERVAL = 180_000;
const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000;
let backgroundFetchTimer: ReturnType<typeof setInterval> | undefined; let backgroundFetchTimer: ReturnType<typeof setInterval> | undefined;
let backgroundFetchInFlight = false; let backgroundFetchInFlight = false;
let lastRepoSwitchAt = 0;
let updateToastOpen = false; let updateToastOpen = false;
let updateToastState: UpdateToastState = "available"; let updateToastState: UpdateToastState = "available";
let pendingUpdate: Update | null = null; let pendingUpdate: Update | null = null;
@@ -264,6 +266,7 @@
// Push buttons instead, not as a background popup. // Push buttons instead, not as a background popup.
async function backgroundFetchTick() { async function backgroundFetchTick() {
if (!autoRefreshEnabled || activeView !== "repository" || !activeRepoPath || isBusy || backgroundFetchInFlight) return; if (!autoRefreshEnabled || activeView !== "repository" || !activeRepoPath || isBusy || backgroundFetchInFlight) return;
if (Date.now() - lastRepoSwitchAt < BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS) return;
backgroundFetchInFlight = true; backgroundFetchInFlight = true;
try { try {
await fetchRemote(activeRepoPath); await fetchRemote(activeRepoPath);
@@ -869,11 +872,8 @@
await refreshBranchList(activeRepoPath, bundle.branches); await refreshBranchList(activeRepoPath, bundle.branches);
await refreshCommitHistory(activeRepoPath, bundle.commits); await refreshCommitHistory(activeRepoPath, bundle.commits);
await refreshExplorerFiles(activeRepoPath, bundle.files); 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() { async function chooseRepositoryFolder() {