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", 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..e6a2b43bcc0fad 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, @@ -1591,7 +1593,13 @@ impl GitRepository for RealGitRepository { command .envs(env.iter()) .current_dir(&working_directory?) - .args(["pull"]) + .arg("pull"); + + if rebase { + command.arg("--rebase"); + } + + command .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..bd5a849db8cd06 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); }); }); } @@ -595,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()) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 736c96f34e171c..9496ac2d468643 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; 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( 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 { 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} |