Skip to content

Commit 2e7d742

Browse files
committed
Merge branch 'release/v8.25'
2 parents 36e035d + 733045c commit 2e7d742

Some content is hidden

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

72 files changed

+2085
-773
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Opensource Git GUI client.
1515
* GIT commands with GUI
1616
* Clone/Fetch/Pull/Push...
1717
* Merge/Rebase/Reset/Revert/Amend/Cherry-pick...
18+
* Amend/Reword
1819
* Interactive rebase (Basic)
1920
* Branches
2021
* Remotes
@@ -30,8 +31,9 @@ Opensource Git GUI client.
3031
* Revision Diffs
3132
* Branch Diff
3233
* Image Diff - Side-By-Side/Swipe/Blend
33-
* GitFlow support
34-
* Git LFS support
34+
* GitFlow
35+
* Git LFS
36+
* Issue Link
3537

3638
> **Linux** only tested on **Debian 12** on both **X11** & **Wayland**.
3739

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8.24
1+
8.25
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace SourceGit.Commands
4+
{
5+
public class CountLocalChangesWithoutUntracked : Command
6+
{
7+
public CountLocalChangesWithoutUntracked(string repo)
8+
{
9+
WorkingDirectory = repo;
10+
Context = repo;
11+
Args = "status -uno --ignore-submodules=dirty --porcelain";
12+
}
13+
14+
public int Result()
15+
{
16+
var rs = ReadToEnd();
17+
if (rs.IsSuccess)
18+
{
19+
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
20+
return lines.Length;
21+
}
22+
23+
return 0;
24+
}
25+
}
26+
}

src/Commands/QueryBranches.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ public class QueryBranches : Command
77
{
88
private const string PREFIX_LOCAL = "refs/heads/";
99
private const string PREFIX_REMOTE = "refs/remotes/";
10-
private const string PREFIX_DETACHED = "(HEAD detached at";
10+
private const string PREFIX_DETACHED_AT = "(HEAD detached at";
11+
private const string PREFIX_DETACHED_FROM = "(HEAD detached from";
1112

1213
public QueryBranches(string repo)
1314
{
@@ -37,9 +38,9 @@ protected override void OnReadline(string line)
3738
if (refName.EndsWith("/HEAD", StringComparison.Ordinal))
3839
return;
3940

40-
if (refName.StartsWith(PREFIX_DETACHED, StringComparison.Ordinal))
41+
if (refName.StartsWith(PREFIX_DETACHED_AT, StringComparison.Ordinal) || refName.StartsWith(PREFIX_DETACHED_FROM, StringComparison.Ordinal))
4142
{
42-
branch.IsHead = true;
43+
branch.IsDetachedHead = true;
4344
}
4445

4546
if (refName.StartsWith(PREFIX_LOCAL, StringComparison.Ordinal))

src/Commands/QuerySubmodules.cs

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

45
namespace SourceGit.Commands
@@ -9,6 +10,8 @@ public partial class QuerySubmodules : Command
910
private static partial Regex REG_FORMAT1();
1011
[GeneratedRegex(@"^[\-\+ ][0-9a-f]+\s(.*)$")]
1112
private static partial Regex REG_FORMAT2();
13+
[GeneratedRegex(@"^\s?[\w\?]{1,4}\s+(.+)$")]
14+
private static partial Regex REG_FORMAT_STATUS();
1215

1316
public QuerySubmodules(string repo)
1417
{
@@ -17,28 +20,59 @@ public QuerySubmodules(string repo)
1720
Args = "submodule status";
1821
}
1922

20-
public List<string> Result()
23+
public List<Models.Submodule> Result()
2124
{
22-
Exec();
23-
return _submodules;
24-
}
25+
var submodules = new List<Models.Submodule>();
26+
var rs = ReadToEnd();
27+
if (!rs.IsSuccess)
28+
return submodules;
2529

26-
protected override void OnReadline(string line)
27-
{
28-
var match = REG_FORMAT1().Match(line);
29-
if (match.Success)
30+
var builder = new StringBuilder();
31+
var lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
32+
foreach (var line in lines)
3033
{
31-
_submodules.Add(match.Groups[1].Value);
32-
return;
34+
var match = REG_FORMAT1().Match(line);
35+
if (match.Success)
36+
{
37+
var path = match.Groups[1].Value;
38+
builder.Append($"\"{path}\" ");
39+
submodules.Add(new Models.Submodule() { Path = path });
40+
continue;
41+
}
42+
43+
match = REG_FORMAT2().Match(line);
44+
if (match.Success)
45+
{
46+
var path = match.Groups[1].Value;
47+
builder.Append($"\"{path}\" ");
48+
submodules.Add(new Models.Submodule() { Path = path });
49+
}
3350
}
3451

35-
match = REG_FORMAT2().Match(line);
36-
if (match.Success)
52+
if (submodules.Count > 0)
3753
{
38-
_submodules.Add(match.Groups[1].Value);
54+
Args = $"status -uno --porcelain -- {builder}";
55+
rs = ReadToEnd();
56+
if (!rs.IsSuccess)
57+
return submodules;
58+
59+
var dirty = new HashSet<string>();
60+
lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
61+
foreach (var line in lines)
62+
{
63+
var match = REG_FORMAT_STATUS().Match(line);
64+
if (match.Success)
65+
{
66+
var path = match.Groups[1].Value;
67+
dirty.Add(path);
68+
}
69+
}
70+
71+
foreach (var submodule in submodules)
72+
submodule.IsDirty = dirty.Contains(submodule.Path);
3973
}
40-
}
4174

42-
private readonly List<string> _submodules = new List<string>();
75+
return submodules;
76+
}
4377
}
4478
}

