Skip to content

Commit 0324830

Browse files
committed
feature: testing for rebase and show testing result in Rebase popup
This feature requires `git > 2.44.0` Signed-off-by: leo <longshuang@msn.cn>
1 parent ea51c41 commit 0324830

8 files changed

Lines changed: 201 additions & 2 deletions

File tree

src/Commands/MergeBase.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Text.RegularExpressions;
2+
using System.Threading.Tasks;
3+
4+
namespace SourceGit.Commands
5+
{
6+
public partial class MergeBase : Command
7+
{
8+
[GeneratedRegex(@"^[0-9a-f]{8,64}$")]
9+
private static partial Regex REG_HEX();
10+
11+
public MergeBase(string repo, string rev1, string rev2)
12+
{
13+
WorkingDirectory = repo;
14+
Context = repo;
15+
RaiseError = false;
16+
Args = $"merge-base {rev1} {rev2}";
17+
}
18+
19+
public async Task<string> GetResultAsync()
20+
{
21+
var rs = await ReadToEndAsync().ConfigureAwait(false);
22+
if (!rs.IsSuccess)
23+
return string.Empty;
24+
25+
var trimmed = rs.StdOut.Trim();
26+
if (REG_HEX().IsMatch(trimmed))
27+
return trimmed;
28+
29+
return string.Empty;
30+
}
31+
}
32+
}

src/Commands/Replay.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Diagnostics;
2+
using System.Threading.Tasks;
3+
4+
namespace SourceGit.Commands
5+
{
6+
public class Replay : Command
7+
{
8+
public Replay(string repo, string onto, string range)
9+
{
10+
WorkingDirectory = repo;
11+
Context = repo;
12+
RaiseError = false;
13+
Args = $"replay --onto {onto} {range}";
14+
}
15+
16+
public async Task<int> GetExitCodeAsync()
17+
{
18+
using var proc = new Process();
19+
proc.StartInfo = CreateGitStartInfo(false);
20+
21+
var exitCode = -1;
22+
try
23+
{
24+
proc.Start();
25+
await proc.WaitForExitAsync().ConfigureAwait(false);
26+
exitCode = proc.ExitCode;
27+
}
28+
catch
29+
{
30+
// Ignore any exceptions and just return -1
31+
}
32+
33+
return exitCode;
34+
}
35+
}
36+
}

src/Models/GitVersions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,10 @@ public static class GitVersions
2121
/// The minimal version of Git that supports the `git merge-tree --write-tree` command, which is used for testing merge results without actually performing a merge.
2222
/// </summary>
2323
public static readonly System.Version TESTING_MERGE = new(2, 38, 0);
24+
25+
/// <summary>
26+
/// The minimal version of Git that supports the `git replay` command.
27+
/// </summary>
28+
public static readonly System.Version REPLAY = new(2, 44, 0);
2429
}
2530
}

src/Resources/Locales/en_US.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,10 @@
739739
<x:String x:Key="Text.Rebase.AutoStash" xml:space="preserve">Stash &amp; reapply local changes</x:String>
740740
<x:String x:Key="Text.Rebase.NoVerify" xml:space="preserve">Bypass the pre-rebase hook</x:String>
741741
<x:String x:Key="Text.Rebase.On" xml:space="preserve">On:</x:String>
742+
<x:String x:Key="Text.Rebase.Test" xml:space="preserve">Testing for rebase ...</x:String>
743+
<x:String x:Key="Text.Rebase.Test.OK" xml:space="preserve">Rebase can be done without conflicts</x:String>
744+
<x:String x:Key="Text.Rebase.Test.UnknownError" xml:space="preserve">Unknown error occurs while testing for rebase</x:String>
745+
<x:String x:Key="Text.Rebase.Test.WillCauseConflicts" xml:space="preserve">Rebase will cause conflicts</x:String>
742746
<x:String x:Key="Text.Remote.AddTitle" xml:space="preserve">Add Remote</x:String>
743747
<x:String x:Key="Text.Remote.EditTitle" xml:space="preserve">Edit Remote</x:String>
744748
<x:String x:Key="Text.Remote.Name" xml:space="preserve">Name:</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,10 @@
743743
<x:String x:Key="Text.Rebase.AutoStash" xml:space="preserve">自动贮藏并恢复本地变更</x:String>
744744
<x:String x:Key="Text.Rebase.NoVerify" xml:space="preserve">绕过 pre-rebase 钩子</x:String>
745745
<x:String x:Key="Text.Rebase.On" xml:space="preserve">目标提交 :</x:String>
746+
<x:String x:Key="Text.Rebase.Test" xml:space="preserve">检测变基冲突中 ...</x:String>
747+
<x:String x:Key="Text.Rebase.Test.OK" xml:space="preserve">未检测到文件冲突</x:String>
748+
<x:String x:Key="Text.Rebase.Test.UnknownError" xml:space="preserve">检测变基冲突时出现未知错误</x:String>
749+
<x:String x:Key="Text.Rebase.Test.WillCauseConflicts" xml:space="preserve">检测到冲突文件</x:String>
746750
<x:String x:Key="Text.Remote.AddTitle" xml:space="preserve">添加远程仓库</x:String>
747751
<x:String x:Key="Text.Remote.EditTitle" xml:space="preserve">编辑远程仓库</x:String>
748752
<x:String x:Key="Text.Remote.Name" xml:space="preserve">远程名 :</x:String>

