Merge branch 'release/v8.10'

This commit is contained in:
leo 2024-04-29 11:01:46 +08:00
commit d60a33af1a
45 changed files with 487 additions and 287 deletions

View file

@ -28,12 +28,20 @@ Opensource Git GUI client.
> **Linux** only tested on **Ubuntu 22.04** on **X11**. > **Linux** only tested on **Ubuntu 22.04** on **X11**.
## How to use ## How to Use
**To use this tool, you need to install Git(>=2.23.0) first.** **To use this tool, you need to install Git(>=2.23.0) first.**
You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits. You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
| OS | PATH |
| --- | --- |
| Windows | `C:\Users\USER_NAME\AppData\Roaming\SourceGit` |
| Linux | `/home/USER_NAME/.config/SourceGit` |
| macOS | `/Users/USER_NAME/.config/SourceGit` |
For **Windows** users: For **Windows** users:
* **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead. * **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead.
@ -64,7 +72,8 @@ This app supports open repository in external tools listed in the table below.
| JetBrains Fleet | YES | YES | YES | FLEET_PATH | | JetBrains Fleet | YES | YES | YES | FLEET_PATH |
| Sublime Text | YES | YES | YES | SUBLIME_TEXT_PATH | | Sublime Text | YES | YES | YES | SUBLIME_TEXT_PATH |
You can set the given environment variable for special tool if it can NOT be found by this app automatically. > * You can set the given environment variable for special tool if it can NOT be found by this app automatically.
> * Installing `JetBrains Toolbox` will help this app to find other JetBrains tools installed on your device.
## Screenshots ## Screenshots

View file

@ -1 +1 @@
8.9 8.10

View file

@ -4,6 +4,7 @@ namespace SourceGit
{ {
[JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)] [JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)]
[JsonSerializable(typeof(Models.Version))] [JsonSerializable(typeof(Models.Version))]
[JsonSerializable(typeof(Models.JetBrainsState))]
[JsonSerializable(typeof(ViewModels.Preference))] [JsonSerializable(typeof(ViewModels.Preference))]
internal partial class JsonCodeGen : JsonSerializerContext { } internal partial class JsonCodeGen : JsonSerializerContext { }
} }

View file

@ -164,7 +164,7 @@ namespace SourceGit
try try
{ {
// Fetch lastest release information. // Fetch lastest release information.
var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) };
var data = await client.GetStringAsync("https://sourcegit-scm.github.io/data/version.json"); var data = await client.GetStringAsync("https://sourcegit-scm.github.io/data/version.json");
// Parse json into Models.Version. // Parse json into Models.Version.

View file

@ -0,0 +1,20 @@
namespace SourceGit.Commands
{
public class IsConflictResolved : Command
{
public IsConflictResolved(string repo, Models.Change change)
{
var opt = new Models.DiffOption(change, true);
WorkingDirectory = repo;
Context = repo;
Args = $"diff -a --ignore-cr-at-eol --check {opt}";
}
public bool Result()
{
var rs = ReadToEnd();
return rs.IsSuccess;
}
}
}

View file

