feature: supports to change submodule's branch (#1513)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-07-05 19:15:41 +08:00
parent 7672218eba
commit fba1b63af8
12 changed files with 213 additions and 12 deletions

View File

@@ -70,6 +70,7 @@ namespace SourceGit.Commands
{
var modules = new Dictionary<string, ModuleInfo>();
lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT_MODULE_INFO().Match(line);
@@ -81,21 +82,39 @@ namespace SourceGit.Commands
if (!modules.TryGetValue(name, out var m))
{
m = new ModuleInfo();
modules.Add(name, m);
// Find name alias.
foreach (var kv in modules)
{
if (kv.Value.Path.Equals(name, StringComparison.Ordinal))
{
m = kv.Value;
break;
}
}
if (m == null)
{
m = new ModuleInfo();
modules.Add(name, m);
}
}
if (key.Equals("path", StringComparison.Ordinal))
m.Path = val;
else if (key.Equals("url", StringComparison.Ordinal))
m.URL = val;
else if (key.Equals("branch", StringComparison.Ordinal))
m.Branch = val;
}
}
foreach (var kv in modules)
{
if (map.TryGetValue(kv.Value.Path, out var m))
{
m.URL = kv.Value.URL;
m.Branch = kv.Value.Branch;
}
}
}
}
@@ -138,6 +157,7 @@ namespace SourceGit.Commands
{
public string Path { get; set; } = string.Empty;
public string URL { get; set; } = string.Empty;
public string Branch { get; set; } = string.Empty;
}
}
}

View File

@@ -27,12 +27,22 @@ namespace SourceGit.Commands
return await ExecAsync().ConfigureAwait(false);
}
public async Task<bool> SetURL(string path, string url)
public async Task<bool> SetURLAsync(string path, string url)
{
Args = $"submodule set-url -- \"{path}\" \"{url}\"";
return await ExecAsync().ConfigureAwait(false);
}
public async Task<bool> SetBranchAsync(string path, string branch)
{
if (string.IsNullOrEmpty(branch))
Args = $"submodule set-branch -d -- \"{path}\"";
else
Args = $"submodule set-branch -b \"{branch}\" -- \"{path}\"";
return await ExecAsync().ConfigureAwait(false);
}
public async Task<bool> UpdateAsync(List<string> modules, bool init, bool recursive, bool useRemote = false)
{
var builder = new StringBuilder();

View File

@@ -14,6 +14,7 @@
public string Path { get; set; } = string.Empty;
public string SHA { get; set; } = string.Empty;
public string URL { get; set; } = string.Empty;
public string Branch { get; set; } = string.Empty;
public SubmoduleStatus Status { get; set; } = SubmoduleStatus.Normal;
public bool IsDirty => Status > SubmoduleStatus.NotInited;
}

View File

@@ -704,6 +704,11 @@
<x:String x:Key="Text.SelfUpdate.IgnoreThisVersion" xml:space="preserve">Skip This Version</x:String>
<x:String x:Key="Text.SelfUpdate.Title" xml:space="preserve">Software Update</x:String>
<x:String x:Key="Text.SelfUpdate.UpToDate" xml:space="preserve">There are currently no updates available.</x:String>
<x:String x:Key="Text.SetSubmoduleBranch" xml:space="preserve">Set Submodule's Branch</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Submodule" xml:space="preserve">Submodule:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Current" xml:space="preserve">Current:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New" xml:space="preserve">Change To:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New.Tip" xml:space="preserve">Optional. Set to default when it is empty.</x:String>
<x:String x:Key="Text.SetUpstream" xml:space="preserve">Set Tracking Branch</x:String>
<x:String x:Key="Text.SetUpstream.Local" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.SetUpstream.Unset" xml:space="preserve">Unset upstream</x:String>
@@ -742,6 +747,7 @@
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMITS: </x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SUBMODULES</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">Add Submodule</x:String>
<x:String x:Key="Text.Submodule.Branch" xml:space="preserve">Branch</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">Relative Path</x:String>
<x:String x:Key="Text.Submodule.Deinit" xml:space="preserve">De-initialize</x:String>
<x:String x:Key="Text.Submodule.FetchNested" xml:space="preserve">Fetch nested submodules</x:String>
@@ -751,6 +757,7 @@
<x:String x:Key="Text.Submodule.RelativePath" xml:space="preserve">Relative Path:</x:String>
<x:String x:Key="Text.Submodule.RelativePath.Placeholder" xml:space="preserve">Relative folder to store this module.</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">Delete</x:String>
<x:String x:Key="Text.Submodule.SetBranch" xml:space="preserve">Set Branch</x:String>
<x:String x:Key="Text.Submodule.SetURL" xml:space="preserve">Change URL</x:String>
<x:String x:Key="Text.Submodule.Status" xml:space="preserve">STATUS</x:String>
<x:String x:Key="Text.Submodule.Status.Modified" xml:space="preserve">modified</x:String>

