Merge branch 'release/v8.23'

This commit is contained in:
leo 2024-07-29 10:25:32 +08:00
commit b7c6d62c6d
No known key found for this signature in database
78 changed files with 1267 additions and 787 deletions

View file

@ -22,7 +22,7 @@ jobs:
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build -c Release
- name: Publish - name: Publish
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r win-x64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained run: dotnet publish src/SourceGit.csproj -c Release -o publish -r win-x64
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@ -43,7 +43,7 @@ jobs:
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build -c Release
- name: Publish - name: Publish
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-x64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-x64
- name: Packing Program - name: Packing Program
run: tar -cvf sourcegit.osx-x64.tar -C publish/ . run: tar -cvf sourcegit.osx-x64.tar -C publish/ .
- name: Upload Artifact - name: Upload Artifact
@ -66,7 +66,7 @@ jobs:
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build -c Release
- name: Publish - name: Publish
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-arm64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained run: dotnet publish src/SourceGit.csproj -c Release -o publish -r osx-arm64
- name: Packing Program - name: Packing Program
run: tar -cvf sourcegit.osx-arm64.tar -C publish/ . run: tar -cvf sourcegit.osx-arm64.tar -C publish/ .
- name: Upload Artifact - name: Upload Artifact
@ -89,7 +89,7 @@ jobs:
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build -c Release
- name: Publish - name: Publish
run: dotnet publish src/SourceGit.csproj -c Release -o publish -r linux-x64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained run: dotnet publish src/SourceGit.csproj -c Release -o publish -r linux-x64
- name: Rename Executable File - name: Rename Executable File
run: mv publish/SourceGit publish/sourcegit run: mv publish/SourceGit publish/sourcegit
- name: Packing Program - name: Packing Program

View file

@ -75,6 +75,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SPECS", "SPECS", "{7802CD7A
build\resources\rpm\SPECS\build.spec = build\resources\rpm\SPECS\build.spec build\resources\rpm\SPECS\build.spec = build\resources\rpm\SPECS\build.spec
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "appimage", "appimage", "{5D125DD9-B48A-491F-B2FB-D7830D74C4DC}"
ProjectSection(SolutionItems) = preProject
build\resources\appimage\publish-appimage = build\resources\appimage\publish-appimage
build\resources\appimage\publish-appimage.conf = build\resources\appimage\publish-appimage.conf
build\resources\appimage\runtime-x86_64 = build\resources\appimage\runtime-x86_64
build\resources\appimage\sourcegit.appdata.xml = build\resources\appimage\sourcegit.appdata.xml
build\resources\appimage\sourcegit.png = build\resources\appimage\sourcegit.png
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -104,6 +113,7 @@ Global
{F101849D-BDB7-40D4-A516-751150C3CCFC} = {9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC} {F101849D-BDB7-40D4-A516-751150C3CCFC} = {9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC}
{9BA0B044-0CC9-46F8-B551-204F149BF45D} = {FD384607-ED99-47B7-AF31-FB245841BC92} {9BA0B044-0CC9-46F8-B551-204F149BF45D} = {FD384607-ED99-47B7-AF31-FB245841BC92}
{7802CD7A-591B-4EDD-96F8-9BF3F61692E4} = {9BA0B044-0CC9-46F8-B551-204F149BF45D} {7802CD7A-591B-4EDD-96F8-9BF3F61692E4} = {9BA0B044-0CC9-46F8-B551-204F149BF45D}
{5D125DD9-B48A-491F-B2FB-D7830D74C4DC} = {FD384607-ED99-47B7-AF31-FB245841BC92}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7FF1B9C6-B5BF-4A50-949F-4B407A0E31C9} SolutionGuid = {7FF1B9C6-B5BF-4A50-949F-4B407A0E31C9}

View file

@ -1 +1 @@
8.22.1 8.23

View file

@ -9,13 +9,13 @@ cp resources/app/App.icns SourceGit.app/Contents/Resources/App.icns
sed "s/SOURCE_GIT_VERSION/${version}/g" resources/app/App.plist > SourceGit.app/Contents/Info.plist sed "s/SOURCE_GIT_VERSION/${version}/g" resources/app/App.plist > SourceGit.app/Contents/Info.plist
mkdir -p SourceGit.app/Contents/MacOS mkdir -p SourceGit.app/Contents/MacOS
dotnet publish ../src/SourceGit.csproj -c Release -r osx-arm64 -o SourceGit.app/Contents/MacOS -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained dotnet publish ../src/SourceGit.csproj -c Release -r osx-arm64 -o SourceGit.app/Contents/MacOS
zip sourcegit_${version}.osx-arm64.zip -r SourceGit.app -x "*/*\.dsym/*" zip sourcegit_${version}.osx-arm64.zip -r SourceGit.app -x "*/*\.dsym/*"
rm -rf SourceGit.app/Contents/MacOS rm -rf SourceGit.app/Contents/MacOS
mkdir -p SourceGit.app/Contents/MacOS mkdir -p SourceGit.app/Contents/MacOS
dotnet publish ../src/SourceGit.csproj -c Release -r osx-x64 -o SourceGit.app/Contents/MacOS -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained dotnet publish ../src/SourceGit.csproj -c Release -r osx-x64 -o SourceGit.app/Contents/MacOS
zip sourcegit_${version}.osx-x64.zip -r SourceGit.app -x "*/*\.dsym/*" zip sourcegit_${version}.osx-x64.zip -r SourceGit.app -x "*/*\.dsym/*"
rm -rf SourceGit.app rm -rf SourceGit.app

View file

@ -6,7 +6,7 @@ if (Test-Path SourceGit) {
Remove-Item *.zip -Force Remove-Item *.zip -Force
dotnet publish ..\src\SourceGit.csproj -c Release -r win-arm64 -o SourceGit -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained dotnet publish ..\src\SourceGit.csproj -c Release -r win-arm64 -o SourceGit
Remove-Item SourceGit\*.pdb -Force Remove-Item SourceGit\*.pdb -Force
@ -16,7 +16,7 @@ if (Test-Path SourceGit) {
Remove-Item SourceGit -Recurse -Force Remove-Item SourceGit -Recurse -Force
} }
dotnet publish ..\src\SourceGit.csproj -c Release -r win-x64 -o SourceGit -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained dotnet publish ..\src\SourceGit.csproj -c Release -r win-x64 -o SourceGit
Remove-Item SourceGit\*.pdb -Force Remove-Item SourceGit\*.pdb -Force

View file

@ -11,7 +11,7 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>SOURCE_GIT_VERSION.0</string> <string>SOURCE_GIT_VERSION.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.12</string> <string>11.0</string>
<key>LSEnvironment</key> <key>LSEnvironment</key>
<dict> <dict>
<key>PATH</key> <key>PATH</key>

View file

@ -82,7 +82,7 @@ DOTNET_PROJECT_PATH="../../../src/SourceGit.csproj"
# Additional useful arguments include: # Additional useful arguments include:
# "-p:DebugType=None -p:DebugSymbols=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:TrimMode=link" # "-p:DebugType=None -p:DebugSymbols=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:TrimMode=link"
# Refer: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish # Refer: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish
DOTNET_PUBLISH_ARGS="-c Release -p:DebugType=None -p:DebugSymbols=false -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained" DOTNET_PUBLISH_ARGS="-c Release -p:DebugType=None -p:DebugSymbols=false"
######################################## ########################################

View file

@ -62,7 +62,7 @@ namespace SourceGit
[JsonSerializable(typeof(Models.JetBrainsState))] [JsonSerializable(typeof(Models.JetBrainsState))]
[JsonSerializable(typeof(Models.ThemeOverrides))] [JsonSerializable(typeof(Models.ThemeOverrides))]
[JsonSerializable(typeof(Models.Version))] [JsonSerializable(typeof(Models.Version))]
[JsonSerializable(typeof(Models.RepositorySettings))]
[JsonSerializable(typeof(ViewModels.Preference))] [JsonSerializable(typeof(ViewModels.Preference))]
[JsonSerializable(typeof(ViewModels.RepositorySettings))]
internal partial class JsonCodeGen : JsonSerializerContext { } internal partial class JsonCodeGen : JsonSerializerContext { }
} }

View file

@ -302,6 +302,11 @@ namespace SourceGit
}); });
} }
public static ViewModels.Launcher GetLauncer()
{
return Current is App app ? app._launcher : null;
}
public static ViewModels.Repository FindOpenedRepository(string repoPath) public static ViewModels.Repository FindOpenedRepository(string repoPath)
{ {
if (Current is App app && app._launcher != null) if (Current is App app && app._launcher != null)
@ -501,10 +506,15 @@ namespace SourceGit
private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop) private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop)
{ {
var args = desktop.Args; var args = desktop.Args;
if (args == null || args.Length != 1 || !args[0].StartsWith("Enter passphrase", StringComparison.Ordinal)) if (args == null || args.Length != 1)
return false; return false;
desktop.MainWindow = new Views.Askpass(args[0]); var param = args[0];
if (!param.StartsWith("enter passphrase", StringComparison.OrdinalIgnoreCase) &&
!param.Contains(" password", StringComparison.OrdinalIgnoreCase))
return false;
desktop.MainWindow = new Views.Askpass(param);
return true; return true;
} }

View file