@ -388,11 +388,11 @@ namespace SourceGit.Models
} }
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")] [GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
private static partial Regex indicatorRegex(); private static partial Regex REG_INDICATOR();
private bool ProcessIndicatorForPatch(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool tailed) private bool ProcessIndicatorForPatch(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool tailed)
{ {
var match = indicatorRegex().Match(indicator.Content); var match = REG_INDICATOR().Match(indicator.Content);
var oldStart = int.Parse(match.Groups[1].Value); var oldStart = int.Parse(match.Groups[1].Value);
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds; var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
var oldCount = 0; var oldCount = 0;
@ -461,7 +461,7 @@ namespace SourceGit.Models
private bool ProcessIndicatorForPatchSingleSide(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool isOldSide, bool tailed) private bool ProcessIndicatorForPatchSingleSide(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool isOldSide, bool tailed)
{ {
var match = indicatorRegex().Match(indicator.Content); var match = REG_INDICATOR().Match(indicator.Content);
var oldStart = int.Parse(match.Groups[1].Value); var oldStart = int.Parse(match.Groups[1].Value);
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds; var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
var oldCount = 0; var oldCount = 0;

View file

@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
@ -19,8 +21,24 @@ namespace SourceGit.Models
{ {
get get
{ {
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute)); if (_isFirstTimeGetIcon)
return new Bitmap(icon); {
_isFirstTimeGetIcon = false;
if (!string.IsNullOrWhiteSpace(Icon))
{
try
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute));
_iconImage = new Bitmap(icon);
}
catch
{
}
}
}
return _iconImage;
} }
} }
@ -34,6 +52,41 @@ namespace SourceGit.Models
UseShellExecute = false, UseShellExecute = false,
}); });
} }
private bool _isFirstTimeGetIcon = true;
private Bitmap _iconImage = null;
}
public class JetBrainsState
{
[JsonPropertyName("version")]
public int Version { get; set; } = 0;
[JsonPropertyName("appVersion")]
public string AppVersion { get; set; } = string.Empty;
[JsonPropertyName("tools")]
public List<JetBrainsTool> Tools { get; set; } = new List<JetBrainsTool>();
}
public class JetBrainsTool
{
[JsonPropertyName("channelId")]
public string ChannelId { get; set; }
[JsonPropertyName("toolId")]
public string ToolId { get; set; }
[JsonPropertyName("productCode")]
public string ProductCode { get; set; }
[JsonPropertyName("tag")]
public string Tag { get; set; }
[JsonPropertyName("displayName")]
public string DisplayName { get; set; }
[JsonPropertyName("displayVersion")]
public string DisplayVersion { get; set; }
[JsonPropertyName("buildNumber")]
public string BuildNumber { get; set; }
[JsonPropertyName("installLocation")]
public string InstallLocation { get; set; }
[JsonPropertyName("launchCommand")]
public string LaunchCommand { get; set; }
} }
public class ExternalToolsFinder public class ExternalToolsFinder
@ -44,26 +97,6 @@ namespace SourceGit.Models
private set; private set;
} = new List<ExternalTool>(); } = new List<ExternalTool>();
public void VSCode(Func<string> platform_finder)
{
TryAdd("Visual Studio Code", "vscode", "\"{0}\"", "VSCODE_PATH", platform_finder);
}
public void VSCodeInsiders(Func<string> platform_finder)
{
TryAdd("Visual Studio Code - Insiders", "vscode_insiders", "\"{0}\"", "VSCODE_INSIDERS_PATH", platform_finder);
}
public void Fleet(Func<string> platform_finder)
{
TryAdd("JetBrains Fleet", "fleet", "\"{0}\"", "FLEET_PATH", platform_finder);
}
public void SublimeText(Func<string> platform_finder)
{
TryAdd("Sublime Text", "sublime_text", "\"{0}\"", "SUBLIME_TEXT_PATH", platform_finder);
}
public void TryAdd(string name, string icon, string args, string env, Func<string> finder) public void TryAdd(string name, string icon, string args, string env, Func<string> finder)
{ {
var path = Environment.GetEnvironmentVariable(env); var path = Environment.GetEnvironmentVariable(env);
@ -79,8 +112,52 @@ namespace SourceGit.Models
Name = name, Name = name,
Icon = icon, Icon = icon,
OpenCmdArgs = args, OpenCmdArgs = args,
Executable = path, Executable = path
}); });
} }
public void VSCode(Func<string> platform_finder)
{
TryAdd("Visual Studio Code", "vscode", "\"{0}\"", "VSCODE_PATH", platform_finder);
}
public void VSCodeInsiders(Func<string> platform_finder)
{
TryAdd("Visual Studio Code - Insiders", "vscode_insiders", "\"{0}\"", "VSCODE_INSIDERS_PATH", platform_finder);
}
public void Fleet(Func<string> platform_finder)
{
TryAdd("Fleet", "fleet", "\"{0}\"", "FLEET_PATH", platform_finder);
}
public void SublimeText(Func<string> platform_finder)
{
TryAdd("Sublime Text", "sublime_text", "\"{0}\"", "SUBLIME_TEXT_PATH", platform_finder);
}
public void FindJetBrainsFromToolbox(Func<string> platform_finder)
{
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 state = Path.Combine(platform_finder(), "state.json");
if (File.Exists(state))
{
var stateData = JsonSerializer.Deserialize(File.ReadAllText(state), JsonCodeGen.Default.JetBrainsState);
foreach (var tool in stateData.Tools)
{
if (exclude.Contains(tool.ToolId.ToLowerInvariant()))
continue;
Founded.Add(new ExternalTool
{
Name = $"{tool.DisplayName} {tool.DisplayVersion}",
Icon = supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : $"JetBrains/JB",
OpenCmdArgs = "\"{0}\"",
Executable = Path.Combine(tool.InstallLocation, tool.LaunchCommand),
});
}
}
}
} }
} }

View file

@ -57,7 +57,8 @@ namespace SourceGit.Native
var finder = new Models.ExternalToolsFinder(); var finder = new Models.ExternalToolsFinder();
finder.VSCode(() => FindExecutable("code")); finder.VSCode(() => FindExecutable("code"));
finder.VSCodeInsiders(() => FindExecutable("code-insiders")); finder.VSCodeInsiders(() => FindExecutable("code-insiders"));
finder.Fleet(FindJetBrainFleet); finder.Fleet(FindJetBrainsFleet);
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox");
finder.SublimeText(() => FindExecutable("subl")); finder.SublimeText(() => FindExecutable("subl"));
return finder.Founded; return finder.Founded;
} }
@ -174,7 +175,7 @@ namespace SourceGit.Native
return null; return null;
} }
private string FindJetBrainFleet() private string FindJetBrainsFleet()
{ {
var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox/apps/fleet/bin/Fleet"; var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox/apps/fleet/bin/Fleet";
return File.Exists(path) ? path : FindExecutable("fleet"); return File.Exists(path) ? path : FindExecutable("fleet");

View file

@ -33,6 +33,7 @@ namespace SourceGit.Native
finder.VSCode(() => "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"); finder.VSCode(() => "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code");
finder.VSCodeInsiders(() => "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code"); finder.VSCodeInsiders(() => "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code");
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet"); finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Library/Application Support/JetBrains/Toolbox");
finder.SublimeText(() => "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"); finder.SublimeText(() => "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl");
return finder.Founded; return finder.Founded;
} }

View file