src/Models/AvatarManager.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ static AvatarManager()
3434
if (!Directory.Exists(_storePath))
3535
Directory.CreateDirectory(_storePath);
3636

37+
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/github.png", UriKind.RelativeOrAbsolute));
38+
_resources.Add("[email protected]", new Bitmap(icon));
39+
3740
Task.Run(() =>
3841
{
3942
while (true)
@@ -117,19 +120,11 @@ public static void Unsubscribe(IAvatarHost host)
117120

118121
public static Bitmap Request(string email, bool forceRefetch)
119122
{
120-
if (email.Equals("[email protected]", StringComparison.Ordinal))
121-
{
122-
if (_githubEmailAvatar == null)
123-
{
124-
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/github.png", UriKind.RelativeOrAbsolute));
125-
_githubEmailAvatar = new Bitmap(icon);
126-
}
127-
128-
return _githubEmailAvatar;
129-
}
130-
131123
if (forceRefetch)
132124
{
125+
if (email.Equals("[email protected]", StringComparison.Ordinal))
126+
return null;
127+
133128
if (_resources.ContainsKey(email))
134129
_resources.Remove(email);
135130

@@ -198,6 +193,5 @@ private static void NotifyResourceChanged(string email)
198193

199194
[GeneratedRegex(@"^(?:(\d+)\+)?(.+?)@users\.noreply\.github\.com$")]
200195
private static partial Regex REG_GITHUB_USER_EMAIL();
201-
private static Bitmap _githubEmailAvatar = null;
202196
}
203197
}

src/Models/Branch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ public class Branch
2828
public string Head { get; set; }
2929
public bool IsLocal { get; set; }
3030
public bool IsCurrent { get; set; }
31+
public bool IsDetachedHead { get; set; }
3132
public string Upstream { get; set; }
3233
public BranchTrackStatus TrackStatus { get; set; }
3334
public string Remote { get; set; }
34-
public bool IsHead { get; set; }
3535

3636
public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}";
3737
}

src/Models/Commit.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ public static double OpacityForNotMerged
2525
public bool HasDecorators => Decorators.Count > 0;
2626

2727
public bool IsMerged { get; set; } = false;
28-
public bool CanPushToUpstream { get; set; } = false;
29-
public bool CanPullFromUpstream { get; set; } = false;
3028
public Thickness Margin { get; set; } = new Thickness(0);
3129