src/Resources/Locales/zh_TW.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,10 @@
743743
<x:String x:Key="Text.Rebase.AutoStash" xml:space="preserve">自動擱置變更並復原本機變更</x:String>
744744
<x:String x:Key="Text.Rebase.NoVerify" xml:space="preserve">繞過 pre-rebase 鉤子</x:String>
745745
<x:String x:Key="Text.Rebase.On" xml:space="preserve">目標提交:</x:String>
746+
<x:String x:Key="Text.Rebase.Test" xml:space="preserve">偵測重定基底衝突中...</x:String>
747+
<x:String x:Key="Text.Rebase.Test.OK" xml:space="preserve">重定基底操作不會產生衝突</x:String>
748+
<x:String x:Key="Text.Rebase.Test.UnknownError" xml:space="preserve">偵測重定基底衝突時發生未知錯誤</x:String>
749+
<x:String x:Key="Text.Rebase.Test.WillCauseConflicts" xml:space="preserve">重定基底操作將存在衝突檔案</x:String>
746750
<x:String x:Key="Text.Remote.AddTitle" xml:space="preserve">新增遠端存放庫</x:String>
747751
<x:String x:Key="Text.Remote.EditTitle" xml:space="preserve">編輯遠端存放庫</x:String>
748752
<x:String x:Key="Text.Remote.Name" xml:space="preserve">遠端名稱:</x:String>

src/ViewModels/Rebase.cs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
using System.Threading.Tasks;
1+
using System;
2+
using System.Threading.Tasks;
3+
using Avalonia.Threading;
24

35
namespace SourceGit.ViewModels
46
{
7+
public enum RebaseTestingState
8+
{
9+
Disabled = 0,
10+
Testing = 1,
11+
WillCauseConflicts = 2,
12+
UnknownError = 3,
13+
NoConflicts = 4,
14+
}
15+
516
public class Rebase : Popup
617
{
718
public Models.Branch Current
@@ -28,13 +39,21 @@ public bool NoVerify
2839
set;
2940
}
3041

42+
public RebaseTestingState TestingState
43+
{
44+
get => _testingState;
45+
private set => SetProperty(ref _testingState, value);
46+
}
47+
3148
public Rebase(Repository repo, Models.Branch current, Models.Branch on)
3249
{
3350
_repo = repo;
3451
_revision = on.Head;
3552
Current = current;
3653
On = on;
3754
AutoStash = true;
55+
56+
Test();
3857
}
3958

4059
public Rebase(Repository repo, Models.Branch current, Models.Commit on)
@@ -44,6 +63,8 @@ public Rebase(Repository repo, Models.Branch current, Models.Commit on)
4463
Current = current;
4564
On = on;
4665
AutoStash = true;
66+
67+
Test();
4768
}
4869

4970
public override async Task<bool> Sure()
@@ -63,7 +84,48 @@ public override async Task<bool> Sure()
6384
return true;
6485
}
6586

