Compare commits

...

6 commits

Author SHA1 Message Date
leo
3da52208ba
enhance: force enable --patch option
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2024-10-17 19:20:14 +08:00
leo
9e14327f3c
feature: add context menu to copy message of an annotated tag (#567) 2024-10-17 17:38:29 +08:00
leo
fa2eb0cd26
enhance: use --prune=now instead of --prune for git gc command 2024-10-17 15:50:42 +08:00
leo
b23f284e21
feature: add context menu for selected change in stashes page 2024-10-17 15:37:14 +08:00
leo
d0ae24b0b7
localization: update tips 2024-10-17 15:20:16 +08:00
leo
f6e0b0b1c0
feature: hover on tag view shows the message of it (#567) 2024-10-17 14:57:05 +08:00
14 changed files with 167 additions and 64 deletions

View file

@ -24,9 +24,9 @@ namespace SourceGit.Commands
Context = repo;
if (ignoreWhitespace)
Args = $"diff --ignore-cr-at-eol --ignore-all-space --unified={unified} {opt}";
Args = $"diff --patch --ignore-cr-at-eol --ignore-all-space --unified={unified} {opt}";
else
Args = $"diff --ignore-cr-at-eol --unified={unified} {opt}";
Args = $"diff --patch --ignore-cr-at-eol --unified={unified} {opt}";
}
public Models.DiffResult Result()

View file

@ -10,7 +10,7 @@ namespace SourceGit.Commands
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = "gc --prune";
Args = "gc --prune=now";
}
protected override void OnReadline(string line)

View file

@ -7,9 +7,11 @@ namespace SourceGit.Commands
{
public QueryTags(string repo)
{
_boundary = $"----- BOUNDARY OF TAGS {Guid.NewGuid()} -----";
Context = repo;
WorkingDirectory = repo;
Args = "tag -l --sort=-creatordate --format=\"%(refname)%00%(objectname)%00%(*objectname)\"";
Args = $"tag -l --sort=-creatordate --format=\"{_boundary}%(refname)%00%(objectname)%00%(*objectname)%00%(contents:subject)%0a%0a%(contents:body)\"";
}
public List<Models.Tag> Result()
@ -19,27 +21,25 @@ namespace SourceGit.Commands
if (!rs.IsSuccess)
return tags;
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
var records = rs.StdOut.Split(_boundary, StringSplitOptions.RemoveEmptyEntries);
foreach (var record in records)
{
var tag = ParseLine(line);
if (tag != null)
tags.Add(tag);
var subs = record.Split('\0', StringSplitOptions.None);
if (subs.Length != 4)
continue;
var message = subs[3].Trim();
tags.Add(new Models.Tag()
{
Name = subs[0].Substring(10),
SHA = string.IsNullOrEmpty(subs[2]) ? subs[1] : subs[2],
Message = string.IsNullOrEmpty(message) ? null : message,
});
}
return tags;
}
private Models.Tag ParseLine(string line)
{
var subs = line.Split('\0');
if (subs.Length != 3)
return null;
var tag = new Models.Tag();
tag.Name = subs[0].Substring(10);
tag.SHA = string.IsNullOrEmpty(subs[2]) ? subs[1] : subs[2];
return tag;
}
private string _boundary = string.Empty;
}
}

View file

@ -4,6 +4,7 @@
{
public string Name { get; set; }
public string SHA { get; set; }
public string Message { get; set; }
public bool IsFiltered { get; set; }
}
}

View file

@ -322,7 +322,7 @@
<x:String x:Key="Text.Histories.Header.Time" xml:space="preserve">COMMIT TIME</x:String>
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">SELECTED {0} COMMITS</x:String>
<x:String x:Key="Text.Histories.Tips" xml:space="preserve">Holding 'Ctrl' or 'Shift' to select multiple commits.</x:String>
<x:String x:Key="Text.Histories.Tips.MacOS" xml:space="preserve">Holding '⌘' or '⇧' to select multiple commits.</x:String>
<x:String x:Key="Text.Histories.Tips.MacOS" xml:space="preserve">Holding ⌘ or ⇧ to select multiple commits.</x:String>
<x:String x:Key="Text.Histories.Tips.Prefix" xml:space="preserve">TIPS:</x:String>
<x:String x:Key="Text.Hotkeys" xml:space="preserve">Keyboard Shortcuts Reference</x:String>
<x:String x:Key="Text.Hotkeys.Global" xml:space="preserve">GLOBAL</x:String>
@ -595,6 +595,7 @@
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">Delete Submodule</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">OK</x:String>
<x:String x:Key="Text.TagCM.Copy" xml:space="preserve">Copy Tag Name</x:String>
<x:String x:Key="Text.TagCM.CopyMessage" xml:space="preserve">Copy Tag Message</x:String>
<x:String x:Key="Text.TagCM.Delete" xml:space="preserve">Delete ${0}$...</x:String>
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">Push ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">URL:</x:String>

