mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
Merge branch 'release/v8.21'
This commit is contained in:
commit
791b5bd6f8
200 changed files with 3798 additions and 3383 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -604,3 +604,4 @@ build/*.zip
|
|||
build/*.tar.gz
|
||||
build/*.deb
|
||||
build/*.rpm
|
||||
build/*.AppImage
|
||||
|
|
|
@ -44,7 +44,7 @@ You can download the latest stable from [Releases](https://github.com/sourcegit-
|
|||
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}/.config/SourceGit` |
|
||||
| macOS | `${HOME}/Library/Application Support/SourceGit` |
|
||||
|
@ -75,7 +75,7 @@ For **Linux** users:
|
|||
This app supports open repository in external tools listed in the table below.
|
||||
|
||||
| Tool | Windows | macOS | Linux | Environment Variable |
|
||||
| --- | --- | --- | --- | --- |
|
||||
|-------------------------------|---------|-------|-------|----------------------|
|
||||
| Visual Studio Code | YES | YES | YES | VSCODE_PATH |
|
||||
| Visual Studio Code - Insiders | YES | YES | YES | VSCODE_INSIDERS_PATH |
|
||||
| VSCodium | YES | YES | YES | VSCODIUM_PATH |
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.20
|
||||
8.21
|
|
@ -1,13 +1,67 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)]
|
||||
[JsonSerializable(typeof(List<Models.InteractiveRebaseJob>))]
|
||||
public class ColorConverter : JsonConverter<Color>
|
||||
{
|
||||
public override Color Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return Color.Parse(reader.GetString());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Color value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public class FontFamilyConverter : JsonConverter<FontFamily>
|
||||
{
|
||||
public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var name = reader.GetString();
|
||||
return new FontFamily(name);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public class GridLengthConverter : JsonConverter<GridLength>
|
||||
{
|
||||
public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var size = reader.GetDouble();
|
||||
return new GridLength(size, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, GridLength value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(
|
||||
WriteIndented = true,
|
||||
IgnoreReadOnlyFields = true,
|
||||
IgnoreReadOnlyProperties = true,
|
||||
Converters = [
|
||||
typeof(ColorConverter),
|
||||
typeof(FontFamilyConverter),
|
||||
typeof(GridLengthConverter),
|
||||
]
|
||||
)]
|
||||
[JsonSerializable(typeof(Models.InteractiveRebaseJobCollection))]
|
||||
[JsonSerializable(typeof(Models.JetBrainsState))]
|
||||
[JsonSerializable(typeof(Models.ThemeOverrides))]
|
||||
[JsonSerializable(typeof(Models.Version))]
|
||||
[JsonSerializable(typeof(Models.CustomColorSchema))]
|
||||
[JsonSerializable(typeof(ViewModels.Preference))]
|
||||
[JsonSerializable(typeof(ViewModels.RepositorySettings))]
|
||||
internal partial class JsonCodeGen : JsonSerializerContext { }
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||
<StyleInclude Source="avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"/>
|
||||
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
||||
<StyleInclude Source="/Resources/Styles.axaml"/>
|
||||
</Application.Styles>
|
||||
|
|
331
src/App.axaml.cs
331
src/App.axaml.cs
|
@ -44,34 +44,29 @@ namespace SourceGit
|
|||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
|
||||
{
|
||||
LogException(e.ExceptionObject as Exception);
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (_, e) =>
|
||||
{
|
||||
LogException(e.Exception);
|
||||
e.SetObserved();
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
if (args.Length > 1 && args[0].Equals("--rebase-editor", StringComparison.Ordinal))
|
||||
Environment.Exit(Models.InteractiveRebaseEditor.Process(args[1]));
|
||||
if (TryLaunchedAsRebaseTodoEditor(args, out int exitTodo))
|
||||
Environment.Exit(exitTodo);
|
||||
else if (TryLaunchedAsRebaseMessageEditor(args, out int exitMessage))
|
||||
Environment.Exit(exitMessage);
|
||||
else
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
|
||||
builder.Append("----------------------------\n");
|
||||
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
|
||||
builder.Append($"OS: {Environment.OSVersion.ToString()}\n");
|
||||
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
|
||||
builder.Append($"Source: {ex.Source}\n");
|
||||
builder.Append($"---------------------------\n\n");
|
||||
builder.Append(ex.StackTrace);
|
||||
while (ex.InnerException != null)
|
||||
{
|
||||
ex = ex.InnerException;
|
||||
builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n");
|
||||
builder.Append(ex.StackTrace);
|
||||
}
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
|
||||
File.WriteAllText(file, builder.ToString());
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,20 +89,32 @@ namespace SourceGit
|
|||
|
||||
public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() =>
|
||||
{
|
||||
var toplevel = GetTopLevel() as Window;
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.Preference();
|
||||
dialog.ShowDialog(GetTopLevel() as Window);
|
||||
dialog.ShowDialog(toplevel);
|
||||
});
|
||||
|
||||
public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() =>
|
||||
{
|
||||
var toplevel = GetTopLevel() as Window;
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.Hotkeys();
|
||||
dialog.ShowDialog(GetTopLevel() as Window);
|
||||
dialog.ShowDialog(toplevel);
|
||||
});
|
||||
|
||||
public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() =>
|
||||
{
|
||||
var toplevel = GetTopLevel() as Window;
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.About();
|
||||
dialog.ShowDialog(GetTopLevel() as Window);
|
||||
dialog.ShowDialog(toplevel);
|
||||
});
|
||||
|
||||
public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() =>
|
||||
|
@ -115,7 +122,7 @@ namespace SourceGit
|
|||
Check4Update(true);
|
||||
});
|
||||
|
||||
public static readonly SimpleCommand QuitCommand = new SimpleCommand(Quit);
|
||||
public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0));
|
||||
|
||||
public static void RaiseException(string context, string message)
|
||||
{
|
||||
|
@ -132,7 +139,7 @@ namespace SourceGit
|
|||
public static void SetLocale(string localeKey)
|
||||
{
|
||||
var app = Current as App;
|
||||
var targetLocale = app.Resources[localeKey] as ResourceDictionary;
|
||||
var targetLocale = app?.Resources[localeKey] as ResourceDictionary;
|
||||
if (targetLocale == null || targetLocale == app._activeLocale)
|
||||
return;
|
||||
|
||||
|
@ -143,9 +150,11 @@ namespace SourceGit
|
|||
app._activeLocale = targetLocale;
|
||||
}
|
||||
|
||||
public static void SetTheme(string theme, string colorsFile)
|
||||
public static void SetTheme(string theme, string themeOverridesFile)
|
||||
{
|
||||
var app = Current as App;
|
||||
if (app == null)
|
||||
return;
|
||||
|
||||
if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase))
|
||||
app.RequestedThemeVariant = ThemeVariant.Light;
|
||||
|
@ -154,63 +163,59 @@ namespace SourceGit
|
|||
else
|
||||
app.RequestedThemeVariant = ThemeVariant.Default;
|
||||
|
||||
if (app._colorOverrides != null)
|
||||
if (app._themeOverrides != null)
|
||||
{
|
||||
app.Resources.MergedDictionaries.Remove(app._colorOverrides);
|
||||
app._colorOverrides = null;
|
||||
app.Resources.MergedDictionaries.Remove(app._themeOverrides);
|
||||
app._themeOverrides = null;
|
||||
}
|
||||
|
||||
Models.CommitGraph.SetDefaultPens();
|
||||
|
||||
if (!string.IsNullOrEmpty(colorsFile) && File.Exists(colorsFile))
|
||||
if (!string.IsNullOrEmpty(themeOverridesFile) && File.Exists(themeOverridesFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
var resDic = new ResourceDictionary();
|
||||
|
||||
var schema = JsonSerializer.Deserialize(File.ReadAllText(colorsFile), JsonCodeGen.Default.CustomColorSchema);
|
||||
foreach (var kv in schema.Basic)
|
||||
var overrides = JsonSerializer.Deserialize(File.ReadAllText(themeOverridesFile), JsonCodeGen.Default.ThemeOverrides);
|
||||
foreach (var kv in overrides.BasicColors)
|
||||
{
|
||||
if (kv.Key.Equals("SystemAccentColor", StringComparison.Ordinal))
|
||||
resDic["SystemAccentColor"] = Color.Parse(kv.Value);
|
||||
resDic["SystemAccentColor"] = kv.Value;
|
||||
else
|
||||
resDic[$"Color.{kv.Key}"] = Color.Parse(kv.Value);
|
||||
resDic[$"Color.{kv.Key}"] = kv.Value;
|
||||
}
|
||||
|
||||
|
||||
if (schema.Graph.Count > 0)
|
||||
{
|
||||
var penColors = new List<Color>();
|
||||
|
||||
foreach (var c in schema.Graph)
|
||||
penColors.Add(Color.Parse(c));
|
||||
|
||||
Models.CommitGraph.SetPenColors(penColors);
|
||||
}
|
||||
if (overrides.GraphColors.Count > 0)
|
||||
Models.CommitGraph.SetPens(overrides.GraphColors, overrides.GraphPenThickness);
|
||||
else
|
||||
Models.CommitGraph.SetDefaultPens(overrides.GraphPenThickness);
|
||||
|
||||
app.Resources.MergedDictionaries.Add(resDic);
|
||||
app._colorOverrides = resDic;
|
||||
app._themeOverrides = resDic;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Models.CommitGraph.SetDefaultPens();
|
||||
}
|
||||
}
|
||||
|
||||
public static async void CopyText(string data)
|
||||
{
|
||||
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
if (desktop.MainWindow.Clipboard is { } clipbord)
|
||||
if (desktop.MainWindow?.Clipboard is { } clipbord)
|
||||
await clipbord.SetTextAsync(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetClipboardTextAsync()
|
||||
{
|
||||
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
if (desktop.MainWindow.Clipboard is { } clipboard)
|
||||
if (desktop.MainWindow?.Clipboard is { } clipboard)
|
||||
{
|
||||
return await clipboard.GetTextAsync();
|
||||
}
|
||||
|
@ -220,7 +225,7 @@ namespace SourceGit
|
|||
|
||||
public static string Text(string key, params object[] args)
|
||||
{
|
||||
var fmt = Current.FindResource($"Text.{key}") as string;
|
||||
var fmt = Current?.FindResource($"Text.{key}") as string;
|
||||
if (string.IsNullOrWhiteSpace(fmt))
|
||||
return $"Text.{key}";
|
||||
|
||||
|
@ -236,16 +241,21 @@ namespace SourceGit
|
|||
icon.Width = 12;
|
||||
icon.Height = 12;
|
||||
icon.Stretch = Stretch.Uniform;
|
||||
icon.Data = Current.FindResource(key) as StreamGeometry;
|
||||
|
||||
var geo = Current?.FindResource(key) as StreamGeometry;
|
||||
if (geo != null)
|
||||
icon.Data = geo;
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static TopLevel GetTopLevel()
|
||||
{
|
||||
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
return desktop.MainWindow;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -305,12 +315,16 @@ namespace SourceGit
|
|||
return null;
|
||||
}
|
||||
|
||||
public static void Quit()
|
||||
public static void Quit(int exitCode)
|
||||
{
|
||||
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow.Close();
|
||||
desktop.Shutdown();
|
||||
desktop.MainWindow?.Close();
|
||||
desktop.Shutdown(exitCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,7 +335,7 @@ namespace SourceGit
|
|||
var pref = ViewModels.Preference.Instance;
|
||||
|
||||
SetLocale(pref.Locale);
|
||||
SetTheme(pref.Theme, pref.ColorOverrides);
|
||||
SetTheme(pref.Theme, pref.ThemeOverrides);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
|
@ -329,9 +343,178 @@ namespace SourceGit
|
|||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
BindingPlugins.DataValidators.RemoveAt(0);
|
||||
|
||||
if (TryLaunchedAsCoreEditor(desktop))
|
||||
return;
|
||||
|
||||
if (TryLaunchedAsAskpass(desktop))
|
||||
return;
|
||||
|
||||
TryLaunchedAsNormal(desktop);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogException(Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
return;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
|
||||
builder.Append("----------------------------\n");
|
||||
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
|
||||
builder.Append($"OS: {Environment.OSVersion.ToString()}\n");
|
||||
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
|
||||
builder.Append($"Source: {ex.Source}\n");
|
||||
builder.Append($"---------------------------\n\n");
|
||||
builder.Append(ex.StackTrace);
|
||||
while (ex.InnerException != null)
|
||||
{
|
||||
ex = ex.InnerException;
|
||||
builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n");
|
||||
builder.Append(ex.StackTrace);
|
||||
}
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
|
||||
File.WriteAllText(file, builder.ToString());
|
||||
}
|
||||
|
||||
private static void ShowSelfUpdateResult(object data)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop)
|
||||
{
|
||||
var dialog = new Views.SelfUpdate()
|
||||
{
|
||||
DataContext = new ViewModels.SelfUpdate() { Data = data }
|
||||
};
|
||||
|
||||
dialog.Show(desktop.MainWindow);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static bool TryLaunchedAsRebaseTodoEditor(string[] args, out int exitCode)
|
||||
{
|
||||
exitCode = -1;
|
||||
|
||||
if (args.Length <= 1 || !args[0].Equals("--rebase-todo-editor", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
var file = args[1];
|
||||
var filename = Path.GetFileName(file);
|
||||
if (!filename.Equals("git-rebase-todo", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
var dirInfo = new DirectoryInfo(Path.GetDirectoryName(file)!);
|
||||
if (!dirInfo.Exists || !dirInfo.Name.Equals("rebase-merge", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
||||
var jobsFile = Path.Combine(dirInfo.Parent!.FullName, "sourcegit_rebase_jobs.json");
|
||||
if (!File.Exists(jobsFile))
|
||||
return true;
|
||||
|
||||
var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
|
||||
var lines = new List<string>();
|
||||
foreach (var job in collection.Jobs)
|
||||
{
|
||||
switch (job.Action)
|
||||
{
|
||||
case Models.InteractiveRebaseAction.Pick:
|
||||
lines.Add($"p {job.SHA}");
|
||||
break;
|
||||
case Models.InteractiveRebaseAction.Edit:
|
||||
lines.Add($"e {job.SHA}");
|
||||
break;
|
||||
case Models.InteractiveRebaseAction.Reword:
|
||||
lines.Add($"r {job.SHA}");
|
||||
break;
|
||||
case Models.InteractiveRebaseAction.Squash:
|
||||
lines.Add($"s {job.SHA}");
|
||||
break;
|
||||
case Models.InteractiveRebaseAction.Fixup:
|
||||
lines.Add($"f {job.SHA}");
|
||||
break;
|
||||
default:
|
||||
lines.Add($"d {job.SHA}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(file, lines);
|
||||
|
||||
exitCode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryLaunchedAsRebaseMessageEditor(string[] args, out int exitCode)
|
||||
{
|
||||
exitCode = -1;
|
||||
|
||||
if (args.Length <= 1 || !args[0].Equals("--rebase-message-editor", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
var file = args[1];
|
||||
var filename = Path.GetFileName(file);
|
||||
if (!filename.Equals("COMMIT_EDITMSG", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
var jobsFile = Path.Combine(Path.GetDirectoryName(file)!, "sourcegit_rebase_jobs.json");
|
||||
if (!File.Exists(jobsFile))
|
||||
return true;
|
||||
|
||||
var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
|
||||
var doneFile = Path.Combine(Path.GetDirectoryName(file)!, "rebase-merge", "done");
|
||||
if (!File.Exists(doneFile))
|
||||
return true;
|
||||
|
||||
var done = File.ReadAllText(doneFile).Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (done.Length > collection.Jobs.Count)
|
||||
return true;
|
||||
|
||||
var job = collection.Jobs[done.Length - 1];
|
||||
File.WriteAllText(file, job.Message);
|
||||
|
||||
exitCode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLaunchedAsCoreEditor(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var args = desktop.Args;
|
||||
if (args == null || args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
var file = args[1];
|
||||
if (!File.Exists(file))
|
||||
desktop.Shutdown(-1);
|
||||
else
|
||||
desktop.MainWindow = new Views.StandaloneCommitMessageEditor(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var args = desktop.Args;
|
||||
if (args == null || args.Length != 1 || !args[0].StartsWith("Enter passphrase", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
desktop.MainWindow = new Views.Askpass(args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryLaunchedAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
Native.OS.SetupEnternalTools();
|
||||
|
||||
_launcher = new ViewModels.Launcher();
|
||||
string startupRepo = null;
|
||||
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))
|
||||
startupRepo = desktop.Args[0];
|
||||
|
||||
_launcher = new ViewModels.Launcher(startupRepo);
|
||||
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
|
||||
|
||||
var pref = ViewModels.Preference.Instance;
|
||||
|
@ -342,30 +525,8 @@ namespace SourceGit
|
|||
}
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private static void ShowSelfUpdateResult(object data)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var dialog = new Views.SelfUpdate()
|
||||
{
|
||||
DataContext = new ViewModels.SelfUpdate
|
||||
{
|
||||
Data = data
|
||||
}
|
||||
};
|
||||
|
||||
dialog.Show(desktop.MainWindow);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ViewModels.Launcher _launcher = null;
|
||||
private ResourceDictionary _activeLocale = null;
|
||||
private ResourceDictionary _colorOverrides = null;
|
||||
private ResourceDictionary _themeOverrides = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,12 @@
|
|||
var cmd = new Command();
|
||||
cmd.WorkingDirectory = repo;
|
||||
cmd.Context = repo;
|
||||
|
||||
if (string.IsNullOrEmpty(upstream))
|
||||
{
|
||||
cmd.Args = $"branch {name} --unset-upstream";
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.Args = $"branch {name} -u {upstream}";
|
||||
}
|
||||
|
||||
return cmd.Exec();
|
||||
}
|
||||
|
||||
|
@ -50,18 +48,8 @@
|
|||
var cmd = new Command();
|
||||
cmd.WorkingDirectory = repo;
|
||||
cmd.Context = repo;
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
cmd.Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
cmd.Args += $"push {remote} --delete {name}";
|
||||
cmd.SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
cmd.Args = $"push {remote} --delete {name}";
|
||||
return cmd.Exec();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,21 +11,14 @@ namespace SourceGit.Commands
|
|||
Context = ctx;
|
||||
WorkingDirectory = path;
|
||||
TraitErrorAsOutput = true;
|
||||
|
||||
if (string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
|
||||
Args += "clone --progress --verbose --recurse-submodules ";
|
||||
SSHKey = sshKey;
|
||||
Args = "clone --progress --verbose --recurse-submodules ";
|
||||
|
||||
if (!string.IsNullOrEmpty(extraArgs))
|
||||
Args += $"{extraArgs} ";
|
||||
|
||||
Args += $"{url} ";
|
||||
|
||||
if (!string.IsNullOrEmpty(localName))
|
||||
Args += localName;
|
||||
|
||||
|
|
|
@ -19,12 +19,20 @@ namespace SourceGit.Commands
|
|||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string StdOut { get; set; }
|
||||
public string StdErr { get; set; }
|
||||
}
|
||||
|
||||
public enum EditorType
|
||||
{
|
||||
None,
|
||||
CoreEditor,
|
||||
RebaseEditor,
|
||||
}
|
||||
|
||||
public string Context { get; set; } = string.Empty;
|
||||
public CancelToken Cancel { get; set; } = null;
|
||||
public string WorkingDirectory { get; set; } = null;
|
||||
public EditorType Editor { get; set; } = EditorType.CoreEditor; // Only used in Exec() mode
|
||||
public string SSHKey { get; set; } = string.Empty;
|
||||
public string Args { get; set; } = string.Empty;
|
||||
public bool RaiseError { get; set; } = true;
|
||||
public bool TraitErrorAsOutput { get; set; } = false;
|
||||
|
@ -33,7 +41,7 @@ namespace SourceGit.Commands
|
|||
{
|
||||
var start = new ProcessStartInfo();
|
||||
start.FileName = Native.OS.GitExecutable;
|
||||
start.Arguments = "--no-pager -c core.quotepath=off " + Args;
|
||||
start.Arguments = "--no-pager -c core.quotepath=off ";
|
||||
start.UseShellExecute = false;
|
||||
start.CreateNoWindow = true;
|
||||
start.RedirectStandardOutput = true;
|
||||
|
@ -41,10 +49,41 @@ namespace SourceGit.Commands
|
|||
start.StandardOutputEncoding = Encoding.UTF8;
|
||||
start.StandardErrorEncoding = Encoding.UTF8;
|
||||
|
||||
// Force using this app as SSH askpass program
|
||||
var selfExecFile = Process.GetCurrentProcess().MainModule!.FileName;
|
||||
if (!OperatingSystem.IsLinux())
|
||||
start.Environment.Add("DISPLAY", "required");
|
||||
start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec`
|
||||
start.Environment.Add("SSH_ASKPASS_REQUIRE", "prefer");
|
||||
|
||||
// If an SSH private key was provided, sets the environment.
|
||||
if (!string.IsNullOrEmpty(SSHKey))
|
||||
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -i '{SSHKey}'");
|
||||
else
|
||||
start.Arguments += "-c credential.helper=manager ";
|
||||
|
||||
// Force using en_US.UTF-8 locale to avoid GCM crash
|
||||
if (OperatingSystem.IsLinux())
|
||||
start.Environment.Add("LANG", "en_US.UTF-8");
|
||||
|
||||
// Force using this app as git editor.
|
||||
switch (Editor)
|
||||
{
|
||||
case EditorType.CoreEditor:
|
||||
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --core-editor\" ";
|
||||
break;
|
||||
case EditorType.RebaseEditor:
|
||||
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --rebase-message-editor\" -c sequence.editor=\"\\\"{selfExecFile}\\\" --rebase-todo-editor\" -c rebase.abbreviateCommands=true ";
|
||||
break;
|
||||
default:
|
||||
start.Arguments += "-c core.editor=true ";
|
||||
break;
|
||||
}
|
||||
|
||||
// Append command args
|
||||
start.Arguments += Args;
|
||||
|
||||
// Working directory
|
||||
if (!string.IsNullOrEmpty(WorkingDirectory))
|
||||
start.WorkingDirectory = WorkingDirectory;
|
||||
|
||||
|
@ -94,7 +133,7 @@ namespace SourceGit.Commands
|
|||
return;
|
||||
if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal))
|
||||
return;
|
||||
if (_progressRegex().IsMatch(e.Data))
|
||||
if (REG_PROGRESS().IsMatch(e.Data))
|
||||
return;
|
||||
errs.Add(e.Data);
|
||||
};
|
||||
|
@ -159,20 +198,18 @@ namespace SourceGit.Commands
|
|||
{
|
||||
proc.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
catch
|
||||
{
|
||||
return new ReadToEndResult()
|
||||
{
|
||||
IsSuccess = false,
|
||||
StdOut = string.Empty,
|
||||
StdErr = e.Message,
|
||||
};
|
||||
}
|
||||
|
||||
var rs = new ReadToEndResult()
|
||||
{
|
||||
StdOut = proc.StandardOutput.ReadToEnd(),
|
||||
StdErr = proc.StandardError.ReadToEnd(),
|
||||
};
|
||||
|
||||
proc.WaitForExit();
|
||||
|
@ -185,6 +222,6 @@ namespace SourceGit.Commands
|
|||
protected virtual void OnReadline(string line) { }
|
||||
|
||||
[GeneratedRegex(@"\d+%")]
|
||||
private static partial Regex _progressRegex();
|
||||
private static partial Regex REG_PROGRESS();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace SourceGit.Commands
|
|||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
Args = $"commit --file=\"{file}\"";
|
||||
if (autoStage)
|
||||
Args += " --all";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
|
@ -18,7 +19,7 @@ namespace SourceGit.Commands
|
|||
public List<Models.Change> Result()
|
||||
{
|
||||
Exec();
|
||||
_changes.Sort((l, r) => l.Path.CompareTo(r.Path));
|
||||
_changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal));
|
||||
return _changes;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace SourceGit.Commands
|
|||
var rs = new Dictionary<string, string>();
|
||||
if (output.IsSuccess)
|
||||
{
|
||||
var lines = output.StdOut.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var lines = output.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var idx = line.IndexOf('=', StringComparison.Ordinal);
|
||||
|
@ -31,15 +31,8 @@ namespace SourceGit.Commands
|
|||
{
|
||||
var key = line.Substring(0, idx).Trim();
|
||||
var val = line.Substring(idx + 1).Trim();
|
||||
if (rs.ContainsKey(key))
|
||||
{
|
||||
rs[key] = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.Add(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,18 +13,9 @@ namespace SourceGit.Commands
|
|||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
Args = "fetch --progress --verbose ";
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
Args += "fetch --progress --verbose ";
|
||||
if (prune)
|
||||
Args += "--prune ";
|
||||
|
||||
|
@ -44,18 +35,8 @@ namespace SourceGit.Commands
|
|||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
Args += $"fetch --progress --verbose {remote} {remoteBranch}:{localBranch}";
|
||||
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
Args = $"fetch --progress --verbose {remote} {remoteBranch}:{localBranch}";
|
||||
}
|
||||
|
||||
protected override void OnReadline(string line)
|
||||
|
@ -143,16 +124,9 @@ namespace SourceGit.Commands
|
|||
};
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_jobs.ContainsKey(repo))
|
||||
{
|
||||
_jobs[repo] = job;
|
||||
}
|
||||
else
|
||||
{
|
||||
_jobs.Add(repo, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveRepository(string repo)
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace SourceGit.Commands
|
|||
return init.Exec();
|
||||
}
|
||||
|
||||
public static string Prefix(string repo, string type)
|
||||
public static string GetPrefix(string repo, string type)
|
||||
{
|
||||
return new Config(repo).Get($"gitflow.prefix.{type}");
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace SourceGit.Commands
|
|||
var rs = cmd.ReadToEnd();
|
||||
if (rs.IsSuccess)
|
||||
{
|
||||
var lines = rs.StdOut.Split(new char[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var match = REG_LOCK().Match(line);
|
||||
|
|
|
@ -10,18 +10,9 @@ namespace SourceGit.Commands
|
|||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
Args = "pull --verbose --progress --tags ";
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
Args += "pull --verbose --progress --tags ";
|
||||
if (useRebase)
|
||||
Args += "--rebase ";
|
||||
if (noTags)
|
||||
|
|
|
@ -6,22 +6,13 @@ namespace SourceGit.Commands
|
|||
{
|
||||
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool force, bool track, Action<string> onProgress)
|
||||
{
|
||||
_outputHandler = onProgress;
|
||||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
_outputHandler = onProgress;
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
Args += "push --progress --verbose ";
|
||||
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
Args = "push --progress --verbose ";
|
||||
|
||||
if (withTags)
|
||||
Args += "--tags ";
|
||||
|
@ -37,20 +28,12 @@ namespace SourceGit.Commands
|
|||
{
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
Args = "push ";
|
||||
|
||||
var sshKey = new Config(repo).Get($"remote.{remote}.sshkey");
|
||||
if (!string.IsNullOrEmpty(sshKey))
|
||||
{
|
||||
Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
Args = "-c credential.helper=manager ";
|
||||
}
|
||||
|
||||
Args += "push ";
|
||||
if (isDelete)
|
||||
Args += "--delete ";
|
||||
|
||||
Args += $"{remote} refs/tags/{tag}";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
{
|
||||
|
@ -9,10 +10,27 @@ namespace SourceGit.Commands
|
|||
{
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"log --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + limits;
|
||||
Args = "log --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + limits;
|
||||
_findFirstMerged = needFindHead;
|
||||
}
|
||||
|
||||
public QueryCommits(string repo, int maxCount, string messageFilter)
|
||||
{
|
||||
var argsBuilder = new StringBuilder();
|
||||
var words = messageFilter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var word in words)
|
||||
{
|
||||
var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
argsBuilder.Append($"--grep=\"{escaped}\" ");
|
||||
}
|
||||
argsBuilder.Append("--all-match");
|
||||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"log -{maxCount} --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + argsBuilder.ToString();
|
||||
_findFirstMerged = false;
|
||||
}
|
||||
|
||||
public List<Models.Commit> Result()
|
||||
{
|
||||
var rs = ReadToEnd();
|
||||
|
@ -53,8 +71,6 @@ namespace SourceGit.Commands
|
|||
_current.Subject = line;
|
||||
nextPartIdx = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
nextPartIdx++;
|
||||
|
@ -97,6 +113,9 @@ namespace SourceGit.Commands
|
|||
foreach (var sub in subs)
|
||||
{
|
||||
var d = sub.Trim();
|
||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
|
@ -105,10 +124,6 @@ namespace SourceGit.Commands
|
|||
Name = d.Substring(15),
|
||||
});
|
||||
}
|
||||
else if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.IsMerged = true;
|
||||
|
@ -159,10 +174,10 @@ namespace SourceGit.Commands
|
|||
|
||||
private void MarkFirstMerged()
|
||||
{
|
||||
Args = $"log --since=\"{_commits[_commits.Count - 1].CommitterTimeStr}\" --format=\"%H\"";
|
||||
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
|
||||
|
||||
var rs = ReadToEnd();
|
||||
var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var shas = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (shas.Length == 0)
|
||||
return;
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace SourceGit.Commands
|
|||
change.Set(Models.ChangeState.None, Models.ChangeState.Copied);
|
||||
break;
|
||||
case "M":
|
||||
change.Set(Models.ChangeState.Modified, Models.ChangeState.None);
|
||||
change.Set(Models.ChangeState.Modified);
|
||||
break;
|
||||
case "MM":
|
||||
change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified);
|
||||
|
@ -61,7 +61,7 @@ namespace SourceGit.Commands
|
|||
change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted);
|
||||
break;
|
||||
case "A":
|
||||
change.Set(Models.ChangeState.Added, Models.ChangeState.None);
|
||||
change.Set(Models.ChangeState.Added);
|
||||
break;
|
||||
case "AM":
|
||||
change.Set(Models.ChangeState.Added, Models.ChangeState.Modified);
|
||||
|
@ -70,10 +70,10 @@ namespace SourceGit.Commands
|
|||
change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted);
|
||||
break;
|
||||
case "D":
|
||||
change.Set(Models.ChangeState.Deleted, Models.ChangeState.None);
|
||||
change.Set(Models.ChangeState.Deleted);
|
||||
break;
|
||||
case "R":
|
||||
change.Set(Models.ChangeState.Renamed, Models.ChangeState.None);
|
||||
change.Set(Models.ChangeState.Renamed);
|
||||
break;
|
||||
case "RM":
|
||||
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified);
|
||||
|
@ -82,7 +82,7 @@ namespace SourceGit.Commands
|
|||
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted);
|
||||
break;
|
||||
case "C":
|
||||
change.Set(Models.ChangeState.Copied, Models.ChangeState.None);
|
||||
change.Set(Models.ChangeState.Copied);
|
||||
break;
|
||||
case "CM":
|
||||
change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified);
|
||||
|
|
|
@ -47,6 +47,9 @@ namespace SourceGit.Commands
|
|||
foreach (var sub in subs)
|
||||
{
|
||||
var d = sub.Trim();
|
||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||
{
|
||||
decorators.Add(new Models.Decorator()
|
||||
|
@ -55,10 +58,6 @@ namespace SourceGit.Commands
|
|||
Name = d.Substring(15),
|
||||
});
|
||||
}
|
||||
else if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
isHeadOfCurrent = true;
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace SourceGit.Commands
|
|||
|
||||
protected override void OnReadline(string line)
|
||||
{
|
||||
var subs = line.Split(new char[] { '$' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var subs = line.Split('$', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (subs.Length == 2)
|
||||
{
|
||||
_loaded.Add(new Models.Tag()
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
namespace SourceGit.Commands
|
||||
{
|
||||
public class Rebase : Command
|
||||
{
|
||||
|
@ -19,12 +17,10 @@ namespace SourceGit.Commands
|
|||
{
|
||||
public InteractiveRebase(string repo, string basedOn)
|
||||
{
|
||||
var exec = Process.GetCurrentProcess().MainModule.FileName;
|
||||
var editor = $"\\\"{exec}\\\" --rebase-editor";
|
||||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"-c core.editor=\"{editor}\" -c sequence.editor=\"{editor}\" -c rebase.abbreviateCommands=true rebase -i --autosquash {basedOn}";
|
||||
Editor = EditorType.RebaseEditor;
|
||||
Args = $"rebase -i --autosquash {basedOn}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,7 @@ namespace SourceGit.Commands
|
|||
return;
|
||||
|
||||
var dateStr = line.Substring(0, dateEndIdx);
|
||||
var date = 0.0;
|
||||
if (!double.TryParse(dateStr, out date))
|
||||
return;
|
||||
|
||||
if (double.TryParse(dateStr, out var date))
|
||||
_statistics.AddCommit(line.Substring(dateEndIdx + 1), date);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
{
|
||||
public partial class Worktree : Command
|
||||
public class Worktree : Command
|
||||
{
|
||||
[GeneratedRegex(@"^(\w)\s(\d+)$")]
|
||||
private static partial Regex REG_AHEAD_BEHIND();
|
||||
|
||||
public Worktree(string repo)
|
||||
{
|
||||
WorkingDirectory = repo;
|
||||
|
@ -24,7 +20,7 @@ namespace SourceGit.Commands
|
|||
var last = null as Models.Worktree;
|
||||
if (rs.IsSuccess)
|
||||
{
|
||||
var lines = rs.StdOut.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var lines = rs.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.StartsWith("worktree ", StringComparison.Ordinal))
|
||||
|
@ -34,27 +30,23 @@ namespace SourceGit.Commands
|
|||
}
|
||||
else if (line.StartsWith("bare", StringComparison.Ordinal))
|
||||
{
|
||||
last.IsBare = true;
|
||||
last!.IsBare = true;
|
||||
}
|
||||
else if (line.StartsWith("HEAD ", StringComparison.Ordinal))
|
||||
{
|
||||
last.Head = line.Substring(5).Trim();
|
||||
last!.Head = line.Substring(5).Trim();
|
||||
}
|
||||
else if (line.StartsWith("branch ", StringComparison.Ordinal))
|
||||
{
|
||||
last.Branch = line.Substring(7).Trim();
|
||||
last!.Branch = line.Substring(7).Trim();
|
||||
}
|
||||
else if (line.StartsWith("detached", StringComparison.Ordinal))
|
||||
{
|
||||
last.IsDetached = true;
|
||||
last!.IsDetached = true;
|
||||
}
|
||||
else if (line.StartsWith("locked", StringComparison.Ordinal))
|
||||
{
|
||||
last.IsLocked = true;
|
||||
}
|
||||
else if (line.StartsWith("prunable", StringComparison.Ordinal))
|
||||
{
|
||||
last.IsPrunable = true;
|
||||
last!.IsLocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
|
||||
|
@ -12,11 +13,11 @@ namespace SourceGit.Converters
|
|||
switch (v)
|
||||
{
|
||||
case Models.ChangeViewMode.List:
|
||||
return App.Current?.FindResource("Icons.List") as StreamGeometry;
|
||||
return Application.Current?.FindResource("Icons.List") as StreamGeometry;
|
||||
case Models.ChangeViewMode.Grid:
|
||||
return App.Current?.FindResource("Icons.Grid") as StreamGeometry;
|
||||
return Application.Current?.FindResource("Icons.Grid") as StreamGeometry;
|
||||
default:
|
||||
return App.Current?.FindResource("Icons.Tree") as StreamGeometry;
|
||||
return Application.Current?.FindResource("Icons.Tree") as StreamGeometry;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace SourceGit.Converters
|
|||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (value as Models.Locale).Key;
|
||||
return (value as Models.Locale)?.Key;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,18 +29,21 @@ namespace SourceGit.Converters
|
|||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var theme = (string)value;
|
||||
if (string.IsNullOrEmpty(theme))
|
||||
return ThemeVariant.Default;
|
||||
|
||||
if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase))
|
||||
return ThemeVariant.Light;
|
||||
else if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase))
|
||||
return ThemeVariant.Dark;
|
||||
else
|
||||
|
||||
return ThemeVariant.Default;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var theme = (ThemeVariant)value;
|
||||
return theme.Key;
|
||||
return (value as ThemeVariant)?.Key;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,10 @@ namespace SourceGit.Models
|
|||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
lock (_synclock)
|
||||
{
|
||||
|
@ -84,10 +87,7 @@ namespace SourceGit.Models
|
|||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (_resources.ContainsKey(md5))
|
||||
_resources[md5] = img;
|
||||
else
|
||||
_resources.Add(md5, img);
|
||||
NotifyResourceChanged(md5);
|
||||
});
|
||||
}
|
||||
|
@ -134,7 +134,10 @@ namespace SourceGit.Models
|
|||
return img;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +159,7 @@ namespace SourceGit.Models
|
|||
}
|
||||
|
||||
private static readonly object _synclock = new object();
|
||||
private static readonly string _storePath = string.Empty;
|
||||
private static readonly string _storePath;
|
||||
private static readonly List<IAvatarHost> _avatars = new List<IAvatarHost>();
|
||||
private static readonly Dictionary<string, Bitmap> _resources = new Dictionary<string, Bitmap>();
|
||||
private static readonly HashSet<string> _requesting = new HashSet<string>();
|
||||
|
|
|
@ -19,13 +19,11 @@ namespace SourceGit.Models
|
|||
public bool IsMerged { get; set; } = false;
|
||||
public Thickness Margin { get; set; } = new Thickness(0);
|
||||
|
||||
|
||||
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd");
|
||||
public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToString("yyyy/MM/dd");
|
||||
|
||||
public bool IsCommitterVisible => Author != Committer || AuthorTime != CommitterTime;
|
||||
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
|
||||
public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,8 +61,13 @@ namespace SourceGit.Models
|
|||
}
|
||||
else if (x < LastX)
|
||||
{
|
||||
if (y > LastY + halfHeight)
|
||||
Add(new Point(LastX, LastY + halfHeight));
|
||||
var testY = LastY + halfHeight;
|
||||
if (y > testY)
|
||||
Add(new Point(LastX, testY));
|
||||
|
||||
if (!isEnd)
|
||||
y += halfHeight;
|
||||
|
||||
Add(new Point(x, y));
|
||||
}
|
||||
else if (isEnd)
|
||||
|
@ -108,17 +113,17 @@ namespace SourceGit.Models
|
|||
private set;
|
||||
} = new List<Pen>();
|
||||
|
||||
public static void SetDefaultPens()
|
||||
public static void SetDefaultPens(double thickness = 2)
|
||||
{
|
||||
SetPenColors(_defaultPenColors);
|
||||
SetPens(_defaultPenColors, thickness);
|
||||
}
|
||||
|
||||
public static void SetPenColors(List<Color> colors)
|
||||
public static void SetPens(List<Color> colors, double thickness)
|
||||
{
|
||||
Pens.Clear();
|
||||
|
||||
foreach (var c in colors)
|
||||
Pens.Add(new Pen(c.ToUInt32(), 2));
|
||||
Pens.Add(new Pen(c.ToUInt32(), thickness));
|
||||
|
||||
_penCount = colors.Count;
|
||||
}
|
||||
|
@ -216,13 +221,17 @@ namespace SourceGit.Models
|
|||
var parent = commit.Parents[j];
|
||||
if (mapUnsolved.TryGetValue(parent, out var value))
|
||||
{
|
||||
// Try to change the merge state of linked graph
|
||||
var l = value;
|
||||
var link = new Link();
|
||||
if (isMerged)
|
||||
l.IsMerged = true;
|
||||
|
||||
var link = new Link();
|
||||
link.Start = position;
|
||||
link.End = new Point(l.LastX, offsetY + HALF_HEIGHT);
|
||||
link.Control = new Point(link.End.X, link.Start.Y);
|
||||
link.Color = l.Path.Color;
|
||||
|
||||
temp.Links.Add(link);
|
||||
}
|
||||
else
|
||||
|
@ -259,8 +268,9 @@ namespace SourceGit.Models
|
|||
var path = unsolved[i];
|
||||
var endY = (commits.Count - 0.5) * UNIT_HEIGHT;
|
||||
|
||||
if (path.Path.Points.Count == 1 && path.Path.Points[0].Y == endY)
|
||||
if (path.Path.Points.Count == 1 && Math.Abs(path.Path.Points[0].Y - endY) < 0.0001)
|
||||
continue;
|
||||
|
||||
path.Add((i + 0.5) * UNIT_WIDTH, endY + HALF_HEIGHT, HALF_HEIGHT, true);
|
||||
}
|
||||
unsolved.Clear();
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class CustomColorSchema
|
||||
{
|
||||
public Dictionary<string, string> Basic { get; set; } = new Dictionary<string, string>();
|
||||
public List<string> Graph { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
|
@ -108,8 +108,8 @@ namespace SourceGit.Models
|
|||
|
||||
private readonly Change _workingCopyChange = null;
|
||||
private readonly bool _isUnstaged = false;
|
||||
private readonly string _path;
|
||||
private readonly string _orgPath = string.Empty;
|
||||
private readonly string _path = string.Empty;
|
||||
private readonly string _extra = string.Empty;
|
||||
private readonly List<string> _revisions = new List<string>();
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ namespace SourceGit.Models
|
|||
{
|
||||
public class ExternalTool
|
||||
{
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
public string Executable { get; private set; } = string.Empty;
|
||||
public string OpenCmdArgs { get; private set; } = string.Empty;
|
||||
public string Name { get; private set; }
|
||||
public string Executable { get; private set; }
|
||||
public string OpenCmdArgs { get; private set; }
|
||||
public Bitmap IconImage { get; private set; } = null;
|
||||
|
||||
public ExternalTool(string name, string icon, string executable, string openCmdArgs)
|
||||
|
@ -25,10 +25,14 @@ namespace SourceGit.Models
|
|||
|
||||
try
|
||||
{
|
||||
var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png", UriKind.RelativeOrAbsolute));
|
||||
var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png",
|
||||
UriKind.RelativeOrAbsolute));
|
||||
IconImage = new Bitmap(asset);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void Open(string repo)
|
||||
|
|
26
src/Models/InteractiveRebase.cs
Normal file
26
src/Models/InteractiveRebase.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public enum InteractiveRebaseAction
|
||||
{
|
||||
Pick,
|
||||
Edit,
|
||||
Reword,
|
||||
Squash,
|
||||
Fixup,
|
||||
Drop,
|
||||
}
|
||||
|
||||
public class InteractiveRebaseJob
|
||||
{
|
||||
public string SHA { get; set; } = string.Empty;
|
||||
public InteractiveRebaseAction Action { get; set; } = InteractiveRebaseAction.Pick;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class InteractiveRebaseJobCollection
|
||||
{
|
||||
public List<InteractiveRebaseJob> Jobs { get; set; } = new List<InteractiveRebaseJob>();
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public enum InteractiveRebaseAction
|
||||
{
|
||||
Pick,
|
||||
Edit,
|
||||
Reword,
|
||||
Squash,
|
||||
Fixup,
|
||||
Drop,
|
||||
}
|
||||
|
||||
public class InteractiveRebaseJob
|
||||
{
|
||||
public string SHA { get; set; } = string.Empty;
|
||||
public InteractiveRebaseAction Action { get; set; } = InteractiveRebaseAction.Pick;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public static class InteractiveRebaseEditor
|
||||
{
|
||||
public static int Process(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filename = Path.GetFileName(file);
|
||||
if (filename.Equals("git-rebase-todo", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(Path.GetDirectoryName(file));
|
||||
if (!dirInfo.Exists || !dirInfo.Name.Equals("rebase-merge", StringComparison.Ordinal))
|
||||
return -1;
|
||||
|
||||
var jobsFile = Path.Combine(dirInfo.Parent.FullName, "sourcegit_rebase_jobs.json");
|
||||
if (!File.Exists(jobsFile))
|
||||
return -1;
|
||||
|
||||
var jobs = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.ListInteractiveRebaseJob);
|
||||
var lines = new List<string>();
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
switch (job.Action)
|
||||
{
|
||||
case InteractiveRebaseAction.Pick:
|
||||
lines.Add($"p {job.SHA}");
|
||||
break;
|
||||
case InteractiveRebaseAction.Edit:
|
||||
lines.Add($"e {job.SHA}");
|
||||
break;
|
||||
case InteractiveRebaseAction.Reword:
|
||||
lines.Add($"r {job.SHA}");
|
||||
break;
|
||||
case InteractiveRebaseAction.Squash:
|
||||
lines.Add($"s {job.SHA}");
|
||||
break;
|
||||
case InteractiveRebaseAction.Fixup:
|
||||
lines.Add($"f {job.SHA}");
|
||||
break;
|
||||
default:
|
||||
lines.Add($"d {job.SHA}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(file, lines);
|
||||
}
|
||||
else if (filename.Equals("COMMIT_EDITMSG", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var jobsFile = Path.Combine(Path.GetDirectoryName(file), "sourcegit_rebase_jobs.json");
|
||||
if (!File.Exists(jobsFile))
|
||||
return 0;
|
||||
|
||||
var jobs = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.ListInteractiveRebaseJob);
|
||||
var doneFile = Path.Combine(Path.GetDirectoryName(file), "rebase-merge", "done");
|
||||
if (!File.Exists(doneFile))
|
||||
return -1;
|
||||
|
||||
var done = File.ReadAllText(doneFile).Split(new char[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (done.Length > jobs.Count)
|
||||
return -1;
|
||||
|
||||
var job = jobs[done.Length - 1];
|
||||
File.WriteAllText(file, job.Message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,11 +40,15 @@ namespace SourceGit.Models
|
|||
|
||||
public static bool IsValidURL(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
return false;
|
||||
|
||||
foreach (var fmt in URL_FORMATS)
|
||||
{
|
||||
if (fmt.IsMatch(url))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace SourceGit.Models
|
|||
|
||||
public class RevisionTextFile
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Styling;
|
||||
|
||||
using AvaloniaEdit;
|
||||
|
@ -14,50 +15,39 @@ namespace SourceGit.Models
|
|||
{
|
||||
public static TextMate.Installation CreateForEditor(TextEditor editor)
|
||||
{
|
||||
if (App.Current?.ActualThemeVariant == ThemeVariant.Dark)
|
||||
{
|
||||
if (Application.Current?.ActualThemeVariant == ThemeVariant.Dark)
|
||||
return editor.InstallTextMate(new RegistryOptions(ThemeName.DarkPlus));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return editor.InstallTextMate(new RegistryOptions(ThemeName.LightPlus));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetThemeByApp(TextMate.Installation installation)
|
||||
{
|
||||
if (installation == null)
|
||||
return;
|
||||
|
||||
var reg = installation.RegistryOptions as RegistryOptions;
|
||||
if (App.Current?.ActualThemeVariant == ThemeVariant.Dark)
|
||||
if (installation.RegistryOptions is RegistryOptions reg)
|
||||
{
|
||||
if (Application.Current?.ActualThemeVariant == ThemeVariant.Dark)
|
||||
installation.SetTheme(reg.LoadTheme(ThemeName.DarkPlus));
|
||||
}
|
||||
else
|
||||
{
|
||||
installation.SetTheme(reg.LoadTheme(ThemeName.LightPlus));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetGrammarByFileName(TextMate.Installation installation, string filePath)
|
||||
{
|
||||
if (installation == null)
|
||||
return;
|
||||
|
||||
if (installation is { RegistryOptions: RegistryOptions reg })
|
||||
{
|
||||
var ext = Path.GetExtension(filePath);
|
||||
if (ext == ".h")
|
||||
{
|
||||
ext = ".cpp";
|
||||
}
|
||||
else if (ext == ".resx" || ext == ".plist")
|
||||
{
|
||||
ext = ".xml";
|
||||
}
|
||||
|
||||
var reg = installation.RegistryOptions as RegistryOptions;
|
||||
installation.SetGrammar(reg.GetScopeByExtension(ext));
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
13
src/Models/ThemeOverrides.cs
Normal file
13
src/Models/ThemeOverrides.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class ThemeOverrides
|
||||
{
|
||||
public Dictionary<string, Color> BasicColors { get; set; } = new Dictionary<string, Color>();
|
||||
public double GraphPenThickness { get; set; } = 2;
|
||||
public List<Color> GraphColors { get; set; } = new List<Color>();
|
||||
}
|
||||
}
|
|
@ -1,462 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Models.TreeDataGrid;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Selection;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class TreeDataGridSelectionModel<TModel> : TreeSelectionModelBase<TModel>,
|
||||
ITreeDataGridRowSelectionModel<TModel>,
|
||||
ITreeDataGridSelectionInteraction
|
||||
where TModel : class
|
||||
{
|
||||
private static readonly Point s_InvalidPoint = new(double.NegativeInfinity, double.NegativeInfinity);
|
||||
|
||||
private readonly ITreeDataGridSource<TModel> _source;
|
||||
private EventHandler _viewSelectionChanged;
|
||||
private EventHandler _rowDoubleTapped;
|
||||
private Point _pressedPoint = s_InvalidPoint;
|
||||
private bool _raiseViewSelectionChanged;
|
||||
private Func<TModel, IEnumerable<TModel>> _childrenGetter;
|
||||
|
||||
public TreeDataGridSelectionModel(ITreeDataGridSource<TModel> source, Func<TModel, IEnumerable<TModel>> childrenGetter)
|
||||
: base(source.Items)
|
||||
{
|
||||
_source = source;
|
||||
_childrenGetter = childrenGetter;
|
||||
|
||||
SelectionChanged += (s, e) =>
|
||||
{
|
||||
if (!IsSourceCollectionChanging)
|
||||
_viewSelectionChanged?.Invoke(this, e);
|
||||
else
|
||||
_raiseViewSelectionChanged = true;
|
||||
};
|
||||
}
|
||||
|
||||
public void Select(IEnumerable<TModel> items)
|
||||
{
|
||||
using (BatchUpdate())
|
||||
{
|
||||
Clear();
|
||||
|
||||
foreach (var selected in items)
|
||||
{
|
||||
var idx = GetModelIndex(_source.Items, selected, IndexPath.Unselected);
|
||||
if (!idx.Equals(IndexPath.Unselected))
|
||||
Select(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event EventHandler ITreeDataGridSelectionInteraction.SelectionChanged
|
||||
{
|
||||
add => _viewSelectionChanged += value;
|
||||
remove => _viewSelectionChanged -= value;
|
||||
}
|
||||
|
||||
public event EventHandler RowDoubleTapped
|
||||
{
|
||||
add => _rowDoubleTapped += value;
|
||||
remove => _rowDoubleTapped -= value;
|
||||
}
|
||||
|
||||
IEnumerable ITreeDataGridSelection.Source
|
||||
{
|
||||
get => Source;
|
||||
set => Source = value;
|
||||
}
|
||||
|
||||
bool ITreeDataGridSelectionInteraction.IsRowSelected(IRow rowModel)
|
||||
{
|
||||
if (rowModel is IModelIndexableRow indexable)
|
||||
return IsSelected(indexable.ModelIndexPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ITreeDataGridSelectionInteraction.IsRowSelected(int rowIndex)
|
||||
{
|
||||
if (rowIndex >= 0 && rowIndex < _source.Rows.Count)
|
||||
{
|
||||
if (_source.Rows[rowIndex] is IModelIndexableRow indexable)
|
||||
return IsSelected(indexable.ModelIndexPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ITreeDataGridSelectionInteraction.OnKeyDown(TreeDataGrid sender, KeyEventArgs e)
|
||||
{
|
||||
if (sender.RowsPresenter is null)
|
||||
return;
|
||||
|
||||
if (!e.Handled)
|
||||
{
|
||||
var ctrl = e.KeyModifiers.HasFlag(KeyModifiers.Control);
|
||||
if (e.Key == Key.A && ctrl && !SingleSelect)
|
||||
{
|
||||
using (BatchUpdate())
|
||||
{
|
||||
Clear();
|
||||
|
||||
int num = _source.Rows.Count;
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||
Select(m);
|
||||
}
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
var direction = e.Key.ToNavigationDirection();
|
||||
var shift = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||
if (direction.HasValue)
|
||||
{
|
||||
var anchorRowIndex = _source.Rows.ModelIndexToRowIndex(AnchorIndex);
|
||||
sender.RowsPresenter.BringIntoView(anchorRowIndex);
|
||||
|
||||
var anchor = sender.TryGetRow(anchorRowIndex);
|
||||
if (anchor is not null && !ctrl)
|
||||
{
|
||||
e.Handled = TryKeyExpandCollapse(sender, direction.Value, anchor);
|
||||
}
|
||||
|
||||
if (!e.Handled && (!ctrl || shift))
|
||||
{
|
||||
e.Handled = MoveSelection(sender, direction.Value, shift, anchor);
|
||||
}
|
||||
|
||||
if (!e.Handled && direction == NavigationDirection.Left
|
||||
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows && anchorRowIndex > 0)
|
||||
{
|
||||
var newIndex = hierarchicalRows.GetParentRowIndex(AnchorIndex);
|
||||
UpdateSelection(sender, newIndex, true);
|
||||
FocusRow(sender, sender.RowsPresenter.BringIntoView(newIndex));
|
||||
}
|
||||
|
||||
if (!e.Handled && direction == NavigationDirection.Right
|
||||
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows2 && hierarchicalRows2[anchorRowIndex].IsExpanded)
|
||||
{
|
||||
var newIndex = anchorRowIndex + 1;
|
||||
UpdateSelection(sender, newIndex, true);
|
||||
sender.RowsPresenter.BringIntoView(newIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ITreeDataGridSelectionInteraction.OnPointerPressed(TreeDataGrid sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (!e.Handled &&
|
||||
e.Pointer.Type == PointerType.Mouse &&
|
||||
e.Source is Control source &&
|
||||
sender.TryGetRow(source, out var row) &&
|
||||
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||
{
|
||||
if (!IsSelected(modelIndex))
|
||||
{
|
||||
PointerSelect(sender, row, e);
|
||||
_pressedPoint = s_InvalidPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
var point = e.GetCurrentPoint(sender);
|
||||
if (point.Properties.IsRightButtonPressed)
|
||||
{
|
||||
_pressedPoint = s_InvalidPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.KeyModifiers == KeyModifiers.Control)
|
||||
{
|
||||
Deselect(modelIndex);
|
||||
}
|
||||
else if (e.ClickCount % 2 == 0)
|
||||
{
|
||||
var focus = _source.Rows[row.RowIndex];
|
||||
if (focus is IExpander expander && HasChildren(focus))
|
||||
expander.IsExpanded = !expander.IsExpanded;
|
||||
else
|
||||
_rowDoubleTapped?.Invoke(this, e);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (sender.RowSelection.Count > 1)
|
||||
{
|
||||
using (BatchUpdate())
|
||||
{
|
||||
Clear();
|
||||
Select(modelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
_pressedPoint = s_InvalidPoint;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sender.TryGetRow(e.Source as Control, out var test))
|
||||
Clear();
|
||||
|
||||
_pressedPoint = e.GetPosition(sender);
|
||||
}
|
||||
}
|
||||
|
||||
void ITreeDataGridSelectionInteraction.OnPointerReleased(TreeDataGrid sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (!e.Handled &&
|
||||
_pressedPoint != s_InvalidPoint &&
|
||||
e.Source is Control source &&
|
||||
sender.TryGetRow(source, out var row) &&
|
||||
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||
{
|
||||
if (!IsSelected(modelIndex))
|
||||
{
|
||||
var p = e.GetPosition(sender);
|
||||
if (Math.Abs(p.X - _pressedPoint.X) <= 3 || Math.Abs(p.Y - _pressedPoint.Y) <= 3)
|
||||
PointerSelect(sender, row, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSourceCollectionChangeFinished()
|
||||
{
|
||||
if (_raiseViewSelectionChanged)
|
||||
{
|
||||
_viewSelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
_raiseViewSelectionChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void PointerSelect(TreeDataGrid sender, TreeDataGridRow row, PointerEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(sender);
|
||||
|
||||
var commandModifiers = TopLevel.GetTopLevel(sender)?.PlatformSettings?.HotkeyConfiguration.CommandModifiers;
|
||||
var toggleModifier = commandModifiers is not null && e.KeyModifiers.HasFlag(commandModifiers);
|
||||
var isRightButton = point.Properties.PointerUpdateKind is PointerUpdateKind.RightButtonPressed or
|
||||
PointerUpdateKind.RightButtonReleased;
|
||||
|
||||
UpdateSelection(
|
||||
sender,
|
||||
row.RowIndex,
|
||||
select: true,
|
||||
rangeModifier: e.KeyModifiers.HasFlag(KeyModifiers.Shift),
|
||||
toggleModifier: toggleModifier,
|
||||
rightButton: isRightButton);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void UpdateSelection(TreeDataGrid treeDataGrid, int rowIndex, bool select = true, bool rangeModifier = false, bool toggleModifier = false, bool rightButton = false)
|
||||
{
|
||||
var modelIndex = _source.Rows.RowIndexToModelIndex(rowIndex);
|
||||
if (modelIndex == default)
|
||||
return;
|
||||
|
||||
var mode = SingleSelect ? SelectionMode.Single : SelectionMode.Multiple;
|
||||
var multi = (mode & SelectionMode.Multiple) != 0;
|
||||
var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
|
||||
var range = multi && rangeModifier;
|
||||
|
||||
if (!select)
|
||||
{
|
||||
if (IsSelected(modelIndex) && !treeDataGrid.QueryCancelSelection())
|
||||
Deselect(modelIndex);
|
||||
}
|
||||
else if (rightButton)
|
||||
{
|
||||
if (IsSelected(modelIndex) == false && !treeDataGrid.QueryCancelSelection())
|
||||
SelectedIndex = modelIndex;
|
||||
}
|
||||
else if (range)
|
||||
{
|
||||
if (!treeDataGrid.QueryCancelSelection())
|
||||
{
|
||||
var anchor = RangeAnchorIndex;
|
||||
var i = Math.Max(_source.Rows.ModelIndexToRowIndex(anchor), 0);
|
||||
var step = i < rowIndex ? 1 : -1;
|
||||
|
||||
using (BatchUpdate())
|
||||
{
|
||||
Clear();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||
Select(m);
|
||||
anchor = m;
|
||||
if (i == rowIndex)
|
||||
break;
|
||||
i += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (multi && toggle)
|
||||
{
|
||||
if (!treeDataGrid.QueryCancelSelection())
|
||||
{
|
||||
if (IsSelected(modelIndex) == true)
|
||||
Deselect(modelIndex);
|
||||
else
|
||||
Select(modelIndex);
|
||||
}
|
||||
}
|
||||
else if (toggle)
|
||||
{
|
||||
if (!treeDataGrid.QueryCancelSelection())
|
||||
SelectedIndex = (SelectedIndex == modelIndex) ? -1 : modelIndex;
|
||||
}
|
||||
else if (SelectedIndex != modelIndex || Count > 1)
|
||||
{
|
||||
if (!treeDataGrid.QueryCancelSelection())
|
||||
SelectedIndex = modelIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryKeyExpandCollapse(TreeDataGrid treeDataGrid, NavigationDirection direction, TreeDataGridRow focused)
|
||||
{
|
||||
if (treeDataGrid.RowsPresenter is null || focused.RowIndex < 0)
|
||||
return false;
|
||||
|
||||
var row = _source.Rows[focused.RowIndex];
|
||||
|
||||
if (row is IExpander expander)
|
||||
{
|
||||
if (direction == NavigationDirection.Right && !expander.IsExpanded)
|
||||
{
|
||||
expander.IsExpanded = true;
|
||||
return true;
|
||||
}
|
||||
else if (direction == NavigationDirection.Left && expander.IsExpanded)
|
||||
{
|
||||
expander.IsExpanded = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool MoveSelection(TreeDataGrid treeDataGrid, NavigationDirection direction, bool rangeModifier, TreeDataGridRow focused)
|
||||
{
|
||||
if (treeDataGrid.RowsPresenter is null || _source.Columns.Count == 0 || _source.Rows.Count == 0)
|
||||
return false;
|
||||
|
||||
var currentRowIndex = focused?.RowIndex ?? _source.Rows.ModelIndexToRowIndex(SelectedIndex);
|
||||
int newRowIndex;
|
||||
|
||||
if (direction == NavigationDirection.First || direction == NavigationDirection.Last)
|
||||
{
|
||||
newRowIndex = direction == NavigationDirection.First ? 0 : _source.Rows.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
(var x, var y) = direction switch
|
||||
{
|
||||
NavigationDirection.Up => (0, -1),
|
||||
NavigationDirection.Down => (0, 1),
|
||||
NavigationDirection.Left => (-1, 0),
|
||||
NavigationDirection.Right => (1, 0),
|
||||
_ => (0, 0)
|
||||
};
|
||||
|
||||
newRowIndex = Math.Max(0, Math.Min(currentRowIndex + y, _source.Rows.Count - 1));
|
||||
}
|
||||
|
||||
if (newRowIndex != currentRowIndex)
|
||||
UpdateSelection(treeDataGrid, newRowIndex, true, rangeModifier);
|
||||
|
||||
if (newRowIndex != currentRowIndex)
|
||||
{
|
||||
treeDataGrid.RowsPresenter?.BringIntoView(newRowIndex);
|
||||
FocusRow(treeDataGrid, treeDataGrid.TryGetRow(newRowIndex));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FocusRow(TreeDataGrid owner, Control control)
|
||||
{
|
||||
if (!owner.TryGetRow(control, out var row) || row.CellsPresenter is null)
|
||||
return;
|
||||
|
||||
// Get the column index of the currently focused cell if possible: we'll try to focus the
|
||||
// same column in the new row.
|
||||
if (TopLevel.GetTopLevel(owner)?.FocusManager is { } focusManager &&
|
||||
focusManager.GetFocusedElement() is Control currentFocus &&
|
||||
owner.TryGetCell(currentFocus, out var currentCell) &&
|
||||
row.TryGetCell(currentCell.ColumnIndex) is { } newCell &&
|
||||
newCell.Focusable)
|
||||
{
|
||||
newCell.Focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, just focus the first focusable cell in the row.
|
||||
foreach (var cell in row.CellsPresenter.GetRealizedElements())
|
||||
{
|
||||
if (cell.Focusable)
|
||||
{
|
||||
cell.Focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<TModel> GetChildren(TModel node)
|
||||
{
|
||||
if (node == null)
|
||||
return null;
|
||||
|
||||
return _childrenGetter?.Invoke(node);
|
||||
}
|
||||
|
||||
private IndexPath GetModelIndex(IEnumerable<TModel> collection, TModel model, IndexPath parent)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
var index = parent.Append(i);
|
||||
if (item != null && item == model)
|
||||
return index;
|
||||
|
||||
var children = GetChildren(item);
|
||||
if (children != null)
|
||||
{
|
||||
var findInChildren = GetModelIndex(children, model, index);
|
||||
if (!findInChildren.Equals(IndexPath.Unselected))
|
||||
return findInChildren;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return IndexPath.Unselected;
|
||||
}
|
||||
|
||||
private bool HasChildren(IRow row)
|
||||
{
|
||||
var children = GetChildren(row.Model as TModel);
|
||||
if (children != null)
|
||||
{
|
||||
foreach (var c in children)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,20 @@ namespace SourceGit.Models
|
|||
public string Name { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
public User()
|
||||
{
|
||||
// Only used by User.Invalid
|
||||
}
|
||||
|
||||
public User(string data)
|
||||
{
|
||||
var nameEndIdx = data.IndexOf('±', StringComparison.Ordinal);
|
||||
|
||||
Name = nameEndIdx > 0 ? data.Substring(0, nameEndIdx) : string.Empty;
|
||||
Email = data.Substring(nameEndIdx + 1);
|
||||
_hash = data.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || !(obj is User))
|
||||
|
@ -21,21 +35,15 @@ namespace SourceGit.Models
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
return _hash;
|
||||
}
|
||||
|
||||
public static User FindOrAdd(string data)
|
||||
{
|
||||
return _caches.GetOrAdd(data, key =>
|
||||
{
|
||||
var nameEndIdx = key.IndexOf('±', StringComparison.Ordinal);
|
||||
var name = nameEndIdx > 0 ? key.Substring(0, nameEndIdx) : string.Empty;
|
||||
var email = key.Substring(nameEndIdx + 1);
|
||||
|
||||
return new User() { Name = name, Email = email };
|
||||
});
|
||||
return _caches.GetOrAdd(data, key => new User(key));
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<string, User> _caches = new ConcurrentDictionary<string, User>();
|
||||
private readonly int _hash;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace SourceGit.Models
|
|||
|
||||
var major = int.Parse(match.Groups[1].Value);
|
||||
var minor = int.Parse(match.Groups[2].Value);
|
||||
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||
return ver.Major < major || (ver.Major == major && ver.Minor < minor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace SourceGit.Models
|
|||
|
||||
// If this repository is a worktree repository, just watch the main repository's gitdir.
|
||||
var gitDirNormalized = _repo.GitDir.Replace("\\", "/");
|
||||
var worktreeIdx = gitDirNormalized.IndexOf(".git/worktrees/");
|
||||
var worktreeIdx = gitDirNormalized.IndexOf(".git/worktrees/", StringComparison.Ordinal);
|
||||
var repoWatchDir = _repo.GitDir;
|
||||
if (worktreeIdx > 0)
|
||||
repoWatchDir = _repo.GitDir.Substring(0, worktreeIdx + 4);
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace SourceGit.Models
|
|||
public string Head { get; set; } = string.Empty;
|
||||
public bool IsBare { get; set; } = false;
|
||||
public bool IsDetached { get; set; } = false;
|
||||
public bool IsPrunable { get; set; } = false;
|
||||
|
||||
public bool IsLocked
|
||||
{
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace SourceGit.Native
|
|||
{
|
||||
class Terminal
|
||||
{
|
||||
public string FilePath { get; set; } = string.Empty;
|
||||
public string OpenArgFormat { get; set; } = string.Empty;
|
||||
public string FilePath { get; set; }
|
||||
public string OpenArgFormat { get; set; }
|
||||
|
||||
public Terminal(string exec, string fmt)
|
||||
{
|
||||
|
@ -115,6 +115,8 @@ namespace SourceGit.Native
|
|||
}
|
||||
|
||||
var proc = Process.Start(_xdgOpenPath, $"\"{file}\"");
|
||||
if (proc != null)
|
||||
{
|
||||
proc.WaitForExit();
|
||||
|
||||
if (proc.ExitCode != 0)
|
||||
|
@ -122,6 +124,7 @@ namespace SourceGit.Native
|
|||
|
||||
proc.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private string FindExecutable(string filename)
|
||||
{
|
||||
|
@ -177,7 +180,7 @@ namespace SourceGit.Native
|
|||
return File.Exists(path) ? path : FindExecutable("fleet");
|
||||
}
|
||||
|
||||
private string _xdgOpenPath = string.Empty;
|
||||
private string _xdgOpenPath = null;
|
||||
private Terminal _terminal = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,10 @@ namespace SourceGit.Native
|
|||
File.WriteAllText(tmp, builder.ToString());
|
||||
|
||||
var proc = Process.Start("osascript", $"\"{tmp}\"");
|
||||
proc.Exited += (o, e) => File.Delete(tmp);
|
||||
if (proc != null)
|
||||
proc.Exited += (_, _) => File.Delete(tmp);
|
||||
else
|
||||
File.Delete(tmp);
|
||||
}
|
||||
|
||||
public void OpenWithDefaultEditor(string file)
|
||||
|
|
|
@ -48,20 +48,16 @@ namespace SourceGit.Native
|
|||
public static Models.Shell GetShell()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return (_backend as Windows).Shell;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (_backend as Windows)!.Shell;
|
||||
|
||||
return Models.Shell.Default;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetShell(Models.Shell shell)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var windows = _backend as Windows;
|
||||
var windows = (_backend as Windows)!;
|
||||
if (windows.Shell != shell)
|
||||
{
|
||||
windows.Shell = shell;
|
||||
|
|
|
@ -73,8 +73,8 @@ namespace SourceGit.Native
|
|||
v.dwOSVersionInfoSize = (uint)Marshal.SizeOf<RTL_OSVERSIONINFOEX>();
|
||||
if (RtlGetVersion(ref v) == 0 && (v.dwMajorVersion < 10 || v.dwBuildNumber < 22000))
|
||||
{
|
||||
Window.WindowStateProperty.Changed.AddClassHandler<Window>((w, e) => ExtendWindowFrame(w));
|
||||
Window.LoadedEvent.AddClassHandler<Window>((w, e) => ExtendWindowFrame(w));
|
||||
Window.WindowStateProperty.Changed.AddClassHandler<Window>((w, _) => ExtendWindowFrame(w));
|
||||
Control.LoadedEvent.AddClassHandler<Window>((w, _) => ExtendWindowFrame(w));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,9 +85,9 @@ namespace SourceGit.Native
|
|||
Microsoft.Win32.RegistryView.Registry64);
|
||||
|
||||
var git = reg.OpenSubKey("SOFTWARE\\GitForWindows");
|
||||
if (git != null)
|
||||
if (git != null && git.GetValue("InstallPath") is string installPath)
|
||||
{
|
||||
return Path.Combine(git.GetValue("InstallPath") as string, "bin", "git.exe");
|
||||
return Path.Combine(installPath, "bin", "git.exe");
|
||||
}
|
||||
|
||||
var builder = new StringBuilder("git.exe", 259);
|
||||
|
@ -137,7 +137,13 @@ namespace SourceGit.Native
|
|||
switch (Shell)
|
||||
{
|
||||
case Models.Shell.Default:
|
||||
var binDir = Path.GetDirectoryName(OS.GitExecutable);
|
||||
if (string.IsNullOrEmpty(OS.GitExecutable))
|
||||
{
|
||||
App.RaiseException(workdir, $"Can NOT found bash.exe");
|
||||
return;
|
||||
}
|
||||
|
||||
var binDir = Path.GetDirectoryName(OS.GitExecutable)!;
|
||||
var bash = Path.Combine(binDir, "bash.exe");
|
||||
if (!File.Exists(bash))
|
||||
{
|
||||
|
@ -175,28 +181,23 @@ namespace SourceGit.Native
|
|||
|
||||
public void OpenInFileManager(string path, bool select)
|
||||
{
|
||||
var fullpath = string.Empty;
|
||||
string fullpath;
|
||||
if (File.Exists(path))
|
||||
{
|
||||
fullpath = new FileInfo(path).FullName;
|
||||
|
||||
// For security reason, we never execute a file.
|
||||
// Instead, we open the folder and select it.
|
||||
select = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fullpath = new DirectoryInfo(path).FullName;
|
||||
fullpath = new DirectoryInfo(path!).FullName;
|
||||
}
|
||||
|
||||
if (select)
|
||||
{
|
||||
// The fullpath here may be a file or a folder.
|
||||
OpenFolderAndSelectFile(fullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The fullpath here is always a folder.
|
||||
Process.Start(new ProcessStartInfo(fullpath)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
|
@ -362,7 +363,7 @@ namespace SourceGit.Native
|
|||
if (sublime != null)
|
||||
{
|
||||
var icon = sublime.GetValue("DisplayIcon") as string;
|
||||
return Path.Combine(Path.GetDirectoryName(icon), "subl.exe");
|
||||
return Path.Combine(Path.GetDirectoryName(icon)!, "subl.exe");
|
||||
}
|
||||
|
||||
// Sublime Text 3
|
||||
|
@ -370,7 +371,7 @@ namespace SourceGit.Native
|
|||
if (sublime3 != null)
|
||||
{
|
||||
var icon = sublime3.GetValue("DisplayIcon") as string;
|
||||
return Path.Combine(Path.GetDirectoryName(icon), "subl.exe");
|
||||
return Path.Combine(Path.GetDirectoryName(icon)!, "subl.exe");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
<StreamGeometry x:Key="Icons.Info">M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Init">M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.InteractiveRebase">M512 64A447 447 0 0064 512c0 248 200 448 448 448s448-200 448-448S760 64 512 64zM218 295h31c54 0 105 19 145 55 13 12 13 31 3 43a35 35 0 01-22 10 36 36 0 01-21-7 155 155 0 00-103-39h-31a32 32 0 01-31-31c0-18 13-31 30-31zm31 433h-31a32 32 0 01-31-31c0-16 13-31 31-31h31A154 154 0 00403 512 217 217 0 01620 295h75l-93-67a33 33 0 01-7-43 33 33 0 0143-7l205 148-205 148a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67H620a154 154 0 00-154 154c0 122-97 220-217 220zm390 118a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67h-75c-52 0-103-19-143-54-12-12-13-31-1-43a30 30 0 0142-3 151 151 0 00102 39h75L602 599a33 33 0 01-7-43 33 33 0 0143-7l205 148-203 151z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Password">M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.LayoutHorizontal">M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zM139 832V192c0-6 4-11 11-11h331v661H149c-6 0-11-4-11-11zm747 0c0 6-4 11-11 11H544v-661H875c6 0 11 4 11 11v640z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.LayoutVertical">M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zm-725 64h725c6 0 11 4 11 11v288h-747V192c0-6 4-11 11-11zm725 661H149c-6 0-11-4-11-11V544h747V832c0 6-4 11-11 11z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.LFS">M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z</StreamGeometry>
|
||||
|
@ -59,7 +60,7 @@
|
|||
<StreamGeometry x:Key="Icons.Loading">M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Local">M976 0h-928A48 48 0 000 48v652a48 48 0 0048 48h416V928H200a48 48 0 000 96h624a48 48 0 000-96H560v-180h416a48 48 0 0048-48V48A48 48 0 00976 0zM928 652H96V96h832v556z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Lock">M832 464h-68V240a128 128 0 00-128-128h-248a128 128 0 00-128 128v224H192c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32v-384c0-18-14-32-32-32zm-292 237v53a8 8 0 01-8 8h-40a8 8 0 01-8-8v-53a48 48 0 1156 0zm152-237H332V240a56 56 0 0156-56h248a56 56 0 0156 56v224z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.MacOS.Close">M851 755q0 23-16 39l-78 78q-16 16-39 16t-39-16l-168-168-168 168q-16 16-39 16t-39-16l-78-78q-16-16-16-39t16-39l168-168-168-168q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 168 168-168q16-16 39-16t39 16l78 78q16 16 16 39t-16 39l-168 168 168 168q16 16 16 39z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.MacOS.Close">M887 774 625 511l263-260c11-11 11-28 0-39l-75-75c-5-5-12-8-20-8-7 0-14 3-20 8L512 396 250 137c-5-5-12-8-20-8-7 0-14 3-20 8L136 212c-11 11-11 28 0 39l263 260L137 774c-5 5-8 12-8 20 0 7 3 14 8 20l75 75c5 5 12 8 20 8 7 0 14-3 20-8L512 626l261 262c5 5 12 8 20 8 7 0 14-3 20-8l75-75c5-5 8-12 8-20C895 786 892 779 887 774z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.MacOS.Minimize">M1024 750v110c0 50-41 91-91 91h-841A92 92 0 010 859v-110C0 699 41 658 91 658h841c50 0 91 41 91 91z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.MacOS.Maximize">M0 4 0 20 16 20 0 4M4 0 20 0 20 16 4 0z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Menu">M192 192m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM864 160H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 480H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 800H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32z</StreamGeometry>
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">Select archive file path</x:String>
|
||||
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">Revision:</x:String>
|
||||
<x:String x:Key="Text.Archive.Title" xml:space="preserve">Archive</x:String>
|
||||
<x:String x:Key="Text.Askpass" xml:space="preserve">SourceGit Askpass</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged" xml:space="preserve">FILES ASSUME UNCHANGED</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Empty" xml:space="preserve">NO FILES ASSUMED AS UNCHANGED</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Remove" xml:space="preserve">REMOVE</x:String>
|
||||
|
@ -90,6 +91,7 @@
|
|||
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Parent Folder:</x:String>
|
||||
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">Repository URL:</x:String>
|
||||
<x:String x:Key="Text.Close" xml:space="preserve">CLOSE</x:String>
|
||||
<x:String x:Key="Text.CodeEditor" xml:space="preserve">Editor</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Cherry-Pick This Commit</x:String>
|
||||
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout Commit</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String>
|
||||
|
@ -275,6 +277,10 @@
|
|||
<x:String x:Key="Text.Histories" xml:space="preserve">Histories</x:String>
|
||||
<x:String x:Key="Text.Histories.DisplayMode" xml:space="preserve">Switch Horizontal/Vertical Layout</x:String>
|
||||
<x:String x:Key="Text.Histories.GraphMode" xml:space="preserve">Switch Curve/Polyline Graph Mode</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Author" xml:space="preserve">AUTHOR</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.GraphAndSubject" xml:space="preserve">GRAPH & SUBJECT</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.SHA" xml:space="preserve">SHA</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Time" xml:space="preserve">COMMIT TIME</x:String>
|
||||
<x:String x:Key="Text.Histories.Search" xml:space="preserve">SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT</x:String>
|
||||
<x:String x:Key="Text.Histories.SearchClear" xml:space="preserve">CLEAR</x:String>
|
||||
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">SELECTED {0} COMMITS</x:String>
|
||||
|
@ -337,11 +343,11 @@
|
|||
<x:String x:Key="Text.Paste" xml:space="preserve">Paste</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">Preference</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ColorOverrides" xml:space="preserve">Custom Color Schema</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">Default Font Size</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.MonospaceFont" xml:space="preserve">Monospace Font</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.Theme" xml:space="preserve">Theme</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">Theme Overrides</x:String>
|
||||
<x:String x:Key="Text.Preference.General" xml:space="preserve">GENERAL</x:String>
|
||||
<x:String x:Key="Text.Preference.General.AvatarServer" xml:space="preserve">Avatar Server</x:String>
|
||||
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Check for updates on startup</x:String>
|
||||
|
@ -449,8 +455,10 @@
|
|||
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">RESOLVE</x:String>
|
||||
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Search Commit</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Search By</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByBaseInfo" xml:space="preserve">Information</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">File</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Author & Committer</x:String>
|
||||
<x:String x:Key="Text.Repository.SearchBranchTag" xml:space="preserve">Search Branches & Tags</x:String>
|
||||
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String>
|
||||
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">SUBMODULES</x:String>
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">选择存档文件的存放路径</x:String>
|
||||
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">指定的提交:</x:String>
|
||||
<x:String x:Key="Text.Archive.Title" xml:space="preserve">存档</x:String>
|
||||
<x:String x:Key="Text.Askpass" xml:space="preserve">SourceGit Askpass</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged" xml:space="preserve">不跟踪更改的文件</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Empty" xml:space="preserve">没有不跟踪更改的文件</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Remove" xml:space="preserve">移除</x:String>
|
||||
|
@ -93,6 +94,7 @@
|
|||
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">父级目录 :</x:String>
|
||||
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">远程仓库 :</x:String>
|
||||
<x:String x:Key="Text.Close" xml:space="preserve">关闭</x:String>
|
||||
<x:String x:Key="Text.CodeEditor" xml:space="preserve">提交信息编辑器</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">挑选(cherry-pick)此提交</x:String>
|
||||
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">检出此提交</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String>
|
||||
|
@ -278,6 +280,10 @@
|
|||
<x:String x:Key="Text.Histories" xml:space="preserve">历史记录</x:String>
|
||||
<x:String x:Key="Text.Histories.DisplayMode" xml:space="preserve">切换横向/纵向显示</x:String>
|
||||
<x:String x:Key="Text.Histories.GraphMode" xml:space="preserve">切换曲线/折线显示</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Author" xml:space="preserve">作者</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.GraphAndSubject" xml:space="preserve">路线图与主题</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.SHA" xml:space="preserve">提交指纹</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Time" xml:space="preserve">提交时间</x:String>
|
||||
<x:String x:Key="Text.Histories.Search" xml:space="preserve">查询提交指纹、信息、作者。回车键开始,ESC键取消</x:String>
|
||||
<x:String x:Key="Text.Histories.SearchClear" xml:space="preserve">清空</x:String>
|
||||
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">已选中 {0} 项提交</x:String>
|
||||
|
@ -340,11 +346,11 @@
|
|||
<x:String x:Key="Text.Paste" xml:space="preserve">粘贴</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好设置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外观配置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ColorOverrides" xml:space="preserve">自定义配色文件</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">默认字体大小</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.MonospaceFont" xml:space="preserve">等宽字体</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.Theme" xml:space="preserve">主题</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">主题自定义</x:String>
|
||||
<x:String x:Key="Text.Preference.General" xml:space="preserve">通用配置</x:String>
|
||||
<x:String x:Key="Text.Preference.General.AvatarServer" xml:space="preserve">头像服务</x:String>
|
||||
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">启动时检测软件更新</x:String>
|
||||
|
@ -451,8 +457,10 @@
|
|||
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">解决冲突</x:String>
|
||||
<x:String x:Key="Text.Repository.Search" xml:space="preserve">查找提交</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">搜索途径</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByBaseInfo" xml:space="preserve">摘要</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">文件</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交信息</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交指纹</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">作者及提交者</x:String>
|
||||
<x:String x:Key="Text.Repository.SearchBranchTag" xml:space="preserve">快速查找分支、标签</x:String>
|
||||
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交统计</x:String>
|
||||
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">子模块列表</x:String>
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">選擇存檔檔案的存放路徑</x:String>
|
||||
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">指定的提交:</x:String>
|
||||
<x:String x:Key="Text.Archive.Title" xml:space="preserve">存檔</x:String>
|
||||
<x:String x:Key="Text.Askpass" xml:space="preserve">SourceGit Askpass</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged" xml:space="preserve">不跟蹤更改的檔案</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Empty" xml:space="preserve">沒有不跟蹤更改的檔案</x:String>
|
||||
<x:String x:Key="Text.AssumeUnchanged.Remove" xml:space="preserve">移除</x:String>
|
||||
|
@ -93,6 +94,7 @@
|
|||
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">父級目錄 :</x:String>
|
||||
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">遠端倉庫 :</x:String>
|
||||
<x:String x:Key="Text.Close" xml:space="preserve">關閉</x:String>
|
||||
<x:String x:Key="Text.CodeEditor" xml:space="preserve">提交資訊編輯器</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">挑選(cherry-pick)此提交</x:String>
|
||||
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">檢出此提交</x:String>
|
||||
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">與當前HEAD比較</x:String>
|
||||
|
@ -278,6 +280,10 @@
|
|||
<x:String x:Key="Text.Histories" xml:space="preserve">歷史記錄</x:String>
|
||||
<x:String x:Key="Text.Histories.DisplayMode" xml:space="preserve">切換橫向/縱向顯示</x:String>
|
||||
<x:String x:Key="Text.Histories.GraphMode" xml:space="preserve">切換曲線/折線顯示</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Author" xml:space="preserve">作者</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.GraphAndSubject" xml:space="preserve">路線圖與主題</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.SHA" xml:space="preserve">提交指紋</x:String>
|
||||
<x:String x:Key="Text.Histories.Header.Time" xml:space="preserve">提交時間</x:String>
|
||||
<x:String x:Key="Text.Histories.Search" xml:space="preserve">查詢提交指紋、資訊、作者。回車鍵開始,ESC鍵取消</x:String>
|
||||
<x:String x:Key="Text.Histories.SearchClear" xml:space="preserve">清空</x:String>
|
||||
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">已選中 {0} 項提交</x:String>
|
||||
|
@ -340,11 +346,11 @@
|
|||
<x:String x:Key="Text.Paste" xml:space="preserve">貼上</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好設定</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外觀配置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ColorOverrides" xml:space="preserve">自訂配色檔</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">預設字型</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">預設字型大小</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.MonospaceFont" xml:space="preserve">等寬字型</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.Theme" xml:space="preserve">主題</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">主題自訂</x:String>
|
||||
<x:String x:Key="Text.Preference.General" xml:space="preserve">通用配置</x:String>
|
||||
<x:String x:Key="Text.Preference.General.AvatarServer" xml:space="preserve">頭像服務</x:String>
|
||||
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">啟動時檢測軟體更新</x:String>
|
||||
|
@ -451,8 +457,10 @@
|
|||
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">解決衝突</x:String>
|
||||
<x:String x:Key="Text.Repository.Search" xml:space="preserve">查詢提交</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">查詢方式</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByBaseInfo" xml:space="preserve">摘要</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">檔案</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交資訊</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交指紋</x:String>
|
||||
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">作者及提交者</x:String>
|
||||
<x:String x:Key="Text.Repository.SearchBranchTag" xml:space="preserve">快速查找分支、標籤</x:String>
|
||||
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交統計</x:String>
|
||||
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">子模組列表</x:String>
|
||||
|
|
|
@ -170,6 +170,9 @@
|
|||
<Setter Property="MaxHeight" Value="768"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Grid.repository_leftpanel">
|
||||
</Style>
|
||||
|
||||
<Style Selector="Path">
|
||||
<Setter Property="Fill" Value="{DynamicResource Brush.FG1}"/>
|
||||
<Setter Property="Stretch" Value="Uniform"/>
|
||||
|
@ -178,6 +181,7 @@
|
|||
</Style>
|
||||
<Style Selector="Path[IsVisible=True].rotating">
|
||||
<Setter Property="Opacity" Value="0"/>
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Loading}"/>
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:1" IterationCount="Infinite">
|
||||
<KeyFrame Cue="0%">
|
||||
|
@ -193,6 +197,7 @@
|
|||
</Style>
|
||||
<Style Selector="Path[IsVisible=True].waiting">
|
||||
<Setter Property="Opacity" Value="0"/>
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Waiting}"/>
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:1" IterationCount="Infinite">
|
||||
<KeyFrame Cue="0%">
|
||||
|
@ -240,6 +245,11 @@
|
|||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Margin" Value="10,0,0,0"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.table_header">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
|
||||
<Setter Property="Opacity" Value=".65"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.info_label">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.FG2}"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
|
@ -792,7 +802,6 @@
|
|||
CornerRadius="{DynamicResource OverlayCornerRadius}">
|
||||
<ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}">
|
||||
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
|
||||
Grid.IsSharedSizeScope="True">
|
||||
<ItemsPresenter.ItemsPanel>
|
||||
|
@ -998,7 +1007,7 @@
|
|||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<Grid ColumnDefinitions="Auto,*" Background="Transparent">
|
||||
<Path Grid.Column="0"
|
||||
x:Name="PART_IndicatorIcon"
|
||||
Width="10"
|
||||
|
@ -1045,8 +1054,7 @@
|
|||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Path Grid.Column="0"
|
||||
x:Name="PART_IndicatorIcon"
|
||||
<Path x:Name="PART_IndicatorIcon"
|
||||
Width="12"
|
||||
Data="{StaticResource Icons.Filter}"
|
||||
Fill="Transparent"
|
||||
|
@ -1067,14 +1075,15 @@
|
|||
</Style>
|
||||
<Style Selector="ToggleButton.tree_expander">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Width" Value="8" />
|
||||
<Setter Property="Height" Value="8" />
|
||||
<Setter Property="Width" Value="10" />
|
||||
<Setter Property="Height" Value="10" />
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="Transparent"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
HorizontalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="ChevronPath"
|
||||
Data="M 4 0 L 8 4 L 4 8 Z"
|
||||
|
@ -1089,6 +1098,32 @@
|
|||
<Setter Property="Data" Value="M 0 4 L 8 4 L 4 8 Z" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ToggleButton.folder">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="IsHitTestVisible" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="Transparent"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="ChevronPath"
|
||||
Data="{StaticResource Icons.Folder.Fill}"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^:checked /template/ Path#ChevronPath">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Open}" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ToggleButton.layout_direction">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
|
@ -1098,8 +1133,7 @@
|
|||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="Transparent">
|
||||
<Path Grid.Column="0"
|
||||
x:Name="PART_IndicatorIcon"
|
||||
<Path x:Name="PART_IndicatorIcon"
|
||||
Width="14" Height="14"
|
||||
Stretch="Uniform"
|
||||
Data="{StaticResource Icons.LayoutVertical}"
|
||||
|
@ -1142,8 +1176,7 @@
|
|||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="Transparent">
|
||||
<Path Grid.Column="0"
|
||||
x:Name="PART_IndicatorIcon"
|
||||
<Path x:Name="PART_IndicatorIcon"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
Stretch="Fill"
|
||||
Width="14" Height="10"
|
||||
|
@ -1164,6 +1197,17 @@
|
|||
<Setter Property="Opacity" Value="1"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Slider">
|
||||
<Style.Resources>
|
||||
<Thickness x:Key="SliderTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<GridLength x:Key="SliderPreContentMargin">0</GridLength>
|
||||
<GridLength x:Key="SliderPostContentMargin">0</GridLength>
|
||||
<CornerRadius x:Key="SliderThumbCornerRadius">8</CornerRadius>
|
||||
<x:Double x:Key="SliderHorizontalThumbWidth">16</x:Double>
|
||||
<x:Double x:Key="SliderHorizontalThumbHeight">16</x:Double>
|
||||
</Style.Resources>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TabItem">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="10,0"/>
|
||||
|
@ -1223,8 +1267,7 @@
|
|||
Classes="tree_expander"
|
||||
Focusable="False"
|
||||
HorizontalAlignment="Center"
|
||||
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"
|
||||
IsHitTestVisible="False" />
|
||||
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"/>
|
||||
</Panel>
|
||||
<ContentPresenter Name="PART_HeaderPresenter"
|
||||
Grid.Column="1"
|
||||
|
@ -1264,73 +1307,6 @@
|
|||
<Setter Property="Opacity" Value=".65"/>
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="TreeViewItem[IsExpanded=True] Path.folder_icon">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Open}"/>
|
||||
</Style>
|
||||
<Style Selector="TreeViewItem[IsExpanded=False] Path.folder_icon">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TreeDataGrid">
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border x:Name="RootBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<DockPanel>
|
||||
<ScrollViewer Name="PART_HeaderScrollViewer"
|
||||
DockPanel.Dock="Top"
|
||||
IsVisible="{TemplateBinding ShowColumnHeaders}"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||
<Border x:Name="ColumnHeadersPresenterBorder">
|
||||
<TreeDataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
|
||||
ElementFactory="{TemplateBinding ElementFactory}"
|
||||
Items="{TemplateBinding Columns}" />
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Name="PART_ScrollViewer"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||
<TreeDataGridRowsPresenter Name="PART_RowsPresenter"
|
||||
Columns="{TemplateBinding Columns}"
|
||||
ElementFactory="{TemplateBinding ElementFactory}"
|
||||
Items="{TemplateBinding Rows}" />
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^/template/ Border#ColumnHeadersPresenterBorder">
|
||||
<Setter Property="BorderThickness" Value="0 0 0 1" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource TreeDataGridGridLinesBrush}" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TreeDataGridRow">
|
||||
<Style.Resources>
|
||||
<SolidColorBrush x:Key="TreeDataGridSelectedCellBackgroundBrush" Color="{DynamicResource SystemAccentColor}" Opacity="0.4" />
|
||||
</Style.Resources>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TreeDataGridColumnHeader">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridTextCell TextBlock">
|
||||
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/>
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridExpanderCell[IsExpanded=True] Path.folder_icon">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Open}"/>
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridExpanderCell[IsExpanded=False] Path.folder_icon">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="NumericUpDown">
|
||||
<Style Selector="^ /template/ ButtonSpinner#PART_Spinner">
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
<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.Controls.TreeDataGrid" Version="11.0.10" />
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
||||
|
|
|
@ -88,6 +88,9 @@ namespace SourceGit.ViewModels
|
|||
if (creator == null)
|
||||
return new ValidationResult("Missing runtime context to create branch!");
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return new ValidationResult("Worktree path is required!");
|
||||
|
||||
var fullPath = System.IO.Path.IsPathRooted(path) ? path : System.IO.Path.Combine(creator._repo.FullPath, path);
|
||||
var info = new DirectoryInfo(fullPath);
|
||||
if (info.Exists)
|
||||
|
|
|
@ -6,7 +6,6 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class Archive : Popup
|
||||
{
|
||||
|
||||
[Required(ErrorMessage = "Output file name is required")]
|
||||
public string SaveFile
|
||||
{
|
||||
|
@ -67,7 +66,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private string _saveFile = string.Empty;
|
||||
private readonly string _revision = string.Empty;
|
||||
private string _saveFile;
|
||||
private readonly string _revision;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,6 @@ namespace SourceGit.ViewModels
|
|||
private set;
|
||||
}
|
||||
|
||||
public string SelectedSHA
|
||||
{
|
||||
get => _selectedSHA;
|
||||
private set => SetProperty(ref _selectedSHA, value);
|
||||
}
|
||||
|
||||
public bool IsBinary
|
||||
{
|
||||
get => _data != null && _data.IsBinary;
|
||||
|
@ -53,8 +47,7 @@ namespace SourceGit.ViewModels
|
|||
repo?.NavigateToCommit(commitSHA);
|
||||
}
|
||||
|
||||
private readonly string _repo = string.Empty;
|
||||
private string _selectedSHA = string.Empty;
|
||||
private readonly string _repo;
|
||||
private Models.BlameData _data = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private string _repo;
|
||||
private Models.Commit _baseHead = null;
|
||||
private Models.Commit _toHead = null;
|
||||
private List<Models.Change> _changes = null;
|
||||
|
|
22
src/ViewModels/ChangeCollection.cs
Normal file
22
src/ViewModels/ChangeCollection.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia.Collections;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class ChangeCollectionAsTree
|
||||
{
|
||||
public List<ChangeTreeNode> Tree { get; set; } = new List<ChangeTreeNode>();
|
||||
public AvaloniaList<ChangeTreeNode> Rows { get; set; } = new AvaloniaList<ChangeTreeNode>();
|
||||
}
|
||||
|
||||
public class ChangeCollectionAsGrid
|
||||
{
|
||||
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
|
||||
}
|
||||
|
||||
public class ChangeCollectionAsList
|
||||
{
|
||||
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
|
||||
}
|
||||
}
|
125
src/ViewModels/ChangeTreeNode.cs
Normal file
125
src/ViewModels/ChangeTreeNode.cs
Normal file
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class ChangeTreeNode : ObservableObject
|
||||
{
|
||||
public string FullPath { get; set; }
|
||||
public int Depth { get; private set; } = 0;
|
||||
public Models.Change Change { get; set; } = null;
|
||||
public List<ChangeTreeNode> Children { get; set; } = new List<ChangeTreeNode>();
|
||||
|
||||
public bool IsFolder
|
||||
{
|
||||
get => Change == null;
|
||||
}
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetProperty(ref _isExpanded, value);
|
||||
}
|
||||
|
||||
public ChangeTreeNode(Models.Change c, int depth)
|
||||
{
|
||||
FullPath = c.Path;
|
||||
Depth = depth;
|
||||
Change = c;
|
||||
IsExpanded = false;
|
||||
}
|
||||
|
||||
public ChangeTreeNode(string path, bool isExpanded, int depth)
|
||||
{
|
||||
FullPath = path;
|
||||
Depth = depth;
|
||||
IsExpanded = isExpanded;
|
||||
}
|
||||
|
||||
public static List<ChangeTreeNode> Build(IList<Models.Change> changes, HashSet<string> folded)
|
||||
{
|
||||
var nodes = new List<ChangeTreeNode>();
|
||||
var folders = new Dictionary<string, ChangeTreeNode>();
|
||||
|
||||
foreach (var c in changes)
|
||||
{
|
||||
var sepIdx = c.Path.IndexOf('/', StringComparison.Ordinal);
|
||||
if (sepIdx == -1)
|
||||
{
|
||||
nodes.Add(new ChangeTreeNode(c, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
ChangeTreeNode lastFolder = null;
|
||||
int depth = 0;
|
||||
|
||||
while (sepIdx != -1)
|
||||
{
|
||||
var folder = c.Path.Substring(0, sepIdx);
|
||||
if (folders.TryGetValue(folder, out var value))
|
||||
{
|
||||
lastFolder = value;
|
||||
}
|
||||
else if (lastFolder == null)
|
||||
{
|
||||
lastFolder = new ChangeTreeNode(folder, !folded.Contains(folder), depth);
|
||||
folders.Add(folder, lastFolder);
|
||||
InsertFolder(nodes, lastFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cur = new ChangeTreeNode(folder, !folded.Contains(folder), depth);
|
||||
folders.Add(folder, cur);
|
||||
InsertFolder(lastFolder.Children, cur);
|
||||
lastFolder = cur;
|
||||
}
|
||||
|
||||
depth++;
|
||||
sepIdx = c.Path.IndexOf('/', sepIdx + 1);
|
||||
}
|
||||
|
||||
lastFolder?.Children.Add(new ChangeTreeNode(c, depth));
|
||||
}
|
||||
}
|
||||
|
||||
Sort(nodes);
|
||||
|
||||
folders.Clear();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private static void InsertFolder(List<ChangeTreeNode> collection, ChangeTreeNode subFolder)
|
||||
{
|
||||
for (int i = 0; i < collection.Count; i++)
|
||||
{
|
||||
if (!collection[i].IsFolder)
|
||||
{
|
||||
collection.Insert(i, subFolder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
collection.Add(subFolder);
|
||||
}
|
||||
|
||||
private static void Sort(List<ChangeTreeNode> nodes)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (node.IsFolder)
|
||||
Sort(node.Children);
|
||||
}
|
||||
|
||||
nodes.Sort((l, r) =>
|
||||
{
|
||||
if (l.IsFolder)
|
||||
return r.IsFolder ? string.Compare(l.FullPath, r.FullPath, StringComparison.Ordinal) : -1;
|
||||
return r.IsFolder ? 1 : string.Compare(l.FullPath, r.FullPath, StringComparison.Ordinal);
|
||||
});
|
||||
}
|
||||
|
||||
private bool _isExpanded = true;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -105,8 +106,8 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
else
|
||||
{
|
||||
var name = Path.GetFileName(_remote);
|
||||
if (name.EndsWith(".git"))
|
||||
var name = Path.GetFileName(_remote)!;
|
||||
if (name.EndsWith(".git", StringComparison.Ordinal))
|
||||
name = name.Substring(0, name.Length - 4);
|
||||
path = Path.GetFullPath(Path.Combine(path, name));
|
||||
}
|
||||
|
|
|
@ -176,14 +176,8 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
ViewRevisionFileContent = new Models.RevisionTextFile()
|
||||
{
|
||||
FileName = file.Path,
|
||||
Content = content
|
||||
};
|
||||
});
|
||||
var txt = new Models.RevisionTextFile() { Content = content };
|
||||
Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = txt);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -256,7 +250,7 @@ namespace SourceGit.ViewModels
|
|||
var blame = new MenuItem();
|
||||
blame.Header = App.Text("Blame");
|
||||
blame.Icon = App.CreateMenuIcon("Icons.Blame");
|
||||
blame.Click += (o, ev) =>
|
||||
blame.Click += (_, ev) =>
|
||||
{
|
||||
var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) };
|
||||
window.Show();
|
||||
|
@ -320,7 +314,7 @@ namespace SourceGit.ViewModels
|
|||
blame.Header = App.Text("Blame");
|
||||
blame.Icon = App.CreateMenuIcon("Icons.Blame");
|
||||
blame.IsEnabled = file.Type == Models.ObjectType.Blob;
|
||||
blame.Click += (o, ev) =>
|
||||
blame.Click += (_, ev) =>
|
||||
{
|
||||
var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) };
|
||||
window.Show();
|
||||
|
@ -461,7 +455,7 @@ namespace SourceGit.ViewModels
|
|||
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
||||
};
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private string _repo;
|
||||
private int _activePageIndex = 0;
|
||||
private Models.Commit _commit = null;
|
||||
private string _fullMessage = string.Empty;
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = false;
|
||||
bool succ;
|
||||
if (_annotated)
|
||||
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn, Message, SignTag);
|
||||
else
|
||||
|
@ -104,6 +104,6 @@ namespace SourceGit.ViewModels
|
|||
private readonly Repository _repo = null;
|
||||
private string _tagName = string.Empty;
|
||||
private bool _annotated = true;
|
||||
private readonly string _basedOn = string.Empty;
|
||||
private readonly string _basedOn;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ namespace SourceGit.ViewModels
|
|||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
private set => SetProperty(ref _title, value);
|
||||
}
|
||||
|
||||
public string FileModeChange
|
||||
|
@ -237,9 +236,9 @@ namespace SourceGit.ViewModels
|
|||
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
||||
};
|
||||
|
||||
private readonly string _repo = string.Empty;
|
||||
private readonly string _repo;
|
||||
private readonly Models.DiffOption _option = null;
|
||||
private string _title = string.Empty;
|
||||
private string _title;
|
||||
private string _fileModeChange = string.Empty;
|
||||
private bool _isLoading = true;
|
||||
private bool _isTextDiff = false;
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public class Discard : Popup
|
||||
{
|
||||
|
||||
public object Mode
|
||||
{
|
||||
get;
|
||||
|
@ -54,19 +53,18 @@ namespace SourceGit.ViewModels
|
|||
return Task.Run(() =>
|
||||
{
|
||||
if (_changes == null)
|
||||
{
|
||||
Commands.Discard.All(_repo.FullPath);
|
||||
}
|
||||
else if (_isUnstaged)
|
||||
{
|
||||
Commands.Discard.ChangesInWorkTree(_repo.FullPath, _changes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.Discard.ChangesInStaged(_repo.FullPath, _changes);
|
||||
}
|
||||
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
CallUIThread(() =>
|
||||
{
|
||||
_repo.MarkWorkingCopyDirtyManually();
|
||||
_repo.SetWatcherEnabled(true);
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace SourceGit.ViewModels
|
|||
foreach (var remote in edit._repo.Remotes)
|
||||
{
|
||||
if (remote != edit._remote && name == remote.Name)
|
||||
new ValidationResult("A remote with given name already exists!!!");
|
||||
return new ValidationResult("A remote with given name already exists!!!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ namespace SourceGit.ViewModels
|
|||
foreach (var remote in edit._repo.Remotes)
|
||||
{
|
||||
if (remote != edit._remote && url == remote.URL)
|
||||
new ValidationResult("A remote with the same url already exists!!!");
|
||||
return new ValidationResult("A remote with the same url already exists!!!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +136,8 @@ namespace SourceGit.ViewModels
|
|||
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.Remote _remote = null;
|
||||
private string _name = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private string _name = null;
|
||||
private string _url = null;
|
||||
private bool _useSSH = false;
|
||||
private string _sshkey = string.Empty;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,6 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class EditRepositoryNode : Popup
|
||||
{
|
||||
public RepositoryNode Node
|
||||
{
|
||||
get => _node;
|
||||
set => SetProperty(ref _node, value);
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
|
@ -60,8 +54,8 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
private RepositoryNode _node = null;
|
||||
private string _id = string.Empty;
|
||||
private string _name = string.Empty;
|
||||
private string _id = null;
|
||||
private string _name = null;
|
||||
private bool _isRepository = false;
|
||||
private int _bookmark = 0;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,6 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private bool _fetchAllRemotes = true;
|
||||
private bool _fetchAllRemotes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly string _repo = string.Empty;
|
||||
private readonly string _file = string.Empty;
|
||||
private readonly string _repo = null;
|
||||
private readonly string _file = null;
|
||||
private bool _isLoading = true;
|
||||
private List<Models.Commit> _commits = null;
|
||||
private Models.Commit _selectedCommit = null;
|
||||
|
|
|
@ -43,8 +43,8 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private readonly string _type = "feature";
|
||||
private readonly string _prefix = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private readonly string _type;
|
||||
private readonly string _prefix;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
_repo = repo;
|
||||
_type = type;
|
||||
_prefix = Commands.GitFlow.Prefix(repo.FullPath, type);
|
||||
_prefix = Commands.GitFlow.GetPrefix(repo.FullPath, type);
|
||||
|
||||
View = new Views.GitFlowStart() { DataContext = this };
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private readonly string _type = "feature";
|
||||
private readonly string _prefix = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private readonly string _type;
|
||||
private readonly string _prefix;
|
||||
private string _name = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
|||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
using Avalonia.VisualTree;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
|
@ -156,7 +156,7 @@ namespace SourceGit.ViewModels
|
|||
if (current == null)
|
||||
return null;
|
||||
|
||||
var commit = datagrid.SelectedItem as Models.Commit;
|
||||
var commit = (datagrid.SelectedItem as Models.Commit)!;
|
||||
var menu = new ContextMenu();
|
||||
var tags = new List<Models.Tag>();
|
||||
|
||||
|
@ -202,7 +202,7 @@ namespace SourceGit.ViewModels
|
|||
var reset = new MenuItem();
|
||||
reset.Header = new Views.NameHighlightedTextBlock("CommitCM.Reset", current.Name);
|
||||
reset.Icon = App.CreateMenuIcon("Icons.Reset");
|
||||
reset.Click += (o, e) =>
|
||||
reset.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Reset(_repo, current, commit));
|
||||
|
@ -215,7 +215,7 @@ namespace SourceGit.ViewModels
|
|||
var reword = new MenuItem();
|
||||
reword.Header = App.Text("CommitCM.Reword");
|
||||
reword.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
reword.Click += (o, e) =>
|
||||
reword.Click += (_, e) =>
|
||||
{
|
||||
if (_repo.WorkingCopyChangesCount > 0)
|
||||
{
|
||||
|
@ -233,7 +233,7 @@ namespace SourceGit.ViewModels
|
|||
squash.Header = App.Text("CommitCM.Squash");
|
||||
squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent");
|
||||
squash.IsEnabled = commit.Parents.Count == 1;
|
||||
squash.Click += (o, e) =>
|
||||
squash.Click += (_, e) =>
|
||||
{
|
||||
if (_repo.WorkingCopyChangesCount > 0)
|
||||
{
|
||||
|
@ -258,7 +258,7 @@ namespace SourceGit.ViewModels
|
|||
var rebase = new MenuItem();
|
||||
rebase.Header = new Views.NameHighlightedTextBlock("CommitCM.Rebase", current.Name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (o, e) =>
|
||||
rebase.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Rebase(_repo, current, commit));
|
||||
|
@ -269,7 +269,7 @@ namespace SourceGit.ViewModels
|
|||
var cherryPick = new MenuItem();
|
||||
cherryPick.Header = App.Text("CommitCM.CherryPick");
|
||||
cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick");
|
||||
cherryPick.Click += (o, e) =>
|
||||
cherryPick.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CherryPick(_repo, commit));
|
||||
|
@ -282,7 +282,7 @@ namespace SourceGit.ViewModels
|
|||
var revert = new MenuItem();
|
||||
revert.Header = App.Text("CommitCM.Revert");
|
||||
revert.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
revert.Click += (o, e) =>
|
||||
revert.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Revert(_repo, commit));
|
||||
|
@ -294,7 +294,7 @@ namespace SourceGit.ViewModels
|
|||
interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name);
|
||||
interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
|
||||
interactiveRebase.IsVisible = current.Head != commit.SHA;
|
||||
interactiveRebase.Click += (o, e) =>
|
||||
interactiveRebase.Click += (_, e) =>
|
||||
{
|
||||
if (_repo.WorkingCopyChangesCount > 0)
|
||||
{
|
||||
|
@ -302,8 +302,12 @@ namespace SourceGit.ViewModels
|
|||
return;
|
||||
}
|
||||
|
||||
var toplevel = datagrid.FindAncestorOfType<Views.Launcher>();
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.InteractiveRebase() { DataContext = new InteractiveRebase(_repo, current, commit) };
|
||||
dialog.ShowDialog(App.GetTopLevel() as Window);
|
||||
dialog.ShowDialog(toplevel);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(interactiveRebase);
|
||||
|
@ -314,7 +318,7 @@ namespace SourceGit.ViewModels
|
|||
var checkoutCommit = new MenuItem();
|
||||
checkoutCommit.Header = App.Text("CommitCM.Checkout");
|
||||
checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached");
|
||||
checkoutCommit.Click += (o, e) =>
|
||||
checkoutCommit.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CheckoutCommit(_repo, commit));
|
||||
|
@ -330,7 +334,7 @@ namespace SourceGit.ViewModels
|
|||
var compareWithHead = new MenuItem();
|
||||
compareWithHead.Header = App.Text("CommitCM.CompareWithHead");
|
||||
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithHead.Click += (o, e) =>
|
||||
compareWithHead.Click += (_, e) =>
|
||||
{
|
||||
var head = _commits.Find(x => x.SHA == current.Head);
|
||||
if (head == null)
|
||||
|
@ -354,7 +358,7 @@ namespace SourceGit.ViewModels
|
|||
var compareWithWorktree = new MenuItem();
|
||||
compareWithWorktree.Header = App.Text("CommitCM.CompareWithWorktree");
|
||||
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithWorktree.Click += (o, e) =>
|
||||
compareWithWorktree.Click += (_, e) =>
|
||||
{
|
||||
DetailContext = new RevisionCompare(_repo.FullPath, commit, null);
|
||||
e.Handled = true;
|
||||
|
@ -368,7 +372,7 @@ namespace SourceGit.ViewModels
|
|||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (o, e) =>
|
||||
createBranch.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateBranch(_repo, commit));
|
||||
|
@ -379,7 +383,7 @@ namespace SourceGit.ViewModels
|
|||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (o, e) =>
|
||||
createTag.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateTag(_repo, commit));
|
||||
|
@ -413,7 +417,7 @@ namespace SourceGit.ViewModels
|
|||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (o, e) =>
|
||||
archive.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Archive(_repo, commit));
|
||||
|
@ -425,7 +429,7 @@ namespace SourceGit.ViewModels
|
|||
var copySHA = new MenuItem();
|
||||
copySHA.Header = App.Text("CommitCM.CopySHA");
|
||||
copySHA.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copySHA.Click += (o, e) =>
|
||||
copySHA.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(commit.SHA);
|
||||
e.Handled = true;
|
||||
|
@ -448,7 +452,7 @@ namespace SourceGit.ViewModels
|
|||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(current.UpstreamTrackStatus) && current.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.Click += (o, e) =>
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name));
|
||||
|
@ -459,7 +463,7 @@ namespace SourceGit.ViewModels
|
|||
var pull = new MenuItem();
|
||||
pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (o, e) =>
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Pull(_repo, null));
|
||||
|
@ -472,7 +476,7 @@ namespace SourceGit.ViewModels
|
|||
push.Header = new Views.NameHighlightedTextBlock("BranchCM.Push", current.Name);
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = _repo.Remotes.Count > 0;
|
||||
push.Click += (o, e) =>
|
||||
push.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Push(_repo, current));
|
||||
|
@ -487,7 +491,7 @@ namespace SourceGit.ViewModels
|
|||
var finish = new MenuItem();
|
||||
finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", current.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.GitFlow");
|
||||
finish.Click += (o, e) =>
|
||||
finish.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowFinish(_repo, current, detect.Type, detect.Prefix));
|
||||
|
@ -500,7 +504,7 @@ namespace SourceGit.ViewModels
|
|||
var rename = new MenuItem();
|
||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", current.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (o, e) =>
|
||||
rename.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new RenameBranch(_repo, current));
|
||||
|
@ -520,7 +524,7 @@ namespace SourceGit.ViewModels
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) =>
|
||||
checkout.Click += (_, e) =>
|
||||
{
|
||||
_repo.CheckoutBranch(branch);
|
||||
e.Handled = true;
|
||||
|
@ -531,7 +535,7 @@ namespace SourceGit.ViewModels
|
|||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.IsEnabled = !merged;
|
||||
merge.Click += (o, e) =>
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name));
|
||||
|
@ -546,7 +550,7 @@ namespace SourceGit.ViewModels
|
|||
var finish = new MenuItem();
|
||||
finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.GitFlow");
|
||||
finish.Click += (o, e) =>
|
||||
finish.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, detect.Type, detect.Prefix));
|
||||
|
@ -559,7 +563,7 @@ namespace SourceGit.ViewModels
|
|||
var rename = new MenuItem();
|
||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (o, e) =>
|
||||
rename.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new RenameBranch(_repo, branch));
|
||||
|
@ -570,7 +574,7 @@ namespace SourceGit.ViewModels
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", branch.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
|
||||
|
@ -592,7 +596,7 @@ namespace SourceGit.ViewModels
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) =>
|
||||
checkout.Click += (_, e) =>
|
||||
{
|
||||
_repo.CheckoutBranch(branch);
|
||||
e.Handled = true;
|
||||
|
@ -603,7 +607,7 @@ namespace SourceGit.ViewModels
|
|||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.IsEnabled = !merged;
|
||||
merge.Click += (o, e) =>
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Merge(_repo, name, current.Name));
|
||||
|
@ -616,7 +620,7 @@ namespace SourceGit.ViewModels
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
|
||||
|
@ -638,7 +642,7 @@ namespace SourceGit.ViewModels
|
|||
push.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name);
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = _repo.Remotes.Count > 0;
|
||||
push.Click += (o, e) =>
|
||||
push.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new PushTag(_repo, tag));
|
||||
|
@ -649,7 +653,7 @@ namespace SourceGit.ViewModels
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteTag(_repo, tag));
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
|
@ -39,7 +38,8 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
WorkingDirectory = Repository,
|
||||
Context = Repository,
|
||||
Args = $"-c core.editor=true {Cmd} --continue",
|
||||
Editor = Commands.Command.EditorType.None,
|
||||
Args = $"{Cmd} --continue",
|
||||
}.Exec();
|
||||
}
|
||||
}
|
||||
|
@ -58,14 +58,12 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public override bool Continue()
|
||||
{
|
||||
var exec = Process.GetCurrentProcess().MainModule.FileName;
|
||||
var editor = $"\\\"{exec}\\\" --rebase-editor";
|
||||
|
||||
var succ = new Commands.Command()
|
||||
{
|
||||
WorkingDirectory = Repository,
|
||||
Context = Repository,
|
||||
Args = $"-c core.editor=\"{editor}\" rebase --continue",
|
||||
Editor = Commands.Command.EditorType.RebaseEditor,
|
||||
Args = $"rebase --continue",
|
||||
}.Exec();
|
||||
|
||||
if (succ)
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private string _targetPath = string.Empty;
|
||||
private string _targetPath = null;
|
||||
private RepositoryNode _parentNode = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,8 +114,8 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private string _master = "master";
|
||||
private readonly Repository _repo;
|
||||
private string _master;
|
||||
private string _develop = "develop";
|
||||
private string _featurePrefix = "feature/";
|
||||
private string _releasePrefix = "release/";
|
||||
|
|
|
@ -62,8 +62,8 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
private Models.InteractiveRebaseAction _action = Models.InteractiveRebaseAction.Pick;
|
||||
private string _subject = string.Empty;
|
||||
private string _fullMessage = string.Empty;
|
||||
private string _subject;
|
||||
private string _fullMessage;
|
||||
}
|
||||
|
||||
public class InteractiveRebase : ObservableObject
|
||||
|
@ -168,18 +168,18 @@ namespace SourceGit.ViewModels
|
|||
_repo.SetWatcherEnabled(false);
|
||||
|
||||
var saveFile = Path.Combine(_repo.GitDir, "sourcegit_rebase_jobs.json");
|
||||
var jobs = new List<Models.InteractiveRebaseJob>();
|
||||
var collection = new Models.InteractiveRebaseJobCollection();
|
||||
for (int i = Items.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var item = Items[i];
|
||||
jobs.Add(new Models.InteractiveRebaseJob()
|
||||
collection.Jobs.Add(new Models.InteractiveRebaseJob()
|
||||
{
|
||||
SHA = item.Commit.SHA,
|
||||
Action = item.Action,
|
||||
Message = item.FullMessage,
|
||||
});
|
||||
}
|
||||
File.WriteAllText(saveFile, JsonSerializer.Serialize(jobs, JsonCodeGen.Default.ListInteractiveRebaseJob));
|
||||
File.WriteAllText(saveFile, JsonSerializer.Serialize(collection, JsonCodeGen.Default.InteractiveRebaseJobCollection));
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
|
|
|
@ -67,8 +67,8 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private string _remote = string.Empty;
|
||||
private string _repo;
|
||||
private string _remote;
|
||||
private bool _isLoading = true;
|
||||
private bool _isEmpty = false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
|
@ -28,22 +29,20 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
public Launcher()
|
||||
public Launcher(string startupRepo)
|
||||
{
|
||||
Pages = new AvaloniaList<LauncherPage>();
|
||||
AddNewTab();
|
||||
|
||||
var commandlines = Environment.GetCommandLineArgs();
|
||||
if (commandlines.Length == 2)
|
||||
if (!string.IsNullOrEmpty(startupRepo))
|
||||
{
|
||||
var path = commandlines[1];
|
||||
var root = new Commands.QueryRepositoryRootPath(path).Result();
|
||||
var root = new Commands.QueryRepositoryRootPath(startupRepo).Result();
|
||||
if (string.IsNullOrEmpty(root))
|
||||
{
|
||||
Pages[0].Notifications.Add(new Notification
|
||||
{
|
||||
IsError = true,
|
||||
Message = $"Given path: '{path}' is NOT a valid repository!"
|
||||
Message = $"Given path: '{startupRepo}' is NOT a valid repository!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -154,7 +153,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
else
|
||||
{
|
||||
App.Quit();
|
||||
App.Quit(0);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -274,7 +273,8 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public void DispatchNotification(string pageId, string message, bool isError)
|
||||
{
|
||||
var notification = new Notification() {
|
||||
var notification = new Notification()
|
||||
{
|
||||
IsError = isError,
|
||||
Message = message,
|
||||
};
|
||||
|
@ -293,12 +293,6 @@ namespace SourceGit.ViewModels
|
|||
_activePage.Notifications.Add(notification);
|
||||
}
|
||||
|
||||
public void DismissNotification(Notification notice)
|
||||
{
|
||||
if (notice != null)
|
||||
ActivePage?.Notifications.Remove(notice);
|
||||
}
|
||||
|
||||
public ContextMenu CreateContextForPageTab(LauncherPage page)
|
||||
{
|
||||
if (page == null)
|
||||
|
@ -308,7 +302,7 @@ namespace SourceGit.ViewModels
|
|||
var close = new MenuItem();
|
||||
close.Header = App.Text("PageTabBar.Tab.Close");
|
||||
close.InputGesture = KeyGesture.Parse(OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W");
|
||||
close.Click += (o, e) =>
|
||||
close.Click += (_, e) =>
|
||||
{
|
||||
CloseTab(page);
|
||||
e.Handled = true;
|
||||
|
@ -317,7 +311,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var closeOthers = new MenuItem();
|
||||
closeOthers.Header = App.Text("PageTabBar.Tab.CloseOther");
|
||||
closeOthers.Click += (o, e) =>
|
||||
closeOthers.Click += (_, e) =>
|
||||
{
|
||||
CloseOtherTabs();
|
||||
e.Handled = true;
|
||||
|
@ -326,7 +320,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var closeRight = new MenuItem();
|
||||
closeRight.Header = App.Text("PageTabBar.Tab.CloseRight");
|
||||
closeRight.Click += (o, e) =>
|
||||
closeRight.Click += (_, e) =>
|
||||
{
|
||||
CloseRightTabs();
|
||||
e.Handled = true;
|
||||
|
@ -343,13 +337,13 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
var icon = App.CreateMenuIcon("Icons.Bookmark");
|
||||
icon.Fill = Models.Bookmarks.Brushes[i];
|
||||
icon.Stroke = App.Current.FindResource("Brush.FG1") as Brush;
|
||||
icon.Stroke = Application.Current?.FindResource("Brush.FG1") as Brush;
|
||||
icon.StrokeThickness = i == 0 ? 1.0 : 0;
|
||||
|
||||
var dupIdx = i;
|
||||
var setter = new MenuItem();
|
||||
setter.Header = icon;
|
||||
setter.Click += (o, e) =>
|
||||
setter.Click += (_, e) =>
|
||||
{
|
||||
page.Node.Bookmark = dupIdx;
|
||||
e.Handled = true;
|
||||
|
@ -362,7 +356,7 @@ namespace SourceGit.ViewModels
|
|||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("PageTabBar.Tab.CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (o, e) =>
|
||||
copyPath.Click += (_, e) =>
|
||||
{
|
||||
page.CopyPath();
|
||||
e.Handled = true;
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
|
@ -28,35 +24,30 @@ namespace SourceGit.ViewModels
|
|||
set;
|
||||
} = WindowState.Normal;
|
||||
|
||||
[JsonConverter(typeof(GridLengthConverter))]
|
||||
public GridLength RepositorySidebarWidth
|
||||
{
|
||||
get => _repositorySidebarWidth;
|
||||
set => SetProperty(ref _repositorySidebarWidth, value);
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(GridLengthConverter))]
|
||||
public GridLength WorkingCopyLeftWidth
|
||||
{
|
||||
get => _workingCopyLeftWidth;
|
||||
set => SetProperty(ref _workingCopyLeftWidth, value);
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(GridLengthConverter))]
|
||||
public GridLength StashesLeftWidth
|
||||
{
|
||||
get => _stashesLeftWidth;
|
||||
set => SetProperty(ref _stashesLeftWidth, value);
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(GridLengthConverter))]
|
||||
public GridLength CommitDetailChangesLeftWidth
|
||||
{
|
||||
get => _commitDetailChangesLeftWidth;
|
||||
set => SetProperty(ref _commitDetailChangesLeftWidth, value);
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(GridLengthConverter))]
|
||||
public GridLength CommitDetailFilesLeftWidth
|
||||
{
|
||||
get => _commitDetailFilesLeftWidth;
|
||||
|
@ -69,18 +60,4 @@ namespace SourceGit.ViewModels
|
|||
private GridLength _commitDetailChangesLeftWidth = new GridLength(256, GridUnitType.Pixel);
|
||||
private GridLength _commitDetailFilesLeftWidth = new GridLength(256, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
public class GridLengthConverter : JsonConverter<GridLength>
|
||||
{
|
||||
public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var size = reader.GetDouble();
|
||||
return new GridLength(size, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, GridLength value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,28 +66,26 @@ namespace SourceGit.ViewModels
|
|||
set
|
||||
{
|
||||
if (SetProperty(ref _theme, value))
|
||||
App.SetTheme(_theme, _colorOverrides);
|
||||
App.SetTheme(_theme, _themeOverrides);
|
||||
}
|
||||
}
|
||||
|
||||
public string ColorOverrides
|
||||
public string ThemeOverrides
|
||||
{
|
||||
get => _colorOverrides;
|
||||
get => _themeOverrides;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _colorOverrides, value))
|
||||
if (SetProperty(ref _themeOverrides, value))
|
||||
App.SetTheme(_theme, value);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(FontFamilyConverter))]
|
||||
public FontFamily DefaultFont
|
||||
{
|
||||
get => _defaultFont;
|
||||
set => SetProperty(ref _defaultFont, value);
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(FontFamilyConverter))]
|
||||
public FontFamily MonospaceFont
|
||||
{
|
||||
get => _monospaceFont;
|
||||
|
@ -114,7 +112,7 @@ namespace SourceGit.ViewModels
|
|||
if (Models.AvatarManager.SelectedServer != value)
|
||||
{
|
||||
Models.AvatarManager.SelectedServer = value;
|
||||
OnPropertyChanged(nameof(AvatarServer));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +221,7 @@ namespace SourceGit.ViewModels
|
|||
if (Native.OS.GitExecutable != value)
|
||||
{
|
||||
Native.OS.GitExecutable = value;
|
||||
OnPropertyChanged(nameof(GitInstallPath));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +233,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
if (Native.OS.SetShell(value))
|
||||
{
|
||||
OnPropertyChanged(nameof(GitShell));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +252,7 @@ namespace SourceGit.ViewModels
|
|||
if (Commands.AutoFetch.IsEnabled != value)
|
||||
{
|
||||
Commands.AutoFetch.IsEnabled = value;
|
||||
OnPropertyChanged(nameof(GitAutoFetch));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +268,7 @@ namespace SourceGit.ViewModels
|
|||
if (Commands.AutoFetch.Interval != value)
|
||||
{
|
||||
Commands.AutoFetch.Interval = (int)value;
|
||||
OnPropertyChanged(nameof(GitAutoFetchInterval));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,8 +349,8 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
if (l.IsRepository != r.IsRepository)
|
||||
return l.IsRepository ? 1 : -1;
|
||||
else
|
||||
return l.Name.CompareTo(r.Name);
|
||||
|
||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||
});
|
||||
|
||||
collection.Clear();
|
||||
|
@ -415,13 +413,9 @@ namespace SourceGit.ViewModels
|
|||
list.Sort((l, r) =>
|
||||
{
|
||||
if (l.IsRepository != r.IsRepository)
|
||||
{
|
||||
return l.IsRepository ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return l.Name.CompareTo(r.Name);
|
||||
}
|
||||
|
||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||
});
|
||||
|
||||
container.Clear();
|
||||
|
@ -487,7 +481,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
private string _locale = "en_US";
|
||||
private string _theme = "Default";
|
||||
private string _colorOverrides = string.Empty;
|
||||
private string _themeOverrides = string.Empty;
|
||||
private FontFamily _defaultFont = null;
|
||||
private FontFamily _monospaceFont = null;
|
||||
private double _defaultFontSize = 13;
|
||||
|
@ -517,18 +511,4 @@ namespace SourceGit.ViewModels
|
|||
|
||||
private AvaloniaList<RepositoryNode> _repositoryNodes = new AvaloniaList<RepositoryNode>();
|
||||
}
|
||||
|
||||
public class FontFamilyConverter : JsonConverter<FontFamily>
|
||||
{
|
||||
public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var name = reader.GetString();
|
||||
return new FontFamily(name);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
// Find preferred remote if selected local branch has upstream.
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream))
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch?.Upstream))
|
||||
{
|
||||
foreach (var branch in repo.Branches)
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private readonly string _revision = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private readonly string _revision;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private string _name = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private string _name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,6 @@ namespace SourceGit.ViewModels
|
|||
public RepositorySettings Settings
|
||||
{
|
||||
get => _settings;
|
||||
private set => SetProperty(ref _settings, value);
|
||||
}
|
||||
|
||||
public int SelectedViewIndex
|
||||
|
@ -259,6 +258,12 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsSearchLoadingVisible
|
||||
{
|
||||
get => _isSearchLoadingVisible;
|
||||
private set => SetProperty(ref _isSearchLoadingVisible, value);
|
||||
}
|
||||
|
||||
public int SearchCommitFilterType
|
||||
{
|
||||
get => _searchCommitFilterType;
|
||||
|
@ -430,7 +435,7 @@ namespace SourceGit.ViewModels
|
|||
var item = new MenuItem();
|
||||
item.Header = App.Text("Repository.OpenIn", dupTool.Name);
|
||||
item.Icon = new Image { Width = 16, Height = 16, Source = dupTool.IconImage };
|
||||
item.Click += (o, e) =>
|
||||
item.Click += (_, e) =>
|
||||
{
|
||||
dupTool.Open(_fullpath);
|
||||
e.Handled = true;
|
||||
|
@ -533,29 +538,47 @@ namespace SourceGit.ViewModels
|
|||
if (_histories == null)
|
||||
return;
|
||||
|
||||
IsSearchLoadingVisible = true;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var visible = new List<Models.Commit>();
|
||||
|
||||
if (_searchCommitFilterType == 0)
|
||||
switch (_searchCommitFilterType)
|
||||
{
|
||||
case 0:
|
||||
foreach (var c in _histories.Commits)
|
||||
{
|
||||
if (c.SHA.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
|| c.Subject.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
|| c.Author.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
if (c.SHA.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase))
|
||||
visible.Add(c);
|
||||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
foreach (var c in _histories.Commits)
|
||||
{
|
||||
if (c.Author.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
|| c.Committer.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
|| c.Author.Email.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|
||||
|| c.Committer.Email.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
break;
|
||||
case 2:
|
||||
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter).Result();
|
||||
break;
|
||||
case 3:
|
||||
visible = new Commands.QueryCommits(FullPath, $"-1000 -- \"{_searchCommitFilter}\"", false).Result();
|
||||
break;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
SearchedCommits = visible;
|
||||
IsSearchLoadingVisible = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearSearchBranchFilter()
|
||||
|
@ -989,7 +1012,7 @@ namespace SourceGit.ViewModels
|
|||
var startFeature = new MenuItem();
|
||||
startFeature.Header = App.Text("GitFlow.StartFeature");
|
||||
startFeature.Icon = App.CreateMenuIcon("Icons.GitFlow.Feature");
|
||||
startFeature.Click += (o, e) =>
|
||||
startFeature.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowStart(this, "feature"));
|
||||
|
@ -999,7 +1022,7 @@ namespace SourceGit.ViewModels
|
|||
var startRelease = new MenuItem();
|
||||
startRelease.Header = App.Text("GitFlow.StartRelease");
|
||||
startRelease.Icon = App.CreateMenuIcon("Icons.GitFlow.Release");
|
||||
startRelease.Click += (o, e) =>
|
||||
startRelease.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowStart(this, "release"));
|
||||
|
@ -1009,7 +1032,7 @@ namespace SourceGit.ViewModels
|
|||
var startHotfix = new MenuItem();
|
||||
startHotfix.Header = App.Text("GitFlow.StartHotfix");
|
||||
startHotfix.Icon = App.CreateMenuIcon("Icons.GitFlow.Hotfix");
|
||||
startHotfix.Click += (o, e) =>
|
||||
startHotfix.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowStart(this, "hotfix"));
|
||||
|
@ -1025,7 +1048,7 @@ namespace SourceGit.ViewModels
|
|||
var init = new MenuItem();
|
||||
init.Header = App.Text("GitFlow.Init");
|
||||
init.Icon = App.CreateMenuIcon("Icons.Init");
|
||||
init.Click += (o, e) =>
|
||||
init.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new InitGitFlow(this));
|
||||
|
@ -1047,7 +1070,7 @@ namespace SourceGit.ViewModels
|
|||
var addPattern = new MenuItem();
|
||||
addPattern.Header = App.Text("GitLFS.AddTrackPattern");
|
||||
addPattern.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||
addPattern.Click += (o, e) =>
|
||||
addPattern.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new LFSTrackCustomPattern(this));
|
||||
|
@ -1061,7 +1084,7 @@ namespace SourceGit.ViewModels
|
|||
fetch.Header = App.Text("GitLFS.Fetch");
|
||||
fetch.Icon = App.CreateMenuIcon("Icons.Fetch");
|
||||
fetch.IsEnabled = Remotes.Count > 0;
|
||||
fetch.Click += (o, e) =>
|
||||
fetch.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
|
@ -1079,7 +1102,7 @@ namespace SourceGit.ViewModels
|
|||
pull.Header = App.Text("GitLFS.Pull");
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.IsEnabled = Remotes.Count > 0;
|
||||
pull.Click += (o, e) =>
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
|
@ -1097,7 +1120,7 @@ namespace SourceGit.ViewModels
|
|||
push.Header = App.Text("GitLFS.Push");
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = Remotes.Count > 0;
|
||||
push.Click += (o, e) =>
|
||||
push.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
|
@ -1114,7 +1137,7 @@ namespace SourceGit.ViewModels
|
|||
var prune = new MenuItem();
|
||||
prune.Header = App.Text("GitLFS.Prune");
|
||||
prune.Icon = App.CreateMenuIcon("Icons.Clean");
|
||||
prune.Click += (o, e) =>
|
||||
prune.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new LFSPrune(this));
|
||||
|
@ -1130,10 +1153,14 @@ namespace SourceGit.ViewModels
|
|||
locks.IsEnabled = Remotes.Count > 0;
|
||||
if (Remotes.Count == 1)
|
||||
{
|
||||
locks.Click += (o, e) =>
|
||||
locks.Click += (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel() as Window;
|
||||
if (topLevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, Remotes[0].Name) };
|
||||
dialog.Show(App.GetTopLevel() as Window);
|
||||
dialog.Show(topLevel);
|
||||
e.Handled = true;
|
||||
};
|
||||
}
|
||||
|
@ -1144,10 +1171,14 @@ namespace SourceGit.ViewModels
|
|||
var remoteName = remote.Name;
|
||||
var lockRemote = new MenuItem();
|
||||
lockRemote.Header = remoteName;
|
||||
lockRemote.Click += (o, e) =>
|
||||
lockRemote.Click += (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel() as Window;
|
||||
if (topLevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) };
|
||||
dialog.Show(App.GetTopLevel() as Window);
|
||||
dialog.Show(topLevel);
|
||||
e.Handled = true;
|
||||
};
|
||||
locks.Items.Add(lockRemote);
|
||||
|
@ -1162,7 +1193,7 @@ namespace SourceGit.ViewModels
|
|||
var install = new MenuItem();
|
||||
install.Header = App.Text("GitLFS.Install");
|
||||
install.Icon = App.CreateMenuIcon("Icons.Init");
|
||||
install.Click += (o, e) =>
|
||||
install.Click += (_, e) =>
|
||||
{
|
||||
var succ = new Commands.LFS(_fullpath).Install();
|
||||
if (succ)
|
||||
|
@ -1197,7 +1228,7 @@ namespace SourceGit.ViewModels
|
|||
discard.Header = App.Text("BranchCM.DiscardAll");
|
||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
discard.IsEnabled = _workingCopy.Count > 0;
|
||||
discard.Click += (o, e) =>
|
||||
discard.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Discard(this));
|
||||
|
@ -1214,7 +1245,7 @@ namespace SourceGit.ViewModels
|
|||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.Click += (o, e) =>
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new Merge(this, upstream, branch.Name));
|
||||
|
@ -1224,7 +1255,7 @@ namespace SourceGit.ViewModels
|
|||
var pull = new MenuItem();
|
||||
pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (o, e) =>
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Pull(this, null));
|
||||
|
@ -1251,7 +1282,7 @@ namespace SourceGit.ViewModels
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) =>
|
||||
checkout.Click += (_, e) =>
|
||||
{
|
||||
CheckoutBranch(branch);
|
||||
e.Handled = true;
|
||||
|
@ -1265,7 +1296,7 @@ namespace SourceGit.ViewModels
|
|||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream.FriendlyName);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.Click += (o, e) =>
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new FastForwardWithoutCheckout(this, branch, upstream));
|
||||
|
@ -1282,7 +1313,7 @@ namespace SourceGit.ViewModels
|
|||
var merge = new MenuItem();
|
||||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.Click += (o, e) =>
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name));
|
||||
|
@ -1292,7 +1323,7 @@ namespace SourceGit.ViewModels
|
|||
var rebase = new MenuItem();
|
||||
rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, branch.Name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (o, e) =>
|
||||
rebase.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Rebase(this, current, branch));
|
||||
|
@ -1307,7 +1338,7 @@ namespace SourceGit.ViewModels
|
|||
var compareWithWorktree = new MenuItem();
|
||||
compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree");
|
||||
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithWorktree.Click += (o, e) =>
|
||||
compareWithWorktree.Click += (_, _) =>
|
||||
{
|
||||
SearchResultSelectedCommit = null;
|
||||
|
||||
|
@ -1338,7 +1369,7 @@ namespace SourceGit.ViewModels
|
|||
var finish = new MenuItem();
|
||||
finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.GitFlow");
|
||||
finish.Click += (o, e) =>
|
||||
finish.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new GitFlowFinish(this, branch, detect.Type, detect.Prefix));
|
||||
|
@ -1351,7 +1382,7 @@ namespace SourceGit.ViewModels
|
|||
var rename = new MenuItem();
|
||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (o, e) =>
|
||||
rename.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new RenameBranch(this, branch));
|
||||
|
@ -1362,7 +1393,7 @@ namespace SourceGit.ViewModels
|
|||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", branch.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.IsEnabled = !branch.IsCurrent;
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteBranch(this, branch));
|
||||
|
@ -1372,7 +1403,7 @@ namespace SourceGit.ViewModels
|
|||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (o, e) =>
|
||||
createBranch.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateBranch(this, branch));
|
||||
|
@ -1382,7 +1413,7 @@ namespace SourceGit.ViewModels
|
|||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (o, e) =>
|
||||
createTag.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateTag(this, branch));
|
||||
|
@ -1418,7 +1449,7 @@ namespace SourceGit.ViewModels
|
|||
if (branch.Upstream == b.FullName)
|
||||
target.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
|
||||
target.Click += (o, e) =>
|
||||
target.Click += (_, e) =>
|
||||
{
|
||||
if (Commands.Branch.SetUpstream(_fullpath, branch.Name, upstream))
|
||||
Task.Run(RefreshBranches);
|
||||
|
@ -1447,7 +1478,7 @@ namespace SourceGit.ViewModels
|
|||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (o, e) =>
|
||||
archive.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Archive(this, branch));
|
||||
|
@ -1459,7 +1490,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("BranchCM.CopyName");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, e) =>
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(branch.Name);
|
||||
e.Handled = true;
|
||||
|
@ -1478,7 +1509,7 @@ namespace SourceGit.ViewModels
|
|||
var visit = new MenuItem();
|
||||
visit.Header = App.Text("RemoteCM.OpenInBrowser");
|
||||
visit.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
visit.Click += (o, e) =>
|
||||
visit.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenBrowser(visitURL);
|
||||
e.Handled = true;
|
||||
|
@ -1491,7 +1522,7 @@ namespace SourceGit.ViewModels
|
|||
var fetch = new MenuItem();
|
||||
fetch.Header = App.Text("RemoteCM.Fetch");
|
||||
fetch.Icon = App.CreateMenuIcon("Icons.Fetch");
|
||||
fetch.Click += (o, e) =>
|
||||
fetch.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new Fetch(this, remote));
|
||||
|
@ -1501,7 +1532,7 @@ namespace SourceGit.ViewModels
|
|||
var prune = new MenuItem();
|
||||
prune.Header = App.Text("RemoteCM.Prune");
|
||||
prune.Icon = App.CreateMenuIcon("Icons.Clean");
|
||||
prune.Click += (o, e) =>
|
||||
prune.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowAndStartPopup(new PruneRemote(this, remote));
|
||||
|
@ -1511,7 +1542,7 @@ namespace SourceGit.ViewModels
|
|||
var edit = new MenuItem();
|
||||
edit.Header = App.Text("RemoteCM.Edit");
|
||||
edit.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
edit.Click += (o, e) =>
|
||||
edit.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new EditRemote(this, remote));
|
||||
|
@ -1521,7 +1552,7 @@ namespace SourceGit.ViewModels
|
|||
var delete = new MenuItem();
|
||||
delete.Header = App.Text("RemoteCM.Delete");
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteRemote(this, remote));
|
||||
|
@ -1531,7 +1562,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("RemoteCM.CopyURL");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, e) =>
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(remote.URL);
|
||||
e.Handled = true;
|
||||
|
@ -1556,7 +1587,7 @@ namespace SourceGit.ViewModels
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) =>
|
||||
checkout.Click += (_, e) =>
|
||||
{
|
||||
CheckoutBranch(branch);
|
||||
e.Handled = true;
|
||||
|
@ -1569,7 +1600,7 @@ namespace SourceGit.ViewModels
|
|||
var pull = new MenuItem();
|
||||
pull.Header = new Views.NameHighlightedTextBlock("BranchCM.PullInto", name, current.Name);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (o, e) =>
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Pull(this, branch));
|
||||
|
@ -1579,7 +1610,7 @@ namespace SourceGit.ViewModels
|
|||
var merge = new MenuItem();
|
||||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.Click += (o, e) =>
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Merge(this, name, current.Name));
|
||||
|
@ -1589,7 +1620,7 @@ namespace SourceGit.ViewModels
|
|||
var rebase = new MenuItem();
|
||||
rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (o, e) =>
|
||||
rebase.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Rebase(this, current, branch));
|
||||
|
@ -1608,7 +1639,7 @@ namespace SourceGit.ViewModels
|
|||
var compareWithWorktree = new MenuItem();
|
||||
compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree");
|
||||
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithWorktree.Click += (o, e) =>
|
||||
compareWithWorktree.Click += (_, _) =>
|
||||
{
|
||||
SearchResultSelectedCommit = null;
|
||||
|
||||
|
@ -1636,7 +1667,7 @@ namespace SourceGit.ViewModels
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) =>
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteBranch(this, branch));
|
||||
|
@ -1646,7 +1677,7 @@ namespace SourceGit.ViewModels
|
|||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (o, e) =>
|
||||
createBranch.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateBranch(this, branch));
|
||||
|
@ -1656,7 +1687,7 @@ namespace SourceGit.ViewModels
|
|||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (o, e) =>
|
||||
createTag.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateTag(this, branch));
|
||||
|
@ -1666,7 +1697,7 @@ namespace SourceGit.ViewModels
|
|||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (o, e) =>
|
||||
archive.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Archive(this, branch));
|
||||
|
@ -1676,7 +1707,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("BranchCM.CopyName");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, e) =>
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(name);
|
||||
e.Handled = true;
|
||||
|
@ -1698,7 +1729,7 @@ namespace SourceGit.ViewModels
|
|||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (o, ev) =>
|
||||
createBranch.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new CreateBranch(this, tag));
|
||||
|
@ -1709,7 +1740,7 @@ namespace SourceGit.ViewModels
|
|||
pushTag.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name);
|
||||
pushTag.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
pushTag.IsEnabled = Remotes.Count > 0;
|
||||
pushTag.Click += (o, ev) =>
|
||||
pushTag.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new PushTag(this, tag));
|
||||
|
@ -1719,7 +1750,7 @@ namespace SourceGit.ViewModels
|
|||
var deleteTag = new MenuItem();
|
||||
deleteTag.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name);
|
||||
deleteTag.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
deleteTag.Click += (o, ev) =>
|
||||
deleteTag.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteTag(this, tag));
|
||||
|
@ -1729,7 +1760,7 @@ namespace SourceGit.ViewModels
|
|||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (o, ev) =>
|
||||
archive.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new Archive(this, tag));
|
||||
|
@ -1739,7 +1770,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("TagCM.Copy");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, ev) =>
|
||||
copy.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(tag.Name);
|
||||
ev.Handled = true;
|
||||
|
@ -1762,7 +1793,7 @@ namespace SourceGit.ViewModels
|
|||
var open = new MenuItem();
|
||||
open.Header = App.Text("Submodule.Open");
|
||||
open.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
open.Click += (o, ev) =>
|
||||
open.Click += (_, ev) =>
|
||||
{
|
||||
OpenSubmodule(submodule);
|
||||
ev.Handled = true;
|
||||
|
@ -1771,7 +1802,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("Submodule.CopyPath");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, ev) =>
|
||||
copy.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(submodule);
|
||||
ev.Handled = true;
|
||||
|
@ -1780,7 +1811,7 @@ namespace SourceGit.ViewModels
|
|||
var rm = new MenuItem();
|
||||
rm.Header = App.Text("Submodule.Remove");
|
||||
rm.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
rm.Click += (o, ev) =>
|
||||
rm.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DeleteSubmodule(this, submodule));
|
||||
|
@ -1803,7 +1834,7 @@ namespace SourceGit.ViewModels
|
|||
var unlock = new MenuItem();
|
||||
unlock.Header = App.Text("Worktree.Unlock");
|
||||
unlock.Icon = App.CreateMenuIcon("Icons.Unlock");
|
||||
unlock.Click += (o, ev) =>
|
||||
unlock.Click += (_, ev) =>
|
||||
{
|
||||
SetWatcherEnabled(false);
|
||||
var succ = new Commands.Worktree(_fullpath).Unlock(worktree.FullPath);
|
||||
|
@ -1819,7 +1850,7 @@ namespace SourceGit.ViewModels
|
|||
var loc = new MenuItem();
|
||||
loc.Header = App.Text("Worktree.Lock");
|
||||
loc.Icon = App.CreateMenuIcon("Icons.Lock");
|
||||
loc.Click += (o, ev) =>
|
||||
loc.Click += (_, ev) =>
|
||||
{
|
||||
SetWatcherEnabled(false);
|
||||
var succ = new Commands.Worktree(_fullpath).Lock(worktree.FullPath);
|
||||
|
@ -1834,7 +1865,7 @@ namespace SourceGit.ViewModels
|
|||
var remove = new MenuItem();
|
||||
remove.Header = App.Text("Worktree.Remove");
|
||||
remove.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
remove.Click += (o, ev) =>
|
||||
remove.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new RemoveWorktree(this, worktree));
|
||||
|
@ -1845,7 +1876,7 @@ namespace SourceGit.ViewModels
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("Worktree.CopyPath");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (o, e) =>
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(worktree.FullPath);
|
||||
e.Handled = true;
|
||||
|
@ -1875,12 +1906,16 @@ namespace SourceGit.ViewModels
|
|||
target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch");
|
||||
target.Click += (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel() as Window;
|
||||
if (topLevel == null)
|
||||
return;
|
||||
|
||||
var wnd = new Views.BranchCompare()
|
||||
{
|
||||
DataContext = new BranchCompare(FullPath, branch, dup)
|
||||
};
|
||||
|
||||
wnd.Show(App.GetTopLevel() as Window);
|
||||
wnd.Show(topLevel);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
|
@ -1948,6 +1983,7 @@ namespace SourceGit.ViewModels
|
|||
private object _selectedView = null;
|
||||
|
||||
private bool _isSearching = false;
|
||||
private bool _isSearchLoadingVisible = false;
|
||||
private int _searchCommitFilterType = 0;
|
||||
private string _searchCommitFilter = string.Empty;
|
||||
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
|
||||
|
|
|
@ -209,8 +209,8 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private string _endPoint = string.Empty;
|
||||
private string _repo;
|
||||
private string _endPoint;
|
||||
private List<Models.Change> _changes = null;
|
||||
private List<Models.Change> _visibleChanges = null;
|
||||
private List<Models.Change> _selectedChanges = null;
|
||||
|
|
32
src/ViewModels/RevisionFileTreeNode.cs
Normal file
32
src/ViewModels/RevisionFileTreeNode.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class RevisionFileTreeNode : ObservableObject
|
||||
{
|
||||
public Models.Object Backend { get; set; } = null;
|
||||
public int Depth { get; set; } = 0;
|
||||
public List<RevisionFileTreeNode> Children { get; set; } = new List<RevisionFileTreeNode>();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => Backend == null ? string.Empty : Path.GetFileName(Backend.Path);
|
||||
}
|
||||
|
||||
public bool IsFolder
|
||||
{
|
||||
get => Backend != null && Backend.Type == Models.ObjectType.Tree;
|
||||
}
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetProperty(ref _isExpanded, value);
|
||||
}
|
||||
|
||||
private bool _isExpanded = false;
|
||||
}
|
||||
}
|
|
@ -45,8 +45,8 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private string _message = string.Empty;
|
||||
private string _oldMessage = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private string _message;
|
||||
private string _oldMessage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private string _message = string.Empty;
|
||||
private readonly Repository _repo;
|
||||
private string _message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class StashChanges : Popup
|
||||
{
|
||||
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var apply = new MenuItem();
|
||||
apply.Header = App.Text("StashCM.Apply");
|
||||
apply.Click += (o, ev) =>
|
||||
apply.Click += (_, ev) =>
|
||||
{
|
||||
Task.Run(() => new Commands.Stash(_repo.FullPath).Apply(stash.Name));
|
||||
ev.Handled = true;
|
||||
|
@ -127,7 +127,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var pop = new MenuItem();
|
||||
pop.Header = App.Text("StashCM.Pop");
|
||||
pop.Click += (o, ev) =>
|
||||
pop.Click += (_, ev) =>
|
||||
{
|
||||
Task.Run(() => new Commands.Stash(_repo.FullPath).Pop(stash.Name));
|
||||
ev.Handled = true;
|
||||
|
@ -135,7 +135,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var drop = new MenuItem();
|
||||
drop.Header = App.Text("StashCM.Drop");
|
||||
drop.Click += (o, ev) =>
|
||||
drop.Click += (_, ev) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash));
|
||||
|
|
|
@ -32,11 +32,9 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public Statistics(string repo)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var result = new Commands.Statistics(_repo).Result();
|
||||
var result = new Commands.Statistics(repo).Result();
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
_data = result;
|
||||
|
@ -65,7 +63,6 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private readonly string _repo = string.Empty;
|
||||
private bool _isLoading = true;
|
||||
private Models.Statistics _data = null;
|
||||
private Models.StatisticsReport _selectedReport = null;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class TwoSideTextDiff : ObservableObject
|
||||
{
|
||||
public string File { get; set; } = string.Empty;
|
||||
public string File { get; set; }
|
||||
public List<Models.TextDiffLine> Old { get; set; } = new List<Models.TextDiffLine>();
|
||||
public List<Models.TextDiffLine> New { get; set; } = new List<Models.TextDiffLine>();
|
||||
public int MaxLineNumber = 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
|
@ -49,15 +49,12 @@ namespace SourceGit.ViewModels
|
|||
return;
|
||||
}
|
||||
|
||||
if (PopupHost.CanCreatePopup())
|
||||
if (PopupHost.CanCreatePopup() &&
|
||||
Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } })
|
||||
{
|
||||
if (App.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var launcher = desktop.MainWindow.DataContext as Launcher;
|
||||
PopupHost.ShowPopup(new Clone(launcher));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenTerminal()
|
||||
{
|
||||
|
@ -98,9 +95,9 @@ namespace SourceGit.ViewModels
|
|||
openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
openAll.Click += (_, e) =>
|
||||
{
|
||||
if (App.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (PopupHost.CanCreatePopup() &&
|
||||
Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } })
|
||||
{
|
||||
var launcher = desktop.MainWindow.DataContext as Launcher;
|
||||
OpenAllInNode(launcher, node);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace SourceGit.ViewModels
|
|||
if (_repo.IncludeUntracked != value)
|
||||
{
|
||||
_repo.IncludeUntracked = value;
|
||||
OnPropertyChanged(nameof(IncludeUntracked));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,12 +286,16 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public void OpenAssumeUnchanged()
|
||||
{
|
||||
var toplevel = App.GetTopLevel() as Window;
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var dialog = new Views.AssumeUnchangedManager()
|
||||
{
|
||||
DataContext = new AssumeUnchangedManager(_repo.FullPath)
|
||||
};
|
||||
|
||||
dialog.ShowDialog(App.GetTopLevel() as Window);
|
||||
dialog.ShowDialog(toplevel);
|
||||
}
|
||||
|
||||
public void StageSelected()
|
||||
|
@ -634,7 +638,7 @@ namespace SourceGit.ViewModels
|
|||
lfsTrackByExtension.Header = App.Text("GitLFS.TrackByExtension", extension);
|
||||
lfsTrackByExtension.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension, false));
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension));
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, $"Tracking all *{extension} files successfully!");
|
||||
|
||||
|
@ -652,7 +656,7 @@ namespace SourceGit.ViewModels
|
|||
lfsLock.IsEnabled = _repo.Remotes.Count > 0;
|
||||
if (_repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsLock.Click += async (o, e) =>
|
||||
lfsLock.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(_repo.Remotes[0].Name, change.Path));
|
||||
if (succ)
|
||||
|
@ -668,7 +672,7 @@ namespace SourceGit.ViewModels
|
|||
var remoteName = remote.Name;
|
||||
var lockRemote = new MenuItem();
|
||||
lockRemote.Header = remoteName;
|
||||
lockRemote.Click += async (o, e) =>
|
||||
lockRemote.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(remoteName, change.Path));
|
||||
if (succ)
|
||||
|
@ -687,7 +691,7 @@ namespace SourceGit.ViewModels
|
|||
lfsUnlock.IsEnabled = _repo.Remotes.Count > 0;
|
||||
if (_repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsUnlock.Click += async (o, e) =>
|
||||
lfsUnlock.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(_repo.Remotes[0].Name, change.Path, false));
|
||||
if (succ)
|
||||
|
@ -703,7 +707,7 @@ namespace SourceGit.ViewModels
|
|||
var remoteName = remote.Name;
|
||||
var unlockRemote = new MenuItem();
|
||||
unlockRemote.Header = remoteName;
|
||||
unlockRemote.Click += async (o, e) =>
|
||||
unlockRemote.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(remoteName, change.Path, false));
|
||||
if (succ)
|
||||
|
@ -823,7 +827,7 @@ namespace SourceGit.ViewModels
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (o, e) =>
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null)
|
||||
|
@ -869,7 +873,7 @@ namespace SourceGit.ViewModels
|
|||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Click += (o, e) =>
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
e.Handled = true;
|
||||
|
@ -888,7 +892,7 @@ namespace SourceGit.ViewModels
|
|||
var unstage = new MenuItem();
|
||||
unstage.Header = App.Text("FileCM.Unstage");
|
||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||
unstage.Click += (o, e) =>
|
||||
unstage.Click += (_, e) =>
|
||||
{
|
||||
UnstageChanges(_selectedStaged);
|
||||
e.Handled = true;
|
||||
|
@ -917,7 +921,7 @@ namespace SourceGit.ViewModels
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (o, e) =>
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null)
|
||||
|
@ -942,7 +946,7 @@ namespace SourceGit.ViewModels
|
|||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (o, e) =>
|
||||
copyPath.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(change.Path);
|
||||
e.Handled = true;
|
||||
|
@ -979,7 +983,7 @@ namespace SourceGit.ViewModels
|
|||
lfsLock.IsEnabled = _repo.Remotes.Count > 0;
|
||||
if (_repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsLock.Click += async (o, e) =>
|
||||
lfsLock.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(_repo.Remotes[0].Name, change.Path));
|
||||
if (succ)
|
||||
|
@ -995,7 +999,7 @@ namespace SourceGit.ViewModels
|
|||
var remoteName = remote.Name;
|
||||
var lockRemote = new MenuItem();
|
||||
lockRemote.Header = remoteName;
|
||||
lockRemote.Click += async (o, e) =>
|
||||
lockRemote.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(remoteName, change.Path));
|
||||
if (succ)
|
||||
|
@ -1014,7 +1018,7 @@ namespace SourceGit.ViewModels
|
|||
lfsUnlock.IsEnabled = _repo.Remotes.Count > 0;
|
||||
if (_repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsUnlock.Click += async (o, e) =>
|
||||
lfsUnlock.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(_repo.Remotes[0].Name, change.Path, false));
|
||||
if (succ)
|
||||
|
@ -1030,7 +1034,7 @@ namespace SourceGit.ViewModels
|
|||
var remoteName = remote.Name;
|
||||
var unlockRemote = new MenuItem();
|
||||
unlockRemote.Header = remoteName;
|
||||
unlockRemote.Click += async (o, e) =>
|
||||
unlockRemote.Click += async (_, e) =>
|
||||
{
|
||||
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(remoteName, change.Path, false));
|
||||
if (succ)
|
||||
|
@ -1055,7 +1059,7 @@ namespace SourceGit.ViewModels
|
|||
var unstage = new MenuItem();
|
||||
unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count);
|
||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||
unstage.Click += (o, e) =>
|
||||
unstage.Click += (_, e) =>
|
||||
{
|
||||
UnstageChanges(_selectedStaged);
|
||||
e.Handled = true;
|
||||
|
@ -1140,7 +1144,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var item = new MenuItem();
|
||||
item.Header = dump;
|
||||
item.Click += (o, e) =>
|
||||
item.Click += (_, e) =>
|
||||
{
|
||||
CommitMessage = dump;
|
||||
e.Handled = true;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
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:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.About"
|
||||
|
@ -30,7 +29,7 @@
|
|||
<Button Classes="caption_button_macos" Click="CloseWindow">
|
||||
<Grid>
|
||||
<Ellipse Fill="{DynamicResource Brush.MacOS.Close}"/>
|
||||
<Path Height="6" Width="6" Stretch="Fill" Fill="#404040" Stroke="#404040" StrokeThickness="1" Data="{StaticResource Icons.Window.Close}"/>
|
||||
<Path Height="6" Width="6" Stretch="Fill" Fill="#505050" Data="{StaticResource Icons.MacOS.Close}"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
@ -16,40 +16,42 @@ namespace SourceGit.Views
|
|||
public About()
|
||||
{
|
||||
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
if (ver != null)
|
||||
Version = $"{ver.Major}.{ver.Minor}";
|
||||
|
||||
DataContext = this;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void BeginMoveWindow(object sender, PointerPressedEventArgs e)
|
||||
private void BeginMoveWindow(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
BeginMoveDrag(e);
|
||||
}
|
||||
|
||||
private void CloseWindow(object sender, RoutedEventArgs e)
|
||||
private void CloseWindow(object _1, RoutedEventArgs _2)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnVisitAvaloniaUI(object sender, PointerPressedEventArgs e)
|
||||
private void OnVisitAvaloniaUI(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
Native.OS.OpenBrowser("https://www.avaloniaui.net/");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnVisitAvaloniaEdit(object sender, PointerPressedEventArgs e)
|
||||
private void OnVisitAvaloniaEdit(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
Native.OS.OpenBrowser("https://github.com/AvaloniaUI/AvaloniaEdit");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnVisitJetBrainsMonoFont(object sender, PointerPressedEventArgs e)
|
||||
private void OnVisitJetBrainsMonoFont(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
Native.OS.OpenBrowser("https://www.jetbrains.com/lp/mono/");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnVisitSourceCode(object sender, PointerPressedEventArgs e)
|
||||
private void OnVisitSourceCode(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
Native.OS.OpenBrowser("https://github.com/sourcegit-scm/sourcegit");
|
||||
e.Handled = true;
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
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:v="using:SourceGit.Views"
|
||||
xmlns:c="using:SourceGit.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.AddRemote"
|
||||
x:DataType="vm:AddRemote">
|
||||
|
@ -44,7 +42,7 @@
|
|||
Text="{DynamicResource Text.SSHKey}"
|
||||
IsVisible="{Binding UseSSH}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="txtSSHKey"
|
||||
x:Name="TxtSshKey"
|
||||
Height="28"
|
||||
CornerRadius="3"
|
||||
IsVisible="{Binding UseSSH}"
|
||||
|
|
|
@ -11,15 +11,21 @@ namespace SourceGit.Views
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void SelectSSHKey(object sender, RoutedEventArgs e)
|
||||
private async void SelectSSHKey(object _, RoutedEventArgs e)
|
||||
{
|
||||
var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }] };
|
||||
var toplevel = TopLevel.GetTopLevel(this);
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var options = new FilePickerOpenOptions()
|
||||
{
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }]
|
||||
};
|
||||
|
||||
var selected = await toplevel.StorageProvider.OpenFilePickerAsync(options);
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
txtSSHKey.Text = selected[0].Path.LocalPath;
|
||||
}
|
||||
TxtSshKey.Text = selected[0].Path.LocalPath;
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
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:v="using:SourceGit.Views"
|
||||
xmlns:c="using:SourceGit.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.AddSubmodule"
|
||||
x:DataType="vm:AddSubmodule">
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
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:c="using:SourceGit.Converters"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.AddWorktree"
|
||||
|
|
|
@ -11,13 +11,13 @@ namespace SourceGit.Views
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void SelectLocation(object sender, RoutedEventArgs e)
|
||||
private async void SelectLocation(object _, RoutedEventArgs e)
|
||||
{
|
||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||
var toplevel = TopLevel.GetTopLevel(this);
|
||||
if (toplevel == null)
|
||||
return;
|
||||
|
||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||
var selected = await toplevel.StorageProvider.OpenFolderPickerAsync(options);
|
||||
if (selected.Count == 1)
|
||||
TxtLocation.Text = selected[0].Path.LocalPath;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue