From 62faf10793ba76352fdb1d7d9123214ded44489b Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Fri, 24 Oct 2025 20:28:23 +0530 Subject: [PATCH 1/7] Add rebase for Git-Pull in proto --- crates/proto/proto/git.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/proto/proto/git.proto b/crates/proto/proto/git.proto index 34b57d610be570..6734c86d3537ff 100644 --- a/crates/proto/proto/git.proto +++ b/crates/proto/proto/git.proto @@ -405,6 +405,7 @@ message Pull { string remote_name = 4; string branch_name = 5; uint64 askpass_id = 6; + bool rebase = 7; } message RemoteMessageResponse { From b7ae52d124a104da2e7fb93d9369de0cec55fbae Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Fri, 24 Oct 2025 20:30:44 +0530 Subject: [PATCH 2/7] Support git::PullRebase action --- crates/fs/src/fake_git_repo.rs | 1 + crates/git/src/git.rs | 2 + crates/git/src/repository.rs | 3 ++ crates/git_ui/src/git_panel.rs | 3 +- crates/git_ui/src/git_ui.rs | 10 +++- crates/project/src/git_store.rs | 91 ++++++++++++++++++--------------- 6 files changed, 66 insertions(+), 44 deletions(-) diff --git a/crates/fs/src/fake_git_repo.rs b/crates/fs/src/fake_git_repo.rs index 8e9f8501dbcd48..1dca4f6552286d 100644 --- a/crates/fs/src/fake_git_repo.rs +++ b/crates/fs/src/fake_git_repo.rs @@ -530,6 +530,7 @@ impl GitRepository for FakeGitRepository { &self, _branch: String, _remote: String, + _rebase: bool, _askpass: AskPassDelegate, _env: Arc>, _cx: AsyncApp, diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index 29fa50ddd2bc2a..50a1e1234ba3ca 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -72,6 +72,8 @@ actions!( ForcePush, /// Pulls changes from the remote repository. Pull, + /// Pulls changes from the remote repository with rebase. + PullRebase, /// Fetches changes from the remote repository. Fetch, /// Fetches changes from a specific remote. diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 06bc5ec4114af0..221b03d41f1838 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -480,6 +480,7 @@ pub trait GitRepository: Send + Sync { &self, branch_name: String, upstream_name: String, + rebase: bool, askpass: AskPassDelegate, env: Arc>, // This method takes an AsyncApp to ensure it's invoked on the main thread, @@ -1578,6 +1579,7 @@ impl GitRepository for RealGitRepository { &self, branch_name: String, remote_name: String, + rebase: bool, ask_pass: AskPassDelegate, env: Arc>, cx: AsyncApp, @@ -1592,6 +1594,7 @@ impl GitRepository for RealGitRepository { .envs(env.iter()) .current_dir(&working_directory?) .args(["pull"]) + .arg(if rebase { "--rebase" } else { "--no-rebase" }) .arg(remote_name) .arg(branch_name) .stdout(smol::process::Stdio::piped()) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 9ff8602a18fd1a..6891a46ce2bc50 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -2211,7 +2211,7 @@ impl GitPanel { .detach(); } - pub(crate) fn pull(&mut self, window: &mut Window, cx: &mut Context) { + pub(crate) fn pull(&mut self, rebase: bool, window: &mut Window, cx: &mut Context) { if !self.can_push_and_pull(cx) { return; } @@ -2246,6 +2246,7 @@ impl GitPanel { repo.pull( branch.name().to_owned().into(), remote.name.clone(), + rebase, askpass, cx, ) diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index 919cdf154d438e..cf5334e3fe7b57 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -124,7 +124,15 @@ pub fn init(cx: &mut App) { return; }; panel.update(cx, |panel, cx| { - panel.pull(window, cx); + panel.pull(false, window, cx); + }); + }); + workspace.register_action(|workspace, _: &git::PullRebase, window, cx| { + let Some(panel) = workspace.panel::(cx) else { + return; + }; + panel.update(cx, |panel, cx| { + panel.pull(true, window, cx); }); }); } diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 736c96f34e171c..6b37a535724dae 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -1716,10 +1716,11 @@ impl GitStore { let branch_name = envelope.payload.branch_name.into(); let remote_name = envelope.payload.remote_name.into(); + let rebase = envelope.payload.rebase.into(); let remote_message = repository_handle .update(&mut cx, |repository_handle, cx| { - repository_handle.pull(branch_name, remote_name, askpass, cx) + repository_handle.pull(branch_name, remote_name, rebase, askpass, cx) })? .await??; @@ -4226,6 +4227,7 @@ impl Repository { &mut self, branch: SharedString, remote: SharedString, + rebase: bool, askpass: AskPassDelegate, _cx: &mut App, ) -> oneshot::Receiver> { @@ -4233,50 +4235,55 @@ impl Repository { let askpass_id = util::post_inc(&mut self.latest_askpass_id); let id = self.id; - self.send_job( - Some(format!("git pull {} {}", remote, branch).into()), - move |git_repo, cx| async move { - match git_repo { - RepositoryState::Local { - backend, - environment, - .. - } => { - backend - .pull( - branch.to_string(), - remote.to_string(), - askpass, - environment.clone(), - cx, - ) - .await - } - RepositoryState::Remote { project_id, client } => { - askpass_delegates.lock().insert(askpass_id, askpass); - let _defer = util::defer(|| { - let askpass_delegate = askpass_delegates.lock().remove(&askpass_id); - debug_assert!(askpass_delegate.is_some()); - }); - let response = client - .request(proto::Pull { - project_id: project_id.0, - repository_id: id.to_proto(), - askpass_id, - branch_name: branch.to_string(), - remote_name: remote.to_string(), - }) - .await - .context("sending pull request")?; + let status = if rebase { + Some(format!("git pull --rebase {} {}", remote, branch).into()) + } else { + Some(format!("git pull {} {}", remote, branch).into()) + }; - Ok(RemoteCommandOutput { - stdout: response.stdout, - stderr: response.stderr, + self.send_job(status, move |git_repo, cx| async move { + match git_repo { + RepositoryState::Local { + backend, + environment, + .. + } => { + backend + .pull( + branch.to_string(), + remote.to_string(), + rebase, + askpass, + environment.clone(), + cx, + ) + .await + } + RepositoryState::Remote { project_id, client } => { + askpass_delegates.lock().insert(askpass_id, askpass); + let _defer = util::defer(|| { + let askpass_delegate = askpass_delegates.lock().remove(&askpass_id); + debug_assert!(askpass_delegate.is_some()); + }); + let response = client + .request(proto::Pull { + project_id: project_id.0, + repository_id: id.to_proto(), + askpass_id, + rebase, + branch_name: branch.to_string(), + remote_name: remote.to_string(), }) - } + .await + .context("sending pull request")?; + + Ok(RemoteCommandOutput { + stdout: response.stdout, + stderr: response.stderr, + }) } - }, - ) + } + }) } fn spawn_set_index_text_job( From 3a3d0832e243098bedb3c237ceab6e6a5ab06ea6 Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Fri, 24 Oct 2025 20:42:04 +0530 Subject: [PATCH 3/7] Include Pull Rebase in Git Panel --- crates/git_ui/src/git_ui.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index cf5334e3fe7b57..bd5a849db8cd06 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -603,6 +603,7 @@ mod remote_button { .action("Fetch", git::Fetch.boxed_clone()) .action("Fetch From", git::FetchFrom.boxed_clone()) .action("Pull", git::Pull.boxed_clone()) + .action("Pull (Rebase)", git::PullRebase.boxed_clone()) .separator() .action("Push", git::Push.boxed_clone()) .action("Push To", git::PushTo.boxed_clone()) From 2044ecb9accd408b4e1b28e4cf00ba2813c33037 Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Fri, 24 Oct 2025 20:50:12 +0530 Subject: [PATCH 4/7] Update keybindings for pull-rebase --- assets/keymaps/default-linux.json | 1 + assets/keymaps/default-macos.json | 1 + assets/keymaps/default-windows.json | 1 + 3 files changed, 3 insertions(+) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 3d94edafcdfc1d..b0e3a0ffe15f7f 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -933,6 +933,7 @@ "ctrl-g ctrl-g": "git::Fetch", "ctrl-g up": "git::Push", "ctrl-g down": "git::Pull", + "ctrl-g shift-down": "git::PullRebase", "ctrl-g shift-up": "git::ForcePush", "ctrl-g d": "git::Diff", "ctrl-g backspace": "git::RestoreTrackedFiles", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 6c3f47cb45909c..6ae174f947259d 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -1026,6 +1026,7 @@ "ctrl-g ctrl-g": "git::Fetch", "ctrl-g up": "git::Push", "ctrl-g down": "git::Pull", + "ctrl-g shift-down": "git::PullRebase", "ctrl-g shift-up": "git::ForcePush", "ctrl-g d": "git::Diff", "ctrl-g backspace": "git::RestoreTrackedFiles", diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index 5b96d20633b573..a92c1af823f061 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -943,6 +943,7 @@ "ctrl-g ctrl-g": "git::Fetch", "ctrl-g up": "git::Push", "ctrl-g down": "git::Pull", + "ctrl-g shift-down": "git::PullRebase", "ctrl-g shift-up": "git::ForcePush", "ctrl-g d": "git::Diff", "ctrl-g backspace": "git::RestoreTrackedFiles", From 106c8b8c2caff0c9e24a429c6f7d702f74fe8226 Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Fri, 24 Oct 2025 22:16:41 +0530 Subject: [PATCH 5/7] Update Git Docs --- docs/src/git.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/git.md b/docs/src/git.md index d56de998c9d143..85781e37bc628a 100644 --- a/docs/src/git.md +++ b/docs/src/git.md @@ -179,6 +179,7 @@ When viewing files with changes, Zed displays diff hunks that can be expanded or | {#action git::Push} | {#kb git::Push} | | {#action git::ForcePush} | {#kb git::ForcePush} | | {#action git::Pull} | {#kb git::Pull} | +| {#action git::PullRebase} | {#kb git::PullRebase} | | {#action git::Fetch} | {#kb git::Fetch} | | {#action git::Diff} | {#kb git::Diff} | | {#action git::Restore} | {#kb git::Restore} | From 4334ab6ac28dfa193bf5b874c1bb9b662d332235 Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Tue, 28 Oct 2025 10:25:13 +0530 Subject: [PATCH 6/7] Address PR comment --- crates/git/src/repository.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 221b03d41f1838..e6a2b43bcc0fad 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -1593,8 +1593,13 @@ impl GitRepository for RealGitRepository { command .envs(env.iter()) .current_dir(&working_directory?) - .args(["pull"]) - .arg(if rebase { "--rebase" } else { "--no-rebase" }) + .arg("pull"); + + if rebase { + command.arg("--rebase"); + } + + command .arg(remote_name) .arg(branch_name) .stdout(smol::process::Stdio::piped()) From 59bdf1de5dd78ee7633ed589219f0693fb9df102 Mon Sep 17 00:00:00 2001 From: Sathiyaraman M Date: Tue, 4 Nov 2025 13:09:15 +0530 Subject: [PATCH 7/7] Fix clippy issues --- crates/project/src/git_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 6b37a535724dae..9496ac2d468643 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -1716,7 +1716,7 @@ impl GitStore { let branch_name = envelope.payload.branch_name.into(); let remote_name = envelope.payload.remote_name.into(); - let rebase = envelope.payload.rebase.into(); + let rebase = envelope.payload.rebase; let remote_message = repository_handle .update(&mut cx, |repository_handle, cx| {