87+
private void Test()
88+
{
89+
if (Native.OS.GitVersion < Models.GitVersions.REPLAY)
90+
return;
91+
92+
var head = Current.Head;
93+
TestingState = RebaseTestingState.Testing;
94+
Task.Run(async () =>
95+
{
96+
var mergeBase = await new Commands.MergeBase(_repo.FullPath, head, _revision)
97+
.GetResultAsync()
98+
.ConfigureAwait(false);
99+
100+
if (string.IsNullOrEmpty(mergeBase))
101+
{
102+
Dispatcher.UIThread.Post(() => TestingState = RebaseTestingState.UnknownError);
103+
return;
104+
}
105+
else if (head.Equals(mergeBase, StringComparison.Ordinal))
106+
{
107+
Dispatcher.UIThread.Post(() => TestingState = RebaseTestingState.NoConflicts);
108+
return;
109+
}
110+
111+
var exitCode = await new Commands.Replay(_repo.FullPath, _revision, $"{mergeBase}..{head}")
112+
.GetExitCodeAsync()
113+
.ConfigureAwait(false);
114+
115+
Dispatcher.UIThread.Post(() =>
116+
{
117+
TestingState = exitCode switch
118+
{
119+
0 => RebaseTestingState.NoConflicts,
120+
1 => RebaseTestingState.WillCauseConflicts,
121+
_ => RebaseTestingState.UnknownError,
122+
};
123+
});
124+
});
125+
}
126+
66127
private readonly Repository _repo;
67128
private readonly string _revision;
129+
private RebaseTestingState _testingState = RebaseTestingState.Disabled;
68130
}
69131
}

src/Views/Rebase.axaml

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
55
xmlns:m="using:SourceGit.Models"
66
xmlns:vm="using:SourceGit.ViewModels"
7+
xmlns:v="using:SourceGit.Views"
78
xmlns:c="using:SourceGit.Converters"
89
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
910
x:Class="SourceGit.Views.Rebase"
@@ -19,7 +20,7 @@
1920
Text="{DynamicResource Text.Rebase}"/>
2021
</StackPanel>
2122

22-
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32" ColumnDefinitions="130,*">
23+
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,Auto" ColumnDefinitions="130,*">
2324
<TextBlock Grid.Row="0" Grid.Column="0"
2425
HorizontalAlignment="Right" VerticalAlignment="Center"
2526
Margin="0,0,8,0"
@@ -61,6 +62,57 @@
6162
Content="{DynamicResource Text.Rebase.NoVerify}"
6263
IsChecked="{Binding NoVerify, Mode=TwoWay}"
6364
ToolTip.Tip="--no-verify"/>
65+
66+
<Grid Grid.Row="4" Grid.Column="1"
67+
Height="32"
68+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.NotEqual}, ConverterParameter={x:Static vm:RebaseTestingState.Disabled}}">
69+
<StackPanel Orientation="Horizontal"
70+
VerticalAlignment="Center"
71+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static vm:RebaseTestingState.Testing}}">
72+
<v:LoadingIcon Width="14" Height="14"
73+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static vm:RebaseTestingState.Testing}}"/>
74+
75+
<TextBlock Margin="8,0,0,0"
76+
VerticalAlignment="Center"
77+
Text="{DynamicResource Text.Rebase.Test}"/>
78+
</StackPanel>
79+
80+
<StackPanel Orientation="Horizontal"
81+
VerticalAlignment="Center"
82+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static vm:RebaseTestingState.NoConflicts}}">
83+
<Path Width="14" Height="14"
84+
Data="{DynamicResource Icons.CheckCircled}"
85+
Fill="Green"/>
86+
87+
<TextBlock Margin="8,0,0,0"
88+
VerticalAlignment="Center"
89+
Text="{DynamicResource Text.Rebase.Test.OK}"/>
90+
</StackPanel>
91+
92+
<StackPanel Orientation="Horizontal"
93+
VerticalAlignment="Center"
94+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static vm:RebaseTestingState.WillCauseConflicts}}">
95+
<Path Width="14" Height="14"
96+
Data="{DynamicResource Icons.Error}"
97+
Fill="Goldenrod"/>
98+
99+
<TextBlock Margin="8,0,0,0"
100+
VerticalAlignment="Center"
101+
Text="{DynamicResource Text.Rebase.Test.WillCauseConflicts}"/>
102+
</StackPanel>
103+
104+
<StackPanel Orientation="Horizontal"
105+
VerticalAlignment="Center"
106+
IsVisible="{Binding TestingState, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static vm:RebaseTestingState.UnknownError}}">
107+
<Path Width="14" Height="14"
108+
Data="{DynamicResource Icons.Error}"
109+
Fill="Goldenrod"/>
110+
111+
<TextBlock Margin="8,0,0,0"
112+
VerticalAlignment="Center"
113+
Text="{DynamicResource Text.Rebase.Test.UnknownError}"/>
114+
</StackPanel>
115+
</Grid>
64116
</Grid>
65117
</StackPanel>
66118
</UserControl>

0 commit comments

Comments
 (0)