@ -111,6 +111,7 @@ namespace SourceGit.Native
finder.VSCode(FindVSCode); finder.VSCode(FindVSCode);
finder.VSCodeInsiders(FindVSCodeInsiders); finder.VSCodeInsiders(FindVSCodeInsiders);
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe"); finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox");
finder.SublimeText(FindSublimeText); finder.SublimeText(FindSublimeText);
return finder.Founded; return finder.Founded;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -211,6 +211,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Go to next page</x:String> <x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Go to next page</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Create new page</x:String> <x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Create new page</x:String>
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">REPOSITORY</x:String> <x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">REPOSITORY</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">Force to reload this repository</x:String>
<x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">Stage/Unstage selected changes</x:String> <x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">Stage/Unstage selected changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">Toggle commit search</x:String> <x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">Toggle commit search</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">Switch to 'Changes'</x:String> <x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">Switch to 'Changes'</x:String>
@ -426,11 +427,13 @@
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sort</x:String> <x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sort</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">Changes</x:String> <x:String x:Key="Text.WorkingCopy" xml:space="preserve">Changes</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Amend</x:String> <x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Amend</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">You can stage this file now.</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String> <x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String> <x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">Enter commit message</x:String> <x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">Enter commit message</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String> <x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLICTS DETECTED</x:String> <x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLICTS DETECTED</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">FILE CONFLICTS ARE RESOLVED</x:String>
<x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">RECENT INPUT MESSAGES</x:String> <x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">RECENT INPUT MESSAGES</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUDE UNTRACKED FILES</x:String> <x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUDE UNTRACKED FILES</x:String>
<x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">MESSAGE HISTORIES</x:String> <x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">MESSAGE HISTORIES</x:String>

View file

@ -211,6 +211,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">切换到下一个页面</x:String> <x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">切换到下一个页面</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新建页面</x:String> <x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新建页面</x:String>
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">仓库页面快捷键</x:String> <x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">仓库页面快捷键</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">重新加载仓库状态</x:String>
<x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">将选中的变更暂存或从暂存列表中移除</x:String> <x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">将选中的变更暂存或从暂存列表中移除</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">打开/关闭历史搜索</x:String> <x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">打开/关闭历史搜索</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">显示本地更改</x:String> <x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">显示本地更改</x:String>
@ -426,11 +427,13 @@
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">排序</x:String> <x:String x:Key="Text.Welcome.Sort" xml:space="preserve">排序</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">本地更改</x:String> <x:String x:Key="Text.WorkingCopy" xml:space="preserve">本地更改</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">修补(--amend)</x:String> <x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">修补(--amend)</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">现在您已可将其加入暂存区中</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提交</x:String> <x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交并推送</x:String> <x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交并推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">填写提交信息</x:String> <x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">填写提交信息</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String> <x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">检测到冲突</x:String> <x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">检测到冲突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">文件冲突已解决</x:String>
<x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">最近输入的提交信息</x:String> <x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">最近输入的提交信息</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">显示未跟踪文件</x:String> <x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">显示未跟踪文件</x:String>
<x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">历史提交信息</x:String> <x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">历史提交信息</x:String>

View file

