Skip to content

Commit 9a82d04

Browse files
committed
Merge branch 'release/v8.18'
2 parents 5a1827f + bcd2a96 commit 9a82d04

File tree

102 files changed

+3227
-917
lines changed

Some content is hidden

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

102 files changed

+3227
-917
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
- name: Publish
4646
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-x64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained
4747
- name: Packing Program
48-
run: tar -cvf sourcegit.osx-x64.tar publish/
48+
run: tar -cvf sourcegit.osx-x64.tar -C publish/ .
4949
- name: Upload Artifact
5050
uses: actions/upload-artifact@v4
5151
with:
@@ -68,7 +68,7 @@ jobs:
6868
- name: Publish
6969
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-arm64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained
7070
- name: Packing Program
71-
run: tar -cvf sourcegit.osx-arm64.tar publish/
71+
run: tar -cvf sourcegit.osx-arm64.tar -C publish/ .
7272
- name: Upload Artifact
7373
uses: actions/upload-artifact@v4
7474
with:
@@ -93,7 +93,7 @@ jobs:
9393
- name: Rename Executable File
9494
run: mv publish/SourceGit publish/sourcegit
9595
- name: Packing Program
96-
run: tar -cvf sourcegit.linux-x64.tar publish/
96+
run: tar -cvf sourcegit.linux-x64.tar -C publish/ .
9797
- name: Upload Artifact
9898
uses: actions/upload-artifact@v4
9999
with:

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Opensource Git GUI client.
1414
* Supports SSH access with each remote
1515
* GIT commands with GUI
1616
* Clone/Fetch/Pull/Push...
17+
* Merge/Rebase/Reset/Revert/Amend/Cherry-pick...
18+
* Interactive rebase (Basic)
1719
* Branches
1820
* Remotes
1921
* Tags
@@ -26,10 +28,11 @@ Opensource Git GUI client.
2628
* Blame
2729
* Revision Diffs
2830
* Branch Diff
29-
* Image Diff
31+
* Image Diff - Side-By-Side/Swipe/Blend
3032
* GitFlow support
33+
* Git LFS support
3134

32-
> **Linux** only tested on **Ubuntu 22.04** on **X11**.
35+
> **Linux** only tested on **Debian 12** on both **X11** & **Wayland**.
3336
3437
## How to Use
3538

@@ -110,8 +113,6 @@ This app supports open repository in external tools listed in the table below.
110113
| Color.Border2 | Border color used in visual lines, like seperators, Rectange, etc. |
111114
| Color.FlatButton.Background | Flat button background color, like `Cancel`, `Commit & Push` button |
112115
| Color.FlatButton.BackgroundHovered | Flat button background color when hovered, like `Cancel` button |
113-
| Color.FlatButton.PrimaryBackground | Primary flat button background color, like `Ok`, `Commit` button |
114-
| Color.FlatButton.PrimaryBackgroundHovered | Primary flat button background color when hovered, like `Ok`, `Commit` button |
115116
| Color.FG1 | Primary foreground color for all text elements |
116117
| Color.FG2 | Secondary foreground color for all text elements |
117118
| Color.Diff.EmptyBG | Background color used in empty lines in diff viewer |

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8.17
1+
8.18

src/App.JsonCodeGen.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace SourceGit
66
[JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)]
77
[JsonSerializable(typeof(Models.Version))]
88
[JsonSerializable(typeof(Models.JetBrainsState))]
9+
[JsonSerializable(typeof(List<Models.InteractiveRebaseJob>))]
910
[JsonSerializable(typeof(Dictionary<string, string>))]
1011
[JsonSerializable(typeof(ViewModels.Preference))]
1112
internal partial class JsonCodeGen : JsonSerializerContext { }

src/App.axaml.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,15 @@ public static void Main(string[] args)
4545
{
4646
try
4747
{
48-
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
48+
if (args.Length > 1 && args[0].Equals("--rebase-editor", StringComparison.Ordinal))
49+
Environment.Exit(Models.InteractiveRebaseEditor.Process(args[1]));
50+
else
51+
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
4952
}
5053
catch (Exception ex)
5154
{
5255
var builder = new StringBuilder();
53-
builder.Append("Crash: ");
54-
builder.Append(ex.Message);
55-
builder.Append("\n\n");
56+
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
5657
builder.Append("----------------------------\n");
5758
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
5859
builder.Append($"OS: {Environment.OSVersion.ToString()}\n");
@@ -191,6 +192,10 @@ public static string Text(string key, params object[] args)
191192
var fmt = Current.FindResource($"Text.{key}") as string;
192193
if (string.IsNullOrWhiteSpace(fmt))
193194
return $"Text.{key}";
195+
196+
if (args == null || args.Length == 0)
197+
return fmt;
198+
194199
return string.Format(fmt, args);
195200
}
196201

@@ -313,6 +318,18 @@ private static void ShowSelfUpdateResult(object data)
313318
});
314319
}
315320