@ -4,12 +4,12 @@ using System.Text.RegularExpressions;
namespace SourceGit.Commands namespace SourceGit.Commands
{ {
public partial class AssumeUnchanged public partial class AssumeUnchanged
{
partial class ViewCommand : Command
{ {
[GeneratedRegex(@"^(\w)\s+(.+)$")] [GeneratedRegex(@"^(\w)\s+(.+)$")]
private static partial Regex REG(); private static partial Regex REG_PARSE();
class ViewCommand : Command
{
public ViewCommand(string repo) public ViewCommand(string repo)
{ {
WorkingDirectory = repo; WorkingDirectory = repo;
@ -25,7 +25,7 @@ namespace SourceGit.Commands
protected override void OnReadline(string line) protected override void OnReadline(string line)
{ {
var match = REG().Match(line); var match = REG_PARSE().Match(line);
if (!match.Success) if (!match.Success)
return; return;

View file

@ -13,7 +13,9 @@ namespace SourceGit.Commands
{ {
WorkingDirectory = repo; WorkingDirectory = repo;
Context = repo; Context = repo;
Args = $"diff --name-status {start} {end}";
var based = string.IsNullOrEmpty(start) ? "-R" : start;
Args = $"diff --name-status {based} {end}";
} }
public List<Models.Change> Result() public List<Models.Change> Result()

View file

@ -14,7 +14,8 @@ namespace SourceGit.Commands
public Diff(string repo, Models.DiffOption opt, int unified) public Diff(string repo, Models.DiffOption opt, int unified)
{ {
_result.TextDiff = new Models.TextDiff() { _result.TextDiff = new Models.TextDiff()
{
Repo = repo, Repo = repo,
Option = opt, Option = opt,
}; };

View file

@ -1,18 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands namespace SourceGit.Commands
{ {
public partial class QueryBranches : Command public class QueryBranches : Command
{ {
private const string PREFIX_LOCAL = "refs/heads/"; private const string PREFIX_LOCAL = "refs/heads/";
private const string PREFIX_REMOTE = "refs/remotes/"; private const string PREFIX_REMOTE = "refs/remotes/";
private const string PREFIX_DETACHED = "(HEAD detached at"; private const string PREFIX_DETACHED = "(HEAD detached at";
[GeneratedRegex(@"^(\d+)\s(\d+)$")]
private static partial Regex REG_AHEAD_BEHIND();
public QueryBranches(string repo) public QueryBranches(string repo)
{ {
WorkingDirectory = repo; WorkingDirectory = repo;

View file

@ -38,7 +38,5 @@ namespace SourceGit.Commands
return null; return null;
} }
} }
} }

View file

@ -5,7 +5,6 @@ namespace SourceGit.Commands
{ {
public partial class QueryStashChanges : Command public partial class QueryStashChanges : Command
{ {
[GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")] [GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")]
private static partial Regex REG_FORMAT(); private static partial Regex REG_FORMAT();

View file

@ -9,7 +9,7 @@ namespace SourceGit.Commands
{ {
Context = repo; Context = repo;
WorkingDirectory = repo; WorkingDirectory = repo;
Args = "for-each-ref --sort=-creatordate --format=\"$%(refname:short)$%(objectname)$%(*objectname)\" refs/tags"; Args = "tag -l --sort=-creatordate --format=\"$%(refname)$%(objectname)$%(*objectname)\"";
} }
public List<Models.Tag> Result() public List<Models.Tag> Result()
@ -25,7 +25,7 @@ namespace SourceGit.Commands
{ {
_loaded.Add(new Models.Tag() _loaded.Add(new Models.Tag()
{ {
Name = subs[0], Name = subs[0].Substring(10),
SHA = subs[1], SHA = subs[1],
}); });
} }
@ -33,7 +33,7 @@ namespace SourceGit.Commands
{ {
_loaded.Add(new Models.Tag() _loaded.Add(new Models.Tag()
{ {
Name = subs[0], Name = subs[0].Substring(10),
SHA = subs[2], SHA = subs[2],
}); });
} }

View file

@ -19,7 +19,7 @@ namespace SourceGit.Commands
if (!rs.IsSuccess) if (!rs.IsSuccess)
return status; return status;
var lines = rs.StdOut.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries); var lines = rs.StdOut.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines) foreach (var line in lines)
{ {
if (line[0] == '>') if (line[0] == '>')

View file

@ -13,36 +13,46 @@ namespace SourceGit.Commands
public bool Add(string url, string relativePath, bool recursive, Action<string> outputHandler) public bool Add(string url, string relativePath, bool recursive, Action<string> outputHandler)
{ {
_outputHandler = outputHandler; _outputHandler = outputHandler;
Args = $"submodule add {url} {relativePath}"; Args = $"submodule add {url} \"{relativePath}\"";
if (!Exec()) if (!Exec())
return false; return false;
if (recursive) if (recursive)
{ {
Args = $"submodule update --init --recursive -- {relativePath}"; Args = $"submodule update --init --recursive -- \"{relativePath}\"";
return Exec(); return Exec();
} }
else else
{ {
Args = $"submodule update --init -- {relativePath}"; Args = $"submodule update --init -- \"{relativePath}\"";
return true; return true;
} }
} }
public bool Update(Action<string> outputHandler) public bool Update(string module, bool init, bool recursive, bool useRemote, Action<string> outputHandler)
{ {
Args = $"submodule update --rebase --remote"; Args = "submodule update";
if (init)
Args += " --init";
if (recursive)
Args += " --recursive";
if (useRemote)
Args += " --remote";
if (!string.IsNullOrEmpty(module))
Args += $" -- \"{module}\"";
_outputHandler = outputHandler; _outputHandler = outputHandler;
return Exec(); return Exec();
} }
public bool Delete(string relativePath) public bool Delete(string relativePath)
{ {
Args = $"submodule deinit -f {relativePath}"; Args = $"submodule deinit -f \"{relativePath}\"";
if (!Exec()) if (!Exec())
return false; return false;
Args = $"rm -rf {relativePath}"; Args = $"rm -rf \"{relativePath}\"";
return Exec(); return Exec();
} }

View file

@ -1,46 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace SourceGit.Converters
{
public static class DecoratorTypeConverters
{
public static readonly FuncValueConverter<Models.DecoratorType, IBrush> ToBackground =
new FuncValueConverter<Models.DecoratorType, IBrush>(v =>
{
if (v == Models.DecoratorType.Tag)
return Application.Current.FindResource("Brush.DecoratorTag") as IBrush;
return Application.Current.FindResource("Brush.DecoratorBranch") as IBrush;
});
public static readonly FuncValueConverter<Models.DecoratorType, StreamGeometry> ToIcon =
new FuncValueConverter<Models.DecoratorType, StreamGeometry>(v =>
{
var key = "Icons.Tag";
switch (v)
{
case Models.DecoratorType.CurrentBranchHead:
key = "Icons.Check";
break;
case Models.DecoratorType.RemoteBranchHead:
key = "Icons.Remote";
break;
case Models.DecoratorType.LocalBranchHead:
key = "Icons.Branch";
break;
default:
break;
}
return Application.Current?.FindResource(key) as StreamGeometry;
});
public static readonly FuncValueConverter<Models.DecoratorType, FontWeight> ToFontWeight =
new FuncValueConverter<Models.DecoratorType, FontWeight>(v =>
v is Models.DecoratorType.CurrentBranchHead or Models.DecoratorType.CurrentCommitHead
? FontWeight.Bold : FontWeight.Regular
);
}
}

View file

@ -80,7 +80,7 @@ namespace SourceGit.Models
/// <param name="change"></param> /// <param name="change"></param>
public DiffOption(string baseRevision, string targetRevision, Change change) public DiffOption(string baseRevision, string targetRevision, Change change)
{ {
_revisions.Add(baseRevision); _revisions.Add(string.IsNullOrEmpty(baseRevision) ? "-R" : baseRevision);
_revisions.Add(targetRevision); _revisions.Add(targetRevision);
_path = change.Path; _path = change.Path;
_orgPath = change.OriginalPath; _orgPath = change.OriginalPath;

View file

@ -128,7 +128,7 @@ namespace SourceGit.Models
public void FindJetBrainsFromToolbox(Func<string> platformFinder) public void FindJetBrainsFromToolbox(Func<string> platformFinder)
{ {
var exclude = new List<string> { "fleet", "dotmemory", "dottrace", "resharper-u", "androidstudio" }; var exclude = new List<string> { "fleet", "dotmemory", "dottrace", "resharper-u", "androidstudio" };
var supported_icons = new List<string> { "CL", "DB", "DL", "DS", "GO", "IC", "IU", "JB", "PC", "PS", "PY", "QA", "QD", "RD", "RM", "RR", "WRS", "WS" }; var supported_icons = new List<string> { "CL", "DB", "DL", "DS", "GO", "JB", "PC", "PS", "PY", "QA", "QD", "RD", "RM", "RR", "WRS", "WS" };
var state = Path.Combine(platformFinder(), "state.json"); var state = Path.Combine(platformFinder(), "state.json");
if (File.Exists(state)) if (File.Exists(state))
{ {

24
src/Models/MergeMode.cs Normal file
View file

@ -0,0 +1,24 @@
namespace SourceGit.Models
{
public class MergeMode
{
public static readonly MergeMode[] Supported =
[
new MergeMode("Default", "Fast-forward if possible", ""),
new MergeMode("No Fast-forward", "Always create a merge commit", "--no-ff"),
new MergeMode("Squash", "Use '--squash'", "--squash"),
new MergeMode("Don't commit", "Merge without commit", "--no-commit"),
];
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public MergeMode(string n, string d, string a)
{
Name = n;
Desc = d;
Arg = a;
}
}
}

View file

@ -1,4 +1,4 @@
namespace SourceGit.ViewModels namespace SourceGit.Models
{ {
public class Notification public class Notification
{ {

6
src/Models/Null.cs Normal file
View file

@ -0,0 +1,6 @@
namespace SourceGit.Models
{
public class Null
{
}
}

View file

@ -0,0 +1,97 @@
using Avalonia.Collections;
namespace SourceGit.Models
{
public class RepositorySettings
{
public DealWithLocalChanges DealWithLocalChangesOnCheckoutBranch
{
get;
set;
} = DealWithLocalChanges.DoNothing;
public bool FetchWithoutTags
{
get;
set;
} = false;
public DealWithLocalChanges DealWithLocalChangesOnPull
{
get;
set;
} = DealWithLocalChanges.DoNothing;
public bool PreferRebaseInsteadOfMerge
{
get;
set;
} = true;
public bool FetchWithoutTagsOnPull
{
get;
set;
} = false;
public bool FetchAllBranchesOnPull
{
get;
set;
} = true;
public bool PushAllTags
{
get;
set;
} = false;
public DealWithLocalChanges DealWithLocalChangesOnCreateBranch
{
get;
set;
} = DealWithLocalChanges.DoNothing;
public bool CheckoutBranchOnCreateBranch
{
get;
set;
} = true;
public bool AutoStageBeforeCommit
{
get;
set;
} = false;
public AvaloniaList<string> Filters
{
get;
set;
} = new AvaloniaList<string>();
public AvaloniaList<string> CommitMessages
{
get;
set;
} = new AvaloniaList<string>();
public void PushCommitMessage(string message)
{
var existIdx = CommitMessages.IndexOf(message);
if (existIdx == 0)
return;
if (existIdx > 0)
{
CommitMessages.Move(existIdx, 0);
return;
}
if (CommitMessages.Count > 9)
CommitMessages.RemoveRange(9, CommitMessages.Count - 9);
CommitMessages.Insert(0, message);
}
}
}

27
src/Models/ResetMode.cs Normal file
View file

@ -0,0 +1,27 @@
using Avalonia.Media;
namespace SourceGit.Models
{
public class ResetMode
{
public static readonly ResetMode[] Supported =
[
new ResetMode("Soft", "Keep all changes. Stage differences", "--soft", Brushes.Green),
new ResetMode("Mixed", "Keep all changes. Unstage differences", "--mixed", Brushes.Orange),
new ResetMode("Hard", "Discard all changes", "--hard", Brushes.Red),
];
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public IBrush Color { get; set; }
public ResetMode(string n, string d, string a, IBrush b)
{
Name = n;
Desc = d;
Arg = a;
Color = b;
}
}
}

View file

@ -1,10 +1,9 @@
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace SourceGit.Models namespace SourceGit.Models
{ {
public partial class Version public class Version
{ {
[JsonPropertyName("name")] [JsonPropertyName("name")]
public string Name { get; set; } public string Name { get; set; }
@ -15,21 +14,20 @@ namespace SourceGit.Models
[JsonPropertyName("body")] [JsonPropertyName("body")]
public string Body { get; set; } public string Body { get; set; }
[GeneratedRegex(@"^v(\d+)\.(\d+)$")]
private static partial Regex REG_VERSION_TAG();
public bool IsNewVersion public bool IsNewVersion
{ {
get get
{ {
var match = REG_VERSION_TAG().Match(TagName); try
if (!match.Success) {
System.Version version = new System.Version(TagName.Substring(1));
System.Version current = Assembly.GetExecutingAssembly().GetName().Version!;
return current.CompareTo(version) < 0;
}
catch
{
return false; return false;
}
var major = int.Parse(match.Groups[1].Value);
var minor = int.Parse(match.Groups[2].Value);
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
return ver.Major < major || (ver.Major == major && ver.Minor < minor);
} }
} }
} }

View file

@ -16,7 +16,7 @@
<StreamGeometry x:Key="Icons.Copy">M896 811l-128 0c-23 0-43-19-43-43 0-23 19-43 43-43l107 0c13 0 21-9 21-21L896 107c0-13-9-21-21-21L448 85c-13 0-21 9-21 21l0 21c0 23-19 43-43 43-23 0-43-19-43-43L341 85c0-47 38-85 85-85l469 0c47 0 85 38 85 85l0 640C981 772 943 811 896 811zM683 299l0 640c0 47-38 85-85 85L128 1024c-47 0-85-38-85-85L43 299c0-47 38-85 85-85l469 0C644 213 683 252 683 299zM576 299 149 299c-13 0-21 9-21 21l0 597c0 13 9 21 21 21l427 0c13 0 21-9 21-21L597 320C597 307 589 299 576 299z</StreamGeometry> <StreamGeometry x:Key="Icons.Copy">M896 811l-128 0c-23 0-43-19-43-43 0-23 19-43 43-43l107 0c13 0 21-9 21-21L896 107c0-13-9-21-21-21L448 85c-13 0-21 9-21 21l0 21c0 23-19 43-43 43-23 0-43-19-43-43L341 85c0-47 38-85 85-85l469 0c47 0 85 38 85 85l0 640C981 772 943 811 896 811zM683 299l0 640c0 47-38 85-85 85L128 1024c-47 0-85-38-85-85L43 299c0-47 38-85 85-85l469 0C644 213 683 252 683 299zM576 299 149 299c-13 0-21 9-21 21l0 597c0 13 9 21 21 21l427 0c13 0 21-9 21-21L597 320C597 307 589 299 576 299z</StreamGeometry>
<StreamGeometry x:Key="Icons.Cut">M280 145l243 341 0-0 45 63-0 0 79 110a143 143 0 11-36 75l-88-123-92 126c1 4 1 9 1 13l0 5a143 143 0 11-36-95l82-113L221 188l60-43zm473 541a70 70 0 100 140 70 70 0 000-140zm-463 0a70 70 0 100 140 70 70 0 000-140zM772 145l59 43-232 319-45-63L772 145z</StreamGeometry> <StreamGeometry x:Key="Icons.Cut">M280 145l243 341 0-0 45 63-0 0 79 110a143 143 0 11-36 75l-88-123-92 126c1 4 1 9 1 13l0 5a143 143 0 11-36-95l82-113L221 188l60-43zm473 541a70 70 0 100 140 70 70 0 000-140zm-463 0a70 70 0 100 140 70 70 0 000-140zM772 145l59 43-232 319-45-63L772 145z</StreamGeometry>
<StreamGeometry x:Key="Icons.Detached">M128 183C128 154 154 128 183 128h521c30 0 55 26 55 55v38c0 17-17 34-34 34s-34-17-34-34v-26H196v495h26c17 0 34 17 34 34s-17 34-34 34h-38c-30 0-55-26-55-55V183zM380 896h-34c-26 0-47-21-47-47v-90h68V828h64V896H380c4 0 0 0 0 0zM759 828V896h90c26 0 47-21 47-47v-90h-68V828h-68zM828 435H896V346c0-26-21-47-47-47h-90v68H828v68zM435 299v68H367V439H299V346C299 320 320 299 346 299h90zM367 649H299v-107h68v107zM546 367V299h107v68h-107zM828 546H896v107h-68v-107zM649 828V896h-107v-68h107zM730 508v188c0 17-17 34-34 34h-188c-17 0-34-17-34-34s17-34 34-34h102l-124-124c-13-13-13-34 0-47 13-13 34-13 47 0l124 124V512c0-17 17-34 34-34 21-4 38 9 38 30z</StreamGeometry> <StreamGeometry x:Key="Icons.Detached">M128 183C128 154 154 128 183 128h521c30 0 55 26 55 55v38c0 17-17 34-34 34s-34-17-34-34v-26H196v495h26c17 0 34 17 34 34s-17 34-34 34h-38c-30 0-55-26-55-55V183zM380 896h-34c-26 0-47-21-47-47v-90h68V828h64V896H380c4 0 0 0 0 0zM759 828V896h90c26 0 47-21 47-47v-90h-68V828h-68zM828 435H896V346c0-26-21-47-47-47h-90v68H828v68zM435 299v68H367V439H299V346C299 320 320 299 346 299h90zM367 649H299v-107h68v107zM546 367V299h107v68h-107zM828 546H896v107h-68v-107zM649 828V896h-107v-68h107zM730 508v188c0 17-17 34-34 34h-188c-17 0-34-17-34-34s17-34 34-34h102l-124-124c-13-13-13-34 0-47 13-13 34-13 47 0l124 124V512c0-17 17-34 34-34 21-4 38 9 38 30z</StreamGeometry>
<StreamGeometry x:Key="Icons.Detail">M888.8 0H135.2c-32.3 0-58.9 26.1-58.9 58.9v906.2c0 32.3 26.1 58.9 58.9 58.9h753.2c32.3 0 58.9-26.1 58.9-58.9v-906.2c.5-32.8-26.1-58.9-58.4-58.9zm-164.9 176.6c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s24.6-55.8 55.8-55.8zm-212 0c30.7 0 55.8 25.1 55.8 55.8S542.7 288.3 512 288.3s-55.8-25.1-55.8-55.8S481.3 176.6 512 176.6zm-212 0c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s25.1-55.8 55.8-55.8zm208.9 606.2H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h223.7c24.6 0 44 20 44 44c0 24.1-19.5 44-44 44zm229.9-212H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h453.1c24.6 0 44 20 44 44c.5 24.1-19.5 44-43.5 44z</StreamGeometry> <StreamGeometry x:Key="Icons.Detail">M889 0H135c-32 0-59 26-59 59v906c0 32 26 59 59 59h753c32 0 59-26 59-59v-906c1-33-26-59-58-59zm-165 177c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm-212 0c31 0 56 25 56 56S543 288 512 288s-56-25-56-56S481 177 512 177zm-212 0c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm209 606H285c-25 0-44-20-44-44 0-25 20-44 44-44h224c25 0 44 20 44 44 0 24-20 44-44 44zm230-212H285c-25 0-44-20-44-44 0-25 20-44 44-44h453c25 0 44 20 44 44 1 24-20 44-44 44z</StreamGeometry>
<StreamGeometry x:Key="Icons.Diff">M854 307 611 73c-6-6-14-9-22-9H296c-4 0-8 4-8 8v56c0 4 4 8 8 8h277l219 211V824c0 4 4 8 8 8h56c4 0 8-4 8-8V330c0-9-4-17-10-23zM553 201c-6-6-14-9-23-9H192c-18 0-32 14-32 32v704c0 18 14 32 32 32h512c18 0 32-14 32-32V397c0-9-3-17-9-23L553 201zM568 753c0 4-3 7-8 7h-225c-4 0-8-3-8-7v-42c0-4 3-7 8-7h225c4 0 8 3 8 7v42zm0-220c0 4-3 7-8 7H476v85c0 4-3 7-7 7h-42c-4 0-7-3-7-7V540h-85c-4 0-8-3-8-7v-42c0-4 3-7 8-7H420v-85c0-4 3-7 7-7h42c4 0 7 3 7 7V484h85c4 0 8 3 8 7v42z</StreamGeometry> <StreamGeometry x:Key="Icons.Diff">M854 307 611 73c-6-6-14-9-22-9H296c-4 0-8 4-8 8v56c0 4 4 8 8 8h277l219 211V824c0 4 4 8 8 8h56c4 0 8-4 8-8V330c0-9-4-17-10-23zM553 201c-6-6-14-9-23-9H192c-18 0-32 14-32 32v704c0 18 14 32 32 32h512c18 0 32-14 32-32V397c0-9-3-17-9-23L553 201zM568 753c0 4-3 7-8 7h-225c-4 0-8-3-8-7v-42c0-4 3-7 8-7h225c4 0 8 3 8 7v42zm0-220c0 4-3 7-8 7H476v85c0 4-3 7-7 7h-42c-4 0-7-3-7-7V540h-85c-4 0-8-3-8-7v-42c0-4 3-7 8-7H420v-85c0-4 3-7 7-7h42c4 0 7 3 7 7V484h85c4 0 8 3 8 7v42z</StreamGeometry>
<StreamGeometry x:Key="Icons.DoubleDown">M256 224l0 115L512 544l256-205 0-115-256 205L256 224zM512 685l-256-205L256 595 512 800 768 595l0-115L512 685z</StreamGeometry> <StreamGeometry x:Key="Icons.DoubleDown">M256 224l0 115L512 544l256-205 0-115-256 205L256 224zM512 685l-256-205L256 595 512 800 768 595l0-115L512 685z</StreamGeometry>
<StreamGeometry x:Key="Icons.DoubleUp">M768 800V685L512 480 256 685V800l256-205L768 800zM512 339 768 544V429L512 224 256 429V544l256-205z</StreamGeometry> <StreamGeometry x:Key="Icons.DoubleUp">M768 800V685L512 480 256 685V800l256-205L768 800zM512 339 768 544V429L512 224 256 429V544l256-205z</StreamGeometry>
@ -45,7 +45,7 @@
<StreamGeometry x:Key="Icons.Grid">M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z</StreamGeometry> <StreamGeometry x:Key="Icons.Grid">M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z</StreamGeometry>
<StreamGeometry x:Key="Icons.Histories">M24 512A488 488 0 01512 24A488 488 0 011000 512A488 488 0 01512 1000A488 488 0 0124 512zm447-325v327L243 619l51 111 300-138V187H471z</StreamGeometry> <StreamGeometry x:Key="Icons.Histories">M24 512A488 488 0 01512 24A488 488 0 011000 512A488 488 0 01512 1000A488 488 0 0124 512zm447-325v327L243 619l51 111 300-138V187H471z</StreamGeometry>
<StreamGeometry x:Key="Icons.Home">M832 64h128v278l-128-146V64zm64 448L512 73 128 512H0L448 0h128l448 512h-128zm0 83V1024H640V704c0-35-29-64-64-64h-128a64 64 0 00-64 64v320H128V595l384-424 384 424z</StreamGeometry> <StreamGeometry x:Key="Icons.Home">M832 64h128v278l-128-146V64zm64 448L512 73 128 512H0L448 0h128l448 512h-128zm0 83V1024H640V704c0-35-29-64-64-64h-128a64 64 0 00-64 64v320H128V595l384-424 384 424z</StreamGeometry>
<StreamGeometry x:Key="Icons.Hotkeys">M512 0C229.216 0 0 229.216 0 512c0 282.752 229.216 512 512 512s512-229.248 512-512c0-282.784-229.216-512-512-512z m0 957.92C266.112 957.92 66.08 757.888 66.08 512S266.112 66.08 512 66.08 957.92 266.112 957.92 512 757.888 957.92 512 957.92zM192 416h96a32 32 0 0 0 32-32v-32a32 32 0 0 0-32-32H192a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32zM384 416h96a32 32 0 0 0 32-32v-32a32 32 0 0 0-32-32h-96a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32zM576 416h96a32 32 0 0 0 32-32v-32a32 32 0 0 0-32-32h-96a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32zM832 320h-64a32 32 0 0 0-32 32v128h-160a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32h256a32 32 0 0 0 32-32v-192a32 32 0 0 0-32-32zM320 544v-32a32 32 0 0 0-32-32H192a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32h96a32 32 0 0 0 32-32zM384 576h96a32 32 0 0 0 32-32v-32a32 32 0 0 0-32-32h-96a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32zM800 640H256a32 32 0 0 0-32 32v32a32 32 0 0 0 32 32h544a32 32 0 0 0 32-32v-32a32 32 0 0 0-32-32z</StreamGeometry> <StreamGeometry x:Key="Icons.Hotkeys">M512 0C229 0 0 229 0 512c0 283 229 512 512 512s512-229 512-512c0-283-229-512-512-512zm0 958C266 958 66 758 66 512S266 66 512 66 958 266 958 512 758 958 512 958zM192 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32H192a32 32 0 00-32 32v32a32 32 0 0032 32zM384 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM576 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM832 320h-64a32 32 0 00-32 32v128h-160a32 32 0 00-32 32v32a32 32 0 0032 32h256a32 32 0 0032-32v-192a32 32 0 00-32-32zM320 544v-32a32 32 0 00-32-32H192a32 32 0 00-32 32v32a32 32 0 0032 32h96a32 32 0 0032-32zM384 576h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM800 640H256a32 32 0 00-32 32v32a32 32 0 0032 32h544a32 32 0 0032-32v-32a32 32 0 00-32-32z</StreamGeometry>
<StreamGeometry x:Key="Icons.Incoming">M973 358a51 51 0 0151 51v563a51 51 0 01-51 51H51a51 51 0 01-51-51V410a51 51 0 0151-51h256a51 51 0 110 102H102v461h819V461h-205a51 51 0 110-102h256zM51 102a51 51 0 110-102h256c141 0 256 115 256 256v388l66-66a51 51 0 1172 72l-154 154a51 51 0 01-72 0l-154-154a51 51 0 1172-72L461 644V256c0-85-69-154-154-154H51z</StreamGeometry> <StreamGeometry x:Key="Icons.Incoming">M973 358a51 51 0 0151 51v563a51 51 0 01-51 51H51a51 51 0 01-51-51V410a51 51 0 0151-51h256a51 51 0 110 102H102v461h819V461h-205a51 51 0 110-102h256zM51 102a51 51 0 110-102h256c141 0 256 115 256 256v388l66-66a51 51 0 1172 72l-154 154a51 51 0 01-72 0l-154-154a51 51 0 1172-72L461 644V256c0-85-69-154-154-154H51z</StreamGeometry>
<StreamGeometry x:Key="Icons.Info">M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z</StreamGeometry> <StreamGeometry x:Key="Icons.Info">M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z</StreamGeometry>
<StreamGeometry x:Key="Icons.Init">M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z</StreamGeometry> <StreamGeometry x:Key="Icons.Init">M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z</StreamGeometry>
@ -73,6 +73,7 @@
<StreamGeometry x:Key="Icons.Rebase">M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z</StreamGeometry> <StreamGeometry x:Key="Icons.Rebase">M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z</StreamGeometry>
<StreamGeometry x:Key="Icons.Remote">M706 302a289 289 0 00-173 44 27 27 0 1029 46 234 234 0 01125-36c23 0 45 3 66 9 93 28 161 114 161 215C914 704 813 805 687 805H337C211 805 110 704 110 580c0-96 61-178 147-210C282 263 379 183 495 183a245 245 0 01210 119z</StreamGeometry> <StreamGeometry x:Key="Icons.Remote">M706 302a289 289 0 00-173 44 27 27 0 1029 46 234 234 0 01125-36c23 0 45 3 66 9 93 28 161 114 161 215C914 704 813 805 687 805H337C211 805 110 704 110 580c0-96 61-178 147-210C282 263 379 183 495 183a245 245 0 01210 119z</StreamGeometry>
<StreamGeometry x:Key="Icons.Remote.Add">M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z</StreamGeometry> <StreamGeometry x:Key="Icons.Remote.Add">M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z</StreamGeometry>
<StreamGeometry x:Key="Icons.RemoveAll">M1024 64v704h-128v128h-128v128h-768v-704h128v-128h128v-128zM64 960h640v-576h-640zM320 128v64h576v512h64v-576zM192 256v64h576v512h64v-576zM432 688L576 832H480L384 736 288 832H192l144-144L192 544h96L384 640l96-96H576z</StreamGeometry>
<StreamGeometry x:Key="Icons.Rename">M853 256h-43v512h43c47 0 85-38 85-85v-341c0-47-38-85-85-85zM725 768V171h128V85h-341v85H640v85H171c-47 0-85 38-85 85v341c0 47 38 85 85 85h469V853h-128v85h341v-85H725v-86zm-469-171v-171h384v171H256z</StreamGeometry> <StreamGeometry x:Key="Icons.Rename">M853 256h-43v512h43c47 0 85-38 85-85v-341c0-47-38-85-85-85zM725 768V171h128V85h-341v85H640v85H171c-47 0-85 38-85 85v341c0 47 38 85 85 85h469V853h-128v85h341v-85H725v-86zm-469-171v-171h384v171H256z</StreamGeometry>
<StreamGeometry x:Key="Icons.Repositories">M960 146v91C960 318 759 384 512 384S64 318 64 238V146C64 66 265 0 512 0s448 66 448 146zM960 352v206C960 638 759 704 512 704S64 638 64 558V352c96 66 272 97 448 97S864 418 960 352zm0 320v206C960 958 759 1024 512 1024S64 958 64 878V672c96 66 272 97 448 97S864 738 960 672z</StreamGeometry> <StreamGeometry x:Key="Icons.Repositories">M960 146v91C960 318 759 384 512 384S64 318 64 238V146C64 66 265 0 512 0s448 66 448 146zM960 352v206C960 638 759 704 512 704S64 638 64 558V352c96 66 272 97 448 97S864 418 960 352zm0 320v206C960 958 759 1024 512 1024S64 958 64 878V672c96 66 272 97 448 97S864 738 960 672z</StreamGeometry>
<StreamGeometry x:Key="Icons.Reset">M883 567l-128-128c-17-17-43-17-60 0l-128 128c-17 17-17 43 0 60 17 17 43 17 60 0l55-55V683c0 21-21 43-43 43H418c-13-38-43-64-77-77V375c51-17 85-64 85-119 0-73-60-128-128-128-73 0-128 55-128 128 0 55 34 102 85 119v269c-51 17-85 64-85 119 0 73 55 128 128 128 55 0 102-34 119-85H640c73 0 128-55 128-128v-111l55 55c9 9 17 13 30 13 13 0 21-4 30-13 17-13 17-43 0-55zM299 213c26 0 43 17 43 43 0 21-21 43-43 43-26 0-43-21-43-43 0-26 17-43 43-43zm0 597c-26 0-43-21-43-43 0-26 17-43 43-43s43 17 43 43c0 21-17 43-43 43zM725 384c-73 0-128-60-128-128 0-73 55-128 128-128s128 55 128 128c0 68-55 128-128 128zm0-171c-26 0-43 17-43 43s17 43 43 43 43-17 43-43-17-43-43-43z</StreamGeometry> <StreamGeometry x:Key="Icons.Reset">M883 567l-128-128c-17-17-43-17-60 0l-128 128c-17 17-17 43 0 60 17 17 43 17 60 0l55-55V683c0 21-21 43-43 43H418c-13-38-43-64-77-77V375c51-17 85-64 85-119 0-73-60-128-128-128-73 0-128 55-128 128 0 55 34 102 85 119v269c-51 17-85 64-85 119 0 73 55 128 128 128 55 0 102-34 119-85H640c73 0 128-55 128-128v-111l55 55c9 9 17 13 30 13 13 0 21-4 30-13 17-13 17-43 0-55zM299 213c26 0 43 17 43 43 0 21-21 43-43 43-26 0-43-21-43-43 0-26 17-43 43-43zm0 597c-26 0-43-21-43-43 0-26 17-43 43-43s43 17 43 43c0 21-17 43-43 43zM725 384c-73 0-128-60-128-128 0-73 55-128 128-128s128 55 128 128c0 68-55 128-128 128zm0-171c-26 0-43 17-43 43s17 43 43 43 43-17 43-43-17-43-43-43z</StreamGeometry>
@ -85,7 +86,7 @@
<StreamGeometry x:Key="Icons.Stashes">M961 320 512 577 63 320 512 62l449 258zM512 628 185 442 63 512 512 770 961 512l-123-70L512 628zM512 821 185 634 63 704 512 962l449-258L839 634 512 821z</StreamGeometry> <StreamGeometry x:Key="Icons.Stashes">M961 320 512 577 63 320 512 62l449 258zM512 628 185 442 63 512 512 770 961 512l-123-70L512 628zM512 821 185 634 63 704 512 962l449-258L839 634 512 821z</StreamGeometry>
<StreamGeometry x:Key="Icons.Statistics">M447 561a26 26 0 0126 26v171H421v-171a26 26 0 0126-26zm-98 65a26 26 0 0126 26v104H323v-104a26 26 0 0126-26zm0 0M561 268a32 32 0 0132 30v457h-65V299a32 32 0 0132-32zm0 0M675 384a26 26 0 0126 26v348H649v-350a26 26 0 0126-24zm0 0M801 223v579H223V223h579M805 171H219A49 49 0 00171 219v585A49 49 0 00219 853h585A49 49 0 00853 805V219A49 49 0 00805 171z</StreamGeometry> <StreamGeometry x:Key="Icons.Statistics">M447 561a26 26 0 0126 26v171H421v-171a26 26 0 0126-26zm-98 65a26 26 0 0126 26v104H323v-104a26 26 0 0126-26zm0 0M561 268a32 32 0 0132 30v457h-65V299a32 32 0 0132-32zm0 0M675 384a26 26 0 0126 26v348H649v-350a26 26 0 0126-24zm0 0M801 223v579H223V223h579M805 171H219A49 49 0 00171 219v585A49 49 0 00219 853h585A49 49 0 00853 805V219A49 49 0 00805 171z</StreamGeometry>
<StreamGeometry x:Key="Icons.Stopwatch">M576 160H448c-18 0-32-14-32-32s14-32 32-32h128c18 0 32 14 32 32s-14 32-32 32zm243 186 36-36c13-13 13-33 0-45s-33-13-45 0l-33 33C708 233 614 192 512 192c-212 0-384 172-384 384s172 384 384 384 384-172 384-384c0-86-29-166-77-230zM544 894V864c0-18-14-32-32-32s-32 14-32 32v30C329 879 209 759 194 608H224c18 0 32-14 32-32s-14-32-32-32h-30C209 393 329 273 480 258V288c0 18 14 32 32 32s32-14 32-32v-30C695 273 815 393 830 544H800c-18 0-32 14-32 32s14 32 32 32h30C815 759 695 879 544 894zm108-471-160 128c-14 11-16 31-5 45 6 8 16 12 25 12 7 0 14-2 20-7l160-128c14-11 16-31 5-45-11-14-31-16-45-5z</StreamGeometry> <StreamGeometry x:Key="Icons.Stopwatch">M576 160H448c-18 0-32-14-32-32s14-32 32-32h128c18 0 32 14 32 32s-14 32-32 32zm243 186 36-36c13-13 13-33 0-45s-33-13-45 0l-33 33C708 233 614 192 512 192c-212 0-384 172-384 384s172 384 384 384 384-172 384-384c0-86-29-166-77-230zM544 894V864c0-18-14-32-32-32s-32 14-32 32v30C329 879 209 759 194 608H224c18 0 32-14 32-32s-14-32-32-32h-30C209 393 329 273 480 258V288c0 18 14 32 32 32s32-14 32-32v-30C695 273 815 393 830 544H800c-18 0-32 14-32 32s14 32 32 32h30C815 759 695 879 544 894zm108-471-160 128c-14 11-16 31-5 45 6 8 16 12 25 12 7 0 14-2 20-7l160-128c14-11 16-31 5-45-11-14-31-16-45-5z</StreamGeometry>
<StreamGeometry x:Key="Icons.Submodule">M557.7 545.3 789.9 402.7c24-15 31.3-46.5 16.4-70.5c-14.8-23.8-46-31.2-70-16.7L506.5 456.6 277.1 315.4c-24.1-14.8-55.6-7.3-70.5 16.8c-14.8 24.1-7.3 55.6 16.8 70.5l231.8 142.6V819.1c0 28.3 22.9 51.2 51.2 51.2c28.3 0 51.2-22.9 51.2-51.2V545.3h.1zM506.5 0l443.4 256v511.9L506.5 1023.9 63.1 767.9v-511.9L506.5 0z</StreamGeometry> <StreamGeometry x:Key="Icons.Submodule">M558 545 790 403c24-15 31-47 16-71-15-24-46-31-70-17L507 457 277 315c-24-15-56-7-71 17-15 24-7 56 17 71l232 143V819c0 28 23 51 51 51 28 0 51-23 51-51V545h0zM507 0l443 256v512L507 1024 63 768v-512L507 0z</StreamGeometry>
<StreamGeometry x:Key="Icons.Submodule.Add">M770 320a41 41 0 00-56-14l-252 153L207 306a41 41 0 10-43 70l255 153 2 296a41 41 0 0082 0l-2-295 255-155a41 41 0 0014-56zM481 935a42 42 0 01-42 0L105 741a42 42 0 01-21-36v-386a42 42 0 0121-36L439 89a42 42 0 0142 0l335 193a42 42 0 0121 36v87h84v-87a126 126 0 00-63-109L523 17a126 126 0 00-126 0L63 210a126 126 0 00-63 109v386a126 126 0 0063 109l335 193a126 126 0 00126 0l94-54-42-72zM1029 700h-126v-125a42 42 0 00-84 0v126h-126a42 42 0 000 84h126v126a42 42 0 1084 0v-126h126a42 42 0 000-84z</StreamGeometry> <StreamGeometry x:Key="Icons.Submodule.Add">M770 320a41 41 0 00-56-14l-252 153L207 306a41 41 0 10-43 70l255 153 2 296a41 41 0 0082 0l-2-295 255-155a41 41 0 0014-56zM481 935a42 42 0 01-42 0L105 741a42 42 0 01-21-36v-386a42 42 0 0121-36L439 89a42 42 0 0142 0l335 193a42 42 0 0121 36v87h84v-87a126 126 0 00-63-109L523 17a126 126 0 00-126 0L63 210a126 126 0 00-63 109v386a126 126 0 0063 109l335 193a126 126 0 00126 0l94-54-42-72zM1029 700h-126v-125a42 42 0 00-84 0v126h-126a42 42 0 000 84h126v126a42 42 0 1084 0v-126h126a42 42 0 000-84z</StreamGeometry>
<StreamGeometry x:Key="Icons.SyntaxHighlight">M875 128h-725A107 107 0 0043 235v555A107 107 0 00149 896h725a107 107 0 00107-107v-555A107 107 0 00875 128zm-115 640h-183v-58l25-3c15 0 19-8 14-24l-22-61H419l-28 82 39 2V768h-166v-58l18-3c18-2 22-11 26-24l125-363-40-4V256h168l160 448 39 3zM506 340l-72 218h145l-71-218h-2z</StreamGeometry> <StreamGeometry x:Key="Icons.SyntaxHighlight">M875 128h-725A107 107 0 0043 235v555A107 107 0 00149 896h725a107 107 0 00107-107v-555A107 107 0 00875 128zm-115 640h-183v-58l25-3c15 0 19-8 14-24l-22-61H419l-28 82 39 2V768h-166v-58l18-3c18-2 22-11 26-24l125-363-40-4V256h168l160 448 39 3zM506 340l-72 218h145l-71-218h-2z</StreamGeometry>
<StreamGeometry x:Key="Icons.Tag">M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z</StreamGeometry> <StreamGeometry x:Key="Icons.Tag">M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z</StreamGeometry>

View file

@ -96,6 +96,7 @@
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout Commit</x:String> <x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String> <x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String> <x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String>
<x:String x:Key="Text.CommitCM.CopyInfo" xml:space="preserve">Copy Info</x:String>
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">Copy SHA</x:String> <x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">Copy SHA</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Interactive Rebase ${0}$ to Here</x:String> <x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Interactive Rebase ${0}$ to Here</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebase ${0}$ to Here</x:String> <x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebase ${0}$ to Here</x:String>
@ -189,6 +190,7 @@
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">Increase Number of Visible Lines</x:String> <x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">Increase Number of Visible Lines</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SELECT FILE TO VIEW CHANGES</x:String> <x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SELECT FILE TO VIEW CHANGES</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Show hidden symbols</x:String> <x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Show hidden symbols</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Swap</x:String>
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">Open In Merge Tool</x:String> <x:String x:Key="Text.DiffWithMerger" xml:space="preserve">Open In Merge Tool</x:String>
<x:String x:Key="Text.Discard" xml:space="preserve">Discard Changes</x:String> <x:String x:Key="Text.Discard" xml:space="preserve">Discard Changes</x:String>
<x:String x:Key="Text.Discard.All" xml:space="preserve">All local changes in working copy.</x:String> <x:String x:Key="Text.Discard.All" xml:space="preserve">All local changes in working copy.</x:String>
@ -401,6 +403,7 @@
<x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">Prune worktree information in `$GIT_DIR/worktrees`</x:String> <x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">Prune worktree information in `$GIT_DIR/worktrees`</x:String>
<x:String x:Key="Text.Pull" xml:space="preserve">Pull</x:String> <x:String x:Key="Text.Pull" xml:space="preserve">Pull</x:String>
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch:</x:String> <x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Pull.FetchAllBranches" xml:space="preserve">Fetch all branches</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Into:</x:String> <x:String x:Key="Text.Pull.Into" xml:space="preserve">Into:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Local Changes:</x:String> <x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Local Changes:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Discard</x:String> <x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
@ -543,7 +546,11 @@
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">Push ${0}$...</x:String> <x:String x:Key="Text.TagCM.Push" xml:space="preserve">Push ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">URL:</x:String> <x:String x:Key="Text.URL" xml:space="preserve">URL:</x:String>
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">Update Submodules</x:String> <x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">Update Submodules</x:String>
<x:String x:Key="Text.UpdateSubmodules.Tip" xml:space="preserve">Run `submodule update` command for this repository.</x:String> <x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">All submodules</x:String>
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">Initialize as needed</x:String>
<x:String x:Key="Text.UpdateSubmodules.Recursive" xml:space="preserve">Recursively</x:String>
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">Submodule:</x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">Use --remote option</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">Warning</x:String> <x:String x:Key="Text.Warn" xml:space="preserve">Warning</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">Create Group</x:String> <x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">Create Group</x:String>
<x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">Create Sub-Group</x:String> <x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">Create Sub-Group</x:String>

View file

@ -99,6 +99,7 @@
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">检出此提交</x:String> <x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">检出此提交</x:String>
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String> <x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String> <x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String>
<x:String x:Key="Text.CommitCM.CopyInfo" xml:space="preserve">复制简要信息</x:String>
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">复制提交指纹</x:String> <x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">复制提交指纹</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">交互式变基(rebase -i) ${0}$ 到此处</x:String> <x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">交互式变基(rebase -i) ${0}$ 到此处</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">变基(rebase) ${0}$ 到此处</x:String> <x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">变基(rebase) ${0}$ 到此处</x:String>
@ -192,10 +193,11 @@
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可见的行数</x:String> <x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可见的行数</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">请选择需要对比的文件</x:String> <x:String x:Key="Text.Diff.Welcome" xml:space="preserve">请选择需要对比的文件</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">显示隐藏符号</x:String> <x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">显示隐藏符号</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交换比对双方</x:String>
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比对工具查看</x:String> <x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比对工具查看</x:String>
<x:String x:Key="Text.Discard" xml:space="preserve">放弃更改确认</x:String> <x:String x:Key="Text.Discard" xml:space="preserve">放弃更改确认</x:String>
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本地址未提交的修改。</x:String> <x:String x:Key="Text.Discard.All" xml:space="preserve">所有本地址未提交的修改。</x:String>
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">需要放弃的变更 </x:String> <x:String x:Key="Text.Discard.Changes" xml:space="preserve">变更 </x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">总计{0}项选中更改</x:String> <x:String x:Key="Text.Discard.Total" xml:space="preserve">总计{0}项选中更改</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支持回退,请确认后继续!!!</x:String> <x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支持回退,请确认后继续!!!</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">书签 </x:String> <x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">书签 </x:String>
@ -404,6 +406,7 @@
<x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">清理在`$GIT_DIR/worktrees`中的无效工作树信息</x:String> <x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">清理在`$GIT_DIR/worktrees`中的无效工作树信息</x:String>
<x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String> <x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String>
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 </x:String> <x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 </x:String>
<x:String x:Key="Text.Pull.FetchAllBranches" xml:space="preserve">拉取远程中的所有分支变更</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 </x:String> <x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 </x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 </x:String> <x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 </x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String> <x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
@ -545,7 +548,11 @@
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String> <x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">仓库地址 </x:String> <x:String x:Key="Text.URL" xml:space="preserve">仓库地址 </x:String>
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模块</x:String> <x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模块</x:String>
<x:String x:Key="Text.UpdateSubmodules.Tip" xml:space="preserve">为此仓库执行`submodule update`命令,更新所有的子模块。</x:String> <x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">更新所有子模块</x:String>
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">启用 '--init'</x:String>
<x:String x:Key="Text.UpdateSubmodules.Recursive" xml:space="preserve">启用 '--recursive'</x:String>
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">子模块 </x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">启用 '--remote'</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String> <x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新建分组</x:String> <x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新建分组</x:String>
<x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">新建子分组</x:String> <x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">新建子分组</x:String>

View file

@ -99,6 +99,7 @@
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">檢出此提交</x:String> <x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">檢出此提交</x:String>
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">與當前HEAD比較</x:String> <x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">與當前HEAD比較</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">與本地工作樹比較</x:String> <x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">與本地工作樹比較</x:String>
<x:String x:Key="Text.CommitCM.CopyInfo" xml:space="preserve">複製簡要資訊</x:String>
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">複製提交指紋</x:String> <x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">複製提交指紋</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">互動式變基(rebase -i) ${0}$ 到此處</x:String> <x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">互動式變基(rebase -i) ${0}$ 到此處</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">變基(rebase) ${0}$ 到此處</x:String> <x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">變基(rebase) ${0}$ 到此處</x:String>
@ -192,10 +193,11 @@
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可見的行數</x:String> <x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可見的行數</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">請選擇需要對比的檔案</x:String> <x:String x:Key="Text.Diff.Welcome" xml:space="preserve">請選擇需要對比的檔案</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">顯示隱藏符號</x:String> <x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">顯示隱藏符號</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交換比對雙方</x:String>
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比對工具檢視</x:String> <x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比對工具檢視</x:String>
<x:String x:Key="Text.Discard" xml:space="preserve">放棄更改確認</x:String> <x:String x:Key="Text.Discard" xml:space="preserve">放棄更改確認</x:String>
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本地址未提交的修改。</x:String> <x:String x:Key="Text.Discard.All" xml:space="preserve">所有本地址未提交的修改。</x:String>
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">需要放棄的變更 </x:String> <x:String x:Key="Text.Discard.Changes" xml:space="preserve">變更 </x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">總計{0}項選中更改</x:String> <x:String x:Key="Text.Discard.Total" xml:space="preserve">總計{0}項選中更改</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支援回退,請確認後繼續!!!</x:String> <x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支援回退,請確認後繼續!!!</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">書籤 </x:String> <x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">書籤 </x:String>
@ -404,6 +406,7 @@
<x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">清理在`$GIT_DIR/worktrees`中的無效工作樹資訊</x:String> <x:String x:Key="Text.PruneWorktrees.Tip" xml:space="preserve">清理在`$GIT_DIR/worktrees`中的無效工作樹資訊</x:String>
<x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String> <x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String>
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 </x:String> <x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 </x:String>
<x:String x:Key="Text.Pull.FetchAllBranches" xml:space="preserve">拉取遠端中的所有分支變更</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 </x:String> <x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 </x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 </x:String> <x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 </x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丟棄更改</x:String> <x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丟棄更改</x:String>
@ -545,7 +548,11 @@
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String> <x:String x:Key="Text.TagCM.Push" xml:space="preserve">推送 ${0}$...</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">倉庫地址 </x:String> <x:String x:Key="Text.URL" xml:space="preserve">倉庫地址 </x:String>
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模組</x:String> <x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">更新子模組</x:String>
<x:String x:Key="Text.UpdateSubmodules.Tip" xml:space="preserve">本操作將執行 `submodule update` 。</x:String> <x:String x:Key="Text.UpdateSubmodules.All" xml:space="preserve">更新所有子模組</x:String>
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">啟用『--init』選項</x:String>
<x:String x:Key="Text.UpdateSubmodules.Recursive" xml:space="preserve">啟用『--recursive』選項</x:String>
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">子模組 </x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">啟用『--remote』選項</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String> <x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新建分組</x:String> <x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新建分組</x:String>
<x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">新建子分組</x:String> <x:String x:Key="Text.Welcome.AddSubFolder" xml:space="preserve">新建子分組</x:String>

View file

@ -163,6 +163,27 @@
<Style Selector="ToolTip"> <Style Selector="ToolTip">
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/> <Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
<Setter Property="Background" Value="{DynamicResource Brush.Popup}"/> <Setter Property="Background" Value="{DynamicResource Brush.Popup}"/>
<Setter Property="VerticalOffset" Value="-8"/>
<Setter Property="Template">
<ControlTemplate>
<Grid Effect="drop-shadow(0 0 8 #80000000)">
<Border Margin="8"
Padding="8,6"
CornerRadius="4"
Background="{DynamicResource Brush.Popup}"
BorderThickness="0"
MaxWidth="{TemplateBinding MaxWidth}"
MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<ContentPresenter Name="PART_ContentPresenter"
MaxWidth="{TemplateBinding MaxWidth}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextBlock.TextWrapping="Wrap"/>
</Border>
</Grid>
</ControlTemplate>
</Setter>
</Style> </Style>
<Style Selector="FlyoutPresenter"> <Style Selector="FlyoutPresenter">

View file

@ -27,11 +27,11 @@
<Color x:Key="Color.FlatButton.BackgroundHovered">White</Color> <Color x:Key="Color.FlatButton.BackgroundHovered">White</Color>
<Color x:Key="Color.FG1">#FF1F1F1F</Color> <Color x:Key="Color.FG1">#FF1F1F1F</Color>
<Color x:Key="Color.FG2">#FF6F6F6F</Color> <Color x:Key="Color.FG2">#FF6F6F6F</Color>
<Color x:Key="Color.Diff.EmptyBG">#3C000000</Color> <Color x:Key="Color.Diff.EmptyBG">#10000000</Color>
<Color x:Key="Color.Diff.AddedBG">#3C00FF00</Color> <Color x:Key="Color.Diff.AddedBG">#80BFE6C1</Color>
<Color x:Key="Color.Diff.DeletedBG">#3CFF0000</Color> <Color x:Key="Color.Diff.DeletedBG">#80FF9797</Color>
<Color x:Key="Color.Diff.AddedHighlight">#5A00FF00</Color> <Color x:Key="Color.Diff.AddedHighlight">#A7E1A7</Color>
<Color x:Key="Color.Diff.DeletedHighlight">#50FF0000</Color> <Color x:Key="Color.Diff.DeletedHighlight">#F19B9D</Color>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Key="Dark"> <ResourceDictionary x:Key="Dark">
@ -43,7 +43,7 @@
<Color x:Key="Color.TitleBar">#FF1F1F1F</Color> <Color x:Key="Color.TitleBar">#FF1F1F1F</Color>
<Color x:Key="Color.ToolBar">#FF2C2C2C</Color> <Color x:Key="Color.ToolBar">#FF2C2C2C</Color>
<Color x:Key="Color.Popup">#FF2B2B2B</Color> <Color x:Key="Color.Popup">#FF2B2B2B</Color>
<Color x:Key="Color.Contents">#FF1B1B1B</Color> <Color x:Key="Color.Contents">#FF1C1C1C</Color>
<Color x:Key="Color.Badge">#FF8F8F8F</Color> <Color x:Key="Color.Badge">#FF8F8F8F</Color>
<Color x:Key="Color.BadgeFG">#FFDDDDDD</Color> <Color x:Key="Color.BadgeFG">#FFDDDDDD</Color>
<Color x:Key="Color.DecoratorIconBG">#FF505050</Color> <Color x:Key="Color.DecoratorIconBG">#FF505050</Color>
@ -61,10 +61,10 @@
<Color x:Key="Color.FG1">#FFDDDDDD</Color> <Color x:Key="Color.FG1">#FFDDDDDD</Color>
<Color x:Key="Color.FG2">#40F1F1F1</Color> <Color x:Key="Color.FG2">#40F1F1F1</Color>
<Color x:Key="Color.Diff.EmptyBG">#3C000000</Color> <Color x:Key="Color.Diff.EmptyBG">#3C000000</Color>
<Color x:Key="Color.Diff.AddedBG">#3C00FF00</Color> <Color x:Key="Color.Diff.AddedBG">#C03A5C3F</Color>
<Color x:Key="Color.Diff.DeletedBG">#3CFF0000</Color> <Color x:Key="Color.Diff.DeletedBG">#C0633F3E</Color>
<Color x:Key="Color.Diff.AddedHighlight">#5A00FF00</Color> <Color x:Key="Color.Diff.AddedHighlight">#A0308D3C</Color>
<Color x:Key="Color.Diff.DeletedHighlight">#50FF0000</Color> <Color x:Key="Color.Diff.DeletedHighlight">#A09F4247</Color>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>

View file

@ -20,6 +20,13 @@
<RepositoryType>Public</RepositoryType> <RepositoryType>Public</RepositoryType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<OptimizationPreference>Size</OptimizationPreference>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="App.ico" /> <AvaloniaResource Include="App.ico" />
<AvaloniaResource Include="Resources/ExternalToolIcons/*" /> <AvaloniaResource Include="Resources/ExternalToolIcons/*" />
@ -29,12 +36,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.10" /> <PackageReference Include="Avalonia" Version="11.1.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" /> <PackageReference Include="Avalonia.Desktop" Version="11.1.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.1" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.10" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.1" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" /> <PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" /> <PackageReference Include="Avalonia.Diagnostics" Version="11.1.1" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" /> <PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.56" /> <PackageReference Include="TextMateSharp.Grammars" Version="1.0.56" />
@ -44,4 +51,8 @@
<TrimmerRootAssembly Include="SourceGit" /> <TrimmerRootAssembly Include="SourceGit" />
<TrimmerRootAssembly Include="Avalonia.Themes.Fluent" /> <TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<LinkerArg Include="-mmacosx-version-min=11.0" Condition="$([MSBuild]::IsOSPlatform('OSX'))"/>
</ItemGroup>
</Project> </Project>

View file

@ -14,14 +14,14 @@ namespace SourceGit.ViewModels
{ {
public Models.Branch Base public Models.Branch Base
{ {
get; get => _based;
private set; private set => SetProperty(ref _based, value);
} }
public Models.Branch To public Models.Branch To
{ {
get; get => _to;
private set; private set => SetProperty(ref _to, value);
} }
public Models.Commit BaseHead public Models.Commit BaseHead
@ -50,7 +50,7 @@ namespace SourceGit.ViewModels
if (SetProperty(ref _selectedChanges, value)) if (SetProperty(ref _selectedChanges, value))
{ {
if (value != null && value.Count == 1) if (value != null && value.Count == 1)
DiffContext = new DiffContext(_repo, new Models.DiffOption(Base.Head, To.Head, value[0]), _diffContext); DiffContext = new DiffContext(_repo, new Models.DiffOption(_based.Head, _to.Head, value[0]), _diffContext);
else else
DiffContext = null; DiffContext = null;
} }
@ -78,34 +78,10 @@ namespace SourceGit.ViewModels
public BranchCompare(string repo, Models.Branch baseBranch, Models.Branch toBranch) public BranchCompare(string repo, Models.Branch baseBranch, Models.Branch toBranch)
{ {
_repo = repo; _repo = repo;
_based = baseBranch;
_to = toBranch;
Base = baseBranch; Refresh();
To = toBranch;
Task.Run(() =>
{
var baseHead = new Commands.QuerySingleCommit(_repo, Base.Head).Result();
var toHead = new Commands.QuerySingleCommit(_repo, To.Head).Result();
_changes = new Commands.CompareRevisions(_repo, Base.Head, To.Head).Result();
var visible = _changes;
if (!string.IsNullOrWhiteSpace(_searchFilter))
{
visible = new List<Models.Change>();
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
}
Dispatcher.UIThread.Invoke(() =>
{
BaseHead = baseHead;
ToHead = toHead;
VisibleChanges = visible;
});
});
} }
public void NavigateTo(string commitSHA) public void NavigateTo(string commitSHA)
@ -114,6 +90,17 @@ namespace SourceGit.ViewModels
repo?.NavigateToCommit(commitSHA); repo?.NavigateToCommit(commitSHA);
} }
public void Swap()
{
(Base, To) = (_to, _based);
SelectedChanges = [];
if (_baseHead != null)
(BaseHead, ToHead) = (_toHead, _baseHead);
Refresh();
}
public void ClearSearchFilter() public void ClearSearchFilter()
{ {
SearchFilter = string.Empty; SearchFilter = string.Empty;
@ -134,7 +121,7 @@ namespace SourceGit.ViewModels
{ {
var toolType = Preference.Instance.ExternalMergeToolType; var toolType = Preference.Instance.ExternalMergeToolType;
var toolPath = Preference.Instance.ExternalMergeToolPath; var toolPath = Preference.Instance.ExternalMergeToolPath;
var opt = new Models.DiffOption(Base.Head, To.Head, change); var opt = new Models.DiffOption(_based.Head, _to.Head, change);
Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, toolType, toolPath, opt)); Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, toolType, toolPath, opt));
ev.Handled = true; ev.Handled = true;
@ -179,6 +166,38 @@ namespace SourceGit.ViewModels
return menu; return menu;
} }
private void Refresh()
{
Task.Run(() =>
{
if (_baseHead == null)
{
var baseHead = new Commands.QuerySingleCommit(_repo, _based.Head).Result();
var toHead = new Commands.QuerySingleCommit(_repo, _to.Head).Result();
Dispatcher.UIThread.Invoke(() =>
{
BaseHead = baseHead;
ToHead = toHead;
});
}
_changes = new Commands.CompareRevisions(_repo, _based.Head, _to.Head).Result();
var visible = _changes;
if (!string.IsNullOrWhiteSpace(_searchFilter))
{
visible = new List<Models.Change>();
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
}
Dispatcher.UIThread.Invoke(() => VisibleChanges = visible);
});
}
private void RefreshVisible() private void RefreshVisible()
{ {
if (_changes == null) if (_changes == null)
@ -202,6 +221,8 @@ namespace SourceGit.ViewModels
} }
private string _repo; private string _repo;
private Models.Branch _based = null;
private Models.Branch _to = null;
private Models.Commit _baseHead = null; private Models.Commit _baseHead = null;
private Models.Commit _toHead = null; private Models.Commit _toHead = null;
private List<Models.Change> _changes = null; private List<Models.Change> _changes = null;

View file

@ -3,6 +3,8 @@ using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Threading;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class Clone : Popup public class Clone : Popup
@ -51,26 +53,23 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _extraArgs, value); set => SetProperty(ref _extraArgs, value);
} }
public Clone(Launcher launcher) public Clone()
{ {
_launcher = launcher;
_page = launcher.ActivePage;
View = new Views.Clone() { DataContext = this }; View = new Views.Clone() { DataContext = this };
App.GetClipboardTextAsync()
.ContinueWith(t => Task.Run(async () =>
{ {
if (t.IsFaulted) try
{ {
t.Exception.Handle(static _ => true); var text = await App.GetClipboardTextAsync();
if (Models.Remote.IsValidURL(text))
{
Dispatcher.UIThread.Invoke(() => Remote = text);
} }
else if (t.IsCompleted)
{
var result = t.Result;
if (Models.Remote.IsValidURL(result))
{
Remote = result;
} }
catch
{
// ignore
} }
}); });
} }
@ -131,15 +130,24 @@ namespace SourceGit.ViewModels
{ {
var normalizedPath = path.Replace("\\", "/"); var normalizedPath = path.Replace("\\", "/");
var node = Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, null, true); var node = Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, null, true);
_launcher.OpenRepositoryInTab(node, _page); var launcher = App.GetLauncer();
var page = null as LauncherPage;
foreach (var one in launcher.Pages)
{
if (one.GetId() == HostPageId)
{
page = one;
break;
}
}
launcher.OpenRepositoryInTab(node, page);
}); });
return true; return true;
}); });
} }
private readonly Launcher _launcher = null;
private readonly LauncherPage _page = null;
private string _remote = string.Empty; private string _remote = string.Empty;
private bool _useSSH = false; private bool _useSSH = false;
private string _sshKey = string.Empty; private string _sshKey = string.Empty;

View file

@ -129,7 +129,11 @@ namespace SourceGit.ViewModels
Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision); Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision);
} }
CallUIThread(() => _repo.SetWatcherEnabled(true)); CallUIThread(() =>
{
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
});
return true; return true;
}); });
} }

View file

@ -12,7 +12,7 @@ namespace SourceGit.ViewModels
} }
[Required(ErrorMessage = "Tag name is required!")] [Required(ErrorMessage = "Tag name is required!")]
[RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad tag name format!")] [RegularExpression(@"^[^/]{1}[\w\-\./]*$", ErrorMessage = "Bad tag name format!")]
[CustomValidation(typeof(CreateTag), nameof(ValidateTagName))] [CustomValidation(typeof(CreateTag), nameof(ValidateTagName))]
public string TagName public string TagName
{ {

View file

@ -65,7 +65,11 @@ namespace SourceGit.ViewModels
Commands.Branch.DeleteRemote(_repo.FullPath, Target.Remote, Target.Name); Commands.Branch.DeleteRemote(_repo.FullPath, Target.Remote, Target.Name);
} }
CallUIThread(() => _repo.SetWatcherEnabled(true)); CallUIThread(() =>
{
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
});
return true; return true;
}); });
} }