View file

@ -593,6 +593,7 @@
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">删除子模块</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">确 定</x:String>
<x:String x:Key="Text.TagCM.Copy" xml:space="preserve">复制标签名</x:String>
<x:String x:Key="Text.TagCM.CopyMessage" xml:space="preserve">复制标签信息</x:String>
<x:String x:Key="Text.TagCM.Delete" xml:space="preserve">删除 ${0}$...</x:String>
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">仓库地址 </x:String>

View file

@ -324,8 +324,8 @@
<x:String x:Key="Text.Histories.Header.SHA" xml:space="preserve">提交編號</x:String>
<x:String x:Key="Text.Histories.Header.Time" xml:space="preserve">提交時間</x:String>
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">已選取 {0} 項提交</x:String>
<x:String x:Key="Text.Histories.Tips" xml:space="preserve">可以按住Ctrl或Shift鍵選擇多個提交</x:String>
<x:String x:Key="Text.Histories.Tips.MacOS" xml:space="preserve">可以按住⌘或⇧鍵選擇多個提交</x:String>
<x:String x:Key="Text.Histories.Tips" xml:space="preserve">可以按住 Ctrl 或 Shift 鍵選擇多個提交</x:String>
<x:String x:Key="Text.Histories.Tips.MacOS" xml:space="preserve">可以按住 ⌘ 或 ⇧ 鍵選擇多個提交</x:String>
<x:String x:Key="Text.Histories.Tips.Prefix" xml:space="preserve">小提示:</x:String>
<x:String x:Key="Text.Hotkeys" xml:space="preserve">快速鍵參考</x:String>
<x:String x:Key="Text.Hotkeys.Global" xml:space="preserve">全域快速鍵</x:String>
@ -598,6 +598,7 @@
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">刪除子模組</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">確 定</x:String>
<x:String x:Key="Text.TagCM.Copy" xml:space="preserve">複製標籤名稱</x:String>
<x:String x:Key="Text.TagCM.CopyMessage" xml:space="preserve">複製標籤訊息</x:String>
<x:String x:Key="Text.TagCM.Delete" xml:space="preserve">刪除 ${0}$...</x:String>
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">存放庫網址:</x:String>

View file