@ -88,9 +88,9 @@
<Setter.Value> <Setter.Value>
<MenuFlyout Placement="Bottom"> <MenuFlyout Placement="Bottom">
<MenuItem Header="{DynamicResource Text.Copy}" <MenuItem Header="{DynamicResource Text.Copy}"
Command="{Binding $parent[SelectableTextBlock].Copy}" Command="{Binding $parent[SelectableTextBlock].Copy}"
IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}" IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}"
InputGesture="{x:Static TextBox.CopyGesture}"> InputGesture="{x:Static TextBox.CopyGesture}">
<MenuItem.Icon> <MenuItem.Icon>
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/> <Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
</MenuItem.Icon> </MenuItem.Icon>
@ -487,21 +487,21 @@
<ControlTemplate> <ControlTemplate>
<Grid> <Grid>
<ContentPresenter Name="PART_ContentPresenter" <ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}" Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}" Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" /> HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
<Border Background="{DynamicResource Brush.FG2}" <Border Background="{DynamicResource Brush.FG2}"
Width="4" Width="4"
HorizontalAlignment="Left" VerticalAlignment="Stretch" HorizontalAlignment="Left" VerticalAlignment="Stretch"
IsHitTestVisible="False" IsHitTestVisible="False"
IsVisible="{TemplateBinding IsSelected}"/> IsVisible="{TemplateBinding IsSelected}"/>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
@ -529,16 +529,16 @@
</Border> </Border>
<Border BorderThickness="0" <Border BorderThickness="0"
Margin="8,4" Margin="8,4"
MaxWidth="{TemplateBinding MaxWidth}" MaxWidth="{TemplateBinding MaxWidth}"
MinHeight="{TemplateBinding MinHeight}" MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"> HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}"> <ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}" Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue" KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" /> Grid.IsSharedSizeScope="True" />
</ScrollViewer> </ScrollViewer>
</Border> </Border>
</Grid> </Grid>
@ -557,20 +557,20 @@
</Border> </Border>
<Border Name="LayoutRoot" <Border Name="LayoutRoot"
Margin="4" Margin="4"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0" BorderThickness="0"
Padding="4,2" Padding="4,2"
CornerRadius="{TemplateBinding CornerRadius}"> CornerRadius="{TemplateBinding CornerRadius}">
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
Theme="{StaticResource FluentMenuScrollViewer}" Theme="{StaticResource FluentMenuScrollViewer}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"> VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}" Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue" KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" /> Grid.IsSharedSizeScope="True" />
</ScrollViewer> </ScrollViewer>
</Border> </Border>
</Grid> </Grid>
@ -581,18 +581,18 @@
<Style Selector="MenuItem"> <Style Selector="MenuItem">
<Style.Resources> <Style.Resources>
<ControlTheme x:Key="{x:Type MenuItem}" TargetType="MenuItem"> <ControlTheme x:Key="{x:Type MenuItem}" TargetType="MenuItem">
<Setter Property="Height" Value="26"/> <Setter Property="Height" Value="28"/>
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}" /> <Setter Property="Foreground" Value="{DynamicResource Brush.FG1}" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Panel> <Panel>
<Border Name="PART_LayoutRoot" <Border Name="PART_LayoutRoot"
Padding="0,0,4,0" Padding="0,0,4,0"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3"> CornerRadius="3">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIcon" /> <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIcon" />
@ -603,44 +603,44 @@
<Border Grid.Column="0" Width="28"> <Border Grid.Column="0" Width="28">
<ContentControl x:Name="PART_IconPresenter" <ContentControl x:Name="PART_IconPresenter"
Content="{TemplateBinding Icon}" Content="{TemplateBinding Icon}"
IsVisible="False" IsVisible="False"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</Border> </Border>
<ContentPresenter Grid.Column="1" <ContentPresenter Grid.Column="1"
Name="PART_HeaderPresenter" Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplate="{TemplateBinding HeaderTemplate}"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
RecognizesAccessKey="True"/> RecognizesAccessKey="True"/>
<TextBlock x:Name="PART_InputGestureText" <TextBlock x:Name="PART_InputGestureText"
Grid.Column="2" Grid.Column="2"
Classes="CaptionTextBlockStyle" Classes="CaptionTextBlockStyle"
Margin="{DynamicResource MenuInputGestureTextMargin}" Margin="{DynamicResource MenuInputGestureTextMargin}"
Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}" Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}" Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
FontSize="11"/> FontSize="11"/>
<Path Name="PART_ChevronPath" <Path Name="PART_ChevronPath"
Width="6" Width="6"
Data="M 0 0 L 0 7 L 4 3.5 Z" Data="M573 512 215 881c-20 20-20 51 0 61l61 61c20 20 51 20 61 0l461-461c10-10 10-20 10-31s0-20-10-31L338 20C317 0 287 0 276 20L215 82c-20 20-20 51 0 61L573 512z"
Fill="{DynamicResource MenuFlyoutSubItemChevron}" Fill="{DynamicResource Brush.FG2}"
Margin="8,0,0,0" Margin="8,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Grid.Column="3" /> Grid.Column="3" />
</Grid> </Grid>
</Border> </Border>
<Popup Name="PART_Popup" <Popup Name="PART_Popup"
WindowManagerAddShadowHint="False" WindowManagerAddShadowHint="False"
Placement="RightEdgeAlignedTop" Placement="RightEdgeAlignedTop"
IsLightDismissEnabled="False" IsLightDismissEnabled="False"
HorizontalOffset="-4" HorizontalOffset="-4"
VerticalOffset="-4" VerticalOffset="-4"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"> IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Grid> <Grid>
<Border Background="{DynamicResource Brush.Popup}" Margin="4" CornerRadius="{DynamicResource OverlayCornerRadius}"> <Border Background="{DynamicResource Brush.Popup}" Margin="4" CornerRadius="{DynamicResource OverlayCornerRadius}">
<Border.Effect> <Border.Effect>
@ -649,16 +649,16 @@
</Border> </Border>
<Border BorderThickness="0" <Border BorderThickness="0"
Margin="8,4" Margin="8,4"
MaxWidth="{DynamicResource FlyoutThemeMaxWidth}" MaxWidth="{DynamicResource FlyoutThemeMaxWidth}"
MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}" MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}"> CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}"> <ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}" Margin="{DynamicResource MenuFlyoutScrollerMargin}"
Grid.IsSharedSizeScope="True" /> Grid.IsSharedSizeScope="True" />
</ScrollViewer> </ScrollViewer>
</Border> </Border>
</Grid> </Grid>
@ -834,26 +834,26 @@
<ControlTemplate> <ControlTemplate>
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*">
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="PART_IndicatorIcon" x:Name="PART_IndicatorIcon"
Margin="4,0,0,0" Margin="4,0,0,0"
Width="10" Width="10"
Data="{StaticResource Icons.Down}" Data="{StaticResource Icons.Down}"
Fill="{DynamicResource Brush.FG2}" Fill="{DynamicResource Brush.FG2}"
VerticalAlignment="Center" VerticalAlignment="Center"
RenderTransformOrigin="50%,50%" RenderTransformOrigin="50%,50%"
RenderTransform="rotate(270deg)"/> RenderTransform="rotate(270deg)"/>
<ContentPresenter Grid.Column="1" <ContentPresenter Grid.Column="1"
x:Name="PART_ContentPresenter" x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}" Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="0" Padding="0"
RecognizesAccessKey="True" RecognizesAccessKey="True"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" /> VerticalContentAlignment="Stretch" />
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
@ -880,13 +880,13 @@
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="PART_IndicatorIcon" x:Name="PART_IndicatorIcon"
Width="12" Width="12"
Data="{StaticResource Icons.Filter}" Data="{StaticResource Icons.Filter}"
Fill="Transparent" Fill="Transparent"
StrokeThickness="1" StrokeThickness="1"
Stroke="{DynamicResource Brush.FG2}" Stroke="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center" VerticalAlignment="Center"/> HorizontalAlignment="Center" VerticalAlignment="Center"/>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>
@ -906,15 +906,15 @@
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border Background="Transparent" <Border Background="Transparent"
Width="{TemplateBinding Width}" Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}" Height="{TemplateBinding Height}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"> VerticalAlignment="Center">
<Path x:Name="ChevronPath" <Path x:Name="ChevronPath"
Data="M 4 0 L 8 4 L 4 8 Z" Data="M 4 0 L 8 4 L 4 8 Z"
Fill="{DynamicResource TreeViewItemForeground}" Fill="{DynamicResource TreeViewItemForeground}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
@ -933,11 +933,11 @@
<ControlTemplate> <ControlTemplate>
<Border Background="Transparent"> <Border Background="Transparent">
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="PART_IndicatorIcon" x:Name="PART_IndicatorIcon"
Width="14" Height="14" Width="14" Height="14"
Stretch="Uniform" Stretch="Uniform"
Data="{StaticResource Icons.LayoutVertical}" Data="{StaticResource Icons.LayoutVertical}"
Fill="{DynamicResource Brush.FG1}" Fill="{DynamicResource Brush.FG1}"
Opacity=".8"/> Opacity=".8"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
@ -977,12 +977,12 @@
<ControlTemplate> <ControlTemplate>
<Border Background="Transparent"> <Border Background="Transparent">
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="PART_IndicatorIcon" x:Name="PART_IndicatorIcon"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
Stretch="Fill" Stretch="Fill"
Width="14" Height="10" Width="14" Height="10"
Data="{StaticResource Icons.EyeClose}" Data="{StaticResource Icons.EyeClose}"
Fill="{DynamicResource Brush.FG1}" Fill="{DynamicResource Brush.FG1}"
Opacity=".8"/> Opacity=".8"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
@ -1040,36 +1040,36 @@
<ControlTemplate> <ControlTemplate>
<StackPanel> <StackPanel>
<Border Name="PART_LayoutRoot" <Border Name="PART_LayoutRoot"
Classes="TreeViewItemLayoutRoot" Classes="TreeViewItemLayoutRoot"
Focusable="True" Focusable="True"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" CornerRadius="{TemplateBinding CornerRadius}"
MinHeight="{TemplateBinding MinHeight}" MinHeight="{TemplateBinding MinHeight}"
TemplatedControl.IsTemplateFocusTarget="True"> TemplatedControl.IsTemplateFocusTarget="True">
<Grid Name="PART_Header" ColumnDefinitions="16,*" Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}"> <Grid Name="PART_Header" ColumnDefinitions="16,*" Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
<Panel Name="PART_ExpandCollapseChevronContainer"> <Panel Name="PART_ExpandCollapseChevronContainer">
<ToggleButton Name="PART_ExpandCollapseChevron" <ToggleButton Name="PART_ExpandCollapseChevron"
Classes="tree_expander" Classes="tree_expander"
Focusable="False" Focusable="False"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" /> IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
</Panel> </Panel>
<ContentPresenter Name="PART_HeaderPresenter" <ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1" Grid.Column="1"
Focusable="False" Focusable="False"
Background="Transparent" Background="Transparent"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" /> Margin="{TemplateBinding Padding}" />
</Grid> </Grid>
</Border> </Border>
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}" IsVisible="{TemplateBinding IsExpanded}"
ItemsPanel="{TemplateBinding ItemsPanel}" /> ItemsPanel="{TemplateBinding ItemsPanel}" />
</StackPanel> </StackPanel>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>