View file

@ -3,10 +3,6 @@ using System.Threading.Tasks;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class DiscardModeAll { }
public class DiscardModeSingle { public string File { get; set; } }
public class DiscardModeMulti { public int Count { get; set; } }
public class Discard : Popup public class Discard : Popup
{ {
public object Mode public object Mode
@ -19,7 +15,7 @@ namespace SourceGit.ViewModels
{ {
_repo = repo; _repo = repo;
Mode = new DiscardModeAll(); Mode = new Models.Null();
View = new Views.Discard { DataContext = this }; View = new Views.Discard { DataContext = this };
} }
@ -30,17 +26,11 @@ namespace SourceGit.ViewModels
_isUnstaged = isUnstaged; _isUnstaged = isUnstaged;
if (_changes == null) if (_changes == null)
{ Mode = new Models.Null();
Mode = new DiscardModeAll();
}
else if (_changes.Count == 1) else if (_changes.Count == 1)
{ Mode = _changes[0].Path;
Mode = new DiscardModeSingle() { File = _changes[0].Path };
}
else else
{ Mode = _changes.Count;
Mode = new DiscardModeMulti() { Count = _changes.Count };
}
View = new Views.Discard() { DataContext = this }; View = new Views.Discard() { DataContext = this };
} }

View file

@ -9,11 +9,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class CountSelectedCommits
{
public int Count { get; set; }
}
public class Histories : ObservableObject public class Histories : ObservableObject
{ {
public bool IsLoading public bool IsLoading
@ -143,7 +138,7 @@ namespace SourceGit.ViewModels
else else
{ {
_repo.SearchResultSelectedCommit = null; _repo.SearchResultSelectedCommit = null;
DetailContext = new CountSelectedCommits() { Count = commits.Count }; DetailContext = commits.Count;
} }
} }
@ -152,7 +147,7 @@ namespace SourceGit.ViewModels
if (datagrid.SelectedItems.Count != 1) if (datagrid.SelectedItems.Count != 1)
return null; return null;
var current = _repo.Branches.Find(x => x.IsCurrent); var current = _repo.CurrentBranch;
if (current == null) if (current == null)
return null; return null;
@ -435,6 +430,17 @@ namespace SourceGit.ViewModels
e.Handled = true; e.Handled = true;
}; };
menu.Items.Add(copySHA); menu.Items.Add(copySHA);
var copyInfo = new MenuItem();
copyInfo.Header = App.Text("CommitCM.CopyInfo");
copyInfo.Icon = App.CreateMenuIcon("Icons.Copy");
copyInfo.Click += (_, e) =>
{
App.CopyText($"{commit.SHA.Substring(0, 10)} - {commit.Subject}");
e.Handled = true;
};
menu.Items.Add(copyInfo);
return menu; return menu;
} }