321+
public static async Task<string> GetClipboardTextAsync()
322+
{
323+
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
324+
{
325+
if (desktop.MainWindow.Clipboard is { } clipboard)
326+
{
327+
return await clipboard.GetTextAsync();
328+
}
329+
}
330+
return default;
331+
}
332+
316333
private ResourceDictionary _activeLocale = null;
317334
private ResourceDictionary _colorOverrides = null;
318335
private Models.INotificationReceiver _notificationReceiver = null;

src/Commands/Command.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ public bool Exec()
4343

4444
// Force using en_US.UTF-8 locale to avoid GCM crash
4545
if (OperatingSystem.IsLinux())
46-
{
4746
start.Environment.Add("LANG", "en_US.UTF-8");
48-
}
4947

5048
if (!string.IsNullOrEmpty(WorkingDirectory))
5149
start.WorkingDirectory = WorkingDirectory;

src/Commands/Config.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ public Config(string repository)
1414

1515
public Dictionary<string, string> ListAll()
1616
{
17-
Args = "config -l";
17+
if (string.IsNullOrEmpty(WorkingDirectory))
18+
Args = "config --global -l";
19+
else
20+
Args = "config -l";
1821

1922
var output = ReadToEnd();
2023
var rs = new Dictionary<string, string>();

src/Commands/GitIgnore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ public static void Add(string repo, string pattern)
88
{
99
var file = Path.Combine(repo, ".gitignore");
1010
if (!File.Exists(file))
11-
File.WriteAllLines(file, [ pattern ]);
11+
File.WriteAllLines(file, [pattern]);
1212
else
13-
File.AppendAllLines(file, [ pattern ]);
13+
File.AppendAllLines(file, [pattern]);
1414
}
1515
}
1616
}

src/Commands/LFS.cs

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Text.RegularExpressions;
35