@ -1829,6 +1829,16 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
var copyMessage = new MenuItem();
copyMessage.Header = App.Text("TagCM.CopyMessage");
copyMessage.Icon = App.CreateMenuIcon("Icons.Copy");
copyMessage.IsEnabled = !string.IsNullOrEmpty(tag.Message);
copyMessage.Click += (_, ev) =>
{
App.CopyText(tag.Message);
ev.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(createBranch);
menu.Items.Add(new MenuItem() { Header = "-" });
@ -1838,6 +1848,7 @@ namespace SourceGit.ViewModels
menu.Items.Add(archive);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copy);
menu.Items.Add(copyMessage);
return menu;
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
@ -150,6 +151,74 @@ namespace SourceGit.ViewModels
return menu;
}
public ContextMenu MakeContextMenuForChange(Models.Change change)
{
if (change == null)
return null;
var diffWithMerger = new MenuItem();
diffWithMerger.Header = App.Text("DiffWithMerger");
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
diffWithMerger.Click += (_, ev) =>
{
var toolType = Preference.Instance.ExternalMergeToolType;
var toolPath = Preference.Instance.ExternalMergeToolPath;
var opt = new Models.DiffOption($"{_selectedStash.SHA}^", _selectedStash.SHA, change);
Task.Run(() => Commands.MergeTool.OpenForDiff(_repo.FullPath, toolType, toolPath, opt));
ev.Handled = true;
};
var fullPath = Path.Combine(_repo.FullPath, change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = App.CreateMenuIcon("Icons.Explore");
explore.IsEnabled = File.Exists(fullPath);
explore.Click += (_, ev) =>
{
Native.OS.OpenInFileManager(fullPath, true);
ev.Handled = true;
};
var resetToThisRevision = new MenuItem();
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
resetToThisRevision.Click += (_, ev) =>
{
new Commands.Checkout(_repo.FullPath).FileWithRevision(change.Path, $"{_selectedStash.SHA}");
ev.Handled = true;
};
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyPath.Click += (_, ev) =>
{
App.CopyText(change.Path);
ev.Handled = true;
};
var copyFileName = new MenuItem();
copyFileName.Header = App.Text("CopyFileName");
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
copyFileName.Click += (_, e) =>
{
App.CopyText(Path.GetFileName(change.Path));
e.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(diffWithMerger);
menu.Items.Add(explore);
menu.Items.Add(new MenuItem { Header = "-" });
menu.Items.Add(resetToThisRevision);
menu.Items.Add(new MenuItem { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFileName);
return menu;
}
public void Clear()
{
if (PopupHost.CanCreatePopup())

View file

@ -12,6 +12,11 @@ namespace SourceGit.ViewModels
public Models.Tag Tag { get; private set; } = null;
public List<TagTreeNode> Children { get; private set; } = [];
public object ToolTip
{
get => Tag?.Message;
}
public bool IsFolder
{
get => Tag == null;

View file

@ -33,49 +33,49 @@
ColumnDefinitions="16,*"
ToolTip.Tip="{Binding Tooltip}">
<!-- Tree Expander -->
<v:BranchTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsVisible="{Binding !IsBranch}"/>
<!-- Tree Expander -->
<v:BranchTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsVisible="{Binding !IsBranch}"/>
<!-- Content Area (allows double-click) -->
<Grid Grid.Column="1"
Background="Transparent"
ColumnDefinitions="18,*,Auto,Auto"
DoubleTapped="OnDoubleTappedBranchNode">
<!-- Content Area (allows double-click) -->
<Grid Grid.Column="1"
Background="Transparent"
ColumnDefinitions="18,*,Auto,Auto"
DoubleTapped="OnDoubleTappedBranchNode">
<!-- Icon -->
<v:BranchTreeNodeIcon Grid.Column="0"
Node="{Binding}"
IsExpanded="{Binding IsExpanded}"/>
<!-- Icon -->
<v:BranchTreeNodeIcon Grid.Column="0"
Node="{Binding}"
IsExpanded="{Binding IsExpanded}"/>
<!-- Name -->
<TextBlock Grid.Column="1"
Text="{Binding Name}"
Classes="primary"
FontWeight="{Binding NameFontWeight}"
TextTrimming="CharacterEllipsis"/>
<!-- Name -->
<TextBlock Grid.Column="1"
Text="{Binding Name}"
Classes="primary"
FontWeight="{Binding NameFontWeight}"
TextTrimming="CharacterEllipsis"/>
<!-- Tracking status -->
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"
VerticalAlignment="Center"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/>
<!-- Tracking status -->
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"
VerticalAlignment="Center"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/>
<!-- Filter Toggle Button -->
<ToggleButton Grid.Column="3"
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
IsVisible="{Binding IsBranch}"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
<!-- Filter Toggle Button -->
<ToggleButton Grid.Column="3"
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
IsVisible="{Binding IsBranch}"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid>
</Grid>
</DataTemplate>

View file

@ -136,7 +136,7 @@
<ListBox.ItemTemplate>
<DataTemplate DataType="m:Change">
<Grid ColumnDefinitions="24,*">
<Grid ColumnDefinitions="24,*" Background="Transparent" ContextRequested="OnChangeContextRequested">
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" Change="{Binding}"/>
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding Path}" Margin="4,0,0,0"/>
</Grid>

View file

@ -18,5 +18,15 @@ namespace SourceGit.Views
}
e.Handled = true;
}
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.StashesPage vm && sender is Grid grid)
{
var menu = vm.MakeContextMenuForChange(grid.DataContext as Models.Change);
grid.OpenContextMenu(menu);
}
e.Handled = true;
}
}
}

View file

@ -26,7 +26,8 @@
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
Background="Transparent"
ContextRequested="OnRowContextRequested"
DoubleTapped="OnDoubleTappedNode">
DoubleTapped="OnDoubleTappedNode"
ToolTip.Tip="{Binding ToolTip}">
<v:TagTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander"
Focusable="False"
@ -64,7 +65,10 @@
SelectionChanged="OnRowSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate DataType="m:Tag">
<Grid ColumnDefinitions="Auto,*,Auto" Background="Transparent" ContextRequested="OnRowContextRequested">
<Grid ColumnDefinitions="Auto,*,Auto"
Background="Transparent"
ContextRequested="OnRowContextRequested"
ToolTip.Tip="{Binding Message}">
<Path Grid.Column="0"
Width="10" Height="10"
Margin="8,0,0,0"