From d84fec8c7c1dee4369cf062d14b41ffd964efc92 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Thu, 12 Feb 2026 14:49:30 -0800 Subject: [PATCH] Fix git pull conflicts with pull.ff=only and improve commit UX - Replace git pull with fetch + merge to avoid conflicts with global git config (e.g. pull.ff=only) and background fetch --all - Disable commit/commit+push buttons when message is empty - Always show Push/Pull menu items even without remotes configured - Default remote name to 'origin' when adding a new remote --- crates/yaak-git/src/pull.rs | 56 +++++++++++++------ src-web/components/git/GitCommitDialog.tsx | 12 ++-- src-web/components/git/GitDropdown.tsx | 2 - src-web/components/git/callbacks.tsx | 4 +- .../components/git/showAddRemoteDialog.tsx | 4 +- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/crates/yaak-git/src/pull.rs b/crates/yaak-git/src/pull.rs index 0bf75474..4185350e 100644 --- a/crates/yaak-git/src/pull.rs +++ b/crates/yaak-git/src/pull.rs @@ -44,43 +44,65 @@ pub async fn git_pull(dir: &Path) -> Result { (branch_name, remote_name, remote_url) }; - let out = new_binary_command(dir) + // Step 1: fetch the specific branch + // NOTE: We use fetch + merge instead of `git pull` to avoid conflicts with + // global git config (e.g. pull.ff=only) and the background fetch --all. + let fetch_out = new_binary_command(dir) .await? - .args(["pull", &remote_name, &branch_name]) + .args(["fetch", &remote_name, &branch_name]) .env("GIT_TERMINAL_PROMPT", "0") .output() .await - .map_err(|e| GenericError(format!("failed to run git pull: {e}")))?; + .map_err(|e| GenericError(format!("failed to run git fetch: {e}")))?; - let stdout = String::from_utf8_lossy(&out.stdout); - let stderr = String::from_utf8_lossy(&out.stderr); - let combined = stdout + stderr; + let fetch_stdout = String::from_utf8_lossy(&fetch_out.stdout); + let fetch_stderr = String::from_utf8_lossy(&fetch_out.stderr); + let fetch_combined = format!("{fetch_stdout}{fetch_stderr}"); - info!("Pulled status={} {combined}", out.status); + info!("Fetched status={} {fetch_combined}", fetch_out.status); - if combined.to_lowercase().contains("could not read") { + if fetch_combined.to_lowercase().contains("could not read") { return Ok(PullResult::NeedsCredentials { url: remote_url.to_string(), error: None }); } - if combined.to_lowercase().contains("unable to access") { + if fetch_combined.to_lowercase().contains("unable to access") { return Ok(PullResult::NeedsCredentials { url: remote_url.to_string(), - error: Some(combined.to_string()), + error: Some(fetch_combined.to_string()), }); } - if !out.status.success() { - let combined_lower = combined.to_lowercase(); - if combined_lower.contains("cannot fast-forward") - || combined_lower.contains("not possible to fast-forward") - || combined_lower.contains("diverged") + if !fetch_out.status.success() { + return Err(GenericError(format!("Failed to fetch: {fetch_combined}"))); + } + + // Step 2: merge the fetched branch + let ref_name = format!("{}/{}", remote_name, branch_name); + let merge_out = new_binary_command(dir) + .await? + .args(["merge", "--ff-only", &ref_name]) + .output() + .await + .map_err(|e| GenericError(format!("failed to run git merge: {e}")))?; + + let merge_stdout = String::from_utf8_lossy(&merge_out.stdout); + let merge_stderr = String::from_utf8_lossy(&merge_out.stderr); + let merge_combined = format!("{merge_stdout}{merge_stderr}"); + + info!("Merged status={} {merge_combined}", merge_out.status); + + if !merge_out.status.success() { + let merge_lower = merge_combined.to_lowercase(); + if merge_lower.contains("cannot fast-forward") + || merge_lower.contains("not possible to fast-forward") + || merge_lower.contains("diverged") { return Ok(PullResult::Diverged { remote: remote_name, branch: branch_name }); } - return Err(GenericError(format!("Failed to pull {combined}"))); + return Err(GenericError(format!("Failed to merge: {merge_combined}"))); } - if combined.to_lowercase().contains("up to date") { + if merge_combined.to_lowercase().contains("up to date") { return Ok(PullResult::UpToDate); } diff --git a/src-web/components/git/GitCommitDialog.tsx b/src-web/components/git/GitCommitDialog.tsx index 7435f3b3..7de28a23 100644 --- a/src-web/components/git/GitCommitDialog.tsx +++ b/src-web/components/git/GitCommitDialog.tsx @@ -176,7 +176,11 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) { } if (!hasAnythingToAdd) { - return No changes since last commit; + return ( +
+ No changes since last commit +
+ ); } return ( @@ -230,14 +234,14 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) { hideLabel /> {commitError && {commitError}} - + {status.data?.headRefShorthand}