diff --git a/README.md b/README.md index 6a688d99..1cc9b64e 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ ## Translation Status -[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-96.05%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-97.08%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-87.72%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-90.79%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md) +[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.56%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.86%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-86.60%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.86%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.86%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md) ## How to Use diff --git a/TRANSLATION.md b/TRANSLATION.md index b6c255a1..fa6701f0 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -1,69 +1,33 @@ -### de_DE.axaml: 96.05% +### de_DE.axaml: 98.56%
Missing Keys -- Text.BranchCM.FetchInto -- Text.ChangeCM.GenerateCommitMessage -- Text.CommitCM.CustomAction -- Text.Configure.CustomAction -- Text.Configure.CustomAction.Arguments -- Text.Configure.CustomAction.Arguments.Tip -- Text.Configure.CustomAction.Executable -- Text.Configure.CustomAction.Name -- Text.Configure.CustomAction.Scope -- Text.Configure.CustomAction.Scope.Commit -- Text.Configure.CustomAction.Scope.Repository -- Text.Configure.Git.EnablePruneOnFetch -- Text.Configure.Git.EnableSignOff -- Text.Configure.IssueTracker.AddSampleGitLabIssue -- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest -- Text.Configure.OpenAI -- Text.Configure.OpenAI.Prefered -- Text.Configure.OpenAI.Prefered.Tip -- Text.ExecuteCustomAction -- Text.ExecuteCustomAction.Name -- Text.Preference.AI.AnalyzeDiffPrompt -- Text.Preference.AI.GenerateSubjectPrompt -- Text.Preference.AI.Name -- Text.Repository.CustomActions -- Text.Repository.CustomActions.Empty -- Text.Stash.KeepIndex -- Text.WorkingCopy.ConfirmCommitWithoutFiles +- Text.CommitDetail.Info.Children +- Text.Diff.SaveAsPatch +- Text.Diff.VisualLines.All +- Text.Hotkeys.Repo.CreateBranchOnCommit +- Text.Hotkeys.Repo.Fetch +- Text.Hotkeys.Repo.Pull +- Text.Hotkeys.Repo.Push +- Text.IssueLinkCM.OpenInBrowser +- Text.IssueLinkCM.CopyLink +- Text.Preference.Appearance.EditorFontSize
-### es_ES.axaml: 97.08% +### es_ES.axaml: 99.86%
Missing Keys -- Text.ChangeCM.GenerateCommitMessage -- Text.CommitCM.CustomAction -- Text.Configure.CustomAction -- Text.Configure.CustomAction.Arguments -- Text.Configure.CustomAction.Arguments.Tip -- Text.Configure.CustomAction.Executable -- Text.Configure.CustomAction.Name -- Text.Configure.CustomAction.Scope -- Text.Configure.CustomAction.Scope.Commit -- Text.Configure.CustomAction.Scope.Repository -- Text.Configure.Git.EnablePruneOnFetch -- Text.Configure.OpenAI -- Text.Configure.OpenAI.Prefered -- Text.Configure.OpenAI.Prefered.Tip -- Text.ExecuteCustomAction -- Text.ExecuteCustomAction.Name -- Text.Preference.AI.Name -- Text.Repository.CustomActions -- Text.Repository.CustomActions.Empty -- Text.Stash.KeepIndex +- Text.CommitDetail.Info.Children
-### fr_FR.axaml: 87.72% +### fr_FR.axaml: 86.60%
@@ -108,6 +72,8 @@ - Text.ConventionalCommit.ShortDescription - Text.ConventionalCommit.Type - Text.Diff.IgnoreWhitespace +- Text.Diff.SaveAsPatch +- Text.Diff.VisualLines.All - Text.Discard.IncludeIgnored - Text.ExecuteCustomAction - Text.ExecuteCustomAction.Name @@ -118,7 +84,13 @@ - Text.Histories.Tips.MacOS - Text.Histories.Tips.Prefix - Text.Hotkeys.Repo.CommitWithAutoStage +- Text.Hotkeys.Repo.CreateBranchOnCommit - Text.Hotkeys.Repo.DiscardSelected +- Text.Hotkeys.Repo.Fetch +- Text.Hotkeys.Repo.Pull +- Text.Hotkeys.Repo.Push +- Text.IssueLinkCM.OpenInBrowser +- Text.IssueLinkCM.CopyLink - Text.MoveRepositoryNode - Text.MoveRepositoryNode.Target - Text.Preference.AI @@ -128,6 +100,7 @@ - Text.Preference.AI.Model - Text.Preference.AI.Name - Text.Preference.AI.Server +- Text.Preference.Appearance.EditorFontSize - Text.Preference.General.ShowAuthorTime - Text.Preference.Integration - Text.Preference.Shell @@ -156,85 +129,23 @@
-### pt_BR.axaml: 90.79% +### pt_BR.axaml: 99.86%
Missing Keys -- Text.About.Chart -- Text.AIAssistant -- Text.AIAssistant.Tip -- Text.BranchCM.FetchInto -- Text.ChangeCM.GenerateCommitMessage -- Text.CherryPick.AppendSourceToMessage -- Text.CherryPick.Mainline -- Text.CherryPick.Mainline.Tips -- Text.CommitCM.CherryPickMultiple -- Text.CommitCM.CustomAction -- Text.CommitCM.SquashCommitsSinceThis -- Text.CommitDetail.Info.ContainsIn -- Text.CommitDetail.Info.ContainsIn.Title -- Text.CommitDetail.Info.WebLinks -- Text.Configure.CustomAction -- Text.Configure.CustomAction.Arguments -- Text.Configure.CustomAction.Arguments.Tip -- Text.Configure.CustomAction.Executable -- Text.Configure.CustomAction.Name -- Text.Configure.CustomAction.Scope -- Text.Configure.CustomAction.Scope.Commit -- Text.Configure.CustomAction.Scope.Repository -- Text.Configure.Git.DefaultRemote -- Text.Configure.Git.EnablePruneOnFetch -- Text.Configure.Git.EnableSignOff -- Text.Configure.IssueTracker.AddSampleGitLabIssue -- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest -- Text.Configure.OpenAI -- Text.Configure.OpenAI.Prefered -- Text.Configure.OpenAI.Prefered.Tip -- Text.ConfigureWorkspace -- Text.ConfigureWorkspace.Color -- Text.ConfigureWorkspace.Restore -- Text.ConventionalCommit -- Text.ConventionalCommit.BreakingChanges -- Text.ConventionalCommit.ClosedIssue -- Text.ConventionalCommit.Detail -- Text.ConventionalCommit.Scope -- Text.ConventionalCommit.ShortDescription -- Text.ConventionalCommit.Type -- Text.CopyAllText -- Text.Discard.IncludeIgnored -- Text.ExecuteCustomAction -- Text.ExecuteCustomAction.Name -- Text.FileHistory.FileContent -- Text.FileHistory.FileChange -- Text.GitLFS.Locks.OnlyMine -- Text.MoveRepositoryNode -- Text.MoveRepositoryNode.Target -- Text.Preference.AI.Name -- Text.Push.CheckSubmodules -- Text.Repository.CustomActions -- Text.Repository.CustomActions.Empty -- Text.Squash.Into -- Text.Stash.KeepIndex -- Text.Stash.OnlyStagedChanges -- Text.Stash.TipForSelectedFiles -- Text.Statistics.Overview -- Text.TagCM.CopyMessage -- Text.WorkingCopy.Staged.UnstageAll -- Text.WorkingCopy.Unstaged -- Text.WorkingCopy.Unstaged.Stage -- Text.WorkingCopy.Unstaged.StageAll +- Text.CommitDetail.Info.Children
-### ru_RU.axaml: 100.00% +### ru_RU.axaml: 99.86%
Missing Keys - +- Text.CommitDetail.Info.Children
diff --git a/VERSION b/VERSION index a5e93156..833d11c8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.37 \ No newline at end of file +8.38 \ No newline at end of file diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index b9b6e064..04103d68 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -8,6 +8,10 @@ namespace SourceGit.Commands { [GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")] private static partial Regex REG_INDICATOR(); + + [GeneratedRegex(@"^index\s([0-9a-f]{6,40})\.\.([0-9a-f]{6,40})(\s[1-9]{6})?")] + private static partial Regex REG_HASH_CHANGE(); + private const string PREFIX_LFS_NEW = "+version https://git-lfs.github.com/spec/"; private const string PREFIX_LFS_DEL = "-version https://git-lfs.github.com/spec/"; private const string PREFIX_LFS_MODIFY = " version https://git-lfs.github.com/spec/"; @@ -101,17 +105,31 @@ namespace SourceGit.Commands if (_result.TextDiff.Lines.Count == 0) { - var match = REG_INDICATOR().Match(line); - if (!match.Success) + if (line.StartsWith("Binary", StringComparison.Ordinal)) { - if (line.StartsWith("Binary", StringComparison.Ordinal)) - _result.IsBinary = true; + _result.IsBinary = true; return; } - _oldLine = int.Parse(match.Groups[1].Value); - _newLine = int.Parse(match.Groups[2].Value); - _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0)); + if (string.IsNullOrEmpty(_result.OldHash)) + { + var match = REG_HASH_CHANGE().Match(line); + if (!match.Success) + return; + + _result.OldHash = match.Groups[1].Value; + _result.NewHash = match.Groups[2].Value; + } + else + { + var match = REG_INDICATOR().Match(line); + if (!match.Success) + return; + + _oldLine = int.Parse(match.Groups[1].Value); + _newLine = int.Parse(match.Groups[2].Value); + _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0)); + } } else { diff --git a/src/Commands/ExecuteCustomAction.cs b/src/Commands/ExecuteCustomAction.cs index 253c6b43..f5fec82c 100644 --- a/src/Commands/ExecuteCustomAction.cs +++ b/src/Commands/ExecuteCustomAction.cs @@ -30,6 +30,8 @@ namespace SourceGit.Commands start.Environment.Add("PATH", Native.OS.CustomPathEnv); var proc = new Process() { StartInfo = start }; + var builder = new StringBuilder(); + proc.OutputDataReceived += (_, e) => { if (e.Data != null) @@ -39,7 +41,10 @@ namespace SourceGit.Commands proc.ErrorDataReceived += (_, e) => { if (e.Data != null) + { outputHandler?.Invoke(e.Data); + builder.AppendLine(e.Data); + } }; try @@ -57,7 +62,17 @@ namespace SourceGit.Commands }); } + var exitCode = proc.ExitCode; proc.Close(); + + if (exitCode != 0) + { + var errMsg = builder.ToString(); + Dispatcher.UIThread.Invoke(() => + { + App.RaiseException(repo, errMsg); + }); + } } } } diff --git a/src/Commands/QueryCommitChildren.cs b/src/Commands/QueryCommitChildren.cs new file mode 100644 index 00000000..22a44563 --- /dev/null +++ b/src/Commands/QueryCommitChildren.cs @@ -0,0 +1,34 @@ +namespace SourceGit.Commands +{ + public class QueryCommitChildren : Command + { + public QueryCommitChildren(string repo, string sha) + { + WorkingDirectory = repo; + Context = repo; + _sha = sha; + Args = $"rev-list --children --all {sha}^.."; + } + + public string[] Result() + { + var rs = ReadToEnd(); + if (!rs.IsSuccess) + return []; + + int start = rs.StdOut.IndexOf($"\n{_sha}"); + if (start != -1) + { + int end = rs.StdOut.IndexOf('\n', start + 1); + if (end == -1) + end = rs.StdOut.Length; + start = rs.StdOut.IndexOf(' ', start); + if (start != -1 && start < end) + return rs.StdOut.Substring(start + 1, end - start - 1).Split(' ', System.StringSplitOptions.RemoveEmptyEntries); + } + return []; + } + + private string _sha; + } +} diff --git a/src/Commands/SaveChangesAsPatch.cs b/src/Commands/SaveChangesAsPatch.cs index 409127ba..461bbfb5 100644 --- a/src/Commands/SaveChangesAsPatch.cs +++ b/src/Commands/SaveChangesAsPatch.cs @@ -9,7 +9,7 @@ namespace SourceGit.Commands { public static class SaveChangesAsPatch { - public static bool Exec(string repo, List changes, bool isUnstaged, string saveTo) + public static bool ProcessLocalChanges(string repo, List changes, bool isUnstaged, string saveTo) { using (var sw = File.Create(saveTo)) { @@ -23,6 +23,20 @@ namespace SourceGit.Commands return true; } + public static bool ProcessRevisionCompareChanges(string repo, List changes, string baseRevision, string targetRevision, string saveTo) + { + using (var sw = File.Create(saveTo)) + { + foreach (var change in changes) + { + if (!ProcessSingleChange(repo, new Models.DiffOption(baseRevision, targetRevision, change), sw)) + return false; + } + } + + return true; + } + private static bool ProcessSingleChange(string repo, Models.DiffOption opt, FileStream writer) { var starter = new ProcessStartInfo(); diff --git a/src/Models/CustomAction.cs b/src/Models/CustomAction.cs index 2a400b02..8452a42d 100644 --- a/src/Models/CustomAction.cs +++ b/src/Models/CustomAction.cs @@ -30,7 +30,7 @@ namespace SourceGit.Models public string Arguments { - get => _arguments; + get => _arguments; set => SetProperty(ref _arguments, value); } diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index e7cecaa3..e0ae82e0 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -63,7 +63,7 @@ namespace SourceGit.Models { public string File { get; set; } = string.Empty; public List Lines { get; set; } = new List(); - public Vector SyncScrollOffset { get; set; } = Vector.Zero; + public Vector ScrollOffset { get; set; } = Vector.Zero; public int MaxLineNumber = 0; public string Repo { get; set; } = null; @@ -674,6 +674,8 @@ namespace SourceGit.Models { public bool IsBinary { get; set; } = false; public bool IsLFS { get; set; } = false; + public string OldHash { get; set; } = string.Empty; + public string NewHash { get; set; } = string.Empty; public string OldMode { get; set; } = string.Empty; public string NewMode { get; set; } = string.Empty; public TextDiff TextDiff { get; set; } = null; diff --git a/src/Models/Notification.cs b/src/Models/Notification.cs index 2261e327..473947b0 100644 --- a/src/Models/Notification.cs +++ b/src/Models/Notification.cs @@ -4,10 +4,5 @@ { public bool IsError { get; set; } = false; public string Message { get; set; } = string.Empty; - - public void CopyMessage() - { - App.CopyText(Message); - } } } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index a57d26d2..48fbb287 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -325,8 +325,8 @@ namespace SourceGit.Native if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher) { // Get actual path to the executable - if (launcher.GetValue(string.Empty) is string CLSID && - localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv && + if (launcher.GetValue(string.Empty) is string CLSID && + localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv && devenv.GetValue(string.Empty) is string localServer32) { return localServer32!.Trim('\"'); @@ -353,23 +353,26 @@ namespace SourceGit.Native private string GenerateCommandlineArgsForVisualStudio(string repo) { - var sln = FindVSSolutionFile(repo, 4); + var sln = FindVSSolutionFile(new DirectoryInfo(repo), 4); return string.IsNullOrEmpty(sln) ? $"\"{repo}\"" : $"\"{sln}\""; } - private string FindVSSolutionFile(string path, int leftDepth) + private string FindVSSolutionFile(DirectoryInfo dir, int leftDepth) { - var found = Directory.GetFiles(path, "*.sln", SearchOption.TopDirectoryOnly); - if (found != null && found.Length > 0) - return Path.GetFullPath(found[0]); + var files = dir.GetFiles(); + foreach (var f in files) + { + if (f.Name.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) + return f.FullName; + } if (leftDepth <= 0) return null; - var subfolders = Directory.GetDirectories(path); - foreach (var subfolder in subfolders) + var subDirs = dir.GetDirectories(); + foreach (var subDir in subDirs) { - var first = FindVSSolutionFile(subfolder, leftDepth - 1); + var first = FindVSSolutionFile(subDir, leftDepth - 1); if (!string.IsNullOrEmpty(first)) return first; } diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 6cf53d96..53b2b3d3 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -1,6 +1,6 @@ M41 512c0-128 46-241 138-333C271 87 384 41 512 41s241 46 333 138c92 92 138 205 138 333s-46 241-138 333c-92 92-205 138-333 138s-241-46-333-138C87 753 41 640 41 512zm87 0c0 108 36 195 113 271s164 113 271 113c108 0 195-36 271-113s113-164 113-271-36-195-113-271c-77-77-164-113-271-113-108 0-195 36-271 113C164 317 128 404 128 512zm256 148V292l195 113L768 512l-195 113-195 113v-77zm148-113-61 36V440l61 36 61 36-61 36z - M951 419a255 255 0 00-22-209 258 258 0 00-278-124A259 259 0 00213 178a255 255 0 00-171 124 258 258 0 0032 303 255 255 0 0022 210 258 258 0 00278 124A255 255 0 00566 1024a258 258 0 00246-179 256 256 0 00171-124 258 258 0 00-32-302zM566 957a191 191 0 01-123-44l6-3 204-118a34 34 0 0017-29v-287l86 50a3 3 0 012 2v238a192 192 0 01-192 192zM154 781a191 191 0 01-23-129l6 4 204 118a33 33 0 0033 0l249-144v99a3 3 0 01-1 3L416 851a192 192 0 01-262-70zM100 337a191 191 0 01101-84V495a33 33 0 0017 29l248 143-86 50a3 3 0 01-3 0l-206-119A192 192 0 01100 336zm708 164-249-145L645 307a3 3 0 013 0l206 119a192 192 0 01-29 346v-242a34 34 0 00-17-28zm86-129-6-4-204-119a33 33 0 00-33 0L401 394V294a3 3 0 011-3l206-119a192 192 0 01285 199zm-539 176-86-50a3 3 0 01-2-2V259a192 192 0 01315-147l-6 3-204 118a34 34 0 00-17 29zm47-101 111-64 111 64v128l-111 64-111-64z + M304 464a128 128 0 01128-128c71 0 128 57 128 128v224a32 32 0 01-64 0V592h-128v95a32 32 0 01-64 0v-224zm64 1v64h128v-64a64 64 0 00-64-64c-35 0-64 29-64 64zM688 337c18 0 32 14 32 32v319a32 32 0 01-32 32c-18 0-32-14-32-32v-319a32 32 0 0132-32zM84 911l60-143A446 446 0 0164 512C64 265 265 64 512 64s448 201 448 448-201 448-448 448c-54 0-105-9-153-27l-242 22a32 32 0 01-32-44zm133-150-53 126 203-18 13 5c41 15 85 23 131 23 212 0 384-172 384-384S724 128 512 128 128 300 128 512c0 82 26 157 69 220l20 29z M296 392h64v64h-64zM296 582v160h128V582h-64v-62h-64v62zm80 48v64h-32v-64h32zM360 328h64v64h-64zM296 264h64v64h-64zM360 456h64v64h-64zM360 200h64v64h-64zM855 289 639 73c-6-6-14-9-23-9H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V311c0-9-3-17-9-23zM790 326H602V138L790 326zm2 562H232V136h64v64h64v-64h174v216c0 23 19 42 42 42h216v494z M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z @@ -65,6 +65,7 @@ M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zM139 832V192c0-6 4-11 11-11h331v661H149c-6 0-11-4-11-11zm747 0c0 6-4 11-11 11H544v-661H875c6 0 11 4 11 11v640z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zm-725 64h725c6 0 11 4 11 11v288h-747V192c0-6 4-11 11-11zm725 661H149c-6 0-11-4-11-11V544h747V832c0 6-4 11-11 11z M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z + M416 192m32 0 448 0q32 0 32 32l0 0q0 32-32 32l-448 0q-32 0-32-32l0 0q0-32 32-32ZM416 448m32 0 448 0q32 0 32 32l0 0q0 32-32 32l-448 0q-32 0-32-32l0 0q0-32 32-32ZM416 704m32 0 448 0q32 0 32 32l0 0q0 32-32 32l-448 0q-32 0-32-32l0 0q0-32 32-32ZM96 320l128-192 128 192h-256zM96 640l128 192 128-192h-256zM190 320h64v320H190z M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l132 0 0-128 64 0 0 128 132 0 0 64-132 0 0 128-64 0 0-128-132 0Z M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l328 0 0 64-328 0Z M 968 418 l -95 94 c -59 59 -146 71 -218 37 L 874 331 a 64 64 0 0 0 0 -90 L 783 150 a 64 64 0 0 0 -90 0 L 475 368 c -34 -71 -22 -159 37 -218 l 94 -94 c 75 -75 196 -75 271 0 l 90 90 c 75 75 75 196 0 271 z M 332 693 a 64 64 0 0 1 0 -90 l 271 -271 c 25 -25 65 -25 90 0 s 25 65 0 90 L 422 693 a 64 64 0 0 1 -90 0 z M 151 783 l 90 90 a 64 64 0 0 0 90 0 l 218 -218 c 34 71 22 159 -37 218 l -86 94 a 192 192 0 0 1 -271 0 l -98 -98 a 192 192 0 0 1 0 -271 l 94 -86 c 59 -59 146 -71 218 -37 L 151 693 a 64 64 0 0 0 0 90 z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index e45eecdb..d1f43695 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -58,6 +58,7 @@ Lösche alle ausgewählten {0} Branches Alle Änderungen verwerfen Fast-Forward zu ${0}$ + Fetche ${0}$ nach ${1}$... Git Flow - Abschließen ${0}$ Merge ${0}$ in ${1}$ hinein... Pull ${0}$ @@ -72,6 +73,7 @@ ABBRECHEN Auf diese Revision zurücksetzen Auf Vorgänger-Revision zurücksetzen + Generiere Commit-Nachricht ANZEIGE MODUS ÄNDERN Zeige als Datei- und Ordnerliste Zeige als Pfadliste @@ -109,6 +111,7 @@ Mit Worktree vergleichen Info kopieren SHA kopieren + Benutzerdefinierte Aktion Interactives Rebase von ${0}$ auf diesen Commit Rebase von ${0}$ auf diesen Commit Reset ${0}$ auf diesen Commit @@ -140,20 +143,35 @@ COMMIT TEMPLATE Template Name: Template Inhalt: + BENUTZERDEFINIERTE AKTION + Argumente: + ${REPO} - Repository Pfad; ${SHA} - SHA-Wert des selektierten Commits + Ausführbare Datei: + Name: + Geltungsbereich: + Commit + Repository Email Adresse Email Adresse GIT Remotes automatisch fetchen Minute(n) Standard Remote + Aktivere --prune beim fetchen + Aktiviere --signoff für Commits TICKETSYSTEM Beispiel für Github-Regel hinzufügen Beispiel für Jira-Regel hinzufügen + Beispiel für eine Gitlab Issue Regel einfügen + Beispiel für einen Gitlab Merge Request einfügen Neue Regel Ticketnummer Regex-Ausdruck: Name: Ergebnis-URL: Verwende bitte $1, $2 um auf Regex-Gruppenwerte zuzugreifen. + OPEN AI + Bevorzugter Service: + Wenn der 'Bevorzugte Service' aktiviert ist, wird SourceGit nur dieses Repository nutzen. Ansonsten wird, wenn mehrere Services verfügbar sind, eine Kontextmenü zur Auswahl angezeigt. HTTP Proxy HTTP Proxy für dieses Repository Benutzername @@ -162,15 +180,14 @@ Farbe Zuletzt geöffnete Tabs beim Starten wiederherstellen Konventionelle Commit-Hilfe - Inkompatible Änderung: + Breaking Change: Geschlossenes Ticket: Änderungen im Detail: - Geltungsbereich: + Gültigkeitsbereich: Kurzbeschreibung: - Art der Änderung: + Typ der Änderung: Kopieren Kopiere gesamten Text - COMMIT-NACHRICHT KOPIEREN Pfad kopieren Dateinamen kopieren Branch erstellen... @@ -246,6 +263,8 @@ Ziel: Ausgewählte Gruppe bearbeiten Ausgewähltes Repository bearbeiten + Führe benutzerte Aktion aus + Name der Aktion: Fast-Forward (ohne Auschecken) Fetch Alle Remotes fetchen @@ -402,8 +421,11 @@ Vor {0} Jahren Einstellungen OPEN AI - Server + Analysierung des Diff Befehl API Schlüssel + Generiere Nachricht Befehl + Name + Server Modell DARSTELLUNG Standardschriftart @@ -507,7 +529,9 @@ Alles löschen Repository Einstellungen WEITER - Option '--reflog' einschalten + Benutzerdefinierte Aktionen + Keine benutzerdefinierten Aktionen + Aktiviere '--reflog' Option Öffne im Datei-Browser Suche Branches/Tags/Submodule GEFILTERT: @@ -569,6 +593,7 @@ START Stash Inklusive nicht-verfolgter Dateien + Behalte Dateien des Stages Name: Optional. Name dieses Stashes Nur gestagte Änderungen @@ -602,6 +627,7 @@ Tag-Namen kopieren Tag-Nachricht kopieren Lösche ${0}$... + Merge ${0}$ in ${1}$ hinein... Pushe ${0}$... URL: Submodule aktualisieren @@ -638,6 +664,7 @@ Template/Historie Klick-Ereignis auslösen Alle Änderungen stagen und committen + Leerer Commit erkannt! Fortfahren (--allow-empty)? KONFLIKTE ERKANNT DATEI KONFLIKTE GELÖST NICHT-VERFOLGTE DATEIEN INKLUDIEREN diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 2e96e391..302f7cba 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -18,8 +18,8 @@ Optional. Default is the destination folder name. Track Branch: Tracking remote branch - OpenAI Assistant - Use OpenAI to generate commit message + AI Assistant + Use AI to generate commit message Patch Error Raise errors and refuses to apply the patch @@ -125,6 +125,7 @@ INFORMATION AUTHOR CHANGED + CHILDREN COMMITTER Check refs that contains this commit COMMIT IS CONTAINED BY @@ -166,7 +167,7 @@ Rule Name: Result URL: Please use $1, $2 to access regex groups values. - OPEN AI + AI Prefered Service: If the 'Prefered Service' is set, SourceGit will only use it in this repository. Otherwise, if there is more than one service available, a context menu to choose one of them will be shown. HTTP Proxy @@ -185,7 +186,6 @@ Type of Change: Copy Copy All Text - COPY MESSAGE Copy Path Copy File Name Create Branch... @@ -238,6 +238,7 @@ Next Difference NO CHANGES OR ONLY EOL CHANGES Previous Difference + Save as Patch Show hidden symbols Side-By-Side Diff SUBMODULE @@ -246,6 +247,7 @@ Syntax Highlighting Line Word Wrap Open in Merge Tool + Show All Lines Decrease Number of Visible Lines Increase Number of Visible Lines SELECT FILE TO VIEW CHANGES @@ -274,7 +276,7 @@ Discard {0} files... Discard Changes in Selected Line(s) Open External Merge Tool - Save As Patch... + Save as Patch... Stage Stage {0} files Stage Changes in Selected Line(s) @@ -362,8 +364,12 @@ Commit staged changes Commit and push staged changes Stage all changes and commit + Creates a new branch based on selected commit Discard selected changes + Fetch, starts directly Dashboard mode (Default) + Pull, starts directly + Push, starts directly Force to reload this repository Stage/Unstage selected changes Commit search mode @@ -387,6 +393,8 @@ Interactive Rebase Target Branch: On: + Open in Browser + Copy Link ERROR NOTICE Merge Branch @@ -398,7 +406,7 @@ Name: Git has NOT been configured. Please to go [Preference] and configure it first. Open App Data Dir - Open With... + Open with... Optional. Create New Page Bookmark @@ -418,7 +426,7 @@ Last year {0} years ago Preference - OPEN AI + AI Analyze Diff Prompt API Key Generate Subject Prompt @@ -428,6 +436,7 @@ APPEARANCE Default Font Default Font Size + Editor Font Size Monospace Font Only use monospace font in text editor Theme @@ -530,15 +539,15 @@ Custom Actions No Custom Actions Enable '--reflog' Option - Open In File Browser + Open in File Browser Search Branches/Tags/Submodules FILTERED BY: LOCAL BRANCHES - Navigate To HEAD + Navigate to HEAD Enable '--first-parent' Option Create Branch - Open In {0} - Open In External Tools + Open in {0} + Open in External Tools Refresh REMOTES ADD REMOTE @@ -556,7 +565,7 @@ UPDATE SUBMODULE TAGS NEW TAG - Open In Terminal + Open in Terminal WORKTREES ADD WORKTREE PRUNE @@ -625,6 +634,7 @@ Copy Tag Name Copy Tag Message Delete ${0}$... + Merge ${0}$ into ${1}$... Push ${0}$... URL: Update Submodules diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index f8a2fa68..6bc96dd1 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -73,6 +73,7 @@ CANCELAR Resetear a Esta Revisión Resetear a Revisión Padre + Generar mensaje de commit CAMBIAR MODO DE VISUALIZACIÓN Mostrar como Lista de Archivos y Directorios Mostrar como Lista de Rutas @@ -110,7 +111,10 @@ Comparar con Worktree Copiar Información Copiar SHA + Acción personalizada Rebase Interactivo ${0}$ hasta Aquí + Abrir en el Navegador + Copiar Enlace Rebase ${0}$ hasta Aquí Reset ${0}$ hasta Aquí Revertir Commit @@ -141,12 +145,21 @@ PLANTILLA DE COMMIT Nombre de la Plantilla: Contenido de la Plantilla: + ACCIÓN PERSONALIZADA + Argumentos: + ${REPO} - Ruta del repositorio; ${SHA} - SHA del commit seleccionado + Archivo Ejecutable: + Nombre: + Alcance: + Commit + Repositorio Dirección de Email Dirección de email GIT Fetch remotos automáticamente Minuto(s) Remoto por Defecto + Habilitar --prune para fetch Habilitar --signoff para commit SEGUIMIENTO DE INCIDENCIAS Añadir Regla de Ejemplo para Github @@ -158,6 +171,9 @@ Nombre de la Regla: URL Resultante: Por favor, use $1, $2 para acceder a los valores de los grupos regex. + OPEN AI + Servicio Preferido: + Si el 'Servicio Preferido' está establecido, SourceGit sólo lo usará en este repositorio. De lo contrario, si hay más de un servicio disponible, se mostrará un menú de contexto para elegir uno. Proxy HTTP Proxy HTTP utilizado por este repositorio Nombre de Usuario @@ -174,7 +190,6 @@ Tipo de Cambio: Copiar Copiar Todo el Texto - COPIAR MENSAJE Copiar Ruta Copiar Nombre del Archivo Crear Rama... @@ -227,6 +242,7 @@ Siguiente Diferencia SIN CAMBIOS O SOLO CAMBIOS DE EOL Diferencia Anterior + Guardar como Patch Mostrar símbolos ocultos Diferencia Lado a Lado SUBMÓDULO @@ -235,6 +251,7 @@ Resaltado de Sintaxis Ajuste de Línea Abrir en Herramienta de Merge + Mostrar Todas las Líneas Disminuir Número de Líneas Visibles Aumentar Número de Líneas Visibles SELECCIONA ARCHIVO PARA VER CAMBIOS @@ -250,6 +267,8 @@ Destino: Editar Grupo Seleccionado Editar Repositorio Seleccionado + Ejecutar Acción Personalizada + Nombre de la Acción: Fast-Forward (sin checkout) Fetch Fetch todos los remotos @@ -349,8 +368,12 @@ Commit cambios staged Commit y push cambios staged Stage todos los cambios y commit + Crea una nueva rama basada en el commit seleccionado Descartar cambios seleccionados + Fetch, empieza directamente Modo Dashboard (Por Defecto) + Pull, empieza directamente + Push, empieza directamente Forzar a recargar este repositorio Stage/Unstage cambios seleccionados Modo de búsqueda de commits @@ -411,10 +434,12 @@ Clave API Generar Subject Prompt Modelo + Nombre Servidor APARIENCIA Fuente por defecto Tamaño de fuente por defecto + Tamaño de fuente del editor Fuente Monospace Usar solo fuente monospace en el editor de texto Tema @@ -514,6 +539,8 @@ Limpiar todo Configurar este repositorio CONTINUAR + Acciones Personalizadas + No hay ninguna Acción Personalizada Habilitar Opción '--reflog' Abrir en el Explorador Buscar Ramas/Etiquetas/Submódulos @@ -576,6 +603,7 @@ INICIAR Stash Incluir archivos no rastreados + Mantener archivos staged Mensaje: Opcional. Nombre de este stash Solo cambios staged @@ -609,6 +637,7 @@ Copiar Nombre de la Etiqueta Copiar Mensaje de la Etiqueta Eliminar ${0}$... + Merge ${0}$ en ${1}$... Push ${0}$... URL: Actualizar Submódulos diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index e096071f..88e2a661 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -118,6 +118,7 @@ INFORMATIONS AUTEUR CHANGÉ + ENFANTS COMMITTER Vérifier les références contenant ce commit LE COMMIT EST CONTENU PAR @@ -152,7 +153,6 @@ Copier Copier tout le texte Copier le nom de fichier - COPIER LE MESSAGE Copier le chemin Créer une branche... Basé sur : @@ -552,6 +552,7 @@ OK Copy Tag Name Delete ${0}$... + Fusionner ${0}$ dans ${1}$... Push ${0}$... URL : Actualiser les sous-modules diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index c9e0f915..61ded334 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -2,7 +2,32 @@ + + • Construído com + • Gráfico desenhado por © 2024 sourcegit-scm • Editor de Texto de • Fontes monoespaçadas de @@ -20,6 +45,8 @@ Branch Existente O que Checar: Adicionar Worktree + Assietente IA + Utilizar IA para gerar mensagem de commit Erros levantados e se recusa a aplicar o patch Erro Semelhante a 'erro', mas mostra mais @@ -55,6 +82,7 @@ Excluir {0} branches selecionados Descartar todas as alterações Fast-Forward para ${0}$ + Buscar ${0}$ em ${1}$... Git Flow - Finalizar ${0}$ Mesclar ${0}$ em ${1}$... Puxar ${0}$ @@ -69,9 +97,10 @@ CANCELAR Resetar para Revisão Pai Resetar para Esta Revisão - Mostrar como Lista de Arquivos e Diretórios - Mostrar como Lista de Caminhos - Mostrar como Árvore de Sistema de Arquivos + Gerar mensagem de commit + Exibir como Lista de Arquivos e Diretórios + Exibir como Lista de Caminhos + Exibir como Árvore de Sistema de Arquivos ALTERAR MODO DE EXIBIÇÃO Commit: Aviso: Ao fazer o checkout de um commit, seu Head ficará desanexado @@ -82,8 +111,11 @@ Alterações Locais: Branch: Checkout Branch + Adicionar origem à mensagem de commit Commit(s): Commitar todas as alterações + Mainline: + Geralmente você não pode fazer cherry-pick de um merge commit porque você não sabe qual lado do merge deve ser considerado na mainline. Esta opção permite ao cherry-pick reaplicar a mudança relativa ao parent especificado. Cherry-Pick Você está tentando limpar todas as stashes. Tem certeza que deseja continuar? Limpar Stashes @@ -97,11 +129,13 @@ FECHAR Editor Checar Commit - Cherry-Pick Este Commit + Cherry-Pick este commit + Cherry-Pick ... Comparar com HEAD Comparar com Worktree Copiar Informações Copiar SHA + Ação customizada Rebase Interativo ${0}$ até Aqui Rebase ${0}$ até Aqui Resetar ${0}$ até Aqui @@ -109,6 +143,7 @@ Modificar Mensagem Salvar como Patch... Mesclar ao Commit Pai + Mesclar commits filhos para este Buscar Alterações... ALTERAÇÕES Arquivo LFS @@ -117,38 +152,68 @@ AUTOR ALTERADO COMMITTER + Verificar referências que contenham este commit + COMMIT EXISTE EM Mostra apenas as primeiras 100 alterações. Veja todas as alterações na aba ALTERAÇÕES. MENSAGEM PAIS REFERÊNCIAS SHA INFORMAÇÃO + Abrir no navegador Descrição Insira o assunto do commit Conteúdo do Template: Nome do Template: TEMPLATE DE COMMIT + AÇÃO CUSTOMIZADA + Argumentos: + ${REPO} - Caminho do repositório; ${SHA} - SHA do commit selecionado + Caminho do executável: + Nome: + Escopo: + Commit + Repositório + Endereço de email Endereço de email Endereço de Email Buscar remotos automaticamente Minuto(s) + Remoto padrão + Habilita --prune ao buscar + Habilita --signoff para commits GIT Adicionar Regra de Exemplo do Github Adicionar Regra de Exemplo do Jira + Adicionar Regra de Exemplo do GitLab + Adicionar regra de exemplo de Merge Request do GitLab Nova Regra Expressão Regex de Issue: Nome da Regra: Por favor, use $1, $2 para acessar os valores de grupos do regex. URL de Resultado: RASTREADOR DE PROBLEMAS + IA + Serviço desejado: + Se o 'Serviço desejado' for definido, SourceGit usará ele neste Repositório. Senão, caso haja mais de um serviço disponível, será exibido um menu para seleção. Proxy HTTP usado por este repositório Proxy HTTP Nome de usuário para este repositório Nome de Usuário Configurar Repositório + Workspaces + Cor + Restaurar abas ao inicializar + Assistente de Conventional Commit + Breaking Change: + Ticket encerrado: + Detalhes: + Escopo: + Breve resumo: + Tipo de mudança: Copiar + Copiar todo o texto Copiar Nome do Arquivo - COPIAR MENSAGEM Copiar Caminho Baseado Em: Checar o branch criado @@ -200,7 +265,8 @@ Próxima Diferença SEM MUDANÇAS OU APENAS MUDANÇAS DE EOL Diferença Anterior - Mostrar símbolos ocultos + Salvar como um Patch + Exibir símbolos ocultos Diferença Lado a Lado NOVO SUBMÓDULO @@ -208,12 +274,14 @@ Realce de Sintaxe Quebra de Linha Abrir na Ferramenta de Mesclagem + Exibir todas as linhas Diminuir Número de Linhas Visíveis Aumentar Número de Linhas Visíveis SELECIONE O ARQUIVO PARA VISUALIZAR AS MUDANÇAS Abrir na Ferramenta de Mesclagem Todas as alterações locais na cópia de trabalho. Alterações: + Incluir arquivos ignorados Um total de {0} alterações será descartado Você não pode desfazer esta ação!!! Descartar Alterações @@ -222,6 +290,8 @@ Alvo: Editar Grupo Selecionado Editar Repositório Selecionado + Executar ação customizada + Nome da ação: Fast-Forward (sem checkout) Buscar todos os remotos Buscar sem tags @@ -245,6 +315,8 @@ Usar Meu (checkout --ours) Usar Deles (checkout --theirs) Histórico de Arquivos + CONTEUDO + MUDANÇA FILTRO Branch de Desenvolvimento: Feature: @@ -279,10 +351,11 @@ Instalar hooks do Git LFS Sem Arquivos Bloqueados Bloquear - Locks LFS + Exibir apenas meus bloqueios + Bloqueios LFS Desbloquear Forçar Desbloqueio - Mostrar Locks + Exibir bloqueios Execute `git lfs prune` para excluir arquivos LFS antigos do armazenamento local Prune Execute `git lfs pull` para baixar todos os arquivos Git LFS para a referência atual e checkout @@ -316,9 +389,13 @@ Commitar mudanças preparadas Commitar e enviar mudanças preparadas Preparar todas as mudanças e commitar + Cria um novo branch partindo do commit selecionado Descartar mudanças selecionadas + Buscar, imediatamente Modo de Dashboard (Padrão) Modo de busca de commits + Puxar, imediatamente + Enviar, imediatamente Forçar recarregamento deste repositório Preparar/Despreparar mudanças selecionadas Alternar para 'Mudanças' @@ -343,11 +420,15 @@ Em: Ramo Alvo: Rebase Interativo + Copiar link + Abrir no navegador ERRO AVISO Para: Opção de Mesclagem: Ramo de Origem: + Mover nó do repositório + Selecionar nó pai para: Mesclar Ramo Nome: O Git NÃO foi configurado. Por favor, vá para [Preferências] e configure primeiro. @@ -375,10 +456,12 @@ Chave da API Prompt para Gerar Título Modelo + Nome Servidor INTELIGÊNCIA ARTIFICIAL Fonte Padrão - Tamanho da Fonte Padrão + Tamanho da fonte padrão + Tamanho da fonte do editor Fonte Monoespaçada Usar fonte monoespaçada apenas no editor de texto Tema @@ -393,7 +476,7 @@ Verificar atualizações na inicialização Idioma Commits do Histórico - Mostrar data do autor em vez da data do commit no gráfico + Exibir data do autor em vez da data do commit no gráfico Comprimento do Guia de Assunto GERAL Habilitar Auto CRLF @@ -435,6 +518,7 @@ Puxar (Buscar & Mesclar) Usar rebase em vez de merge Puxar + Certifica de que submodules foram enviadas Forçar push Branch Local: Remoto: @@ -479,6 +563,8 @@ Limpar tudo Configurar este repositório CONTINUAR + Ações customizada + Nenhuma ação customizada Habilitar opção '--reflog' Abrir no Navegador de Arquivos Pesquisar Branches/Tags/Submódulos @@ -499,7 +585,7 @@ Autor & Committer Branch Atual Pesquisar Commit - Mostrar Tags como Árvore + Exibir Tags como Árvore Estatísticas ADICIONAR SUBMÓDULO ATUALIZAR SUBMÓDULO @@ -535,12 +621,16 @@ Não há atualizações disponíveis no momento. Verificar atualizações... Squash Commits + Squash commits em: Caminho para a chave SSH privada Chave SSH Privada: INICIAR Incluir arquivos não rastreados + Manter arquivos em stage Opcional. Nome deste stash Mensagem: + Apenas mudanças em stage + Tanto mudanças em stage e fora de stage dos arquivos selecionados serão enviadas para stash!!! Guardar Alterações Locais Stash Aplicar @@ -553,6 +643,7 @@ Stashes COMMITS COMMITTER + VISÃO GERAL MÊS SEMANA AUTORES: @@ -568,7 +659,9 @@ SUBMÓDULOS OK Copiar Nome da Tag + Copiar mensage da Tag Excluir ${0}$... + Mesclar ${0}$ em ${1}$... Enviar ${0}$... Todos os submódulos Inicializar conforme necessário @@ -611,16 +704,20 @@ SEM MENSAGENS DE ENTRADA RECENTES SEM MODELOS DE COMMIT Clique com o botão direito nos arquivos selecionados e escolha como resolver conflitos. - DESSTAGEAR + UNSTAGE + UNSTAGE TODOS STAGED + UNSTAGED + STAGE + STAGE TODOS VER SUPOR NÃO ALTERADO Template: ${0}$ Alterações - Configurar Espaços de Trabalho... - ESPAÇO DE TRABALHO: + Configurar workspaces... + Workspaces: Copiar Caminho - Travar + Bloquear Remover - Destravar + Desbloquear WORKTREE diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index d2076099..3ea6a922 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -189,7 +189,6 @@ Тип изменения: Копировать Копировать весь текст - КОПИРОВАТЬ СООБЩЕНИЕ Копировать путь Копировать имя файла Создать ветку... @@ -243,6 +242,7 @@ Следующее различие НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL Предыдущее различие + Сохранить как исправление Различие бок о бок ПОДМОДУЛЬ НОВЫЙ @@ -250,6 +250,7 @@ Подсветка синтаксиса Перенос слов в строке Открыть в инструменте слияния + Показывать все линии Уменьшить количество видимых линий Увеличить количество видимых линий ВЫБРАТЬ ФАЙЛ ДЛЯ ПРОСМОТРА ИЗМЕНЕНИЙ @@ -366,9 +367,13 @@ Зафиксировать подготовленные изменения Зафиксировать и выложить подготовленные изменения Подготовить все изменения и зафиксировать + Создать новую ветку на основе выбранной ветки Отклонить выбранные изменения + Извлечение, запускается непосредственно Режим доски (по-умолчанию) Принудительно перезагрузить этот хранилище + Забрать, запускается непосредственно + Выложить, запускается непосредственно Подготовленные/Неподготовленные выбранные изменения Режим поиска фиксаций Переключить на «Изменения» @@ -391,7 +396,9 @@ Интерактивное перемещение Целевая ветка: На: - ОШИБКА + Открыть в браузере + Копировать ссылку + ОШИБКА УВЕДОМЛЕНИЕ Слить ветку В: @@ -432,6 +439,7 @@ ВИД Шрифт по-умолчанию Размер шрифта по-умолчанию + Размер шрифта редактора Моноширный шрифт В текстовом редакторе используется только моноширный шрифт Тема @@ -629,6 +637,7 @@ Копировать имя метки Копировать сообщение с метки Удалить ${0}$... + Слить ${0}$ в ${1}$... Выложить ${0}$... Сетевой адрес: Обновление подмодулей diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 469a88bb..34c8e647 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -21,8 +21,8 @@ 选填。默认使用目标文件夹名称。 跟踪分支 设置上游跟踪分支 - OpenAI助手 - 使用OpenAI助手生成提交信息 + AI助手 + 使用AI助手生成提交信息 应用补丁(apply) 错误 输出错误,并终止应用补丁 @@ -128,6 +128,7 @@ 基本信息 修改者 变更列表 + 子提交 提交者 查看包含此提交的分支/标签 本提交已被以下分支/标签包含 @@ -169,9 +170,9 @@ 规则名 : 为ISSUE生成的URL链接 : 可在URL中使用$1,$2等变量填入正则表达式匹配的内容 - OPEN AI + AI 启用特定服务 : - 当【启用特定服务】被设置时,SourceGit将在本仓库中仅使用该服务。否则将弹出可用的OpenAI服务列表供用户选择。 + 当【启用特定服务】被设置时,SourceGit将在本仓库中仅使用该服务。否则将弹出可用的AI服务列表供用户选择。 HTTP代理 HTTP网络代理 用户名 @@ -188,7 +189,6 @@ 类型: 复制 复制全部文本 - 复制内容 复制路径 复制文件名 新建分支 ... @@ -241,6 +241,7 @@ 下一个差异 没有变更或仅有换行符差异 上一个差异 + 保存为补丁文件 显示隐藏符号 分列对比 子模块 @@ -249,6 +250,7 @@ 语法高亮 自动换行 使用外部合并工具查看 + 显示完整文件 减少可见的行数 增加可见的行数 请选择需要对比的文件 @@ -365,8 +367,12 @@ 提交暂存区更改 提交暂存区更改并推送 自动暂存全部变更并提交 + 基于选中提交创建新分支 丢弃选中的更改 + 拉取 (fetch) 远程变更 切换左边栏为分支/标签等显示模式(默认) + 拉回 (pull) 远程变更 + 推送本地变更到远程 重新加载仓库状态 将选中的变更暂存或从暂存列表中移除 切换左边栏为提交搜索模式 @@ -390,6 +396,8 @@ 交互式变基 目标分支 : 起始提交 : + 在浏览器中访问 + 复制链接地址 出错了 系统提示 合并分支 @@ -421,7 +429,7 @@ 一年前 {0}年前 偏好设置 - OPEN AI + AI Analyze Diff Prompt API密钥 Generate Subject Prompt @@ -431,6 +439,7 @@ 外观配置 缺省字体 默认字体大小 + 代码字体大小 等宽字体 仅在文本编辑器中使用等宽字体 主题 @@ -628,6 +637,7 @@ 复制标签名 复制标签信息 删除 ${0}$... + 合并 ${0}$ 到 ${1}$... 推送 ${0}$... 仓库地址 : 更新子模块 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 77a832ff..a35abc1c 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -21,8 +21,8 @@ 選填。預設使用目標資料夾名稱。 追蹤分支 設定遠端追蹤分支 - OpenAI 助理 - 使用 OpenAI 產生提交訊息 + AI 助理 + 使用 AI 產生提交訊息 套用修補檔 (apply patch) 錯誤 輸出錯誤,並中止套用修補檔 @@ -128,6 +128,7 @@ 基本資訊 作者 變更列表 + 子提交 提交者 檢視包含此提交的分支或標籤 本提交包含於以下分支或標籤 @@ -169,9 +170,9 @@ 規則名稱: 為 Issue 產生的網址連結: 可在網址中使用 $1、$2 等變數填入正規表達式相符的內容 - OpenAI + AI 偏好服務: - 設定 [偏好服務] 後,SourceGit 將於此存放庫中使用該服務,否則會顯示 OpenAI 服務列表供使用者選擇。 + 設定 [偏好服務] 後,SourceGit 將於此存放庫中使用該服務,否則會顯示 AI 服務列表供使用者選擇。 HTTP 代理 HTTP 網路代理 使用者名稱 @@ -188,7 +189,6 @@ 類型: 複製 複製全部內容 - 複製內容 複製路徑 複製檔案名稱 新增分支... @@ -241,6 +241,7 @@ 下一個差異 沒有變更或僅有換行字元差異 上一個差異 + 另存為修補檔 顯示隱藏符號 並排對比 子模組 @@ -249,6 +250,7 @@ 語法上色 自動換行 使用外部合併工具檢視 + 顯示檔案的全部內容 減少可見的行數 增加可見的行數 請選擇需要對比的檔案 @@ -365,8 +367,12 @@ 提交暫存區變更 提交暫存區變更並推送 自動暫存全部變更並提交 + 根據選取的提交建立新的分支 捨棄選取的變更 + 提取 (fetch) 遠端的變更 切換左邊欄為分支/標籤等顯示模式 (預設) + 拉取 (pull) 遠端的變更 + 推送 (push) 本地變更到遠端存放庫 強制重新載入存放庫 暫存或取消暫存選取的變更 切換左邊欄為歷史搜尋模式 @@ -390,6 +396,8 @@ 互動式重定基底 目標分支: 起始提交: + 在瀏覽器中存取網址 + 複製網址 發生錯誤 系統提示 合併分支 @@ -421,7 +429,7 @@ 一年前 {0} 年前 偏好設定 - OpenAI + AI 伺服器 API 金鑰 模型 @@ -431,6 +439,7 @@ 外觀設定 預設字型 預設字型大小 + 程式碼字型大小 等寬字型 僅在文字編輯器中使用等寬字型 佈景主題 @@ -628,6 +637,7 @@ 複製標籤名稱 複製標籤訊息 刪除 ${0}$... + 合併 ${0}$ 到 ${1}$... 推送 ${0}$... 存放庫網址: 更新子模組 diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index fdfb75d5..bc0aa570 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -24,7 +24,6 @@ true true link - Speed diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 1600c4cd..d180f6d6 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -78,6 +78,12 @@ namespace SourceGit.ViewModels } } + public AvaloniaList Children + { + get; + private set; + } = new AvaloniaList(); + public string SearchChangeFilter { get => _searchChangeFilter; @@ -115,14 +121,24 @@ namespace SourceGit.ViewModels { if (remote.TryGetVisitURL(out var url)) { + var trimmedUrl = url; + if (url.EndsWith(".git")) + trimmedUrl = url.Substring(0, url.Length - 4); + if (url.StartsWith("https://github.com/", StringComparison.Ordinal)) - WebLinks.Add(new Models.CommitLink() { Name = "Github", URLPrefix = $"{url}/commit/" }); - else if (url.StartsWith("https://gitlab.com/", StringComparison.Ordinal)) - WebLinks.Add(new Models.CommitLink() { Name = "GitLab", URLPrefix = $"{url}/-/commit/" }); + WebLinks.Add(new Models.CommitLink() { Name = $"Github ({trimmedUrl.Substring(19)})", URLPrefix = $"{url}/commit/" }); + else if (url.StartsWith("https://gitlab.", StringComparison.Ordinal)) + WebLinks.Add(new Models.CommitLink() { Name = $"GitLab ({trimmedUrl.Substring(trimmedUrl.Substring(15).IndexOf('/') + 16)})", URLPrefix = $"{url}/-/commit/" }); else if (url.StartsWith("https://gitee.com/", StringComparison.Ordinal)) - WebLinks.Add(new Models.CommitLink() { Name = "Gitee", URLPrefix = $"{url}/commit/" }); + WebLinks.Add(new Models.CommitLink() { Name = $"Gitee ({trimmedUrl.Substring(18)})", URLPrefix = $"{url}/commit/" }); else if (url.StartsWith("https://bitbucket.org/", StringComparison.Ordinal)) - WebLinks.Add(new Models.CommitLink() { Name = "Bitbucket", URLPrefix = $"{url}/commits/" }); + WebLinks.Add(new Models.CommitLink() { Name = $"BitBucket ({trimmedUrl.Substring(22)})", URLPrefix = $"{url}/commits/" }); + else if (url.StartsWith("https://codeberg.org/", StringComparison.Ordinal)) + WebLinks.Add(new Models.CommitLink() { Name = $"Codeberg ({trimmedUrl.Substring(21)})", URLPrefix = $"{url}/commit/" }); + else if (url.StartsWith("https://gitea.org/", StringComparison.Ordinal)) + WebLinks.Add(new Models.CommitLink() { Name = $"Gitea ({trimmedUrl.Substring(18)})", URLPrefix = $"{url}/commit/" }); + else if (url.StartsWith("https://git.sr.ht/", StringComparison.Ordinal)) + WebLinks.Add(new Models.CommitLink() { Name = $"sourcehut ({trimmedUrl.Substring(18)})", URLPrefix = $"{url}/commit/" }); } } } @@ -186,7 +202,7 @@ namespace SourceGit.ViewModels var stream = Commands.QueryFileContent.Run(_repo.FullPath, _commit.SHA, file.Path); var fileSize = stream.Length; var bitmap = fileSize > 0 ? new Bitmap(stream) : null; - var imageType = Path.GetExtension(file.Path).TrimStart('.').ToUpper(CultureInfo.CurrentCulture); + var imageType = ext!.Substring(1).ToUpper(CultureInfo.CurrentCulture); var image = new Models.RevisionImageFile() { Image = bitmap, FileSize = fileSize, ImageType = imageType }; Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = image); } @@ -368,6 +384,16 @@ namespace SourceGit.ViewModels ev.Handled = true; }; + var openWith = new MenuItem(); + openWith.Header = App.Text("OpenWith"); + openWith.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWith.IsEnabled = File.Exists(fullPath); + openWith.Click += (_, ev) => + { + Native.OS.OpenWithDefaultEditor(fullPath); + ev.Handled = true; + }; + var saveAs = new MenuItem(); saveAs.Header = App.Text("SaveAs"); saveAs.Icon = App.CreateMenuIcon("Icons.Save"); @@ -397,6 +423,7 @@ namespace SourceGit.ViewModels }; menu.Items.Add(explore); + menu.Items.Add(openWith); menu.Items.Add(saveAs); menu.Items.Add(new MenuItem() { Header = "-" }); @@ -486,6 +513,7 @@ namespace SourceGit.ViewModels VisibleChanges = null; SelectedChanges = null; ViewRevisionFileContent = null; + Children.Clear(); if (_commit == null) return; @@ -502,6 +530,12 @@ namespace SourceGit.ViewModels Dispatcher.UIThread.Invoke(() => SignInfo = signInfo); }); + Task.Run(() => + { + var children = new Commands.QueryCommitChildren(_repo.FullPath, _commit.SHA).Result(); + Dispatcher.UIThread.Invoke(() => Children.AddRange(children)); + }); + if (_cancelToken != null) _cancelToken.Requested = true; diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs index 1a48c8e0..87b1a6de 100644 --- a/src/ViewModels/DiffContext.cs +++ b/src/ViewModels/DiffContext.cs @@ -33,12 +33,6 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _fileModeChange, value); } - public bool IsLoading - { - get => _isLoading; - private set => SetProperty(ref _isLoading, value); - } - public bool IsTextDiff { get => _isTextDiff; @@ -68,6 +62,7 @@ namespace SourceGit.ViewModels _content = previous._content; _unifiedLines = previous._unifiedLines; _ignoreWhitespace = previous._ignoreWhitespace; + _info = previous._info; } if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null") @@ -78,6 +73,12 @@ namespace SourceGit.ViewModels LoadDiffContent(); } + public void ToggleFullTextDiff() + { + Preference.Instance.UseFullTextDiff = !Preference.Instance.UseFullTextDiff; + LoadDiffContent(); + } + public void IncrUnified() { UnifiedLines = _unifiedLines + 1; @@ -103,15 +104,23 @@ namespace SourceGit.ViewModels { Content = null; IsTextDiff = false; - IsLoading = false; return; } Task.Run(() => { - var latest = new Commands.Diff(_repo, _option, _unifiedLines, _ignoreWhitespace).Result(); - var rs = null as object; + // NOTE: Here we override the UnifiedLines value (if UseFullTextDiff is on). + // There is no way to tell a git-diff to use "ALL lines of context", + // so instead we set a very high number for the "lines of context" parameter. + var numLines = Preference.Instance.UseFullTextDiff ? 999999999 : _unifiedLines; + var latest = new Commands.Diff(_repo, _option, numLines, _ignoreWhitespace).Result(); + var info = new Info(_option, numLines, _ignoreWhitespace, latest); + if (_info != null && info.IsSame(_info)) + return; + _info = info; + + var rs = null as object; if (latest.TextDiff != null) { var count = latest.TextDiff.Lines.Count; @@ -203,12 +212,11 @@ namespace SourceGit.ViewModels Dispatcher.UIThread.Post(() => { if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File) - cur.SyncScrollOffset = old.SyncScrollOffset; + cur.ScrollOffset = old.ScrollOffset; FileModeChange = latest.FileModeChange; Content = rs; IsTextDiff = rs is Models.TextDiff; - IsLoading = false; }); }); } @@ -241,14 +249,41 @@ namespace SourceGit.ViewModels ".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp" }; + private class Info + { + public string Argument { get; set; } + public int UnifiedLines { get; set; } + public bool IgnoreWhitespace { get; set; } + public string OldHash { get; set; } + public string NewHash { get; set; } + + public Info(Models.DiffOption option, int unifiedLines, bool ignoreWhitespace, Models.DiffResult result) + { + Argument = option.ToString(); + UnifiedLines = unifiedLines; + IgnoreWhitespace = ignoreWhitespace; + OldHash = result.OldHash; + NewHash = result.NewHash; + } + + public bool IsSame(Info other) + { + return Argument.Equals(other.Argument, StringComparison.Ordinal) && + UnifiedLines == other.UnifiedLines && + IgnoreWhitespace == other.IgnoreWhitespace && + OldHash.Equals(other.OldHash, StringComparison.Ordinal) && + NewHash.Equals(other.NewHash, StringComparison.Ordinal); + } + } + private readonly string _repo; private readonly Models.DiffOption _option = null; private string _title; private string _fileModeChange = string.Empty; private int _unifiedLines = 4; - private bool _isLoading = true; private bool _isTextDiff = false; private bool _ignoreWhitespace = false; private object _content = null; + private Info _info = null; } } diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 713e1635..55b04713 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Platform.Storage; @@ -152,12 +153,9 @@ namespace SourceGit.ViewModels } else if (commits.Count == 1) { - var commit = commits[0] as Models.Commit; - + var commit = (commits[0] as Models.Commit)!; if (_repo.SearchResultSelectedCommit == null || _repo.SearchResultSelectedCommit.SHA != commit.SHA) - { _repo.SearchResultSelectedCommit = _repo.SearchedCommits.Find(x => x.SHA == commit.SHA); - } AutoSelectedCommit = commit; NavigationId = _navigationId + 1; @@ -223,7 +221,7 @@ namespace SourceGit.ViewModels public ContextMenu MakeContextMenu(ListBox list) { var current = _repo.CurrentBranch; - if (current == null) + if (current == null || list.SelectedItems == null) return null; if (list.SelectedItems.Count > 1) @@ -258,6 +256,44 @@ namespace SourceGit.ViewModels multipleMenu.Items.Add(new MenuItem() { Header = "-" }); } + var saveToPatchMultiple = new MenuItem(); + saveToPatchMultiple.Icon = App.CreateMenuIcon("Icons.Diff"); + saveToPatchMultiple.Header = App.Text("CommitCM.SaveAsPatch"); + saveToPatchMultiple.Click += async (_, e) => + { + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) + return; + + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + try + { + var picker = await storageProvider.OpenFolderPickerAsync(options); + if (picker.Count == 1) + { + var saveTo = $"{picker[0].Path.LocalPath}/patches"; + var succ = false; + foreach (var c in selected) + { + succ = await Task.Run(() => new Commands.FormatPatch(_repo.FullPath, c.SHA, saveTo).Exec()); + if (!succ) + break; + } + + if (succ) + App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + } + catch (Exception exception) + { + App.RaiseException(_repo.FullPath, $"Failed to save as patch: {exception.Message}"); + } + + e.Handled = true; + }; + multipleMenu.Items.Add(saveToPatchMultiple); + multipleMenu.Items.Add(new MenuItem() { Header = "-" }); + var copyMultipleSHAs = new MenuItem(); copyMultipleSHAs.Header = App.Text("CommitCM.CopySHA"); copyMultipleSHAs.Icon = App.CreateMenuIcon("Icons.Copy"); @@ -326,7 +362,7 @@ namespace SourceGit.ViewModels if (tags.Count > 0) { foreach (var tag in tags) - FillTagMenu(menu, tag); + FillTagMenu(menu, tag, current, commit.IsMerged); menu.Items.Add(new MenuItem() { Header = "-" }); } @@ -854,7 +890,7 @@ namespace SourceGit.ViewModels menu.Items.Add(submenu); } - private void FillTagMenu(ContextMenu menu, Models.Tag tag) + private void FillTagMenu(ContextMenu menu, Models.Tag tag, Models.Branch current, bool merged) { var submenu = new MenuItem(); submenu.Header = tag.Name; @@ -873,6 +909,19 @@ namespace SourceGit.ViewModels }; submenu.Items.Add(push); + var merge = new MenuItem(); + merge.Header = new Views.NameHighlightedTextBlock("TagCM.Merge", tag.Name, current.Name); + merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.IsEnabled = !merged; + merge.Click += (_, e) => + { + if (PopupHost.CanCreatePopup()) + PopupHost.ShowPopup(new Merge(_repo, tag.Name, current.Name)); + e.Handled = true; + }; + submenu.Items.Add(merge); + submenu.Items.Add(new MenuItem() { Header = "-" }); + var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 2741650c..68065df1 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -102,6 +102,12 @@ namespace SourceGit.ViewModels set => SetProperty(ref _defaultFontSize, value); } + public double EditorFontSize + { + get => _editorFontSize; + set => SetProperty(ref _editorFontSize, value); + } + public LayoutInfo Layout { get => _layout; @@ -186,6 +192,12 @@ namespace SourceGit.ViewModels set => SetProperty(ref _showHiddenSymbolsInDiffView, value); } + public bool UseFullTextDiff + { + get => _useFullTextDiff; + set => SetProperty(ref _useFullTextDiff, value); + } + public Models.ChangeViewMode UnstagedChangeViewMode { get => _unstagedChangeViewMode; @@ -573,6 +585,7 @@ namespace SourceGit.ViewModels private bool _onlyUseMonoFontInEditor = false; private bool _useSystemWindowFrame = false; private double _defaultFontSize = 13; + private double _editorFontSize = 13; private LayoutInfo _layout = new LayoutInfo(); private int _maxHistoryCommits = 20000; @@ -591,6 +604,7 @@ namespace SourceGit.ViewModels private bool _useSyntaxHighlighting = false; private bool _enableDiffViewWordWrap = false; private bool _showHiddenSymbolsInDiffView = false; + private bool _useFullTextDiff = false; private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List; private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List; diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs index 55c85129..ffc05643 100644 --- a/src/ViewModels/RevisionCompare.cs +++ b/src/ViewModels/RevisionCompare.cs @@ -24,6 +24,11 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _endPoint, value); } + public bool CanSaveAsPatch + { + get => _canSaveAsPatch; + } + public List VisibleChanges { get => _visibleChanges; @@ -73,6 +78,7 @@ namespace SourceGit.ViewModels _repo = repo; _startPoint = (object)startPoint ?? new Models.Null(); _endPoint = (object)endPoint ?? new Models.Null(); + _canSaveAsPatch = startPoint != null && endPoint != null; Task.Run(Refresh); } @@ -105,6 +111,16 @@ namespace SourceGit.ViewModels Task.Run(Refresh); } + public void SaveAsPatch(string saveTo) + { + Task.Run(() => + { + var succ = Commands.SaveChangesAsPatch.ProcessRevisionCompareChanges(_repo, _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo); + if (succ) + Dispatcher.UIThread.Invoke(() => App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"))); + }); + } + public void ClearSearchFilter() { SearchFilter = string.Empty; @@ -218,6 +234,7 @@ namespace SourceGit.ViewModels private string _repo; private object _startPoint = null; private object _endPoint = null; + private bool _canSaveAsPatch = false; private List _changes = null; private List _visibleChanges = null; private List _selectedChanges = null; diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 020053f9..00680251 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -539,7 +539,7 @@ namespace SourceGit.ViewModels var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { - var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessLocalChanges(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); } @@ -599,7 +599,8 @@ namespace SourceGit.ViewModels byParentFolder.IsVisible = !isRooted; byParentFolder.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, Path.GetDirectoryName(change.Path) + "/"); + var path = Path.GetDirectoryName(change.Path).Replace("\\", "/"); + Commands.GitIgnore.Add(_repo.FullPath, path + "/"); e.Handled = true; }; addToIgnore.Items.Add(byParentFolder); @@ -620,7 +621,8 @@ namespace SourceGit.ViewModels byExtensionInSameFolder.IsVisible = !isRooted; byExtensionInSameFolder.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, Path.GetDirectoryName(change.Path) + "/*" + extension); + var path = Path.GetDirectoryName(change.Path).Replace("\\", "/"); + Commands.GitIgnore.Add(_repo.FullPath, path + "/*" + extension); e.Handled = true; }; addToIgnore.Items.Add(byExtensionInSameFolder); @@ -858,7 +860,7 @@ namespace SourceGit.ViewModels var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { - var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessLocalChanges(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); } @@ -981,7 +983,7 @@ namespace SourceGit.ViewModels var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { - var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessLocalChanges(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); } @@ -1156,7 +1158,7 @@ namespace SourceGit.ViewModels var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { - var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessLocalChanges(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); } diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml index 98e1c4f2..9d3afd77 100644 --- a/src/Views/Blame.axaml +++ b/src/Views/Blame.axaml @@ -57,6 +57,7 @@ Background="Transparent" Foreground="{DynamicResource Brush.FG1}" FontFamily="{DynamicResource Fonts.Monospace}" + FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=EditorFontSize}" BlameData="{Binding Data}"/> diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 164b89de..d32e4370 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -407,8 +407,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs index 90ec1af5..ca90a180 100644 --- a/src/Views/BranchCompare.axaml.cs +++ b/src/Views/BranchCompare.axaml.cs @@ -15,7 +15,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.BranchCompare vm && sender is ChangeCollectionView view) { var menu = vm.CreateChangeContextMenu(); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 081160d0..e96b2594 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -374,7 +374,7 @@ namespace SourceGit.Views if (selected.Count == 1 && selected[0] is ViewModels.BranchTreeNode { Backend: Models.Remote remote }) { var menu = repo.CreateContextMenuForRemote(remote); - this.OpenContextMenu(menu); + menu?.Open(this); return; } @@ -391,7 +391,7 @@ namespace SourceGit.Views var menu = branch.IsLocal ? repo.CreateContextMenuForLocalBranch(branch) : repo.CreateContextMenuForRemoteBranch(branch); - this.OpenContextMenu(menu); + menu?.Open(this); } else if (branches.Find(x => x.IsCurrent) == null) { @@ -405,7 +405,7 @@ namespace SourceGit.Views ev.Handled = true; }; menu.Items.Add(deleteMulti); - this.OpenContextMenu(menu); + menu?.Open(this); } } diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index 62117480..5f80f5f9 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -51,7 +51,7 @@ - + @@ -117,14 +117,42 @@ TextDecorations="Underline" Cursor="Hand" Margin="0,0,16,0" - PointerPressed="OnParentSHAPressed"/> + PointerPressed="OnSHAPressed"/> + + + + + + + + + + + + + + + + - - + + - - + + TextWrapping="Wrap"> + + + + diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index e31ddfba..c417f7a6 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -53,6 +53,15 @@ namespace SourceGit.Views set => SetValue(IssueTrackerRulesProperty, value); } + public static readonly StyledProperty> ChildrenProperty = + AvaloniaProperty.Register>(nameof(Children)); + + public AvaloniaList Children + { + get => GetValue(ChildrenProperty); + set => SetValue(ChildrenProperty, value); + } + public CommitBaseInfo() { InitializeComponent(); @@ -68,7 +77,7 @@ namespace SourceGit.Views private void OnOpenWebLink(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.CommitDetail detail) + if (DataContext is ViewModels.CommitDetail detail && sender is Control control) { var links = WebLinks; if (links.Count > 1) @@ -88,7 +97,7 @@ namespace SourceGit.Views menu.Items.Add(item); } - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); } else if (links.Count == 1) { @@ -113,7 +122,7 @@ namespace SourceGit.Views e.Handled = true; } - private void OnParentSHAPressed(object sender, PointerPressedEventArgs e) + private void OnSHAPressed(object sender, PointerPressedEventArgs e) { if (DataContext is ViewModels.CommitDetail detail && sender is Control { DataContext: string sha }) { diff --git a/src/Views/CommitChanges.axaml.cs b/src/Views/CommitChanges.axaml.cs index f197bdd5..c3d30018 100644 --- a/src/Views/CommitChanges.axaml.cs +++ b/src/Views/CommitChanges.axaml.cs @@ -16,7 +16,7 @@ namespace SourceGit.Views DataContext is ViewModels.CommitDetail vm) { var menu = vm.CreateChangeContextMenu(selected[0]); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml index cb99b3d9..4c6fd5dc 100644 --- a/src/Views/CommitDetail.axaml +++ b/src/Views/CommitDetail.axaml @@ -24,6 +24,7 @@ SignInfo="{Binding SignInfo}" SupportsContainsIn="True" WebLinks="{Binding WebLinks}" + Children="{Binding Children}" IssueTrackerRules="{Binding IssueTrackerRules}"/> diff --git a/src/Views/CommitDetail.axaml.cs b/src/Views/CommitDetail.axaml.cs index 999d1c07..f0599c66 100644 --- a/src/Views/CommitDetail.axaml.cs +++ b/src/Views/CommitDetail.axaml.cs @@ -26,7 +26,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.CommitDetail detail && sender is Grid grid && grid.DataContext is Models.Change change) { var menu = detail.CreateChangeContextMenu(change); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index 55e1dfb1..862ce9e1 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -176,7 +176,38 @@ namespace SourceGit.Views } else { - Native.OS.OpenBrowser(_lastHover.Link); + var point = e.GetCurrentPoint(this); + var link = _lastHover.Link; + + if (point.Properties.IsLeftButtonPressed) + { + Native.OS.OpenBrowser(link); + } + else if (point.Properties.IsRightButtonPressed) + { + var open = new MenuItem(); + open.Header = App.Text("IssueLinkCM.OpenInBrowser"); + open.Icon = App.CreateMenuIcon("Icons.OpenWith"); + open.Click += (_, ev) => + { + Native.OS.OpenBrowser(link); + ev.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("IssueLinkCM.CopyLink"); + copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Click += (_, ev) => + { + App.CopyText(link); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(open); + menu.Items.Add(copy); + menu.Open(this); + } } e.Handled = true; diff --git a/src/Views/CommitMessageTextBox.axaml.cs b/src/Views/CommitMessageTextBox.axaml.cs index 6eec0379..91f8c47e 100644 --- a/src/Views/CommitMessageTextBox.axaml.cs +++ b/src/Views/CommitMessageTextBox.axaml.cs @@ -125,7 +125,7 @@ namespace SourceGit.Views DescriptionEditor.CaretIndex = 0; e.Handled = true; } - else if (e.Key == Key.V && ((OperatingSystem.IsMacOS() && e.KeyModifiers == KeyModifiers.Meta) || (!OperatingSystem.IsMacOS() && e.KeyModifiers == KeyModifiers.Control))) + else if (e.Key == Key.V && e.KeyModifiers == (OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { e.Handled = true; diff --git a/src/Views/ContextMenuExtension.cs b/src/Views/ContextMenuExtension.cs deleted file mode 100644 index 2abcf2b9..00000000 --- a/src/Views/ContextMenuExtension.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.ComponentModel; -using Avalonia.Controls; - -namespace SourceGit.Views -{ - public static class ContextMenuExtension - { - public static void OpenContextMenu(this Control control, ContextMenu menu) - { - if (menu == null) - return; - - menu.PlacementTarget = control; - menu.Closing += OnContextMenuClosing; // Clear context menu because it is dynamic. - - control.ContextMenu = menu; - control.ContextMenu?.Open(); - } - - private static void OnContextMenuClosing(object sender, CancelEventArgs e) - { - if (sender is ContextMenu menu && menu.PlacementTarget != null) - menu.PlacementTarget.ContextMenu = null; - } - } -} diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index 8ccc25bb..06525abd 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -39,6 +39,9 @@ Command="{Binding IncrUnified}" IsVisible="{Binding IsTextDiff}" ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Incr}"> + + + @@ -46,10 +49,26 @@ Width="32" Command="{Binding DecrUnified}" IsVisible="{Binding IsTextDiff}" - ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Decr}" - IsEnabled="{Binding UnifiedLines, Converter={x:Static c:IntConverters.IsGreaterThanFour}}"> + ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Decr}"> + + + + + + + + + + - + diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 137fd298..9f436346 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -706,7 +706,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.Histories histories && sender is ListBox { SelectedItems: { Count: > 0 } } list) { var menu = histories.MakeContextMenu(list); - list.OpenContextMenu(menu); + menu?.Open(list); } e.Handled = true; } @@ -722,19 +722,39 @@ namespace SourceGit.Views private void OnCommitListKeyDown(object sender, KeyEventArgs e) { - if (sender is ListBox { SelectedItems: { Count: > 0 } selected } && - e.Key == Key.C && - e.KeyModifiers.HasFlag(KeyModifiers.Control)) + if (!e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) + return; + + // These shortcuts are not mentioned in the Shortcut Reference window. Is this expected? + if (sender is ListBox { SelectedItems: { Count: > 0 } selected }) { - var builder = new StringBuilder(); - foreach (var item in selected) + // CTRL/COMMAND + C -> Copy selected commit SHA and subject. + if (e.Key == Key.C) { - if (item is Models.Commit commit) - builder.AppendLine($"{commit.SHA.Substring(0, 10)} - {commit.Subject}"); + var builder = new StringBuilder(); + foreach (var item in selected) + { + if (item is Models.Commit commit) + builder.AppendLine($"{commit.SHA.Substring(0, 10)} - {commit.Subject}"); + } + + App.CopyText(builder.ToString()); + e.Handled = true; + return; } - App.CopyText(builder.ToString()); - e.Handled = true; + // CTRL/COMMAND + B -> shows Create Branch pop-up at selected commit. + if (e.Key == Key.B) + { + if (selected.Count == 1 && + selected[0] is Models.Commit commit && + DataContext is ViewModels.Histories histories && + ViewModels.PopupHost.CanCreatePopup()) + { + ViewModels.PopupHost.ShowPopup(new ViewModels.CreateBranch(histories.Repo, commit)); + e.Handled = true; + } + } } } diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index a28bc566..4fe77066 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -71,7 +71,7 @@ FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}" Margin="0,8"/> - + @@ -102,8 +102,20 @@ - - + + + + + + + + + + + + + + @@ -38,103 +39,100 @@ - - - - - - - - - - - + + + + + + + + - - - + - - - - - - + + + diff --git a/src/Views/RevisionCompare.axaml.cs b/src/Views/RevisionCompare.axaml.cs index e3ecb2b7..2c548240 100644 --- a/src/Views/RevisionCompare.axaml.cs +++ b/src/Views/RevisionCompare.axaml.cs @@ -1,5 +1,7 @@ using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; namespace SourceGit.Views { @@ -15,7 +17,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.RevisionCompare vm && sender is ChangeCollectionView view) { var menu = vm.CreateChangeContextMenu(); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; @@ -28,5 +30,27 @@ namespace SourceGit.Views e.Handled = true; } + + private async void OnSaveAsPatch(object sender, RoutedEventArgs e) + { + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel == null) + return; + + var vm = DataContext as ViewModels.RevisionCompare; + if (vm == null) + return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + if (storageFile != null) + vm.SaveAsPatch(storageFile.Path.LocalPath); + + e.Handled = true; + } } } diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml index 2084a8b2..5c2501e9 100644 --- a/src/Views/RevisionFileContentViewer.axaml +++ b/src/Views/RevisionFileContentViewer.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:m="using:SourceGit.Models" xmlns:v="using:SourceGit.Views" + xmlns:vm="using:SourceGit.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.RevisionFileContentViewer"> @@ -19,7 +20,9 @@ - + diff --git a/src/Views/RevisionFileTreeView.axaml.cs b/src/Views/RevisionFileTreeView.axaml.cs index e198f6f0..af9beb7d 100644 --- a/src/Views/RevisionFileTreeView.axaml.cs +++ b/src/Views/RevisionFileTreeView.axaml.cs @@ -229,7 +229,7 @@ namespace SourceGit.Views if (obj.Type != Models.ObjectType.Tree) { var menu = vm.CreateRevisionFileContextMenu(obj); - grid.OpenContextMenu(menu); + menu?.Open(grid); } } diff --git a/src/Views/RevisionFiles.axaml.cs b/src/Views/RevisionFiles.axaml.cs index b76e1360..53c36b1c 100644 --- a/src/Views/RevisionFiles.axaml.cs +++ b/src/Views/RevisionFiles.axaml.cs @@ -95,8 +95,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index f3048889..af32cb2c 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -28,7 +28,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.StashesPage vm && sender is Border border) { var menu = vm.MakeContextMenu(border.DataContext as Models.Stash); - border.OpenContextMenu(menu); + menu?.Open(border); } e.Handled = true; } @@ -38,7 +38,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.StashesPage vm && sender is Grid grid) { var menu = vm.MakeContextMenuForChange(grid.DataContext as Models.Change); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; } diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 29b591fb..8d4168b2 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -225,7 +225,7 @@ namespace SourceGit.Views if (selected != null && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForTag(selected); - control.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true; diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index 4ba8628e..57427321 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -23,6 +23,7 @@ DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" + FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=EditorFontSize}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -44,6 +45,7 @@ DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" + FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=EditorFontSize}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="False" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -64,6 +66,7 @@ DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" + FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=EditorFontSize}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="False" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 63833fc1..913a6340 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -589,8 +589,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } @@ -902,7 +902,7 @@ namespace SourceGit.Views var scroller = this.FindDescendantOfType(); if (scroller != null) { - scroller.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay)); + scroller.Bind(ScrollViewer.OffsetProperty, new Binding("ScrollOffset", BindingMode.TwoWay)); scroller.GotFocus += OnTextViewScrollGotFocus; } } @@ -1205,6 +1205,15 @@ namespace SourceGit.Views set => SetValue(UseSideBySideDiffProperty, value); } + public static readonly StyledProperty UseFullTextDiffProperty = + AvaloniaProperty.Register(nameof(UseFullTextDiff)); + + public bool UseFullTextDiff + { + get => GetValue(UseFullTextDiffProperty); + set => SetValue(UseFullTextDiffProperty, value); + } + public static readonly StyledProperty SelectedChunkProperty = AvaloniaProperty.Register(nameof(SelectedChunk)); @@ -1236,15 +1245,12 @@ namespace SourceGit.Views { UseSideBySideDiffProperty.Changed.AddClassHandler((v, _) => { - if (v.DataContext is Models.TextDiff diff) - { - diff.SyncScrollOffset = Vector.Zero; + v.RefreshContent(v.DataContext as Models.TextDiff, false); + }); - if (v.UseSideBySideDiff) - v.Editor.Content = new ViewModels.TwoSideTextDiff(diff); - else - v.Editor.Content = diff; - } + UseFullTextDiffProperty.Changed.AddClassHandler((v, _) => + { + v.RefreshContent(v.DataContext as Models.TextDiff, false); }); SelectedChunkProperty.Changed.AddClassHandler((v, _) => @@ -1271,25 +1277,7 @@ namespace SourceGit.Views protected override void OnDataContextChanged(EventArgs e) { base.OnDataContextChanged(e); - - if (SelectedChunk != null) - SetCurrentValue(SelectedChunkProperty, null); - - var diff = DataContext as Models.TextDiff; - if (diff == null) - { - Editor.Content = null; - GC.Collect(); - return; - } - - if (UseSideBySideDiff) - Editor.Content = new ViewModels.TwoSideTextDiff(diff, Editor.Content as ViewModels.TwoSideTextDiff); - else - Editor.Content = diff; - - IsUnstagedChange = diff.Option.IsUnstaged; - EnableChunkSelection = diff.Option.WorkingCopyChange != null; + RefreshContent(DataContext as Models.TextDiff, true); } protected override void OnPointerExited(PointerEventArgs e) @@ -1300,6 +1288,34 @@ namespace SourceGit.Views SetCurrentValue(SelectedChunkProperty, null); } + private void RefreshContent(Models.TextDiff diff, bool keepScrollOffset = true) + { + if (SelectedChunk != null) + SetCurrentValue(SelectedChunkProperty, null); + + if (diff == null) + { + Editor.Content = null; + GC.Collect(); + return; + } + + if (UseSideBySideDiff) + { + var previousContent = Editor.Content as ViewModels.TwoSideTextDiff; + Editor.Content = new ViewModels.TwoSideTextDiff(diff, keepScrollOffset ? previousContent : null); + } + else + { + if (!keepScrollOffset) + diff.ScrollOffset = Vector.Zero; + Editor.Content = diff; + } + + IsUnstagedChange = diff.Option.IsUnstaged; + EnableChunkSelection = diff.Option.WorkingCopyChange != null; + } + private void OnStageChunk(object _1, RoutedEventArgs _2) { var chunk = SelectedChunk; diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index a8045aa9..a292a6ef 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -117,7 +117,7 @@ namespace SourceGit.Views if (sender is Grid { DataContext: ViewModels.RepositoryNode node } grid) { var menu = ViewModels.Welcome.Instance.CreateContextMenu(node); - grid.OpenContextMenu(menu); + menu?.Open(grid); e.Handled = true; } } diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs index f64e1a30..df45a7f1 100644 --- a/src/Views/WorkingCopy.axaml.cs +++ b/src/Views/WorkingCopy.axaml.cs @@ -31,27 +31,27 @@ namespace SourceGit.Views { var menu = vm.CreateContextMenuForCommitMessages(); menu.Placement = PlacementMode.TopEdgeAlignedLeft; - button.OpenContextMenu(menu); + menu?.Open(button); e.Handled = true; } } private void OnUnstagedContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextMenuForUnstagedChanges(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); e.Handled = true; } } private void OnStagedContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextMenuForStagedChanges(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); e.Handled = true; } } @@ -136,10 +136,10 @@ namespace SourceGit.Views private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextForOpenAI(); - (sender as Button)?.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true;