View file

@ -38,7 +38,7 @@ namespace SourceGit.ViewModels
var root = new Commands.QueryRepositoryRootPath(startupRepo).Result(); var root = new Commands.QueryRepositoryRootPath(startupRepo).Result();
if (string.IsNullOrEmpty(root)) if (string.IsNullOrEmpty(root))
{ {
Pages[0].Notifications.Add(new Notification Pages[0].Notifications.Add(new Models.Notification
{ {
IsError = true, IsError = true,
Message = $"Given path: '{startupRepo}' is NOT a valid repository!" Message = $"Given path: '{startupRepo}' is NOT a valid repository!"
@ -272,7 +272,7 @@ namespace SourceGit.ViewModels
public void DispatchNotification(string pageId, string message, bool isError) public void DispatchNotification(string pageId, string message, bool isError)
{ {
var notification = new Notification() var notification = new Models.Notification()
{ {
IsError = isError, IsError = isError,
Message = message, Message = message,

View file

@ -18,11 +18,11 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _data, value); set => SetProperty(ref _data, value);
} }
public AvaloniaList<Notification> Notifications public AvaloniaList<Models.Notification> Notifications
{ {
get; get;
set; set;
} = new AvaloniaList<Notification>(); } = new AvaloniaList<Models.Notification>();
public LauncherPage() public LauncherPage()
{ {

View file

@ -1,22 +1,7 @@
using System.Collections.Generic; using System.Threading.Tasks;
using System.Threading.Tasks;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class MergeMode
{
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public MergeMode(string n, string d, string a)
{
Name = n;
Desc = d;
Arg = a;
}
}
public class Merge : Popup public class Merge : Popup
{ {
public string Source public string Source
@ -31,13 +16,7 @@ namespace SourceGit.ViewModels
private set; private set;
} }
public List<MergeMode> Modes public Models.MergeMode SelectedMode
{
get;
private set;
}
public MergeMode SelectedMode
{ {
get; get;
set; set;
@ -48,13 +27,7 @@ namespace SourceGit.ViewModels
_repo = repo; _repo = repo;
Source = source; Source = source;
Into = into; Into = into;
Modes = new List<MergeMode>() { SelectedMode = Models.MergeMode.Supported[0];
new MergeMode("Default", "Fast-forward if possible", ""),
new MergeMode("No Fast-forward", "Always create a merge commit", "--no-ff"),
new MergeMode("Squash", "Use '--squash'", "--squash"),
new MergeMode("Don't commit", "Merge without commit", "--no-commit"),
};
SelectedMode = Modes[0];
View = new Views.Merge() { DataContext = this }; View = new Views.Merge() { DataContext = this };
} }

View file

@ -59,6 +59,12 @@ namespace SourceGit.ViewModels
set => _repo.Settings.PreferRebaseInsteadOfMerge = value; set => _repo.Settings.PreferRebaseInsteadOfMerge = value;
} }
public bool FetchAllBranches
{
get => _repo.Settings.FetchAllBranchesOnPull;
set => _repo.Settings.FetchAllBranchesOnPull = value;
}
public bool NoTags public bool NoTags
{ {
get => _repo.Settings.FetchWithoutTagsOnPull; get => _repo.Settings.FetchWithoutTagsOnPull;
@ -68,7 +74,7 @@ namespace SourceGit.ViewModels
public Pull(Repository repo, Models.Branch specifiedRemoteBranch) public Pull(Repository repo, Models.Branch specifiedRemoteBranch)
{ {
_repo = repo; _repo = repo;
_current = repo.Branches.Find(x => x.IsCurrent); _current = repo.CurrentBranch;
if (specifiedRemoteBranch != null) if (specifiedRemoteBranch != null)
{ {
@ -151,8 +157,32 @@ namespace SourceGit.ViewModels
} }
} }
var rs = false;
if (FetchAllBranches)
{
SetProgressDescription($"Fetching remote: {_selectedRemote.Name}...");
rs = new Commands.Fetch(_repo.FullPath, _selectedRemote.Name, false, NoTags, SetProgressDescription).Exec();
if (!rs)
return false;
// Use merge/rebase instead of pull as fetch is done manually.
if (UseRebase)
{
SetProgressDescription($"Rebase {_current.Name} on {_selectedBranch.FriendlyName} ...");
rs = new Commands.Rebase(_repo.FullPath, _selectedBranch.FriendlyName, false).Exec();
}
else
{
SetProgressDescription($"Merge {_selectedBranch.FriendlyName} into {_current.Name} ...");
rs = new Commands.Merge(_repo.FullPath, _selectedBranch.FriendlyName, "", SetProgressDescription).Exec();
}
}
else
{
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}..."); SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
var rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, NoTags, SetProgressDescription).Exec(); rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, NoTags, SetProgressDescription).Exec();
}
if (rs && needPopStash) if (rs && needPopStash)
{ {
SetProgressDescription("Re-apply local changes..."); SetProgressDescription("Re-apply local changes...");

View file

@ -4,7 +4,6 @@ using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@ -14,93 +13,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class RepositorySettings
{
public Models.DealWithLocalChanges DealWithLocalChangesOnCheckoutBranch
{
get;
set;
} = Models.DealWithLocalChanges.DoNothing;
public bool FetchWithoutTags
{
get;
set;
} = false;
public Models.DealWithLocalChanges DealWithLocalChangesOnPull
{
get;
set;
} = Models.DealWithLocalChanges.DoNothing;
public bool PreferRebaseInsteadOfMerge
{
get;
set;
} = true;
public bool FetchWithoutTagsOnPull
{
get;
set;
} = false;
public bool PushAllTags
{
get;
set;
} = false;
public Models.DealWithLocalChanges DealWithLocalChangesOnCreateBranch
{
get;
set;
} = Models.DealWithLocalChanges.DoNothing;
public bool CheckoutBranchOnCreateBranch
{
get;
set;
} = true;
public bool AutoStageBeforeCommit
{
get;
set;
} = false;
public AvaloniaList<string> Filters
{
get;
set;
} = new AvaloniaList<string>();
public AvaloniaList<string> CommitMessages
{
get;
set;
} = new AvaloniaList<string>();
public void PushCommitMessage(string message)
{
var existIdx = CommitMessages.IndexOf(message);
if (existIdx == 0)
return;
if (existIdx > 0)
{
CommitMessages.Move(existIdx, 0);
return;
}
if (CommitMessages.Count > 9)
CommitMessages.RemoveRange(9, CommitMessages.Count - 9);
CommitMessages.Insert(0, message);
}
}
public class Repository : ObservableObject, Models.IRepository public class Repository : ObservableObject, Models.IRepository
{ {
public string FullPath public string FullPath
@ -126,7 +38,7 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _gitDir, value); set => SetProperty(ref _gitDir, value);
} }
public RepositorySettings Settings public Models.RepositorySettings Settings
{ {
get => _settings; get => _settings;
} }
@ -187,6 +99,12 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _branches, value); private set => SetProperty(ref _branches, value);
} }
public Models.Branch CurrentBranch
{
get => _currentBranch;
private set => SetProperty(ref _currentBranch, value);
}
public List<BranchTreeNode> LocalBranchTrees public List<BranchTreeNode> LocalBranchTrees
{ {
get => _localBranchTrees; get => _localBranchTrees;
@ -341,12 +259,12 @@ namespace SourceGit.ViewModels
} }
catch catch
{ {
_settings = new RepositorySettings(); _settings = new Models.RepositorySettings();
} }
} }
else else
{ {
_settings = new RepositorySettings(); _settings = new Models.RepositorySettings();
} }
_watcher = new Models.Watcher(this); _watcher = new Models.Watcher(this);
@ -363,7 +281,7 @@ namespace SourceGit.ViewModels
public void Close() public void Close()
{ {
SelectedView = 0.0; // Do NOT modify. Used to remove exists widgets for GC.Collect SelectedView = null; // Do NOT modify. Used to remove exists widgets for GC.Collect
var settingsSerialized = JsonSerializer.Serialize(_settings, JsonCodeGen.Default.RepositorySettings); var settingsSerialized = JsonSerializer.Serialize(_settings, JsonCodeGen.Default.RepositorySettings);
File.WriteAllText(Path.Combine(_gitDir, "sourcegit.settings"), settingsSerialized); File.WriteAllText(Path.Combine(_gitDir, "sourcegit.settings"), settingsSerialized);
@ -452,7 +370,7 @@ namespace SourceGit.ViewModels
if (!PopupHost.CanCreatePopup()) if (!PopupHost.CanCreatePopup())
return; return;
if (Remotes.Count == 0) if (_remotes.Count == 0)
{ {
App.RaiseException(_fullpath, "No remotes added to this repository!!!"); App.RaiseException(_fullpath, "No remotes added to this repository!!!");
return; return;
@ -466,7 +384,7 @@ namespace SourceGit.ViewModels
if (!PopupHost.CanCreatePopup()) if (!PopupHost.CanCreatePopup())
return; return;
if (Remotes.Count == 0) if (_remotes.Count == 0)
{ {
App.RaiseException(_fullpath, "No remotes added to this repository!!!"); App.RaiseException(_fullpath, "No remotes added to this repository!!!");
return; return;
@ -480,13 +398,13 @@ namespace SourceGit.ViewModels
if (!PopupHost.CanCreatePopup()) if (!PopupHost.CanCreatePopup())
return; return;
if (Remotes.Count == 0) if (_remotes.Count == 0)
{ {
App.RaiseException(_fullpath, "No remotes added to this repository!!!"); App.RaiseException(_fullpath, "No remotes added to this repository!!!");
return; return;
} }
if (Branches.Find(x => x.IsCurrent) == null) if (_currentBranch == null)
{ {
App.RaiseException(_fullpath, "Can NOT found current branch!!!"); App.RaiseException(_fullpath, "Can NOT found current branch!!!");
return; return;
@ -509,13 +427,6 @@ namespace SourceGit.ViewModels
PopupHost.ShowAndStartPopup(new Cleanup(this)); PopupHost.ShowAndStartPopup(new Cleanup(this));
} }
public void OpenConfigure()
{
if (!PopupHost.CanCreatePopup())
return;
PopupHost.ShowPopup(new RepositoryConfigure(this));
}
public void ClearHistoriesFilter() public void ClearHistoriesFilter()
{ {
_settings.Filters.Clear(); _settings.Filters.Clear();
@ -566,10 +477,10 @@ namespace SourceGit.ViewModels
break; break;
case 2: case 2:
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter, false).Result(); visible = new Commands.QueryCommits(_fullpath, 1000, _searchCommitFilter, false).Result();
break; break;
case 3: case 3:
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter, true).Result(); visible = new Commands.QueryCommits(_fullpath, 1000, _searchCommitFilter, true).Result();
break; break;
} }
@ -615,9 +526,8 @@ namespace SourceGit.ViewModels
public void NavigateToCurrentHead() public void NavigateToCurrentHead()
{ {
var cur = Branches.Find(x => x.IsCurrent); if (_currentBranch != null)
if (cur != null) NavigateToCommit(_currentBranch.Head);
NavigateToCommit(cur.Head);
} }
public void UpdateFilter(string filter, bool toggle) public void UpdateFilter(string filter, bool toggle)
@ -695,22 +605,20 @@ namespace SourceGit.ViewModels
public void RefreshBranches() public void RefreshBranches()
{ {
var branches = new Commands.QueryBranches(FullPath).Result(); var branches = new Commands.QueryBranches(_fullpath).Result();
var remotes = new Commands.QueryRemotes(FullPath).Result(); var remotes = new Commands.QueryRemotes(_fullpath).Result();
var builder = BuildBranchTree(branches, remotes); var builder = BuildBranchTree(branches, remotes);
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.Invoke(() =>
{ {
Remotes = remotes; Remotes = remotes;
Branches = branches; Branches = branches;
CurrentBranch = branches.Find(x => x.IsCurrent);
LocalBranchTrees = builder.Locals; LocalBranchTrees = builder.Locals;
RemoteBranchTrees = builder.Remotes; RemoteBranchTrees = builder.Remotes;
if (_workingCopy != null) if (_workingCopy != null)
{ _workingCopy.CanCommitWithPush = _currentBranch != null && !string.IsNullOrEmpty(_currentBranch.Upstream);
var cur = Branches.Find(x => x.IsCurrent);
_workingCopy.CanCommitWithPush = cur != null && !string.IsNullOrEmpty(cur.Upstream);
}
}); });
} }
@ -735,7 +643,7 @@ namespace SourceGit.ViewModels
public void RefreshTags() public void RefreshTags()
{ {
var tags = new Commands.QueryTags(FullPath).Result(); var tags = new Commands.QueryTags(_fullpath).Result();
foreach (var tag in tags) foreach (var tag in tags)
tag.IsFiltered = _settings.Filters.Contains(tag.Name); tag.IsFiltered = _settings.Filters.Contains(tag.Name);
@ -786,7 +694,7 @@ namespace SourceGit.ViewModels
var canPushCommits = new HashSet<string>(); var canPushCommits = new HashSet<string>();
var canPullCommits = new HashSet<string>(); var canPullCommits = new HashSet<string>();
var currentBranch = Branches.Find(x => x.IsCurrent); var currentBranch = _branches.Find(x => x.IsCurrent);
if (currentBranch != null) if (currentBranch != null)
{ {
foreach (var sha in currentBranch.TrackStatus.Ahead) foreach (var sha in currentBranch.TrackStatus.Ahead)
@ -812,13 +720,13 @@ namespace SourceGit.ViewModels
public void RefreshSubmodules() public void RefreshSubmodules()
{ {
var submodules = new Commands.QuerySubmodules(FullPath).Result(); var submodules = new Commands.QuerySubmodules(_fullpath).Result();
Dispatcher.UIThread.Invoke(() => Submodules = submodules); Dispatcher.UIThread.Invoke(() => Submodules = submodules);
} }
public void RefreshWorkingCopyChanges() public void RefreshWorkingCopyChanges()
{ {
var changes = new Commands.QueryLocalChanges(FullPath, _includeUntracked).Result(); var changes = new Commands.QueryLocalChanges(_fullpath, _includeUntracked).Result();
if (_workingCopy == null) if (_workingCopy == null)
return; return;
@ -862,7 +770,7 @@ namespace SourceGit.ViewModels
public void RefreshStashes() public void RefreshStashes()
{ {
var stashes = new Commands.QueryStashes(FullPath).Result(); var stashes = new Commands.QueryStashes(_fullpath).Result();
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.Invoke(() =>
{ {
if (_stashesPage != null) if (_stashesPage != null)
@ -873,15 +781,14 @@ namespace SourceGit.ViewModels
public void CreateNewBranch() public void CreateNewBranch()
{ {
var current = Branches.Find(x => x.IsCurrent); if (_currentBranch == null)
if (current == null)
{ {
App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit."); App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit.");
return; return;
} }
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new CreateBranch(this, current)); PopupHost.ShowPopup(new CreateBranch(this, _currentBranch));
} }
public void CheckoutBranch(Models.Branch branch) public void CheckoutBranch(Models.Branch branch)
@ -908,7 +815,7 @@ namespace SourceGit.ViewModels
} }
else else
{ {
foreach (var b in Branches) foreach (var b in _branches)
{ {
if (b.IsLocal && b.Upstream == branch.FullName) if (b.IsLocal && b.Upstream == branch.FullName)
{ {
@ -931,15 +838,14 @@ namespace SourceGit.ViewModels
public void CreateNewTag() public void CreateNewTag()
{ {
var current = Branches.Find(x => x.IsCurrent); if (_currentBranch == null)
if (current == null)
{ {
App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit."); App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit.");
return; return;
} }
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new CreateTag(this, current)); PopupHost.ShowPopup(new CreateTag(this, _currentBranch));
} }
public void AddRemote() public void AddRemote()
@ -957,7 +863,7 @@ namespace SourceGit.ViewModels
public void UpdateSubmodules() public void UpdateSubmodules()
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowAndStartPopup(new UpdateSubmodules(this)); PopupHost.ShowPopup(new UpdateSubmodules(this));
} }
public void OpenSubmodule(string submodule) public void OpenSubmodule(string submodule)
@ -1095,12 +1001,12 @@ namespace SourceGit.ViewModels
var fetch = new MenuItem(); var fetch = new MenuItem();
fetch.Header = App.Text("GitLFS.Fetch"); fetch.Header = App.Text("GitLFS.Fetch");
fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); fetch.Icon = App.CreateMenuIcon("Icons.Fetch");
fetch.IsEnabled = Remotes.Count > 0; fetch.IsEnabled = _remotes.Count > 0;
fetch.Click += (_, e) => fetch.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
{ {
if (Remotes.Count == 1) if (_remotes.Count == 1)
PopupHost.ShowAndStartPopup(new LFSFetch(this)); PopupHost.ShowAndStartPopup(new LFSFetch(this));
else else
PopupHost.ShowPopup(new LFSFetch(this)); PopupHost.ShowPopup(new LFSFetch(this));
@ -1113,12 +1019,12 @@ namespace SourceGit.ViewModels
var pull = new MenuItem(); var pull = new MenuItem();
pull.Header = App.Text("GitLFS.Pull"); pull.Header = App.Text("GitLFS.Pull");
pull.Icon = App.CreateMenuIcon("Icons.Pull"); pull.Icon = App.CreateMenuIcon("Icons.Pull");
pull.IsEnabled = Remotes.Count > 0; pull.IsEnabled = _remotes.Count > 0;
pull.Click += (_, e) => pull.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
{ {
if (Remotes.Count == 1) if (_remotes.Count == 1)
PopupHost.ShowAndStartPopup(new LFSPull(this)); PopupHost.ShowAndStartPopup(new LFSPull(this));
else else
PopupHost.ShowPopup(new LFSPull(this)); PopupHost.ShowPopup(new LFSPull(this));
@ -1131,12 +1037,12 @@ namespace SourceGit.ViewModels
var push = new MenuItem(); var push = new MenuItem();
push.Header = App.Text("GitLFS.Push"); push.Header = App.Text("GitLFS.Push");
push.Icon = App.CreateMenuIcon("Icons.Push"); push.Icon = App.CreateMenuIcon("Icons.Push");
push.IsEnabled = Remotes.Count > 0; push.IsEnabled = _remotes.Count > 0;
push.Click += (_, e) => push.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
{ {
if (Remotes.Count == 1) if (_remotes.Count == 1)
PopupHost.ShowAndStartPopup(new LFSPush(this)); PopupHost.ShowAndStartPopup(new LFSPush(this));
else else
PopupHost.ShowPopup(new LFSPush(this)); PopupHost.ShowPopup(new LFSPush(this));
@ -1162,8 +1068,8 @@ namespace SourceGit.ViewModels
var locks = new MenuItem(); var locks = new MenuItem();
locks.Header = App.Text("GitLFS.Locks"); locks.Header = App.Text("GitLFS.Locks");
locks.Icon = App.CreateMenuIcon("Icons.Lock"); locks.Icon = App.CreateMenuIcon("Icons.Lock");
locks.IsEnabled = Remotes.Count > 0; locks.IsEnabled = _remotes.Count > 0;
if (Remotes.Count == 1) if (_remotes.Count == 1)
{ {
locks.Click += (_, e) => locks.Click += (_, e) =>
{ {
@ -1171,14 +1077,14 @@ namespace SourceGit.ViewModels
if (topLevel == null) if (topLevel == null)
return; return;
var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, Remotes[0].Name) }; var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, _remotes[0].Name) };
dialog.Show(topLevel); dialog.Show(topLevel);
e.Handled = true; e.Handled = true;
}; };
} }
else else
{ {
foreach (var remote in Remotes) foreach (var remote in _remotes)
{ {
var remoteName = remote.Name; var remoteName = remote.Name;
var lockRemote = new MenuItem(); var lockRemote = new MenuItem();
@ -1226,7 +1132,7 @@ namespace SourceGit.ViewModels
var push = new MenuItem(); var push = new MenuItem();
push.Header = new Views.NameHighlightedTextBlock("BranchCM.Push", branch.Name); push.Header = new Views.NameHighlightedTextBlock("BranchCM.Push", branch.Name);
push.Icon = App.CreateMenuIcon("Icons.Push"); push.Icon = App.CreateMenuIcon("Icons.Push");
push.IsEnabled = Remotes.Count > 0; push.IsEnabled = _remotes.Count > 0;
push.Click += (_, e) => push.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
@ -1289,8 +1195,6 @@ namespace SourceGit.ViewModels
} }
else else
{ {
var current = Branches.Find(x => x.IsCurrent);
var checkout = new MenuItem(); var checkout = new MenuItem();
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name);
checkout.Icon = App.CreateMenuIcon("Icons.Check"); checkout.Icon = App.CreateMenuIcon("Icons.Check");
@ -1301,7 +1205,7 @@ namespace SourceGit.ViewModels
}; };
menu.Items.Add(checkout); menu.Items.Add(checkout);
var upstream = Branches.Find(x => x.FullName == branch.Upstream); var upstream = _branches.Find(x => x.FullName == branch.Upstream);
if (upstream != null) if (upstream != null)
{ {
var fastForward = new MenuItem(); var fastForward = new MenuItem();
@ -1323,22 +1227,22 @@ namespace SourceGit.ViewModels
menu.Items.Add(push); menu.Items.Add(push);
var merge = new MenuItem(); var merge = new MenuItem();
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name); merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, _currentBranch.Name);
merge.Icon = App.CreateMenuIcon("Icons.Merge"); merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.Click += (_, e) => merge.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name)); PopupHost.ShowPopup(new Merge(this, branch.Name, _currentBranch.Name));
e.Handled = true; e.Handled = true;
}; };
var rebase = new MenuItem(); var rebase = new MenuItem();
rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, branch.Name); rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", _currentBranch.Name, branch.Name);
rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
rebase.Click += (_, e) => rebase.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new Rebase(this, current, branch)); PopupHost.ShowPopup(new Rebase(this, _currentBranch, branch));
e.Handled = true; e.Handled = true;
}; };
@ -1356,9 +1260,9 @@ namespace SourceGit.ViewModels
if (_histories != null) if (_histories != null)
{ {
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); var target = new Commands.QuerySingleCommit(_fullpath, branch.Head).Result();
_histories.AutoSelectedCommit = null; _histories.AutoSelectedCommit = null;
_histories.DetailContext = new RevisionCompare(FullPath, target, null); _histories.DetailContext = new RevisionCompare(_fullpath, target, null);
} }
}; };
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
@ -1441,7 +1345,7 @@ namespace SourceGit.ViewModels
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
var remoteBranches = new List<Models.Branch>(); var remoteBranches = new List<Models.Branch>();
foreach (var b in Branches) foreach (var b in _branches)
{ {
if (!b.IsLocal) if (!b.IsLocal)
remoteBranches.Add(b); remoteBranches.Add(b);
@ -1593,7 +1497,6 @@ namespace SourceGit.ViewModels
public ContextMenu CreateContextMenuForRemoteBranch(Models.Branch branch) public ContextMenu CreateContextMenuForRemoteBranch(Models.Branch branch)
{ {
var menu = new ContextMenu(); var menu = new ContextMenu();
var current = Branches.Find(x => x.IsCurrent);
var name = branch.FriendlyName; var name = branch.FriendlyName;
var checkout = new MenuItem(); var checkout = new MenuItem();
@ -1607,10 +1510,10 @@ namespace SourceGit.ViewModels
menu.Items.Add(checkout); menu.Items.Add(checkout);
menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(new MenuItem() { Header = "-" });
if (current != null) if (_currentBranch != null)
{ {
var pull = new MenuItem(); var pull = new MenuItem();
pull.Header = new Views.NameHighlightedTextBlock("BranchCM.PullInto", name, current.Name); pull.Header = new Views.NameHighlightedTextBlock("BranchCM.PullInto", name, _currentBranch.Name);
pull.Icon = App.CreateMenuIcon("Icons.Pull"); pull.Icon = App.CreateMenuIcon("Icons.Pull");
pull.Click += (_, e) => pull.Click += (_, e) =>
{ {
@ -1620,22 +1523,22 @@ namespace SourceGit.ViewModels
}; };
var merge = new MenuItem(); var merge = new MenuItem();
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name); merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, _currentBranch.Name);
merge.Icon = App.CreateMenuIcon("Icons.Merge"); merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.Click += (_, e) => merge.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new Merge(this, name, current.Name)); PopupHost.ShowPopup(new Merge(this, name, _currentBranch.Name));
e.Handled = true; e.Handled = true;
}; };
var rebase = new MenuItem(); var rebase = new MenuItem();
rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, name); rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", _currentBranch.Name, name);
rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
rebase.Click += (_, e) => rebase.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new Rebase(this, current, branch)); PopupHost.ShowPopup(new Rebase(this, _currentBranch, branch));
e.Handled = true; e.Handled = true;
}; };
@ -1657,9 +1560,9 @@ namespace SourceGit.ViewModels
if (_histories != null) if (_histories != null)
{ {
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); var target = new Commands.QuerySingleCommit(_fullpath, branch.Head).Result();
_histories.AutoSelectedCommit = null; _histories.AutoSelectedCommit = null;
_histories.DetailContext = new RevisionCompare(FullPath, target, null); _histories.DetailContext = new RevisionCompare(_fullpath, target, null);
} }
}; };
menu.Items.Add(compareWithWorktree); menu.Items.Add(compareWithWorktree);
@ -1751,7 +1654,7 @@ namespace SourceGit.ViewModels
var pushTag = new MenuItem(); var pushTag = new MenuItem();
pushTag.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name); pushTag.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name);
pushTag.Icon = App.CreateMenuIcon("Icons.Push"); pushTag.Icon = App.CreateMenuIcon("Icons.Push");
pushTag.IsEnabled = Remotes.Count > 0; pushTag.IsEnabled = _remotes.Count > 0;
pushTag.Click += (_, ev) => pushTag.Click += (_, ev) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
@ -1901,14 +1804,14 @@ namespace SourceGit.ViewModels
private MenuItem CreateMenuItemToCompareBranches(Models.Branch branch) private MenuItem CreateMenuItemToCompareBranches(Models.Branch branch)
{ {
if (Branches.Count == 1) if (_branches.Count == 1)
return null; return null;
var compare = new MenuItem(); var compare = new MenuItem();
compare.Header = App.Text("BranchCM.CompareWithBranch"); compare.Header = App.Text("BranchCM.CompareWithBranch");
compare.Icon = App.CreateMenuIcon("Icons.Compare"); compare.Icon = App.CreateMenuIcon("Icons.Compare");
foreach (var b in Branches) foreach (var b in _branches)
{ {
if (b.FullName != branch.FullName) if (b.FullName != branch.FullName)
{ {
@ -1924,7 +1827,7 @@ namespace SourceGit.ViewModels
var wnd = new Views.BranchCompare() var wnd = new Views.BranchCompare()
{ {
DataContext = new BranchCompare(FullPath, branch, dup) DataContext = new BranchCompare(_fullpath, branch, dup)
}; };
wnd.Show(topLevel); wnd.Show(topLevel);
@ -1985,7 +1888,7 @@ namespace SourceGit.ViewModels
private string _fullpath = string.Empty; private string _fullpath = string.Empty;
private string _gitDir = string.Empty; private string _gitDir = string.Empty;
private RepositorySettings _settings = null; private Models.RepositorySettings _settings = null;
private Models.Watcher _watcher = null; private Models.Watcher _watcher = null;
private Histories _histories = null; private Histories _histories = null;
@ -2010,6 +1913,7 @@ namespace SourceGit.ViewModels
private List<Models.Remote> _remotes = new List<Models.Remote>(); private List<Models.Remote> _remotes = new List<Models.Remote>();
private List<Models.Branch> _branches = new List<Models.Branch>(); private List<Models.Branch> _branches = new List<Models.Branch>();
private Models.Branch _currentBranch = null;
private List<BranchTreeNode> _localBranchTrees = new List<BranchTreeNode>(); private List<BranchTreeNode> _localBranchTrees = new List<BranchTreeNode>();
private List<BranchTreeNode> _remoteBranchTrees = new List<BranchTreeNode>(); private List<BranchTreeNode> _remoteBranchTrees = new List<BranchTreeNode>();
private List<Models.Worktree> _worktrees = new List<Models.Worktree>(); private List<Models.Worktree> _worktrees = new List<Models.Worktree>();

View file

@ -1,9 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class RepositoryConfigure : Popup public class RepositoryConfigure : ObservableObject
{ {
public string UserName public string UserName
{ {
@ -37,8 +37,8 @@ namespace SourceGit.ViewModels
public string HttpProxy public string HttpProxy
{ {
get; get => _httpProxy;
set; set => SetProperty(ref _httpProxy, value);
} }
public RepositoryConfigure(Repository repo) public RepositoryConfigure(Repository repo)
@ -58,11 +58,14 @@ namespace SourceGit.ViewModels
GPGUserSigningKey = signingKey; GPGUserSigningKey = signingKey;
if (_cached.TryGetValue("http.proxy", out var proxy)) if (_cached.TryGetValue("http.proxy", out var proxy))
HttpProxy = proxy; HttpProxy = proxy;
View = new Views.RepositoryConfigure() { DataContext = this };
} }
public override Task<bool> Sure() public void ClearHttpProxy()
{
HttpProxy = string.Empty;
}
public void Save()
{ {
SetIfChanged("user.name", UserName); SetIfChanged("user.name", UserName);
SetIfChanged("user.email", UserEmail); SetIfChanged("user.email", UserEmail);
@ -70,7 +73,6 @@ namespace SourceGit.ViewModels
SetIfChanged("tag.gpgsign", GPGTagSigningEnabled ? "true" : "false"); SetIfChanged("tag.gpgsign", GPGTagSigningEnabled ? "true" : "false");
SetIfChanged("user.signingkey", GPGUserSigningKey); SetIfChanged("user.signingkey", GPGUserSigningKey);
SetIfChanged("http.proxy", HttpProxy); SetIfChanged("http.proxy", HttpProxy);
return null;
} }
private void SetIfChanged(string key, string value) private void SetIfChanged(string key, string value)
@ -93,5 +95,6 @@ namespace SourceGit.ViewModels
private readonly Repository _repo = null; private readonly Repository _repo = null;
private readonly Dictionary<string, string> _cached = null; private readonly Dictionary<string, string> _cached = null;
private string _httpProxy;
} }
} }

View file

@ -1,26 +1,7 @@
using System.Collections.Generic; using System.Threading.Tasks;
using System.Threading.Tasks;
using Avalonia.Media;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class ResetMode
{
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public IBrush Color { get; set; }
public ResetMode(string n, string d, string a, IBrush b)
{
Name = n;
Desc = d;
Arg = a;
Color = b;
}
}
public class Reset : Popup public class Reset : Popup
{ {
public Models.Branch Current public Models.Branch Current
@ -35,13 +16,7 @@ namespace SourceGit.ViewModels
private set; private set;
} }
public List<ResetMode> Modes public Models.ResetMode SelectedMode
{
get;
private set;
}
public ResetMode SelectedMode
{ {
get; get;
set; set;
@ -52,12 +27,7 @@ namespace SourceGit.ViewModels
_repo = repo; _repo = repo;
Current = current; Current = current;
To = to; To = to;
Modes = new List<ResetMode>() { SelectedMode = Models.ResetMode.Supported[0];
new ResetMode("Soft", "Keep all changes. Stage differences", "--soft", Brushes.Green),
new ResetMode("Mixed", "Keep all changes. Unstage differences", "--mixed", Brushes.Orange),
new ResetMode("Hard", "Discard all changes", "--hard", Brushes.Red),
};
SelectedMode = Modes[0];
View = new Views.Reset() { DataContext = this }; View = new Views.Reset() { DataContext = this };
} }

View file

@ -10,23 +10,18 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class CompareTargetWorktree
{
public string SHA => string.Empty;
}
public class RevisionCompare : ObservableObject public class RevisionCompare : ObservableObject
{ {
public Models.Commit StartPoint public object StartPoint
{ {
get; get => _startPoint;
private set; private set => SetProperty(ref _startPoint, value);
} }
public object EndPoint public object EndPoint
{ {
get; get => _endPoint;
private set; private set => SetProperty(ref _endPoint, value);
} }
public List<Models.Change> VisibleChanges public List<Models.Change> VisibleChanges
@ -43,12 +38,17 @@ namespace SourceGit.ViewModels
if (SetProperty(ref _selectedChanges, value)) if (SetProperty(ref _selectedChanges, value))
{ {
if (value != null && value.Count == 1) if (value != null && value.Count == 1)
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, _endPoint, value[0]), _diffContext); {
var option = new Models.DiffOption(GetSHA(_startPoint), GetSHA(_endPoint), value[0]);
DiffContext = new DiffContext(_repo, option, _diffContext);
}
else else
{
DiffContext = null; DiffContext = null;
} }
} }
} }
}
public string SearchFilter public string SearchFilter
{ {
@ -71,41 +71,17 @@ namespace SourceGit.ViewModels
public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint) public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint)
{ {
_repo = repo; _repo = repo;
StartPoint = startPoint; _startPoint = (object)startPoint ?? new Models.Null();
_endPoint = (object)endPoint ?? new Models.Null();
if (endPoint == null) Task.Run(Refresh);
{
EndPoint = new CompareTargetWorktree();
_endPoint = string.Empty;
}
else
{
EndPoint = endPoint;
_endPoint = endPoint.SHA;
}
Task.Run(() =>
{
_changes = new Commands.CompareRevisions(_repo, startPoint.SHA, _endPoint).Result();
var visible = _changes;
if (!string.IsNullOrWhiteSpace(_searchFilter))
{
visible = new List<Models.Change>();
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
}
Dispatcher.UIThread.Invoke(() => VisibleChanges = visible);
});
} }
public void Cleanup() public void Cleanup()
{ {
_repo = null; _repo = null;
_startPoint = null;
_endPoint = null;
if (_changes != null) if (_changes != null)
_changes.Clear(); _changes.Clear();
if (_visibleChanges != null) if (_visibleChanges != null)
@ -122,6 +98,13 @@ namespace SourceGit.ViewModels
repo?.NavigateToCommit(commitSHA); repo?.NavigateToCommit(commitSHA);
} }
public void Swap()
{
(StartPoint, EndPoint) = (_endPoint, _startPoint);
SelectedChanges = [];
Task.Run(Refresh);
}
public void ClearSearchFilter() public void ClearSearchFilter()
{ {
SearchFilter = string.Empty; SearchFilter = string.Empty;
@ -140,7 +123,7 @@ namespace SourceGit.ViewModels
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
diffWithMerger.Click += (_, ev) => diffWithMerger.Click += (_, ev) =>
{ {
var opt = new Models.DiffOption(StartPoint.SHA, _endPoint, change); var opt = new Models.DiffOption(GetSHA(_startPoint), GetSHA(_endPoint), change);
var toolType = Preference.Instance.ExternalMergeToolType; var toolType = Preference.Instance.ExternalMergeToolType;
var toolPath = Preference.Instance.ExternalMergeToolPath; var toolPath = Preference.Instance.ExternalMergeToolPath;
@ -209,8 +192,32 @@ namespace SourceGit.ViewModels
} }
} }
private void Refresh()
{
_changes = new Commands.CompareRevisions(_repo, GetSHA(_startPoint), GetSHA(_endPoint)).Result();
var visible = _changes;
if (!string.IsNullOrWhiteSpace(_searchFilter))
{
visible = [];
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
}
Dispatcher.UIThread.Invoke(() => VisibleChanges = visible);
}
private string GetSHA(object obj)
{
return obj is Models.Commit commit ? commit.SHA : string.Empty;
}
private string _repo; private string _repo;
private string _endPoint; private object _startPoint = null;
private object _endPoint = null;
private List<Models.Change> _changes = null; private List<Models.Change> _changes = null;
private List<Models.Change> _visibleChanges = null; private List<Models.Change> _visibleChanges = null;
private List<Models.Change> _selectedChanges = null; private List<Models.Change> _selectedChanges = null;

