diff --git a/README.md b/README.md index 07cdc098..45d6bbf5 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-99.86%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-98.01%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.44%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.29%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-99.86%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-98.01%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.44%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.29%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) ## How to Use diff --git a/TRANSLATION.md b/TRANSLATION.md index c15fdcfb..508c0095 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -58,28 +58,12 @@ -### it_IT.axaml: 97.87% +### it_IT.axaml: 100.0%
Missing Keys -- Text.CommitDetail.Info.Children -- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest -- Text.Configure.OpenAI.Preferred -- Text.Configure.OpenAI.Preferred.Tip -- Text.Fetch.Force -- Text.Preference.General.ShowChildren -- Text.Repository.FilterCommits -- Text.Repository.FilterCommits.Default -- Text.Repository.FilterCommits.Exclude -- Text.Repository.FilterCommits.Include -- Text.Repository.HistoriesOrder -- Text.Repository.HistoriesOrder.ByDate -- Text.Repository.HistoriesOrder.Topo -- Text.SHALinkCM.CopySHA -- Text.SHALinkCM.NavigateTo -
### pt_BR.axaml: 99.29% diff --git a/src/Commands/QueryCommitChildren.cs b/src/Commands/QueryCommitChildren.cs index bef09abb..293de912 100644 --- a/src/Commands/QueryCommitChildren.cs +++ b/src/Commands/QueryCommitChildren.cs @@ -4,12 +4,14 @@ namespace SourceGit.Commands { public class QueryCommitChildren : Command { - public QueryCommitChildren(string repo, string commit, int max) + public QueryCommitChildren(string repo, string commit, int max, string filters) { WorkingDirectory = repo; Context = repo; _commit = commit; - Args = $"rev-list -{max} --parents --branches --remotes ^{commit}"; + if (string.IsNullOrEmpty(filters)) + filters = "--branches --remotes --tags"; + Args = $"rev-list -{max} --parents {filters} ^{commit}"; } public IEnumerable Result() diff --git a/src/Models/Filter.cs b/src/Models/Filter.cs index 8d419800..af4569fa 100644 --- a/src/Models/Filter.cs +++ b/src/Models/Filter.cs @@ -20,11 +20,12 @@ namespace SourceGit.Models public class Filter : ObservableObject { - public string Pattern + public string Pattern { get => _pattern; set => SetProperty(ref _pattern, value); } + public FilterType Type { get; diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs index 586f52ec..5b3aa331 100644 --- a/src/Models/RepositorySettings.cs +++ b/src/Models/RepositorySettings.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Text; using Avalonia.Collections; -using Avalonia.Threading; namespace SourceGit.Models { @@ -153,22 +152,51 @@ namespace SourceGit.Models set; } = "---"; - public FilterMode GetHistoriesFilterMode(string pattern, FilterType type) + public Dictionary CollectHistoriesFilters() { + var map = new Dictionary(); foreach (var filter in HistoriesFilters) - { - if (filter.Type != type) - continue; - - if (filter.Pattern.Equals(pattern, StringComparison.Ordinal)) - return filter.Mode; - } - - return FilterMode.None; + map.Add(filter.Pattern, filter.Mode); + return map; } public bool UpdateHistoriesFilter(string pattern, FilterType type, FilterMode mode) { + // Clear all filters when there's a filter that has different mode. + if (mode != FilterMode.None) + { + var clear = false; + foreach (var filter in HistoriesFilters) + { + if (filter.Mode != mode) + { + clear = true; + break; + } + } + + if (clear) + { + HistoriesFilters.Clear(); + HistoriesFilters.Add(new Filter(pattern, type, mode)); + return true; + } + } + else + { + for (int i = 0; i < HistoriesFilters.Count; i++) + { + var filter = HistoriesFilters[i]; + if (filter.Type == type && filter.Pattern.Equals(pattern, StringComparison.Ordinal)) + { + HistoriesFilters.RemoveAt(i); + return true; + } + } + + return false; + } + for (int i = 0; i < HistoriesFilters.Count; i++) { var filter = HistoriesFilters[i]; @@ -176,34 +204,33 @@ namespace SourceGit.Models continue; if (filter.Pattern.Equals(pattern, StringComparison.Ordinal)) - { - if (mode == FilterMode.None) - { - HistoriesFilters.RemoveAt(i); - return true; - } - - if (mode != filter.Mode) - { - filter.Mode = mode; - return true; - } - } + return false; } - if (mode != FilterMode.None) + HistoriesFilters.Add(new Filter(pattern, type, mode)); + return true; + } + + public void RemoveChildrenBranchFilters(string pattern) + { + var dirty = new List(); + var prefix = $"{pattern}/"; + + foreach (var filter in HistoriesFilters) { - HistoriesFilters.Add(new Filter(pattern, type, mode)); - return true; + if (filter.Type == FilterType.Tag) + continue; + + if (filter.Pattern.StartsWith(prefix, StringComparison.Ordinal)) + dirty.Add(filter); } - return false; + foreach (var filter in dirty) + HistoriesFilters.Remove(filter); } public string BuildHistoriesFilter() { - var builder = new StringBuilder(); - var excludedBranches = new List(); var excludedRemotes = new List(); var excludedTags = new List(); @@ -216,7 +243,7 @@ namespace SourceGit.Models { var name = filter.Pattern.Substring(11); var b = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]"; - + if (filter.Mode == FilterMode.Included) includedBranches.Add(b); else if (filter.Mode == FilterMode.Excluded) @@ -258,14 +285,11 @@ namespace SourceGit.Models } } - foreach (var b in excludedBranches) - { - builder.Append("--exclude="); - builder.Append(b); - builder.Append(' '); - } + bool hasIncluded = includedBranches.Count > 0 || includedRemotes.Count > 0 || includedTags.Count > 0; + bool hasExcluded = excludedBranches.Count > 0 || excludedRemotes.Count > 0 || excludedTags.Count > 0; - if (includedBranches.Count > 0) + var builder = new StringBuilder(); + if (hasIncluded) { foreach (var b in includedBranches) { @@ -273,42 +297,14 @@ namespace SourceGit.Models builder.Append(b); builder.Append(' '); } - } - else if (excludedBranches.Count > 0) - { - builder.Append("--branches "); - } - foreach (var r in excludedRemotes) - { - builder.Append("--exclude="); - builder.Append(r); - builder.Append(' '); - } - - if (includedRemotes.Count > 0) - { foreach (var r in includedRemotes) { builder.Append("--remotes="); builder.Append(r); builder.Append(' '); } - } - else if (excludedRemotes.Count > 0) - { - builder.Append("--remotes "); - } - foreach (var t in excludedTags) - { - builder.Append("--exclude="); - builder.Append(t); - builder.Append(' '); - } - - if (includedTags.Count > 0) - { foreach (var t in includedTags) { builder.Append("--tags="); @@ -316,8 +312,48 @@ namespace SourceGit.Models builder.Append(' '); } } - else if (excludedTags.Count > 0) + else if (hasExcluded) { + if (excludedBranches.Count > 0) + { + foreach (var b in excludedBranches) + { + builder.Append("--exclude="); + builder.Append(b); + builder.Append(" --decorate-refs-exclude=refs/heads/"); + builder.Append(b); + builder.Append(' '); + } + } + + builder.Append("--exclude=HEA[D] --branches "); + + if (excludedRemotes.Count > 0) + { + foreach (var r in excludedRemotes) + { + builder.Append("--exclude="); + builder.Append(r); + builder.Append(" --decorate-refs-exclude=refs/remotes/"); + builder.Append(r); + builder.Append(' '); + } + } + + builder.Append("--exclude=origin/HEA[D] --remotes "); + + if (excludedTags.Count > 0) + { + foreach (var t in excludedTags) + { + builder.Append("--exclude="); + builder.Append(t); + builder.Append(" --decorate-refs-exclude=refs/tags/"); + builder.Append(t); + builder.Append(' '); + } + } + builder.Append("--tags "); } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index fd42b0f5..8a0ee04e 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -341,7 +341,7 @@ Remote: Track files named '{0}' Track all *{0} files - HISTORY + Histories Switch Horizontal/Vertical Layout AUTHOR AUTHOR TIME @@ -453,7 +453,7 @@ Language History Commits Show author time instead of commit time in graph - Show children in the commit details + Show children in the comment details Subject Guide Length GIT Enable Auto CRLF @@ -621,7 +621,7 @@ Pop Drop Stash Drop: - STASHES + Stashes CHANGES STASHES Statistics @@ -668,7 +668,7 @@ Rescan Repositories in Default Clone Dir Search Repositories... Sort - LOCAL CHANGES + Changes Git Ignore Ignore all *{0} files Ignore *{0} files in the same folder diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 6ea2af10..82970549 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -164,7 +164,6 @@ + + @@ -113,6 +119,12 @@ Margin="0,0,16,0" PointerEntered="OnSHAPointerEntered" PointerPressed="OnSHAPressed"> + + + + @@ -150,6 +162,12 @@ Margin="0,0,16,0" PointerEntered="OnSHAPointerEntered" PointerPressed="OnSHAPressed"> + + + + @@ -187,7 +205,13 @@ Message="{Binding #ThisControl.Message}" IssueTrackerRules="{Binding #ThisControl.IssueTrackerRules}" HorizontalAlignment="Stretch" - TextWrapping="Wrap"> + TextWrapping="Wrap"> + + + + diff --git a/src/Views/CreateBranch.axaml b/src/Views/CreateBranch.axaml index caea0be4..3516c317 100644 --- a/src/Views/CreateBranch.axaml +++ b/src/Views/CreateBranch.axaml @@ -69,20 +69,21 @@ Margin="0,0,8,0" Text="{DynamicResource Text.CreateBranch.LocalChanges}"/> + + + + + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.DoNothing}}"/> + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.StashAndReaply}}"/> + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.Discard}}"/> - - - - - - - - - - + Fill="{DynamicResource Brush.FG2}" + IsVisible="{Binding #ThisControl.Mode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.None}}"/> (); @@ -57,199 +67,100 @@ namespace SourceGit.Views if (button == null) return; + var menu = new ContextMenu(); + var mode = Models.FilterMode.None; if (DataContext is Models.Tag tag) { - var mode = tag.FilterMode; + mode = tag.FilterMode; - var none = new MenuItem(); - none.Icon = App.CreateMenuIcon("Icons.Eye"); - none.Header = "Default"; - none.IsEnabled = mode != Models.FilterMode.None; - none.Click += (_, ev) => + if (mode != Models.FilterMode.None) { - UpdateTagFilterMode(repo, tag, Models.FilterMode.None); - ev.Handled = true; - }; + var unset = new MenuItem(); + unset.Header = App.Text("Repository.FilterCommits.Default"); + unset.Click += (_, ev) => + { + repo.SetTagFilterMode(tag, Models.FilterMode.None); + ev.Handled = true; + }; + + menu.Items.Add(unset); + menu.Items.Add(new MenuItem() { Header = "-" }); + } var include = new MenuItem(); include.Icon = App.CreateMenuIcon("Icons.Filter"); - include.Header = "Filter"; + include.Header = App.Text("Repository.FilterCommits.Include"); include.IsEnabled = mode != Models.FilterMode.Included; include.Click += (_, ev) => { - UpdateTagFilterMode(repo, tag, Models.FilterMode.Included); + repo.SetTagFilterMode(tag, Models.FilterMode.Included); ev.Handled = true; }; var exclude = new MenuItem(); exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); - exclude.Header = "Hide"; + exclude.Header = App.Text("Repository.FilterCommits.Exclude"); exclude.IsEnabled = mode != Models.FilterMode.Excluded; exclude.Click += (_, ev) => { - UpdateTagFilterMode(repo, tag, Models.FilterMode.Excluded); + repo.SetTagFilterMode(tag, Models.FilterMode.Excluded); ev.Handled = true; }; - var menu = new ContextMenu(); - menu.Items.Add(none); menu.Items.Add(include); menu.Items.Add(exclude); - - if (mode == Models.FilterMode.None) - { - IsContextMenuOpening = true; - menu.Closed += (_, _) => IsContextMenuOpening = false; - } - - menu.Open(button); } else if (DataContext is ViewModels.BranchTreeNode node) { - var mode = node.FilterMode; + mode = node.FilterMode; - var none = new MenuItem(); - none.Icon = App.CreateMenuIcon("Icons.Eye"); - none.Header = "Default"; - none.IsEnabled = mode != Models.FilterMode.None; - none.Click += (_, ev) => + if (mode != Models.FilterMode.None) { - UpdateBranchFilterMode(repo, node, Models.FilterMode.None); - ev.Handled = true; - }; + var unset = new MenuItem(); + unset.Header = App.Text("Repository.FilterCommits.Default"); + unset.Click += (_, ev) => + { + repo.SetBranchFilterMode(node, Models.FilterMode.None); + ev.Handled = true; + }; + + menu.Items.Add(unset); + menu.Items.Add(new MenuItem() { Header = "-" }); + } var include = new MenuItem(); include.Icon = App.CreateMenuIcon("Icons.Filter"); - include.Header = "Filter"; + include.Header = App.Text("Repository.FilterCommits.Include"); include.IsEnabled = mode != Models.FilterMode.Included; include.Click += (_, ev) => { - UpdateBranchFilterMode(repo, node, Models.FilterMode.Included); + repo.SetBranchFilterMode(node, Models.FilterMode.Included); ev.Handled = true; }; var exclude = new MenuItem(); exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); - exclude.Header = "Hide"; + exclude.Header = App.Text("Repository.FilterCommits.Exclude"); exclude.IsEnabled = mode != Models.FilterMode.Excluded; exclude.Click += (_, ev) => { - UpdateBranchFilterMode(repo, node, Models.FilterMode.Excluded); + repo.SetBranchFilterMode(node, Models.FilterMode.Excluded); ev.Handled = true; }; - var menu = new ContextMenu(); - menu.Items.Add(none); menu.Items.Add(include); menu.Items.Add(exclude); - - if (mode == Models.FilterMode.None) - { - IsContextMenuOpening = true; - menu.Closed += (_, _) => IsContextMenuOpening = false; - } - - menu.Open(button); } + if (mode == Models.FilterMode.None) + { + IsContextMenuOpening = true; + menu.Closed += (_, _) => IsContextMenuOpening = false; + } + + menu.Open(button); e.Handled = true; } - - private void UpdateTagFilterMode(ViewModels.Repository repo, Models.Tag tag, Models.FilterMode mode) - { - var changed = repo.Settings.UpdateHistoriesFilter(tag.Name, Models.FilterType.Tag, mode); - if (changed) - { - tag.FilterMode = mode; - Task.Run(repo.RefreshCommits); - } - } - - private void UpdateBranchFilterMode(ViewModels.Repository repo, ViewModels.BranchTreeNode node, Models.FilterMode mode) - { - var isLocal = node.Path.StartsWith("refs/heads/", StringComparison.Ordinal); - var type = isLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch; - var tree = isLocal ? repo.LocalBranchTrees : repo.RemoteBranchTrees; - - if (node.Backend is Models.Branch branch) - { - var changed = repo.Settings.UpdateHistoriesFilter(node.Path, type, mode); - if (!changed) - return; - - node.FilterMode = mode; - } - else - { - var changed = repo.Settings.UpdateHistoriesFilter(node.Path, isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder, mode); - if (!changed) - return; - - node.FilterMode = mode; - ResetChildrenBranchNodeFilterMode(repo, node, isLocal); - } - - var parentType = isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder; - var cur = node; - do - { - var lastSepIdx = cur.Path.LastIndexOf('/'); - if (lastSepIdx <= 0) - break; - - var parentPath = cur.Path.Substring(0, lastSepIdx); - var parent = FindParentNode(tree, parentPath); - if (parent == null) - break; - - repo.Settings.UpdateHistoriesFilter(parent.Path, parentType, Models.FilterMode.None); - parent.FilterMode = Models.FilterMode.None; - cur = parent; - } while (true); - - Task.Run(repo.RefreshCommits); - } - - private void ResetChildrenBranchNodeFilterMode(ViewModels.Repository repo, ViewModels.BranchTreeNode node, bool isLocal) - { - foreach (var child in node.Children) - { - child.FilterMode = Models.FilterMode.None; - - if (child.IsBranch) - { - var type = isLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch; - repo.Settings.UpdateHistoriesFilter(child.Path, type, Models.FilterMode.None); - } - else - { - var type = isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder; - repo.Settings.UpdateHistoriesFilter(child.Path, type, Models.FilterMode.None); - ResetChildrenBranchNodeFilterMode(repo, child, isLocal); - } - } - } - - private ViewModels.BranchTreeNode FindParentNode(List nodes, string parent) - { - foreach (var node in nodes) - { - if (node.IsBranch) - continue; - - if (node.Path.Equals(parent, StringComparison.Ordinal)) - return node; - - if (parent.StartsWith(node.Path, StringComparison.Ordinal)) - { - var founded = FindParentNode(node.Children, parent); - if (founded != null) - return founded; - } - } - - return null; - } } } diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 4137579d..29d90e09 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -72,11 +72,6 @@ namespace SourceGit.Views return _unhandledModifiers.HasFlag(modifier); } - public void ClearKeyModifier() - { - _unhandledModifiers = KeyModifiers.None; - } - protected override void OnOpened(EventArgs e) { base.OnOpened(e); diff --git a/src/Views/Pull.axaml b/src/Views/Pull.axaml index f6aa245f..a23bb5d5 100644 --- a/src/Views/Pull.axaml +++ b/src/Views/Pull.axaml @@ -77,20 +77,21 @@ Margin="0,0,8,0" Text="{DynamicResource Text.Pull.LocalChanges}"/> + + + + + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.DoNothing}}"/> + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.StashAndReaply}}"/> + IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.Discard}}"/> - + + @@ -524,7 +531,7 @@ - + @@ -557,14 +564,6 @@ diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 1d9d634b..afc8ac5a 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -53,7 +53,6 @@ namespace SourceGit.Views if (!startDirectly && OperatingSystem.IsMacOS()) startDirectly = launcher.HasKeyModifier(KeyModifiers.Meta); - launcher.ClearKeyModifier(); repo.Fetch(startDirectly); e.Handled = true; } @@ -68,7 +67,6 @@ namespace SourceGit.Views if (!startDirectly && OperatingSystem.IsMacOS()) startDirectly = launcher.HasKeyModifier(KeyModifiers.Meta); - launcher.ClearKeyModifier(); repo.Pull(startDirectly); e.Handled = true; } @@ -83,7 +81,6 @@ namespace SourceGit.Views if (!startDirectly && OperatingSystem.IsMacOS()) startDirectly = launcher.HasKeyModifier(KeyModifiers.Meta); - launcher.ClearKeyModifier(); repo.Push(startDirectly); e.Handled = true; } @@ -92,16 +89,8 @@ namespace SourceGit.Views private void StashAll(object _, RoutedEventArgs e) { var launcher = this.FindAncestorOfType(); - if (launcher is not null && DataContext is ViewModels.Repository repo) - { - var startDirectly = launcher.HasKeyModifier(KeyModifiers.Control); - if (!startDirectly && OperatingSystem.IsMacOS()) - startDirectly = launcher.HasKeyModifier(KeyModifiers.Meta); - - launcher.ClearKeyModifier(); - repo.StashAll(startDirectly); - e.Handled = true; - } + (DataContext as ViewModels.Repository)?.StashAll(launcher?.HasKeyModifier(KeyModifiers.Control) ?? false); + e.Handled = true; } private void OpenGitFlowMenu(object sender, RoutedEventArgs e) diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index ea70ac93..da2d9ed1 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -619,7 +619,6 @@ namespace SourceGit.Views TextArea.TextView.VisualLinesChanged += OnTextViewVisualLinesChanged; UpdateTextMate(); - OnTextViewVisualLinesChanged(null, null); } protected override void OnUnloaded(RoutedEventArgs e)