Skip to content

Commit 8317794

Browse files
committed
feature: support to ternimate running clone/fetch/pull/push command on Windows
Signed-off-by: leo <longshuang@msn.cn>
1 parent d43ce17 commit 8317794

17 files changed

Lines changed: 204 additions & 16 deletions

File tree

src/Commands/Command.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public async Task<bool> ExecAsync()
6262
lock (capturedLock)
6363
{
6464
if (captured is { Process: { HasExited: false } })
65-
captured.Process.Kill(true);
65+
Native.OS.TerminateProcess(captured.Process);
6666
}
6767
});
6868
}

src/Native/Linux.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ public void OpenWithDefaultEditor(string file)
138138
}
139139
}
140140

141+
public void TerminateProcess(Process proc)
142+
{
143+
proc.Kill(true);
144+
}
145+
141146
private string FindExecutable(string filename)
142147
{
143148
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;

src/Native/MacOS.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ public void OpenWithDefaultEditor(string file)
113113
{
114114
Process.Start("open", file.Quoted());
115115
}
116+
117+
public void TerminateProcess(Process proc)
118+
{
119+
proc.Kill(true);
120+
}
116121
}
117122

118123
[SupportedOSPlatform("macOS")]

src/Native/OS.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public interface IBackend
2828
void OpenInFileManager(string path);
2929
void OpenBrowser(string url);
3030
void OpenWithDefaultEditor(string file);
31+
32+
void TerminateProcess(Process proc);
3133
}
3234

3335
public static string DataDir
@@ -297,6 +299,11 @@ public static string GetRelativePathToHome(string path)
297299
return path;
298300
}
299301

302+
public static void TerminateProcess(Process proc)
303+
{
304+
_backend?.TerminateProcess(proc);
305+
}
306+
300307
private static void UpdateGitVersion()
301308
{
302309
if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable))

src/Native/Windows.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ internal class Windows : OS.IBackend
2929
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
3030
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
3131

32+
[DllImport("kernel32.dll")]
33+
private static extern bool SetConsoleCtrlHandler(IntPtr handler, bool add);
34+
35+
[DllImport("kernel32.dll", SetLastError = true)]
36+
private static extern bool AttachConsole(int dwProcessId);
37+
38+
[DllImport("kernel32.dll", SetLastError = true)]
39+
private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, int dwProcessGroupId);
40+
41+
[DllImport("kernel32.dll", SetLastError = true)]
42+
private static extern bool FreeConsole();
43+
3244
public void SetupApp(AppBuilder builder)
3345
{
3446
// Do nothing for now.
@@ -196,6 +208,31 @@ public void OpenWithDefaultEditor(string file)
196208
Process.Start(start);
197209
}
198210

211+
public void TerminateProcess(Process proc)
212+
{
213+
var pid = proc.Id;
214+
if (AttachConsole(pid))
215+
{
216+
SetConsoleCtrlHandler(IntPtr.Zero, true);
217+
try
218+
{
219+
if (!GenerateConsoleCtrlEvent(0, 0))
220+
proc.Kill(true);
221+
222+
proc.WaitForExit(2000);
223+
}
224+
finally
225+
{
226+
FreeConsole();
227+
SetConsoleCtrlHandler(IntPtr.Zero, false);
228+
}
229+
}
230+
else
231+
{
232+
proc.Kill(true);
233+
}
234+
}
235+
199236
#region HELPER_METHODS
200237
private List<Models.ExternalTool.LaunchOption> GenerateVSProjectLaunchOptions(string path)
201238
{

src/Resources/Locales/en_US.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@
963963
<x:String x:Key="Text.TagCM.DeleteMultiple" xml:space="preserve">Delete selected {0} tags...</x:String>
964964
<x:String x:Key="Text.TagCM.Merge" xml:space="preserve">Merge ${0}$ into ${1}$...</x:String>
965965
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">Push ${0}$...</x:String>
966+
<x:String x:Key="Text.Terminate" xml:space="preserve">TERMINATE</x:String>
966967
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">Update Submodules</x:String>
967968
<x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">All submodules</x:String>
968969
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">Initialize as needed</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@
967967
<x:String x:Key="Text.TagCM.DeleteMultiple" xml:space="preserve">删除选中 {0} 个标签...</x:String>
968968
<x:String x:Key="Text.TagCM.Merge" xml:space="preserve">合并 ${0}$ 到 ${1}$...</x:String>
969969
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
970+
<x:String x:Key="Text.Terminate" xml:space="preserve">终止运行</x:String>
970971
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模块</x:String>
971972
<x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">更新所有子模块</x:String>
972973
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">如未初始化子模块,先初始化</x:String>

src/Resources/Locales/zh_TW.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@
967967
<x:String x:Key="Text.TagCM.DeleteMultiple" xml:space="preserve">刪除所選的 {0} 個標籤...</x:String>
968968
<x:String x:Key="Text.TagCM.Merge" xml:space="preserve">合併 ${0}$ 到 ${1}$...</x:String>
969969
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
970+
<x:String x:Key="Text.Terminate" xml:space="preserve">終止執行</x:String>
970971
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模組</x:String>
971972
<x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">更新所有子模組</x:String>
972973
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">如果子模組尚未初始化,則將其初始化</x:String>

src/ViewModels/Clone.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.ComponentModel.DataAnnotations;
44
using System.IO;
5+
using System.Threading;
56
using System.Threading.Tasks;
67

78
namespace SourceGit.ViewModels
@@ -78,6 +79,7 @@ public bool InitAndUpdateSubmodules
7879
public Clone(string pageId)
7980
{
8081
_pageId = pageId;
82+
CanTerminate = true;
8183

8284
Groups = new List<RepositoryNode>();
8385
Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty });
@@ -111,7 +113,11 @@ public override async Task<bool> Sure()
111113
var log = new CommandLog("Clone");
112114
Use(log);
113115