View file

@ -1,28 +1,82 @@
using System.Threading.Tasks; using System.Collections.Generic;
using System.Threading.Tasks;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class UpdateSubmodules : Popup public class UpdateSubmodules : Popup
{ {
public List<string> Submodules
{
get => _repo.Submodules;
}
public string SelectedSubmodule
{
get;
set;
}
public bool UpdateAll
{
get => _updateAll;
set => SetProperty(ref _updateAll, value);
}
public bool EnableInit
{
get;
set;
} = true;
public bool EnableRecursive
{
get;
set;
} = true;
public bool EnableRemote
{
get;
set;
} = false;
public UpdateSubmodules(Repository repo) public UpdateSubmodules(Repository repo)
{ {
_repo = repo; _repo = repo;
SelectedSubmodule = repo.Submodules.Count > 0 ? repo.Submodules[0] : string.Empty;
View = new Views.UpdateSubmodules() { DataContext = this }; View = new Views.UpdateSubmodules() { DataContext = this };
} }
public override Task<bool> Sure() public override Task<bool> Sure()
{ {
_repo.SetWatcherEnabled(false); _repo.SetWatcherEnabled(false);
string target = string.Empty;
if (_updateAll)
{
ProgressDescription = "Updating submodules ..."; ProgressDescription = "Updating submodules ...";
}
else
{
target = SelectedSubmodule;
ProgressDescription = $"Updating submodule {target} ...";
}
return Task.Run(() => return Task.Run(() =>
{ {
new Commands.Submodule(_repo.FullPath).Update(SetProgressDescription); new Commands.Submodule(_repo.FullPath).Update(
target,
EnableInit,
EnableRecursive,
EnableRemote,
SetProgressDescription);
CallUIThread(() => _repo.SetWatcherEnabled(true)); CallUIThread(() => _repo.SetWatcherEnabled(true));
return true; return true;
}); });
} }
private readonly Repository _repo = null; private readonly Repository _repo = null;
private bool _updateAll = true;
} }
} }

