Skip to content

Commit 6fd6bbb

Browse files
committed
Merge branch 'release/v2025.09'
2 parents e3cc987 + 34f8618 commit 6fd6bbb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1578
-1469
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
4848
## Translation Status
4949

50-
[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.08%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.68%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-99.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.41%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md)
50+
[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.07%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.66%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-99.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.39%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md)
5151

5252
> [!NOTE]
5353
> You can find the missing keys in [TRANSLATION.md](TRANSLATION.md)
@@ -79,7 +79,7 @@ For **Windows** users:
7979
```
8080
> [!NOTE]
8181
> `winget` will install this software as a commandline tool. You need run `SourceGit` from console or `Win+R` at the first time. Then you can add it to the taskbar.
82-
* You can install the latest stable by `scoope` with follow commands:
82+
* You can install the latest stable by `scoop` with follow commands:
8383
```shell
8484
scoop bucket add extras
8585
scoop install sourcegit

TRANSLATION.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### de_DE.axaml: 99.08%
1+
### de_DE.axaml: 99.07%
22

33

44
<details>
@@ -24,7 +24,7 @@
2424

2525
</details>
2626

27-
### fr_FR.axaml: 91.68%
27+
### fr_FR.axaml: 91.66%
2828

2929

3030
<details>
@@ -106,7 +106,7 @@
106106

107107
</details>
108108

109-
### pt_BR.axaml: 91.41%
109+
### pt_BR.axaml: 91.39%
110110

111111

112112
<details>

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2025.08
1+
2025.09