View File

@@ -708,6 +708,11 @@
<x:String x:Key="Text.SelfUpdate.IgnoreThisVersion" xml:space="preserve">忽略此版本</x:String>
<x:String x:Key="Text.SelfUpdate.Title" xml:space="preserve">软件更新</x:String>
<x:String x:Key="Text.SelfUpdate.UpToDate" xml:space="preserve">当前已是最新版本。</x:String>
<x:String x:Key="Text.SetSubmoduleBranch" xml:space="preserve">修改子模块追踪分支</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Submodule" xml:space="preserve">子模块 </x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Current" xml:space="preserve">当前追踪分支 </x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New" xml:space="preserve">修改为 </x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New.Tip" xml:space="preserve">可选。当不填写时,恢复默认追踪分支。</x:String>
<x:String x:Key="Text.SetUpstream" xml:space="preserve">切换上游分支</x:String>
<x:String x:Key="Text.SetUpstream.Local" xml:space="preserve">本地分支 </x:String>
<x:String x:Key="Text.SetUpstream.Unset" xml:space="preserve">取消追踪</x:String>
@@ -746,6 +751,7 @@
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">提交次数: </x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">子模块</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">添加子模块</x:String>
<x:String x:Key="Text.Submodule.Branch" xml:space="preserve">跟踪分支</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">相对路径</x:String>
<x:String x:Key="Text.Submodule.Deinit" xml:space="preserve">取消初始化</x:String>
<x:String x:Key="Text.Submodule.FetchNested" xml:space="preserve">拉取子孙模块</x:String>
@@ -755,6 +761,7 @@
<x:String x:Key="Text.Submodule.RelativePath" xml:space="preserve">相对仓库路径 </x:String>
<x:String x:Key="Text.Submodule.RelativePath.Placeholder" xml:space="preserve">本地存放的相对路径。</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">删除</x:String>
<x:String x:Key="Text.Submodule.SetBranch" xml:space="preserve">修改跟踪分支</x:String>
<x:String x:Key="Text.Submodule.SetURL" xml:space="preserve">修改远程地址</x:String>
<x:String x:Key="Text.Submodule.Status" xml:space="preserve">状态</x:String>
<x:String x:Key="Text.Submodule.Status.Modified" xml:space="preserve">未提交修改</x:String>

View File

