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
run: dotnet build -c Release
- 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
uses: actions/upload-artifact@v4
with:
@ -43,7 +43,7 @@ jobs:
- name: Build
run: dotnet build -c Release
- 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
run: tar -cvf sourcegit.osx-x64.tar -C publish/ .
- name: Upload Artifact
@ -66,7 +66,7 @@ jobs:
- name: Build
run: dotnet build -c Release
- 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
run: tar -cvf sourcegit.osx-arm64.tar -C publish/ .
- name: Upload Artifact
@ -89,7 +89,7 @@ jobs:
- name: Build
run: dotnet build -c Release
- 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
run: mv publish/SourceGit publish/sourcegit
- 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
EndProjectSection
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -104,6 +113,7 @@ Global
{F101849D-BDB7-40D4-A516-751150C3CCFC} = {9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC}
{9BA0B044-0CC9-46F8-B551-204F149BF45D} = {FD384607-ED99-47B7-AF31-FB245841BC92}
{7802CD7A-591B-4EDD-96F8-9BF3F61692E4} = {9BA0B044-0CC9-46F8-B551-204F149BF45D}
{5D125DD9-B48A-491F-B2FB-D7830D74C4DC} = {FD384607-ED99-47B7-AF31-FB245841BC92}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
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
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/*"
rm -rf 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/*"
rm -rf SourceGit.app

View file

@ -6,7 +6,7 @@ if (Test-Path SourceGit) {
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
@ -16,7 +16,7 @@ if (Test-Path SourceGit) {
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

View file

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

View file

@ -82,7 +82,7 @@ DOTNET_PROJECT_PATH="../../../src/SourceGit.csproj"
# Additional useful arguments include:
# "-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
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.ThemeOverrides))]
[JsonSerializable(typeof(Models.Version))]
[JsonSerializable(typeof(Models.RepositorySettings))]
[JsonSerializable(typeof(ViewModels.Preference))]
[JsonSerializable(typeof(ViewModels.RepositorySettings))]
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)
{
if (Current is App app && app._launcher != null)
@ -501,10 +506,15 @@ namespace SourceGit
private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop)
{
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;
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;
}

View file

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

View file

@ -13,7 +13,9 @@ namespace SourceGit.Commands
{
WorkingDirectory = 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()

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ namespace SourceGit.Commands
{
Context = 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()
@ -25,7 +25,7 @@ namespace SourceGit.Commands
{
_loaded.Add(new Models.Tag()
{
Name = subs[0],
Name = subs[0].Substring(10),
SHA = subs[1],
});
}
@ -33,7 +33,7 @@ namespace SourceGit.Commands
{
_loaded.Add(new Models.Tag()
{
Name = subs[0],
Name = subs[0].Substring(10),
SHA = subs[2],
});
}

View file

@ -19,7 +19,7 @@ namespace SourceGit.Commands
if (!rs.IsSuccess)
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)
{
if (line[0] == '>')

View file

@ -13,36 +13,46 @@ namespace SourceGit.Commands
public bool Add(string url, string relativePath, bool recursive, Action<string> outputHandler)
{
_outputHandler = outputHandler;
Args = $"submodule add {url} {relativePath}";
Args = $"submodule add {url} \"{relativePath}\"";
if (!Exec())
return false;
if (recursive)
{
Args = $"submodule update --init --recursive -- {relativePath}";
Args = $"submodule update --init --recursive -- \"{relativePath}\"";
return Exec();
}
else
{
Args = $"submodule update --init -- {relativePath}";
Args = $"submodule update --init -- \"{relativePath}\"";
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;
return Exec();
}
public bool Delete(string relativePath)
{
Args = $"submodule deinit -f {relativePath}";
Args = $"submodule deinit -f \"{relativePath}\"";
if (!Exec())
return false;
Args = $"rm -rf {relativePath}";
Args = $"rm -rf \"{relativePath}\"";
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>
public DiffOption(string baseRevision, string targetRevision, Change change)
{
_revisions.Add(baseRevision);
_revisions.Add(string.IsNullOrEmpty(baseRevision) ? "-R" : baseRevision);
_revisions.Add(targetRevision);
_path = change.Path;
_orgPath = change.OriginalPath;

View file

@ -128,7 +128,7 @@ namespace SourceGit.Models
public void FindJetBrainsFromToolbox(Func<string> platformFinder)
{
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");
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
{

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.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace SourceGit.Models
{
public partial class Version
public class Version
{
[JsonPropertyName("name")]
public string Name { get; set; }
@ -15,21 +14,20 @@ namespace SourceGit.Models
[JsonPropertyName("body")]
public string Body { get; set; }
[GeneratedRegex(@"^v(\d+)\.(\d+)$")]
private static partial Regex REG_VERSION_TAG();
public bool IsNewVersion
{
get
{
var match = REG_VERSION_TAG().Match(TagName);
if (!match.Success)
try
{
System.Version version = new System.Version(TagName.Substring(1));
System.Version current = Assembly.GetExecutingAssembly().GetName().Version!;
return current.CompareTo(version) < 0;
}
catch
{
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.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.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.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>
@ -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.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.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.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>
@ -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.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.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.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>
@ -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.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.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.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>

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.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.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.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>
@ -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.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.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.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>
@ -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.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.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.LocalChanges" xml:space="preserve">Local Changes:</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.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.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.Welcome.AddRootFolder" xml:space="preserve">Create 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.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.CopyInfo" 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.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.Welcome" 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.Discard" 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.Warning" 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.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.FetchAllBranches" 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.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.URL" 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.Welcome.AddRootFolder" 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.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.CopyInfo" 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.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.Welcome" 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.Discard" 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.Warning" 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.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.FetchAllBranches" 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.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.URL" 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.Welcome.AddRootFolder" 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">
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
<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 Selector="FlyoutPresenter">

View file

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

View file

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

View file

@ -14,14 +14,14 @@ namespace SourceGit.ViewModels
{
public Models.Branch Base
{
get;
private set;
get => _based;
private set => SetProperty(ref _based, value);
}
public Models.Branch To
{
get;
private set;
get => _to;
private set => SetProperty(ref _to, value);
}
public Models.Commit BaseHead
@ -50,7 +50,7 @@ namespace SourceGit.ViewModels
if (SetProperty(ref _selectedChanges, value))
{
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
DiffContext = null;
}
@ -78,34 +78,10 @@ namespace SourceGit.ViewModels
public BranchCompare(string repo, Models.Branch baseBranch, Models.Branch toBranch)
{
_repo = repo;
_based = baseBranch;
_to = toBranch;
Base = baseBranch;
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;
});
});
Refresh();
}
public void NavigateTo(string commitSHA)
@ -114,6 +90,17 @@ namespace SourceGit.ViewModels
repo?.NavigateToCommit(commitSHA);
}
public void Swap()
{
(Base, To) = (_to, _based);
SelectedChanges = [];
if (_baseHead != null)
(BaseHead, ToHead) = (_toHead, _baseHead);
Refresh();
}
public void ClearSearchFilter()
{
SearchFilter = string.Empty;
@ -134,7 +121,7 @@ namespace SourceGit.ViewModels
{
var toolType = Preference.Instance.ExternalMergeToolType;
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));
ev.Handled = true;
@ -179,6 +166,38 @@ namespace SourceGit.ViewModels
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()
{
if (_changes == null)
@ -202,6 +221,8 @@ namespace SourceGit.ViewModels
}
private string _repo;
private Models.Branch _based = null;
private Models.Branch _to = null;
private Models.Commit _baseHead = null;
private Models.Commit _toHead = null;
private List<Models.Change> _changes = null;

View file

@ -3,6 +3,8 @@ using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Threading;
namespace SourceGit.ViewModels
{
public class Clone : Popup
@ -51,26 +53,23 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _extraArgs, value);
}
public Clone(Launcher launcher)
public Clone()
{
_launcher = launcher;
_page = launcher.ActivePage;
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 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;
});
}
private readonly Launcher _launcher = null;
private readonly LauncherPage _page = null;
private string _remote = string.Empty;
private bool _useSSH = false;
private string _sshKey = string.Empty;

View file

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

View file

@ -12,7 +12,7 @@ namespace SourceGit.ViewModels
}
[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))]
public string TagName
{

View file

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

View file

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

View file

@ -9,11 +9,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class CountSelectedCommits
{
public int Count { get; set; }
}
public class Histories : ObservableObject
{
public bool IsLoading
@ -143,7 +138,7 @@ namespace SourceGit.ViewModels
else
{
_repo.SearchResultSelectedCommit = null;
DetailContext = new CountSelectedCommits() { Count = commits.Count };
DetailContext = commits.Count;
}
}
@ -152,7 +147,7 @@ namespace SourceGit.ViewModels
if (datagrid.SelectedItems.Count != 1)
return null;
var current = _repo.Branches.Find(x => x.IsCurrent);
var current = _repo.CurrentBranch;
if (current == null)
return null;
@ -435,6 +430,17 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
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;
}

View file

@ -38,7 +38,7 @@ namespace SourceGit.ViewModels
var root = new Commands.QueryRepositoryRootPath(startupRepo).Result();
if (string.IsNullOrEmpty(root))
{
Pages[0].Notifications.Add(new Notification
Pages[0].Notifications.Add(new Models.Notification
{
IsError = true,
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)
{
var notification = new Notification()
var notification = new Models.Notification()
{
IsError = isError,
Message = message,

View file

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

View file

@ -1,22 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
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 string Source
@ -31,13 +16,7 @@ namespace SourceGit.ViewModels
private set;
}
public List<MergeMode> Modes
{
get;
private set;
}
public MergeMode SelectedMode
public Models.MergeMode SelectedMode
{
get;
set;
@ -48,13 +27,7 @@ namespace SourceGit.ViewModels
_repo = repo;
Source = source;
Into = into;
Modes = new List<MergeMode>() {
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];
SelectedMode = Models.MergeMode.Supported[0];
View = new Views.Merge() { DataContext = this };
}

View file

@ -59,6 +59,12 @@ namespace SourceGit.ViewModels
set => _repo.Settings.PreferRebaseInsteadOfMerge = value;
}
public bool FetchAllBranches
{
get => _repo.Settings.FetchAllBranchesOnPull;
set => _repo.Settings.FetchAllBranchesOnPull = value;
}
public bool NoTags
{
get => _repo.Settings.FetchWithoutTagsOnPull;
@ -68,7 +74,7 @@ namespace SourceGit.ViewModels
public Pull(Repository repo, Models.Branch specifiedRemoteBranch)
{
_repo = repo;
_current = repo.Branches.Find(x => x.IsCurrent);
_current = repo.CurrentBranch;
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}...");
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)
{
SetProgressDescription("Re-apply local changes...");

View file

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

View file

@ -1,9 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class RepositoryConfigure : Popup
public class RepositoryConfigure : ObservableObject
{
public string UserName
{
@ -37,8 +37,8 @@ namespace SourceGit.ViewModels
public string HttpProxy
{
get;
set;
get => _httpProxy;
set => SetProperty(ref _httpProxy, value);
}
public RepositoryConfigure(Repository repo)
@ -58,11 +58,14 @@ namespace SourceGit.ViewModels
GPGUserSigningKey = signingKey;
if (_cached.TryGetValue("http.proxy", out var 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.email", UserEmail);
@ -70,7 +73,6 @@ namespace SourceGit.ViewModels
SetIfChanged("tag.gpgsign", GPGTagSigningEnabled ? "true" : "false");
SetIfChanged("user.signingkey", GPGUserSigningKey);
SetIfChanged("http.proxy", HttpProxy);
return null;
}
private void SetIfChanged(string key, string value)
@ -93,5 +95,6 @@ namespace SourceGit.ViewModels
private readonly Repository _repo = 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 Avalonia.Media;
using System.Threading.Tasks;
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 Models.Branch Current
@ -35,13 +16,7 @@ namespace SourceGit.ViewModels
private set;
}
public List<ResetMode> Modes
{
get;
private set;
}
public ResetMode SelectedMode
public Models.ResetMode SelectedMode
{
get;
set;
@ -52,12 +27,7 @@ namespace SourceGit.ViewModels
_repo = repo;
Current = current;
To = to;
Modes = new List<ResetMode>() {
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];
SelectedMode = Models.ResetMode.Supported[0];
View = new Views.Reset() { DataContext = this };
}

View file

@ -10,23 +10,18 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class CompareTargetWorktree
{
public string SHA => string.Empty;
}
public class RevisionCompare : ObservableObject
{
public Models.Commit StartPoint
public object StartPoint
{
get;
private set;
get => _startPoint;
private set => SetProperty(ref _startPoint, value);
}
public object EndPoint
{
get;
private set;
get => _endPoint;
private set => SetProperty(ref _endPoint, value);
}
public List<Models.Change> VisibleChanges
@ -43,12 +38,17 @@ namespace SourceGit.ViewModels
if (SetProperty(ref _selectedChanges, value))
{
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
{
DiffContext = null;
}
}
}
}
public string SearchFilter
{
@ -71,41 +71,17 @@ namespace SourceGit.ViewModels
public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint)
{
_repo = repo;
StartPoint = startPoint;
_startPoint = (object)startPoint ?? new Models.Null();
_endPoint = (object)endPoint ?? new Models.Null();
if (endPoint == null)
{
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);
});
Task.Run(Refresh);
}
public void Cleanup()
{
_repo = null;
_startPoint = null;
_endPoint = null;
if (_changes != null)
_changes.Clear();
if (_visibleChanges != null)
@ -122,6 +98,13 @@ namespace SourceGit.ViewModels
repo?.NavigateToCommit(commitSHA);
}
public void Swap()
{
(StartPoint, EndPoint) = (_endPoint, _startPoint);
SelectedChanges = [];
Task.Run(Refresh);
}
public void ClearSearchFilter()
{
SearchFilter = string.Empty;
@ -140,7 +123,7 @@ namespace SourceGit.ViewModels
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
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 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 _endPoint;
private object _startPoint = null;
private object _endPoint = null;
private List<Models.Change> _changes = null;
private List<Models.Change> _visibleChanges = 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
{
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)
{
_repo = repo;
SelectedSubmodule = repo.Submodules.Count > 0 ? repo.Submodules[0] : string.Empty;
View = new Views.UpdateSubmodules() { DataContext = this };
}
public override Task<bool> Sure()
{
_repo.SetWatcherEnabled(false);
string target = string.Empty;
if (_updateAll)
{
ProgressDescription = "Updating submodules ...";
}
else
{
target = SelectedSubmodule;
ProgressDescription = $"Updating submodule {target} ...";
}
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));
return true;
});
}
private readonly Repository _repo = null;
private bool _updateAll = true;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -69,7 +69,10 @@
</Grid>
</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">
<Grid RowDefinitions="Auto,*">

View file

@ -174,8 +174,9 @@ namespace SourceGit.Views
return;
_disableSelectionChangingEvent = true;
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)
{
@ -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;
}
@ -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;
}
}

View file

@ -54,7 +54,7 @@
<Grid RowDefinitions="24,Auto,Auto,Auto" ColumnDefinitions="96,*">
<!-- 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 -->
<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>
<!-- 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}}"/>
<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}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type m:Decorator}">
<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}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Refs}" IsVisible="{Binding HasDecorators}"/>
<Border Grid.Row="2" Grid.Column="1" Margin="12,0,0,0" Height="24" IsVisible="{Binding HasDecorators}">
<v:CommitRefsPresenter IconBackground="{DynamicResource Brush.DecoratorIconBG}"
IconForeground="{DynamicResource Brush.DecoratorIcon}"
BranchNameBackground="{DynamicResource Brush.DecoratorBranch}"
TagNameBackground="{DynamicResource Brush.DecoratorTag}"
LabelForeground="{DynamicResource Brush.DecoratorFG}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontSize="10"
VerticalAlignment="Center"/>
</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 -->
<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>
</StackPanel>
</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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
@ -12,28 +13,28 @@
Classes="bold"
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}"/>
<ContentControl Grid.Row="0" Grid.Column="1" Content="{Binding Mode}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:DiscardModeAll">
<DataTemplate DataType="m:Null">
<StackPanel Orientation="Horizontal">
<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"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="vm:DiscardModeSingle">
<DataTemplate DataType="x:String">
<StackPanel Orientation="Horizontal">
<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>
</DataTemplate>
<DataTemplate DataType="vm:DiscardModeMulti">
<DataTemplate DataType="x:Int32">
<StackPanel Orientation="Horizontal">
<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>
</DataTemplate>
</ContentControl.DataTemplates>

View file

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

View file

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

View file

@ -11,9 +11,7 @@ namespace SourceGit.Views
public Launcher()
{
var layout = ViewModels.Preference.Instance.Layout;
WindowState = layout.LauncherWindowState;
if (WindowState != WindowState.Maximized)
if (layout.LauncherWindowState != WindowState.Maximized)
{
Width = layout.LauncherWidth;
Height = layout.LauncherHeight;
@ -22,6 +20,15 @@ namespace SourceGit.Views
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)
{
base.OnPropertyChanged(change);

View file

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:v="using:SourceGit.Views"
xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
@ -86,7 +87,7 @@
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Notifications}">
<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 Padding="8" CornerRadius="6" Background="{DynamicResource Brush.Popup}">
<Grid RowDefinitions="26,Auto,32">

View file

@ -34,7 +34,7 @@ namespace SourceGit.Views
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)
page.Notifications.Remove(notice);

View file

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

View file

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

View file

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

View file

@ -76,7 +76,7 @@
</ListBox.ItemsPanel>
<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}"/>
<TextBlock Grid.Column="1" Classes="monospace" Text="{DynamicResource Text.Histories}"/>
<ToggleButton Grid.Column="2"
@ -84,9 +84,6 @@
Width="32" Height="26"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseTwoColumnsLayoutInHistories, Mode=TwoWay}"
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>
</ListBoxItem>
@ -157,7 +154,7 @@
</TextBox.InnerRightContent>
</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 -->
<ToggleButton Grid.Row="0" Classes="group_expander" IsChecked="{Binding IsLocalBranchGroupExpanded, Mode=TwoWay}">
<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)
{
if (e.Property == DataGrid.ItemsSourceProperty || e.Property == DataGrid.IsVisibleProperty)
{
UpdateLeftSidebarLayout();
}
private void OnLeftSidebarSizeChanged(object _, SizeChangedEventArgs e)
{
if (e.HeightChanged)
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -6,13 +6,48 @@
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryConfigure"
x:DataType="vm:RepositoryConfigure">
<StackPanel Orientation="Vertical" Margin="8,0">
<TextBlock FontSize="18"
Classes="bold"
Text="{DynamicResource Text.Configure}"/>
x:DataType="vm:RepositoryConfigure"
Icon="/App.ico"
Title="{DynamicResource Text.Configure}"
Width="600" SizeToContent="Height"
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"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@ -42,7 +77,13 @@
Height="28"
CornerRadius="3"
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"
HorizontalAlignment="Right" VerticalAlignment="Center"
@ -62,5 +103,23 @@
Content="{DynamicResource Text.Preference.GPG.TagEnabled}"
IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/>
</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>
</UserControl>
</Grid>
</v:ChromelessWindow>

View file

@ -1,12 +1,29 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
{
public partial class RepositoryConfigure : UserControl
public partial class RepositoryConfigure : ChromelessWindow
{
public RepositoryConfigure()
{
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryToolbar"
@ -9,15 +10,23 @@
<Grid ColumnDefinitions="*,Auto,*">
<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}">
<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 Classes="icon_button" Width="32" Command="{Binding OpenInTerminal}" ToolTip.Tip="{DynamicResource Text.Repository.Terminal}">
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
</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 Classes="icon_button" Width="32" Click="OpenStatistics" ToolTip.Tip="{DynamicResource Text.Repository.Statistics}">
<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>
</StackPanel>
@ -58,19 +67,35 @@
<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}"/>
</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 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="14" Height="14" Data="{StaticResource Icons.Clean}"/>
</Button>
<Path Width="13" Height="13" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
<Button Classes="icon_button" Width="32" Click="OpenStatistics" ToolTip.Tip="{DynamicResource Text.Repository.Statistics}">
<Path Width="14" Height="14" Data="{StaticResource Icons.Statistics}"/>
</Button>
<ContentControl Margin="6,0,0,0">
<ContentControl.Content>
<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}">
<Path Width="15" Height="15" Data="{StaticResource Icons.Settings}"/>
<ContentControl.DataTemplates>
<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>
</StackPanel>
</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)
{
if (DataContext is ViewModels.Repository repo)
@ -41,16 +61,6 @@ namespace SourceGit.Views
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
@ -38,10 +39,10 @@
<ComboBox Grid.Row="2" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Modes}"
ItemsSource="{Binding Source={x:Static m:ResetMode.Supported}}"
SelectedItem="{Binding SelectedMode, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="vm:ResetMode">
<DataTemplate DataType="m:ResetMode">
<Grid ColumnDefinitions="16,60,*">
<Ellipse Grid.Column="0" Width="12" Height="12" Fill="{Binding Color}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="4,0,0,0"/>

View file

@ -10,39 +10,17 @@
x:Class="SourceGit.Views.RevisionCompare"
x:DataType="vm:RevisionCompare"
Background="{DynamicResource Brush.Window}">
<Grid RowDefinitions="50,*" Margin="4">
<Grid Grid.Row="0" Margin="48,0,48,4" ColumnDefinitions="*,48,*">
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<Grid RowDefinitions="Auto,*">
<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"/>
<UserControl.DataTemplates>
<DataTemplate DataType="m:Null">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="{DynamicResource Brush.Accent}" CornerRadius="4">
<TextBlock Text="{DynamicResource Text.Worktree}" Classes="monospace" Margin="4,2" Foreground="#FFDDDDDD"/>
</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"/>
<TextBlock Grid.Column="4" Classes="monospace" Text="{Binding StartPoint.CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
</Grid>
</DataTemplate>
<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">
<Grid RowDefinitions="Auto,*">
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
<v:Avatar Width="16" Height="16"
VerticalAlignment="Center"
IsHitTestVisible="False"
User="{Binding Author}"/>
<v:Avatar Width="16" Height="16" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
<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}">
<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"/>
</Grid>
</DataTemplate>
</UserControl.DataTemplates>
<DataTemplate DataType="vm:CompareTargetWorktree">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="{DynamicResource Brush.Accent}" CornerRadius="4">
<TextBlock Text="{DynamicResource Text.Worktree}" Classes="monospace" Margin="4,2" Foreground="#FFDDDDDD"/>
<Grid RowDefinitions="50,*" Margin="4">
<!-- Compare Revision Info -->
<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>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<!-- 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>
<!-- 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>
</Grid>
<!-- Changes View -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="256" MinWidth="200" MaxWidth="480"/>

View file

@ -29,7 +29,7 @@
Padding="0"
Command="{Binding Clear}"
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>
</Grid>

View file

@ -394,16 +394,10 @@ namespace SourceGit.Views
if (chunk == null || (!chunk.Combined && chunk.IsOldSide != IsOld))
return;
var view = TextArea.TextView;
if (view == null || !view.VisualLinesValid)
return;
var color = (Color)this.FindResource("SystemAccentColor");
var brush = new SolidColorBrush(color, 0.1);
var pen = new Pen(color.ToUInt32());
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);
var rect = new Rect(0, chunk.Y, Bounds.Width, chunk.Height);
context.DrawRectangle(brush, null, rect);
context.DrawLine(pen, rect.TopLeft, rect.TopRight);
@ -639,7 +633,11 @@ namespace SourceGit.Views
base.OnApplyTemplate(e);
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)
@ -675,6 +673,19 @@ namespace SourceGit.Views
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 endLine = view.GetVisualLine(endIdx + 1);
@ -840,6 +851,19 @@ namespace SourceGit.Views
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 endLine = view.GetVisualLine(endIdx + 1);
@ -925,6 +949,7 @@ namespace SourceGit.Views
_scrollViewer = this.FindDescendantOfType<ScrollViewer>();
if (_scrollViewer != null)
{
_scrollViewer.GotFocus += OnTextViewScrollGotFocus;
_scrollViewer.ScrollChanged += OnTextViewScrollChanged;
_scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.OneWay));
}
@ -939,6 +964,7 @@ namespace SourceGit.Views
if (_scrollViewer != null)
{
_scrollViewer.ScrollChanged -= OnTextViewScrollChanged;
_scrollViewer.GotFocus -= OnTextViewScrollGotFocus;
_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)
{
if (TextArea.IsFocused && DataContext is ViewModels.TwoSideTextDiff diff)

View file

@ -10,8 +10,42 @@
<TextBlock FontSize="18"
Classes="bold"
Text="{DynamicResource Text.UpdateSubmodules}"/>
<TextBlock Text="{DynamicResource Text.UpdateSubmodules.Tip}"
Margin="0,16,0,0"
HorizontalAlignment="Center"/>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32" ColumnDefinitions="120,*">
<TextBlock Grid.Row="0" Grid.Column="0"
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>
</UserControl>