diff --git a/README.md b/README.md
index 8ebbbb98..6a688d99 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,17 @@
# SourceGit - Opensource Git GUI client.
-![stars](https://img.shields.io/github/stars/sourcegit-scm/sourcegit.svg) ![forks](https://img.shields.io/github/forks/sourcegit-scm/sourcegit.svg) ![license](https://img.shields.io/github/license/sourcegit-scm/sourcegit.svg) ![latest](https://img.shields.io/github/v/release/sourcegit-scm/sourcegit.svg) ![downloads](https://img.shields.io/github/downloads/sourcegit-scm/sourcegit/total)
+[![stars](https://img.shields.io/github/stars/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/stargazers)
+[![forks](https://img.shields.io/github/forks/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/forks)
+[![license](https://img.shields.io/github/license/sourcegit-scm/sourcegit.svg)](LICENSE)
+[![latest](https://img.shields.io/github/v/release/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/releases/latest)
+[![downloads](https://img.shields.io/github/downloads/sourcegit-scm/sourcegit/total)](https://github.com/sourcegit-scm/sourcegit/releases)
## Highlights
* Supports Windows/macOS/Linux
* Opensource/Free
* Fast
-* English/Français/Deutsch/Português/Русский/简体中文/繁體中文
+* Deutsch/English/Español/Français/Português/Русский/简体中文/繁體中文
* Built-in light/dark themes
* Customize theme
* Visual commit graph
@@ -43,7 +47,7 @@
## Translation Status
-[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.95%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-90.36%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-93.52%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-99.10%25-yellow)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
+[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-96.05%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-97.08%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-87.72%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-90.79%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
## How to Use
@@ -80,11 +84,16 @@ For **Windows** users:
For **macOS** users:
-* Download `sourcegit_x.y.osx-x64.zip` or `sourcegit_x.y.osx-arm64.zip` from Releases. `x64` for Intel and `arm64` for Apple Silicon.
-* Move `SourceGit.app` to `Applications` folder.
-* Make sure your mac trusts all software from anywhere. For more information, search `spctl --master-disable`.
+* Thanks [@ybeapps](https://github.com/ybeapps) for making `SourceGit` available on `Homebrew`. You can simply install it with following command:
+ ```shell
+ brew tap ybeapps/homebrew-sourcegit
+ brew install --cask --no-quarantine sourcegit
+ ```
+* If you want to install `SourceGit.app` from Github Release manually, you need run following command to make sure it works:
+ ```shell
+ sudo xattr -cr /Applications/SourceGit.app
+ ```
* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your mac.
-* You may need to run `sudo xattr -cr /Applications/SourceGit.app` to make sure the software works.
* You can run `echo $PATH > ~/Library/Application\ Support/SourceGit/PATH` to generate a custom PATH env file to introduce `PATH` env to SourceGit.
For **Linux** users:
@@ -110,14 +119,15 @@ For other AI service:
This app supports open repository in external tools listed in the table below.
-| Tool | Windows | macOS | Linux | KEY IN `external_editors.json` |
-|-------------------------------|---------|-------|-------|--------------------------------|
-| Visual Studio Code | YES | YES | YES | VSCODE |
-| Visual Studio Code - Insiders | YES | YES | YES | VSCODE_INSIDERS |
-| VSCodium | YES | YES | YES | VSCODIUM |
-| JetBrains Fleet | YES | YES | YES | FLEET |
-| Sublime Text | YES | YES | YES | SUBLIME_TEXT |
-| Zed | NO | YES | YES | ZED |
+| Tool | Windows | macOS | Linux |
+|-------------------------------|---------|-------|-------|
+| Visual Studio Code | YES | YES | YES |
+| Visual Studio Code - Insiders | YES | YES | YES |
+| VSCodium | YES | YES | YES |
+| Fleet | YES | YES | YES |
+| Sublime Text | YES | YES | YES |
+| Zed | NO | YES | YES |
+| Visual Studio | YES | NO | NO |
> [!NOTE]
> This app will try to find those tools based on some pre-defined or expected locations automatically. If you are using one portable version of these tools, it will not be detected by this app.
@@ -125,7 +135,7 @@ This app supports open repository in external tools listed in the table below.
```json
{
"tools": {
- "VSCODE": "D:\\VSCode\\Code.exe"
+ "Visual Studio Code": "D:\\VSCode\\Code.exe"
}
}
```
@@ -153,4 +163,4 @@ Everyone is welcome to submit a PR. Please make sure your PR is based on the lat
Thanks to all the people who contribute.
-[![Contributors](https://contrib.rocks/image?repo=sourcegit-scm/sourcegit&columns=10)](https://github.com/sourcegit-scm/sourcegit/graphs/contributors)
+[![Contributors](https://contrib.rocks/image?repo=sourcegit-scm/sourcegit&columns=20)](https://github.com/sourcegit-scm/sourcegit/graphs/contributors)
diff --git a/SourceGit.sln b/SourceGit.sln
index abd42aee..9799a09e 100644
--- a/SourceGit.sln
+++ b/SourceGit.sln
@@ -76,6 +76,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "appimage", "appimage", "{5D
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{C54D4001-9940-477C-A0B6-E795ED0A3209}"
ProjectSection(SolutionItems) = preProject
+ build\scripts\localization-check.js = build\scripts\localization-check.js
build\scripts\package.linux.sh = build\scripts\package.linux.sh
build\scripts\package.osx-app.sh = build\scripts\package.osx-app.sh
build\scripts\package.windows-portable.sh = build\scripts\package.windows-portable.sh
diff --git a/TRANSLATION.md b/TRANSLATION.md
index ed06afef..b6c255a1 100644
--- a/TRANSLATION.md
+++ b/TRANSLATION.md
@@ -1,20 +1,69 @@
-### de_DE.axaml: 98.95%
+### de_DE.axaml: 96.05%
Missing Keys
+- Text.BranchCM.FetchInto
+- Text.ChangeCM.GenerateCommitMessage
+- Text.CommitCM.CustomAction
+- Text.Configure.CustomAction
+- Text.Configure.CustomAction.Arguments
+- Text.Configure.CustomAction.Arguments.Tip
+- Text.Configure.CustomAction.Executable
+- Text.Configure.CustomAction.Name
+- Text.Configure.CustomAction.Scope
+- Text.Configure.CustomAction.Scope.Commit
+- Text.Configure.CustomAction.Scope.Repository
+- Text.Configure.Git.EnablePruneOnFetch
- Text.Configure.Git.EnableSignOff
- Text.Configure.IssueTracker.AddSampleGitLabIssue
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
-- Text.Preference.Advanced
+- Text.Configure.OpenAI
+- Text.Configure.OpenAI.Prefered
+- Text.Configure.OpenAI.Prefered.Tip
+- Text.ExecuteCustomAction
+- Text.ExecuteCustomAction.Name
- Text.Preference.AI.AnalyzeDiffPrompt
- Text.Preference.AI.GenerateSubjectPrompt
+- Text.Preference.AI.Name
+- Text.Repository.CustomActions
+- Text.Repository.CustomActions.Empty
+- Text.Stash.KeepIndex
- Text.WorkingCopy.ConfirmCommitWithoutFiles
-### fr_FR.axaml: 90.36%
+### es_ES.axaml: 97.08%
+
+
+
+Missing Keys
+
+- Text.ChangeCM.GenerateCommitMessage
+- Text.CommitCM.CustomAction
+- Text.Configure.CustomAction
+- Text.Configure.CustomAction.Arguments
+- Text.Configure.CustomAction.Arguments.Tip
+- Text.Configure.CustomAction.Executable
+- Text.Configure.CustomAction.Name
+- Text.Configure.CustomAction.Scope
+- Text.Configure.CustomAction.Scope.Commit
+- Text.Configure.CustomAction.Scope.Repository
+- Text.Configure.Git.EnablePruneOnFetch
+- Text.Configure.OpenAI
+- Text.Configure.OpenAI.Prefered
+- Text.Configure.OpenAI.Prefered.Tip
+- Text.ExecuteCustomAction
+- Text.ExecuteCustomAction.Name
+- Text.Preference.AI.Name
+- Text.Repository.CustomActions
+- Text.Repository.CustomActions.Empty
+- Text.Stash.KeepIndex
+
+
+
+### fr_FR.axaml: 87.72%
@@ -23,16 +72,31 @@
- Text.About.Chart
- Text.AIAssistant
- Text.AIAssistant.Tip
+- Text.BranchCM.FetchInto
+- Text.ChangeCM.GenerateCommitMessage
- Text.CherryPick.AppendSourceToMessage
- Text.CherryPick.Mainline
- Text.CherryPick.Mainline.Tips
- Text.CommitCM.CherryPickMultiple
+- Text.CommitCM.CustomAction
- Text.CommitCM.SquashCommitsSinceThis
- Text.CommitDetail.Info.WebLinks
+- Text.Configure.CustomAction
+- Text.Configure.CustomAction.Arguments
+- Text.Configure.CustomAction.Arguments.Tip
+- Text.Configure.CustomAction.Executable
+- Text.Configure.CustomAction.Name
+- Text.Configure.CustomAction.Scope
+- Text.Configure.CustomAction.Scope.Commit
+- Text.Configure.CustomAction.Scope.Repository
- Text.Configure.Git.DefaultRemote
+- Text.Configure.Git.EnablePruneOnFetch
- Text.Configure.Git.EnableSignOff
- Text.Configure.IssueTracker.AddSampleGitLabIssue
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.Configure.OpenAI
+- Text.Configure.OpenAI.Prefered
+- Text.Configure.OpenAI.Prefered.Tip
- Text.ConfigureWorkspace
- Text.ConfigureWorkspace.Color
- Text.ConfigureWorkspace.Restore
@@ -45,6 +109,8 @@
- Text.ConventionalCommit.Type
- Text.Diff.IgnoreWhitespace
- Text.Discard.IncludeIgnored
+- Text.ExecuteCustomAction
+- Text.ExecuteCustomAction.Name
- Text.FileHistory.FileChange
- Text.GitLFS.Locks.OnlyMine
- Text.Histories.Header.AuthorTime
@@ -55,12 +121,12 @@
- Text.Hotkeys.Repo.DiscardSelected
- Text.MoveRepositoryNode
- Text.MoveRepositoryNode.Target
-- Text.Preference.Advanced
- Text.Preference.AI
- Text.Preference.AI.AnalyzeDiffPrompt
- Text.Preference.AI.ApiKey
- Text.Preference.AI.GenerateSubjectPrompt
- Text.Preference.AI.Model
+- Text.Preference.AI.Name
- Text.Preference.AI.Server
- Text.Preference.General.ShowAuthorTime
- Text.Preference.Integration
@@ -68,11 +134,14 @@
- Text.Preference.Shell.Type
- Text.Preference.Shell.Path
- Text.Repository.AutoFetching
+- Text.Repository.CustomActions
+- Text.Repository.CustomActions.Empty
- Text.Repository.EnableReflog
- Text.Repository.Search.InCurrentBranch
- Text.ScanRepositories
- Text.ScanRepositories.RootDir
- Text.Squash.Into
+- Text.Stash.KeepIndex
- Text.Stash.OnlyStagedChanges
- Text.Stash.TipForSelectedFiles
- Text.Statistics.Overview
@@ -87,7 +156,7 @@
-### pt_BR.axaml: 93.52%
+### pt_BR.axaml: 90.79%
@@ -96,18 +165,33 @@
- Text.About.Chart
- Text.AIAssistant
- Text.AIAssistant.Tip
+- Text.BranchCM.FetchInto
+- Text.ChangeCM.GenerateCommitMessage
- Text.CherryPick.AppendSourceToMessage
- Text.CherryPick.Mainline
- Text.CherryPick.Mainline.Tips
- Text.CommitCM.CherryPickMultiple
+- Text.CommitCM.CustomAction
- Text.CommitCM.SquashCommitsSinceThis
- Text.CommitDetail.Info.ContainsIn
- Text.CommitDetail.Info.ContainsIn.Title
- Text.CommitDetail.Info.WebLinks
+- Text.Configure.CustomAction
+- Text.Configure.CustomAction.Arguments
+- Text.Configure.CustomAction.Arguments.Tip
+- Text.Configure.CustomAction.Executable
+- Text.Configure.CustomAction.Name
+- Text.Configure.CustomAction.Scope
+- Text.Configure.CustomAction.Scope.Commit
+- Text.Configure.CustomAction.Scope.Repository
- Text.Configure.Git.DefaultRemote
+- Text.Configure.Git.EnablePruneOnFetch
- Text.Configure.Git.EnableSignOff
- Text.Configure.IssueTracker.AddSampleGitLabIssue
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.Configure.OpenAI
+- Text.Configure.OpenAI.Prefered
+- Text.Configure.OpenAI.Prefered.Tip
- Text.ConfigureWorkspace
- Text.ConfigureWorkspace.Color
- Text.ConfigureWorkspace.Restore
@@ -120,14 +204,19 @@
- Text.ConventionalCommit.Type
- Text.CopyAllText
- Text.Discard.IncludeIgnored
+- Text.ExecuteCustomAction
+- Text.ExecuteCustomAction.Name
- Text.FileHistory.FileContent
- Text.FileHistory.FileChange
- Text.GitLFS.Locks.OnlyMine
- Text.MoveRepositoryNode
- Text.MoveRepositoryNode.Target
-- Text.Preference.Advanced
+- Text.Preference.AI.Name
- Text.Push.CheckSubmodules
+- Text.Repository.CustomActions
+- Text.Repository.CustomActions.Empty
- Text.Squash.Into
+- Text.Stash.KeepIndex
- Text.Stash.OnlyStagedChanges
- Text.Stash.TipForSelectedFiles
- Text.Statistics.Overview
@@ -149,18 +238,13 @@
-### zh_CN.axaml: 99.10%
+### zh_CN.axaml: 100.00%
Missing Keys
-- Text.Preference.AI
-- Text.Preference.AI.AnalyzeDiffPrompt
-- Text.Preference.AI.ApiKey
-- Text.Preference.AI.GenerateSubjectPrompt
-- Text.Preference.AI.Model
-- Text.Preference.AI.Server
+
diff --git a/VERSION b/VERSION
index f42a1bc8..a5e93156 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.36
\ No newline at end of file
+8.37
\ No newline at end of file
diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec
index 289cbe39..9dda5f96 100644
--- a/build/resources/rpm/SPECS/build.spec
+++ b/build/resources/rpm/SPECS/build.spec
@@ -5,8 +5,8 @@ Summary: Open-source & Free Git Gui Client
License: MIT
URL: https://sourcegit-scm.github.io/
Source: https://github.com/sourcegit-scm/sourcegit/archive/refs/tags/v%_version.tar.gz
-Requires: libX11
-Requires: libSM
+Requires: (libX11 or libX11-6)
+Requires: (libSM or libSM6)
%define _build_id_links none
diff --git a/src/App.axaml b/src/App.axaml
index b1fe303b..fc55776e 100644
--- a/src/App.axaml
+++ b/src/App.axaml
@@ -18,6 +18,7 @@
+
diff --git a/src/App.axaml.cs b/src/App.axaml.cs
index 682ec5fc..dfec763b 100644
--- a/src/App.axaml.cs
+++ b/src/App.axaml.cs
@@ -59,6 +59,10 @@ namespace SourceGit
builder.UsePlatformDetect();
builder.LogToTrace();
builder.WithInterFont();
+ builder.With(new FontManagerOptions()
+ {
+ DefaultFamilyName = "fonts:Inter#Inter"
+ });
builder.ConfigureFonts(manager =>
{
var monospace = new EmbeddedFontCollection(
@@ -223,7 +227,7 @@ namespace SourceGit
if (onlyUseMonospaceFontInEditor)
{
if (string.IsNullOrEmpty(defaultFont))
- resDic.Add("Fonts.Primary", new FontFamily("fonts:Inter#Inter, $Default"));
+ resDic.Add("Fonts.Primary", new FontFamily("fonts:Inter#Inter"));
else
resDic.Add("Fonts.Primary", new FontFamily(defaultFont));
}
diff --git a/src/Commands/ExecuteCustomAction.cs b/src/Commands/ExecuteCustomAction.cs
new file mode 100644
index 00000000..253c6b43
--- /dev/null
+++ b/src/Commands/ExecuteCustomAction.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Diagnostics;
+using System.Text;
+
+using Avalonia.Threading;
+
+namespace SourceGit.Commands
+{
+ public static class ExecuteCustomAction
+ {
+ public static void Run(string repo, string file, string args, Action outputHandler)
+ {
+ var start = new ProcessStartInfo();
+ start.FileName = file;
+ start.Arguments = args;
+ start.UseShellExecute = false;
+ start.CreateNoWindow = true;
+ start.RedirectStandardOutput = true;
+ start.RedirectStandardError = true;
+ start.StandardOutputEncoding = Encoding.UTF8;
+ start.StandardErrorEncoding = Encoding.UTF8;
+ start.WorkingDirectory = repo;
+
+ // Force using en_US.UTF-8 locale to avoid GCM crash
+ if (OperatingSystem.IsLinux())
+ start.Environment.Add("LANG", "en_US.UTF-8");
+
+ // Fix macOS `PATH` env
+ if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
+ start.Environment.Add("PATH", Native.OS.CustomPathEnv);
+
+ var proc = new Process() { StartInfo = start };
+ proc.OutputDataReceived += (_, e) =>
+ {
+ if (e.Data != null)
+ outputHandler?.Invoke(e.Data);
+ };
+
+ proc.ErrorDataReceived += (_, e) =>
+ {
+ if (e.Data != null)
+ outputHandler?.Invoke(e.Data);
+ };
+
+ try
+ {
+ proc.Start();
+ proc.BeginOutputReadLine();
+ proc.BeginErrorReadLine();
+ proc.WaitForExit();
+ }
+ catch (Exception e)
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ App.RaiseException(repo, e.Message);
+ });
+ }
+
+ proc.Close();
+ }
+ }
+}
diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs
index ea4e54bf..08d2d1c6 100644
--- a/src/Commands/Fetch.cs
+++ b/src/Commands/Fetch.cs
@@ -4,7 +4,7 @@ namespace SourceGit.Commands
{
public class Fetch : Command
{
- public Fetch(string repo, string remote, bool noTags, Action outputHandler)
+ public Fetch(string repo, string remote, bool noTags, bool prune, Action outputHandler)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
@@ -18,9 +18,22 @@ namespace SourceGit.Commands
else
Args += "--force ";
+ if (prune)
+ Args += "--prune ";
+
Args += remote;
}
+ public Fetch(string repo, Models.Branch local, Models.Branch remote, Action outputHandler)
+ {
+ _outputHandler = outputHandler;
+ WorkingDirectory = repo;
+ Context = repo;
+ TraitErrorAsOutput = true;
+ SSHKey = new Config(repo).Get($"remote.{remote.Remote}.sshkey");
+ Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}";
+ }
+
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
diff --git a/src/Commands/GenerateCommitMessage.cs b/src/Commands/GenerateCommitMessage.cs
index 3bcf7010..e4f25f38 100644
--- a/src/Commands/GenerateCommitMessage.cs
+++ b/src/Commands/GenerateCommitMessage.cs
@@ -20,8 +20,9 @@ namespace SourceGit.Commands
}
}
- public GenerateCommitMessage(string repo, List changes, CancellationToken cancelToken, Action onProgress)
+ public GenerateCommitMessage(Models.OpenAIService service, string repo, List changes, CancellationToken cancelToken, Action onProgress)
{
+ _service = service;
_repo = repo;
_changes = changes;
_cancelToken = cancelToken;
@@ -75,7 +76,7 @@ namespace SourceGit.Commands
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
var diff = rs.IsSuccess ? rs.StdOut : "unknown change";
- var rsp = Models.OpenAI.Chat(Models.OpenAI.AnalyzeDiffPrompt, $"Here is the `git diff` output: {diff}", _cancelToken);
+ var rsp = _service.Chat(_service.AnalyzeDiffPrompt, $"Here is the `git diff` output: {diff}", _cancelToken);
if (rsp != null && rsp.Choices.Count > 0)
return rsp.Choices[0].Message.Content;
@@ -84,13 +85,14 @@ namespace SourceGit.Commands
private string GenerateSubject(string summary)
{
- var rsp = Models.OpenAI.Chat(Models.OpenAI.GenerateSubjectPrompt, $"Here are the summaries changes:\n{summary}", _cancelToken);
+ var rsp = _service.Chat(_service.GenerateSubjectPrompt, $"Here are the summaries changes:\n{summary}", _cancelToken);
if (rsp != null && rsp.Choices.Count > 0)
return rsp.Choices[0].Message.Content;
return string.Empty;
}
+ private Models.OpenAIService _service;
private string _repo;
private List _changes;
private CancellationToken _cancelToken;
diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs
index a4efa4b6..732530f5 100644
--- a/src/Commands/Pull.cs
+++ b/src/Commands/Pull.cs
@@ -4,7 +4,7 @@ namespace SourceGit.Commands
{
public class Pull : Command
{
- public Pull(string repo, string remote, string branch, bool useRebase, bool noTags, Action outputHandler)
+ public Pull(string repo, string remote, string branch, bool useRebase, bool noTags, bool prune, Action outputHandler)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
@@ -17,6 +17,8 @@ namespace SourceGit.Commands
Args += "--rebase ";
if (noTags)
Args += "--no-tags ";
+ if (prune)
+ Args += "--prune ";
Args += $"{remote} {branch}";
}
diff --git a/src/Commands/QueryCommitSignInfo.cs b/src/Commands/QueryCommitSignInfo.cs
new file mode 100644
index 00000000..5c81cf57
--- /dev/null
+++ b/src/Commands/QueryCommitSignInfo.cs
@@ -0,0 +1,35 @@
+namespace SourceGit.Commands
+{
+ public class QueryCommitSignInfo : Command
+ {
+ public QueryCommitSignInfo(string repo, string sha, bool useFakeSignersFile)
+ {
+ WorkingDirectory = repo;
+ Context = repo;
+
+ const string baseArgs = "show --no-show-signature --pretty=format:\"%G?%n%GS%n%GK\" -s";
+ const string fakeSignersFileArg = "-c gpg.ssh.allowedSignersFile=/dev/null";
+ Args = $"{(useFakeSignersFile ? fakeSignersFileArg : string.Empty)} {baseArgs} {sha}";
+ }
+
+ public Models.CommitSignInfo Result()
+ {
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
+ return null;
+
+ var raw = rs.StdOut.Trim();
+ if (raw.Length <= 1)
+ return null;
+
+ var lines = raw.Split('\n');
+ return new Models.CommitSignInfo()
+ {
+ VerifyResult = lines[0][0],
+ Signer = lines[1],
+ Key = lines[2]
+ };
+
+ }
+ }
+}
diff --git a/src/Commands/QueryFileSize.cs b/src/Commands/QueryFileSize.cs
index c36984dd..9016d826 100644
--- a/src/Commands/QueryFileSize.cs
+++ b/src/Commands/QueryFileSize.cs
@@ -11,7 +11,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"ls-tree {revision} -l -- {file}";
+ Args = $"ls-tree {revision} -l -- \"{file}\"";
}
public long Result()
diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs
index fbdb6bd4..77f1af53 100644
--- a/src/Commands/Stash.cs
+++ b/src/Commands/Stash.cs
@@ -17,24 +17,29 @@ namespace SourceGit.Commands
return Exec();
}
- public bool Push(List changes, string message, bool onlyStaged)
+ public bool Push(List changes, string message, bool onlyStaged, bool keepIndex)
{
- var pathsBuilder = new StringBuilder();
+ var builder = new StringBuilder();
+ builder.Append("stash push ");
+ if (onlyStaged)
+ builder.Append("--staged ");
+ if (keepIndex)
+ builder.Append("--keep-index ");
+ builder.Append("-m \"");
+ builder.Append(message);
+ builder.Append("\" -- ");
if (onlyStaged)
{
foreach (var c in changes)
- pathsBuilder.Append($"\"{c.Path}\" ");
-
- var paths = pathsBuilder.ToString();
- Args = $"stash push --staged -m \"{message}\" -- {paths}";
+ builder.Append($"\"{c.Path}\" ");
}
else
{
var needAdd = new List();
foreach (var c in changes)
{
- pathsBuilder.Append($"\"{c.Path}\" ");
+ builder.Append($"\"{c.Path}\" ");
if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked)
{
@@ -51,11 +56,9 @@ namespace SourceGit.Commands
new Add(WorkingDirectory, needAdd).Exec();
needAdd.Clear();
}
-
- var paths = pathsBuilder.ToString();
- Args = $"stash push -m \"{message}\" -- {paths}";
}
+ Args = builder.ToString();
return Exec();
}
diff --git a/src/Models/CommitSignInfo.cs b/src/Models/CommitSignInfo.cs
new file mode 100644
index 00000000..44b95e61
--- /dev/null
+++ b/src/Models/CommitSignInfo.cs
@@ -0,0 +1,60 @@
+using Avalonia.Media;
+
+namespace SourceGit.Models
+{
+ public class CommitSignInfo
+ {
+ public char VerifyResult { get; init; } = 'N';
+ public string Signer { get; init; } = string.Empty;
+ public string Key { get; init; } = string.Empty;
+ public bool HasSigner => !string.IsNullOrEmpty(Signer);
+
+ public IBrush Brush
+ {
+ get
+ {
+ switch (VerifyResult)
+ {
+ case 'G':
+ case 'U':
+ return Brushes.Green;
+ case 'X':
+ case 'Y':
+ case 'R':
+ return Brushes.DarkOrange;
+ case 'B':
+ case 'E':
+ return Brushes.Red;
+ default:
+ return Brushes.Transparent;
+ }
+ }
+ }
+
+ public string ToolTip
+ {
+ get
+ {
+ switch (VerifyResult)
+ {
+ case 'G':
+ return "Good signature.";
+ case 'U':
+ return "Good signature with unknown validity.";
+ case 'X':
+ return "Good signature but has expired.";
+ case 'Y':
+ return "Good signature made by expired key.";
+ case 'R':
+ return "Good signature made by a revoked key.";
+ case 'B':
+ return "Bad signature.";
+ case 'E':
+ return "Signature cannot be checked.";
+ default:
+ return "No signature.";
+ }
+ }
+ }
+ }
+}
diff --git a/src/Models/CustomAction.cs b/src/Models/CustomAction.cs
new file mode 100644
index 00000000..2a400b02
--- /dev/null
+++ b/src/Models/CustomAction.cs
@@ -0,0 +1,42 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace SourceGit.Models
+{
+ public enum CustomActionScope
+ {
+ Repository,
+ Commit,
+ }
+
+ public class CustomAction : ObservableObject
+ {
+ public string Name
+ {
+ get => _name;
+ set => SetProperty(ref _name, value);
+ }
+
+ public CustomActionScope Scope
+ {
+ get => _scope;
+ set => SetProperty(ref _scope, value);
+ }
+
+ public string Executable
+ {
+ get => _executable;
+ set => SetProperty(ref _executable, value);
+ }
+
+ public string Arguments
+ {
+ get => _arguments;
+ set => SetProperty(ref _arguments, value);
+ }
+
+ private string _name = string.Empty;
+ private CustomActionScope _scope = CustomActionScope.Repository;
+ private string _executable = string.Empty;
+ private string _arguments = string.Empty;
+ }
+}
diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs
index b26a9a90..103e91bc 100644
--- a/src/Models/ExternalTool.cs
+++ b/src/Models/ExternalTool.cs
@@ -13,15 +13,13 @@ namespace SourceGit.Models
public class ExternalTool
{
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)
+ public ExternalTool(string name, string icon, string execFile, Func execArgsGenerator = null)
{
Name = name;
- Executable = executable;
- OpenCmdArgs = openCmdArgs;
+ _execFile = execFile;
+ _execArgsGenerator = execArgsGenerator ?? (repo => $"\"{repo}\"");
try
{
@@ -40,11 +38,14 @@ namespace SourceGit.Models
Process.Start(new ProcessStartInfo()
{
WorkingDirectory = repo,
- FileName = Executable,
- Arguments = string.Format(OpenCmdArgs, repo),
+ FileName = _execFile,
+ Arguments = _execArgsGenerator.Invoke(repo),
UseShellExecute = false,
});
}
+
+ private string _execFile = string.Empty;
+ private Func _execArgsGenerator = null;
}
public class JetBrainsState
@@ -110,48 +111,48 @@ namespace SourceGit.Models
_customPaths = new ExternalToolPaths();
}
- public void TryAdd(string name, string icon, string args, string key, Func finder)
+ public void TryAdd(string name, string icon, Func finder, Func execArgsGenerator = null)
{
- if (_customPaths.Tools.TryGetValue(key, out var customPath) && File.Exists(customPath))
+ if (_customPaths.Tools.TryGetValue(name, out var customPath) && File.Exists(customPath))
{
- Founded.Add(new ExternalTool(name, icon, customPath, args));
+ Founded.Add(new ExternalTool(name, icon, customPath, execArgsGenerator));
}
else
{
var path = finder();
if (!string.IsNullOrEmpty(path) && File.Exists(path))
- Founded.Add(new ExternalTool(name, icon, path, args));
+ Founded.Add(new ExternalTool(name, icon, path, execArgsGenerator));
}
}
public void VSCode(Func platformFinder)
{
- TryAdd("Visual Studio Code", "vscode", "\"{0}\"", "VSCODE", platformFinder);
+ TryAdd("Visual Studio Code", "vscode", platformFinder);
}
public void VSCodeInsiders(Func platformFinder)
{
- TryAdd("Visual Studio Code - Insiders", "vscode_insiders", "\"{0}\"", "VSCODE_INSIDERS", platformFinder);
+ TryAdd("Visual Studio Code - Insiders", "vscode_insiders", platformFinder);
}
public void VSCodium(Func platformFinder)
{
- TryAdd("VSCodium", "codium", "\"{0}\"", "VSCODIUM", platformFinder);
+ TryAdd("VSCodium", "codium", platformFinder);
}
public void Fleet(Func platformFinder)
{
- TryAdd("Fleet", "fleet", "\"{0}\"", "FLEET", platformFinder);
+ TryAdd("Fleet", "fleet", platformFinder);
}
public void SublimeText(Func platformFinder)
{
- TryAdd("Sublime Text", "sublime_text", "\"{0}\"", "SUBLIME_TEXT", platformFinder);
+ TryAdd("Sublime Text", "sublime_text", platformFinder);
}
public void Zed(Func platformFinder)
{
- TryAdd("Zed", "zed", "\"{0}\"", "ZED", platformFinder);
+ TryAdd("Zed", "zed", platformFinder);
}
public void FindJetBrainsFromToolbox(Func platformFinder)
@@ -170,8 +171,7 @@ namespace SourceGit.Models
Founded.Add(new ExternalTool(
$"{tool.DisplayName} {tool.DisplayVersion}",
supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : "JetBrains/JB",
- Path.Combine(tool.InstallLocation, tool.LaunchCommand),
- "\"{0}\""));
+ Path.Combine(tool.InstallLocation, tool.LaunchCommand)));
}
}
}
diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs
index 0d9e5f69..9d24f491 100644
--- a/src/Models/Locales.cs
+++ b/src/Models/Locales.cs
@@ -8,8 +8,9 @@ namespace SourceGit.Models
public string Key { get; set; }
public static readonly List Supported = new List() {
- new Locale("English", "en_US"),
new Locale("Deutsch", "de_DE"),
+ new Locale("English", "en_US"),
+ new Locale("Español", "es_ES"),
new Locale("Français", "fr_FR"),
new Locale("Português (Brasil)", "pt_BR"),
new Locale("Русский", "ru_RU"),
diff --git a/src/Models/NumericSort.cs b/src/Models/NumericSort.cs
index bdff95e6..ed5002e6 100644
--- a/src/Models/NumericSort.cs
+++ b/src/Models/NumericSort.cs
@@ -21,6 +21,10 @@
int loc2 = 0;
bool isDigit1 = char.IsDigit(c1);
+ bool isDigit2 = char.IsDigit(c2);
+ if (isDigit1 != isDigit2)
+ return c1.CompareTo(c2);
+
do
{
tmp1[loc1] = c1;
@@ -33,7 +37,6 @@
break;
} while (char.IsDigit(c1) == isDigit1);
- bool isDigit2 = char.IsDigit(c2);
do
{
tmp2[loc2] = c2;
@@ -49,16 +52,10 @@
string sub1 = new string(tmp1, 0, loc1);
string sub2 = new string(tmp2, 0, loc2);
int result;
- if (isDigit1 && isDigit2)
- {
- int num1 = int.Parse(sub1);
- int num2 = int.Parse(sub2);
- result = num1 - num2;
- }
+ if (isDigit1)
+ result = loc1 == loc2 ? string.CompareOrdinal(sub1, sub2) : loc1 - loc2;
else
- {
- result = string.Compare(sub1, sub2, System.StringComparison.Ordinal);
- }
+ result = string.CompareOrdinal(sub1, sub2);
if (result != 0)
return result;
diff --git a/src/Models/OpenAI.cs b/src/Models/OpenAI.cs
index b1bb9465..e9c7b5ed 100644
--- a/src/Models/OpenAI.cs
+++ b/src/Models/OpenAI.cs
@@ -6,6 +6,8 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
+using CommunityToolkit.Mvvm.ComponentModel;
+
namespace SourceGit.Models
{
public class OpenAIChatMessage
@@ -74,44 +76,78 @@ namespace SourceGit.Models
}
}
- public static class OpenAI
+ public class OpenAIService : ObservableObject
{
- public static string Server
+ public string Name
{
- get;
- set;
+ get => _name;
+ set => SetProperty(ref _name, value);
}
- public static string ApiKey
+ public string Server
{
- get;
- set;
+ get => _server;
+ set => SetProperty(ref _server, value);
}
- public static string Model
+ public string ApiKey
{
- get;
- set;
+ get => _apiKey;
+ set => SetProperty(ref _apiKey, value);
}
- public static string AnalyzeDiffPrompt
+ public string Model
{
- get;
- set;
+ get => _model;
+ set => SetProperty(ref _model, value);
}
- public static string GenerateSubjectPrompt
+ public string AnalyzeDiffPrompt
{
- get;
- set;
+ get => _analyzeDiffPrompt;
+ set => SetProperty(ref _analyzeDiffPrompt, value);
}
- public static bool IsValid
+ public string GenerateSubjectPrompt
{
- get => !string.IsNullOrEmpty(Server) && !string.IsNullOrEmpty(Model);
+ get => _generateSubjectPrompt;
+ set => SetProperty(ref _generateSubjectPrompt, value);
}
- public static OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
+ public OpenAIService()
+ {
+ AnalyzeDiffPrompt = """
+ You are an expert developer specialist in creating commits.
+ Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
+ - Do not use any code snippets, imports, file routes or bullets points.
+ - Do not mention the route of file that has been change.
+ - Write clear, concise, and descriptive messages that explain the MAIN GOAL made of the changes.
+ - Use the present tense and active voice in the message, for example, "Fix bug" instead of "Fixed bug.".
+ - Use the imperative mood, which gives the message a sense of command, e.g. "Add feature" instead of "Added feature".
+ - Avoid using general terms like "update" or "change", be specific about what was updated or changed.
+ - Avoid using terms like "The main goal of", just output directly the summary in plain text
+ """;
+
+ GenerateSubjectPrompt = """
+ You are an expert developer specialist in creating commits messages.
+ Your only goal is to retrieve a single commit message.
+ Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
+ - Assign the commit {type} according to the next conditions:
+ feat: Only when adding a new feature.
+ fix: When fixing a bug.
+ docs: When updating documentation.
+ style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
+ test: When adding or updating tests.
+ chore: When making changes to the build process or auxiliary tools and libraries.
+ revert: When undoing a previous commit.
+ refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
+ - Do not add any issues numeration, explain your output nor introduce your answer.
+ - Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
+ - Be as concise as possible, keep the message under 50 characters.
+ """;
+ }
+
+ public OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
{
var chat = new OpenAIChatRequest() { Model = Model };
chat.AddMessage("system", prompt);
@@ -144,5 +180,12 @@ namespace SourceGit.Models
throw;
}
}
+
+ private string _name;
+ private string _server;
+ private string _apiKey;
+ private string _model;
+ private string _analyzeDiffPrompt;
+ private string _generateSubjectPrompt;
}
}
diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs
index 40c00bc9..77c58ee7 100644
--- a/src/Models/RepositorySettings.cs
+++ b/src/Models/RepositorySettings.cs
@@ -16,6 +16,12 @@ namespace SourceGit.Models
set;
} = DealWithLocalChanges.DoNothing;
+ public bool EnablePruneOnFetch
+ {
+ get;
+ set;
+ } = false;
+
public bool FetchWithoutTags
{
get;
@@ -94,6 +100,12 @@ namespace SourceGit.Models
set;
} = new AvaloniaList();
+ public AvaloniaList CustomActions
+ {
+ get;
+ set;
+ } = new AvaloniaList();
+
public bool EnableAutoFetch
{
get;
@@ -112,6 +124,30 @@ namespace SourceGit.Models
set;
} = false;
+ public bool IncludeUntrackedWhenStash
+ {
+ get;
+ set;
+ } = true;
+
+ public bool OnlyStagedWhenStash
+ {
+ get;
+ set;
+ } = false;
+
+ public bool KeepIndexWhenStash
+ {
+ get;
+ set;
+ } = false;
+
+ public string PreferedOpenAIService
+ {
+ get;
+ set;
+ } = "---";
+
public void PushCommitMessage(string message)
{
var existIdx = CommitMessages.IndexOf(message);
@@ -200,5 +236,22 @@ namespace SourceGit.Models
if (rule != null)
IssueTrackerRules.Remove(rule);
}
+
+ public CustomAction AddNewCustomAction()
+ {
+ var act = new CustomAction()
+ {
+ Name = "Unnamed Custom Action",
+ };
+
+ CustomActions.Add(act);
+ return act;
+ }
+
+ public void RemoveCustomAction(CustomAction act)
+ {
+ if (act != null)
+ CustomActions.Remove(act);
+ }
}
}
diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs
index 6ca0bbb0..a57d26d2 100644
--- a/src/Native/Windows.cs
+++ b/src/Native/Windows.cs
@@ -134,6 +134,7 @@ namespace SourceGit.Native
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox");
finder.SublimeText(FindSublimeText);
+ finder.TryAdd("Visual Studio", "vs", FindVisualStudio, GenerateCommandlineArgsForVisualStudio);
return finder.Founded;
}
@@ -313,6 +314,27 @@ namespace SourceGit.Native
return string.Empty;
}
+
+ private string FindVisualStudio()
+ {
+ var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
+ Microsoft.Win32.RegistryHive.LocalMachine,
+ Microsoft.Win32.RegistryView.Registry64);
+
+ // Get default class for VisualStudio.Launcher.sln - the handler for *.sln files
+ if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher)
+ {
+ // Get actual path to the executable
+ if (launcher.GetValue(string.Empty) is string CLSID &&
+ localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv &&
+ devenv.GetValue(string.Empty) is string localServer32)
+ {
+ return localServer32!.Trim('\"');
+ }
+ }
+
+ return string.Empty;
+ }
#endregion
private void OpenFolderAndSelectFile(string folderPath)
@@ -328,5 +350,31 @@ namespace SourceGit.Native
ILFree(pidl);
}
}
+
+ private string GenerateCommandlineArgsForVisualStudio(string repo)
+ {
+ var sln = FindVSSolutionFile(repo, 4);
+ return string.IsNullOrEmpty(sln) ? $"\"{repo}\"" : $"\"{sln}\"";
+ }
+
+ private string FindVSSolutionFile(string path, int leftDepth)
+ {
+ var found = Directory.GetFiles(path, "*.sln", SearchOption.TopDirectoryOnly);
+ if (found != null && found.Length > 0)
+ return Path.GetFullPath(found[0]);
+
+ if (leftDepth <= 0)
+ return null;
+
+ var subfolders = Directory.GetDirectories(path);
+ foreach (var subfolder in subfolders)
+ {
+ var first = FindVSSolutionFile(subfolder, leftDepth - 1);
+ if (!string.IsNullOrEmpty(first))
+ return first;
+ }
+
+ return null;
+ }
}
}
diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml
index 1a84597e..6cf53d96 100644
--- a/src/Resources/Icons.axaml
+++ b/src/Resources/Icons.axaml
@@ -1,4 +1,5 @@
+ M41 512c0-128 46-241 138-333C271 87 384 41 512 41s241 46 333 138c92 92 138 205 138 333s-46 241-138 333c-92 92-205 138-333 138s-241-46-333-138C87 753 41 640 41 512zm87 0c0 108 36 195 113 271s164 113 271 113c108 0 195-36 271-113s113-164 113-271-36-195-113-271c-77-77-164-113-271-113-108 0-195 36-271 113C164 317 128 404 128 512zm256 148V292l195 113L768 512l-195 113-195 113v-77zm148-113-61 36V440l61 36 61 36-61 36z
M951 419a255 255 0 00-22-209 258 258 0 00-278-124A259 259 0 00213 178a255 255 0 00-171 124 258 258 0 0032 303 255 255 0 0022 210 258 258 0 00278 124A255 255 0 00566 1024a258 258 0 00246-179 256 256 0 00171-124 258 258 0 00-32-302zM566 957a191 191 0 01-123-44l6-3 204-118a34 34 0 0017-29v-287l86 50a3 3 0 012 2v238a192 192 0 01-192 192zM154 781a191 191 0 01-23-129l6 4 204 118a33 33 0 0033 0l249-144v99a3 3 0 01-1 3L416 851a192 192 0 01-262-70zM100 337a191 191 0 01101-84V495a33 33 0 0017 29l248 143-86 50a3 3 0 01-3 0l-206-119A192 192 0 01100 336zm708 164-249-145L645 307a3 3 0 013 0l206 119a192 192 0 01-29 346v-242a34 34 0 00-17-28zm86-129-6-4-204-119a33 33 0 00-33 0L401 394V294a3 3 0 011-3l206-119a192 192 0 01285 199zm-539 176-86-50a3 3 0 01-2-2V259a192 192 0 01315-147l-6 3-204 118a34 34 0 00-17 29zm47-101 111-64 111 64v128l-111 64-111-64z
M296 392h64v64h-64zM296 582v160h128V582h-64v-62h-64v62zm80 48v64h-32v-64h32zM360 328h64v64h-64zM296 264h64v64h-64zM360 456h64v64h-64zM360 200h64v64h-64zM855 289 639 73c-6-6-14-9-23-9H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V311c0-9-3-17-9-23zM790 326H602V138L790 326zm2 562H232V136h64v64h64v-64h174v216c0 23 19 42 42 42h216v494z
M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z
@@ -71,9 +72,6 @@
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
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
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
- 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
- M1024 750v110c0 50-41 91-91 91h-841A92 92 0 010 859v-110C0 699 41 658 91 658h841c50 0 91 41 91 91z
- M0 4 0 20 16 20 0 4M4 0 20 0 20 16 4 0z
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
M824 645V307c0-56-46-102-102-102h-102V102l-154 154 154 154V307h102v338c-46 20-82 67-82 123 0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123zm-51 195c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72zM384 256c0-72-61-133-133-133-72 0-133 61-133 133 0 56 36 102 82 123v266C154 666 118 712 118 768c0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123V379C348 358 384 312 384 256zM323 768c0 41-31 72-72 72-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72zM251 328c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72z
M896 64H128C96 64 64 96 64 128v768c0 32 32 64 64 64h768c32 0 64-32 64-64V128c0-32-32-64-64-64z m-64 736c0 16-17 32-32 32H224c-18 0-32-12-32-32V224c0-16 16-32 32-32h576c15 0 32 16 32 32v576zM512 384c-71 0-128 57-128 128s57 128 128 128 128-57 128-128-57-128-128-128z
@@ -121,6 +119,7 @@
M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z
M832 464H332V240c0-31 25-56 56-56h248c31 0 56 25 56 56v68c0 4 4 8 8 8h56c4 0 8-4 8-8v-68c0-71-57-128-128-128H388c-71 0-128 57-128 128v224h-68c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32V496c0-18-14-32-32-32zM540 701v53c0 4-4 8-8 8h-40c-4 0-8-4-8-8v-53c-12-9-20-23-20-39 0-27 22-48 48-48s48 22 48 48c0 16-8 30-20 39z
M170 831l343-342L855 831l105-105-448-448L64 726 170 831z
+ M880 128A722 722 0 01555 13a77 77 0 00-85 0 719 719 0 01-325 115c-40 4-71 38-71 80v369c0 246 329 446 439 446 110 0 439-200 439-446V207c0-41-31-76-71-80zM465 692a36 36 0 01-53 0L305 579a42 42 0 010-57 36 36 0 0153 0l80 85L678 353a36 36 0 0153 0 42 42 0 01-0 57L465 692z
M812 864h-29V654c0-21-11-40-28-52l-133-88 134-89c18-12 28-31 28-52V164h28c18 0 32-14 32-32s-14-32-32-32H212c-18 0-32 14-32 32s14 32 32 32h30v210c0 21 11 40 28 52l133 88-134 89c-18 12-28 31-28 52V864H212c-18 0-32 14-32 32s14 32 32 32h600c18 0 32-14 32-32s-14-32-32-32zM441 566c18-12 28-31 28-52s-11-40-28-52L306 373V164h414v209l-136 90c-18 12-28 31-28 52 0 21 11 40 28 52l135 89V695c-9-7-20-13-32-19-30-15-93-41-176-41-63 0-125 14-175 38-12 6-22 12-31 18v-36l136-90z
M0 512M1024 512M512 0M512 1024M762 412v100h-500v-100h-150v200h800v-200h-150z
M519 459 222 162a37 37 0 10-52 52l297 297L169 809a37 37 0 1052 52l297-297 297 297a37 37 0 1052-52l-297-297 297-297a37 37 0 10-52-52L519 459z
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index ff846035..2e96e391 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -55,6 +55,7 @@
Delete selected {0} branches
Discard all changes
Fast-Forward to ${0}$
+ Fetch ${0}$ into ${1}$...
Git Flow - Finish ${0}$
Merge ${0}$ into ${1}$...
Pull ${0}$
@@ -69,6 +70,7 @@
CANCEL
Reset to This Revision
Reset to Parent Revision
+ Generate commit message
CHANGE DISPLAY MODE
Show as File and Dir List
Show as Path List
@@ -106,6 +108,7 @@
Compare with Worktree
Copy Info
Copy SHA
+ Custom Action
Interactive Rebase ${0}$ to Here
Rebase ${0}$ to Here
Reset ${0}$ to Here
@@ -137,12 +140,21 @@
COMMIT TEMPLATE
Template Name:
Template Content:
+ CUSTOM ACTION
+ Arguments:
+ ${REPO} - Repository's path; ${SHA} - Selected commit's SHA
+ Executable File:
+ Name:
+ Scope:
+ Commit
+ Repository
Email Address
Email address
GIT
Fetch remotes automatically
Minute(s)
Default Remote
+ Enable --prune on fetch
Enable --signoff for commit
ISSUE TRACKER
Add Sample Github Rule
@@ -154,6 +166,9 @@
Rule Name:
Result URL:
Please use $1, $2 to access regex groups values.
+ OPEN AI
+ Prefered Service:
+ If the 'Prefered Service' is set, SourceGit will only use it in this repository. Otherwise, if there is more than one service available, a context menu to choose one of them will be shown.
HTTP Proxy
HTTP proxy used by this repository
User Name
@@ -246,6 +261,8 @@
Target:
Edit Selected Group
Edit Selected Repository
+ Run Custom Action
+ Action Name:
Fast-Forward (without checkout)
Fetch
Fetch all remotes
@@ -401,12 +418,12 @@
Last year
{0} years ago
Preference
- Advanced Options
OPEN AI
Analyze Diff Prompt
API Key
Generate Subject Prompt
Model
+ Name
Server
APPEARANCE
Default Font
@@ -510,6 +527,8 @@
Clear all
Configure this repository
CONTINUE
+ Custom Actions
+ No Custom Actions
Enable '--reflog' Option
Open In File Browser
Search Branches/Tags/Submodules
@@ -572,6 +591,7 @@
START
Stash
Include untracked files
+ Keep staged files
Message:
Optional. Name of this stash
Only staged changes
diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml
new file mode 100644
index 00000000..f8a2fa68
--- /dev/null
+++ b/src/Resources/Locales/es_ES.axaml
@@ -0,0 +1,670 @@
+
+
+
+
+ Acerca de
+ Acerca de SourceGit
+ • Construido con
+ • El gráfico es renderizado por
+ © 2024 sourcegit-scm
+ • Editor de texto de
+ • Las fuentes monoespaciadas provienen de
+ • El código fuente se puede encontrar en
+ Cliente Git GUI de código abierto y gratuito
+ Agregar Worktree
+ Qué Checkout:
+ Rama Existente
+ Crear Nueva Rama
+ Ubicación:
+ Ruta para este worktree. Se admite ruta relativa.
+ Nombre de la Rama:
+ Opcional. Por defecto es el nombre de la carpeta de destino.
+ Rama de Seguimiento:
+ Seguimiento de rama remota
+ Asistente OpenAI
+ Usar OpenAI para generar mensaje de commit
+ Aplicar Patch
+ Error
+ Genera errores y se niega a aplicar el patch
+ Error Todo
+ Similar a 'error', pero muestra más
+ Archivo Patch:
+ Seleccionar archivo .patch para aplicar
+ Ignorar cambios de espacios en blanco
+ Sin Advertencia
+ Desactiva la advertencia de espacios en blanco al final
+ Aplicar Patch
+ Advertencia
+ Genera advertencias para algunos de estos errores, pero aplica
+ Espacios en Blanco:
+ Archivar...
+ Guardar Archivo en:
+ Seleccionar ruta del archivo
+ Revisión:
+ Archivar
+ SourceGit Askpass
+ ARCHIVOS ASUMIDOS COMO SIN CAMBIOS
+ NO HAY ARCHIVOS ASUMIDOS COMO SIN CAMBIOS
+ REMOVER
+ ¡ARCHIVO BINARIO NO SOPORTADO!
+ Blame
+ ¡BLAME EN ESTE ARCHIVO NO SOPORTADO!
+ Checkout ${0}$...
+ Comparar con Rama
+ Comparar con HEAD
+ Comparar con Worktree
+ Copiar Nombre de Rama
+ Eliminar ${0}$...
+ Eliminar {0} ramas seleccionadas
+ Descartar todos los cambios
+ Fast-Forward a ${0}$
+ Fetch ${0}$ en ${1}$...
+ Git Flow - Finalizar ${0}$
+ Merge ${0}$ en ${1}$...
+ Pull ${0}$
+ Pull ${0}$ en ${1}$...
+ Push ${0}$
+ Rebase ${0}$ en ${1}$...
+ Renombrar ${0}$...
+ Establecer Rama de Seguimiento
+ Desestablecer Upstream
+ Comparar Ramas
+ Bytes
+ CANCELAR
+ Resetear a Esta Revisión
+ Resetear a Revisión Padre
+ CAMBIAR MODO DE VISUALIZACIÓN
+ Mostrar como Lista de Archivos y Directorios
+ Mostrar como Lista de Rutas
+ Mostrar como Árbol de Sistema de Archivos
+ Checkout Rama
+ Checkout Commit
+ Advertencia: Al hacer un checkout de commit, tu Head se separará
+ Commit:
+ Rama:
+ Cambios Locales:
+ Descartar
+ No Hacer Nada
+ Stash & Reaplicar
+ Cherry Pick
+ Añadir fuente al mensaje de commit
+ Commit(s):
+ Commit todos los cambios
+ Mainline:
+ Normalmente no puedes cherry-pick un merge porque no sabes qué lado del merge debe considerarse la línea principal. Esta opción permite que cherry-pick reproduzca el cambio en relación con el padre especificado.
+ Limpiar Stashes
+ Estás intentando limpiar todos los stashes. ¿Estás seguro de continuar?
+ Clonar Repositorio Remoto
+ Parámetros Adicionales:
+ Argumentos adicionales para clonar el repositorio. Opcional.
+ Nombre Local:
+ Nombre del repositorio. Opcional.
+ Carpeta Padre:
+ URL del Repositorio:
+ CERRAR
+ Editor
+ Cherry-Pick Este Commit
+ Cherry-Pick ...
+ Checkout Commit
+ Comparar con HEAD
+ Comparar con Worktree
+ Copiar Información
+ Copiar SHA
+ Rebase Interactivo ${0}$ hasta Aquí
+ Rebase ${0}$ hasta Aquí
+ Reset ${0}$ hasta Aquí
+ Revertir Commit
+ Reescribir
+ Guardar como Patch...
+ Squash en Parent
+ Squash Commits Hijos hasta Aquí
+ CAMBIOS
+ Buscar Cambios...
+ ARCHIVOS
+ Archivo LFS
+ Submódulo
+ INFORMACIÓN
+ AUTOR
+ CAMBIADO
+ COMMITTER
+ Ver refs que contienen este commit
+ COMMIT ESTÁ CONTENIDO EN
+ Muestra solo los primeros 100 cambios. Ver todos los cambios en la pestaña CAMBIOS.
+ MENSAJE
+ PADRES
+ REFS
+ SHA
+ Abrir en Navegador
+ Introducir asunto del commit
+ Descripción
+ Configurar Repositorio
+ PLANTILLA DE COMMIT
+ Nombre de la Plantilla:
+ Contenido de la Plantilla:
+ Dirección de Email
+ Dirección de email
+ GIT
+ Fetch remotos automáticamente
+ Minuto(s)
+ Remoto por Defecto
+ Habilitar --signoff para commit
+ SEGUIMIENTO DE INCIDENCIAS
+ Añadir Regla de Ejemplo para Github
+ Añadir Regla de Ejemplo para Jira
+ Añadir Regla de Ejemplo para Incidencias de GitLab
+ Añadir Regla de Ejemplo para Merge Requests de GitLab
+ Nueva Regla
+ Expresión Regex para Incidencias:
+ Nombre de la Regla:
+ URL Resultante:
+ Por favor, use $1, $2 para acceder a los valores de los grupos regex.
+ Proxy HTTP
+ Proxy HTTP utilizado por este repositorio
+ Nombre de Usuario
+ Nombre de usuario para este repositorio
+ Espacios de Trabajo
+ Color
+ Restaurar pestañas al iniciar
+ Asistente de Commit Convencional
+ Cambio Importante:
+ Incidencia Cerrada:
+ Detalles del Cambio:
+ Alcance:
+ Descripción Corta:
+ Tipo de Cambio:
+ Copiar
+ Copiar Todo el Texto
+ COPIAR MENSAJE
+ Copiar Ruta
+ Copiar Nombre del Archivo
+ Crear Rama...
+ Basado En:
+ Checkout de la rama creada
+ Cambios Locales:
+ Descartar
+ No Hacer Nada
+ Stash & Reaplicar
+ Nombre de la Nueva Rama:
+ Introduzca el nombre de la rama.
+ Crear Rama Local
+ Crear Etiqueta...
+ Nueva Etiqueta En:
+ Firma GPG
+ Mensaje de la Etiqueta:
+ Opcional.
+ Nombre de la Etiqueta:
+ Formato recomendado: v1.0.0-alpha
+ Push a todos los remotos después de crear
+ Crear Nueva Etiqueta
+ Tipo:
+ anotada
+ ligera
+ Mantenga Ctrl para iniciar directamente
+ Cortar
+ Eliminar Rama
+ Rama:
+ ¡Estás a punto de eliminar una rama remota!
+ También eliminar la rama remota ${0}$
+ Eliminar Múltiples Ramas
+ Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de revisar antes de tomar acción!
+ Eliminar Remoto
+ Remoto:
+ Destino:
+ Confirmar Eliminación de Grupo
+ Confirmar Eliminación de Repositorio
+ Eliminar Submódulo
+ Ruta del Submódulo:
+ Eliminar Etiqueta
+ Etiqueta:
+ Eliminar de los repositorios remotos
+ DIFERENCIA BINARIA
+ NUEVO
+ ANTIGUO
+ Copiar
+ Modo de Archivo Cambiado
+ Ignorar Cambio de Espacios en Blanco
+ CAMBIO DE OBJETO LFS
+ Siguiente Diferencia
+ SIN CAMBIOS O SOLO CAMBIOS DE EOL
+ Diferencia Anterior
+ Mostrar símbolos ocultos
+ Diferencia Lado a Lado
+ SUBMÓDULO
+ NUEVO
+ Intercambiar
+ Resaltado de Sintaxis
+ Ajuste de Línea
+ Abrir en Herramienta de Merge
+ Disminuir Número de Líneas Visibles
+ Aumentar Número de Líneas Visibles
+ SELECCIONA ARCHIVO PARA VER CAMBIOS
+ Abrir en Herramienta de Merge
+ Descartar Cambios
+ Todos los cambios locales en la copia de trabajo.
+ Cambios:
+ Incluir archivos ignorados
+ Total {0} cambios serán descartados
+ ¡No puedes deshacer esta acción!
+ Marcador:
+ Nuevo Nombre:
+ Destino:
+ Editar Grupo Seleccionado
+ Editar Repositorio Seleccionado
+ Fast-Forward (sin checkout)
+ Fetch
+ Fetch todos los remotos
+ Fetch sin etiquetas
+ Remoto:
+ Fetch Cambios Remotos
+ Asumir sin cambios
+ Descartar...
+ Descartar {0} archivos...
+ Descartar Cambios en Línea(s) Seleccionada(s)
+ Abrir Herramienta de Merge Externa
+ Guardar Como Patch...
+ Stage
+ Stage {0} archivos
+ Stage Cambios en Línea(s) Seleccionada(s)
+ Stash...
+ Stash {0} archivos...
+ Unstage
+ Unstage {0} archivos
+ Unstage Cambios en Línea(s) Seleccionada(s)
+ Usar Suyos (checkout --theirs)
+ Usar Míos (checkout --ours)
+ Historial de Archivos
+ CONTENIDO
+ CAMBIO
+ FILTRO
+ Git-Flow
+ Rama de Desarrollo:
+ Feature:
+ Prefijo de Feature:
+ FLOW - Finalizar Feature
+ FLOW - Finalizar Hotfix
+ FLOW - Finalizar Release
+ Destino:
+ Hotfix:
+ Prefijo de Hotfix:
+ Inicializar Git-Flow
+ Mantener rama
+ Rama de Producción:
+ Release:
+ Prefijo de Release:
+ Iniciar Feature...
+ FLOW - Iniciar Feature
+ Iniciar Hotfix...
+ FLOW - Iniciar Hotfix
+ Introducir nombre
+ Iniciar Release...
+ FLOW - Iniciar Release
+ Prefijo de Etiqueta de Versión:
+ Git LFS
+ Añadir Patrón de Seguimiento...
+ El patrón es el nombre del archivo
+ Patrón Personalizado:
+ Añadir Patrón de Seguimiento a Git LFS
+ Fetch
+ Fetch Objetos LFS
+ Ejecuta `git lfs fetch` para descargar objetos Git LFS. Esto no actualiza la copia de trabajo.
+ Instalar hooks de Git LFS
+ Mostrar Bloqueos
+ No hay archivos bloqueados
+ Bloquear
+ Mostrar solo mis bloqueos
+ Bloqueos LFS
+ Desbloquear
+ Forzar Desbloqueo
+ Prune
+ Ejecuta `git lfs prune` para eliminar archivos LFS antiguos del almacenamiento local
+ Pull
+ Pull Objetos LFS
+ Ejecuta `git lfs pull` para descargar todos los archivos Git LFS para la referencia actual y hacer checkout
+ Push
+ Push Objetos LFS
+ Push archivos grandes en cola al endpoint de Git LFS
+ Remoto:
+ Seguir archivos llamados '{0}'
+ Seguir todos los archivos *{0}
+ Historias
+ Cambiar a Disposición Horizontal/Vertical
+ AUTOR
+ HORA DEL AUTOR
+ GRÁFICO & ASUNTO
+ SHA
+ FECHA DE COMMIT
+ {0} COMMITS SELECCIONADOS
+ Mantén 'Ctrl' o 'Shift' para seleccionar múltiples commits.
+ Mantén ⌘ o ⇧ para seleccionar múltiples commits.
+ CONSEJOS:
+ Referencia de Atajos de Teclado
+ GLOBAL
+ Cancelar popup actual
+ Cerrar página actual
+ Ir a la página anterior
+ Ir a la siguiente página
+ Crear nueva página
+ Abrir diálogo de preferencias
+ REPOSITORIO
+ Commit cambios staged
+ Commit y push cambios staged
+ Stage todos los cambios y commit
+ Descartar cambios seleccionados
+ Modo Dashboard (Por Defecto)
+ Forzar a recargar este repositorio
+ Stage/Unstage cambios seleccionados
+ Modo de búsqueda de commits
+ Cambiar a 'Cambios'
+ Cambiar a 'Historias'
+ Cambiar a 'Stashes'
+ EDITOR DE TEXTO
+ Cerrar panel de búsqueda
+ Buscar siguiente coincidencia
+ Buscar coincidencia anterior
+ Abrir panel de búsqueda
+ Stage
+ Unstage
+ Descartar
+ Inicializar Repositorio
+ Ruta:
+ Cherry-Pick en progreso. Presiona 'Abort' para restaurar el HEAD original.
+ Merge en progreso. Presiona 'Abort' para restaurar el HEAD original.
+ Rebase en progreso. Presiona 'Abort' para restaurar el HEAD original.
+ Revert en progreso. Presiona 'Abort' para restaurar el HEAD original.
+ Rebase Interactivo
+ Rama Objetivo:
+ En:
+ ERROR
+ AVISO
+ Merge Rama
+ En:
+ Opción de Merge:
+ Rama Fuente:
+ Mover Nodo del Repositorio
+ Seleccionar nodo padre para:
+ Nombre:
+ Git NO ha sido configurado. Por favor, ve a [Preferencias] y configúralo primero.
+ Abrir Directorio de Datos de la App
+ Abrir Con...
+ Opcional.
+ Crear Nueva Página
+ Marcador
+ Cerrar Pestaña
+ Cerrar Otras Pestañas
+ Cerrar Pestañas a la Derecha
+ Copiar Ruta del Repositorio
+ Repositorios
+ Pegar
+ Justo ahora
+ Hace {0} minutos
+ Hace {0} horas
+ Ayer
+ Hace {0} días
+ Último mes
+ Hace {0} meses
+ Último año
+ Hace {0} años
+ Preferencias
+ Opciones Avanzadas
+ OPEN AI
+ Analizar Diff Prompt
+ Clave API
+ Generar Subject Prompt
+ Modelo
+ Servidor
+ APARIENCIA
+ Fuente por defecto
+ Tamaño de fuente por defecto
+ Fuente Monospace
+ Usar solo fuente monospace en el editor de texto
+ Tema
+ Sobreescritura de temas
+ Usar ancho de pestaña fijo en la barra de título
+ Usar marco de ventana nativo
+ HERRAMIENTA DIFF/MERGE
+ Ruta de instalación
+ Introducir ruta para la herramienta diff/merge
+ Herramienta
+ GENERAL
+ Buscar actualizaciones al iniciar
+ Idioma
+ Commits en el historial
+ Mostrar hora del autor en lugar de la hora del commit en el gráfico
+ Longitud de la guía del asunto
+ GIT
+ Habilitar Auto CRLF
+ Directorio de clonado por defecto
+ Email de usuario
+ Email global del usuario git
+ Ruta de instalación
+ Nombre de usuario
+ Nombre global del usuario git
+ Versión de Git
+ Se requiere Git (>= 2.23.0) para esta aplicación
+ FIRMA GPG
+ Firma GPG en commit
+ Firma GPG en etiqueta
+ Formato GPG
+ Ruta de instalación del programa
+ Introducir ruta para el programa gpg instalado
+ Clave de firma del usuario
+ Clave de firma gpg del usuario
+ INTEGRACIÓN
+ SHELL/TERMINAL
+ Shell/Terminal
+ Ruta
+ Podar Remoto
+ Destino:
+ Podar Worktrees
+ Podar información de worktree en `$GIT_DIR/worktrees`
+ Pull
+ Rama:
+ Fetch todas las ramas
+ En:
+ Cambios Locales:
+ Descartar
+ No Hacer Nada
+ Stash & Reaplicar
+ Fetch sin etiquetas
+ Remoto:
+ Pull (Fetch & Merge)
+ Usar rebase en lugar de merge
+ Push
+ Asegurarse de que los submódulos se hayan hecho push
+ Forzar push
+ Rama Local:
+ Remoto:
+ Push Cambios al Remoto
+ Rama Remota:
+ Establecer como rama de seguimiento
+ Push todas las etiquetas
+ Push Etiqueta al Remoto
+ Push a todos los remotos
+ Remoto:
+ Etiqueta:
+ Salir
+ Rebase Rama Actual
+ Stash & reaplicar cambios locales
+ En:
+ Rebase:
+ Refrescar
+ Añadir Remoto
+ Editar Remoto
+ Nombre:
+ Nombre remoto
+ URL del Repositorio:
+ URL del repositorio git remoto
+ Copiar URL
+ Borrar...
+ Editar...
+ Fetch
+ Abrir En Navegador
+ Podar (Prune)
+ Confirmar para Eliminar Worktree
+ Utilizar Opción `--force`
+ Destino:
+ Renombrar Rama
+ Nuevo Nombre:
+ Nombre único para esta rama
+ Rama:
+ ABORTAR
+ Auto fetching cambios desde remotos...
+ Limpiar (GC & Prune)
+ Ejecutar comando `git gc` para este repositorio.
+ Limpiar todo
+ Configurar este repositorio
+ CONTINUAR
+ Habilitar Opción '--reflog'
+ Abrir en el Explorador
+ Buscar Ramas/Etiquetas/Submódulos
+ FILTRAR POR:
+ RAMAS LOCALES
+ Navegar a HEAD
+ Habilitar Opción '--first-parent'
+ Crear Rama
+ Abrir en {0}
+ Abrir en Herramientas Externas
+ Refrescar
+ REMOTOS
+ AÑADIR REMOTO
+ RESOLVER
+ Buscar Commit
+ Archivo
+ Mensaje
+ SHA
+ Autor & Committer
+ Rama Actual
+ Mostrar Etiquetas como Árbol
+ Estadísticas
+ SUBMÓDULOS
+ AÑADIR SUBMÓDULO
+ ACTUALIZAR SUBMÓDULO
+ ETIQUETAS
+ NUEVA ETIQUETA
+ Abrir en Terminal
+ WORKTREES
+ AÑADIR WORKTREE
+ PRUNE
+ URL del Repositorio Git
+ Resetear Rama Actual a Revisión
+ Modo de Reset:
+ Mover a:
+ Rama Actual:
+ Revelar en el Explorador de Archivos
+ Revertir Commit
+ Commit:
+ Commit revertir cambios
+ Reescribir Mensaje de Commit
+ Usa 'Shift+Enter' para introducir una nueva línea. 'Enter' es el atajo del botón OK
+ Ejecutando. Por favor espera...
+ GUARDAR
+ Guardar Como...
+ ¡El patch se ha guardado exitosamente!
+ Escanear Repositorios
+ Directorio Raíz:
+ Buscar Actualizaciones...
+ Nueva versión de este software disponible:
+ ¡Error al buscar actualizaciones!
+ Descargar
+ Omitir Esta Versión
+ Actualización de Software
+ Actualmente no hay actualizaciones disponibles.
+ Squash Commits
+ En:
+ Clave Privada SSH:
+ Ruta de almacenamiento de la clave privada SSH
+ INICIAR
+ Stash
+ Incluir archivos no rastreados
+ Mensaje:
+ Opcional. Nombre de este stash
+ Solo cambios staged
+ ¡Tanto los cambios staged como los no staged de los archivos seleccionados serán stashed!
+ Stash Cambios Locales
+ Aplicar
+ Eliminar
+ Pop
+ Eliminar Stash
+ Eliminar:
+ Stashes
+ CAMBIOS
+ STASHES
+ Estadísticas
+ COMMITS
+ COMMITTER
+ MES
+ SEMANA
+ COMMITS:
+ AUTORES:
+ GENERAL
+ SUBMÓDULOS
+ Añadir Submódulo
+ Copiar Ruta Relativa
+ Fetch submódulos anidados
+ Abrir Repositorio del Submódulo
+ Ruta Relativa:
+ Carpeta relativa para almacenar este módulo.
+ Eliminar Submódulo
+ OK
+ Copiar Nombre de la Etiqueta
+ Copiar Mensaje de la Etiqueta
+ Eliminar ${0}$...
+ Push ${0}$...
+ URL:
+ Actualizar Submódulos
+ Todos los submódulos
+ Inicializar según sea necesario
+ Recursivamente
+ Submódulo:
+ Usar opción --remote
+ Advertencia
+ Página de Bienvenida
+ Crear Grupo
+ Crear Sub-Grupo
+ Clonar Repositorio
+ Eliminar
+ SOPORTA ARRASTRAR Y SOLTAR CARPETAS. SOPORTA AGRUPACIÓN PERSONALIZADA.
+ Editar
+ Mover a Otro Grupo
+ Abrir Todos Los Repositorios
+ Abrir Repositorio
+ Abrir Terminal
+ Reescanear Repositorios en el Directorio de Clonado por Defecto
+ Buscar Repositorios...
+ Ordenar
+ Cambios
+ Git Ignore
+ Ignorar todos los archivos *{0}
+ Ignorar archivos *{0} en la misma carpeta
+ Ignorar archivos en la misma carpeta
+ Ignorar solo este archivo
+ Enmendar (Amend)
+ Puedes stagear este archivo ahora.
+ COMMIT
+ COMMIT & PUSH
+ Plantilla/Historias
+ Activar evento de clic
+ Stagear todos los cambios y commit
+ ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty)?
+ CONFLICTOS DETECTADOS
+ LOS CONFLICTOS DE ARCHIVOS ESTÁN RESUELTOS
+ INCLUIR ARCHIVOS NO RASTREADOS
+ NO HAY MENSAJES DE ENTRADA RECIENTES
+ NO HAY PLANTILLAS DE COMMIT
+ STAGED
+ UNSTAGE
+ UNSTAGE TODO
+ UNSTAGED
+ STAGE
+ STAGE TODO
+ VER ASSUME UNCHANGED
+ Plantilla: ${0}$
+ Haz clic derecho en el(los) archivo(s) seleccionado(s) y elige tu opción para resolver conflictos.
+ ESPACIO DE TRABAJO:
+ Configura Espacios de Trabajo...
+ WORKTREE
+ Copiar Ruta
+ Bloquear
+ Eliminar
+ Desbloquear
+
diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml
index 0d7c55fb..d2076099 100644
--- a/src/Resources/Locales/ru_RU.axaml
+++ b/src/Resources/Locales/ru_RU.axaml
@@ -16,7 +16,7 @@
Существующую ветку
Создать новую ветку
Расположение:
- Путь к этому рабочему дереву. Поддерживается относительный путью
+ Путь к этому рабочему дереву. Поддерживается относительный путь.
Имя ветки:
Необязательно. По умолчанию используется имя целевой папки.
Отслеживание ветки:
@@ -27,7 +27,7 @@
Ошибка
Выдает ошибки и отказывается применять исправление
Все ошибки
- Аналогично "ошибке", но показывает больше
+ Аналогично «ошибке», но показывает больше
Файл исправлений:
Выберите файл .patch для применения
Игнорировать изменения пробелов
@@ -58,6 +58,7 @@
Удалить выбранные {0} ветки
Отклонить все изменения.
Перемотать вперёд к ${0}$
+ Извлечь ${0}$ в ${1}$...
Поток Git - Завершение ${0}$
Слить ${0}$ в ${1}$...
Забрать ${0}$
@@ -72,6 +73,7 @@
ОТМЕНА
Сбросить эту ревизию
Сбросить родительскую ревизию
+ Произвести сообщение о фиксации
ИЗМЕНИТЬ РЕЖИМ ОТОБРАЖЕНИЯ
Показывать в виде списка файлов и каталогов
Показывать в виде списка путей
@@ -109,14 +111,15 @@
Сравнить с рабочим деревом
Копировать информацию
Копировать SHA
+ Пользовательское действие
Интерактивное перемещение ${0}$ сюда
Переместить ${0}$ сюда
Сбросить ${0}$ сюда
Вернуть фиксацию
Переформулировать
Сохранить как исправление...
- Уплотнить в родительскую
- Уплотнить дочерную фиксацию сюда
+ Втиснуть в родительскую
+ Втиснуть дочерную фиксацию сюда
ИЗМЕНЕНИЯ
Найти изменения....
ФАЙЛЫ
@@ -140,13 +143,22 @@
ШАБЛОН ФИКСАЦИИ
Имя шаблона:
Шаблон содержания:
+ ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ
+ Аргументы:
+ ${REPO} - Путь хранилища; ${SHA} - Выбранные фиксации SHA
+ Исполняемый фалй:
+ Имя:
+ Диапазон:
+ Фиксация
+ Хранилище
Адрес электронной почты
Адрес электронной почты
GIT
Автоматическое извлечение внешних хранилищ
Минут(а/ы)
- Удалённое хранилище по-умолчанию
Разрешить --signoff для фиксации
+ Удалённое хранилище по-умолчанию
+ Разрешить --prune при извлечении
ОТСЛЕЖИВАНИЕ ПРОБЛЕМ
Добавить пример правила для Git
Добавить пример правила Jira
@@ -157,9 +169,12 @@
Имя правила:
Адрес результата:
Пожалуйста, используйте $1, $2 для доступа к значениям групп регулярных выражений.
+ ОТКРЫТЬ ИИ
+ Предпочитаемый сервис:
+ Если «Предпочитаемый сервис» установлен, SourceGit будет использовать только этот хранилище. В противном случае, если доступно более одной услуги, будет отображено контекстное меню для выбора одной из них.
HTTP-прокси
HTTP-прокси, используемый этим хранилищем
- Имя пользовтаеля
+ Имя пользователя
Имя пользователя для этого хранилища
Рабочие пространства
Имя
@@ -250,6 +265,8 @@
Цель:
Редактировать выбранную группу
Редактировать выбранное хранилище
+ Выполнить пользовательское действие
+ Имя действия:
Быстрая перемотка вперёд (без проверки)
Извлечь
Извлечь все внешние хранилища
@@ -306,7 +323,7 @@
Добавить шаблон отслеживания в ХБФ Git
Извлечь
Извлечь объекты ХБФ
- Запустить `git lfs fetch", чтобы загрузить объекты ХБФ Git. При этом рабочая копия не обновляется.
+ Запустить «git lfs fetch», чтобы загрузить объекты ХБФ Git. При этом рабочая копия не обновляется.
Установить перехват ХБФ Git
Показывать блокировки
Нет заблокированных файлов
@@ -316,15 +333,15 @@
Разблокировать
Принудительно разблокировать
Обрезать
- Запустите `git lfs prune", чтобы удалить старые файлы ХБФ из локального хранилища
+ Запустить «git lfs prune», чтобы удалить старые файлы ХБФ из локального хранилища
Забрать
Забрать объекты ХБФ
- Запустите `git lfs pull", чтобы загрузить все файлы ХБФ Git для текущей ссылки и проверить
+ Запустить «git lfs pull», чтобы загрузить все файлы ХБФ Git для текущей ссылки и проверить
Выложить
Выложить объекты ХБФ
Отправляйте большие файлы, помещенные в очередь, в конечную точку ХБФ Git
Внешнее хранилище:
- Отслеживать файлы с именем '{0}'
+ Отслеживать файлы с именем «{0}»
Отслеживать все *{0} файлов
Истории
Переключение горизонтального/вертикального расположения
@@ -334,7 +351,7 @@
SHA
ВРЕМЯ ФИКСАЦИИ
ВЫБРАННЫЕ {0} ФИКСАЦИИ
- Удерживайте 'Ctrl' или 'Shift', чтобы выбрать несколько фиксаций.
+ Удерживайте Ctrl или Shift, чтобы выбрать несколько фиксаций.
Удерживайте ⌘ или ⇧, чтобы выбрать несколько фиксаций.
ПОДСКАЗКИ:
Ссылка на сочетания клавиш
@@ -354,9 +371,9 @@
Принудительно перезагрузить этот хранилище
Подготовленные/Неподготовленные выбранные изменения
Режим поиска фиксаций
- Переключить на 'Изменения'
- Переключить на 'Истории'
- Переключить на 'Отложенные'
+ Переключить на «Изменения»
+ Переключить на «Истории»
+ Переключить на «Отложенные»
ТЕКСТОВЫЙ РЕДАКТОР
Закрыть панель поиска
Найти следующее совпадение
@@ -367,10 +384,10 @@
Отклонить
Инициализировать хранилище
Путь:
- Выполняется частичный забор. Нажмите 'Отказ' для восстановления заголовка.
- Выполняет запрос слияния. Нажмите 'Отказ' для восстановления заголовка.
- Выполняется перенос. Нажмите 'Отказ' для восстановления заголовка.
- Выполняется возврат. Нажмите 'Отказ' для восстановления заголовка.
+ Выполняется частичный забор. Нажмите «Отказ» для восстановления заголовка.
+ Выполняет запрос слияния. Нажмите «Отказ» для восстановления заголовка.
+ Выполняется перенос. Нажмите «Отказ» для восстановления заголовка.
+ Выполняется возврат. Нажмите «Отказ» для восстановления заголовка.
Интерактивное перемещение
Целевая ветка:
На:
@@ -405,12 +422,12 @@
В пролому году
{0} лет назад
Параметры
- Расширенные опции
ОТКРЫТЬ ИИ
Ключ API
Запрос на анализ различий
- Сгенерировать запрос на тему
+ Произвести запрос на тему
Модель
+ Имя:
Сервер
ВИД
Шрифт по-умолчанию
@@ -456,7 +473,7 @@
Удалить внешнее хранилище
Цель:
Удалить рабочее дерево
- Информация об обрезке рабочего дерева в `$GIT_DIR/worktrees`
+ Информация об обрезке рабочего дерева в «$GIT_DIR/worktrees»
Забрать
Ветка:
Извлечь все ветки
@@ -501,7 +518,7 @@
Открыть в браузере
Удалить
Подтвердить удаление рабочего дерева
- Включить опцию `--force`
+ Включить опцию --force
Цель:
Переименовать ветку
Новое имя:
@@ -510,17 +527,19 @@
Отказ
Автоматическое извлечение изменений с внешних хранилищ...
Очистить (Сбор мусора и удаление)
- Запустить команду `git gc` для данного хранилища.
+ Запустить команду «git gc» для данного хранилища.
Очистить всё
Настройка этого хранилища
ПРОДОЛЖИТЬ
- Разрешить опцию '--reflog'
+ Изменить действия
+ Не изменять действия
+ Разрешить опцию --reflog
Открыть в файловом менеджере
Поиск веток, меток и подмодулей
- ОТФИЛЬТРОВАНО ОТ:
+ ОТФИЛЬТРОВАНО:
ЛОКАЛЬНЫЕ ВЕТКИ
Навигация по заголовку
- Включить опцию '--first-parent'
+ Включить опцию --first-parent
Создать ветку
Открыть в {0}
Открыть в расширенном инструменте
@@ -550,12 +569,12 @@
Режим сброса:
Переместить в:
Текущая ветка:
- Раскрыть в файловом менеджере
+ Открыть в файловом менеджере
Отменить фиксацию
Фиксация:
Отмена изменений фиксации
Переформулировать сообщение фиксации
- Использовать "Shift+Enter" для ввода новой строки. "Enter" - это горячая клавиша кнопки OK
+ Использовать «Shift+Enter» для ввода новой строки. «Enter» - это горячая клавиша кнопки OK
Запуск. Подождите пожалуйста...
СОХРАНИТЬ
Сохранить как...
@@ -569,7 +588,7 @@
Пропустить эту версию
Обновление ПО
В настоящее время обновления недоступны.
- Уплотнить фиксации
+ Втиснуть фиксации
В:
Частный ключ SSH:
Путь хранения частного ключа SSH
@@ -578,6 +597,7 @@
ЗАПУСК
Отложить
Включить неотслеживаемые файлы
+ Хранить отложенные файлы
Сообщение:
Необязательно. Имя этого тайника
Отложить локальные изменения
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index f4027111..469a88bb 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -58,6 +58,7 @@
删除选中的 {0} 个分支
放弃所有更改
快进(fast-forward)到 ${0}$
+ 拉取(fetch) ${0}$ 至 ${1}$...
GIT工作流 - 完成 ${0}$
合并 ${0}$ 到 ${1}$...
拉回(pull) ${0}$
@@ -72,6 +73,7 @@
取 消
重置文件到该版本
重置文件到上一版本
+ 生成提交信息
切换变更显示模式
文件名+路径列表模式
全路径列表模式
@@ -109,6 +111,7 @@
与本地工作树比较
复制简要信息
复制提交指纹
+ 自定义操作
交互式变基(rebase -i) ${0}$ 到此处
变基(rebase) ${0}$ 到此处
重置(reset) ${0}$ 到此处
@@ -140,6 +143,14 @@
提交信息模板
模板名 :
模板内容 :
+ 自定义操作
+ 命令行参数 :
+ 请使用${REPO}代替仓库路径,${SHA}代替提交哈希
+ 可执行文件路径 :
+ 名称 :
+ 作用目标 :
+ 选中的提交
+ 仓库
电子邮箱
邮箱地址
GIT配置
@@ -147,6 +158,7 @@
分钟
默认远程
提交信息追加署名 (--signoff)
+ 拉取更新时启用修剪(--prune)
ISSUE追踪
新增匹配Github Issue规则
新增匹配Jira规则
@@ -157,6 +169,9 @@
规则名 :
为ISSUE生成的URL链接 :
可在URL中使用$1,$2等变量填入正则表达式匹配的内容
+ OPEN AI
+ 启用特定服务 :
+ 当【启用特定服务】被设置时,SourceGit将在本仓库中仅使用该服务。否则将弹出可用的OpenAI服务列表供用户选择。
HTTP代理
HTTP网络代理
用户名
@@ -249,6 +264,8 @@
目标 :
编辑分组
编辑仓库
+ 执行自定义操作
+ 自定义操作 :
快进(fast-forward,无需checkout)
拉取(fetch)
拉取所有的远程仓库
@@ -404,7 +421,13 @@
一年前
{0}年前
偏好设置
- 高级设置
+ OPEN AI
+ Analyze Diff Prompt
+ API密钥
+ Generate Subject Prompt
+ 模型
+ 配置名称
+ 服务地址
外观配置
缺省字体
默认字体大小
@@ -507,6 +530,8 @@
清空过滤规则
配置本仓库
下一步
+ 自定义操作
+ 自定义操作未设置
启用 --reflog 选项
在文件浏览器中打开
快速查找分支/标签/子模块
@@ -569,6 +594,7 @@
开 始
贮藏(stash)
包含未跟踪的文件
+ 保留暂存区文件
信息 :
选填,用于命名此贮藏
仅贮藏暂存区的变更
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index a72914f7..77a832ff 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -58,6 +58,7 @@
刪除所選的 {0} 個分支
捨棄所有變更
快轉 (fast-forward) 到 ${0}$
+ 提取 (fetch) ${0}$ 到 ${1}$...
Git 工作流 - 完成 ${0}$
合併 ${0}$ 到 ${1}$...
拉取 (pull) ${0}$
@@ -72,6 +73,7 @@
取 消
重設檔案為此版本
重設檔案到上一版本
+ 產生提交訊息
切換變更顯示模式
檔案名稱 + 路徑列表模式
全路徑列表模式
@@ -109,6 +111,7 @@
與本機工作區比較
複製摘要資訊
複製提交編號
+ 自訂動作
互動式重定基底 (rebase -i) ${0}$ 到此處
重定基底 (rebase) ${0}$ 到此處
重設 (reset) ${0}$ 到此處
@@ -140,6 +143,14 @@
提交訊息範本
範本名稱:
範本內容:
+ 自訂動作
+ 指令參數:
+ 使用 ${REPO} 表示存放庫路徑、${SHA} 表示所選的提交編號
+ 可執行檔案路徑:
+ 名稱:
+ 執行範圍:
+ 選取的提交
+ 存放庫
電子郵件
電子郵件地址
Git 設定
@@ -147,16 +158,20 @@
分鐘
預設遠端存放庫
提交訊息追加署名 (--signoff)
+ 拉取變更時進行清理 (--prune)
Issue 追蹤
新增符合 GitHub Issue 規則
新增符合 Jira 規則
新增符合 GitLab 議題規則
新增符合 GitLab 合併請求規則
新增自訂規則
- 符合 Issue 的正則表達式:
+ 符合 Issue 的正規表達式:
規則名稱:
為 Issue 產生的網址連結:
- 可在網址中使用 $1、$2 等變數填入正則表示式相符的內容
+ 可在網址中使用 $1、$2 等變數填入正規表達式相符的內容
+ OpenAI
+ 偏好服務:
+ 設定 [偏好服務] 後,SourceGit 將於此存放庫中使用該服務,否則會顯示 OpenAI 服務列表供使用者選擇。
HTTP 代理
HTTP 網路代理
使用者名稱
@@ -249,6 +264,8 @@
目標:
編輯群組
編輯存放庫
+ 執行自訂動作
+ 自訂動作:
快進 (fast-forward,無需 checkout)
提取 (fetch)
提取所有的遠端存放庫
@@ -404,11 +421,11 @@
一年前
{0} 年前
偏好設定
- 進階設定
OpenAI
伺服器
API 金鑰
模型
+ 名稱
分析變更差異提示詞
產生提交訊息提示詞
外觀設定
@@ -513,6 +530,8 @@
清空篩選規則
設定本存放庫
下一步
+ 自訂動作
+ 沒有自訂的動作
啟用 [--reflog] 選項
在檔案瀏覽器中開啟
快速搜尋分支/標籤/子模組
@@ -575,6 +594,7 @@
開 始
擱置變更 (stash)
包含未追蹤的檔案
+ 保留已暫存的變更
擱置變更訊息:
選填,用於命名此擱置變更
僅擱置已暫存的變更
diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml
index aa0cbbb8..6326023a 100644
--- a/src/Resources/Themes.axaml
+++ b/src/Resources/Themes.axaml
@@ -83,7 +83,7 @@
- fonts:Inter#Inter, $Default
+ fonts:Inter#Inter
fonts:SourceGit#JetBrains Mono
fonts:SourceGit#JetBrains Mono
diff --git a/src/ViewModels/AddRemote.cs b/src/ViewModels/AddRemote.cs
index d2a7729a..d6424572 100644
--- a/src/ViewModels/AddRemote.cs
+++ b/src/ViewModels/AddRemote.cs
@@ -100,7 +100,7 @@ namespace SourceGit.ViewModels
{
SetProgressDescription("Fetching from added remote ...");
new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", _useSSH ? SSHKey : null);
- new Commands.Fetch(_repo.FullPath, _name, false, SetProgressDescription).Exec();
+ new Commands.Fetch(_repo.FullPath, _name, false, false, SetProgressDescription).Exec();
}
CallUIThread(() =>
{
diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs
index 3692be10..1600c4cd 100644
--- a/src/ViewModels/CommitDetail.cs
+++ b/src/ViewModels/CommitDetail.cs
@@ -45,6 +45,12 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _fullMessage, value);
}
+ public Models.CommitSignInfo SignInfo
+ {
+ get => _signInfo;
+ private set => SetProperty(ref _signInfo, value);
+ }
+
public List Changes
{
get => _changes;
@@ -131,6 +137,7 @@ namespace SourceGit.ViewModels
_visibleChanges.Clear();
if (_selectedChanges != null)
_selectedChanges.Clear();
+ _signInfo = null;
_searchChangeFilter = null;
_diffContext = null;
_viewRevisionFileContent = null;
@@ -474,6 +481,7 @@ namespace SourceGit.ViewModels
{
_changes = null;
FullMessage = string.Empty;
+ SignInfo = null;
Changes = [];
VisibleChanges = null;
SelectedChanges = null;
@@ -488,6 +496,12 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(() => FullMessage = fullMessage);
});
+ Task.Run(() =>
+ {
+ var signInfo = new Commands.QueryCommitSignInfo(_repo.FullPath, _commit.SHA, !_repo.HasAllowedSignersFile).Result();
+ Dispatcher.UIThread.Invoke(() => SignInfo = signInfo);
+ });
+
if (_cancelToken != null)
_cancelToken.Requested = true;
@@ -637,6 +651,7 @@ namespace SourceGit.ViewModels
private int _activePageIndex = 0;
private Models.Commit _commit = null;
private string _fullMessage = string.Empty;
+ private Models.CommitSignInfo _signInfo = null;
private List _changes = null;
private List _visibleChanges = null;
private List _selectedChanges = null;
diff --git a/src/ViewModels/ExecuteCustomAction.cs b/src/ViewModels/ExecuteCustomAction.cs
new file mode 100644
index 00000000..920b9f43
--- /dev/null
+++ b/src/ViewModels/ExecuteCustomAction.cs
@@ -0,0 +1,40 @@
+using System.Threading.Tasks;
+
+namespace SourceGit.ViewModels
+{
+ public class ExecuteCustomAction : Popup
+ {
+ public Models.CustomAction CustomAction
+ {
+ get;
+ private set;
+ }
+
+ public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit)
+ {
+ _repo = repo;
+ _args = action.Arguments.Replace("${REPO}", _repo.FullPath);
+ if (commit != null)
+ _args = _args.Replace("${SHA}", commit.SHA);
+
+ CustomAction = action;
+ View = new Views.ExecuteCustomAction() { DataContext = this };
+ }
+
+ public override Task Sure()
+ {
+ _repo.SetWatcherEnabled(false);
+ ProgressDescription = "Run custom action ...";
+
+ return Task.Run(() =>
+ {
+ Commands.ExecuteCustomAction.Run(_repo.FullPath, CustomAction.Executable, _args, SetProgressDescription);
+ CallUIThread(() => _repo.SetWatcherEnabled(true));
+ return true;
+ });
+ }
+
+ private readonly Repository _repo = null;
+ private string _args = string.Empty;
+ }
+}
diff --git a/src/ViewModels/Fetch.cs b/src/ViewModels/Fetch.cs
index 3fe92a5f..7f54680d 100644
--- a/src/ViewModels/Fetch.cs
+++ b/src/ViewModels/Fetch.cs
@@ -40,6 +40,8 @@ namespace SourceGit.ViewModels
{
_repo.SetWatcherEnabled(false);
+ var notags = _repo.Settings.FetchWithoutTags;
+ var prune = _repo.Settings.EnablePruneOnFetch;
return Task.Run(() =>
{
if (FetchAllRemotes)
@@ -47,13 +49,13 @@ namespace SourceGit.ViewModels
foreach (var remote in _repo.Remotes)
{
SetProgressDescription($"Fetching remote: {remote.Name}");
- new Commands.Fetch(_repo.FullPath, remote.Name, NoTags, SetProgressDescription).Exec();
+ new Commands.Fetch(_repo.FullPath, remote.Name, notags, prune, SetProgressDescription).Exec();
}
}
else
{
SetProgressDescription($"Fetching remote: {SelectedRemote.Name}");
- new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, NoTags, SetProgressDescription).Exec();
+ new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, notags, prune, SetProgressDescription).Exec();
}
CallUIThread(() =>
diff --git a/src/ViewModels/FetchInto.cs b/src/ViewModels/FetchInto.cs
new file mode 100644
index 00000000..a52b1cd6
--- /dev/null
+++ b/src/ViewModels/FetchInto.cs
@@ -0,0 +1,42 @@
+using System.Threading.Tasks;
+
+namespace SourceGit.ViewModels
+{
+ public class FetchInto : Popup
+ {
+ public Models.Branch Local
+ {
+ get;
+ private set;
+ }
+
+ public Models.Branch Upstream
+ {
+ get;
+ private set;
+ }
+
+ public FetchInto(Repository repo, Models.Branch local, Models.Branch upstream)
+ {
+ _repo = repo;
+ Local = local;
+ Upstream = upstream;
+ View = new Views.FetchInto() { DataContext = this };
+ }
+
+ public override Task Sure()
+ {
+ _repo.SetWatcherEnabled(false);
+ ProgressDescription = "Fast-Forward ...";
+
+ return Task.Run(() =>
+ {
+ new Commands.Fetch(_repo.FullPath, Local, Upstream, SetProgressDescription).Exec();
+ CallUIThread(() => _repo.SetWatcherEnabled(true));
+ return true;
+ });
+ }
+
+ private readonly Repository _repo = null;
+ }
+}
diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs
index 179f6d97..713e1635 100644
--- a/src/ViewModels/Histories.cs
+++ b/src/ViewModels/Histories.cs
@@ -605,6 +605,39 @@ namespace SourceGit.ViewModels
menu.Items.Add(archive);
menu.Items.Add(new MenuItem() { Header = "-" });
+ var actions = new List();
+ foreach (var action in _repo.Settings.CustomActions)
+ {
+ if (action.Scope == Models.CustomActionScope.Commit)
+ actions.Add(action);
+ }
+ if (actions.Count > 0)
+ {
+ var custom = new MenuItem();
+ custom.Header = App.Text("CommitCM.CustomAction");
+ custom.Icon = App.CreateMenuIcon("Icons.Action");
+
+ foreach (var action in actions)
+ {
+ var dup = action;
+ var item = new MenuItem();
+ item.Icon = App.CreateMenuIcon("Icons.Action");
+ item.Header = dup.Name;
+ item.Click += (_, e) =>
+ {
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowAndStartPopup(new ExecuteCustomAction(_repo, dup, commit));
+
+ e.Handled = true;
+ };
+
+ custom.Items.Add(item);
+ }
+
+ menu.Items.Add(custom);
+ menu.Items.Add(new MenuItem() { Header = "-" });
+ }
+
var copySHA = new MenuItem();
copySHA.Header = App.Text("CommitCM.CopySHA");
copySHA.Icon = App.CreateMenuIcon("Icons.Copy");
diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs
index dd618e41..8e4f7775 100644
--- a/src/ViewModels/Launcher.cs
+++ b/src/ViewModels/Launcher.cs
@@ -204,12 +204,7 @@ namespace SourceGit.ViewModels
var activeIdx = Pages.IndexOf(_activePage);
if (removeIdx == activeIdx)
{
- ActivePage = Pages[removeIdx == Pages.Count - 1 ? removeIdx - 1 : removeIdx + 1];
- CloseRepositoryInTab(page);
- Pages.RemoveAt(removeIdx);
- }
- else if (removeIdx + 1 == activeIdx)
- {
+ ActivePage = Pages[removeIdx > 0 ? removeIdx - 1 : removeIdx + 1];
CloseRepositoryInTab(page);
Pages.RemoveAt(removeIdx);
}
diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs
index 72667f54..2741650c 100644
--- a/src/ViewModels/Preference.cs
+++ b/src/ViewModels/Preference.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
-
+using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@@ -25,7 +25,6 @@ namespace SourceGit.ViewModels
_instance.PrepareGit();
_instance.PrepareShellOrTerminal();
_instance.PrepareWorkspaces();
- _instance.PrepareOpenAIPrompt();
return _instance;
}
@@ -277,71 +276,6 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _externalMergeToolPath, value);
}
- public string OpenAIServer
- {
- get => Models.OpenAI.Server;
- set
- {
- if (value != Models.OpenAI.Server)
- {
- Models.OpenAI.Server = value;
- OnPropertyChanged();
- }
- }
- }
-
- public string OpenAIApiKey
- {
- get => Models.OpenAI.ApiKey;
- set
- {
- if (value != Models.OpenAI.ApiKey)
- {
- Models.OpenAI.ApiKey = value;
- OnPropertyChanged();
- }
- }
- }
-
- public string OpenAIModel
- {
- get => Models.OpenAI.Model;
- set
- {
- if (value != Models.OpenAI.Model)
- {
- Models.OpenAI.Model = value;
- OnPropertyChanged();
- }
- }
- }
-
- public string OpenAIAnalyzeDiffPrompt
- {
- get => Models.OpenAI.AnalyzeDiffPrompt;
- set
- {
- if (value != Models.OpenAI.AnalyzeDiffPrompt)
- {
- Models.OpenAI.AnalyzeDiffPrompt = value;
- OnPropertyChanged();
- }
- }
- }
-
- public string OpenAIGenerateSubjectPrompt
- {
- get => Models.OpenAI.GenerateSubjectPrompt;
- set
- {
- if (value != Models.OpenAI.GenerateSubjectPrompt)
- {
- Models.OpenAI.GenerateSubjectPrompt = value;
- OnPropertyChanged();
- }
- }
- }
-
public uint StatisticsSampleColor
{
get => _statisticsSampleColor;
@@ -360,6 +294,12 @@ namespace SourceGit.ViewModels
set;
} = [];
+ public AvaloniaList OpenAIServices
+ {
+ get;
+ set;
+ } = [];
+
public double LastCheckUpdateTime
{
get => _lastCheckUpdateTime;
@@ -554,45 +494,6 @@ namespace SourceGit.ViewModels
}
}
- private void PrepareOpenAIPrompt()
- {
- if (string.IsNullOrEmpty(Models.OpenAI.AnalyzeDiffPrompt))
- {
- Models.OpenAI.AnalyzeDiffPrompt = """
- You are an expert developer specialist in creating commits.
- Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
- - Do not use any code snippets, imports, file routes or bullets points.
- - Do not mention the route of file that has been change.
- - Write clear, concise, and descriptive messages that explain the MAIN GOAL made of the changes.
- - Use the present tense and active voice in the message, for example, "Fix bug" instead of "Fixed bug.".
- - Use the imperative mood, which gives the message a sense of command, e.g. "Add feature" instead of "Added feature".
- - Avoid using general terms like "update" or "change", be specific about what was updated or changed.
- - Avoid using terms like "The main goal of", just output directly the summary in plain text
- """;
- }
-
- if (string.IsNullOrEmpty(Models.OpenAI.GenerateSubjectPrompt))
- {
- Models.OpenAI.GenerateSubjectPrompt = """
- You are an expert developer specialist in creating commits messages.
- Your only goal is to retrieve a single commit message.
- Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
- - Assign the commit {type} according to the next conditions:
- feat: Only when adding a new feature.
- fix: When fixing a bug.
- docs: When updating documentation.
- style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
- test: When adding or updating tests.
- chore: When making changes to the build process or auxiliary tools and libraries.
- revert: When undoing a previous commit.
- refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
- - Do not add any issues numeration, explain your output nor introduce your answer.
- - Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
- - Be as concise as possible, keep the message under 50 characters.
- """;
- }
- }
-
private RepositoryNode FindNodeRecursive(string id, List collection)
{
foreach (var node in collection)
diff --git a/src/ViewModels/Pull.cs b/src/ViewModels/Pull.cs
index fdaad920..6c493449 100644
--- a/src/ViewModels/Pull.cs
+++ b/src/ViewModels/Pull.cs
@@ -147,9 +147,18 @@ namespace SourceGit.ViewModels
if (FetchAllBranches)
{
SetProgressDescription($"Fetching remote: {_selectedRemote.Name}...");
- rs = new Commands.Fetch(_repo.FullPath, _selectedRemote.Name, NoTags, SetProgressDescription).Exec();
+ rs = new Commands.Fetch(
+ _repo.FullPath,
+ _selectedRemote.Name,
+ NoTags,
+ _repo.Settings.EnablePruneOnFetch,
+ SetProgressDescription).Exec();
+
if (!rs)
+ {
+ CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
+ }
_repo.MarkFetched();
@@ -168,7 +177,14 @@ namespace SourceGit.ViewModels
else
{
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
- rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, NoTags, SetProgressDescription).Exec();
+ rs = new Commands.Pull(
+ _repo.FullPath,
+ _selectedRemote.Name,
+ _selectedBranch.Name,
+ UseRebase,
+ NoTags,
+ _repo.Settings.EnablePruneOnFetch,
+ SetProgressDescription).Exec();
}
if (rs && needPopStash)
diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs
index 8f6e1f88..9dd5ab9d 100644
--- a/src/ViewModels/Repository.cs
+++ b/src/ViewModels/Repository.cs
@@ -45,6 +45,11 @@ namespace SourceGit.ViewModels
get => _settings;
}
+ public bool HasAllowedSignersFile
+ {
+ get => _hasAllowedSignersFile;
+ }
+
public int SelectedViewIndex
{
get => _selectedViewIndex;
@@ -444,6 +449,12 @@ namespace SourceGit.ViewModels
public void RefreshAll()
{
+ Task.Run(() =>
+ {
+ var allowedSignersFile = new Commands.Config(_fullpath).Get("gpg.ssh.allowedSignersFile");
+ _hasAllowedSignersFile = !string.IsNullOrEmpty(allowedSignersFile);
+ });
+
Task.Run(() =>
{
RefreshBranches();
@@ -1276,6 +1287,45 @@ namespace SourceGit.ViewModels
return menu;
}
+ public ContextMenu CreateContextMenuForCustomAction()
+ {
+ var actions = new List();
+ foreach (var action in _settings.CustomActions)
+ {
+ if (action.Scope == Models.CustomActionScope.Repository)
+ actions.Add(action);
+ }
+
+ var menu = new ContextMenu();
+ menu.Placement = PlacementMode.BottomEdgeAlignedLeft;
+
+ if (actions.Count > 0)
+ {
+ foreach (var action in actions)
+ {
+ var dup = action;
+ var item = new MenuItem();
+ item.Icon = App.CreateMenuIcon("Icons.Action");
+ item.Header = dup.Name;
+ item.Click += (_, e) =>
+ {
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowAndStartPopup(new ExecuteCustomAction(this, dup, null));
+
+ e.Handled = true;
+ };
+
+ menu.Items.Add(item);
+ }
+ }
+ else
+ {
+ menu.Items.Add(new MenuItem() { Header = App.Text("Repository.CustomActions.Empty") });
+ }
+
+ return menu;
+ }
+
public ContextMenu CreateContextMenuForLocalBranch(Models.Branch branch)
{
var menu = new ContextMenu();
@@ -1354,6 +1404,7 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
menu.Items.Add(checkout);
+ menu.Items.Add(new MenuItem() { Header = "-" });
var worktree = _worktrees.Find(x => x.Branch == branch.FullName);
var upstream = _branches.Find(x => x.FullName == branch.Upstream);
@@ -1370,11 +1421,22 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
- menu.Items.Add(new MenuItem() { Header = "-" });
+ var fetchInto = new MenuItem();
+ fetchInto.Header = new Views.NameHighlightedTextBlock("BranchCM.FetchInto", upstream.FriendlyName, branch.Name);
+ fetchInto.Icon = App.CreateMenuIcon("Icons.Fetch");
+ fetchInto.IsEnabled = branch.TrackStatus.Ahead.Count == 0;
+ fetchInto.Click += (_, e) =>
+ {
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowAndStartPopup(new FetchInto(this, branch, upstream));
+ e.Handled = true;
+ };
+
menu.Items.Add(fastForward);
+ menu.Items.Add(new MenuItem() { Header = "-" });
+ menu.Items.Add(fetchInto);
}
- menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(push);
var merge = new MenuItem();
@@ -2114,7 +2176,7 @@ namespace SourceGit.ViewModels
IsAutoFetching = true;
Dispatcher.UIThread.Invoke(() => OnPropertyChanged(nameof(IsAutoFetching)));
- new Commands.Fetch(_fullpath, "--all", false, null) { RaiseError = false }.Exec();
+ new Commands.Fetch(_fullpath, "--all", false, _settings.EnablePruneOnFetch, null) { RaiseError = false }.Exec();
_lastFetchTime = DateTime.Now;
IsAutoFetching = false;
Dispatcher.UIThread.Invoke(() => OnPropertyChanged(nameof(IsAutoFetching)));
@@ -2123,6 +2185,7 @@ namespace SourceGit.ViewModels
private string _fullpath = string.Empty;
private string _gitDir = string.Empty;
private Models.RepositorySettings _settings = null;
+ private bool _hasAllowedSignersFile = false;
private Models.Watcher _watcher = null;
private Histories _histories = null;
diff --git a/src/ViewModels/RepositoryConfigure.cs b/src/ViewModels/RepositoryConfigure.cs
index 3c969c4d..620db074 100644
--- a/src/ViewModels/RepositoryConfigure.cs
+++ b/src/ViewModels/RepositoryConfigure.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -66,6 +67,12 @@ namespace SourceGit.ViewModels
set => _repo.Settings.EnableSignOffForCommit = value;
}
+ public bool EnablePruneOnFetch
+ {
+ get => _repo.Settings.EnablePruneOnFetch;
+ set => _repo.Settings.EnablePruneOnFetch = value;
+ }
+
public bool EnableAutoFetch
{
get => _repo.Settings.EnableAutoFetch;
@@ -108,6 +115,29 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _selectedIssueTrackerRule, value);
}
+ public List AvailableOpenAIServices
+ {
+ get;
+ private set;
+ }
+
+ public string PreferedOpenAIService
+ {
+ get => _repo.Settings.PreferedOpenAIService;
+ set => _repo.Settings.PreferedOpenAIService = value;
+ }
+
+ public AvaloniaList CustomActions
+ {
+ get => _repo.Settings.CustomActions;
+ }
+
+ public Models.CustomAction SelectedCustomAction
+ {
+ get => _selectedCustomAction;
+ set => SetProperty(ref _selectedCustomAction, value);
+ }
+
public RepositoryConfigure(Repository repo)
{
_repo = repo;
@@ -116,6 +146,13 @@ namespace SourceGit.ViewModels
foreach (var remote in _repo.Remotes)
Remotes.Add(remote.Name);
+ AvailableOpenAIServices = new List() { "---" };
+ foreach (var service in Preference.Instance.OpenAIServices)
+ AvailableOpenAIServices.Add(service.Name);
+
+ if (AvailableOpenAIServices.IndexOf(PreferedOpenAIService) == -1)
+ PreferedOpenAIService = "---";
+
_cached = new Commands.Config(repo.FullPath).ListAll();
if (_cached.TryGetValue("user.name", out var name))
UserName = name;
@@ -207,11 +244,21 @@ namespace SourceGit.ViewModels
public void RemoveSelectedIssueTracker()
{
- if (_selectedIssueTrackerRule != null)
- _repo.Settings.RemoveIssueTracker(_selectedIssueTrackerRule);
+ _repo.Settings.RemoveIssueTracker(_selectedIssueTrackerRule);
SelectedIssueTrackerRule = null;
}
+ public void AddNewCustomAction()
+ {
+ SelectedCustomAction = _repo.Settings.AddNewCustomAction();
+ }
+
+ public void RemoveSelectedCustomAction()
+ {
+ _repo.Settings.RemoveCustomAction(_selectedCustomAction);
+ SelectedCustomAction = null;
+ }
+
public void Save()
{
SetIfChanged("user.name", UserName, "");
@@ -245,5 +292,6 @@ namespace SourceGit.ViewModels
private string _httpProxy;
private Models.CommitTemplate _selectedCommitTemplate = null;
private Models.IssueTrackerRule _selectedIssueTrackerRule = null;
+ private Models.CustomAction _selectedCustomAction = null;
}
}
diff --git a/src/ViewModels/StashChanges.cs b/src/ViewModels/StashChanges.cs
index ed3d2bfd..1ad3a0da 100644
--- a/src/ViewModels/StashChanges.cs
+++ b/src/ViewModels/StashChanges.cs
@@ -18,24 +18,27 @@ namespace SourceGit.ViewModels
public bool IncludeUntracked
{
- get;
- set;
+ get => _repo.Settings.IncludeUntrackedWhenStash;
+ set => _repo.Settings.IncludeUntrackedWhenStash = value;
}
public bool OnlyStaged
{
- get;
- set;
+ get => _repo.Settings.OnlyStagedWhenStash;
+ set => _repo.Settings.OnlyStagedWhenStash = value;
+ }
+
+ public bool KeepIndex
+ {
+ get => _repo.Settings.KeepIndexWhenStash;
+ set => _repo.Settings.KeepIndexWhenStash = value;
}
public StashChanges(Repository repo, List changes, bool hasSelectedFiles)
{
_repo = repo;
_changes = changes;
-
HasSelectedFiles = hasSelectedFiles;
- IncludeUntracked = true;
- OnlyStaged = false;
View = new Views.StashChanges() { DataContext = this };
}
@@ -63,7 +66,7 @@ namespace SourceGit.ViewModels
return Task.Run(() =>
{
- var succ = new Commands.Stash(_repo.FullPath).Push(jobs, Message, !HasSelectedFiles && OnlyStaged);
+ var succ = new Commands.Stash(_repo.FullPath).Push(jobs, Message, !HasSelectedFiles && OnlyStaged, KeepIndex);
CallUIThread(() =>
{
_repo.MarkWorkingCopyDirtyManually();
diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs
index 6fcbedd9..020053f9 100644
--- a/src/ViewModels/WorkingCopy.cs
+++ b/src/ViewModels/WorkingCopy.cs
@@ -403,25 +403,6 @@ namespace SourceGit.ViewModels
}
}
- public void GenerateCommitMessageByAI()
- {
- if (!Models.OpenAI.IsValid)
- {
- App.RaiseException(_repo.FullPath, "Bad configuration for OpenAI");
- return;
- }
-
- if (_staged is { Count: > 0 })
- {
- var dialog = new Views.AIAssistant(_repo.FullPath, _staged, generated => CommitMessage = generated);
- App.OpenDialog(dialog);
- }
- else
- {
- App.RaiseException(_repo.FullPath, "No files added to commit!");
- }
- }
-
public void Commit()
{
DoCommit(false, false, false);
@@ -900,6 +881,44 @@ namespace SourceGit.ViewModels
return null;
var menu = new ContextMenu();
+
+ var ai = null as MenuItem;
+ var services = GetPreferedOpenAIServices();
+ if (services.Count > 0)
+ {
+ ai = new MenuItem();
+ ai.Icon = App.CreateMenuIcon("Icons.AIAssist");
+ ai.Header = App.Text("ChangeCM.GenerateCommitMessage");
+
+ if (services.Count == 1)
+ {
+ ai.Click += (_, e) =>
+ {
+ var dialog = new Views.AIAssistant(services[0], _repo.FullPath, _selectedStaged, generated => CommitMessage = generated);
+ App.OpenDialog(dialog);
+ e.Handled = true;
+ };
+ }
+ else
+ {
+ foreach (var service in services)
+ {
+ var dup = service;
+
+ var item = new MenuItem();
+ item.Header = service.Name;
+ item.Click += (_, e) =>
+ {
+ var dialog = new Views.AIAssistant(dup, _repo.FullPath, _selectedStaged, generated => CommitMessage = generated);
+ App.OpenDialog(dialog);
+ e.Handled = true;
+ };
+
+ ai.Items.Add(item);
+ }
+ }
+ }
+
if (_selectedStaged.Count == 1)
{
var change = _selectedStaged[0];
@@ -980,24 +999,6 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
- var copyPath = new MenuItem();
- copyPath.Header = App.Text("CopyPath");
- copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
- copyPath.Click += (_, e) =>
- {
- App.CopyText(change.Path);
- e.Handled = true;
- };
-
- var copyFileName = new MenuItem();
- copyFileName.Header = App.Text("CopyFileName");
- copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
- copyFileName.Click += (_, e) =>
- {
- App.CopyText(Path.GetFileName(change.Path));
- e.Handled = true;
- };
-
menu.Items.Add(explore);
menu.Items.Add(openWith);
menu.Items.Add(new MenuItem() { Header = "-" });
@@ -1089,6 +1090,30 @@ namespace SourceGit.ViewModels
menu.Items.Add(new MenuItem() { Header = "-" });
}
+ if (ai != null)
+ {
+ menu.Items.Add(ai);
+ menu.Items.Add(new MenuItem() { Header = "-" });
+ }
+
+ var copyPath = new MenuItem();
+ copyPath.Header = App.Text("CopyPath");
+ copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
+ copyPath.Click += (_, e) =>
+ {
+ App.CopyText(change.Path);
+ e.Handled = true;
+ };
+
+ var copyFileName = new MenuItem();
+ copyFileName.Header = App.Text("CopyFileName");
+ copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
+ copyFileName.Click += (_, e) =>
+ {
+ App.CopyText(Path.GetFileName(change.Path));
+ e.Handled = true;
+ };
+
menu.Items.Add(copyPath);
menu.Items.Add(copyFileName);
}
@@ -1142,6 +1167,12 @@ namespace SourceGit.ViewModels
menu.Items.Add(unstage);
menu.Items.Add(stash);
menu.Items.Add(patch);
+
+ if (ai != null)
+ {
+ menu.Items.Add(new MenuItem() { Header = "-" });
+ menu.Items.Add(ai);
+ }
}
return menu;
@@ -1211,6 +1242,51 @@ namespace SourceGit.ViewModels
return menu;
}
+ public ContextMenu CreateContextForOpenAI()
+ {
+ if (_staged == null || _staged.Count == 0)
+ {
+ App.RaiseException(_repo.FullPath, "No files added to commit!");
+ return null;
+ }
+
+ var services = GetPreferedOpenAIServices();
+ if (services.Count == 0)
+ {
+ App.RaiseException(_repo.FullPath, "Bad configuration for OpenAI");
+ return null;
+ }
+
+ if (services.Count == 1)
+ {
+ var dialog = new Views.AIAssistant(services[0], _repo.FullPath, _staged, generated => CommitMessage = generated);
+ App.OpenDialog(dialog);
+ return null;
+ }
+ else
+ {
+ var menu = new ContextMenu() { Placement = PlacementMode.TopEdgeAlignedLeft };
+
+ foreach (var service in services)
+ {
+ var dup = service;
+
+ var item = new MenuItem();
+ item.Header = service.Name;
+ item.Click += (_, e) =>
+ {
+ var dialog = new Views.AIAssistant(dup, _repo.FullPath, _staged, generated => CommitMessage = generated);
+ App.OpenDialog(dialog);
+ e.Handled = true;
+ };
+
+ menu.Items.Add(item);
+ }
+
+ return menu;
+ }
+ }
+
private List GetStagedChanges()
{
if (_useAmend)
@@ -1363,6 +1439,25 @@ namespace SourceGit.ViewModels
return false;
}
+ private IList GetPreferedOpenAIServices()
+ {
+ var services = Preference.Instance.OpenAIServices;
+ if (services == null || services.Count == 0)
+ return [];
+
+ if (services.Count == 1)
+ return services;
+
+ var prefered = _repo.Settings.PreferedOpenAIService;
+ foreach (var service in services)
+ {
+ if (service.Name.Equals(prefered, StringComparison.Ordinal))
+ return [service];
+ }
+
+ return services;
+ }
+
private Repository _repo = null;
private bool _isLoadingData = false;
private bool _isStaging = false;
diff --git a/src/Views/AIAssistant.axaml b/src/Views/AIAssistant.axaml
index 1c266715..528d0e5b 100644
--- a/src/Views/AIAssistant.axaml
+++ b/src/Views/AIAssistant.axaml
@@ -15,30 +15,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/AIAssistant.axaml.cs b/src/Views/AIAssistant.axaml.cs
index 6ceb5610..d81335eb 100644
--- a/src/Views/AIAssistant.axaml.cs
+++ b/src/Views/AIAssistant.axaml.cs
@@ -4,7 +4,6 @@ using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Threading;
namespace SourceGit.Views
@@ -17,12 +16,14 @@ namespace SourceGit.Views
InitializeComponent();
}
- public AIAssistant(string repo, List changes, Action onDone)
+ public AIAssistant(Models.OpenAIService service, string repo, List changes, Action onDone)
{
+ _service = service;
_repo = repo;
_changes = changes;
_onDone = onDone;
_cancel = new CancellationTokenSource();
+
InitializeComponent();
}
@@ -35,7 +36,7 @@ namespace SourceGit.Views
Task.Run(() =>
{
- var message = new Commands.GenerateCommitMessage(_repo, _changes, _cancel.Token, SetDescription).Result();
+ var message = new Commands.GenerateCommitMessage(_service, _repo, _changes, _cancel.Token, SetDescription).Result();
if (_cancel.IsCancellationRequested)
return;
@@ -53,16 +54,12 @@ namespace SourceGit.Views
_cancel.Cancel();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void SetDescription(string message)
{
Dispatcher.UIThread.Invoke(() => ProgressMessage.Text = message);
}
+ private Models.OpenAIService _service;
private string _repo;
private List _changes;
private Action _onDone;
diff --git a/src/Views/About.axaml b/src/Views/About.axaml
index 159a34a9..d781ee9e 100644
--- a/src/Views/About.axaml
+++ b/src/Views/About.axaml
@@ -13,30 +13,23 @@
WindowStartupLocation="CenterScreen">
-
-
+
-
-
-
-
-
diff --git a/src/Views/About.axaml.cs b/src/Views/About.axaml.cs
index 4f7f953f..b02cd13f 100644
--- a/src/Views/About.axaml.cs
+++ b/src/Views/About.axaml.cs
@@ -21,11 +21,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void OnVisitAvaloniaUI(object _, PointerPressedEventArgs e)
{
Native.OS.OpenBrowser("https://www.avaloniaui.net/");
diff --git a/src/Views/Askpass.axaml b/src/Views/Askpass.axaml
index e3e9a499..f2308805 100644
--- a/src/Views/Askpass.axaml
+++ b/src/Views/Askpass.axaml
@@ -13,30 +13,23 @@
WindowStartupLocation="CenterScreen">
-
-
+
-
-
-
-
-
@@ -49,10 +42,12 @@
+ HorizontalAlignment="Stretch"
+ v:AutoFocusBehaviour.IsEnabled="True">
-
-
+
-
-
-
-
-
diff --git a/src/Views/AssumeUnchangedManager.axaml.cs b/src/Views/AssumeUnchangedManager.axaml.cs
index ae9c7fe6..a0a5a352 100644
--- a/src/Views/AssumeUnchangedManager.axaml.cs
+++ b/src/Views/AssumeUnchangedManager.axaml.cs
@@ -1,5 +1,4 @@
using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -11,11 +10,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void OnRemoveButtonClicked(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.AssumeUnchangedManager vm && sender is Button button)
diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml
index 9cf8b78a..98e1c4f2 100644
--- a/src/Views/Blame.axaml
+++ b/src/Views/Blame.axaml
@@ -19,29 +19,26 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs
index f96870e0..164b89de 100644
--- a/src/Views/Blame.axaml.cs
+++ b/src/Views/Blame.axaml.cs
@@ -435,24 +435,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void MaximizeOrRestoreWindow(object _, TappedEventArgs e)
- {
- if (WindowState == WindowState.Maximized)
- WindowState = WindowState.Normal;
- else
- WindowState = WindowState.Maximized;
-
- e.Handled = true;
- }
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- if (e.ClickCount == 1)
- BeginMoveDrag(e);
-
- e.Handled = true;
- }
-
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
diff --git a/src/Views/BranchCompare.axaml b/src/Views/BranchCompare.axaml
index a8bbc4af..b0e227c2 100644
--- a/src/Views/BranchCompare.axaml
+++ b/src/Views/BranchCompare.axaml
@@ -21,29 +21,26 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs
index 844a1b6b..90ec1af5 100644
--- a/src/Views/BranchCompare.axaml.cs
+++ b/src/Views/BranchCompare.axaml.cs
@@ -10,24 +10,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void MaximizeOrRestoreWindow(object _, TappedEventArgs e)
- {
- if (WindowState == WindowState.Maximized)
- WindowState = WindowState.Normal;
- else
- WindowState = WindowState.Maximized;
-
- e.Handled = true;
- }
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- if (e.ClickCount == 1)
- BeginMoveDrag(e);
-
- e.Handled = true;
- }
-
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.BranchCompare vm && sender is ChangeCollectionView view)
diff --git a/src/Views/CaptionButtonsMacOS.axaml b/src/Views/CaptionButtonsMacOS.axaml
deleted file mode 100644
index 59c12e8d..00000000
--- a/src/Views/CaptionButtonsMacOS.axaml
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-
- #FFED6A5E
- #FF69110A
- #FFB24F46
- #FF2E0402
- #FFF4BF4F
- #FF8F591D
- #FFB78F3A
- #FF522A0A
- #FF61C554
- #FF296017
- #FF48943F
- #FF102F07
-
-
- #FFED6A5E
- #FF8C1A10
- #FFF09389
- #FF69120A
- #FFF4BF4F
- #FF8F591D
- #FFFBEB74
- #FF705F1B
- #FF61C554
- #FF296017
- #FF86F37F
- #FF2C681A
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Views/CaptionButtonsMacOS.axaml.cs b/src/Views/CaptionButtonsMacOS.axaml.cs
deleted file mode 100644
index 98bbb88f..00000000
--- a/src/Views/CaptionButtonsMacOS.axaml.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Interactivity;
-using Avalonia.VisualTree;
-
-namespace SourceGit.Views
-{
- public partial class CaptionButtonsMacOS : UserControl
- {
- public static readonly StyledProperty IsCloseButtonOnlyProperty =
- AvaloniaProperty.Register(nameof(IsCloseButtonOnly));
-
- public bool IsCloseButtonOnly
- {
- get => GetValue(IsCloseButtonOnlyProperty);
- set => SetValue(IsCloseButtonOnlyProperty, value);
- }
-
- public CaptionButtonsMacOS()
- {
- InitializeComponent();
- }
-
- private void MinimizeWindow(object _, RoutedEventArgs e)
- {
- var window = this.FindAncestorOfType();
- if (window != null)
- window.WindowState = WindowState.Minimized;
-
- e.Handled = true;
- }
-
- private void MaximizeOrRestoreWindow(object _, RoutedEventArgs e)
- {
- var window = this.FindAncestorOfType();
- if (window != null)
- window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
-
- e.Handled = true;
- }
-
- private void CloseWindow(object _, RoutedEventArgs e)
- {
- var window = this.FindAncestorOfType();
- window?.Close();
- e.Handled = true;
- }
- }
-}
diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs
index a9b9f259..107a7ba3 100644
--- a/src/Views/ChromelessWindow.cs
+++ b/src/Views/ChromelessWindow.cs
@@ -32,14 +32,35 @@ namespace SourceGit.Views
Classes.Add("custom_window_frame");
}
}
- else
+ else if (OperatingSystem.IsWindows())
{
ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
ExtendClientAreaToDecorationsHint = true;
-
- if (OperatingSystem.IsWindows())
- Classes.Add("fix_maximized_padding");
+ Classes.Add("fix_maximized_padding");
}
+ else
+ {
+ ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome;
+ ExtendClientAreaToDecorationsHint = true;
+ }
+ }
+
+ public void BeginMoveWindow(object _, PointerPressedEventArgs e)
+ {
+ if (e.ClickCount == 1)
+ BeginMoveDrag(e);
+
+ e.Handled = true;
+ }
+
+ public void MaximizeOrRestoreWindow(object _, TappedEventArgs e)
+ {
+ if (WindowState == WindowState.Maximized)
+ WindowState = WindowState.Normal;
+ else
+ WindowState = WindowState.Maximized;
+
+ e.Handled = true;
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml
index 623332c4..62117480 100644
--- a/src/Views/CommitBaseInfo.axaml
+++ b/src/Views/CommitBaseInfo.axaml
@@ -71,6 +71,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -105,7 +132,7 @@
VerticalAlignment="Center"
UseGraphColor="False"/>
-
+
SetValue(MessageProperty, value);
}
+ public static readonly StyledProperty SignInfoProperty =
+ AvaloniaProperty.Register(nameof(SignInfo));
+
+ public Models.CommitSignInfo SignInfo
+ {
+ get => GetValue(SignInfoProperty);
+ set => SetValue(SignInfoProperty, value);
+ }
+
public static readonly StyledProperty SupportsContainsInProperty =
AvaloniaProperty.Register(nameof(SupportsContainsIn));
diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml
index 8307d650..cb99b3d9 100644
--- a/src/Views/CommitDetail.axaml
+++ b/src/Views/CommitDetail.axaml
@@ -21,6 +21,7 @@
diff --git a/src/Views/ConfigureWorkspace.axaml b/src/Views/ConfigureWorkspace.axaml
index c7ed900b..9c18ad04 100644
--- a/src/Views/ConfigureWorkspace.axaml
+++ b/src/Views/ConfigureWorkspace.axaml
@@ -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.ConfigureWorkspace"
x:DataType="vm:ConfigureWorkspace"
@@ -17,30 +15,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/ConfigureWorkspace.axaml.cs b/src/Views/ConfigureWorkspace.axaml.cs
index 82d8cd30..e2cc1cb2 100644
--- a/src/Views/ConfigureWorkspace.axaml.cs
+++ b/src/Views/ConfigureWorkspace.axaml.cs
@@ -1,5 +1,4 @@
using Avalonia.Controls;
-using Avalonia.Input;
namespace SourceGit.Views
{
@@ -15,10 +14,5 @@ namespace SourceGit.Views
ViewModels.Preference.Instance.Save();
base.OnClosing(e);
}
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
}
}
diff --git a/src/Views/ConfirmCommitWithoutFiles.axaml b/src/Views/ConfirmCommitWithoutFiles.axaml
index 0b457531..a056f016 100644
--- a/src/Views/ConfirmCommitWithoutFiles.axaml
+++ b/src/Views/ConfirmCommitWithoutFiles.axaml
@@ -15,30 +15,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/ConfirmCommitWithoutFiles.axaml.cs b/src/Views/ConfirmCommitWithoutFiles.axaml.cs
index 0be18902..342600fc 100644
--- a/src/Views/ConfirmCommitWithoutFiles.axaml.cs
+++ b/src/Views/ConfirmCommitWithoutFiles.axaml.cs
@@ -1,4 +1,3 @@
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -10,11 +9,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void Sure(object _1, RoutedEventArgs _2)
{
if (DataContext is ViewModels.ConfirmCommitWithoutFiles vm)
diff --git a/src/Views/ConfirmRestart.axaml b/src/Views/ConfirmRestart.axaml
index 9aafda7e..de2ddd4f 100644
--- a/src/Views/ConfirmRestart.axaml
+++ b/src/Views/ConfirmRestart.axaml
@@ -13,30 +13,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/ConfirmRestart.axaml.cs b/src/Views/ConfirmRestart.axaml.cs
index d0647731..ea49bea1 100644
--- a/src/Views/ConfirmRestart.axaml.cs
+++ b/src/Views/ConfirmRestart.axaml.cs
@@ -1,8 +1,6 @@
using System;
using System.Diagnostics;
-using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -14,11 +12,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void CloseWindow(object _1, RoutedEventArgs _2)
{
Console.Out.WriteLine("No passphrase entered.");
diff --git a/src/Views/ConventionalCommitMessageBuilder.axaml b/src/Views/ConventionalCommitMessageBuilder.axaml
index ab2ae37f..4fe8b8f9 100644
--- a/src/Views/ConventionalCommitMessageBuilder.axaml
+++ b/src/Views/ConventionalCommitMessageBuilder.axaml
@@ -5,7 +5,6 @@
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="800" d:DesignHeight="450"
x:Class="SourceGit.Views.ConventionalCommitMessageBuilder"
x:DataType="vm:ConventionalCommitMessageBuilder"
@@ -18,30 +17,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/ConventionalCommitMessageBuilder.axaml.cs b/src/Views/ConventionalCommitMessageBuilder.axaml.cs
index dfe56f0d..955450ed 100644
--- a/src/Views/ConventionalCommitMessageBuilder.axaml.cs
+++ b/src/Views/ConventionalCommitMessageBuilder.axaml.cs
@@ -1,4 +1,3 @@
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -10,11 +9,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void OnApplyClicked(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.ConventionalCommitMessageBuilder builder)
diff --git a/src/Views/ExecuteCustomAction.axaml b/src/Views/ExecuteCustomAction.axaml
new file mode 100644
index 00000000..9ee2b55d
--- /dev/null
+++ b/src/Views/ExecuteCustomAction.axaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/ExecuteCustomAction.axaml.cs b/src/Views/ExecuteCustomAction.axaml.cs
new file mode 100644
index 00000000..e4f9cecf
--- /dev/null
+++ b/src/Views/ExecuteCustomAction.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace SourceGit.Views
+{
+ public partial class ExecuteCustomAction : UserControl
+ {
+ public ExecuteCustomAction()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/Views/FetchInto.axaml b/src/Views/FetchInto.axaml
new file mode 100644
index 00000000..4a0c0966
--- /dev/null
+++ b/src/Views/FetchInto.axaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/FetchInto.axaml.cs b/src/Views/FetchInto.axaml.cs
new file mode 100644
index 00000000..c61c052e
--- /dev/null
+++ b/src/Views/FetchInto.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace SourceGit.Views
+{
+ public partial class FetchInto : UserControl
+ {
+ public FetchInto()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml
index bc048706..703957b8 100644
--- a/src/Views/FileHistories.axaml
+++ b/src/Views/FileHistories.axaml
@@ -20,29 +20,26 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs
index 6a2ee7c9..9d74892b 100644
--- a/src/Views/FileHistories.axaml.cs
+++ b/src/Views/FileHistories.axaml.cs
@@ -11,24 +11,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void MaximizeOrRestoreWindow(object _, TappedEventArgs e)
- {
- if (WindowState == WindowState.Maximized)
- WindowState = WindowState.Normal;
- else
- WindowState = WindowState.Maximized;
-
- e.Handled = true;
- }
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- if (e.ClickCount == 1)
- BeginMoveDrag(e);
-
- e.Handled = true;
- }
-
private void OnPressCommitSHA(object sender, PointerPressedEventArgs e)
{
if (sender is TextBlock { DataContext: Models.Commit commit } &&
diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml
index f372d5d5..20f49d90 100644
--- a/src/Views/Histories.axaml
+++ b/src/Views/Histories.axaml
@@ -12,15 +12,15 @@
x:Name="ThisControl">
-
+
-
+
-
+
-
+
diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml
index 5d98238d..a28bc566 100644
--- a/src/Views/Hotkeys.axaml
+++ b/src/Views/Hotkeys.axaml
@@ -15,30 +15,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/Hotkeys.axaml.cs b/src/Views/Hotkeys.axaml.cs
index 2a21fef9..d8b5e1a8 100644
--- a/src/Views/Hotkeys.axaml.cs
+++ b/src/Views/Hotkeys.axaml.cs
@@ -1,5 +1,3 @@
-using Avalonia.Input;
-
namespace SourceGit.Views
{
public partial class Hotkeys : ChromelessWindow
@@ -8,10 +6,5 @@ namespace SourceGit.Views
{
InitializeComponent();
}
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
}
}
diff --git a/src/Views/InteractiveRebase.axaml b/src/Views/InteractiveRebase.axaml
index e1161a2b..c008193b 100644
--- a/src/Views/InteractiveRebase.axaml
+++ b/src/Views/InteractiveRebase.axaml
@@ -16,30 +16,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/InteractiveRebase.axaml.cs b/src/Views/InteractiveRebase.axaml.cs
index 8f75cc26..a31b8a23 100644
--- a/src/Views/InteractiveRebase.axaml.cs
+++ b/src/Views/InteractiveRebase.axaml.cs
@@ -81,11 +81,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void CloseWindow(object _1, RoutedEventArgs _2)
{
Close();
diff --git a/src/Views/LFSLocks.axaml b/src/Views/LFSLocks.axaml
index ccbe9fe2..ac495bf1 100644
--- a/src/Views/LFSLocks.axaml
+++ b/src/Views/LFSLocks.axaml
@@ -16,30 +16,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/LFSLocks.axaml.cs b/src/Views/LFSLocks.axaml.cs
index ee4b6ff1..695341f4 100644
--- a/src/Views/LFSLocks.axaml.cs
+++ b/src/Views/LFSLocks.axaml.cs
@@ -1,5 +1,4 @@
using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -11,11 +10,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void OnUnlockButtonClicked(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.LFSLocks vm && sender is Button button)
diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml
index 285ea72c..ad5a7f34 100644
--- a/src/Views/Launcher.axaml
+++ b/src/Views/Launcher.axaml
@@ -25,13 +25,11 @@
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/Preference.axaml.cs b/src/Views/Preference.axaml.cs
index 2f08e0db..e54d4c25 100644
--- a/src/Views/Preference.axaml.cs
+++ b/src/Views/Preference.axaml.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
@@ -74,6 +73,15 @@ namespace SourceGit.Views
set;
}
+ public static readonly StyledProperty SelectedOpenAIServiceProperty =
+ AvaloniaProperty.Register(nameof(SelectedOpenAIService));
+
+ public Models.OpenAIService SelectedOpenAIService
+ {
+ get => GetValue(SelectedOpenAIServiceProperty);
+ set => SetValue(SelectedOpenAIServiceProperty, value);
+ }
+
public Preference()
{
var pref = ViewModels.Preference.Instance;
@@ -157,11 +165,6 @@ namespace SourceGit.Views
base.OnClosing(e);
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private async void SelectThemeOverrideFile(object _, RoutedEventArgs e)
{
var options = new FilePickerOpenOptions()
@@ -312,5 +315,24 @@ namespace SourceGit.Views
e.Handled = true;
}
+
+ private void OnAddOpenAIService(object sender, RoutedEventArgs e)
+ {
+ var service = new Models.OpenAIService() { Name = "Unnamed Service" };
+ ViewModels.Preference.Instance.OpenAIServices.Add(service);
+ SelectedOpenAIService = service;
+
+ e.Handled = true;
+ }
+
+ private void OnRemoveSelectedOpenAIService(object sender, RoutedEventArgs e)
+ {
+ if (SelectedOpenAIService == null)
+ return;
+
+ ViewModels.Preference.Instance.OpenAIServices.Remove(SelectedOpenAIService);
+ SelectedOpenAIService = null;
+ e.Handled = true;
+ }
}
}
diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml
index 6383d904..f1deca3a 100644
--- a/src/Views/RepositoryConfigure.axaml
+++ b/src/Views/RepositoryConfigure.axaml
@@ -5,6 +5,7 @@
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
+ xmlns:ac="using:Avalonia.Controls.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RepositoryConfigure"
x:DataType="vm:RepositoryConfigure"
@@ -16,30 +17,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
@@ -51,7 +45,7 @@
-
+
-
+
+
+
@@ -340,6 +338,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/RepositoryConfigure.axaml.cs b/src/Views/RepositoryConfigure.axaml.cs
index 7e559cc2..3faba5ee 100644
--- a/src/Views/RepositoryConfigure.axaml.cs
+++ b/src/Views/RepositoryConfigure.axaml.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls;
-using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Platform.Storage;
namespace SourceGit.Views
{
@@ -16,9 +17,19 @@ namespace SourceGit.Views
base.OnClosing(e);
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
+ private async void SelectExecutableForCustomAction(object sender, RoutedEventArgs e)
{
- BeginMoveDrag(e);
+ var options = new FilePickerOpenOptions()
+ {
+ FileTypeFilter = [new FilePickerFileType("Executable file(script)") { Patterns = ["*.*"] }],
+ AllowMultiple = false,
+ };
+
+ var selected = await StorageProvider.OpenFilePickerAsync(options);
+ if (selected.Count == 1 && sender is Button { DataContext: Models.CustomAction action })
+ action.Executable = selected[0].Path.LocalPath;
+
+ e.Handled = true;
}
}
}
diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml
index b76cfd63..c1eec786 100644
--- a/src/Views/RepositoryToolbar.axaml
+++ b/src/Views/RepositoryToolbar.axaml
@@ -96,6 +96,10 @@
+
+
+
+
diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs
index 27ac43cd..55132620 100644
--- a/src/Views/RepositoryToolbar.axaml.cs
+++ b/src/Views/RepositoryToolbar.axaml.cs
@@ -91,6 +91,17 @@ namespace SourceGit.Views
e.Handled = true;
}
+
+ private void OpenCustomActionMenu(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.Repository repo)
+ {
+ var menu = repo.CreateContextMenuForCustomAction();
+ (sender as Control)?.OpenContextMenu(menu);
+ }
+
+ e.Handled = true;
+ }
}
}
diff --git a/src/Views/SelfUpdate.axaml b/src/Views/SelfUpdate.axaml
index a8eb152c..6fd2c415 100644
--- a/src/Views/SelfUpdate.axaml
+++ b/src/Views/SelfUpdate.axaml
@@ -17,30 +17,23 @@
WindowStartupLocation="CenterOwner">
-
-
+
-
-
-
-
-
diff --git a/src/Views/SelfUpdate.axaml.cs b/src/Views/SelfUpdate.axaml.cs
index 800d9295..cdaac940 100644
--- a/src/Views/SelfUpdate.axaml.cs
+++ b/src/Views/SelfUpdate.axaml.cs
@@ -1,5 +1,4 @@
using Avalonia.Controls;
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -11,11 +10,6 @@ namespace SourceGit.Views
InitializeComponent();
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void CloseWindow(object _1, RoutedEventArgs _2)
{
Close();
diff --git a/src/Views/StandaloneCommitMessageEditor.axaml b/src/Views/StandaloneCommitMessageEditor.axaml
index f59d3e84..8a0dc91a 100644
--- a/src/Views/StandaloneCommitMessageEditor.axaml
+++ b/src/Views/StandaloneCommitMessageEditor.axaml
@@ -14,30 +14,23 @@
WindowStartupLocation="CenterScreen">
-
-
+
-
-
-
-
-
diff --git a/src/Views/StandaloneCommitMessageEditor.axaml.cs b/src/Views/StandaloneCommitMessageEditor.axaml.cs
index 08b0d6f4..6833fb33 100644
--- a/src/Views/StandaloneCommitMessageEditor.axaml.cs
+++ b/src/Views/StandaloneCommitMessageEditor.axaml.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
-using Avalonia.Input;
using Avalonia.Interactivity;
namespace SourceGit.Views
@@ -40,11 +39,6 @@ namespace SourceGit.Views
App.Quit(_exitCode);
}
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
-
private void SaveAndClose(object _1, RoutedEventArgs _2)
{
File.WriteAllText(_file, Editor.Text);
diff --git a/src/Views/StashChanges.axaml b/src/Views/StashChanges.axaml
index 5396da4d..b3e6254a 100644
--- a/src/Views/StashChanges.axaml
+++ b/src/Views/StashChanges.axaml
@@ -11,7 +11,7 @@
-
+
-
-
+
+
+
+
diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml
index b3e52771..87847c06 100644
--- a/src/Views/StashesPage.axaml
+++ b/src/Views/StashesPage.axaml
@@ -9,11 +9,11 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.StashesPage"
x:DataType="vm:StashesPage">
-
+
-
+
diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs
index 60e8f0c1..f3048889 100644
--- a/src/Views/StashesPage.axaml.cs
+++ b/src/Views/StashesPage.axaml.cs
@@ -9,6 +9,20 @@ namespace SourceGit.Views
InitializeComponent();
}
+ private void OnMainLayoutSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ var grid = sender as Grid;
+ if (grid == null)
+ return;
+
+ var layout = ViewModels.Preference.Instance.Layout;
+ var width = grid.Bounds.Width;
+ var maxLeft = width - 304;
+
+ if (layout.StashesLeftWidth.Value - maxLeft > 1.0)
+ layout.StashesLeftWidth = new GridLength(maxLeft, GridUnitType.Pixel);
+ }
+
private void OnStashContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.StashesPage vm && sender is Border border)
diff --git a/src/Views/Statistics.axaml b/src/Views/Statistics.axaml
index 6e2a00dd..0c002407 100644
--- a/src/Views/Statistics.axaml
+++ b/src/Views/Statistics.axaml
@@ -16,30 +16,23 @@
CanResize="False">
-
-
+
-
-
-
-
-
diff --git a/src/Views/Statistics.axaml.cs b/src/Views/Statistics.axaml.cs
index 3c5e70b6..4ebf9016 100644
--- a/src/Views/Statistics.axaml.cs
+++ b/src/Views/Statistics.axaml.cs
@@ -1,5 +1,3 @@
-using Avalonia.Input;
-
namespace SourceGit.Views
{
public partial class Statistics : ChromelessWindow
@@ -8,10 +6,5 @@ namespace SourceGit.Views
{
InitializeComponent();
}
-
- private void BeginMoveWindow(object _, PointerPressedEventArgs e)
- {
- BeginMoveDrag(e);
- }
}
}
diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml
index 3d080fc6..4550e46a 100644
--- a/src/Views/WorkingCopy.axaml
+++ b/src/Views/WorkingCopy.axaml
@@ -8,116 +8,134 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
x:Class="SourceGit.Views.WorkingCopy"
x:DataType="vm:WorkingCopy">
-
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs
index 2b01b861..f64e1a30 100644
--- a/src/Views/WorkingCopy.axaml.cs
+++ b/src/Views/WorkingCopy.axaml.cs
@@ -11,6 +11,20 @@ namespace SourceGit.Views
InitializeComponent();
}
+ private void OnMainLayoutSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ var grid = sender as Grid;
+ if (grid == null)
+ return;
+
+ var layout = ViewModels.Preference.Instance.Layout;
+ var width = grid.Bounds.Width;
+ var maxLeft = width - 304;
+
+ if (layout.WorkingCopyLeftWidth.Value - maxLeft > 1.0)
+ layout.WorkingCopyLeftWidth = new GridLength(maxLeft, GridUnitType.Pixel);
+ }
+
private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
{
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
@@ -120,6 +134,17 @@ namespace SourceGit.Views
e.Handled = true;
}
+ private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.WorkingCopy vm)
+ {
+ var menu = vm.CreateContextForOpenAI();
+ (sender as Button)?.OpenContextMenu(menu);
+ }
+
+ e.Handled = true;
+ }
+
private void OnOpenConventionalCommitHelper(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)