View file

@ -1,9 +1,7 @@
using System; using System;
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -50,24 +48,17 @@ namespace SourceGit.ViewModels
return; return;
} }
if (PopupHost.CanCreatePopup() && if (PopupHost.CanCreatePopup())
Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } }) PopupHost.ShowPopup(new Clone());
{
PopupHost.ShowPopup(new Clone(launcher));
}
} }
public void OpenTerminal() public void OpenTerminal()
{ {
if (!Preference.Instance.IsGitConfigured()) if (!Preference.Instance.IsGitConfigured())
{
App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured")); App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured"));
}
else else
{
Native.OS.OpenTerminal(null); Native.OS.OpenTerminal(null);
} }
}
public void ClearSearchFilter() public void ClearSearchFilter()
{ {
@ -96,12 +87,7 @@ namespace SourceGit.ViewModels
openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open"); openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open");
openAll.Click += (_, e) => openAll.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup() && OpenAllInNode(App.GetLauncer(), node);
Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } })
{
OpenAllInNode(launcher, node);
}
e.Handled = true; e.Handled = true;
}; };

View file

@ -90,7 +90,7 @@ namespace SourceGit.ViewModels
{ {
if (SetProperty(ref _useAmend, value) && value) if (SetProperty(ref _useAmend, value) && value)
{ {
var currentBranch = _repo.Branches.Find(x => x.IsCurrent); var currentBranch = _repo.CurrentBranch;
if (currentBranch == null) if (currentBranch == null)
{ {
App.RaiseException(_repo.FullPath, "No commits to amend!!!"); App.RaiseException(_repo.FullPath, "No commits to amend!!!");
@ -137,6 +137,7 @@ namespace SourceGit.ViewModels
} }
else else
{ {
if (_selectedStaged != null && _selectedStaged.Count > 0)
SelectedStaged = []; SelectedStaged = [];
if (value.Count == 1) if (value.Count == 1)
@ -162,6 +163,7 @@ namespace SourceGit.ViewModels
} }
else else
{ {
if (_selectedUnstaged != null && _selectedUnstaged.Count > 0)
SelectedUnstaged = []; SelectedUnstaged = [];
if (value.Count == 1) if (value.Count == 1)

View file

@ -9,7 +9,6 @@
x:DataType="vm:Blame" x:DataType="vm:Blame"
Icon="/App.ico" Icon="/App.ico"
Title="{DynamicResource Text.Blame}" Title="{DynamicResource Text.Blame}"
WindowStartupLocation="CenterOwner"
MinWidth="1280" MinHeight="720"> MinWidth="1280" MinHeight="720">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>

View file

@ -4,7 +4,6 @@ using System.Globalization;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
@ -326,11 +325,6 @@ namespace SourceGit.Views
{ {
public Blame() public Blame()
{ {
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
Owner = desktop.MainWindow;
}
InitializeComponent(); InitializeComponent();
} }

View file

@ -69,7 +69,10 @@
</Grid> </Grid>
</Border> </Border>
<Path Grid.Column="1" Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Down}" RenderTransformOrigin="50%,50%" RenderTransform="rotate(270deg)"/> <!-- Swap Button -->
<Button Grid.Column="1" Classes="icon_button" Command="{Binding Swap}" HorizontalAlignment="Center" ToolTip.Tip="{DynamicResource Text.Diff.SwapCommits}">
<Path Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Compare}"/>
</Button>
<Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4"> <Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,*">

View file

@ -174,8 +174,9 @@ namespace SourceGit.Views
return; return;
_disableSelectionChangingEvent = true; _disableSelectionChangingEvent = true;
var selected = new List<Models.Change>(); var selected = new List<Models.Change>();
if (sender is ListBox { SelectedItems: not null } list) if (sender is ListBox list)
{ {
foreach (var item in list.SelectedItems) foreach (var item in list.SelectedItems)
{ {
@ -186,7 +187,27 @@ namespace SourceGit.Views
} }
} }
TrySetSelected(selected); var old = SelectedChanges ?? [];
if (old.Count != selected.Count)
{
SetCurrentValue(SelectedChangesProperty, selected);
}
else
{
bool allEquals = true;
foreach (var c in old)
{
if (!selected.Contains(c))
{
allEquals = false;
break;
}
}
if (!allEquals)
SetCurrentValue(SelectedChangesProperty, selected);
}
_disableSelectionChangingEvent = false; _disableSelectionChangingEvent = false;
} }
@ -330,33 +351,6 @@ namespace SourceGit.Views
} }
} }
private void TrySetSelected(List<Models.Change> changes)
{
var old = SelectedChanges;
if (old == null && changes.Count == 0)
return;
if (old != null && old.Count == changes.Count)
{
bool allEquals = true;
foreach (var c in old)
{
if (!changes.Contains(c))
{
allEquals = false;
break;
}
}
if (allEquals)
return;
}
_disableSelectionChangingEvent = true;
SetCurrentValue(SelectedChangesProperty, changes);
_disableSelectionChangingEvent = false;
}
private bool _disableSelectionChangingEvent = false; private bool _disableSelectionChangingEvent = false;
} }
} }

View file