View file

@ -23,6 +23,7 @@
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="App.ico" /> <AvaloniaResource Include="App.ico" />
<AvaloniaResource Include="Resources/ExternalToolIcons/*" /> <AvaloniaResource Include="Resources/ExternalToolIcons/*" />
<AvaloniaResource Include="Resources/ExternalToolIcons/JetBrains/*" />
<AvaloniaResource Include="Resources/Fonts/*" /> <AvaloniaResource Include="Resources/Fonts/*" />
<AvaloniaResource Include="Resources/ShellIcons/*" /> <AvaloniaResource Include="Resources/ShellIcons/*" />
</ItemGroup> </ItemGroup>

View file

@ -22,9 +22,9 @@ namespace SourceGit.ViewModels
Cmd = cmd; Cmd = cmd;
} }
public void Abort() public bool Abort()
{ {
new Commands.Command() return new Commands.Command()
{ {
WorkingDirectory = Repository, WorkingDirectory = Repository,
Context = Repository, Context = Repository,

View file

@ -34,7 +34,7 @@ namespace SourceGit.ViewModels
{ {
foreach (var b in rename._repo.Branches) foreach (var b in rename._repo.Branches)
{ {
if (b != rename.Target && b.Name == name) if (b.IsLocal && b != rename.Target && b.Name == name)
{ {
return new ValidationResult("A branch with same name already exists!!!"); return new ValidationResult("A branch with same name already exists!!!");
} }

View file

@ -233,17 +233,7 @@ namespace SourceGit.ViewModels
_inProgressContext = null; _inProgressContext = null;
_hasUnsolvedConflicts = false; _hasUnsolvedConflicts = false;
Task.Run(() => RefreshAll();
{
RefreshBranches();
RefreshTags();
RefreshCommits();
});
Task.Run(RefreshSubmodules);
Task.Run(RefreshWorkingCopyChanges);
Task.Run(RefreshStashes);
Task.Run(RefreshGitFlow);
} }
public void Close() public void Close()
@ -277,6 +267,21 @@ namespace SourceGit.ViewModels
_searchedCommits.Clear(); _searchedCommits.Clear();
} }
public void RefreshAll()
{
Task.Run(() =>
{
RefreshBranches();
RefreshTags();
RefreshCommits();
});
Task.Run(RefreshSubmodules);
Task.Run(RefreshWorkingCopyChanges);
Task.Run(RefreshStashes);
Task.Run(RefreshGitFlow);
}
public void OpenInFileManager() public void OpenInFileManager()
{ {
Native.OS.OpenInFileManager(_fullpath); Native.OS.OpenInFileManager(_fullpath);
@ -506,7 +511,11 @@ namespace SourceGit.ViewModels
if (_inProgressContext != null) if (_inProgressContext != null)
{ {
SetWatcherEnabled(false); SetWatcherEnabled(false);
await Task.Run(_inProgressContext.Abort); var succ = await Task.Run(_inProgressContext.Abort);
if (succ && _workingCopy != null)
{
_workingCopy.CommitMessage = string.Empty;
}
SetWatcherEnabled(true); SetWatcherEnabled(true);
} }
else else

View file

@ -1,6 +1,7 @@
using System; using System;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -79,7 +80,7 @@ namespace SourceGit.ViewModels
SearchFilter = string.Empty; SearchFilter = string.Empty;
} }
public void AddFolder() public void AddRootNode()
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new CreateGroup(null)); PopupHost.ShowPopup(new CreateGroup(null));
@ -90,6 +91,72 @@ namespace SourceGit.ViewModels
Preference.MoveNode(from, to); Preference.MoveNode(from, to);
} }
public ContextMenu CreateContextMenu(RepositoryNode node)
{
var menu = new ContextMenu();
var hasRepo = Preference.FindRepository(node.Id) != null;
var edit = new MenuItem();
edit.Header = App.Text("Welcome.Edit");
edit.Icon = App.CreateMenuIcon("Icons.Edit");
edit.IsEnabled = !node.IsRepository || hasRepo;
edit.Click += (_, e) =>
{
node.Edit();
e.Handled = true;
};
menu.Items.Add(edit);
if (node.IsRepository)
{
var explore = new MenuItem();
explore.Header = App.Text("Repository.Explore");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.IsEnabled = hasRepo;
explore.Click += (_, e) =>
{
node.OpenInFileManager();
e.Handled = true;
};
menu.Items.Add(explore);
var terminal = new MenuItem();
terminal.Header = App.Text("Repository.Terminal");
terminal.Icon = App.CreateMenuIcon("Icons.Terminal");
terminal.IsEnabled = hasRepo;
terminal.Click += (_, e) =>
{
node.OpenTerminal();
e.Handled = true;
};
menu.Items.Add(terminal);
}
else
{
var addSubFolder = new MenuItem();
addSubFolder.Header = App.Text("Welcome.AddSubFolder");
addSubFolder.Icon = App.CreateMenuIcon("Icons.Folder.Add");
addSubFolder.Click += (_, e) =>
{
node.AddSubFolder();
e.Handled = true;
};
menu.Items.Add(addSubFolder);
}
var delete = new MenuItem();
delete.Header = App.Text("Welcome.Delete");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (_, e) =>
{
node.Delete();
e.Handled = true;
};
menu.Items.Add(delete);
return menu;
}
private void Referesh() private void Referesh()
{ {
if (string.IsNullOrWhiteSpace(_searchFilter)) if (string.IsNullOrWhiteSpace(_searchFilter))

View file

@ -12,9 +12,27 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
{ {
public class ConflictContext public class ConflictContext : ObservableObject
{ {
public Models.Change Change { get; set; } public bool IsResolved
{
get => _isResolved;
set => SetProperty(ref _isResolved, value);
}
public ConflictContext(string repo, Models.Change change)
{
Task.Run(() =>
{
var result = new Commands.IsConflictResolved(repo, change).Result();
Dispatcher.UIThread.Post(() =>
{
IsResolved = result;
});
});
}
private bool _isResolved = false;
} }
public class WorkingCopy : ObservableObject public class WorkingCopy : ObservableObject
@ -274,6 +292,14 @@ namespace SourceGit.ViewModels
SelectedStagedTreeNode = null; SelectedStagedTreeNode = null;
SetDetail(null, false); SetDetail(null, false);
} }
// Try to load merge message from MERGE_MSG
if (string.IsNullOrEmpty(_commitMessage))
{
var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG");
if (File.Exists(mergeMsgFile))
CommitMessage = File.ReadAllText(mergeMsgFile);
}
}); });
return hasConflict; return hasConflict;
@ -288,9 +314,9 @@ namespace SourceGit.ViewModels
{ {
DetailContext = null; DetailContext = null;
} }
else if (change.IsConflit) else if (change.IsConflit && isUnstaged)
{ {
DetailContext = new ConflictContext() { Change = change }; DetailContext = new ConflictContext(_repo.FullPath, change);
} }
else else
{ {
@ -424,26 +450,23 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);
} }
public async void UseExternalMergeTool() public async void UseExternalMergeTool(Models.Change change)
{ {
if (_detailContext is ConflictContext ctx) var type = Preference.Instance.ExternalMergeToolType;
var exec = Preference.Instance.ExternalMergeToolPath;
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
if (tool == null)
{ {
var type = Preference.Instance.ExternalMergeToolType; App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
var exec = Preference.Instance.ExternalMergeToolPath; return;
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
if (tool == null)
{
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
return;
}
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
_repo.SetWatcherEnabled(false);
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, ctx.Change.Path));
_repo.SetWatcherEnabled(true);
} }
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
_repo.SetWatcherEnabled(false);
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
_repo.SetWatcherEnabled(true);
} }
public async void DoCommit(bool autoPush) public async void DoCommit(bool autoPush)
@ -546,7 +569,7 @@ namespace SourceGit.ViewModels
openMerger.Header = App.Text("FileCM.OpenWithExternalMerger"); openMerger.Header = App.Text("FileCM.OpenWithExternalMerger");
openMerger.Click += (_, e) => openMerger.Click += (_, e) =>
{ {
UseExternalMergeTool(); UseExternalMergeTool(change);
e.Handled = true; e.Handled = true;
}; };

