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',