diff --git a/CHANGELOG.md b/CHANGELOG.md
index 012b60e5ff..911355a72e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## Unreleased
 
 ### Added
+* support choosing checkout branch method when status is not empty [[@fatpandac](https://github.com/fatpandac)] ([#2404](https://github.com/extrawurst/gitui/issues/2404))
 * support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565))
 * Select syntax highlighting theme out of the defaults from syntect [[@vasilismanol](https://github.com/vasilismanol)] ([#1931](https://github.com/extrawurst/gitui/issues/1931))
 * new command-line option to override the default log file path (`--logfile`) [[@acuteenvy](https://github.com/acuteenvy)] ([#2539](https://github.com/gitui-org/gitui/pull/2539))
diff --git a/asyncgit/src/sync/status.rs b/asyncgit/src/sync/status.rs
index 1cd5bcc847..11a80b1409 100644
--- a/asyncgit/src/sync/status.rs
+++ b/asyncgit/src/sync/status.rs
@@ -195,3 +195,46 @@ pub fn get_status(
 
 	Ok(res)
 }
+
+/// discard all changes in the working directory
+pub fn discard_status(repo_path: &RepoPath) -> Result<bool> {
+	let repo = repo(repo_path)?;
+	let commit = repo.head()?.peel_to_commit()?;
+
+	repo.reset(commit.as_object(), git2::ResetType::Hard, None)?;
+
+	Ok(true)
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use crate::sync::tests::repo_init;
+	use std::{fs::File, io::Write, path::Path};
+
+	#[test]
+	fn test_discard_status() {
+		let file_path = Path::new("foo");
+		let (_td, repo) = repo_init().unwrap();
+		let root = repo.path().parent().unwrap();
+		let repo_path: &RepoPath =
+			&root.as_os_str().to_str().unwrap().into();
+
+		File::create(root.join(file_path))
+			.unwrap()
+			.write_all(b"test\nfoo")
+			.unwrap();
+
+		let statuses =
+			get_status(repo_path, StatusType::WorkingDir, None)
+				.unwrap();
+		assert_eq!(statuses.len(), 1);
+
+		discard_status(repo_path).unwrap();
+
+		let statuses =
+			get_status(repo_path, StatusType::WorkingDir, None)
+				.unwrap();
+		assert_eq!(statuses.len(), 0);
+	}
+}
diff --git a/src/app.rs b/src/app.rs
index 45037f048f..89e87b4633 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -10,16 +10,16 @@ use crate::{
 	options::{Options, SharedOptions},
 	popup_stack::PopupStack,
 	popups::{
-		AppOption, BlameFilePopup, BranchListPopup, CommitPopup,
-		CompareCommitsPopup, ConfirmPopup, CreateBranchPopup,
-		CreateRemotePopup, ExternalEditorPopup, FetchPopup,
-		FileRevlogPopup, FuzzyFindPopup, HelpPopup,
-		InspectCommitPopup, LogSearchPopupPopup, MsgPopup,
-		OptionsPopup, PullPopup, PushPopup, PushTagsPopup,
-		RemoteListPopup, RenameBranchPopup, RenameRemotePopup,
-		ResetPopup, RevisionFilesPopup, StashMsgPopup,
-		SubmodulesListPopup, TagCommitPopup, TagListPopup,
-		UpdateRemoteUrlPopup,
+		AppOption, BlameFilePopup, BranchListPopup,
+		CheckoutOptionPopup, CommitPopup, CompareCommitsPopup,
+		ConfirmPopup, CreateBranchPopup, CreateRemotePopup,
+		ExternalEditorPopup, FetchPopup, FileRevlogPopup,
+		FuzzyFindPopup, HelpPopup, InspectCommitPopup,
+		LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup,
+		PushPopup, PushTagsPopup, RemoteListPopup, RenameBranchPopup,
+		RenameRemotePopup, ResetPopup, RevisionFilesPopup,
+		StashMsgPopup, SubmodulesListPopup, TagCommitPopup,
+		TagListPopup, UpdateRemoteUrlPopup,
 	},
 	queue::{
 		Action, AppTabs, InternalEvent, NeedsUpdate, Queue,
@@ -98,6 +98,7 @@ pub struct App {
 	submodule_popup: SubmodulesListPopup,
 	tags_popup: TagListPopup,
 	reset_popup: ResetPopup,
+	checkout_option_popup: CheckoutOptionPopup,
 	cmdbar: RefCell<CommandBar>,
 	tab: usize,
 	revlog: Revlog,
@@ -218,6 +219,7 @@ impl App {
 			stashing_tab: Stashing::new(&env),
 			stashlist_tab: StashList::new(&env),
 			files_tab: FilesTab::new(&env),
+			checkout_option_popup: CheckoutOptionPopup::new(&env),
 			tab: 0,
 			queue: env.queue,
 			theme: env.theme,
@@ -493,6 +495,7 @@ impl App {
 			fetch_popup,
 			tag_commit_popup,
 			reset_popup,
+			checkout_option_popup,
 			create_branch_popup,
 			create_remote_popup,
 			rename_remote_popup,
@@ -533,6 +536,7 @@ impl App {
 			submodule_popup,
 			tags_popup,
 			reset_popup,
+			checkout_option_popup,
 			create_branch_popup,
 			rename_branch_popup,
 			revision_files_popup,
@@ -905,6 +909,9 @@ impl App {
 			InternalEvent::CommitSearch(options) => {
 				self.revlog.search(options);
 			}
+			InternalEvent::CheckoutOption(branch, is_local) => {
+				self.checkout_option_popup.open(branch, is_local)?;
+			}
 		};
 
 		Ok(flags)
diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs
index 9eb5c57e09..2bcf108417 100644
--- a/src/popups/branchlist.rs
+++ b/src/popups/branchlist.rs
@@ -20,8 +20,9 @@ use asyncgit::{
 			checkout_remote_branch, BranchDetails, LocalBranch,
 			RemoteBranch,
 		},
-		checkout_branch, get_branches_info, BranchInfo, BranchType,
-		CommitId, RepoPathRef, RepoState,
+		checkout_branch, get_branches_info,
+		status::StatusType,
+		BranchInfo, BranchType, CommitId, RepoPathRef, RepoState,
 	},
 	AsyncGitNotification,
 };
@@ -582,23 +583,37 @@ impl BranchListPopup {
 			anyhow::bail!("no valid branch selected");
 		}
 
-		if self.local {
-			checkout_branch(
-				&self.repo.borrow(),
-				&self.branches[self.selection as usize].name,
-			)?;
-			self.hide();
+		let status = sync::status::get_status(
+			&self.repo.borrow(),
+			StatusType::WorkingDir,
+			None,
+		)
+		.unwrap();
+
+		let selected_branch = &self.branches[self.selection as usize];
+		if status.is_empty() {
+			if self.local {
+				checkout_branch(
+					&self.repo.borrow(),
+					&selected_branch.name,
+				)?;
+				self.hide();
+			} else {
+				checkout_remote_branch(
+					&self.repo.borrow(),
+					&selected_branch,
+				)?;
+				self.local = true;
+				self.update_branches()?;
+			}
+			self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
 		} else {
-			checkout_remote_branch(
-				&self.repo.borrow(),
-				&self.branches[self.selection as usize],
-			)?;
-			self.local = true;
-			self.update_branches()?;
+			self.queue.push(InternalEvent::CheckoutOption(
+				selected_branch.clone(),
+				self.local.clone(),
+			));
 		}
 
-		self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
-
 		Ok(())
 	}
 
diff --git a/src/popups/checkout_option.rs b/src/popups/checkout_option.rs
new file mode 100644
index 0000000000..9c40322a4a
--- /dev/null
+++ b/src/popups/checkout_option.rs
@@ -0,0 +1,275 @@
+use crate::components::{
+	visibility_blocking, CommandBlocking, CommandInfo, Component,
+	DrawableComponent, EventState,
+};
+use crate::queue::{InternalEvent, NeedsUpdate};
+use crate::strings::{checkout_option_to_string, CheckoutOptions};
+use crate::try_or_popup;
+use crate::{
+	app::Environment,
+	keys::{key_match, SharedKeyConfig},
+	queue::Queue,
+	strings,
+	ui::{self, style::SharedTheme},
+};
+use anyhow::{Ok, Result};
+use asyncgit::sync::branch::checkout_remote_branch;
+use asyncgit::sync::status::discard_status;
+use asyncgit::sync::{
+	checkout_branch, stash_pop, stash_save, BranchInfo, RepoPath,
+};
+use crossterm::event::Event;
+use ratatui::{
+	layout::{Alignment, Rect},
+	text::{Line, Span},
+	widgets::{Block, Borders, Clear, Paragraph},
+	Frame,
+};
+
+pub struct CheckoutOptionPopup {
+	queue: Queue,
+	repo: RepoPath,
+	local: bool,
+	branch: Option<BranchInfo>,
+	option: CheckoutOptions,
+	visible: bool,
+	key_config: SharedKeyConfig,
+	theme: SharedTheme,
+}
+
+impl CheckoutOptionPopup {
+	///
+	pub fn new(env: &Environment) -> Self {
+		Self {
+			queue: env.queue.clone(),
+			repo: env.repo.borrow().clone(),
+			local: false,
+			branch: None,
+			option: CheckoutOptions::StashAndReapply,
+			visible: false,
+			key_config: env.key_config.clone(),
+			theme: env.theme.clone(),
+		}
+	}
+
+	fn get_text(&self, _width: u16) -> Vec<Line> {
+		let mut txt: Vec<Line> = Vec::with_capacity(10);
+
+		txt.push(Line::from(vec![
+			Span::styled(
+				String::from("Switch to: "),
+				self.theme.text(true, false),
+			),
+			Span::styled(
+				self.branch.as_ref().unwrap().name.clone(),
+				self.theme.commit_hash(false),
+			),
+		]));
+
+		let (kind_name, kind_desc) =
+			checkout_option_to_string(self.option);
+
+		txt.push(Line::from(vec![
+			Span::styled(
+				String::from("How: "),
+				self.theme.text(true, false),
+			),
+			Span::styled(kind_name, self.theme.text(true, true)),
+			Span::styled(kind_desc, self.theme.text(true, false)),
+		]));
+
+		txt
+	}
+
+	///
+	pub fn open(
+		&mut self,
+		branch: BranchInfo,
+		is_local: bool,
+	) -> Result<()> {
+		self.show()?;
+
+		self.branch = Some(branch);
+		self.local = is_local;
+
+		Ok(())
+	}
+
+	fn checkout(&self) -> Result<()> {
+		if self.local {
+			checkout_branch(
+				&self.repo,
+				&self.branch.as_ref().unwrap().name,
+			)?
+		} else {
+			checkout_remote_branch(
+				&self.repo,
+				&self.branch.as_ref().unwrap(),
+			)?;
+		}
+
+		Ok(())
+	}
+
+	fn handle_event(&mut self) -> Result<()> {
+		match self.option {
+			CheckoutOptions::StashAndReapply => {
+				let stash_id =
+					stash_save(&self.repo, None, true, false)?;
+				self.checkout()?;
+				stash_pop(&self.repo, stash_id)?;
+			}
+			CheckoutOptions::Unchange => {
+				self.checkout()?;
+			}
+			CheckoutOptions::Discard => {
+				discard_status(&self.repo)?;
+				self.checkout()?;
+			}
+		}
+
+		self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
+		self.queue.push(InternalEvent::SelectBranch);
+		self.hide();
+
+		Ok(())
+	}
+
+	fn change_kind(&mut self, incr: bool) {
+		self.option = if incr {
+			match self.option {
+				CheckoutOptions::StashAndReapply => {
+					CheckoutOptions::Unchange
+				}
+				CheckoutOptions::Unchange => CheckoutOptions::Discard,
+				CheckoutOptions::Discard => {
+					CheckoutOptions::StashAndReapply
+				}
+			}
+		} else {
+			match self.option {
+				CheckoutOptions::StashAndReapply => {
+					CheckoutOptions::Discard
+				}
+				CheckoutOptions::Unchange => {
+					CheckoutOptions::StashAndReapply
+				}
+				CheckoutOptions::Discard => CheckoutOptions::Unchange,
+			}
+		};
+	}
+}
+
+impl DrawableComponent for CheckoutOptionPopup {
+	fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
+		if self.is_visible() {
+			const SIZE: (u16, u16) = (55, 4);
+			let area =
+				ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
+
+			let width = area.width;
+
+			f.render_widget(Clear, area);
+			f.render_widget(
+				Paragraph::new(self.get_text(width))
+					.block(
+						Block::default()
+							.borders(Borders::ALL)
+							.title(Span::styled(
+								"Checkout options",
+								self.theme.title(true),
+							))
+							.border_style(self.theme.block(true)),
+					)
+					.alignment(Alignment::Left),
+				area,
+			);
+		}
+
+		Ok(())
+	}
+}
+
+impl Component for CheckoutOptionPopup {
+	fn commands(
+		&self,
+		out: &mut Vec<CommandInfo>,
+		force_all: bool,
+	) -> CommandBlocking {
+		if self.is_visible() || force_all {
+			out.push(
+				CommandInfo::new(
+					strings::commands::close_popup(&self.key_config),
+					true,
+					true,
+				)
+				.order(1),
+			);
+
+			out.push(
+				CommandInfo::new(
+					strings::commands::reset_commit(&self.key_config),
+					true,
+					true,
+				)
+				.order(1),
+			);
+
+			out.push(
+				CommandInfo::new(
+					strings::commands::reset_type(&self.key_config),
+					true,
+					true,
+				)
+				.order(1),
+			);
+		}
+
+		visibility_blocking(self)
+	}
+
+	fn event(
+		&mut self,
+		event: &crossterm::event::Event,
+	) -> Result<EventState> {
+		if self.is_visible() {
+			if let Event::Key(key) = &event {
+				if key_match(key, self.key_config.keys.exit_popup) {
+					self.hide();
+				} else if key_match(
+					key,
+					self.key_config.keys.move_down,
+				) {
+					self.change_kind(true);
+				} else if key_match(key, self.key_config.keys.move_up)
+				{
+					self.change_kind(false);
+				} else if key_match(key, self.key_config.keys.enter) {
+					try_or_popup!(
+						self,
+						"checkout error:",
+						self.handle_event()
+					);
+				}
+			}
+
+			return Ok(EventState::Consumed);
+		}
+
+		Ok(EventState::NotConsumed)
+	}
+
+	fn is_visible(&self) -> bool {
+		self.visible
+	}
+
+	fn hide(&mut self) {
+		self.visible = false;
+	}
+
+	fn show(&mut self) -> Result<()> {
+		self.visible = true;
+
+		Ok(())
+	}
+}
diff --git a/src/popups/mod.rs b/src/popups/mod.rs
index cb3ae1af74..f47f1e61ef 100644
--- a/src/popups/mod.rs
+++ b/src/popups/mod.rs
@@ -1,5 +1,6 @@
 mod blame_file;
 mod branchlist;
+mod checkout_option;
 mod commit;
 mod compare_commits;
 mod confirm;
@@ -30,6 +31,7 @@ mod update_remote_url;
 
 pub use blame_file::{BlameFileOpen, BlameFilePopup};
 pub use branchlist::BranchListPopup;
+pub use checkout_option::CheckoutOptionPopup;
 pub use commit::CommitPopup;
 pub use compare_commits::CompareCommitsPopup;
 pub use confirm::ConfirmPopup;
diff --git a/src/queue.rs b/src/queue.rs
index 44268a851d..fcbb6ddfb4 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -8,7 +8,8 @@ use crate::{
 };
 use asyncgit::{
 	sync::{
-		diff::DiffLinePosition, CommitId, LogFilterSearchOptions,
+		diff::DiffLinePosition, BranchInfo, CommitId,
+		LogFilterSearchOptions,
 	},
 	PushType,
 };
@@ -157,6 +158,8 @@ pub enum InternalEvent {
 	RewordCommit(CommitId),
 	///
 	CommitSearch(LogFilterSearchOptions),
+	///
+	CheckoutOption(BranchInfo, bool),
 }
 
 /// single threaded simple queue for components to communicate with each other
diff --git a/src/strings.rs b/src/strings.rs
index c4cff10f70..af26af5907 100644
--- a/src/strings.rs
+++ b/src/strings.rs
@@ -438,6 +438,35 @@ pub fn ellipsis_trim_start(s: &str, width: usize) -> Cow<str> {
 	}
 }
 
+#[derive(PartialEq, Clone, Copy)]
+pub enum CheckoutOptions {
+	StashAndReapply,
+	Unchange,
+	Discard,
+}
+
+pub fn checkout_option_to_string(
+	kind: CheckoutOptions,
+) -> (&'static str, &'static str) {
+	const CHECKOUT_OPTION_STASH_AND_REAPPLY: &str =
+		" 🟢 Stash and reapply changes";
+	const CHECKOUT_OPTION_UNCHANGE: &str = " 🟡 Keep local changes";
+	const CHECKOUT_OPTION_DISCARD: &str =
+		" 🔴 Discard all local changes";
+
+	match kind {
+		CheckoutOptions::StashAndReapply => {
+			("Stash and reapply", CHECKOUT_OPTION_STASH_AND_REAPPLY)
+		}
+		CheckoutOptions::Unchange => {
+			("Don't change", CHECKOUT_OPTION_UNCHANGE)
+		}
+		CheckoutOptions::Discard => {
+			("Discard", CHECKOUT_OPTION_DISCARD)
+		}
+	}
+}
+
 pub mod commit {
 	use crate::keys::SharedKeyConfig;