View file

@ -99,7 +99,7 @@
<!-- Messages --> <!-- Messages -->
<TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" /> <TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" />
<ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,0,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<SelectableTextBlock Text="{Binding FullMessage}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/> <SelectableTextBlock Text="{Binding FullMessage}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View file

@ -87,7 +87,7 @@
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Increase}}" FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Increase}}"
Margin="0,8"/> Margin="0,8"/>
<Grid RowDefinitions="20,20,20,20,20" ColumnDefinitions="80,*"> <Grid RowDefinitions="20,20,20,20,20,20" ColumnDefinitions="80,*">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="monospace bold" Text="Ctrl+F"/> <TextBlock Grid.Row="0" Grid.Column="0" Classes="monospace bold" Text="Ctrl+F"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.ToggleSearch}" /> <TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.ToggleSearch}" />
@ -102,6 +102,9 @@
<TextBlock Grid.Row="4" Grid.Column="0" Classes="monospace bold" Text="SPACE"/> <TextBlock Grid.Row="4" Grid.Column="0" Classes="monospace bold" Text="SPACE"/>
<TextBlock Grid.Row="4" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.StageOrUnstageSelected}" /> <TextBlock Grid.Row="4" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.StageOrUnstageSelected}" />
<TextBlock Grid.Row="5" Grid.Column="0" Classes="monospace bold" Text="F5"/>
<TextBlock Grid.Row="5" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
</Grid> </Grid>
<TextBlock Text="{DynamicResource Text.Hotkeys.TextEditor}" <TextBlock Text="{DynamicResource Text.Hotkeys.TextEditor}"