116+
_cancellation = new CancellationTokenSource();
117+
var token = _cancellation.Token;
118+
114119
var succ = await new Commands.Clone(_pageId, _parentFolder, _remote, _local, _useSSH ? _sshKey : "", _extraArgs)
120+
.WithCancellation(token)
115121
.Use(log)
116122
.ExecAsync();
117123
if (!succ)
@@ -146,11 +152,12 @@ public override async Task<bool> Sure()
146152
.SetAsync("remote.origin.sshkey", _sshKey);
147153
}
148154

149-
if (InitAndUpdateSubmodules)
155+
if (InitAndUpdateSubmodules && !token.IsCancellationRequested)
150156
{
151157
var submodules = await new Commands.QueryUpdatableSubmodules(path, true).GetResultAsync();
152158
if (submodules.Count > 0)
153159
await new Commands.Submodule(path)
160+
.WithCancellation(token)
154161
.Use(log)
155162
.UpdateAsync(submodules, true, true, false);
156163
}
@@ -175,9 +182,16 @@ public override async Task<bool> Sure()
175182

176183
Welcome.Instance.Refresh();
177184
launcher.OpenRepositoryInTab(node, page);
185+
186+
_cancellation = null;
178187
return true;
179188
}
180189

190+
public override void Terminate()
191+
{
192+
_cancellation?.Cancel();
193+
}
194+
181195
private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> collections)
182196
{
183197
foreach (var node in collections)
@@ -199,5 +213,6 @@ private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> colle
199213
private string _extraArgs = string.Empty;
200214
private RepositoryNode _selectedGroup = null;
201215
private int _bookmark = 0;
216+
private CancellationTokenSource _cancellation = null;
202217
}
203218
}

src/ViewModels/Fetch.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Threading;
23
using System.Threading.Tasks;
34

45
namespace SourceGit.ViewModels
@@ -48,6 +49,7 @@ public Fetch(Repository repo, Models.Remote preferredRemote = null)
4849
_repo = repo;
4950
IsFetchAllRemoteVisible = repo.Remotes.Count > 1 && preferredRemote == null;
5051
_fetchAllRemotes = IsFetchAllRemoteVisible && _repo.UIStates.FetchAllRemotes;
52+
CanTerminate = true;
5153

5254
if (preferredRemote != null)
5355
{
@@ -77,23 +79,33 @@ public override async Task<bool> Sure()
7779
var log = _repo.CreateLog("Fetch");
7880
Use(log);
7981

82+
_cancellation = new CancellationTokenSource();
83+
var token = _cancellation.Token;
84+
8085
if (FetchAllRemotes)
8186
{
8287
foreach (var remote in _repo.Remotes)
88+
{
8389
await new Commands.Fetch(_repo.FullPath, remote.Name, notags, force)
90+
.WithCancellation(token)
8491
.Use(log)
8592
.RunAsync();
93+
94+
if (token.IsCancellationRequested)
95+
break;
96+
}
8697
}
8798
else
8899
{
89100
await new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, notags, force)
101+
.WithCancellation(token)
90102
.Use(log)
91103
.RunAsync();
92104
}
93105

94106
log.Complete();
95107

96-
if (navigateToUpstreamHEAD)
108+
if (navigateToUpstreamHEAD && !token.IsCancellationRequested)
97109
{
98110
var upstream = _repo.CurrentBranch?.Upstream;
99111
if (!string.IsNullOrEmpty(upstream))
@@ -103,11 +115,20 @@ public override async Task<bool> Sure()
103115
}
104116
}
105117

106-
_repo.MarkFetched();
118+
if (!token.IsCancellationRequested)
119+
_repo.MarkFetched();
120+
121+
_cancellation = null;
107122
return true;
108123
}
109124

125+
public override void Terminate()
126+
{
127+
_cancellation?.Cancel();
128+
}
129+
110130
private readonly Repository _repo = null;
111131
private bool _fetchAllRemotes = false;
132+
private CancellationTokenSource _cancellation = null;
112133
}
113134
}

0 commit comments

Comments
 (0)