3230
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");

src/Models/CommitGraph.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public static void SetPens(List<Color> colors, double thickness)
128128
_penCount = colors.Count;
129129
}
130130

131-
public static CommitGraph Parse(List<Commit> commits, HashSet<string> canPushCommits, HashSet<string> canPullCommits)
131+
public static CommitGraph Parse(List<Commit> commits)
132132
{
133133
double UNIT_WIDTH = 12;
134134
double HALF_WIDTH = 6;
@@ -148,9 +148,6 @@ public static CommitGraph Parse(List<Commit> commits, HashSet<string> canPushCom
148148
var isMerged = commit.IsMerged;
149149
var oldCount = unsolved.Count;
150150

151-
commit.CanPushToUpstream = canPushCommits.Remove(commit.SHA);
152-
commit.CanPullFromUpstream = canPullCommits.Remove(commit.SHA);
153-
154151
// Update current y offset
155152
offsetY += UNIT_HEIGHT;
156153

src/Models/IssueTrackerRule.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System.Collections.Generic;
2+
using System.Text.RegularExpressions;
3+
4+
using CommunityToolkit.Mvvm.ComponentModel;
5+
6+
namespace SourceGit.Models
7+
{
8+
public class IssueTrackerMatch
9+
{
10+
public int Start { get; set; } = 0;
11+
public int Length { get; set; } = 0;
12+
public string URL { get; set; } = "";
13+
14+
public bool Intersect(int start, int length)
15+
{
16+
if (start == Start)
17+
return true;
18+
19+
if (start < Start)
20+
return start + length > Start;
21+
22+
return start < Start + Length;
23+
}
24+
}
25+
26+
public class IssueTrackerRule : ObservableObject
27+
{
28+
public string Name
29+
{
30+
get => _name;
31+
set => SetProperty(ref _name, value);
32+
}
33+
34+
public string RegexString
35+
{
36+
get => _regexString;
37+
set
38+
{
39+
if (SetProperty(ref _regexString, value))
40+
{
41+
try
42+
{
43+
_regex = null;
44+
_regex = new Regex(_regexString, RegexOptions.Multiline);
45+
}
46+
catch
47+
{
48+
// Ignore errors.
49+
}
50+
}
51+
52+
OnPropertyChanged(nameof(IsRegexValid));
53+
}
54+
}
55+
56+
public bool IsRegexValid
57+
{
58+
get => _regex != null;
59+
}
60+
61+
public string URLTemplate
62+
{
63+
get => _urlTemplate;
64+
set => SetProperty(ref _urlTemplate, value);
65+
}
66+
67+
public void Matches(List<IssueTrackerMatch> outs, string message)
68+
{
69+
if (_regex == null || string.IsNullOrEmpty(_urlTemplate))
70+
return;
71+
72+
var matches = _regex.Matches(message);
73+
for (var i = 0; i < matches.Count; i++)
74+
{
75+
var match = matches[i];
76+
if (!match.Success)
77+
continue;
78+
79+
var start = match.Index;
80+
var len = match.Length;
81+
var intersect = false;
82+
foreach (var exist in outs)
83+
{
84+
if (exist.Intersect(start, len))
85+
{
86+
intersect = true;
87+
break;
88+
}
89+
}
90+
91+
if (intersect)
92+
continue;
93+
94+
var range = new IssueTrackerMatch();
95+
range.Start = start;
96+
range.Length = len;
97+
range.URL = _urlTemplate;
98+
for (var j = 1; j < match.Groups.Count; j++)
99+
{
100+
var group = match.Groups[j];
101+
if (group.Success)
102+
range.URL = range.URL.Replace($"${j}", group.Value);
103+
}
104+
105+
outs.Add(range);
106+
}
107+
}
108+
109+
private string _name;
110+
private string _regexString;
111+
private string _urlTemplate;
112+
private Regex _regex = null;
113+
}
114+
}

0 commit comments

Comments
 (0)