View file

@ -146,6 +146,15 @@ namespace SourceGit.Views
e.Handled = true; e.Handled = true;
return; return;
} }
else if (e.Key == Key.F5)
{
if (vm.ActivePage.Data is ViewModels.Repository repo)
{
repo.RefreshAll();
e.Handled = true;
return;
}
}
base.OnKeyDown(e); base.OnKeyDown(e);
} }

View file

@ -7,6 +7,7 @@ using System.Text;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -657,6 +658,7 @@ namespace SourceGit.Views
UpdateTextMate(); UpdateTextMate();
TextArea.PointerWheelChanged += OnTextAreaPointerWheelChanged;
TextArea.TextView.ContextRequested += OnTextViewContextRequested; TextArea.TextView.ContextRequested += OnTextViewContextRequested;
} }
@ -676,21 +678,20 @@ namespace SourceGit.Views
_textMate = null; _textMate = null;
} }
TextArea.PointerWheelChanged -= OnTextAreaPointerWheelChanged;
TextArea.TextView.ContextRequested -= OnTextViewContextRequested; TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
GC.Collect(); GC.Collect();
} }
private void OnTextAreaPointerWheelChanged(object sender, PointerWheelEventArgs e)
{
if (!TextArea.IsFocused) Focus();
}
private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e) private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e)
{ {
if (_syncScrollingByOthers) if (TextArea.IsFocused) SetCurrentValue(SyncScrollOffsetProperty, _scrollViewer.Offset);
{
_syncScrollingByOthers = false;
}
else
{
SetCurrentValue(SyncScrollOffsetProperty, _scrollViewer.Offset);
}
} }
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e) private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
@ -754,24 +755,8 @@ namespace SourceGit.Views
} }
else if (change.Property == SyncScrollOffsetProperty) else if (change.Property == SyncScrollOffsetProperty)
{ {
if (_scrollViewer == null) if (!TextArea.IsFocused && _scrollViewer != null)
return; _scrollViewer.Offset = SyncScrollOffset;
var curOffset = _scrollViewer.Offset;
if (!curOffset.Equals(SyncScrollOffset))
{
_syncScrollingByOthers = true;
if (curOffset.X != SyncScrollOffset.X)
{
var offset = new Vector(Math.Min(_scrollViewer.ScrollBarMaximum.X, SyncScrollOffset.X), SyncScrollOffset.Y);
_scrollViewer.Offset = offset;
}
else
{
_scrollViewer.Offset = SyncScrollOffset;
}
}
} }
else if (change.Property == UseSyntaxHighlightingProperty) else if (change.Property == UseSyntaxHighlightingProperty)
{ {
@ -813,7 +798,6 @@ namespace SourceGit.Views
private TextMate.Installation _textMate; private TextMate.Installation _textMate;
private readonly LineStyleTransformer _lineStyleTransformer = null; private readonly LineStyleTransformer _lineStyleTransformer = null;
private ScrollViewer _scrollViewer = null; private ScrollViewer _scrollViewer = null;
private bool _syncScrollingByOthers = false;
} }
public partial class TextDiffView : UserControl public partial class TextDiffView : UserControl

