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]
pub fn fetch(
pub async fn fetch(
path: String,
username: Option<String>,
password: Option<String>,
) -> Result<GitStatus, String> {
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<GitStatus, String> {
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]
+4 -4
View File
@@ -189,8 +189,10 @@
const AUTO_REFRESH_INTERVAL = 4000;
let autoRefreshTimer: ReturnType<typeof setInterval> | undefined;
const BACKGROUND_FETCH_INTERVAL = 180_000;
const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000;
let backgroundFetchTimer: ReturnType<typeof setInterval> | 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() {