diff --git a/po/POTFILES b/po/POTFILES
index 5b5c70006..1225a73ef 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -5,6 +5,7 @@ src/Dialogs/GlobalSearchDialog.vala
src/Dialogs/NewBranchDialog.vala
src/Dialogs/PreferencesDialog.vala
src/Dialogs/RestoreConfirmationDialog.vala
+src/Dialogs/OverwriteUncommittedConfirmationDialog.vala
src/FolderManager/File.vala
src/FolderManager/FileItem.vala
src/FolderManager/FileView.vala
diff --git a/src/Dialogs/OverwriteUncommittedConfirmationDialog.vala b/src/Dialogs/OverwriteUncommittedConfirmationDialog.vala
new file mode 100644
index 000000000..513040471
--- /dev/null
+++ b/src/Dialogs/OverwriteUncommittedConfirmationDialog.vala
@@ -0,0 +1,49 @@
+/*
+* Copyright 2025 elementary, Inc. (https://elementary.io)
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 3 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+* Boston, MA 02110-1301 USA
+*/
+
+public class Scratch.Dialogs.OverwriteUncommittedConfirmationDialog : Granite.MessageDialog {
+
+ public string branch_name { get; construct; }
+ public OverwriteUncommittedConfirmationDialog (
+ Gtk.Window parent,
+ string new_branch_name,
+ string details
+ ) {
+ Object (
+ buttons: Gtk.ButtonsType.NONE,
+ transient_for: parent,
+ branch_name: new_branch_name
+ );
+
+ show_error_details (details);
+ }
+
+ construct {
+ modal = true;
+ image_icon = new ThemedIcon ("dialog-stop");
+
+ primary_text = _("There are uncommitted changes in the current branch");
+ ///TRANSLATORS '%s' is a placeholder for the name of the branch to be checked out
+ secondary_text = _("Uncommitted changes will be permanently lost if '%s' is checked out now.\n\nIt is recommended that uncommitted changes are stashed, committed, or reverted before proceeding ").printf (branch_name);
+
+ var proceed_button = (Gtk.Button) add_button (_("Checkout and Overwrite"), Gtk.ResponseType.ACCEPT);
+ proceed_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION);
+ var cancel_button = add_button (_("Do not Checkout"), Gtk.ResponseType.REJECT);
+ cancel_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
+ }
+}
diff --git a/src/FolderManager/ProjectFolderItem.vala b/src/FolderManager/ProjectFolderItem.vala
index 9998db6be..fd9dbaa71 100644
--- a/src/FolderManager/ProjectFolderItem.vala
+++ b/src/FolderManager/ProjectFolderItem.vala
@@ -349,7 +349,7 @@ namespace Scratch.FolderManager {
private void handle_change_branch_action (GLib.Variant? parameter) {
var branch_name = parameter.get_string ();
try {
- monitored_repo.change_branch (branch_name);
+ monitored_repo.change_local_branch (branch_name);
} catch (GLib.Error e) {
warning ("Failed to change branch to %s. %s", branch_name, e.message);
}
diff --git a/src/Services/MonitoredRepository.vala b/src/Services/MonitoredRepository.vala
index 0bf8d8c45..9563a0070 100644
--- a/src/Services/MonitoredRepository.vala
+++ b/src/Services/MonitoredRepository.vala
@@ -186,9 +186,18 @@ namespace Scratch.Services {
return true;
}
- public void change_branch (string new_branch_name) throws Error {
+ public void change_local_branch (string new_branch_name) throws Error {
var branch = git_repo.lookup_branch (new_branch_name, Ggit.BranchType.LOCAL);
- git_repo.set_head (((Ggit.Ref)branch).get_name ());
+ checkout_branch (branch);
+ }
+
+ private void checkout_branch (Ggit.Branch new_head_branch, bool confirm = true) throws Error {
+ if (confirm && has_uncommitted) {
+ confirm_checkout_branch (new_head_branch);
+ return;
+ }
+
+ git_repo.set_head (((Ggit.Ref) new_head_branch).get_name ());
var options = new Ggit.CheckoutOptions () {
//Ensure documents match checked out branch (deal with potential conflicts/losses beforehand)
strategy = Ggit.CheckoutStrategy.FORCE
@@ -196,7 +205,24 @@ namespace Scratch.Services {
git_repo.checkout_head (options);
- branch_name = new_branch_name;
+ branch_name = new_head_branch.get_name ();
+ }
+
+ private void confirm_checkout_branch (Ggit.Branch new_head_branch) {
+ var parent = ((Gtk.Application)(GLib.Application.get_default ())).get_active_window ();
+ var dialog = new Scratch.Dialogs.OverwriteUncommittedConfirmationDialog (
+ parent,
+ new_head_branch.get_name (),
+ get_project_diff ()
+ );
+ dialog.response.connect ((res) => {
+ dialog.destroy ();
+ if (res == Gtk.ResponseType.ACCEPT) {
+ checkout_branch (new_head_branch, false);
+ }
+ });
+
+ dialog.present ();
}
public void create_new_branch (string name) throws Error {
@@ -264,6 +290,41 @@ namespace Scratch.Services {
return git_repo.path_is_ignored (path);
}
+ public string get_project_diff () throws GLib.Error {
+ var sb = new StringBuilder ("");
+ var repo_diff_list = new Ggit.Diff.index_to_workdir (git_repo, null, null);
+ repo_diff_list.print (Ggit.DiffFormatType.PATCH, (delta, hunk, line) => {
+ unowned var file_diff = delta.get_old_file ();
+ if (file_diff == null) {
+ return 0;
+ }
+
+ if (line != null) {
+ var delta_type = line.get_origin ();
+ string prefix = "?";
+ switch (delta_type) {
+ case Ggit.DiffLineType.ADDITION:
+ prefix = "+";
+ break;
+ case Ggit.DiffLineType.DELETION:
+ prefix = "-";
+ break;
+ case Ggit.DiffLineType.CONTEXT:
+ prefix = " ";
+ break;
+ default:
+ break;
+ }
+ //TODO Add color according to linetype
+ sb.append (prefix + line.get_text ());
+ }
+ return 0;
+ });
+
+ return sb.str;
+ }
+
+
private bool refreshing = false;
public void refresh_diff (string file_path, ref Gee.HashMap line_status_map) {
if (refreshing) {
diff --git a/src/meson.build b/src/meson.build
index 08497a110..a62845ccd 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,6 +20,7 @@ code_files = files(
'Utils.vala',
'Dialogs/PreferencesDialog.vala',
'Dialogs/RestoreConfirmationDialog.vala',
+ 'Dialogs/OverwriteUncommittedConfirmationDialog.vala',
'Dialogs/GlobalSearchDialog.vala',
'Dialogs/NewBranchDialog.vala',
'FolderManager/File.vala',