@ -54,7 +54,7 @@
<Grid RowDefinitions="24,Auto,Auto,Auto" ColumnDefinitions="96,*"> <Grid RowDefinitions="24,Auto,Auto,Auto" ColumnDefinitions="96,*">
<!-- SHA --> <!-- SHA -->
<TextBlock Grid.Row="0" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.SHA}" /> <TextBlock Grid.Row="0" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.SHA}" />
<SelectableTextBlock Grid.Row="0" Grid.Column="1" Text="{Binding SHA}" Margin="12,0,0,0" VerticalAlignment="Center" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/> <SelectableTextBlock Grid.Row="0" Grid.Column="1" Classes="monospace" Text="{Binding SHA}" Margin="12,0,0,0" VerticalAlignment="Center"/>
<!-- PARENTS --> <!-- PARENTS -->
<TextBlock Grid.Row="1" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Parents}" IsVisible="{Binding Parents.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/> <TextBlock Grid.Row="1" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Parents}" IsVisible="{Binding Parents.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
@ -78,33 +78,21 @@
</ItemsControl> </ItemsControl>
<!-- REFS --> <!-- REFS -->
<TextBlock Grid.Row="2" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Refs}" IsVisible="{Binding Decorators.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/> <TextBlock Grid.Row="2" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Refs}" IsVisible="{Binding HasDecorators}"/>
<ItemsControl Grid.Row="2" Grid.Column="1" Height="24" Margin="12,0,0,0" ItemsSource="{Binding Decorators}" IsVisible="{Binding Decorators.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"> <Border Grid.Row="2" Grid.Column="1" Margin="12,0,0,0" Height="24" IsVisible="{Binding HasDecorators}">
<ItemsControl.ItemsPanel> <v:CommitRefsPresenter IconBackground="{DynamicResource Brush.DecoratorIconBG}"
<ItemsPanelTemplate> IconForeground="{DynamicResource Brush.DecoratorIcon}"
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"/> BranchNameBackground="{DynamicResource Brush.DecoratorBranch}"
</ItemsPanelTemplate> TagNameBackground="{DynamicResource Brush.DecoratorTag}"
</ItemsControl.ItemsPanel> LabelForeground="{DynamicResource Brush.DecoratorFG}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
<ItemsControl.ItemTemplate> FontSize="10"
<DataTemplate DataType="{x:Type m:Decorator}"> VerticalAlignment="Center"/>
<Border Height="16" Margin="0,0,6,0" CornerRadius="2" ClipToBounds="True">
<StackPanel Orientation="Horizontal">
<Border Background="{DynamicResource Brush.DecoratorIconBG}" Width="16">
<Path Width="8" Height="8" Data="{Binding Type, Converter={x:Static c:DecoratorTypeConverters.ToIcon}}" Fill="{DynamicResource Brush.DecoratorIcon}"/>
</Border> </Border>
<Border Background="{Binding Type, Converter={x:Static c:DecoratorTypeConverters.ToBackground}}">
<TextBlock Classes="monospace" Text="{Binding Name}" FontSize="10" Margin="4,0" Foreground="{DynamicResource Brush.DecoratorFG}"/>
</Border>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Messages --> <!-- Messages -->
<TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" /> <TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" />
<SelectableTextBlock Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" Text="{Binding #ThisControl.Message}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/> <SelectableTextBlock Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" Classes="monospace" Text="{Binding #ThisControl.Message}" TextWrapping="Wrap"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>

View file

@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace SourceGit.Views
{
public class CommitRefsPresenter : Control
{
public class RenderItem
{
public Geometry Icon { get; set; } = null;
public FormattedText Label { get; set; } = null;
public bool IsTag { get; set; } = false;
}
public static readonly StyledProperty<FontFamily> FontFamilyProperty =
TextBlock.FontFamilyProperty.AddOwner<CommitRefsPresenter>();
public FontFamily FontFamily
{
get => GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
public static readonly StyledProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<CommitRefsPresenter>();
public double FontSize
{
get => GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly StyledProperty<IBrush> IconBackgroundProperty =
AvaloniaProperty.Register<CommitRefsPresenter, IBrush>(nameof(IconBackground), Brushes.White);
public IBrush IconBackground
{
get => GetValue(IconBackgroundProperty);
set => SetValue(IconBackgroundProperty, value);
}
public static readonly StyledProperty<IBrush> IconForegroundProperty =
AvaloniaProperty.Register<CommitRefsPresenter, IBrush>(nameof(IconForeground), Brushes.White);
public IBrush IconForeground
{
get => GetValue(IconForegroundProperty);
set => SetValue(IconForegroundProperty, value);
}
public static readonly StyledProperty<IBrush> LabelForegroundProperty =
AvaloniaProperty.Register<CommitRefsPresenter, IBrush>(nameof(LabelForeground), Brushes.White);
public IBrush LabelForeground
{
get => GetValue(LabelForegroundProperty);
set => SetValue(LabelForegroundProperty, value);
}
public static readonly StyledProperty<IBrush> BranchNameBackgroundProperty =
AvaloniaProperty.Register<CommitRefsPresenter, IBrush>(nameof(BranchNameBackground), Brushes.White);
public IBrush BranchNameBackground
{
get => GetValue(BranchNameBackgroundProperty);
set => SetValue(BranchNameBackgroundProperty, value);
}
public static readonly StyledProperty<IBrush> TagNameBackgroundProperty =
AvaloniaProperty.Register<CommitRefsPresenter, IBrush>(nameof(TagNameBackground), Brushes.White);
public IBrush TagNameBackground
{
get => GetValue(TagNameBackgroundProperty);
set => SetValue(TagNameBackgroundProperty, value);
}
static CommitRefsPresenter()
{
AffectsMeasure<CommitRefsPresenter>(
FontFamilyProperty,
FontSizeProperty,
LabelForegroundProperty);
AffectsRender<CommitRefsPresenter>(
IconBackgroundProperty,
IconForegroundProperty,
BranchNameBackgroundProperty,
TagNameBackgroundProperty);
}
public override void Render(DrawingContext context)
{
if (_items.Count == 0)
return;
var iconFG = IconForeground;
var iconBG = IconBackground;
var branchBG = BranchNameBackground;
var tagBG = TagNameBackground;
var x = 0.0;
foreach (var item in _items)
{
var iconRect = new RoundedRect(new Rect(x, 0, 16, 16), new CornerRadius(2, 0, 0, 2));
var labelRect = new RoundedRect(new Rect(x + 16, 0, item.Label.Width + 8, 16), new CornerRadius(0, 2, 2, 0));
context.DrawRectangle(iconBG, null, iconRect);
context.DrawRectangle(item.IsTag ? tagBG : branchBG, null, labelRect);
context.DrawText(item.Label, new Point(x + 20, 8.0 - item.Label.Height * 0.5));
using (context.PushTransform(Matrix.CreateTranslation(x + 4, 4)))
context.DrawGeometry(iconFG, null, item.Icon);
x += item.Label.Width + 16 + 8 + 4;
}
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
InvalidateMeasure();
}
protected override Size MeasureOverride(Size availableSize)
{
_items.Clear();
if (DataContext is Models.Commit commit && commit.HasDecorators)
{
var typeface = new Typeface(FontFamily);
var typefaceBold = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold);
var labelFG = LabelForeground;
var labelSize = FontSize;
var requiredWidth = 0.0;
foreach (var decorator in commit.Decorators)
{
var isHead = decorator.Type == Models.DecoratorType.CurrentBranchHead ||
decorator.Type == Models.DecoratorType.CurrentCommitHead;
var label = new FormattedText(
decorator.Name,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
isHead ? typefaceBold : typeface,
labelSize,
labelFG);
var item = new RenderItem()
{
Label = label,
IsTag = decorator.Type == Models.DecoratorType.Tag,
};
var geo = null as StreamGeometry;
switch (decorator.Type)
{
case Models.DecoratorType.CurrentBranchHead:
case Models.DecoratorType.CurrentCommitHead:
geo = this.FindResource("Icons.Check") as StreamGeometry;
break;
case Models.DecoratorType.RemoteBranchHead:
geo = this.FindResource("Icons.Remote") as StreamGeometry;
break;
case Models.DecoratorType.Tag:
geo = this.FindResource("Icons.Tag") as StreamGeometry;
break;
default:
geo = this.FindResource("Icons.Branch") as StreamGeometry;
break;
}
var drawGeo = geo.Clone();
var iconBounds = drawGeo.Bounds;
var translation = Matrix.CreateTranslation(-(Vector)iconBounds.Position);
var scale = Math.Min(8.0 / iconBounds.Width, 8.0 / iconBounds.Height);
var transform = translation * Matrix.CreateScale(scale, scale);
if (drawGeo.Transform == null || drawGeo.Transform.Value == Matrix.Identity)
drawGeo.Transform = new MatrixTransform(transform);
else
drawGeo.Transform = new MatrixTransform(drawGeo.Transform.Value * transform);
item.Icon = drawGeo;
_items.Add(item);
requiredWidth += label.Width + 16 /* icon */ + 8 /* label margin */ + 4 /* item right margin */;
}
return new Size(requiredWidth, 16);
}
return new Size(0, 0);
}
private List<RenderItem> _items = new List<RenderItem>();
}
}

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters" xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
@ -12,28 +13,28 @@
Classes="bold" Classes="bold"
Text="{DynamicResource Text.Discard}"/> Text="{DynamicResource Text.Discard}"/>
<Grid Margin="0,16,0,8" RowDefinitions="32,32" ColumnDefinitions="150,*"> <Grid Margin="0,16,0,8" RowDefinitions="32,32" ColumnDefinitions="120,*">
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,8,0" HorizontalAlignment="Right" Text="{DynamicResource Text.Discard.Changes}"/> <TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,8,0" HorizontalAlignment="Right" Text="{DynamicResource Text.Discard.Changes}"/>
<ContentControl Grid.Row="0" Grid.Column="1" Content="{Binding Mode}"> <ContentControl Grid.Row="0" Grid.Column="1" Content="{Binding Mode}">
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate DataType="vm:DiscardModeAll"> <DataTemplate DataType="m:Null">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Margin="0,2,0,0" Data="{StaticResource Icons.Folder.Open}"/> <Path Width="12" Height="12" Margin="0,2,0,0" Data="{StaticResource Icons.Folder.Open}"/>
<TextBlock Text="{DynamicResource Text.Discard.All}" Margin="4,0,0,0"/> <TextBlock Text="{DynamicResource Text.Discard.All}" Margin="4,0,0,0"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="vm:DiscardModeSingle"> <DataTemplate DataType="x:String">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Data="{StaticResource Icons.File}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.File}"/>
<TextBlock Text="{Binding File}" Margin="4,0,0,0"/> <TextBlock Text="{Binding}" Margin="4,0,0,0"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="vm:DiscardModeMulti"> <DataTemplate DataType="x:Int32">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Data="{StaticResource Icons.File}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.File}"/>
<TextBlock Text="{Binding Count, Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Discard.Total'}" Margin="4,0,0,0"/> <TextBlock Text="{Binding Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Discard.Total'}" Margin="4,0,0,0"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ContentControl.DataTemplates> </ContentControl.DataTemplates>

View file

@ -33,26 +33,16 @@
<DataGrid.Styles> <DataGrid.Styles>
<Style Selector="DataGridColumnHeader"> <Style Selector="DataGridColumnHeader">
<Setter Property="MinHeight" Value="24"/> <Setter Property="MinHeight" Value="24"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border Background="{DynamicResource Brush.Window}" <Border Background="{DynamicResource Brush.Window}"
BorderBrush="{DynamicResource Brush.Border0}" BorderBrush="{DynamicResource Brush.Border0}"
BorderThickness="0,0,0,1"> BorderThickness="0,0,1,1">
<Grid ColumnDefinitions="*,1"> <ContentPresenter x:Name="PART_ContentPresenter"
<ContentControl x:Name="PART_ContentPresenter"
Grid.Column="0"
Margin="4,0"
Content="{TemplateBinding Content}" Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Center"/>
HorizontalContentAlignment="Center"/>
<Rectangle Name="VerticalSeparator"
Grid.Column="1"
Width="1"
VerticalAlignment="Stretch"
Fill="{DynamicResource Brush.Border0}"
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
</Grid>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
@ -79,37 +69,15 @@
Fill="{DynamicResource Brush.FG1}" Fill="{DynamicResource Brush.FG1}"
IsVisible="{Binding CanPullFromUpstream}"/> IsVisible="{Binding CanPullFromUpstream}"/>
<ItemsControl ItemsSource="{Binding Decorators}" IsVisible="{Binding HasDecorators}"> <v:CommitRefsPresenter IsVisible="{Binding HasDecorators}"
<ItemsControl.ItemsPanel> IconBackground="{DynamicResource Brush.DecoratorIconBG}"
<ItemsPanelTemplate> IconForeground="{DynamicResource Brush.DecoratorIcon}"
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center"/> BranchNameBackground="{DynamicResource Brush.DecoratorBranch}"
</ItemsPanelTemplate> TagNameBackground="{DynamicResource Brush.DecoratorTag}"
</ItemsControl.ItemsPanel> LabelForeground="{DynamicResource Brush.DecoratorFG}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type m:Decorator}">
<Border Height="16" Margin="0,0,4,0" CornerRadius="2" ClipToBounds="True">
<StackPanel Orientation="Horizontal">
<Border Background="{DynamicResource Brush.DecoratorIconBG}" Width="16">
<Path Width="8" Height="8"
Stretch="Uniform"
Data="{Binding Type, Converter={x:Static c:DecoratorTypeConverters.ToIcon}}"
Fill="{DynamicResource Brush.DecoratorIcon}"
VerticalAlignment="Center"/>
</Border>
<Border Background="{Binding Type, Converter={x:Static c:DecoratorTypeConverters.ToBackground}}">
<TextBlock Classes="monospace"
Text="{Binding Name}"
FontSize="10" FontSize="10"
Margin="4,0" VerticalAlignment="Center"/>
Foreground="{DynamicResource Brush.DecoratorFG}"
FontWeight="{Binding Type, Converter={x:Static c:DecoratorTypeConverters.ToFontWeight}}"/>
</Border>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Classes="monospace" <TextBlock Classes="monospace"
Text="{Binding Subject}" Text="{Binding Subject}"
@ -169,7 +137,7 @@
<ToggleButton Classes="time_display_mode" <ToggleButton Classes="time_display_mode"
Width="10" Height="10" Width="10" Height="10"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=DisplayTimeAsPeriodInHistories, Mode=TwoWay}"/> IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=DisplayTimeAsPeriodInHistories, Mode=TwoWay}"/>
<TextBlock Classes="table_header" Margin="4,0,0,0" Text="{DynamicResource Text.Histories.Header.Time}"/> <TextBlock Classes="table_header" Margin="6,0,0,0" Text="{DynamicResource Text.Histories.Header.Time}"/>
</StackPanel> </StackPanel>
</DataGridTemplateColumn.Header> </DataGridTemplateColumn.Header>
@ -224,7 +192,7 @@
<v:RevisionCompare/> <v:RevisionCompare/>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="vm:CountSelectedCommits"> <DataTemplate DataType="x:Int32">
<Grid Background="{DynamicResource Brush.Window}"> <Grid Background="{DynamicResource Brush.Window}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Width="128" Height="128" <Path Width="128" Height="128"
@ -236,7 +204,7 @@
Margin="0,16" Margin="0,16"
FontSize="24" FontWeight="Bold" FontSize="24" FontWeight="Bold"
Foreground="{DynamicResource Brush.FG2}" Foreground="{DynamicResource Brush.FG2}"
Text="{Binding Count, Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Histories.Selected'}"/> Text="{Binding Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Histories.Selected'}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</DataTemplate> </DataTemplate>

View file

@ -371,6 +371,9 @@ namespace SourceGit.Views
{ {
NavigationIdProperty.Changed.AddClassHandler<Histories>((h, _) => NavigationIdProperty.Changed.AddClassHandler<Histories>((h, _) =>
{ {
if (h.DataContext == null)
return;
// Force scroll selected item (current head) into view. see issue #58 // Force scroll selected item (current head) into view. see issue #58
var datagrid = h.CommitDataGrid; var datagrid = h.CommitDataGrid;
if (datagrid != null && datagrid.SelectedItems.Count == 1) if (datagrid != null && datagrid.SelectedItems.Count == 1)

View file

@ -11,9 +11,7 @@ namespace SourceGit.Views
public Launcher() public Launcher()
{ {
var layout = ViewModels.Preference.Instance.Layout; var layout = ViewModels.Preference.Instance.Layout;
WindowState = layout.LauncherWindowState; if (layout.LauncherWindowState != WindowState.Maximized)
if (WindowState != WindowState.Maximized)
{ {
Width = layout.LauncherWidth; Width = layout.LauncherWidth;
Height = layout.LauncherHeight; Height = layout.LauncherHeight;
@ -22,6 +20,15 @@ namespace SourceGit.Views
InitializeComponent(); InitializeComponent();
} }
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
var layout = ViewModels.Preference.Instance.Layout;
if (layout.LauncherWindowState == WindowState.Maximized)
WindowState = WindowState.Maximized;
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:v="using:SourceGit.Views" xmlns:v="using:SourceGit.Views"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
@ -86,7 +87,7 @@
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Notifications}"> <ItemsControl ItemsSource="{Binding Notifications}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:Notification"> <DataTemplate DataType="m:Notification">
<Border Margin="10,6" HorizontalAlignment="Stretch" VerticalAlignment="Top" Effect="drop-shadow(0 0 12 #A0000000)"> <Border Margin="10,6" HorizontalAlignment="Stretch" VerticalAlignment="Top" Effect="drop-shadow(0 0 12 #A0000000)">
<Border Padding="8" CornerRadius="6" Background="{DynamicResource Brush.Popup}"> <Border Padding="8" CornerRadius="6" Background="{DynamicResource Brush.Popup}">
<Grid RowDefinitions="26,Auto,32"> <Grid RowDefinitions="26,Auto,32">

View file

@ -34,7 +34,7 @@ namespace SourceGit.Views
private void OnDismissNotification(object sender, RoutedEventArgs e) private void OnDismissNotification(object sender, RoutedEventArgs e)
{ {
if (sender is Button { DataContext: ViewModels.Notification notice } && if (sender is Button { DataContext: Models.Notification notice } &&
DataContext is ViewModels.LauncherPage page) DataContext is ViewModels.LauncherPage page)
page.Notifications.Remove(notice); page.Notifications.Remove(notice);

View file

@ -58,7 +58,7 @@ namespace SourceGit.Views
var geo = new StreamGeometry(); var geo = new StreamGeometry();
var angle = Math.PI / 2; var angle = Math.PI / 2;
var y = height + 0.25; var y = height + 0.1;
using (var ctx = geo.Open()) using (var ctx = geo.Open())
{ {
double x; double x;
@ -94,17 +94,17 @@ namespace SourceGit.Views
x = drawRightX; x = drawRightX;
y = 6; y = 6;
ctx.ArcTo(new Point(x, y), new Size(6, 6), angle, false, SweepDirection.Clockwise); ctx.ArcTo(new Point(x, y), new Size(6, 6), angle, false, SweepDirection.Clockwise);
y = height + 0.25 - 5; y = height + 0.1 - 5;
ctx.LineTo(new Point(x, y)); ctx.LineTo(new Point(x, y));
x += 5; x += 5;
y = height + 0.25; y = height + 0.1;
ctx.ArcTo(new Point(x, y), new Size(5, 5), angle, false, SweepDirection.CounterClockwise); ctx.ArcTo(new Point(x, y), new Size(5, 5), angle, false, SweepDirection.CounterClockwise);
} }
else else
{ {
x = LauncherTabsScroller.Bounds.Right; x = LauncherTabsScroller.Bounds.Right;
ctx.LineTo(new Point(x, y)); ctx.LineTo(new Point(x, y));
y = height + 0.25; y = height + 0.1;
ctx.LineTo(new Point(x, y)); ctx.LineTo(new Point(x, y));
} }
} }

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Merge" x:Class="SourceGit.Views.Merge"
@ -36,10 +37,10 @@
<ComboBox Grid.Row="2" Grid.Column="1" <ComboBox Grid.Row="2" Grid.Column="1"
Height="28" Padding="8,0" Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Modes}" ItemsSource="{Binding Source={x:Static m:MergeMode.Supported}}"
SelectedItem="{Binding SelectedMode, Mode=TwoWay}"> SelectedItem="{Binding SelectedMode, Mode=TwoWay}">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate DataType="vm:MergeMode"> <DataTemplate DataType="m:MergeMode">
<StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Desc}" Margin="8,0,0,0" FontSize="11" Foreground="{DynamicResource Brush.FG2}"/> <TextBlock Text="{Binding Desc}" Margin="8,0,0,0" FontSize="11" Foreground="{DynamicResource Brush.FG2}"/>

View file

@ -12,7 +12,7 @@
<TextBlock FontSize="18" <TextBlock FontSize="18"
Classes="bold" Classes="bold"
Text="{DynamicResource Text.Pull.Title}"/> Text="{DynamicResource Text.Pull.Title}"/>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="140,*"> <Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32,32,32" ColumnDefinitions="140,*">
<TextBlock Grid.Row="0" Grid.Column="0" <TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0" Margin="0,0,8,0"
@ -85,12 +85,16 @@
</StackPanel> </StackPanel>
<CheckBox Grid.Row="4" Grid.Column="1" <CheckBox Grid.Row="4" Grid.Column="1"
Content="{DynamicResource Text.Pull.UseRebase}" Content="{DynamicResource Text.Pull.FetchAllBranches}"
IsChecked="{Binding UseRebase, Mode=TwoWay}"/> IsChecked="{Binding FetchAllBranches, Mode=TwoWay}"/>
<CheckBox Grid.Row="5" Grid.Column="1" <CheckBox Grid.Row="5" Grid.Column="1"
Content="{DynamicResource Text.Pull.NoTags}" Content="{DynamicResource Text.Pull.NoTags}"
IsChecked="{Binding NoTags, Mode=TwoWay}"/> IsChecked="{Binding NoTags, Mode=TwoWay}"/>
<CheckBox Grid.Row="6" Grid.Column="1"
Content="{DynamicResource Text.Pull.UseRebase}"
IsChecked="{Binding UseRebase, Mode=TwoWay}"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View file

@ -76,7 +76,7 @@
</ListBox.ItemsPanel> </ListBox.ItemsPanel>
<ListBoxItem> <ListBoxItem>
<Grid Classes="view_mode" ColumnDefinitions="32,*,Auto,Auto"> <Grid Classes="view_mode" ColumnDefinitions="32,*,Auto">
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Histories}"/> <Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Histories}"/>
<TextBlock Grid.Column="1" Classes="monospace" Text="{DynamicResource Text.Histories}"/> <TextBlock Grid.Column="1" Classes="monospace" Text="{DynamicResource Text.Histories}"/>
<ToggleButton Grid.Column="2" <ToggleButton Grid.Column="2"
@ -84,9 +84,6 @@
Width="32" Height="26" Width="32" Height="26"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseTwoColumnsLayoutInHistories, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseTwoColumnsLayoutInHistories, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Histories.DisplayMode}"/> ToolTip.Tip="{DynamicResource Text.Histories.DisplayMode}"/>
<Button Grid.Column="3" Classes="icon_button" Width="32" Command="{Binding NavigateToCurrentHead}" ToolTip.Tip="{DynamicResource Text.Repository.NavigateToCurrentHead}">
<Path Width="14" Height="14" Data="{StaticResource Icons.Target}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</Grid> </Grid>
</ListBoxItem> </ListBoxItem>
@ -157,7 +154,7 @@
</TextBox.InnerRightContent> </TextBox.InnerRightContent>
</TextBox> </TextBox>
<Grid Grid.Row="2" x:Name="LeftSidebarGroups" Margin="0,4,0,0" RowDefinitions="28,Auto,28,Auto,28,Auto,28,Auto,28,Auto"> <Grid Grid.Row="2" x:Name="LeftSidebarGroups" Margin="0,4,0,0" RowDefinitions="28,Auto,28,Auto,28,Auto,28,Auto,28,Auto" SizeChanged="OnLeftSidebarSizeChanged">
<!-- Local Branches --> <!-- Local Branches -->
<ToggleButton Grid.Row="0" Classes="group_expander" IsChecked="{Binding IsLocalBranchGroupExpanded, Mode=TwoWay}"> <ToggleButton Grid.Row="0" Classes="group_expander" IsChecked="{Binding IsLocalBranchGroupExpanded, Mode=TwoWay}">
<TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.LocalBranches}"/> <TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.LocalBranches}"/>

View file

@ -144,9 +144,13 @@ namespace SourceGit.Views
private void OnLeftSidebarDataGridPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e) private void OnLeftSidebarDataGridPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e)
{ {
if (e.Property == DataGrid.ItemsSourceProperty || e.Property == DataGrid.IsVisibleProperty) if (e.Property == DataGrid.ItemsSourceProperty || e.Property == DataGrid.IsVisibleProperty)
{
UpdateLeftSidebarLayout(); UpdateLeftSidebarLayout();
} }
private void OnLeftSidebarSizeChanged(object _, SizeChangedEventArgs e)
{
if (e.HeightChanged)
UpdateLeftSidebarLayout();
} }
private void UpdateLeftSidebarLayout() private void UpdateLeftSidebarLayout()

View file

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui" <v:ChromelessWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -6,13 +6,48 @@
xmlns:v="using:SourceGit.Views" xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryConfigure" x:Class="SourceGit.Views.RepositoryConfigure"
x:DataType="vm:RepositoryConfigure"> x:DataType="vm:RepositoryConfigure"
<StackPanel Orientation="Vertical" Margin="8,0"> Icon="/App.ico"
<TextBlock FontSize="18" Title="{DynamicResource Text.Configure}"
Classes="bold" Width="600" SizeToContent="Height"
Text="{DynamicResource Text.Configure}"/> CanResize="False"
WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,Auto">
<!-- TitleBar -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto" Height="30">
<Border Grid.Column="0" Grid.ColumnSpan="3"
Background="{DynamicResource Brush.TitleBar}"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}"
PointerPressed="BeginMoveWindow"/>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="150,*"> <Path Grid.Column="0"
Width="14" Height="14"
Data="{StaticResource Icons.Settings}"
Margin="10,0,0,0"
IsVisible="{OnPlatform True, macOS=False}"/>
<Grid Grid.Column="0" Classes="caption_button_box" Margin="2,4,0,0" IsVisible="{OnPlatform False, macOS=True}">
<Button Classes="caption_button_macos" Click="CloseWindow">
<Grid>
<Ellipse Fill="{DynamicResource Brush.MacOS.Close}"/>
<Path Height="6" Width="6" Stretch="Fill" Fill="#505050" Data="{StaticResource Icons.MacOS.Close}"/>
</Grid>
</Button>
</Grid>
<TextBlock Grid.Column="0" Grid.ColumnSpan="3"
Classes="bold"
Text="{DynamicResource Text.Configure}"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsHitTestVisible="False"/>
<Button Grid.Column="2" Classes="caption_button" Click="CloseWindow" IsVisible="{OnPlatform True, macOS=False}">
<Path Data="{StaticResource Icons.Window.Close}"/>
</Button>
</Grid>
<!-- Body -->
<Grid Grid.Row="1" Margin="16,8,16,0" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0" <TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0" Margin="0,0,8,0"
@ -42,7 +77,13 @@
Height="28" Height="28"
CornerRadius="3" CornerRadius="3"
Watermark="{DynamicResource Text.Configure.Proxy.Placeholder}" Watermark="{DynamicResource Text.Configure.Proxy.Placeholder}"
Text="{Binding HttpProxy, Mode=TwoWay}"/> Text="{Binding HttpProxy, Mode=TwoWay}">
<TextBox.InnerRightContent>
<Button Classes="icon_button" IsVisible="{Binding HttpProxy, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Command="{Binding ClearHttpProxy}">
<Path Width="16" Height="16" Margin="0,0,0,0" Data="{StaticResource Icons.Clear}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="3" Grid.Column="0" <TextBlock Grid.Row="3" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Center"
@ -62,5 +103,23 @@
Content="{DynamicResource Text.Preference.GPG.TagEnabled}" Content="{DynamicResource Text.Preference.GPG.TagEnabled}"
IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/> IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/>
</Grid> </Grid>
<!-- Options -->
<StackPanel Grid.Row="2"
Margin="8,4,8,8"
Height="32"
Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Classes="flat primary"
Width="80"
Content="{DynamicResource Text.Sure}"
Click="SaveAndClose"
HotKey="Enter"/>
<Button Classes="flat"
Width="80"
Margin="8,0,0,0"
Content="{DynamicResource Text.Cancel}"
Click="CloseWindow"/>
</StackPanel> </StackPanel>
</UserControl> </Grid>
</v:ChromelessWindow>

