diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 0231c64d..adbeab52 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: true - name: Set up .NET uses: actions/setup-dotnet@v4 diff --git a/TRANSLATION.md b/TRANSLATION.md index 43863997..994f52a3 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,30 +6,39 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-99.58%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.46%25-yellow)
Missing keys in de_DE.axaml +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.GotoRevisionSelector - Text.Hotkeys.Repo.GoToChild +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip +- Text.Preferences.General.Use24Hours - Text.StashCM.ApplyFileChanges +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path
-### ![es__ES](https://img.shields.io/badge/es__ES-99.69%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.90%25-yellow)
Missing keys in es_ES.axaml -- Text.GotoRevisionSelector -- Text.Hotkeys.Repo.GoToChild -- Text.StashCM.ApplyFileChanges +- Text.Preferences.General.Use24Hours
-### ![fr__FR](https://img.shields.io/badge/fr__FR-93.33%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-92.28%25-yellow)
Missing keys in fr_FR.axaml @@ -44,10 +53,15 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder - Text.Compare.WithHead - Text.Configure.Git.AskBeforeAutoUpdatingSubmodules +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.EditBranchDescription - Text.EditBranchDescription.Target - Text.FileCM.CustomAction @@ -58,6 +72,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.Zoom - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.MergeConflictEditor.AcceptBoth.MineFirst - Text.MergeConflictEditor.AcceptBoth.TheirsFirst - Text.MergeConflictEditor.UseBoth @@ -82,6 +98,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.DiffMerge.DiffArgs.Tip - Text.Preferences.DiffMerge.MergeArgs - Text.Preferences.DiffMerge.MergeArgs.Tip +- Text.Preferences.General.Use24Hours - Text.Preferences.Shell.Args - Text.Preferences.Shell.Args.Tip - Text.Repository.OpenAsFolder @@ -97,11 +114,14 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CompareWithHead - Text.WorkingCopy.Conflicts.Merge - Text.WorkingCopy.Conflicts.MergeExternal +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path - Text.Yes
-### ![id__ID](https://img.shields.io/badge/id__ID-91.25%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-90.22%25-yellow)
Missing keys in id_ID.axaml @@ -120,6 +140,10 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitCM.Drop - Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder @@ -128,6 +152,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.Git.AskBeforeAutoUpdatingSubmodules - Text.Configure.Git.ConventionalTypesOverride - Text.ConfigureCustomActionControls.StringValue.Tip +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.DropHead - Text.DropHead.Commit - Text.DropHead.NewHead @@ -144,6 +169,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.Launcher.Commands - Text.Launcher.OpenRepository - Text.MergeConflictEditor.AcceptBoth.MineFirst @@ -172,6 +199,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.DiffMerge.DiffArgs.Tip - Text.Preferences.DiffMerge.MergeArgs - Text.Preferences.DiffMerge.MergeArgs.Tip +- Text.Preferences.General.Use24Hours - Text.Preferences.Shell.Args - Text.Preferences.Shell.Args.Tip - Text.PushToNewBranch @@ -189,31 +217,62 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CompareWithHead - Text.WorkingCopy.Conflicts.Merge - Text.WorkingCopy.Conflicts.MergeExternal +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path - Text.Yes
-### ![it__IT](https://img.shields.io/badge/it__IT-98.96%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-97.84%25-yellow)
Missing keys in it_IT.axaml - Text.ChangeCM.ResetFileTo +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.GotoRevisionSelector - Text.Histories.Header.DateTime - Text.Histories.ShowColumns - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip +- Text.Preferences.General.Use24Hours - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate - Text.StashCM.ApplyFileChanges +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path
-### ![ja__JP](https://img.shields.io/badge/ja__JP-%E2%88%9A-brightgreen) +### ![ja__JP](https://img.shields.io/badge/ja__JP-98.87%25-yellow) -### ![ko__KR](https://img.shields.io/badge/ko__KR-91.56%25-yellow) +
+Missing keys in ja_JP.axaml + +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles +- Text.ConfirmEmptyCommit.StageSelectedThenCommit +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip +- Text.Preferences.General.Use24Hours +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path + +
+ +### ![ko__KR](https://img.shields.io/badge/ko__KR-90.53%25-yellow)
Missing keys in ko_KR.axaml @@ -232,12 +291,17 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder - Text.Compare.WithHead - Text.Configure.Git.AskBeforeAutoUpdatingSubmodules - Text.Configure.Git.ConventionalTypesOverride - Text.ConfigureCustomActionControls.StringValue.Tip +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.EditBranchDescription - Text.EditBranchDescription.Target - Text.FileCM.CustomAction @@ -251,6 +315,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.Launcher.Commands - Text.Launcher.OpenRepository - Text.MergeConflictEditor.AcceptBoth.MineFirst @@ -280,6 +346,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.DiffMerge.DiffArgs.Tip - Text.Preferences.DiffMerge.MergeArgs - Text.Preferences.DiffMerge.MergeArgs.Tip +- Text.Preferences.General.Use24Hours - Text.Preferences.Shell.Args - Text.Preferences.Shell.Args.Tip - Text.PushToNewBranch @@ -298,11 +365,14 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CompareWithHead - Text.WorkingCopy.Conflicts.Merge - Text.WorkingCopy.Conflicts.MergeExternal +- Text.Worktree.Branch +- Text.Worktree.Head +- Text.Worktree.Path - Text.Yes
-### ![pt__BR](https://img.shields.io/badge/pt__BR-69.27%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-68.49%25-red)
Missing keys in pt_BR.axaml @@ -325,6 +395,10 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream - Text.Clone.RecurseSubmodules +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter @@ -385,6 +459,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges - Text.ConfirmEmptyCommit.StageAllThenCommit +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.ConfirmRestart.Title - Text.ConfirmRestart.Message @@ -438,6 +513,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.InProgress.CherryPick.Head - Text.InProgress.Merge.Operating - Text.InProgress.Rebase.StoppedAt @@ -492,6 +569,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.General.ShowChangesTabInCommitDetailByDefault - Text.Preferences.General.ShowChildren - Text.Preferences.General.ShowTagsInGraph +- Text.Preferences.General.Use24Hours - Text.Preferences.General.UseGitHubStyleAvatar - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Preferences.Git.SSLVerify @@ -600,23 +678,31 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor - Text.WorkingCopy.SignOff +- Text.Worktree.Branch +- Text.Worktree.Head - Text.Worktree.Open +- Text.Worktree.Path - Text.Yes
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.69%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.18%25-yellow)
Missing keys in ru_RU.axaml -- Text.GotoRevisionSelector -- Text.Hotkeys.Repo.GoToChild -- Text.StashCM.ApplyFileChanges +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles +- Text.ConfirmEmptyCommit.StageSelectedThenCommit +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip +- Text.Preferences.General.Use24Hours
-### ![ta__IN](https://img.shields.io/badge/ta__IN-71.56%25-red) +### ![ta__IN](https://img.shields.io/badge/ta__IN-70.75%25-red)
Missing keys in ta_IN.axaml @@ -667,6 +753,10 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter @@ -719,6 +809,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges - Text.ConfirmEmptyCommit.StageAllThenCommit +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.ConfirmRestart.Title - Text.ConfirmRestart.Message @@ -763,6 +854,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.InteractiveRebase.ReorderTip - Text.Launcher.Commands - Text.Launcher.OpenRepository @@ -803,6 +896,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.General.EnableCompactFolders - Text.Preferences.General.ShowChangesPageByDefault - Text.Preferences.General.ShowChangesTabInCommitDetailByDefault +- Text.Preferences.General.Use24Hours - Text.Preferences.General.UseGitHubStyleAvatar - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Preferences.Git.UseLibsecret @@ -892,12 +986,15 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.Conflicts.UseTheirs - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor +- Text.Worktree.Branch +- Text.Worktree.Head - Text.Worktree.Open +- Text.Worktree.Path - Text.Yes
-### ![uk__UA](https://img.shields.io/badge/uk__UA-72.40%25-red) +### ![uk__UA](https://img.shields.io/badge/uk__UA-71.58%25-red)
Missing keys in uk_UA.axaml @@ -948,6 +1045,10 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CommandPalette.Branches +- Text.CommandPalette.BranchesAndTags +- Text.CommandPalette.RepositoryActions +- Text.CommandPalette.RevisionFiles - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter @@ -997,6 +1098,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfigureWorkspace.Name +- Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.ConfirmRestart.Title - Text.ConfirmRestart.Message - Text.CreateBranch.OverwriteExisting @@ -1040,6 +1142,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Init.CommandTip +- Text.Init.ErrorMessageTip - Text.InteractiveRebase.ReorderTip - Text.Launcher.Commands - Text.Launcher.OpenRepository @@ -1080,6 +1184,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.General.EnableCompactFolders - Text.Preferences.General.ShowChangesPageByDefault - Text.Preferences.General.ShowChangesTabInCommitDetailByDefault +- Text.Preferences.General.Use24Hours - Text.Preferences.General.UseGitHubStyleAvatar - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Preferences.Git.UseLibsecret @@ -1165,7 +1270,10 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.Conflicts.MergeExternal - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor +- Text.Worktree.Branch +- Text.Worktree.Head - Text.Worktree.Open +- Text.Worktree.Path - Text.Yes
diff --git a/VERSION b/VERSION index 72282df5..e2d8189b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026.05 \ No newline at end of file +2026.06 \ No newline at end of file diff --git a/depends/AvaloniaEdit b/depends/AvaloniaEdit index 80222a80..87e83c5c 160000 --- a/depends/AvaloniaEdit +++ b/depends/AvaloniaEdit @@ -1 +1 @@ -Subproject commit 80222a80b3f78d21da7db474d54a0d6a2e526c00 +Subproject commit 87e83c5c0e4c3886d5db9acf3d0b37366690f762 diff --git a/src/App.Extensions.cs b/src/App.Extensions.cs index 1e8b3a8e..1819d3c8 100644 --- a/src/App.Extensions.cs +++ b/src/App.Extensions.cs @@ -57,10 +57,5 @@ namespace SourceGit // Ignore exceptions. } } - - public static string GetRelativePath(this DirectoryInfo dir, string fullpath) - { - return fullpath.Substring(dir.FullName.Length).TrimStart(Path.DirectorySeparatorChar); - } } } diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 8753ef50..a4c25193 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -208,13 +208,14 @@ namespace SourceGit return false; } - public static async Task AskConfirmEmptyCommitAsync(bool hasLocalChanges) + public static async Task AskConfirmEmptyCommitAsync(bool hasLocalChanges, bool hasSelectedUnstaged) { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) { var confirm = new Views.ConfirmEmptyCommit(); confirm.TxtMessage.Text = Text(hasLocalChanges ? "ConfirmEmptyCommit.WithLocalChanges" : "ConfirmEmptyCommit.NoLocalChanges"); confirm.BtnStageAllAndCommit.IsVisible = hasLocalChanges; + confirm.BtnStageSelectedAndCommit.IsVisible = hasSelectedUnstaged; return await confirm.ShowDialog(owner); } diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs index a4ed972d..2e1062cf 100644 --- a/src/Commands/CompareRevisions.cs +++ b/src/Commands/CompareRevisions.cs @@ -7,9 +7,9 @@ namespace SourceGit.Commands { public partial class CompareRevisions : Command { - [GeneratedRegex(@"^([MADC])\s+(.+)$")] + [GeneratedRegex(@"^([MAD])\s+(.+)$")] private static partial Regex REG_FORMAT(); - [GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")] + [GeneratedRegex(@"^([CR])[0-9]{0,4}\s+(.+)$")] private static partial Regex REG_RENAME_FORMAT(); public CompareRevisions(string repo, string start, string end) @@ -47,8 +47,9 @@ namespace SourceGit.Commands match = REG_RENAME_FORMAT().Match(line); if (match.Success) { - var renamed = new Models.Change() { Path = match.Groups[1].Value }; - renamed.Set(Models.ChangeState.Renamed); + var type = match.Groups[1].Value; + var renamed = new Models.Change() { Path = match.Groups[2].Value }; + renamed.Set(type == "R" ? Models.ChangeState.Renamed : Models.ChangeState.Copied); changes.Add(renamed); } @@ -72,10 +73,6 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Deleted); changes.Add(change); break; - case 'C': - change.Set(Models.ChangeState.Copied); - changes.Add(change); - break; } } diff --git a/src/Commands/QueryFileHistory.cs b/src/Commands/QueryFileHistory.cs new file mode 100644 index 00000000..d112377a --- /dev/null +++ b/src/Commands/QueryFileHistory.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SourceGit.Commands +{ + public partial class QueryFileHistory : Command + { + [GeneratedRegex(@"^([MAD])\s+(.+)$")] + private static partial Regex REG_FORMAT(); + [GeneratedRegex(@"^([CR])[0-9]{0,4}\s+(.+)$")] + private static partial Regex REG_RENAME_FORMAT(); + + public QueryFileHistory(string repo, string path, string head) + { + WorkingDirectory = repo; + Context = repo; + RaiseError = false; + + var builder = new StringBuilder(); + builder.Append("log --no-show-signature --date-order -n 10000 --decorate=no --format=\"@%H%x00%P%x00%aN±%aE%x00%at%x00%s\" --follow --name-status "); + if (!string.IsNullOrEmpty(head)) + builder.Append(head).Append(" "); + builder.Append("-- ").Append(path.Quoted()); + + Args = builder.ToString(); + } + + public async Task> GetResultAsync() + { + var versions = new List(); + var rs = await ReadToEndAsync().ConfigureAwait(false); + if (!rs.IsSuccess) + return versions; + + var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + if (lines.Length == 0) + return versions; + + Models.FileVersion last = null; + foreach (var line in lines) + { + if (line.StartsWith('@')) + { + var parts = line.Split('\0'); + if (parts.Length != 5) + continue; + + last = new Models.FileVersion(); + last.SHA = parts[0].Substring(1); + last.HasParent = !string.IsNullOrEmpty(parts[1]); + last.Author = Models.User.FindOrAdd(parts[2]); + last.AuthorTime = ulong.Parse(parts[3]); + last.Subject = parts[4]; + versions.Add(last); + } + else if (last != null) + { + var match = REG_FORMAT().Match(line); + if (!match.Success) + { + match = REG_RENAME_FORMAT().Match(line); + if (match.Success) + { + var type = match.Groups[1].Value; + last.Change.Path = match.Groups[2].Value; + last.Change.Set(type == "R" ? Models.ChangeState.Renamed : Models.ChangeState.Copied); + } + + continue; + } + + last.Change.Path = match.Groups[2].Value; + + var status = match.Groups[1].Value; + switch (status[0]) + { + case 'M': + last.Change.Set(Models.ChangeState.Modified); + break; + case 'A': + last.Change.Set(Models.ChangeState.Added); + break; + case 'D': + last.Change.Set(Models.ChangeState.Deleted); + break; + } + } + } + + return versions; + } + } +} diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs index 229d9e65..cff939e2 100644 --- a/src/Commands/QueryStagedChangesWithAmend.cs +++ b/src/Commands/QueryStagedChangesWithAmend.cs @@ -6,9 +6,9 @@ namespace SourceGit.Commands { public partial class QueryStagedChangesWithAmend : Command { - [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMT])\d{0,6}\t(.*)$")] + [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ADMT])\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.*)$")] + [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([RC])\d{0,6}\t(.*\t.*)$")] private static partial Regex REG_FORMAT2(); public QueryStagedChangesWithAmend(string repo, string parent) @@ -34,7 +34,7 @@ namespace SourceGit.Commands { var change = new Models.Change() { - Path = match.Groups[3].Value, + Path = match.Groups[4].Value, DataForAmend = new Models.ChangeDataForAmend() { FileMode = match.Groups[1].Value, @@ -42,7 +42,8 @@ namespace SourceGit.Commands ParentSHA = _parent, }, }; - change.Set(Models.ChangeState.Renamed); + var type = match.Groups[3].Value; + change.Set(type == "R" ? Models.ChangeState.Renamed : Models.ChangeState.Copied); changes.Add(change); continue; } @@ -67,9 +68,6 @@ namespace SourceGit.Commands case "A": change.Set(Models.ChangeState.Added); break; - case "C": - change.Set(Models.ChangeState.Copied); - break; case "D": change.Set(Models.ChangeState.Deleted); break; diff --git a/src/Commands/Reset.cs b/src/Commands/Reset.cs index cfcd337a..f640d0de 100644 --- a/src/Commands/Reset.cs +++ b/src/Commands/Reset.cs @@ -13,7 +13,7 @@ { WorkingDirectory = repo; Context = repo; - Args = $"reset HEAD --pathspec-from-file={pathspec.Quoted()}"; + Args = $"reset --pathspec-from-file={pathspec.Quoted()}"; } } } diff --git a/src/Commands/Worktree.cs b/src/Commands/Worktree.cs index 50ee9724..7b70e2ab 100644 --- a/src/Commands/Worktree.cs +++ b/src/Commands/Worktree.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Text; using System.Threading.Tasks; @@ -29,7 +28,6 @@ namespace SourceGit.Commands if (line.StartsWith("worktree ", StringComparison.Ordinal)) { last = new Models.Worktree() { FullPath = line.Substring(9).Trim() }; - last.RelativePath = Path.GetRelativePath(WorkingDirectory, last.FullPath); worktrees.Add(last); continue; } @@ -39,8 +37,7 @@ namespace SourceGit.Commands if (line.StartsWith("bare", StringComparison.Ordinal)) { - worktrees.Remove(last); - last = null; + last.IsBare = true; } else if (line.StartsWith("HEAD ", StringComparison.Ordinal)) { diff --git a/src/Converters/InteractiveRebaseActionConverters.cs b/src/Converters/InteractiveRebaseActionConverters.cs index 76967ce3..81f5564a 100644 --- a/src/Converters/InteractiveRebaseActionConverters.cs +++ b/src/Converters/InteractiveRebaseActionConverters.cs @@ -6,7 +6,7 @@ namespace SourceGit.Converters public static class InteractiveRebaseActionConverters { public static readonly FuncValueConverter ToIconBrush = - new FuncValueConverter(v => + new(v => { return v switch { @@ -20,6 +20,12 @@ namespace SourceGit.Converters }); public static readonly FuncValueConverter ToName = - new FuncValueConverter(v => v.ToString()); + new(v => v.ToString()); + + public static readonly FuncValueConverter IsDrop = + new(v => v == Models.InteractiveRebaseAction.Drop); + + public static readonly FuncValueConverter ToOpacity = + new(v => v > Models.InteractiveRebaseAction.Reword ? 0.65 : 1.0); } } diff --git a/src/Models/Blame.cs b/src/Models/Blame.cs index 6a75e750..a8fac34c 100644 --- a/src/Models/Blame.cs +++ b/src/Models/Blame.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SourceGit.Models { @@ -10,7 +9,6 @@ namespace SourceGit.Models public string File { get; set; } = string.Empty; public string Author { get; set; } = string.Empty; public ulong Timestamp { get; set; } = 0; - public string Time => DateTime.UnixEpoch.AddSeconds(Timestamp).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly); } public class BlameData diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 2ad448ad..baf6e850 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -60,7 +60,7 @@ Index = index; WorkTree = workTree; - if (index == ChangeState.Renamed || workTree == ChangeState.Renamed) + if (index == ChangeState.Renamed || index == ChangeState.Copied || workTree == ChangeState.Renamed) { var parts = Path.Split('\t', 2); if (parts.Length < 2) diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 4a98b985..60501ba4 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -30,11 +30,6 @@ namespace SourceGit.Models public int Color { get; set; } = 0; public double LeftMargin { get; set; } = 0; - public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime); - public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime); - public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly); - public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly); - public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime; public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null; public bool HasDecorators => Decorators.Count > 0; diff --git a/src/Models/ConfirmEmptyCommitResult.cs b/src/Models/ConfirmEmptyCommitResult.cs index 176845b9..9c36493e 100644 --- a/src/Models/ConfirmEmptyCommitResult.cs +++ b/src/Models/ConfirmEmptyCommitResult.cs @@ -3,6 +3,7 @@ public enum ConfirmEmptyCommitResult { Cancel = 0, + StageSelectedAndCommit, StageAllAndCommit, CreateEmptyCommit, } diff --git a/src/Models/DateTimeFormat.cs b/src/Models/DateTimeFormat.cs index 16276c40..9010e523 100644 --- a/src/Models/DateTimeFormat.cs +++ b/src/Models/DateTimeFormat.cs @@ -5,19 +5,20 @@ namespace SourceGit.Models { public class DateTimeFormat { - public string DateOnly { get; set; } - public string DateTime { get; set; } - - public string Example + public static readonly List Supported = new List { - get => _example.ToString(DateTime); - } - - public DateTimeFormat(string dateOnly, string dateTime) - { - DateOnly = dateOnly; - DateTime = dateTime; - } + new("yyyy/MM/dd"), + new("yyyy.MM.dd"), + new("yyyy-MM-dd"), + new("MM/dd/yyyy"), + new("MM.dd.yyyy"), + new("MM-dd-yyyy"), + new("dd/MM/yyyy"), + new("dd.MM.yyyy"), + new("dd-MM-yyyy"), + new("MMM d yyyy"), + new("d MMM yyyy"), + }; public static int ActiveIndex { @@ -25,26 +26,41 @@ namespace SourceGit.Models set; } = 0; - public static DateTimeFormat Active + public static bool Use24Hours { - get => Supported[ActiveIndex]; + get; + set; + } = true; + + public string DateFormat + { + get; } - public static readonly List Supported = new List + public string Example { - new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd, HH:mm:ss"), - new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd, HH:mm:ss"), - new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd, HH:mm:ss"), - new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy, HH:mm:ss"), - new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy, HH:mm:ss"), - new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy, HH:mm:ss"), - new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy, HH:mm:ss"), - new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy, HH:mm:ss"), - new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy, HH:mm:ss"), - new DateTimeFormat("MMM d yyyy", "MMM d yyyy, HH:mm:ss"), - new DateTimeFormat("d MMM yyyy", "d MMM yyyy, HH:mm:ss"), - }; + get => DateTime.Now.ToString(DateFormat); + } - private static readonly DateTime _example = new DateTime(2025, 1, 31, 8, 0, 0, DateTimeKind.Local); + public DateTimeFormat(string date) + { + DateFormat = date; + } + + public static string Format(ulong timestamp, bool dateOnly = false) + { + var localTime = DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime(); + return Format(localTime, dateOnly); + } + + public static string Format(DateTime localTime, bool dateOnly = false) + { + var actived = Supported[ActiveIndex]; + if (dateOnly) + return localTime.ToString(actived.DateFormat); + + var format = Use24Hours ? $"{actived.DateFormat} HH:mm:ss" : $"{actived.DateFormat} hh:mm:ss tt"; + return localTime.ToString(format); + } } } diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index 2ecfe458..0dc8bc31 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text; namespace SourceGit.Models @@ -79,6 +80,63 @@ namespace SourceGit.Models _path = file; } + /// + /// Used to diff in `FileHistory` + /// + /// + public DiffOption(FileVersion ver) + { + if (string.IsNullOrEmpty(ver.OriginalPath)) + { + _revisions.Add(ver.HasParent ? $"{ver.SHA}^" : Commit.EmptyTreeSHA1); + _revisions.Add(ver.SHA); + _path = ver.Path; + } + else + { + _revisions.Add($"{ver.SHA}^:{ver.OriginalPath.Quoted()}"); + _revisions.Add($"{ver.SHA}:{ver.Path.Quoted()}"); + _path = ver.Path; + _orgPath = ver.Change.OriginalPath; + _ignorePaths = true; + } + } + + /// + /// Used to diff two revisions in `FileHistory` + /// + /// + /// + public DiffOption(FileVersion start, FileVersion end) + { + if (start.Change.Index == ChangeState.Deleted) + { + _revisions.Add(Commit.EmptyTreeSHA1); + _revisions.Add(end.SHA); + _path = end.Path; + } + else if (end.Change.Index == ChangeState.Deleted) + { + _revisions.Add(start.SHA); + _revisions.Add(Commit.EmptyTreeSHA1); + _path = start.Path; + } + else if (!end.Path.Equals(start.Path, StringComparison.Ordinal)) + { + _revisions.Add($"{start.SHA}:{start.Path.Quoted()}"); + _revisions.Add($"{end.SHA}:{end.Path.Quoted()}"); + _path = end.Path; + _orgPath = start.Path; + _ignorePaths = true; + } + else + { + _revisions.Add(start.SHA); + _revisions.Add(end.SHA); + _path = start.Path; + } + } + /// /// Used to show differences between two revisions. /// @@ -104,6 +162,9 @@ namespace SourceGit.Models foreach (var r in _revisions) builder.Append($"{r} "); + if (_ignorePaths) + return builder.ToString(); + builder.Append("-- "); if (!string.IsNullOrEmpty(_orgPath)) builder.Append($"{_orgPath.Quoted()} "); @@ -118,5 +179,6 @@ namespace SourceGit.Models private readonly string _orgPath = string.Empty; private readonly string _extra = string.Empty; private readonly List _revisions = []; + private readonly bool _ignorePaths = false; } } diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index 6c381df3..df8f204c 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -124,9 +124,9 @@ namespace SourceGit.Models continue; if (i >= selection.StartLine - 1 && i < selection.EndLine) - writer.WriteLine($"+{line.Content}"); + WriteLine(writer, '+', line); else - writer.WriteLine($" {line.Content}"); + WriteLine(writer, ' ', line); } } else @@ -137,11 +137,10 @@ namespace SourceGit.Models var line = Lines[i]; if (line.Type != TextDiffLineType.Added) continue; - writer.WriteLine($"+{line.Content}"); + WriteLine(writer, '+', line); } } - writer.WriteLine("\\ No newline at end of file"); writer.Flush(); } @@ -255,7 +254,8 @@ namespace SourceGit.Models } } - writer.WriteLine($" {tail}"); + if (!string.IsNullOrEmpty(tail)) + writer.WriteLine($" {tail}"); writer.Flush(); } @@ -406,7 +406,8 @@ namespace SourceGit.Models } } - writer.WriteLine($" {tail}"); + if (!string.IsNullOrEmpty(tail)) + writer.WriteLine($" {tail}"); writer.Flush(); } diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs index abd2ae49..d6d1030a 100644 --- a/src/Models/ExternalTool.cs +++ b/src/Models/ExternalTool.cs @@ -230,10 +230,14 @@ namespace SourceGit.Models return null; var options = new List(); + var prefixLen = root.FullName.Length; root.WalkFiles(f => { if (f.EndsWith(".code-workspace", StringComparison.OrdinalIgnoreCase)) - options.Add(new(root.GetRelativePath(f), f.Quoted())); + { + var display = f.Substring(prefixLen).TrimStart(Path.DirectorySeparatorChar); + options.Add(new(display, f.Quoted())); + } }, 2); return options; } diff --git a/src/Models/FileVersion.cs b/src/Models/FileVersion.cs new file mode 100644 index 00000000..d822b960 --- /dev/null +++ b/src/Models/FileVersion.cs @@ -0,0 +1,14 @@ +namespace SourceGit.Models +{ + public class FileVersion + { + public string SHA { get; set; } = string.Empty; + public bool HasParent { get; set; } = false; + public User Author { get; set; } = User.Invalid; + public ulong AuthorTime { get; set; } = 0; + public string Subject { get; set; } = string.Empty; + public Change Change { get; set; } = new(); + public string Path => Change.Path; + public string OriginalPath => Change.OriginalPath; + } +} diff --git a/src/Models/SelfUpdate.cs b/src/Models/SelfUpdate.cs index 116b1b04..9cf95a14 100644 --- a/src/Models/SelfUpdate.cs +++ b/src/Models/SelfUpdate.cs @@ -28,7 +28,7 @@ namespace SourceGit.Models public bool IsNewVersion => CurrentVersion.CompareTo(new System.Version(TagName.Substring(1))) < 0; [JsonIgnore] - public string ReleaseDateStr => PublishedAt.ToString(DateTimeFormat.Active.DateOnly); + public string ReleaseDateStr => DateTimeFormat.Format(PublishedAt, true); public Version() { diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index bc01e9db..4f5ad692 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SourceGit.Models { @@ -10,24 +9,6 @@ namespace SourceGit.Models public List Parents { get; set; } = []; public ulong Time { get; set; } = 0; public string Message { get; set; } = ""; - - public string Subject - { - get - { - return Message.Split('\n', 2)[0].Trim(); - } - } - - public string TimeStr - { - get - { - return DateTime.UnixEpoch - .AddSeconds(Time) - .ToLocalTime() - .ToString(DateTimeFormat.Active.DateTime); - } - } + public string Subject => Message.Split('\n', 2)[0].Trim(); } } diff --git a/src/Models/Tag.cs b/src/Models/Tag.cs index 9e165457..5e3ed7b4 100644 --- a/src/Models/Tag.cs +++ b/src/Models/Tag.cs @@ -1,6 +1,4 @@ -using System; - -namespace SourceGit.Models +namespace SourceGit.Models { public enum TagSortMode { @@ -16,10 +14,5 @@ namespace SourceGit.Models public User Creator { get; set; } = null; public ulong CreatorDate { get; set; } = 0; public string Message { get; set; } = string.Empty; - - public string CreatorDateStr - { - get => DateTime.UnixEpoch.AddSeconds(CreatorDate).ToLocalTime().ToString(DateTimeFormat.Active.DateTime); - } } } diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs index 00eb33a0..27a9415d 100644 --- a/src/Models/Worktree.cs +++ b/src/Models/Worktree.cs @@ -1,39 +1,12 @@ -using System; -using CommunityToolkit.Mvvm.ComponentModel; - namespace SourceGit.Models { - public class Worktree : ObservableObject + public class Worktree { public string Branch { get; set; } = string.Empty; public string FullPath { get; set; } = string.Empty; - public string RelativePath { get; set; } = string.Empty; public string Head { get; set; } = string.Empty; + public bool IsBare { get; set; } = false; public bool IsDetached { get; set; } = false; - - public bool IsLocked - { - get => _isLocked; - set => SetProperty(ref _isLocked, value); - } - - public string Name - { - get - { - if (IsDetached) - return $"detached HEAD at {Head.AsSpan(10)}"; - - if (Branch.StartsWith("refs/heads/", StringComparison.Ordinal)) - return Branch.Substring(11); - - if (Branch.StartsWith("refs/remotes/", StringComparison.Ordinal)) - return Branch.Substring(13); - - return Branch; - } - } - - private bool _isLocked = false; + public bool IsLocked { get; set; } = false; } } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index c96a74ca..6fbcd880 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -234,11 +234,15 @@ namespace SourceGit.Native return null; var options = new List(); + var prefixLen = root.FullName.Length; root.WalkFiles(f => { if (f.EndsWith(".sln", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".slnx", StringComparison.OrdinalIgnoreCase)) - options.Add(new(root.GetRelativePath(f), f.Quoted())); + { + var display = f.Substring(prefixLen).TrimStart(Path.DirectorySeparatorChar); + options.Add(new(display, f.Quoted())); + } }); return options; } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 81a8543f..0866c1a0 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -134,6 +134,10 @@ Repository URL: CLOSE Editor + Branches + Branches & Tags + Repository Custom Actions + Revision Files Checkout Commit Cherry-Pick Commit Cherry-Pick ... @@ -277,7 +281,8 @@ CONTINUE Empty commit detected! Do you want to continue (--allow-empty)? STAGE ALL & COMMIT - Empty commit detected! Do you want to continue (--allow-empty) or stage all then commit? + STAGE SELECTED & COMMIT + Empty commit detected! Do you want to continue (--allow-empty) or auto-stage then commit? Restart Required You need to restart this app to apply changes. Conventional Commit Helper @@ -309,7 +314,7 @@ Tag Name: Recommended format: v1.0.0-alpha Push to all remotes after created - Create New Tag + Create Tag Kind: annotated lightweight @@ -519,6 +524,8 @@ Stage Unstage Initialize Repository + Do you want to run `git init` command under this path? + Open repository failed. Reason: Path: Cherry-Pick in progress. Processing commit @@ -642,6 +649,7 @@ Show children in the commit details Show tags in commit graph Subject Guide Length + 24-Hours Generate Github style default avatar GIT Enable Auto CRLF @@ -969,9 +977,12 @@ WORKSPACE: Configure Workspaces... WORKTREE + BRANCH Copy Path + HEAD Lock Open + PATH Remove Unlock YES diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 4b327c6c..736cc079 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -138,6 +138,10 @@ URL del Repositorio: CERRAR Editor + Ramas + Ramas & Etiquetas + Acciones Personalizadas del Repositorio + Archivos de Revisión Checkout Commit Cherry-Pick Este Commit Cherry-Pick ... @@ -281,6 +285,7 @@ CONTINUAR ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty)? HACER STAGE A TODO & COMMIT + STAGE LO SELECCIONADO & COMMIT ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty) o hacer stage a todo y después commit? Reinicio Requerido Necesita reiniciar esta aplicación para aplicar los cambios. @@ -473,6 +478,7 @@ Remoto: Seguir archivos llamados '{0}' Seguir todos los archivos *{0} + Seleccionar Commit Historias AUTOR HORA DEL AUTOR @@ -502,6 +508,7 @@ Stage todos los cambios y commit Fetch, empieza directamente Modo Dashboard (Por Defecto) + Ir al hijo del commit seleccionado Ir al padre del commit seleccionado Abrir paleta de comandos Modo de búsqueda de commits @@ -521,6 +528,8 @@ Stage Unstage Inicializar Repositorio + ¿Quieres correr el comando `git init` en esta ruta? + Falló la apertura del repositorio. Razón: Ruta: Cherry-Pick en progreso. Procesando commit @@ -853,6 +862,7 @@ ¡Tanto los cambios staged como los no staged de los archivos seleccionados serán stashed! Stash Cambios Locales Aplicar + Aplicar Cambios Copiar Mensaje Eliminar Guardar como Parche... @@ -970,9 +980,12 @@ ESPACIO DE TRABAJO: Configura Espacios de Trabajo... WORKTREE + RAMA Copiar Ruta + HEAD Bloquear Abrir + RUTA Eliminar Desbloquear diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index ea989bb4..0a566ac3 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -473,6 +473,7 @@ Внешнее хранилище: Отслеживать файлы с именем «{0}» Отслеживать все файлы (*{0}) + Выбрать ревизию Истории АВТОР ВРЕМЯ АВТОРА @@ -502,7 +503,8 @@ Сформировать все изменения и зафиксировать Извлечь (fetch), запускается сразу Режим доски (по умолчанию) - Перейти к родительскому выбранной ревизии + К дочернему выбранной ревизии + К родительскому выбранной ревизии Открыть палитру команд Режим поиска ревизий Загрузить (pull), запускается сразу @@ -853,6 +855,7 @@ Сформированные так и несформированные изменения выбранных файлов будут сохранены!!! Отложить локальные изменения Принять + Применить изменения Копировать сообщение Отбросить Сохранить как заплатку... @@ -970,9 +973,12 @@ РАБОЧЕЕ ПРОСТРАНСТВО: Настройка рабочего пространства... РАБОЧИЙ КАТАЛОГ + ВЕТКА Копировать путь + ГОЛОВА Заблокировать Открыть + ПУТЬ Удалить Разблокировать Да diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 29094248..4dc0d07a 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -138,6 +138,10 @@ 远程仓库 : 关闭 提交信息编辑器 + 分支列表 + 分支 & 标签 + 自定义操作列表 + 文件列表 检出此提交 挑选(cherry-pick)此提交 挑选(cherry-pick)... @@ -280,8 +284,9 @@ 启动时恢复打开的仓库 确认继续 提交未包含变更文件!是否继续(--allow-empty)? - 自动暂存并提交 - 提交未包含变更文件!是否继续(--allow-empty)或是自动暂存所有变更并提交? + 暂存所有变更并提交 + 仅暂存所选变更并提交 + 提交未包含变更文件!是否继续(--allow-empty)或是自动暂存变更并提交? 系统提示 程序需要重新启动,以便修改生效! 规范化提交信息生成 @@ -523,6 +528,8 @@ 暂存 移出暂存区 初始化新仓库 + 是否在该路径下执行 `git init` 命令(初始化仓库)? + 打开本地仓库失败,原因: 路径 : 挑选(Cherry-Pick)操作进行中。 正在处理提交 @@ -646,6 +653,7 @@ 在提交详情页中显示子提交列表 在提交路线图中显示标签 SUBJECT字数检测 + 24小时制 生成GitHub风格的默认头像 GIT配置 自动换行转换 @@ -973,9 +981,12 @@ 工作区: 配置工作区... 本地工作树 + 連結分支 复制工作树路径 + 最新提交 锁定工作树 打开工作树 + 位置 移除工作树 解除工作树锁定 好的 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 0dc4afc4..8d3659b0 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -138,6 +138,10 @@ 遠端存放庫: 關閉 提交訊息編輯器 + 分支列表 + 分支 & 標籤 + 自訂動作 + 檔案列表 簽出 (checkout) 此提交 揀選 (cherry-pick) 此提交 揀選 (cherry-pick)... @@ -280,8 +284,9 @@ 啟動時還原上次開啟的存放庫 確認繼續 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)? - 自動暫存並提交 - 未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存全部變更並提交? + 暫存全部變更並提交 + 僅暫存所選變更并提交 + 未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存變更並提交? 系統提示 您需要重新啟動此應用程式才能套用變更! 產生約定式提交訊息 @@ -523,6 +528,8 @@ 暫存 取消暫存 初始化存放庫 + 在該路徑執行 git init 以初始化 git 倉庫? + 無法在指定路徑開啟本機儲存庫。原因: 路徑: 揀選 (cherry-pick) 操作進行中。 正在處理提交 @@ -646,6 +653,7 @@ 在提交詳細資訊中顯示後續提交 在路線圖中顯示標籤 提交標題字數偵測 + 24小時制 產生 GitHub 風格的預設頭貼 Git 設定 自動換行轉換 @@ -973,9 +981,12 @@ 工作區: 設定工作區... 本機工作區 + 关联分支 複製工作區路徑 + 最新提交 鎖定工作區 開啟工作區 + 位置 移除工作區 解除鎖定工作區 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 4ca2e74f..3167a8c2 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -182,6 +182,14 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/ExecuteCustomActionCommandPalette.axaml.cs b/src/Views/ExecuteCustomActionCommandPalette.axaml.cs new file mode 100644 index 00000000..8986b3b7 --- /dev/null +++ b/src/Views/ExecuteCustomActionCommandPalette.axaml.cs @@ -0,0 +1,63 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views +{ + public partial class ExecuteCustomActionCommandPalette : UserControl + { + public ExecuteCustomActionCommandPalette() + { + InitializeComponent(); + } + + protected override async void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (DataContext is not ViewModels.ExecuteCustomActionCommandPalette vm) + return; + + if (e.Key == Key.Enter) + { + await vm.ExecAsync(); + e.Handled = true; + } + else if (e.Key == Key.Up) + { + if (ActionListBox.IsKeyboardFocusWithin) + { + FilterTextBox.Focus(NavigationMethod.Directional); + e.Handled = true; + return; + } + } + else if (e.Key == Key.Down || e.Key == Key.Tab) + { + if (FilterTextBox.IsKeyboardFocusWithin) + { + if (vm.VisibleActions.Count > 0) + ActionListBox.Focus(NavigationMethod.Directional); + + e.Handled = true; + return; + } + + if (ActionListBox.IsKeyboardFocusWithin && e.Key == Key.Tab) + { + FilterTextBox.Focus(NavigationMethod.Directional); + e.Handled = true; + return; + } + } + } + + private async void OnItemTapped(object sender, TappedEventArgs e) + { + if (DataContext is ViewModels.ExecuteCustomActionCommandPalette vm) + { + await vm.ExecAsync(); + e.Handled = true; + } + } + } +} diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 05530f1e..f929b0a6 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -59,8 +59,8 @@ BorderThickness="1" Margin="8,4,4,8" BorderBrush="{DynamicResource Brush.Border2}" - ItemsSource="{Binding Commits}" - SelectedItems="{Binding SelectedCommits, Mode=TwoWay}" + ItemsSource="{Binding Revisions}" + SelectedItems="{Binding SelectedRevisions, Mode=TwoWay}" SelectionMode="Multiple" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> @@ -79,7 +79,7 @@ - + @@ -93,7 +93,12 @@ TextDecorations="Underline" Margin="8,0,0,0" PointerPressed="OnPressCommitSHA"/> - + @@ -110,7 +115,8 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" BorderThickness="1,0,0,0" - BorderBrush="{DynamicResource Brush.Border0}"/> + BorderBrush="{DynamicResource Brush.Border0}" + Focusable="False"/> @@ -195,19 +201,19 @@ - + - - + + - - - - - + + - + diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index 793fd411..749b4862 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -16,10 +16,10 @@ namespace SourceGit.Views private void OnPressCommitSHA(object sender, PointerPressedEventArgs e) { - if (sender is TextBlock { DataContext: Models.Commit commit } && + if (sender is TextBlock { DataContext: Models.FileVersion ver } && DataContext is ViewModels.FileHistories vm) { - vm.NavigateToCommit(commit); + vm.NavigateToCommit(ver); } e.Handled = true; @@ -76,12 +76,12 @@ namespace SourceGit.Views private void OnCommitSubjectPointerMoved(object sender, PointerEventArgs e) { - if (sender is Border { DataContext: Models.Commit commit } border && + if (sender is Border { DataContext: Models.FileVersion ver } border && DataContext is ViewModels.FileHistories vm) { var tooltip = ToolTip.GetTip(border); if (tooltip == null) - ToolTip.SetTip(border, vm.GetCommitFullMessage(commit)); + ToolTip.SetTip(border, vm.GetCommitFullMessage(ver)); } } diff --git a/src/Views/FileHistoryCommandPalette.axaml b/src/Views/FileHistoryCommandPalette.axaml index ecc3fed3..af16f1b3 100644 --- a/src/Views/FileHistoryCommandPalette.axaml +++ b/src/Views/FileHistoryCommandPalette.axaml @@ -8,7 +8,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.FileHistoryCommandPalette" x:DataType="vm:FileHistoryCommandPalette"> - + - + + - + @@ -38,8 +37,7 @@ VerticalAlignment="Center" CornerRadius="2" Watermark="{DynamicResource Text.GitFlow.StartPlaceholder}" - Text="{Binding Name, Mode=TwoWay}" - v:AutoFocusBehaviour.IsEnabled="True"/> + Text="{Binding Name, Mode=TwoWay}"/> diff --git a/src/Views/GotoRevisionSelector.axaml b/src/Views/GotoRevisionSelector.axaml index f2f363d6..a2bd55bb 100644 --- a/src/Views/GotoRevisionSelector.axaml +++ b/src/Views/GotoRevisionSelector.axaml @@ -87,11 +87,11 @@ Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" VerticalAlignment="Center"/> - - + + diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 1a10f11d..0b55e9c8 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -157,6 +157,7 @@ + ShowAsDateTime="{Binding Source={x:Static vm:Preferences.Instance}, Path=!DisplayTimeAsPeriodInHistories, Mode=OneWay}" + DateTimeFormat="{Binding Source={x:Static vm:Preferences.Instance}, Path=DateTimeFormat, Mode=OneWay}" + Use24Hours="{Binding Source={x:Static vm:Preferences.Instance}, Path=Use24Hours, Mode=OneWay}"/> @@ -277,7 +279,8 @@ MinWidth="1" MinHeight="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{DynamicResource Brush.Window}" - BorderBrush="{DynamicResource Brush.Border0}"/> + BorderBrush="{DynamicResource Brush.Border0}" + Focusable="False"/> diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index e800906a..a4846e48 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -87,7 +87,7 @@ - + diff --git a/src/Views/Init.axaml b/src/Views/Init.axaml index e0ea1a27..a83ea507 100644 --- a/src/Views/Init.axaml +++ b/src/Views/Init.axaml @@ -16,11 +16,12 @@ Classes="bold" Text="{DynamicResource Text.Init}"/> - + + + + + + + + diff --git a/src/Views/InitGitFlow.axaml b/src/Views/InitGitFlow.axaml index a767be59..83415803 100644 --- a/src/Views/InitGitFlow.axaml +++ b/src/Views/InitGitFlow.axaml @@ -3,7 +3,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:SourceGit.ViewModels" - xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" x:Class="SourceGit.Views.InitGitFlow" x:DataType="vm:InitGitFlow"> @@ -27,8 +26,7 @@ Height="26" VerticalAlignment="Center" CornerRadius="2" - Text="{Binding Master, Mode=TwoWay}" - v:AutoFocusBehaviour.IsEnabled="True"/> + Text="{Binding Master, Mode=TwoWay}"/> + Opacity="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.ToOpacity}}" + ShowStrikethrough="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.IsDrop}}"/> @@ -173,27 +174,29 @@ Width="16" Height="16" Margin="8,0,0,0" VerticalAlignment="Center" - User="{Binding Commit.Author}" - Opacity="{Binding IsFullMessageUsed, Converter={x:Static c:BoolConverters.IsMergedToOpacity}}"/> + User="{Binding Commit.Author}"/> + Text="{Binding Commit.Author.Name}" + Opacity="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.ToOpacity}}" + Classes.dropped="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.IsDrop}}"/> + Opacity="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.ToOpacity}}" + Classes.dropped="{Binding Action, Mode=OneWay, Converter={x:Static c:InteractiveRebaseActionConverters.IsDrop}}"/> - + @@ -207,7 +210,8 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" BorderThickness="0,1,0,0" - BorderBrush="{DynamicResource Brush.Border2}"/> + BorderBrush="{DynamicResource Brush.Border2}" + Focusable="False"/> diff --git a/src/Views/LFSTrackCustomPattern.axaml b/src/Views/LFSTrackCustomPattern.axaml index eca40275..5103917b 100644 --- a/src/Views/LFSTrackCustomPattern.axaml +++ b/src/Views/LFSTrackCustomPattern.axaml @@ -3,7 +3,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:SourceGit.ViewModels" - xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.LFSTrackCustomPattern" x:DataType="vm:LFSTrackCustomPattern"> @@ -26,8 +25,7 @@ + Text="{Binding Pattern, Mode=TwoWay}"/> - + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index c70c576a..a1cd3d92 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -131,6 +131,11 @@ namespace SourceGit.Views ViewModels.Preferences.Instance.Layout.LauncherWindowState = state; } + else if (change.Property == IsActiveProperty) + { + if (!IsActive && DataContext is ViewModels.Launcher { CommandPalette: { } } vm) + vm.CommandPalette = null; + } } protected override void OnSizeChanged(SizeChangedEventArgs e) @@ -174,6 +179,7 @@ namespace SourceGit.Views if (e is { KeyModifiers: KeyModifiers.None, Key: Key.F1 }) { await App.ShowDialog(new Hotkeys()); + e.Handled = true; return; } @@ -184,7 +190,28 @@ namespace SourceGit.Views } } - if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) + var cmdKey = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; + + if (vm.CommandPalette != null) + { + if (e.Key == Key.Escape) + { + vm.CommandPalette = null; + e.Handled = true; + } + else if (vm.ActivePage.Data is ViewModels.Repository repo + && vm.CommandPalette is ViewModels.LauncherPagesCommandPalette + && e.Key == Key.P + && e.KeyModifiers == (cmdKey | KeyModifiers.Shift)) + { + vm.CommandPalette = new ViewModels.RepositoryCommandPalette(repo); + e.Handled = true; + } + + return; + } + + if (e.KeyModifiers.HasFlag(cmdKey)) { if (e.Key == Key.W) { @@ -203,6 +230,13 @@ namespace SourceGit.Views return; } + if (e.Key == Key.T) + { + vm.AddNewTab(); + e.Handled = true; + return; + } + if ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Right) || (!OperatingSystem.IsMacOS() && !e.KeyModifiers.HasFlag(KeyModifiers.Shift) && e.Key == Key.Tab)) { @@ -235,7 +269,7 @@ namespace SourceGit.Views repo.SelectedViewIndex = 2; e.Handled = true; return; - case Key.F: + case Key.F when e.KeyModifiers.HasFlag(KeyModifiers.Shift): repo.IsSearchingCommits = true; e.Handled = true; return; @@ -244,7 +278,7 @@ namespace SourceGit.Views e.Handled = true; return; case Key.P when e.KeyModifiers.HasFlag(KeyModifiers.Shift): - vm.OpenCommandPalette(new ViewModels.RepositoryCommandPalette(vm, repo)); + vm.CommandPalette = new ViewModels.RepositoryCommandPalette(repo); e.Handled = true; return; } @@ -265,11 +299,7 @@ namespace SourceGit.Views } else if (e.Key == Key.Escape) { - if (vm.CommandPalette != null) - vm.CancelCommandPalette(); - else - vm.ActivePage.CancelPopup(); - + vm.ActivePage.CancelPopup(); e.Handled = true; return; } @@ -317,6 +347,9 @@ namespace SourceGit.Views { if (sender is Button btn && DataContext is ViewModels.Launcher launcher) { + if (launcher.CommandPalette != null) + launcher.CommandPalette = null; + var pref = ViewModels.Preferences.Instance; var menu = new ContextMenu(); menu.Placement = PlacementMode.BottomEdgeAlignedLeft; @@ -372,15 +405,15 @@ namespace SourceGit.Views private void OnOpenPagesCommandPalette(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.Launcher launcher) - launcher.OpenCommandPalette(new ViewModels.LauncherPagesCommandPalette(launcher)); + if (DataContext is ViewModels.Launcher vm) + vm.CommandPalette = new ViewModels.LauncherPagesCommandPalette(vm); e.Handled = true; } private void OnCloseCommandPalette(object sender, PointerPressedEventArgs e) { - if (e.Source == sender && DataContext is ViewModels.Launcher launcher) - launcher.CancelCommandPalette(); + if (e.Source == sender && DataContext is ViewModels.Launcher vm) + vm.CommandPalette = null; e.Handled = true; } diff --git a/src/Views/LauncherPage.axaml b/src/Views/LauncherPage.axaml index 20f4e8ca..78da1097 100644 --- a/src/Views/LauncherPage.axaml +++ b/src/Views/LauncherPage.axaml @@ -86,7 +86,8 @@ IsVisible="{Binding InProgress, Converter={x:Static BoolConverters.Not}}"> - - - - - - - - - + +