feature: support add custom actions for selected branch (#980)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-02-14 10:43:08 +08:00
parent 9b07034846
commit 9104060d79
No known key found for this signature in database
7 changed files with 73 additions and 22 deletions

View file

@ -6,6 +6,7 @@ namespace SourceGit.Models
{ {
Repository, Repository,
Commit, Commit,
Branch,
} }
public class CustomAction : ObservableObject public class CustomAction : ObservableObject

View file

@ -56,6 +56,7 @@
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String> <x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String> <x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String> <x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String>
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">Custom Action</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String> <x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Discard all changes</x:String> <x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Discard all changes</x:String>
@ -152,10 +153,11 @@
<x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">Template Content:</x:String> <x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">Template Content:</x:String>
<x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">CUSTOM ACTION</x:String> <x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">CUSTOM ACTION</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">Arguments:</x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">Arguments:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">${REPO} - Repository's path; ${SHA} - Selected commit's SHA</x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">${REPO} - Repository's path; ${BRANCH} - Selected branch; ${SHA} - Selected commit's SHA</x:String>
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Executable File:</x:String> <x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Executable File:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Name:</x:String> <x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Name:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Scope:</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Scope:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Branch" xml:space="preserve">Branch</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repository</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repository</x:String>
<x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">Wait for action exit</x:String> <x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">Wait for action exit</x:String>

View file

@ -59,6 +59,7 @@
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String> <x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String> <x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String> <x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String>
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">自定义操作</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除 ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String> <x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">放弃所有更改</x:String> <x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">放弃所有更改</x:String>
@ -155,10 +156,11 @@
<x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">模板内容 </x:String> <x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">模板内容 </x:String>
<x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">自定义操作</x:String> <x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">自定义操作</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">命令行参数 </x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">命令行参数 </x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">请使用${REPO}代替仓库路径,${SHA}代替提交哈希</x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">请使用${REPO}代替仓库路径,${BRANCH}代替选中的分支,${SHA}代替提交哈希</x:String>
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">可执行文件路径 </x:String> <x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">可执行文件路径 </x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">名称 </x:String> <x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">名称 </x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">作用目标 </x:String> <x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">作用目标 </x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Branch" xml:space="preserve">选中的分支</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">选中的提交</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">选中的提交</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">仓库</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">仓库</x:String>
<x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">等待操作执行完成</x:String> <x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">等待操作执行完成</x:String>

View file

@ -59,6 +59,7 @@
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">與目前 HEAD 比較</x:String> <x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">與目前 HEAD 比較</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">與本機工作區比較</x:String> <x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">與本機工作區比較</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">複製分支名稱</x:String> <x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">複製分支名稱</x:String>
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">自訂動作</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">刪除 ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">刪除 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">刪除所選的 {0} 個分支</x:String> <x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">刪除所選的 {0} 個分支</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">捨棄所有變更</x:String> <x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">捨棄所有變更</x:String>
@ -155,10 +156,11 @@
<x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">範本內容:</x:String> <x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">範本內容:</x:String>
<x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">自訂動作</x:String> <x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">自訂動作</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">指令參數:</x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">指令參數:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">使用 ${REPO} 表示存放庫路徑、${SHA} 表示所選的提交編號</x:String> <x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">使用 ${REPO} 表示存放庫路徑、${BRANCH} 表示所選的分支、${SHA} 表示所選的提交編號</x:String>
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">可執行檔案路徑:</x:String> <x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">可執行檔案路徑:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">名稱:</x:String> <x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">名稱:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">執行範圍:</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">執行範圍:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Branch" xml:space="preserve">選取的分支</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">選取的提交</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">選取的提交</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">存放庫</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">存放庫</x:String>
<x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">等待自訂操作退出</x:String> <x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">等待自訂操作退出</x:String>

View file

@ -10,13 +10,26 @@ namespace SourceGit.ViewModels
private set; private set;
} }
public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit) public ExecuteCustomAction(Repository repo, Models.CustomAction action)
{ {
_repo = repo; _repo = repo;
_args = action.Arguments.Replace("${REPO}", _repo.FullPath); _args = action.Arguments.Replace("${REPO}", _repo.FullPath);
if (commit != null) CustomAction = action;
_args = _args.Replace("${SHA}", commit.SHA); View = new Views.ExecuteCustomAction() { DataContext = this };
}
public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Branch branch)
{
_repo = repo;
_args = action.Arguments.Replace("${REPO}", _repo.FullPath).Replace("${BRANCH}", branch.FriendlyName);
CustomAction = action;
View = new Views.ExecuteCustomAction() { DataContext = this };
}
public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit)
{
_repo = repo;
_args = action.Arguments.Replace("${REPO}", _repo.FullPath).Replace("${SHA}", commit.SHA);
CustomAction = action; CustomAction = action;
View = new Views.ExecuteCustomAction() { DataContext = this }; View = new Views.ExecuteCustomAction() { DataContext = this };
} }

