diff --git a/src/Commands/QueryRefsContainsCommit.cs b/src/Commands/QueryRefsContainsCommit.cs new file mode 100644 index 00000000..df45cfc6 --- /dev/null +++ b/src/Commands/QueryRefsContainsCommit.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace SourceGit.Commands +{ + public class QueryRefsContainsCommit : Command + { + public QueryRefsContainsCommit(string repo, string commit) + { + WorkingDirectory = repo; + RaiseError = false; + Args = $"for-each-ref --format=\"%(refname)\" --contains {commit}"; + } + + public List Result() + { + var rs = new List(); + + var output = ReadToEnd(); + if (!output.IsSuccess) + return rs; + + var lines = output.StdOut.Split('\n'); + foreach (var line in lines) + { + if (line.StartsWith("refs/heads/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/heads/".Length), Type = Models.DecoratorType.LocalBranchHead }); + else if (line.StartsWith("refs/remotes/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/remotes/".Length), Type = Models.DecoratorType.RemoteBranchHead }); + else if (line.StartsWith("refs/tags/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/tags/".Length), Type = Models.DecoratorType.Tag }); + } + + return rs; + } + } +} diff --git a/src/Models/Decorator.cs b/src/Models/Decorator.cs index 235101cc..7d985e31 100644 --- a/src/Models/Decorator.cs +++ b/src/Models/Decorator.cs @@ -14,5 +14,6 @@ { public DecoratorType Type { get; set; } = DecoratorType.None; public string Name { get; set; } = ""; + public bool IsTag => Type == DecoratorType.Tag; } } diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 61989186..bbef234a 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -80,6 +80,7 @@ M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z M592 768h-160c-27 0-48-21-48-48V384h-175c-36 0-53-43-28-68L485 11c15-15 40-15 55 0l304 304c25 25 7 68-28 68H640v336c0 27-21 48-48 48zm432-16v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h272v16c0 62 50 112 112 112h160c62 0 112-50 112-112v-16h272c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z + M672 336l64-89c-13-19-26-45-26-70 0-64 51-115 109-115s109 51 109 115-51 115-109 115c-19 0-38-6-51-13l-64 89c32 38 51 89 51 147 0 70-32 128-77 172l51 64c13-6 32-13 51-13 57 0 109 51 109 115s-51 109-109 109-109-51-109-115c0-26 13-51 26-70L646 707c-32 19-64 26-102 26-121 0-217-102-217-223v-38l-57-13c-19 32-57 57-96 57C116 515 65 464 65 400s51-115 109-115 109 51 109 115v13l57 19C372 349 448 292 538 292c51 0 102 19 134 45z M706 302a289 289 0 00-173 44 27 27 0 1029 46 234 234 0 01125-36c23 0 45 3 66 9 93 28 161 114 161 215C914 704 813 805 687 805H337C211 805 110 704 110 580c0-96 61-178 147-210C282 263 379 183 495 183a245 245 0 01210 119z M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z M1024 64v704h-128v128h-128v128h-768v-704h128v-128h128v-128zM64 960h640v-576h-640zM320 128v64h576v512h64v-576zM192 256v64h576v512h64v-576zM432 688L576 832H480L384 736 288 832H192l144-144L192 544h96L384 640l96-96H576z diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 29e5d802..a6e1a4cf 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -116,6 +116,8 @@ AUTHOR CHANGED COMMITTER + Check refs that contains this commit + COMMIT CONTAINS IN Shows only the first 100 changes. See all changes on the CHANGES tab. MESSAGE PARENTS diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 4bb0eae1..94c92d53 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -119,6 +119,8 @@ 修改者 变更列表 提交者 + 查看包含此提交的分支/标签 + 本提交已被以下分支/标签包含 仅显示前100项变更。请前往【变更对比】页面查看全部。 提交信息 父提交 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 6a628fc7..8e35c100 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -119,6 +119,8 @@ 修改者 變更列表 提交者 + 查看包含此提交的分支/標籤 + 本提交已被以下分支/標籤包含 僅顯示前100項變更。 請前往『變更對比』頁面查看全部。 提交資訊 父提交 diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 1462d701..f6ff79b2 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -141,6 +141,11 @@ namespace SourceGit.ViewModels _repo?.NavigateToCommit(commitSHA); } + public List GetRefsContainsThisCommit() + { + return new Commands.QueryRefsContainsCommit(_repo.FullPath, _commit.SHA).Result(); + } + public void ClearSearchChangeFilter() { SearchChangeFilter = string.Empty; @@ -460,14 +465,18 @@ namespace SourceGit.ViewModels if (_commit == null) return; + Task.Run(() => + { + var fullMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, _commit.SHA).Result(); + Dispatcher.UIThread.Invoke(() => FullMessage = fullMessage); + }); + if (_cancelToken != null) _cancelToken.Requested = true; _cancelToken = new Commands.Command.CancelToken(); - Task.Run(() => { - var fullMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, _commit.SHA).Result(); var parent = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0]; var cmdChanges = new Commands.CompareRevisions(_repo.FullPath, parent, _commit.SHA) { Cancel = _cancelToken }; var changes = cmdChanges.Result(); @@ -486,7 +495,6 @@ namespace SourceGit.ViewModels { Dispatcher.UIThread.Post(() => { - FullMessage = fullMessage; Changes = changes; VisibleChanges = visible; }); diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index 0ef17e71..26192d12 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -57,12 +57,16 @@ - + + @@ -98,7 +102,8 @@ LabelForeground="{DynamicResource Brush.DecoratorFG}" FontFamily="{DynamicResource Fonts.Monospace}" FontSize="10" - VerticalAlignment="Center"/> + VerticalAlignment="Center" + Refs="{Binding Decorators}"/> diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index f7c44f17..a55e4687 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -1,3 +1,5 @@ +using System.Threading.Tasks; + using Avalonia; using Avalonia.Collections; using Avalonia.Controls; @@ -17,6 +19,15 @@ namespace SourceGit.Views set => SetValue(MessageProperty, value); } + public static readonly StyledProperty SupportsContainsInProperty = + AvaloniaProperty.Register(nameof(SupportsContainsIn)); + + public bool SupportsContainsIn + { + get => GetValue(SupportsContainsInProperty); + set => SetValue(SupportsContainsInProperty, value); + } + public static readonly StyledProperty> WebLinksProperty = AvaloniaProperty.Register>(nameof(WebLinks)); @@ -74,6 +85,19 @@ namespace SourceGit.Views e.Handled = true; } + private void OnOpenContainsIn(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.CommitDetail detail && sender is Button button) + { + var tracking = new CommitRelationTracking(detail); + var flyout = new Flyout(); + flyout.Content = tracking; + flyout.ShowAt(button); + } + + e.Handled = true; + } + private void OnParentSHAPressed(object sender, PointerPressedEventArgs e) { if (DataContext is ViewModels.CommitDetail detail && sender is Control { DataContext: string sha }) diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml index 432fa737..8307d650 100644 --- a/src/Views/CommitDetail.axaml +++ b/src/Views/CommitDetail.axaml @@ -21,6 +21,7 @@ diff --git a/src/Views/CommitRefsPresenter.cs b/src/Views/CommitRefsPresenter.cs index da842182..3dff6253 100644 --- a/src/Views/CommitRefsPresenter.cs +++ b/src/Views/CommitRefsPresenter.cs @@ -17,6 +17,15 @@ namespace SourceGit.Views public bool IsTag { get; set; } = false; } + public static readonly StyledProperty> RefsProperty = + AvaloniaProperty.Register>(nameof(Refs)); + + public List Refs + { + get => GetValue(RefsProperty); + set => SetValue(RefsProperty, value); + } + public static readonly StyledProperty FontFamilyProperty = TextBlock.FontFamilyProperty.AddOwner(); @@ -85,7 +94,8 @@ namespace SourceGit.Views AffectsMeasure( FontFamilyProperty, FontSizeProperty, - LabelForegroundProperty); + LabelForegroundProperty, + RefsProperty); AffectsRender( IconBackgroundProperty, @@ -121,17 +131,12 @@ namespace SourceGit.Views } } - protected override void OnDataContextChanged(EventArgs e) - { - base.OnDataContextChanged(e); - InvalidateMeasure(); - } - protected override Size MeasureOverride(Size availableSize) { _items.Clear(); - if (DataContext is Models.Commit commit && commit.HasDecorators) + var refs = Refs; + if (refs != null && refs.Count > 0) { var typeface = new Typeface(FontFamily); var typefaceBold = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold); @@ -139,7 +144,7 @@ namespace SourceGit.Views var labelSize = FontSize; var requiredWidth = 0.0; - foreach (var decorator in commit.Decorators) + foreach (var decorator in refs) { var isHead = decorator.Type == Models.DecoratorType.CurrentBranchHead || decorator.Type == Models.DecoratorType.CurrentCommitHead; diff --git a/src/Views/CommitRelationTracking.axaml b/src/Views/CommitRelationTracking.axaml new file mode 100644 index 00000000..5e3574d8 --- /dev/null +++ b/src/Views/CommitRelationTracking.axaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitRelationTracking.axaml.cs b/src/Views/CommitRelationTracking.axaml.cs new file mode 100644 index 00000000..1e436552 --- /dev/null +++ b/src/Views/CommitRelationTracking.axaml.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; + +using Avalonia.Controls; +using Avalonia.Threading; + +namespace SourceGit.Views +{ + public partial class CommitRelationTracking : UserControl + { + public CommitRelationTracking() + { + InitializeComponent(); + } + + public CommitRelationTracking(ViewModels.CommitDetail detail) + { + InitializeComponent(); + + LoadingIcon.IsVisible = true; + + Task.Run(() => + { + var containsIn = detail.GetRefsContainsThisCommit(); + Dispatcher.UIThread.Invoke(() => + { + Container.ItemsSource = containsIn; + LoadingIcon.IsVisible = false; + }); + }); + } + } +} diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 9643098d..a06f805a 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -67,15 +67,15 @@ BehindBrush="{DynamicResource Brush.FG1}" VerticalAlignment="Center"/> - + VerticalAlignment="Center" + Refs="{Binding Decorators}"/> (); + if (welcome != null) + { + if (e.Key == Key.F) + { + welcome.SearchBox.Focus(); + e.Handled = true; + return; + } + } + } } else if (e.Key == Key.Escape) { diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index e4a329e8..9e887ab0 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -45,13 +45,6 @@ namespace SourceGit.Views TreeContainer.Focus(NavigationMethod.Directional); e.Handled = true; } - else if (e.Key == Key.F && - ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Meta)) || - (!OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Control)))) - { - SearchBox.Focus(); - e.Handled = true; - } else if (e.Key == Key.Escape) { ViewModels.Welcome.Instance.ClearSearchFilter();