From 29ce95e81e54b05b2ac63c0d5baeef1a1c3fe48f Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Jul 2024 14:38:09 +0800 Subject: [PATCH 01/33] enhance: new regex for tag name (#290) * `(?!\.)` should not starts with `.` * `(?!/)` should not starts with `/` * `(?!.*\.$)` should not end with `.` * `(?!.*/$)` should not end with '/` * `(?!.*\.\.)` should not contains `..` --- src/ViewModels/CreateTag.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ViewModels/CreateTag.cs b/src/ViewModels/CreateTag.cs index 498218cb..7318e00e 100644 --- a/src/ViewModels/CreateTag.cs +++ b/src/ViewModels/CreateTag.cs @@ -12,7 +12,7 @@ namespace SourceGit.ViewModels } [Required(ErrorMessage = "Tag name is required!")] - [RegularExpression(@"^[^/]{1}[\w\-\./]*$", ErrorMessage = "Bad tag name format!")] + [RegularExpression(@"^(?!\.)(?!/)(?!.*\.$)(?!.*/$)(?!.*\.\.)[\w\-\./]+$", ErrorMessage = "Bad tag name format!")] [CustomValidation(typeof(CreateTag), nameof(ValidateTagName))] public string TagName { From efa02d9e96f68bc90648cdcf965528e37cd15528 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Jul 2024 15:07:41 +0800 Subject: [PATCH 02/33] ux: remove unnecessary controls --- src/ViewModels/CommitDetail.cs | 1 + src/Views/CommitDetail.axaml | 3 +- src/Views/FileHistories.axaml | 24 +++++++++------- src/Views/Histories.axaml | 48 ++++++++++++++++--------------- src/Views/InteractiveRebase.axaml | 11 ++++--- src/Views/LauncherPage.axaml | 2 +- src/Views/RevisionCompare.axaml | 3 +- 7 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index c4e7e8e4..5efd2c26 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -384,6 +384,7 @@ namespace SourceGit.ViewModels { _changes = null; FullMessage = string.Empty; + Changes = []; VisibleChanges = null; SelectedChanges = null; ViewRevisionFileContent = null; diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml index d2f467d7..76e33582 100644 --- a/src/Views/CommitDetail.axaml +++ b/src/Views/CommitDetail.axaml @@ -8,8 +8,7 @@ xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.CommitDetail" - x:DataType="vm:CommitDetail" - Background="{DynamicResource Brush.Window}"> + x:DataType="vm:CommitDetail"> diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index c97582bc..5ae1d15e 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -120,22 +120,26 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/> - - - - - - + + + + + + + + + + - + diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index a2fe5383..cec18992 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -174,15 +174,19 @@ Background="{DynamicResource Brush.Window}" BorderBrush="{DynamicResource Brush.Border0}"/> - - - - - - + + + + + + + + + + @@ -193,23 +197,21 @@ - - - + + - - - + + - + diff --git a/src/Views/InteractiveRebase.axaml b/src/Views/InteractiveRebase.axaml index d0a4cc33..5f8b4bce 100644 --- a/src/Views/InteractiveRebase.axaml +++ b/src/Views/InteractiveRebase.axaml @@ -260,15 +260,14 @@ BorderThickness="0,1,0,0" BorderBrush="{DynamicResource Brush.Border2}"/> - + - - - - + Fill="{DynamicResource Brush.FG2}" + IsVisible="{Binding SelectedItem, Converter={x:Static ObjectConverters.IsNull}}"/> + + diff --git a/src/Views/LauncherPage.axaml b/src/Views/LauncherPage.axaml index dda5b7ef..3d57947b 100644 --- a/src/Views/LauncherPage.axaml +++ b/src/Views/LauncherPage.axaml @@ -25,7 +25,7 @@ - + diff --git a/src/Views/RevisionCompare.axaml b/src/Views/RevisionCompare.axaml index 14ac66a7..9ba28b49 100644 --- a/src/Views/RevisionCompare.axaml +++ b/src/Views/RevisionCompare.axaml @@ -8,8 +8,7 @@ xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.RevisionCompare" - x:DataType="vm:RevisionCompare" - Background="{DynamicResource Brush.Window}"> + x:DataType="vm:RevisionCompare"> From 20744d09bd6054a89d0302d57823e2b1aedd2d5b Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Jul 2024 16:19:35 +0800 Subject: [PATCH 03/33] enhance: set `SuggestedStartLocation` to default clone dir when open local repository (#293) --- src/Views/WelcomeToolbar.axaml.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 597d964d..e0a6acfb 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -24,6 +24,12 @@ namespace SourceGit.Views return; var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + if (Directory.Exists(ViewModels.Preference.Instance.GitDefaultCloneDir)) + { + var folder = await topLevel.StorageProvider.TryGetFolderFromPathAsync(ViewModels.Preference.Instance.GitDefaultCloneDir); + options.SuggestedStartLocation = folder; + } + var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); if (selected.Count == 1) OpenOrInitRepository(selected[0].Path.LocalPath); From d2d95e09b78171484ee897176b72a095abd069b4 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Jul 2024 19:40:35 +0800 Subject: [PATCH 04/33] fix: NRE on commit filters --- src/Converters/StringConverters.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Converters/StringConverters.cs b/src/Converters/StringConverters.cs index a3e3bbba..585e0f02 100644 --- a/src/Converters/StringConverters.cs +++ b/src/Converters/StringConverters.cs @@ -87,6 +87,8 @@ namespace SourceGit.Converters public static readonly FuncValueConverter TrimRefsPrefix = new FuncValueConverter(v => { + if (v == null) + return string.Empty; if (v.StartsWith("refs/heads/", StringComparison.Ordinal)) return v.Substring(11); if (v.StartsWith("refs/remotes/", StringComparison.Ordinal)) From aea61e006764805c45d3ad07d38c3c8e54e07bc3 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Jul 2024 20:20:27 +0800 Subject: [PATCH 05/33] feature: add a toggle button in Preference dialog to only use monospace font in code editor (#292) --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Resources/Styles.axaml | 2 +- src/ViewModels/Preference.cs | 17 +++++++++++++++++ src/Views/Preference.axaml | 6 +++++- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 6e9ff911..2aa5eb5f 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -360,6 +360,7 @@ Default Font Default Font Size Monospace Font + Only use monospace font in text editor Theme Theme Overrides GENERAL diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 9e1794a9..ac777530 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -363,6 +363,7 @@ 缺省字体 默认字体大小 等宽字体 + 仅在文本编辑器中使用等宽字体 主题 主题自定义 通用配置 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index ee620cba..dad46505 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -363,6 +363,7 @@ 預設字型 預設字型大小 等寬字型 + 僅在文字編輯器中使用等寬字體 主題 主題自訂 通用配置 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 89df6a01..389bd138 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -259,7 +259,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Tue, 30 Jul 2024 21:32:23 +0800 Subject: [PATCH 12/33] feature: supports type change --- src/Commands/QueryLocalChanges.cs | 27 +++++++++++++++++++++++++++ src/Models/Change.cs | 1 + src/Views/ChangeStatusIcon.cs | 8 +++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index a9f777b1..d58f42f4 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -39,6 +39,9 @@ namespace SourceGit.Commands case " M": change.Set(Models.ChangeState.None, Models.ChangeState.Modified); break; + case " T": + change.Set(Models.ChangeState.None, Models.ChangeState.TypeChanged); + break; case " A": change.Set(Models.ChangeState.None, Models.ChangeState.Added); break; @@ -57,15 +60,33 @@ namespace SourceGit.Commands case "MM": change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified); break; + case "MT": + change.Set(Models.ChangeState.Modified, Models.ChangeState.TypeChanged); + break; case "MD": change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted); break; + case "T": + change.Set(Models.ChangeState.TypeChanged); + break; + case "TM": + change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Modified); + break; + case "TT": + change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.TypeChanged); + break; + case "TD": + change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Deleted); + break; case "A": change.Set(Models.ChangeState.Added); break; case "AM": change.Set(Models.ChangeState.Added, Models.ChangeState.Modified); break; + case "AT": + change.Set(Models.ChangeState.Added, Models.ChangeState.TypeChanged); + break; case "AD": change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted); break; @@ -78,6 +99,9 @@ namespace SourceGit.Commands case "RM": change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified); break; + case "RT": + change.Set(Models.ChangeState.Renamed, Models.ChangeState.TypeChanged); + break; case "RD": change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted); break; @@ -87,6 +111,9 @@ namespace SourceGit.Commands case "CM": change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified); break; + case "CT": + change.Set(Models.ChangeState.Copied, Models.ChangeState.TypeChanged); + break; case "CD": change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted); break; diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 5350c169..07ce4e2b 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -13,6 +13,7 @@ namespace SourceGit.Models { None, Modified, + TypeChanged, Added, Deleted, Renamed, diff --git a/src/Views/ChangeStatusIcon.cs b/src/Views/ChangeStatusIcon.cs index e54b765e..06508638 100644 --- a/src/Views/ChangeStatusIcon.cs +++ b/src/Views/ChangeStatusIcon.cs @@ -18,6 +18,12 @@ namespace SourceGit.Views EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), }, new LinearGradientBrush + { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) }, StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), @@ -55,7 +61,7 @@ namespace SourceGit.Views }, ]; - private static readonly string[] INDICATOR = ["?", "±", "+", "−", "➜", "❏", "U", "★"]; + private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "U", "★"]; public static readonly StyledProperty IsUnstagedChangeProperty = AvaloniaProperty.Register(nameof(IsUnstagedChange)); From f55a5760135c1bd12749b4db5d8a18c7dc7d428a Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 31 Jul 2024 12:04:29 +0800 Subject: [PATCH 13/33] refactor: rewrite amend behaviour (#300) * toggle amend will show changes in HEAD commit * since discard is not compatible with staged changes in `amend` mode, we only allows user to discard unstaged changes --- src/Commands/Discard.cs | 18 +--- src/Commands/QueryStagedChangesWithAmend.cs | 90 ++++++++++++++++ src/Commands/UnstageChangesForAmend.cs | 97 +++++++++++++++++ src/Models/Change.cs | 9 +- src/Models/DiffOption.cs | 6 +- src/ViewModels/Discard.cs | 8 +- src/ViewModels/WorkingCopy.cs | 113 ++++++++++---------- src/Views/TextDiffView.axaml | 2 +- src/Views/TextDiffView.axaml.cs | 4 +- 9 files changed, 261 insertions(+), 86 deletions(-) create mode 100644 src/Commands/QueryStagedChangesWithAmend.cs create mode 100644 src/Commands/UnstageChangesForAmend.cs diff --git a/src/Commands/Discard.cs b/src/Commands/Discard.cs index 22b579a3..63fcaa8e 100644 --- a/src/Commands/Discard.cs +++ b/src/Commands/Discard.cs @@ -11,7 +11,7 @@ namespace SourceGit.Commands new Clean(repo).Exec(); } - public static void ChangesInWorkTree(string repo, List changes) + public static void Changes(string repo, List changes) { var needClean = new List(); var needCheckout = new List(); @@ -19,13 +19,9 @@ namespace SourceGit.Commands foreach (var c in changes) { if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added) - { needClean.Add(c.Path); - } else - { needCheckout.Add(c.Path); - } } for (int i = 0; i < needClean.Count; i += 10) @@ -40,17 +36,5 @@ namespace SourceGit.Commands new Restore(repo, needCheckout.GetRange(i, count), "--worktree --recurse-submodules").Exec(); } } - - public static void ChangesInStaged(string repo, List changes) - { - for (int i = 0; i < changes.Count; i += 10) - { - var count = Math.Min(10, changes.Count - i); - var files = new List(); - for (int j = 0; j < count; j++) - files.Add(changes[i + j].Path); - new Restore(repo, files, "--staged --worktree --recurse-submodules").Exec(); - } - } } } diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs new file mode 100644 index 00000000..93db31cb --- /dev/null +++ b/src/Commands/QueryStagedChangesWithAmend.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands +{ + public partial class QueryStagedChangesWithAmend : Command + { + [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMTUX])\d{0,6}\t(.*)$")] + private static partial Regex REG_FORMAT1(); + [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} R\d{0,6}\t(.*\t.*)$")] + private static partial Regex REG_FORMAT2(); + + public QueryStagedChangesWithAmend(string repo) + { + WorkingDirectory = repo; + Context = repo; + Args = "diff-index --cached -M HEAD^"; + } + + public List Result() + { + var rs = ReadToEnd(); + if (rs.IsSuccess) + { + var changes = new List(); + var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + var match = REG_FORMAT2().Match(line); + if (match.Success) + { + var change = new Models.Change() { + Path = match.Groups[3].Value, + DataForAmend = new Models.ChangeDataForAmend() + { + FileMode = match.Groups[1].Value, + ObjectHash = match.Groups[2].Value, + }, + }; + change.Set(Models.ChangeState.Renamed); + changes.Add(change); + continue; + } + + match = REG_FORMAT1().Match(line); + if (match.Success) + { + var change = new Models.Change() { + Path = match.Groups[4].Value, + DataForAmend = new Models.ChangeDataForAmend() + { + FileMode = match.Groups[1].Value, + ObjectHash = match.Groups[2].Value, + }, + }; + + var type = match.Groups[3].Value; + switch (type) + { + case "A": + change.Set(Models.ChangeState.Added); + break; + case "C": + change.Set(Models.ChangeState.Copied); + break; + case "D": + change.Set(Models.ChangeState.Deleted); + break; + case "M": + change.Set(Models.ChangeState.Modified); + break; + case "T": + change.Set(Models.ChangeState.TypeChanged); + break; + case "U": + change.Set(Models.ChangeState.Unmerged); + break; + } + changes.Add(change); + } + } + + return changes; + } + + return []; + } + } +} diff --git a/src/Commands/UnstageChangesForAmend.cs b/src/Commands/UnstageChangesForAmend.cs new file mode 100644 index 00000000..c930f136 --- /dev/null +++ b/src/Commands/UnstageChangesForAmend.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +using Avalonia.Threading; + +namespace SourceGit.Commands +{ + public class UnstageChangesForAmend + { + public UnstageChangesForAmend(string repo, List changes) + { + _repo = repo; + + foreach (var c in changes) + { + if (c.Index == Models.ChangeState.Renamed) + { + _patchBuilder.Append("0 0000000000000000000000000000000000000000\t"); + _patchBuilder.Append(c.Path); + _patchBuilder.Append("\0100644 "); + _patchBuilder.Append(c.DataForAmend.ObjectHash); + _patchBuilder.Append("\t"); + _patchBuilder.Append(c.OriginalPath); + _patchBuilder.Append("\n"); + } + else if (c.Index == Models.ChangeState.Added) + { + _patchBuilder.Append("0 0000000000000000000000000000000000000000\t"); + _patchBuilder.Append(c.Path); + _patchBuilder.Append("\n"); + } + else if (c.Index == Models.ChangeState.Deleted) + { + _patchBuilder.Append("100644 "); + _patchBuilder.Append(c.DataForAmend.ObjectHash); + _patchBuilder.Append("\t"); + _patchBuilder.Append(c.Path); + _patchBuilder.Append("\n"); + } + else + { + _patchBuilder.Append(c.DataForAmend.FileMode); + _patchBuilder.Append(" "); + _patchBuilder.Append(c.DataForAmend.ObjectHash); + _patchBuilder.Append("\t"); + _patchBuilder.Append(c.Path); + _patchBuilder.Append("\n"); + } + } + } + + public bool Exec() + { + var starter = new ProcessStartInfo(); + starter.WorkingDirectory = _repo; + starter.FileName = Native.OS.GitExecutable; + starter.Arguments = "-c core.editor=true update-index --index-info"; + starter.UseShellExecute = false; + starter.CreateNoWindow = true; + starter.WindowStyle = ProcessWindowStyle.Hidden; + starter.RedirectStandardInput = true; + starter.RedirectStandardOutput = false; + starter.RedirectStandardError = true; + + try + { + var proc = new Process() { StartInfo = starter }; + proc.Start(); + proc.StandardInput.Write(_patchBuilder.ToString()); + proc.StandardInput.Close(); + + var err = proc.StandardError.ReadToEnd(); + proc.WaitForExit(); + var rs = proc.ExitCode == 0; + proc.Close(); + + if (!rs) + Dispatcher.UIThread.Invoke(() => App.RaiseException(_repo, err)); + + return rs; + } + catch (Exception e) + { + Dispatcher.UIThread.Invoke(() => + { + App.RaiseException(_repo, "Failed to unstage changes: " + e.Message); + }); + return false; + } + } + + private string _repo = ""; + private StringBuilder _patchBuilder = new StringBuilder(); + } +} diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 07ce4e2b..36fe20ac 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -22,12 +22,19 @@ namespace SourceGit.Models Untracked } + public class ChangeDataForAmend + { + public string FileMode { get; set; } = ""; + public string ObjectHash { get; set; } = ""; + } + public class Change { - public ChangeState Index { get; set; } + public ChangeState Index { get; set; } = ChangeState.None; public ChangeState WorkTree { get; set; } = ChangeState.None; public string Path { get; set; } = ""; public string OriginalPath { get; set; } = ""; + public ChangeDataForAmend DataForAmend { get; set; } = null; public bool IsConflit { diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index e122ad67..98387e7f 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -39,7 +39,11 @@ namespace SourceGit.Models } else { - _extra = "--cached"; + if (change.DataForAmend != null) + _extra = "--cached HEAD^"; + else + _extra = "--cached"; + _path = change.Path; _orgPath = change.OriginalPath; } diff --git a/src/ViewModels/Discard.cs b/src/ViewModels/Discard.cs index f37d5f0c..916c3b86 100644 --- a/src/ViewModels/Discard.cs +++ b/src/ViewModels/Discard.cs @@ -19,11 +19,10 @@ namespace SourceGit.ViewModels View = new Views.Discard { DataContext = this }; } - public Discard(Repository repo, List changes, bool isUnstaged) + public Discard(Repository repo, List changes) { _repo = repo; _changes = changes; - _isUnstaged = isUnstaged; if (_changes == null) Mode = new Models.Null(); @@ -44,10 +43,8 @@ namespace SourceGit.ViewModels { if (_changes == null) Commands.Discard.All(_repo.FullPath); - else if (_isUnstaged) - Commands.Discard.ChangesInWorkTree(_repo.FullPath, _changes); else - Commands.Discard.ChangesInStaged(_repo.FullPath, _changes); + Commands.Discard.Changes(_repo.FullPath, _changes); CallUIThread(() => { @@ -61,6 +58,5 @@ namespace SourceGit.ViewModels private readonly Repository _repo = null; private readonly List _changes = null; - private readonly bool _isUnstaged = true; } } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 4a5c7c4e..813be025 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -88,21 +88,26 @@ namespace SourceGit.ViewModels get => _useAmend; set { - if (SetProperty(ref _useAmend, value) && value) + if (SetProperty(ref _useAmend, value)) { - var currentBranch = _repo.CurrentBranch; - if (currentBranch == null) + if (value) { - App.RaiseException(_repo.FullPath, "No commits to amend!!!"); - _useAmend = false; - OnPropertyChanged(); - return; + var currentBranch = _repo.CurrentBranch; + if (currentBranch == null) + { + App.RaiseException(_repo.FullPath, "No commits to amend!!!"); + _useAmend = false; + OnPropertyChanged(); + return; + } + + CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, currentBranch.Head).Result(); } - CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, currentBranch.Head).Result(); + Staged = GetStagedChanges(); + SelectedStaged = []; + OnPropertyChanged(nameof(IsCommitWithPushVisible)); } - - OnPropertyChanged(nameof(IsCommitWithPushVisible)); } } @@ -216,6 +221,8 @@ namespace SourceGit.ViewModels public bool SetData(List changes) { + _cached = changes; + var unstaged = new List(); var staged = new List(); var selectedUnstaged = new List(); @@ -237,17 +244,6 @@ namespace SourceGit.ViewModels var hasConflict = false; foreach (var c in changes) { - if (c.Index == Models.ChangeState.Modified - || c.Index == Models.ChangeState.Added - || c.Index == Models.ChangeState.Deleted - || c.Index == Models.ChangeState.Renamed) - { - staged.Add(c); - - if (lastSelectedStaged.Contains(c.Path)) - selectedStaged.Add(c); - } - if (c.WorkTree != Models.ChangeState.None) { unstaged.Add(c); @@ -258,6 +254,13 @@ namespace SourceGit.ViewModels } } + staged = GetStagedChanges(); + foreach (var c in staged) + { + if (lastSelectedStaged.Contains(c.Path)) + selectedStaged.Add(c); + } + _count = changes.Count; Dispatcher.UIThread.Invoke(() => @@ -358,7 +361,11 @@ namespace SourceGit.ViewModels SetDetail(null); IsUnstaging = true; _repo.SetWatcherEnabled(false); - if (changes.Count == _staged.Count) + if (_useAmend) + { + await Task.Run(() => new Commands.UnstageChangesForAmend(_repo.FullPath, changes).Exec()); + } + else if (changes.Count == _staged.Count) { await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec()); } @@ -376,24 +383,14 @@ namespace SourceGit.ViewModels IsUnstaging = false; } - public void Discard(List changes, bool isUnstaged) + public void Discard(List changes) { if (PopupHost.CanCreatePopup()) { - if (isUnstaged) - { - if (changes.Count == _unstaged.Count && _staged.Count == 0) - PopupHost.ShowPopup(new Discard(_repo)); - else - PopupHost.ShowPopup(new Discard(_repo, changes, true)); - } + if (changes.Count == _unstaged.Count && _staged.Count == 0) + PopupHost.ShowPopup(new Discard(_repo)); else - { - if (changes.Count == _staged.Count && _unstaged.Count == 0) - PopupHost.ShowPopup(new Discard(_repo)); - else - PopupHost.ShowPopup(new Discard(_repo, changes, false)); - } + PopupHost.ShowPopup(new Discard(_repo, changes)); } } @@ -491,7 +488,7 @@ namespace SourceGit.ViewModels discard.Icon = App.CreateMenuIcon("Icons.Undo"); discard.Click += (_, e) => { - Discard(_selectedUnstaged, true); + Discard(_selectedUnstaged); e.Handled = true; }; @@ -815,7 +812,7 @@ namespace SourceGit.ViewModels discard.Icon = App.CreateMenuIcon("Icons.Undo"); discard.Click += (_, e) => { - Discard(_selectedUnstaged, true); + Discard(_selectedUnstaged); e.Handled = true; }; @@ -904,15 +901,6 @@ namespace SourceGit.ViewModels e.Handled = true; }; - var discard = new MenuItem(); - discard.Header = App.Text("FileCM.Discard"); - discard.Icon = App.CreateMenuIcon("Icons.Undo"); - discard.Click += (_, e) => - { - Discard(_selectedStaged, false); - e.Handled = true; - }; - var stash = new MenuItem(); stash.Header = App.Text("FileCM.Stash"); stash.Icon = App.CreateMenuIcon("Icons.Stashes"); @@ -971,7 +959,6 @@ namespace SourceGit.ViewModels menu.Items.Add(openWith); menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(unstage); - menu.Items.Add(discard); menu.Items.Add(stash); menu.Items.Add(patch); menu.Items.Add(new MenuItem() { Header = "-" }); @@ -1071,15 +1058,6 @@ namespace SourceGit.ViewModels e.Handled = true; }; - var discard = new MenuItem(); - discard.Header = App.Text("FileCM.DiscardMulti", _selectedStaged.Count); - discard.Icon = App.CreateMenuIcon("Icons.Undo"); - discard.Click += (_, e) => - { - Discard(_selectedStaged, false); - e.Handled = true; - }; - var stash = new MenuItem(); stash.Header = App.Text("FileCM.StashMulti", _selectedStaged.Count); stash.Icon = App.CreateMenuIcon("Icons.Stashes"); @@ -1118,7 +1096,6 @@ namespace SourceGit.ViewModels }; menu.Items.Add(unstage); - menu.Items.Add(discard); menu.Items.Add(stash); menu.Items.Add(patch); } @@ -1162,6 +1139,25 @@ namespace SourceGit.ViewModels return menu; } + private List GetStagedChanges() + { + if (_useAmend) + { + return new Commands.QueryStagedChangesWithAmend(_repo.FullPath).Result(); + } + else + { + var rs = new List(); + foreach (var c in _cached) + { + if (c.Index != Models.ChangeState.None && + c.Index != Models.ChangeState.Untracked) + rs.Add(c); + } + return rs; + } + } + private void SetDetail(Models.Change change) { if (_isLoadingData) @@ -1287,6 +1283,7 @@ namespace SourceGit.ViewModels private bool _isCommitting = false; private bool _useAmend = false; private bool _canCommitWithPush = false; + private List _cached = []; private List _unstaged = []; private List _staged = []; private List _selectedUnstaged = []; diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index 4b1cad3d..b49faa7d 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -77,7 +77,7 @@