View file

@ -1439,7 +1439,7 @@ namespace SourceGit.ViewModels
item.Click += (_, e) => item.Click += (_, e) =>
{ {
if (CanCreatePopup()) if (CanCreatePopup())
ShowAndStartPopup(new ExecuteCustomAction(this, dup, null)); ShowAndStartPopup(new ExecuteCustomAction(this, dup));
e.Handled = true; e.Handled = true;
}; };
@ -1698,6 +1698,7 @@ namespace SourceGit.ViewModels
menu.Items.Add(createBranch); menu.Items.Add(createBranch);
menu.Items.Add(createTag); menu.Items.Add(createTag);
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
TryToAddCustomActionsToBranchContextMenu(menu, branch);
if (!IsBare) if (!IsBare)
{ {
@ -1968,7 +1969,9 @@ namespace SourceGit.ViewModels
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(archive); menu.Items.Add(archive);
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
TryToAddCustomActionsToBranchContextMenu(menu, branch);
menu.Items.Add(copy); menu.Items.Add(copy);
return menu; return menu;
} }
@ -2319,6 +2322,43 @@ namespace SourceGit.ViewModels
return null; return null;
} }
private void TryToAddCustomActionsToBranchContextMenu(ContextMenu menu, Models.Branch branch)
{
var actions = new List<Models.CustomAction>();
foreach (var action in Settings.CustomActions)
{
if (action.Scope == Models.CustomActionScope.Branch)
actions.Add(action);
}
if (actions.Count == 0)
return;
var custom = new MenuItem();
custom.Header = App.Text("BranchCM.CustomAction");
custom.Icon = App.CreateMenuIcon("Icons.Action");
foreach (var action in actions)
{
var dup = action;
var item = new MenuItem();
item.Icon = App.CreateMenuIcon("Icons.Action");
item.Header = dup.Name;
item.Click += (_, e) =>
{
if (CanCreatePopup())
ShowAndStartPopup(new ExecuteCustomAction(this, dup, branch));
e.Handled = true;
};
custom.Items.Add(item);
}
menu.Items.Add(custom);
menu.Items.Add(new MenuItem() { Header = "-" });
}
private void UpdateCurrentRevisionFilesForSearchSuggestion() private void UpdateCurrentRevisionFilesForSearchSuggestion()
{ {
_revisionFiles.Clear(); _revisionFiles.Clear();

View file

@ -5,7 +5,6 @@
xmlns:m="using:SourceGit.Models" xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views" xmlns:v="using:SourceGit.Views"
xmlns:ac="using:Avalonia.Controls.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryConfigure" x:Class="SourceGit.Views.RepositoryConfigure"
x:DataType="vm:RepositoryConfigure" x:DataType="vm:RepositoryConfigure"
@ -412,20 +411,12 @@
<TextBlock Text="{DynamicResource Text.Configure.CustomAction.Name}"/> <TextBlock Text="{DynamicResource Text.Configure.CustomAction.Name}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/> <TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0"> <TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Configure.CustomAction.Scope}"/>
<StackPanel.Resources> <ComboBox Margin="0,4,0,0" Height="28" HorizontalAlignment="Stretch" SelectedIndex="{Binding Scope, Mode=TwoWay}">
<ac:EnumToBoolConverter x:Key="EnumToBoolConverter"/> <ComboBoxItem Content="{DynamicResource Text.Configure.CustomAction.Scope.Repository}"/>
</StackPanel.Resources> <ComboBoxItem Content="{DynamicResource Text.Configure.CustomAction.Scope.Commit}"/>
<ComboBoxItem Content="{DynamicResource Text.Configure.CustomAction.Scope.Branch}"/>
<TextBlock Text="{DynamicResource Text.Configure.CustomAction.Scope}"/> </ComboBox>
<RadioButton Content="{DynamicResource Text.Configure.CustomAction.Scope.Repository}"
GroupName="CustomActionScope"
Margin="8,0"
IsChecked="{Binding Scope, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:CustomActionScope.Repository}}"/>
<RadioButton Content="{DynamicResource Text.Configure.CustomAction.Scope.Commit}"
GroupName="CustomActionScope"
IsChecked="{Binding Scope, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:CustomActionScope.Commit}}"/>
</StackPanel>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Configure.CustomAction.Executable}"/> <TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Configure.CustomAction.Executable}"/>
<TextBox Margin="0,4,0,0" Height="28" CornerRadius="3" Text="{Binding Executable, Mode=TwoWay}"> <TextBox Margin="0,4,0,0" Height="28" CornerRadius="3" Text="{Binding Executable, Mode=TwoWay}">