View file

@ -1,12 +1,29 @@
using Avalonia.Controls; using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views namespace SourceGit.Views
{ {
public partial class RepositoryConfigure : UserControl public partial class RepositoryConfigure : ChromelessWindow
{ {
public RepositoryConfigure() public RepositoryConfigure()
{ {
InitializeComponent(); InitializeComponent();
} }
private void BeginMoveWindow(object _, PointerPressedEventArgs e)
{
BeginMoveDrag(e);
}
private void CloseWindow(object _1, RoutedEventArgs _2)
{
Close();
}
private void SaveAndClose(object _1, RoutedEventArgs _2)
{
(DataContext as ViewModels.RepositoryConfigure)?.Save();
Close();
}
} }
} }

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryToolbar" x:Class="SourceGit.Views.RepositoryToolbar"
@ -9,15 +10,23 @@
<Grid ColumnDefinitions="*,Auto,*"> <Grid ColumnDefinitions="*,Auto,*">
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="4,0,0,0"> <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="4,0,0,0">
<Button Classes="icon_button" Width="32" Command="{Binding OpenInFileManager}" ToolTip.Tip="{DynamicResource Text.Repository.Explore}"> <Button Classes="icon_button" Width="32" Command="{Binding OpenInFileManager}" ToolTip.Tip="{DynamicResource Text.Repository.Explore}">
<Path Width="15" Height="15" Data="{StaticResource Icons.Explore}" Margin="0,2,0,0"/> <Path Width="14" Height="14" Data="{StaticResource Icons.Explore}" Margin="0,2,0,0"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenWithExternalTools" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}">
<Path Width="13" Height="13" Data="{StaticResource Icons.OpenWith}"/>
</Button> </Button>
<Button Classes="icon_button" Width="32" Command="{Binding OpenInTerminal}" ToolTip.Tip="{DynamicResource Text.Repository.Terminal}"> <Button Classes="icon_button" Width="32" Command="{Binding OpenInTerminal}" ToolTip.Tip="{DynamicResource Text.Repository.Terminal}">
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/> <Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
</Button> </Button>
<Button Classes="icon_button" Width="32" Click="OpenWithExternalTools" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}"> <Button Classes="icon_button" Width="32" Click="OpenStatistics" ToolTip.Tip="{DynamicResource Text.Repository.Statistics}">
<Path Width="13" Height="13" Data="{StaticResource Icons.OpenWith}"/> <Path Width="13" Height="13" Data="{StaticResource Icons.Statistics}"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenConfigure" ToolTip.Tip="{DynamicResource Text.Repository.Configure}">
<Path Width="14" Height="14" Data="{StaticResource Icons.Settings}"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -58,19 +67,35 @@
<Button Classes="icon_button" Width="32" Margin="8,0,0,0" Click="OpenGitLFSMenu" ToolTip.Tip="{DynamicResource Text.GitLFS}"> <Button Classes="icon_button" Width="32" Margin="8,0,0,0" Click="OpenGitLFSMenu" ToolTip.Tip="{DynamicResource Text.GitLFS}">
<Path Width="14" Height="14" Data="{StaticResource Icons.LFS}"/> <Path Width="14" Height="14" Data="{StaticResource Icons.LFS}"/>
</Button> </Button>
<Button Classes="icon_button" Width="32" Margin="8,0,0,0" Command="{Binding Cleanup}" ToolTip.Tip="{DynamicResource Text.Repository.Clean}">
<Path Width="14" Height="14" Margin="0,1,0,0" Data="{StaticResource Icons.Clean}"/>
</Button>
</StackPanel> </StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,4,0"> <StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,4,0">
<Button Classes="icon_button" Width="32" Command="{Binding Cleanup}" ToolTip.Tip="{DynamicResource Text.Repository.Clean}"> <Path Width="13" Height="13" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
<Path Width="14" Height="14" Data="{StaticResource Icons.Clean}"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenStatistics" ToolTip.Tip="{DynamicResource Text.Repository.Statistics}"> <ContentControl Margin="6,0,0,0">
<Path Width="14" Height="14" Data="{StaticResource Icons.Statistics}"/> <ContentControl.Content>
</Button> <Binding Path="CurrentBranch">
<Binding.TargetNullValue>
<TextBlock Classes="monospace" Text="--"/>
</Binding.TargetNullValue>
</Binding>
</ContentControl.Content>
<Button Classes="icon_button" Width="32" Command="{Binding OpenConfigure}" ToolTip.Tip="{DynamicResource Text.Repository.Configure}"> <ContentControl.DataTemplates>
<Path Width="15" Height="15" Data="{StaticResource Icons.Settings}"/> <DataTemplate DataType="m:Branch">
<Border Background="Transparent" ToolTip.Tip="{Binding FriendlyName}">
<TextBlock Classes="monospace" Text="{Binding FriendlyName}" MaxWidth="250" TextTrimming="CharacterEllipsis"/>
</Border>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<Button Classes="icon_button" Width="32" Command="{Binding NavigateToCurrentHead}" ToolTip.Tip="{DynamicResource Text.Repository.NavigateToCurrentHead}">
<Path Width="13" Height="13" Margin="0,2,0,0" Data="{StaticResource Icons.Target}" Fill="{DynamicResource Brush.FG1}"/>
</Button> </Button>
</StackPanel> </StackPanel>
</Grid> </Grid>

View file

@ -20,6 +20,26 @@ namespace SourceGit.Views
} }
} }
private async void OpenStatistics(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner)
{
var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) };
await dialog.ShowDialog(owner);
e.Handled = true;
}
}
private async void OpenConfigure(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner)
{
var dialog = new RepositoryConfigure() { DataContext = new ViewModels.RepositoryConfigure(repo) };
await dialog.ShowDialog(owner);
e.Handled = true;
}
}
private void OpenGitFlowMenu(object sender, RoutedEventArgs e) private void OpenGitFlowMenu(object sender, RoutedEventArgs e)
{ {
if (DataContext is ViewModels.Repository repo) if (DataContext is ViewModels.Repository repo)
@ -41,16 +61,6 @@ namespace SourceGit.Views
e.Handled = true; e.Handled = true;
} }
private async void OpenStatistics(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner)
{
var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) };
await dialog.ShowDialog(owner);
e.Handled = true;
}
}
} }
} }

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters" xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
@ -38,10 +39,10 @@
<ComboBox Grid.Row="2" Grid.Column="1" <ComboBox Grid.Row="2" Grid.Column="1"
Height="28" Padding="8,0" Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Modes}" ItemsSource="{Binding Source={x:Static m:ResetMode.Supported}}"
SelectedItem="{Binding SelectedMode, Mode=TwoWay}"> SelectedItem="{Binding SelectedMode, Mode=TwoWay}">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate DataType="vm:ResetMode"> <DataTemplate DataType="m:ResetMode">
<Grid ColumnDefinitions="16,60,*"> <Grid ColumnDefinitions="16,60,*">
<Ellipse Grid.Column="0" Width="12" Height="12" Fill="{Binding Color}"/> <Ellipse Grid.Column="0" Width="12" Height="12" Fill="{Binding Color}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="4,0,0,0"/> <TextBlock Grid.Column="1" Text="{Binding Name}" Margin="4,0,0,0"/>

View file

@ -10,39 +10,17 @@
x:Class="SourceGit.Views.RevisionCompare" x:Class="SourceGit.Views.RevisionCompare"
x:DataType="vm:RevisionCompare" x:DataType="vm:RevisionCompare"
Background="{DynamicResource Brush.Window}"> Background="{DynamicResource Brush.Window}">
<Grid RowDefinitions="50,*" Margin="4"> <UserControl.DataTemplates>
<Grid Grid.Row="0" Margin="48,0,48,4" ColumnDefinitions="*,48,*"> <DataTemplate DataType="m:Null">
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4"> <Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="{DynamicResource Brush.Accent}" CornerRadius="4">
<Grid RowDefinitions="Auto,*"> <TextBlock Text="{DynamicResource Text.Worktree}" Classes="monospace" Margin="4,2" Foreground="#FFDDDDDD"/>
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
<v:Avatar Width="16" Height="16"
VerticalAlignment="Center"
IsHitTestVisible="False"
User="{Binding StartPoint.Author}"/>
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding StartPoint.Author.Name}" Margin="8,0,0,0"/>
<Border Grid.Column="2" Background="{DynamicResource Brush.Accent}" CornerRadius="4" IsVisible="{Binding StartPoint.IsCurrentHead}">
<TextBlock Text="HEAD" Classes="monospace" Margin="4,0" Foreground="#FFDDDDDD"/>
</Border> </Border>
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding StartPoint.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" PointerPressed="OnPressedSHA"/> </DataTemplate>
<TextBlock Grid.Column="4" Classes="monospace" Text="{Binding StartPoint.CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
</Grid>
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding StartPoint.Subject}" VerticalAlignment="Bottom"/>
</Grid>
</Border>
<Path Grid.Column="1" Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Down}" RenderTransformOrigin="50%,50%" RenderTransform="rotate(270deg)"/>
<Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<ContentControl Content="{Binding EndPoint}">
<ContentControl.DataTemplates>
<DataTemplate DataType="m:Commit"> <DataTemplate DataType="m:Commit">
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,*">
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto"> <Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
<v:Avatar Width="16" Height="16" <v:Avatar Width="16" Height="16" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
VerticalAlignment="Center"
IsHitTestVisible="False"
User="{Binding Author}"/>
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Author.Name}" Margin="8,0,0,0"/> <TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Author.Name}" Margin="8,0,0,0"/>
<Border Grid.Column="2" Background="{DynamicResource Brush.Accent}" CornerRadius="4" IsVisible="{Binding IsCurrentHead}"> <Border Grid.Column="2" Background="{DynamicResource Brush.Accent}" CornerRadius="4" IsVisible="{Binding IsCurrentHead}">
<TextBlock Text="HEAD" Classes="monospace" Margin="4,0" Foreground="#FFDDDDDD"/> <TextBlock Text="HEAD" Classes="monospace" Margin="4,0" Foreground="#FFDDDDDD"/>
@ -54,17 +32,28 @@
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding Subject}" VerticalAlignment="Bottom"/> <TextBlock Grid.Row="1" Classes="monospace" Text="{Binding Subject}" VerticalAlignment="Bottom"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</UserControl.DataTemplates>
<DataTemplate DataType="vm:CompareTargetWorktree"> <Grid RowDefinitions="50,*" Margin="4">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="{DynamicResource Brush.Accent}" CornerRadius="4"> <!-- Compare Revision Info -->
<TextBlock Text="{DynamicResource Text.Worktree}" Classes="monospace" Margin="4,2" Foreground="#FFDDDDDD"/> <Grid Grid.Row="0" Margin="48,0,48,4" ColumnDefinitions="*,48,*">
<!-- Base Revision -->
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<ContentControl Content="{Binding StartPoint}"/>
</Border> </Border>
</DataTemplate>
</ContentControl.DataTemplates> <!-- Swap Button -->
</ContentControl> <Button Grid.Column="1" Classes="icon_button" Command="{Binding Swap}" HorizontalAlignment="Center" ToolTip.Tip="{DynamicResource Text.Diff.SwapCommits}">
<Path Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Compare}"/>
</Button>
<!-- Right Revision -->
<Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<ContentControl Content="{Binding EndPoint}"/>
</Border> </Border>
</Grid> </Grid>
<!-- Changes View -->
<Grid Grid.Row="1"> <Grid Grid.Row="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="256" MinWidth="200" MaxWidth="480"/> <ColumnDefinition Width="256" MinWidth="200" MaxWidth="480"/>

View file

@ -29,7 +29,7 @@
Padding="0" Padding="0"
Command="{Binding Clear}" Command="{Binding Clear}"
IsEnabled="{Binding Stashes.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"> IsEnabled="{Binding Stashes.Count, Converter={x:Static c:IntConverters.IsGreaterThanZero}}">
<Path Width="14" Height="14" Data="{StaticResource Icons.Clean}"/> <Path Width="14" Height="14" Data="{StaticResource Icons.RemoveAll}"/>
</Button> </Button>
</Grid> </Grid>

View file

@ -394,16 +394,10 @@ namespace SourceGit.Views
if (chunk == null || (!chunk.Combined && chunk.IsOldSide != IsOld)) if (chunk == null || (!chunk.Combined && chunk.IsOldSide != IsOld))
return; return;
var view = TextArea.TextView;
if (view == null || !view.VisualLinesValid)
return;
var color = (Color)this.FindResource("SystemAccentColor"); var color = (Color)this.FindResource("SystemAccentColor");
var brush = new SolidColorBrush(color, 0.1); var brush = new SolidColorBrush(color, 0.1);
var pen = new Pen(color.ToUInt32()); var pen = new Pen(color.ToUInt32());
var rect = new Rect(0, chunk.Y, Bounds.Width, chunk.Height);
var x = ((Point)view.TranslatePoint(new Point(0, 0), this)).X;
var rect = new Rect(x - 4, chunk.Y, view.Bounds.Width + 7, chunk.Height);
context.DrawRectangle(brush, null, rect); context.DrawRectangle(brush, null, rect);
context.DrawLine(pen, rect.TopLeft, rect.TopRight); context.DrawLine(pen, rect.TopLeft, rect.TopRight);
@ -639,7 +633,11 @@ namespace SourceGit.Views
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
var scroller = (ScrollViewer)e.NameScope.Find("PART_ScrollViewer"); var scroller = (ScrollViewer)e.NameScope.Find("PART_ScrollViewer");
scroller?.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay)); if (scroller != null)
{
scroller.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay));
scroller.GotFocus += (_, _) => TrySetChunk(null);
}
} }
public override void UpdateSelectedChunk(double y) public override void UpdateSelectedChunk(double y)
@ -675,6 +673,19 @@ namespace SourceGit.Views
return; return;
} }
var firstLineIdx = view.VisualLines[0].FirstDocumentLine.LineNumber - 1;
var lastLineIdx = view.VisualLines[^1].FirstDocumentLine.LineNumber - 1;
if (endIdx < firstLineIdx)
{
TrySetChunk(null);
return;
}
else if (startIdx > lastLineIdx)
{
TrySetChunk(null);
return;
}
var startLine = view.GetVisualLine(startIdx + 1); var startLine = view.GetVisualLine(startIdx + 1);
var endLine = view.GetVisualLine(endIdx + 1); var endLine = view.GetVisualLine(endIdx + 1);
@ -840,6 +851,19 @@ namespace SourceGit.Views
return; return;
} }
var firstLineIdx = view.VisualLines[0].FirstDocumentLine.LineNumber - 1;
var lastLineIdx = view.VisualLines[^1].FirstDocumentLine.LineNumber - 1;
if (endIdx < firstLineIdx)
{
TrySetChunk(null);
return;
}
else if (startIdx > lastLineIdx)
{
TrySetChunk(null);
return;
}
var startLine = view.GetVisualLine(startIdx + 1); var startLine = view.GetVisualLine(startIdx + 1);
var endLine = view.GetVisualLine(endIdx + 1); var endLine = view.GetVisualLine(endIdx + 1);
@ -925,6 +949,7 @@ namespace SourceGit.Views
_scrollViewer = this.FindDescendantOfType<ScrollViewer>(); _scrollViewer = this.FindDescendantOfType<ScrollViewer>();
if (_scrollViewer != null) if (_scrollViewer != null)
{ {
_scrollViewer.GotFocus += OnTextViewScrollGotFocus;
_scrollViewer.ScrollChanged += OnTextViewScrollChanged; _scrollViewer.ScrollChanged += OnTextViewScrollChanged;
_scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.OneWay)); _scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.OneWay));
} }
@ -939,6 +964,7 @@ namespace SourceGit.Views
if (_scrollViewer != null) if (_scrollViewer != null)
{ {
_scrollViewer.ScrollChanged -= OnTextViewScrollChanged; _scrollViewer.ScrollChanged -= OnTextViewScrollChanged;
_scrollViewer.GotFocus -= OnTextViewScrollGotFocus;
_scrollViewer = null; _scrollViewer = null;
} }
@ -977,6 +1003,11 @@ namespace SourceGit.Views
} }
} }
private void OnTextViewScrollGotFocus(object sender, GotFocusEventArgs e)
{
TrySetChunk(null);
}
private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e) private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e)
{ {
if (TextArea.IsFocused && DataContext is ViewModels.TwoSideTextDiff diff) if (TextArea.IsFocused && DataContext is ViewModels.TwoSideTextDiff diff)

View file

@ -10,8 +10,42 @@
<TextBlock FontSize="18" <TextBlock FontSize="18"
Classes="bold" Classes="bold"
Text="{DynamicResource Text.UpdateSubmodules}"/> Text="{DynamicResource Text.UpdateSubmodules}"/>
<TextBlock Text="{DynamicResource Text.UpdateSubmodules.Tip}" <Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32" ColumnDefinitions="120,*">
Margin="0,16,0,0" <TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.UpdateSubmodules.Target}"/>
<ComboBox Grid.Row="0" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Submodules}"
SelectedItem="{Binding SelectedSubmodule, Mode=TwoWay}"
IsEnabled="{Binding !UpdateAll}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="x:String">
<StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Center">
<Path Margin="0,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Submodule}"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Grid.Row="1" Grid.Column="1"
Content="{DynamicResource Text.UpdateSubmodules.All}"
IsChecked="{Binding UpdateAll, Mode=TwoWay}"/>
<CheckBox Grid.Row="2" Grid.Column="1"
Content="{DynamicResource Text.UpdateSubmodules.Init}"
IsChecked="{Binding EnableInit, Mode=TwoWay}"/>
<CheckBox Grid.Row="3" Grid.Column="1"
Content="{DynamicResource Text.UpdateSubmodules.Recursive}"
IsChecked="{Binding EnableRecursive, Mode=TwoWay}"/>
<CheckBox Grid.Row="4" Grid.Column="1"
Content="{DynamicResource Text.UpdateSubmodules.UseRemote}"
IsChecked="{Binding EnableRemote, Mode=TwoWay}"/>
</Grid>
</StackPanel> </StackPanel>
</UserControl> </UserControl>