From 935fa385e96c5507db54b2917ccaa63741e9098e Mon Sep 17 00:00:00 2001 From: SalOrak <99770300+SalOrak@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:47:53 +0200 Subject: [PATCH 1/2] feat(remote): ability to rename & delete using picker. Added show option. Adds the ability to rename and delete upon selecting a remote instead of typing down the remote name. It also adds an extra option to show (in a picker manner) the available remotes. Selecting one does not do anything --- src/default_config.toml | 1 + src/git/mod.rs | 10 +++++++ src/ops/mod.rs | 2 ++ src/ops/remote.rs | 66 +++++++++++++++++++++++++++++++++-------- src/picker.rs | 15 ++++++++++ 5 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/default_config.toml b/src/default_config.toml index d95974629d..7e45410f1d 100644 --- a/src/default_config.toml +++ b/src/default_config.toml @@ -223,6 +223,7 @@ root.remote_menu=["M"] remote_menu.add_remote=["a"] remote_menu.remove_remote=["K"] remote_menu.rename_remote=["r"] +remote_menu.show_remote=["l"] remote_menu.quit = ["q", "esc"] root.reset_menu = ["X"] diff --git a/src/git/mod.rs b/src/git/mod.rs index 5607ee6410..5f408c7d07 100644 --- a/src/git/mod.rs +++ b/src/git/mod.rs @@ -576,6 +576,16 @@ pub(crate) fn branches(repo: &git2::Repository, filter: Option .collect()) } +pub(crate) fn remotes(repo: &git2::Repository) -> Res> { + Ok(repo + .remotes() + .map_err(Error::ListGitReferences)? + .into_iter() + .flatten() + .map(|remote_name| Ref::Remote(remote_name.to_string())) + .collect()) +} + pub(crate) fn tags(repo: &git2::Repository) -> Res> { Ok(repo .tag_names(None) diff --git a/src/ops/mod.rs b/src/ops/mod.rs index 37fedd322a..f4edc511ac 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -78,6 +78,7 @@ pub(crate) enum Op { RemoveRemote, Rename, RenameRemote, + ShowRemote, ShowRefs, Stash, StashApply, @@ -218,6 +219,7 @@ impl Op { Op::AddRemote => Box::new(remote::AddRemote), Op::RemoveRemote => Box::new(remote::RemoveRemote), Op::RenameRemote => Box::new(remote::RenameRemote), + Op::ShowRemote => Box::new(remote::ShowRemote), Op::Merge => Box::new(merge::Merge), Op::MergeAbort => Box::new(merge::MergeAbort), diff --git a/src/ops/remote.rs b/src/ops/remote.rs index 006c0b2f0e..5f69750393 100644 --- a/src/ops/remote.rs +++ b/src/ops/remote.rs @@ -3,7 +3,9 @@ use std::{process::Command, rc::Rc}; use crate::{ Res, app::{App, PromptParams, State}, + git, item_data::ItemData, + picker::{PickerParams, PickerState}, term::Term, }; @@ -44,12 +46,15 @@ pub(crate) struct RenameRemote; impl OpTrait for RenameRemote { fn get_action(&self, _target: &ItemData) -> Option { Some(Rc::new(|app: &mut App, term: &mut Term| { - let remote_name = app.prompt( + let remote_name = app.pick( term, - &PromptParams { - prompt: "Rename remote", - ..Default::default() - }, + PickerState::with_remotes(PickerParams { + prompt: "Choose remote to rename".into(), + refs: &git::remotes(&app.state.repo)?, + exclude_ref: None, + default: None, + allow_custom_input: false, + }), )?; let new_remote_name = app.prompt( @@ -60,7 +65,10 @@ impl OpTrait for RenameRemote { }, )?; - rename_remote(app, term, &remote_name, &new_remote_name)?; + if let Some(result) = remote_name { + rename_remote(app, term, result.display(), &new_remote_name)?; + } + Ok(()) })) } @@ -70,20 +78,52 @@ impl OpTrait for RenameRemote { } } +pub(crate) struct ShowRemote; +impl OpTrait for ShowRemote { + fn get_action(&self, _target: &ItemData) -> Option { + Some(Rc::new(|app: &mut App, term: &mut Term| { + let _ = app.pick( + term, + PickerState::with_remotes(PickerParams { + prompt: "Remotes".into(), + refs: &git::remotes(&app.state.repo)?, + exclude_ref: None, + default: None, + allow_custom_input: false, + }), + )?; + + Ok(()) + })) + } + + fn display(&self, _state: &State) -> String { + "show remotes".to_string() + } +} + pub(crate) struct RemoveRemote; impl OpTrait for RemoveRemote { fn get_action(&self, _target: &ItemData) -> Option { Some(Rc::new(|app: &mut App, term: &mut Term| { - let remote_name = app.prompt( + let result = app.pick( term, - &PromptParams { - prompt: "Delete remote", - ..Default::default() - }, + PickerState::with_remotes(PickerParams { + prompt: "Remotes".into(), + refs: &git::remotes(&app.state.repo)?, + exclude_ref: None, + default: None, + allow_custom_input: false, + }), )?; - app.confirm(term, "Really delete remote (y or n)")?; - remove_remote(app, term, &remote_name)?; + if let Some(remote_name) = result { + // TODO: Would be nice to show which remote is acting upon. + // But app.confirm expects an 'static str which complicates things a bit. + app.confirm(term, "Really delete remote (y or n) >")?; + remove_remote(app, term, remote_name.display())?; + return Ok(()); + } Ok(()) })) diff --git a/src/picker.rs b/src/picker.rs index f01dbc0b6e..325d78465b 100644 --- a/src/picker.rs +++ b/src/picker.rs @@ -111,6 +111,21 @@ impl PickerState { state } + pub(crate) fn with_remotes(params: PickerParams) -> Self { + let items = params + .refs + .iter() + .map(|name| { + PickerItem::new( + name.shorthand().to_string(), + PickerData::Item(name.shorthand().to_string().clone()), + ) + }) + .collect(); + + Self::new(params.prompt, items, params.allow_custom_input) + } + /// Create a picker showing only local branches by shorthand name. /// The default branch (if any) is listed first. The exclude_ref is omitted. pub(crate) fn with_branches(params: PickerParams) -> Self { From 9437a11ddc8d7143ae38fee8fc664bede9ad31b0 Mon Sep 17 00:00:00 2001 From: SalOrak <99770300+SalOrak@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:13:11 +0200 Subject: [PATCH 2/2] test: fixed & update snapshots --- src/tests/remote.rs | 2 +- src/tests/snapshots/gitu__tests__remote__remote_menu.snap | 4 ++-- .../gitu__tests__remote__rename_remote_name_prompt.snap | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/remote.rs b/src/tests/remote.rs index 0ac14f5631..bead0a738c 100644 --- a/src/tests/remote.rs +++ b/src/tests/remote.rs @@ -27,7 +27,7 @@ fn add_remote() { #[test] fn rename_remote_name_prompt() { - snapshot!(setup_clone!(), "Mr"); + snapshot!(setup_clone!(), "Mrorigin"); } #[test] diff --git a/src/tests/snapshots/gitu__tests__remote__remote_menu.snap b/src/tests/snapshots/gitu__tests__remote__remote_menu.snap index e19b759845..c28fa564ac 100644 --- a/src/tests/snapshots/gitu__tests__remote__remote_menu.snap +++ b/src/tests/snapshots/gitu__tests__remote__remote_menu.snap @@ -15,11 +15,11 @@ expression: ctx.redact_buffer() | | | - | ────────────────────────────────────────────────────────────────────────────────| Remote | a add remote | K remove remote | r rename remote | + l show remotes | q/esc Quit/Close | -styles_hash: f0b3d15664a45e6 +styles_hash: eead60180401b8f7 diff --git a/src/tests/snapshots/gitu__tests__remote__rename_remote_name_prompt.snap b/src/tests/snapshots/gitu__tests__remote__rename_remote_name_prompt.snap index 2578f8c8fb..6b2e9d8e23 100644 --- a/src/tests/snapshots/gitu__tests__remote__rename_remote_name_prompt.snap +++ b/src/tests/snapshots/gitu__tests__remote__rename_remote_name_prompt.snap @@ -21,5 +21,5 @@ expression: ctx.redact_buffer() | | ────────────────────────────────────────────────────────────────────────────────| -? Rename remote: › █ | -styles_hash: 802b36a3883cd0c0 +? Rename to: › █ | +styles_hash: d234beab6b72dd64