@@ -708,6 +708,11 @@
<x:String x:Key="Text.SelfUpdate.IgnoreThisVersion" xml:space="preserve">忽略此版本</x:String>
<x:String x:Key="Text.SelfUpdate.Title" xml:space="preserve">軟體更新</x:String>
<x:String x:Key="Text.SelfUpdate.UpToDate" xml:space="preserve">目前已是最新版本。</x:String>
<x:String x:Key="Text.SetSubmoduleBranch" xml:space="preserve">設定子模組的追蹤分支</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Submodule" xml:space="preserve">子模組:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Current" xml:space="preserve">目前追蹤分支:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New" xml:space="preserve">變更為:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New.Tip" xml:space="preserve">選購。為空時設定為預設值。</x:String>
<x:String x:Key="Text.SetUpstream" xml:space="preserve">切換上游分支</x:String>
<x:String x:Key="Text.SetUpstream.Local" xml:space="preserve">本機分支:</x:String>
<x:String x:Key="Text.SetUpstream.Unset" xml:space="preserve">取消設定上游分支</x:String>
@@ -746,6 +751,7 @@
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">提交次數:</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">子模組</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">新增子模組</x:String>
<x:String x:Key="Text.Submodule.Branch" xml:space="preserve">追蹤分支</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">相對路徑</x:String>
<x:String x:Key="Text.Submodule.Deinit" xml:space="preserve">取消初始化</x:String>
<x:String x:Key="Text.Submodule.FetchNested" xml:space="preserve">提取子模組</x:String>
@@ -755,6 +761,7 @@
<x:String x:Key="Text.Submodule.RelativePath" xml:space="preserve">相對存放庫路徑:</x:String>
<x:String x:Key="Text.Submodule.RelativePath.Placeholder" xml:space="preserve">本機存放的相對路徑。</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">刪除</x:String>
<x:String x:Key="Text.Submodule.SetBranch" xml:space="preserve">更改追蹤分支</x:String>
<x:String x:Key="Text.Submodule.SetURL" xml:space="preserve">更改遠端網址</x:String>
<x:String x:Key="Text.Submodule.Status" xml:space="preserve">狀態</x:String>
<x:String x:Key="Text.Submodule.Status.Modified" xml:space="preserve">未提交變更</x:String>

View File

@@ -47,7 +47,7 @@ namespace SourceGit.ViewModels
var succ = await new Commands.Submodule(_repo.FullPath)
.Use(log)
.SetURL(Submodule.Path, _url);
.SetURLAsync(Submodule.Path, _url);
log.Complete();
_repo.SetWatcherEnabled(true);

View File

@@ -1272,8 +1272,9 @@ namespace SourceGit.ViewModels
}
hasChanged = !exist.SHA.Equals(module.SHA, StringComparison.Ordinal) ||
!exist.URL.Equals(module.URL, StringComparison.Ordinal) ||
exist.Status != module.Status;
!exist.Branch.Equals(module.Branch, StringComparison.Ordinal) ||
!exist.URL.Equals(module.URL, StringComparison.Ordinal) ||
exist.Status != module.Status;
if (hasChanged)
break;
@@ -2584,6 +2585,16 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
var setBranch = new MenuItem();
setBranch.Header = App.Text("Submodule.SetBranch");
setBranch.Icon = App.CreateMenuIcon("Icons.Track");
setBranch.Click += (_, ev) =>
{
if (CanCreatePopup())
ShowPopup(new SetSubmoduleBranch(this, submodule));
ev.Handled = true;
};
var deinit = new MenuItem();
deinit.Header = App.Text("Submodule.Deinit");
deinit.Icon = App.CreateMenuIcon("Icons.Undo");
@@ -2641,10 +2652,20 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
var copyBranch = new MenuItem();
copyBranch.Header = App.Text("Submodule.Branch");
copyBranch.Icon = App.CreateMenuIcon("Icons.Branch");
copyBranch.Click += async (_, ev) =>
{
await App.CopyTextAsync(submodule.Branch);
ev.Handled = true;
};
var copy = new MenuItem();
copy.Header = App.Text("Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Items.Add(copySHA);
copy.Items.Add(copyBranch);
copy.Items.Add(copyRelativePath);
copy.Items.Add(copyURL);
@@ -2653,6 +2674,7 @@ namespace SourceGit.ViewModels
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(update);
menu.Items.Add(setURL);
menu.Items.Add(setBranch);
menu.Items.Add(move);
menu.Items.Add(deinit);
menu.Items.Add(rm);

View File

@@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
namespace SourceGit.ViewModels
{
public class SetSubmoduleBranch : Popup
{
public Models.Submodule Submodule
{
get;
}
public string ChangeTo
{
get => _changeTo;
set => SetProperty(ref _changeTo, value);
}
public SetSubmoduleBranch(Repository repo, Models.Submodule submodule)
{
_repo = repo;
_changeTo = submodule.Branch;
Submodule = submodule;
}
public override async Task<bool> Sure()
{
ProgressDescription = "Set submodule's branch ...";
if (_changeTo.Equals(Submodule.Branch, StringComparison.Ordinal))
return true;
var log = _repo.CreateLog("Set Submodule's Branch");
_repo.SetWatcherEnabled(false);
Use(log);
var succ = await new Commands.Submodule(_repo.FullPath)
.Use(log)
.SetBranchAsync(Submodule.Path, _changeTo);
log.Complete();
_repo.SetWatcherEnabled(true);
return succ;
}
private readonly Repository _repo;
private string _changeTo;
}
}

View File

@@ -0,0 +1,49 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.SetSubmoduleBranch"
x:DataType="vm:SetSubmoduleBranch">
<StackPanel Orientation="Vertical" Margin="8,0">
<TextBlock FontSize="18"
Classes="bold"
Text="{DynamicResource Text.SetSubmoduleBranch}"/>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32" ColumnDefinitions="120,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.SetSubmoduleBranch.Submodule}"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
<Path Width="14" Height="14" Data="{StaticResource Icons.Submodule}"/>
<TextBlock Margin="6,0,0,0" Text="{Binding Submodule.Path}"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.SetSubmoduleBranch.Current}"/>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
<Path Width="14" Height="14" Data="{StaticResource Icons.Branch}"/>
<TextBlock Margin="6,0,0,0"
Text="{Binding Submodule.Branch, Mode=OneWay}"
IsVisible="{Binding Submodule.Branch, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Margin="6,0,0,0"
Text="--"
IsVisible="{Binding Submodule.Branch, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
</StackPanel>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.SetSubmoduleBranch.New}"/>
<TextBox Grid.Row="2" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
Watermark="{DynamicResource Text.SetSubmoduleBranch.New.Tip}"
Text="{Binding ChangeTo, Mode=TwoWay}"/>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,13 @@
using Avalonia.Controls;
namespace SourceGit.Views
{
public partial class SetSubmoduleBranch : UserControl
{
public SetSubmoduleBranch()
{
InitializeComponent();
}
}
}