View file

@ -57,7 +57,7 @@
LostFocus="OnTreeViewLostFocus"> LostFocus="OnTreeViewLostFocus">
<TreeView.ContextMenu> <TreeView.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{DynamicResource Text.Welcome.AddRootFolder}" Command="{Binding AddFolder}"> <MenuItem Header="{DynamicResource Text.Welcome.AddRootFolder}" Command="{Binding AddRootNode}">
<MenuItem.Icon> <MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Add}"/> <Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Add}"/>
</MenuItem.Icon> </MenuItem.Icon>
@ -70,37 +70,6 @@
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsVisible" Value="{Binding IsVisible}"/> <Setter Property="IsVisible" Value="{Binding IsVisible}"/>
<Setter Property="CornerRadius" Value="4"/> <Setter Property="CornerRadius" Value="4"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{DynamicResource Text.Welcome.Edit}" Command="{Binding Edit}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Edit}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Welcome.AddSubFolder}" Command="{Binding AddSubFolder}" IsVisible="{Binding !IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Add}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Repository.Explore}" Command="{Binding OpenInFileManager}" IsVisible="{Binding IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Open}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Repository.Terminal}" Command="{Binding OpenTerminal}" IsVisible="{Binding IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Terminal}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Welcome.Delete}" Command="{Binding Delete}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Clear}"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
@ -109,11 +78,12 @@
<Grid Height="30" <Grid Height="30"
ColumnDefinitions="Auto,Auto,*" ColumnDefinitions="Auto,Auto,*"
Background="Transparent" Background="Transparent"
Loaded="SetupTreeNodeDragAndDrop"
ContextRequested="OnTreeNodeContextRequested"
PointerPressed="OnPointerPressedTreeNode" PointerPressed="OnPointerPressedTreeNode"
PointerMoved="OnPointerMovedOverTreeNode" PointerMoved="OnPointerMovedOverTreeNode"
PointerReleased="OnPointerReleasedOnTreeNode" PointerReleased="OnPointerReleasedOnTreeNode"
DoubleTapped="OnDoubleTappedTreeNode" DoubleTapped="OnDoubleTappedTreeNode"
Loaded="SetupTreeNodeDragAndDrop"
ClipToBounds="True"> ClipToBounds="True">
<Path Grid.Column="0" Width="12" Height="12" Margin="0,0,8,0" <Path Grid.Column="0" Width="12" Height="12" Margin="0,0,8,0"
Fill="{Binding Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}" Fill="{Binding Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"

View file

@ -37,6 +37,16 @@ namespace SourceGit.Views
} }
} }
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (sender is Grid grid && DataContext is ViewModels.Welcome vm)
{
var menu = vm.CreateContextMenu(grid.DataContext as ViewModels.RepositoryNode);
menu?.Open(grid);
e.Handled = true;
}
}
private void OnPointerPressedTreeNode(object sender, PointerPressedEventArgs e) private void OnPointerPressedTreeNode(object sender, PointerPressedEventArgs e)
{ {
if (e.GetCurrentPoint(sender as Visual).Properties.IsLeftButtonPressed) if (e.GetCurrentPoint(sender as Visual).Properties.IsLeftButtonPressed)

View file

@ -319,11 +319,19 @@
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate DataType="vm:ConflictContext"> <DataTemplate DataType="vm:ConflictContext">
<Border Background="{DynamicResource Brush.Window}" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"> <Border Background="{DynamicResource Brush.Window}" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Width="64" Height="64" Data="{StaticResource Icons.Conflict}" Fill="{DynamicResource Brush.FG2}"/> <StackPanel Orientation="Vertical" IsVisible="{Binding !IsResolved}">
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/> <Path Width="64" Height="64" Data="{StaticResource Icons.Conflict}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.ResolveTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/> <TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel> <TextBlock Text="{DynamicResource Text.WorkingCopy.ResolveTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Vertical" IsVisible="{Binding IsResolved}">
<Path Width="64" Height="64" Data="{StaticResource Icons.Check}" Fill="Green"/>
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts.Resolved}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.CanStageTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>