46
namespace SourceGit.Commands
57
{
6-
public class LFS
8+
public partial class LFS
79
{
8-
class PruneCmd : Command
10+
[GeneratedRegex(@"^(.+)\s+(\w+)\s+\w+:(\d+)$")]
11+
private static partial Regex REG_LOCK();
12+
13+
class SubCmd : Command
914
{
10-
public PruneCmd(string repo, Action<string> onProgress)
15+
public SubCmd(string repo, string args, Action<string> onProgress)
1116
{
1217
WorkingDirectory = repo;
1318
Context = repo;
14-
Args = "lfs prune";
19+
Args = args;
1520
TraitErrorAsOutput = true;
1621
_outputHandler = onProgress;
1722
}
@@ -39,9 +44,73 @@ public bool IsEnabled()
3944
return content.Contains("git lfs pre-push");
4045
}
4146

47+
public bool Install()
48+
{
49+
return new SubCmd(_repo, $"lfs install", null).Exec();
50+
}
51+
52+
public bool Track(string pattern, bool isFilenameMode = false)
53+
{
54+
var opt = isFilenameMode ? "--filename" : "";
55+
return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", null).Exec();
56+
}
57+
58+
public void Fetch(Action<string> outputHandler)
59+
{
60+
new SubCmd(_repo, $"lfs fetch", outputHandler).Exec();
61+
}
62+
63+
public void Pull(Action<string> outputHandler)
64+
{
65+
new SubCmd(_repo, $"lfs pull", outputHandler).Exec();
66+
}
67+
4268
public void Prune(Action<string> outputHandler)
4369
{
44-
new PruneCmd(_repo, outputHandler).Exec();
70+
new SubCmd(_repo, "lfs prune", outputHandler).Exec();
71+
}
72+
73+
public List<Models.LFSLock> Locks()
74+
{
75+
var locks = new List<Models.LFSLock>();
76+
var cmd = new SubCmd(_repo, "lfs locks", null);
77+
var rs = cmd.ReadToEnd();
78+
if (rs.IsSuccess)
79+
{
80+
var lines = rs.StdOut.Split(new char[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
81+
foreach (var line in lines)
82+
{
83+
var match = REG_LOCK().Match(line);
84+
if (match.Success)
85+
{
86+
locks.Add(new Models.LFSLock()
87+
{
88+
File = match.Groups[1].Value,
89+
User = match.Groups[2].Value,
90+
ID = long.Parse(match.Groups[3].Value),
91+
});
92+
}
93+
}
94+
}
95+
96+
return locks;
97+
}
98+
99+
public bool Lock(string file)
100+
{
101+
return new SubCmd(_repo, $"lfs lock \"{file}\"", null).Exec();
102+
}
103+
104+
public bool Unlock(string file, bool force)
105+
{
106+
var opt = force ? "-f" : "";
107+
return new SubCmd(_repo, $"lfs unlock {opt} \"{file}\"", null).Exec();
108+
}
109+
110+
public bool Unlock(long id, bool force)
111+
{
112+
var opt = force ? "-f" : "";
113+
return new SubCmd(_repo, $"lfs unlock {opt} --id={id}", null).Exec();
45114
}
46115

47116
private readonly string _repo;

src/Commands/MergeTool.cs

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,63 @@ namespace SourceGit.Commands
66
{
77
public static class MergeTool
88
{
9-
public static bool OpenForMerge(string repo, string tool, string mergeCmd, string file)
9+
public static bool OpenForMerge(string repo, int toolType, string toolPath, string file)
1010
{
11-
if (string.IsNullOrWhiteSpace(tool) || string.IsNullOrWhiteSpace(mergeCmd))
11+
var cmd = new Command();
12+
cmd.WorkingDirectory = repo;
13+
cmd.Context = repo;
14+
cmd.RaiseError = true;
15+
16+
if (toolType == 0)
17+
{
18+
cmd.Args = $"mergetool \"{file}\"";
19+
return cmd.Exec();
20+
}
21+
22+
if (!File.Exists(toolPath))
1223
{
13-
Dispatcher.UIThread.Invoke(() =>
14-
{
15-
App.RaiseException(repo, "Invalid external merge tool settings!");
16-
});
24+
Dispatcher.UIThread.Post(() => App.RaiseException(repo, $"Can NOT found external merge tool in '{toolPath}'!"));
1725
return false;
1826
}
1927

20-
if (!File.Exists(tool))
28+
var supported = Models.ExternalMerger.Supported.Find(x => x.Type == toolType);
29+
if (supported == null)
2130
{
22-
Dispatcher.UIThread.Invoke(() =>
23-
{
24-
App.RaiseException(repo, $"Can NOT found external merge tool in '{tool}'!");
25-
});
31+
Dispatcher.UIThread.Post(() => App.RaiseException(repo, "Invalid merge tool in preference setting!"));
2632
return false;
2733
}
2834

29-
var cmd = new Command();
30-
cmd.WorkingDirectory = repo;
31-
cmd.RaiseError = false;
32-
cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{tool}\\\" {mergeCmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit \"{file}\"";
35+
cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.Cmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit \"{file}\"";
3336
return cmd.Exec();
3437
}
3538

36-
public static bool OpenForDiff(string repo, string tool, string diffCmd, Models.DiffOption option)
39+
public static bool OpenForDiff(string repo, int toolType, string toolPath, Models.DiffOption option)
3740
{
38-
if (string.IsNullOrWhiteSpace(tool) || string.IsNullOrWhiteSpace(diffCmd))
41+
var cmd = new Command();
42+
cmd.WorkingDirectory = repo;
43+
cmd.Context = repo;
44+
cmd.RaiseError = true;
45+
46+
if (toolType == 0)
47+
{
48+
cmd.Args = $"difftool -g --no-prompt {option}";
49+
return cmd.Exec();
50+
}
51+
52+
if (!File.Exists(toolPath))
3953
{
40-
Dispatcher.UIThread.Invoke(() =>
41-
{
42-
App.RaiseException(repo, "Invalid external merge tool settings!");
43-
});
54+
Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, $"Can NOT found external diff tool in '{toolPath}'!"));
4455
return false;
4556
}
4657

47-
if (!File.Exists(tool))
58+
var supported = Models.ExternalMerger.Supported.Find(x => x.Type == toolType);
59+
if (supported == null)
4860
{
49-
Dispatcher.UIThread.Invoke(() =>
50-
{
51-
App.RaiseException(repo, $"Can NOT found external merge tool in '{tool}'!");
52-
});
61+
Dispatcher.UIThread.Post(() => App.RaiseException(repo, "Invalid merge tool in preference setting!"));
5362
return false;
5463
}
5564

56-
var cmd = new Command();
57-
cmd.WorkingDirectory = repo;
58-
cmd.RaiseError = false;
59-
cmd.Args = $"-c difftool.sourcegit.cmd=\"\\\"{tool}\\\" {diffCmd}\" difftool --tool=sourcegit --no-prompt {option}";
65+
cmd.Args = $"-c difftool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.DiffCmd}\" difftool --tool=sourcegit --no-prompt {option}";
6066
return cmd.Exec();
6167
}
6268
}

0 commit comments

Comments
 (0)