View File

@@ -147,14 +147,31 @@
<TextBlock FontWeight="Bold" Margin="4,0,0,0" Text="{Binding Path}"/>
</StackPanel>
<Grid RowDefinitions="24,24" ColumnDefinitions="Auto,Auto" Margin="0,8,0,0">
<Grid RowDefinitions="24,24,24" ColumnDefinitions="Auto,Auto" Margin="0,8,0,0">
<TextBlock Grid.Row="0" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.CommitDetail.Info.SHA}"/>
Text="{DynamicResource Text.Submodule.Branch}"/>
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal"
Margin="8,0,0,0">
<Path Width="14" Height="14" Data="{StaticResource Icons.Branch}"/>
<TextBlock Margin="6,0,0,0"
Text="{Binding Branch, Mode=OneWay}"
IsVisible="{Binding Branch, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Margin="6,0,0,0"
Text="--"
IsVisible="{Binding Branch, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.CommitDetail.Info.SHA}"/>
<StackPanel Grid.Row="1" Grid.Column="1"
Orientation="Horizontal"
Margin="8,0,0,0">
<TextBlock Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
VerticalAlignment="Center"/>
@@ -190,12 +207,12 @@
</Grid>
</Border>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
<TextBlock Grid.Row="2" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.URL}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
<TextBlock Grid.Row="2" Grid.Column="1"
Margin="8,0,0,0"
Text="{Binding URL}"
Foreground="{DynamicResource Brush.Link}"
@@ -203,7 +220,6 @@
</Grid>
</StackPanel>
</ToolTip.Tip>
<Grid ColumnDefinitions="16,*,Auto" Margin="8,0,0,0" VerticalAlignment="Center">
<Path Grid.Column="0" Width="10" Height="10" Margin="8,0" Data="{StaticResource Icons.Submodule}"/>
<TextBlock Grid.Column="1" Text="{Binding Path}" ClipToBounds="True" Classes="primary" TextTrimming="CharacterEllipsis"/>