build/scripts/package.windows.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cd build
1010
rm -rf SourceGit/*.pdb
1111

1212
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
13-
powershell -Command "Compress-Archive -Path SourceGit\\* -DestinationPath \"sourcegit_$VERSION.$RUNTIME.zip\" -Force"
13+
powershell -Command "Compress-Archive -Path SourceGit -DestinationPath \"sourcegit_$VERSION.$RUNTIME.zip\" -Force"
1414
else
15-
zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit
15+
zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit
1616
fi

src/App.axaml.cs

+46-25
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,31 @@ public static AppBuilder BuildAvaloniaApp()
7777
Native.OS.SetupApp(builder);
7878
return builder;
7979
}
80+
81+
private static void LogException(Exception ex)
82+
{
83+
if (ex == null)
84+
return;
85+
86+
var builder = new StringBuilder();
87+
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
88+
builder.Append("----------------------------\n");
89+
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
90+
builder.Append($"OS: {Environment.OSVersion}\n");
91+
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
92+
builder.Append($"Source: {ex.Source}\n");
93+
builder.Append($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}\n");
94+
builder.Append($"User: {Environment.UserName}\n");
95+
builder.Append($"App Start Time: {Process.GetCurrentProcess().StartTime}\n");
96+
builder.Append($"Exception Time: {DateTime.Now}\n");
97+
builder.Append($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB\n");
98+
builder.Append($"---------------------------\n\n");
99+
builder.Append(ex);
100+
101+
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
102+
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
103+
File.WriteAllText(file, builder.ToString());
104+
}
80105
#endregion
81106

82107
#region Utility Functions
@@ -181,6 +206,9 @@ public static void SetFonts(string defaultFont, string monospaceFont, bool onlyU
181206
app._fontsOverrides = null;
182207
}
183208

209+
defaultFont = app.FixFontFamilyName(defaultFont);
210+
monospaceFont = app.FixFontFamilyName(monospaceFont);
211+
184212
var resDic = new ResourceDictionary();
185213
if (!string.IsNullOrEmpty(defaultFont))
186214
resDic.Add("Fonts.Default", new FontFamily(defaultFont));
@@ -325,31 +353,6 @@ public override void OnFrameworkInitializationCompleted()
325353
}
326354
#endregion
327355

328-
private static void LogException(Exception ex)
329-
{
330-
if (ex == null)
331-
return;
332-
333-
var builder = new StringBuilder();
334-
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
335-
builder.Append("----------------------------\n");
336-
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
337-
builder.Append($"OS: {Environment.OSVersion}\n");
338-
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
339-
builder.Append($"Source: {ex.Source}\n");
340-
builder.Append($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}\n");
341-
builder.Append($"User: {Environment.UserName}\n");
342-
builder.Append($"App Start Time: {Process.GetCurrentProcess().StartTime}\n");
343-
builder.Append($"Exception Time: {DateTime.Now}\n");
344-
builder.Append($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB\n");
345-
builder.Append($"---------------------------\n\n");
346-
builder.Append(ex);
347-
348-
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
349-
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
350-
File.WriteAllText(file, builder.ToString());
351-
}
352-
353356
private static bool TryLaunchAsRebaseTodoEditor(string[] args, out int exitCode)
354357
{
355358
exitCode = -1;
@@ -546,6 +549,24 @@ private void ShowSelfUpdateResult(object data)
546549
});
547550
}
548551

552+
private string FixFontFamilyName(string input)
553+
{
554+
if (string.IsNullOrEmpty(input))
555+
return string.Empty;
556+
557+
var parts = input.Split(',');
558+
var trimmed = new List<string>();
559+
560+
foreach (var part in parts)
561+
{
562+
var t = part.Trim();
563+
if (!string.IsNullOrEmpty(t))
564+
trimmed.Add(t);
565+
}
566+
567+
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
568+
}
569+
549570
private ViewModels.Launcher _launcher = null;
550571
private ResourceDictionary _activeLocale = null;
551572
private ResourceDictionary _themeOverrides = null;

src/Commands/Branch.cs

+5-12
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,14 @@ public static bool DeleteLocal(string repo, string name)
5454

5555
public static bool DeleteRemote(string repo, string remote, string name)
5656
{
57-
var cmd = new Command();
58-
cmd.WorkingDirectory = repo;
59-
cmd.Context = repo;
60-
6157
bool exists = new Remote(repo).HasBranch(remote, name);
6258
if (exists)
63-
{
64-
cmd.SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
65-
cmd.Args = $"push {remote} --delete {name}";
66-
}
67-
else
68-
{
69-
cmd.Args = $"branch -D -r {remote}/{name}";
70-
}
59+
return new Push(repo, remote, $"refs/heads/{name}", true).Exec();
7160

61+
var cmd = new Command();
62+
cmd.WorkingDirectory = repo;
63+
cmd.Context = repo;
64+
cmd.Args = $"branch -D -r {remote}/{name}";
7265
return cmd.Exec();
7366
}
7467
}

src/Commands/Command.cs

+32-34
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@
33
using System.Diagnostics;
44
using System.Text;
55
using System.Text.RegularExpressions;
6+
using System.Threading;
67

78
using Avalonia.Threading;
89

910
namespace SourceGit.Commands
1011
{
1112
public partial class Command
1213
{
13-
public class CancelToken
14-
{
15-
public bool Requested { get; set; } = false;
16-
}
17-
1814
public class ReadToEndResult
1915
{
2016
public bool IsSuccess { get; set; } = false;
@@ -30,7 +26,7 @@ public enum EditorType
3026
}
3127

3228
public string Context { get; set; } = string.Empty;
33-
public CancelToken Cancel { get; set; } = null;
29+
public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
3430
public string WorkingDirectory { get; set; } = null;
3531
public EditorType Editor { get; set; } = EditorType.CoreEditor; // Only used in Exec() mode
3632
public string SSHKey { get; set; } = string.Empty;
@@ -43,36 +39,15 @@ public bool Exec()
4339
var start = CreateGitStartInfo();
4440
var errs = new List<string>();
4541
var proc = new Process() { StartInfo = start };
46-
var isCancelled = false;
4742

4843
proc.OutputDataReceived += (_, e) =>
4944
{
50-
if (Cancel != null && Cancel.Requested)
51-
{
52-
isCancelled = true;
53-
proc.CancelErrorRead();
54-
proc.CancelOutputRead();
55-
if (!proc.HasExited)
56-
proc.Kill(true);
57-
return;
58-
}
59-
6045
if (e.Data != null)
6146
OnReadline(e.Data);
6247
};
6348

6449
proc.ErrorDataReceived += (_, e) =>
6550
{
66-
if (Cancel != null && Cancel.Requested)
67-
{
68-
isCancelled = true;
69-
proc.CancelErrorRead();
70-
proc.CancelOutputRead();
71-
if (!proc.HasExited)
72-
proc.Kill(true);
73-
return;
74-
}
75-
7651
if (string.IsNullOrEmpty(e.Data))
7752
{
7853
errs.Add(string.Empty);
@@ -97,9 +72,25 @@ public bool Exec()
9772
errs.Add(e.Data);
9873
};
9974

75+
var dummy = null as Process;
76+
var dummyProcLock = new object();
10077
try
10178
{
10279
proc.Start();
80+
81+
// It not safe, please only use `CancellationToken` in readonly commands.
82+
if (CancellationToken.CanBeCanceled)
83+
{
84+
dummy = proc;
85+
CancellationToken.Register(() =>
86+
{
87+
lock (dummyProcLock)
88+
{
89+
if (dummy is { HasExited: false })
90+
dummy.Kill();
91+
}
92+
});
93+
}
10394
}
10495
catch (Exception e)
10596
{
@@ -113,10 +104,18 @@ public bool Exec()
113104
proc.BeginErrorReadLine();
114105
proc.WaitForExit();
115106

107+
if (dummy != null)
108+
{
109+
lock (dummyProcLock)
110+
{
111+
dummy = null;
112+
}
113+
}
114+
116115
int exitCode = proc.ExitCode;
117116
proc.Close();
118117

119-
if (!isCancelled && exitCode != 0)
118+
if (!CancellationToken.IsCancellationRequested && exitCode != 0)
120119
{
121120
if (RaiseError)
122121
{
@@ -192,13 +191,12 @@ private ProcessStartInfo CreateGitStartInfo()
192191
if (!start.Environment.ContainsKey("GIT_SSH_COMMAND") && !string.IsNullOrEmpty(SSHKey))
193192
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -i '{SSHKey}'");
194193

195-
// Force using en_US.UTF-8 locale to avoid GCM crash
194+
// Force using en_US.UTF-8 locale
196195
if (OperatingSystem.IsLinux())
197-
start.Environment.Add("LANG", "en_US.UTF-8");
198-
199-
// Fix macOS `PATH` env
200-
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
201-
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
196+
{
197+
start.Environment.Add("LANG", "C");
198+
start.Environment.Add("LC_ALL", "C");
199+
}
202200

203201
// Force using this app as git editor.
204202
switch (Editor)

src/Commands/CountLocalChangesWithoutUntracked.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public CountLocalChangesWithoutUntracked(string repo)
88
{
99
WorkingDirectory = repo;
1010
Context = repo;
11-
Args = "status -uno --ignore-submodules=dirty --porcelain";
11+
Args = "--no-optional-locks status -uno --ignore-submodules=dirty --porcelain";
1212
}
1313

1414
public int Result()

src/Commands/ExecuteCustomAction.cs

-16
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ public static void Run(string repo, string file, string args)
1717
start.CreateNoWindow = true;
1818
start.WorkingDirectory = repo;
1919

20-
// Force using en_US.UTF-8 locale to avoid GCM crash
21-
if (OperatingSystem.IsLinux())
22-
start.Environment.Add("LANG", "en_US.UTF-8");
23-
24-
// Fix macOS `PATH` env
25-
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
26-
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
27-
2820
try
2921
{
3022
Process.Start(start);
@@ -48,14 +40,6 @@ public static void RunAndWait(string repo, string file, string args, Action<stri
4840
start.StandardErrorEncoding = Encoding.UTF8;
4941
start.WorkingDirectory = repo;
5042

51-
// Force using en_US.UTF-8 locale to avoid GCM crash
52-
if (OperatingSystem.IsLinux())
53-
start.Environment.Add("LANG", "en_US.UTF-8");
54-
55-
// Fix macOS `PATH` env
56-
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
57-
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
58-
5943
var proc = new Process() { StartInfo = start };
6044
var builder = new StringBuilder();
6145

src/Commands/Push.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public Push(string repo, string local, string remote, string remoteBranch, bool
2626
Args += $"{remote} {local}:{remoteBranch}";
2727
}
2828

29-
public Push(string repo, string remote, string tag, bool isDelete)
29+
public Push(string repo, string remote, string refname, bool isDelete)
3030
{
3131
WorkingDirectory = repo;
3232
Context = repo;
@@ -36,7 +36,7 @@ public Push(string repo, string remote, string tag, bool isDelete)
3636
if (isDelete)
3737
Args += "--delete ";
3838

39-
Args += $"{remote} refs/tags/{tag}";
39+
Args += $"{remote} {refname}";
4040
}
4141

4242
protected override void OnReadline(string line)

src/Commands/QueryCommitChildren.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public QueryCommitChildren(string repo, string commit, int max)
99
WorkingDirectory = repo;
1010
Context = repo;
1111
_commit = commit;
12-
Args = $"rev-list -{max} --parents --branches --remotes ^{commit}";
12+
Args = $"rev-list -{max} --parents --branches --remotes --ancestry-path={commit} ^{commit}";
1313
}
1414

1515
public List<string> Result()

src/Commands/QueryLocalChanges.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public QueryLocalChanges(string repo, bool includeUntracked = true)
1313
{
1414
WorkingDirectory = repo;
1515
Context = repo;
16-
Args = $"status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain";
16+
Args = $"--no-optional-locks status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain";
1717
}
1818

1919
public List<Models.Change> Result()

0 commit comments

Comments
 (0)