Merge branch 'release/v2026.07'

This commit is contained in:
leo
2026-03-30 10:32:50 +08:00
105 changed files with 1673 additions and 1141 deletions

View File

@@ -39,11 +39,11 @@ jobs:
apt-get install -y sudo
sudo apt-get install -y curl wget git unzip zip libicu66 tzdata clang
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: true
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Configure arm64 packages
@@ -75,7 +75,7 @@ jobs:
rm -r publish/*
mv "sourcegit.${{ matrix.runtime }}.tar" publish
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: sourcegit.${{ matrix.runtime }}
path: publish/*

View File

@@ -17,7 +17,7 @@ jobs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Output version string
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"

View File

@@ -13,12 +13,12 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: true
- name: Set up .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

View File

@@ -13,12 +13,12 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: '24.x'
- name: Install dependencies
run: npm install fs-extra@11.2.0 path@0.12.7 xml2js@0.6.2

View File

@@ -15,9 +15,9 @@ jobs:
runtime: [win-x64, win-arm64]
steps:
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Download build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: sourcegit.${{ matrix.runtime }}
path: build/SourceGit
@@ -28,12 +28,12 @@ jobs:
RUNTIME: ${{ matrix.runtime }}
run: ./build/scripts/package.win.ps1
- name: Upload package artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: package.${{ matrix.runtime }}
path: build/sourcegit_*.zip
- name: Delete temp artifacts
uses: geekyeggo/delete-artifact@v5
uses: geekyeggo/delete-artifact@v6
with:
name: sourcegit.${{ matrix.runtime }}
osx-app:
@@ -44,9 +44,9 @@ jobs:
runtime: [osx-x64, osx-arm64]
steps:
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Download build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: sourcegit.${{ matrix.runtime }}
path: build
@@ -59,12 +59,12 @@ jobs:
tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit
./build/scripts/package.osx-app.sh
- name: Upload package artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: package.${{ matrix.runtime }}
path: build/sourcegit_*.zip
- name: Delete temp artifacts
uses: geekyeggo/delete-artifact@v5
uses: geekyeggo/delete-artifact@v6
with:
name: sourcegit.${{ matrix.runtime }}
linux:
@@ -76,7 +76,7 @@ jobs:
runtime: [linux-x64, linux-arm64]
steps:
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Download package dependencies
run: |
export DEBIAN_FRONTEND=noninteractive
@@ -84,7 +84,7 @@ jobs:
apt-get update
apt-get install -y curl wget git dpkg-dev fakeroot tzdata zip unzip desktop-file-utils rpm libfuse2 file build-essential binutils
- name: Download build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: sourcegit.${{ matrix.runtime }}
path: build
@@ -98,7 +98,7 @@ jobs:
tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit
./build/scripts/package.linux.sh
- name: Upload package artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: package.${{ matrix.runtime }}
path: |
@@ -106,6 +106,6 @@ jobs:
build/sourcegit_*.deb
build/sourcegit-*.rpm
- name: Delete temp artifacts
uses: geekyeggo/delete-artifact@v5
uses: geekyeggo/delete-artifact@v6
with:
name: sourcegit.${{ matrix.runtime }}

View File

@@ -32,7 +32,7 @@ jobs:
contents: write
steps:
- name: Checkout sources
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Create release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -40,7 +40,7 @@ jobs:
VERSION: ${{ needs.version.outputs.version }}
run: gh release create "$TAG" -t "$VERSION" --notes-from-tag
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
pattern: package.*
path: packages

View File

@@ -58,7 +58,7 @@
* Workspace
* Custom Action
* Create PR on GitHub/Gitlab/Gitea/Gitee/Bitbucket...
* Using AI to generate commit message (C# port of [anjerodev/commitollama](https://github.com/anjerodev/commitollama))
* Using AI to generate commit message
* Built-in conventional commit message helper.
> [!WARNING]

View File

@@ -36,14 +36,14 @@ The project uses the following third-party libraries or assets
### OpenAI .NET SDK
- **Source**: https://github.com/openai/openai-dotnet
- **Version**: 2.8.0
- **Version**: 2.9.1
- **License**: MIT License
- **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE
### Azure.AI.OpenAI
- **Source**: https://github.com/Azure/azure-sdk-for-net
- **Version**: 2.8.0-beta.1
- **Version**: 2.9.0-beta.1
- **License**: MIT License
- **License Link**: https://github.com/Azure/azure-sdk-for-net/blob/main/LICENSE.txt

View File

@@ -6,11 +6,13 @@ This document shows the translation status of each locale file in the repository
### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)
### ![de__DE](https://img.shields.io/badge/de__DE-98.46%25-yellow)
### ![de__DE](https://img.shields.io/badge/de__DE-98.04%25-yellow)
<details>
<summary>Missing keys in de_DE.axaml</summary>
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -18,9 +20,11 @@ This document shows the translation status of each locale file in the repository
- Text.CommitMessageTextBox.Column
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.GotoRevisionSelector
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.StashCM.ApplyFileChanges
- Text.Worktree.Branch
@@ -29,21 +33,16 @@ This document shows the translation status of each locale file in the repository
</details>
### ![es__ES](https://img.shields.io/badge/es__ES-99.90%25-yellow)
### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)
<details>
<summary>Missing keys in es_ES.axaml</summary>
- Text.Preferences.General.Use24Hours
</details>
### ![fr__FR](https://img.shields.io/badge/fr__FR-92.28%25-yellow)
### ![fr__FR](https://img.shields.io/badge/fr__FR-91.84%25-yellow)
<details>
<summary>Missing keys in fr_FR.axaml</summary>
- Text.About.ReleaseDate
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.Blame.IgnoreWhitespace
- Text.BranchCM.CompareTwo
- Text.BranchCM.CompareWith
@@ -70,6 +69,7 @@ This document shows the translation status of each locale file in the repository
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Init.CommandTip
@@ -94,6 +94,7 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.DiffMerge.DiffArgs
- Text.Preferences.DiffMerge.DiffArgs.Tip
- Text.Preferences.DiffMerge.MergeArgs
@@ -121,13 +122,15 @@ This document shows the translation status of each locale file in the repository
</details>
### ![id__ID](https://img.shields.io/badge/id__ID-90.22%25-yellow)
### ![id__ID](https://img.shields.io/badge/id__ID-89.67%25-yellow)
<details>
<summary>Missing keys in id_ID.axaml</summary>
- Text.About.ReleaseDate
- Text.About.ReleaseNotes
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.Blame.IgnoreWhitespace
- Text.BranchCM.CompareTwo
@@ -153,6 +156,7 @@ This document shows the translation status of each locale file in the repository
- Text.Configure.Git.ConventionalTypesOverride
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.DropHead
- Text.DropHead.Commit
- Text.DropHead.NewHead
@@ -166,6 +170,7 @@ This document shows the translation status of each locale file in the repository
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Hotkeys.Repo.OpenCommandPalette
@@ -195,6 +200,7 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.DiffMerge.DiffArgs
- Text.Preferences.DiffMerge.DiffArgs.Tip
- Text.Preferences.DiffMerge.MergeArgs
@@ -224,11 +230,13 @@ This document shows the translation status of each locale file in the repository
</details>
### ![it__IT](https://img.shields.io/badge/it__IT-97.84%25-yellow)
### ![it__IT](https://img.shields.io/badge/it__IT-97.42%25-yellow)
<details>
<summary>Missing keys in it_IT.axaml</summary>
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.ChangeCM.ResetFileTo
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
@@ -239,10 +247,12 @@ This document shows the translation status of each locale file in the repository
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.SelfUpdate.CurrentVersion
- Text.SelfUpdate.ReleaseDate
@@ -253,18 +263,23 @@ This document shows the translation status of each locale file in the repository
</details>
### ![ja__JP](https://img.shields.io/badge/ja__JP-98.87%25-yellow)
### ![ja__JP](https://img.shields.io/badge/ja__JP-98.35%25-yellow)
<details>
<summary>Missing keys in ja_JP.axaml</summary>
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
- Text.CommandPalette.RevisionFiles
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.Hotkeys.Repo.CreateBranch
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.Worktree.Branch
- Text.Worktree.Head
@@ -272,12 +287,14 @@ This document shows the translation status of each locale file in the repository
</details>
### ![ko__KR](https://img.shields.io/badge/ko__KR-90.53%25-yellow)
### ![ko__KR](https://img.shields.io/badge/ko__KR-89.98%25-yellow)
<details>
<summary>Missing keys in ko_KR.axaml</summary>
- Text.About.ReleaseDate
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.Blame.IgnoreWhitespace
- Text.Blame.TypeNotSupported
@@ -302,6 +319,7 @@ This document shows the translation status of each locale file in the repository
- Text.Configure.Git.ConventionalTypesOverride
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.EditBranchDescription
- Text.EditBranchDescription.Target
- Text.FileCM.CustomAction
@@ -312,6 +330,7 @@ This document shows the translation status of each locale file in the repository
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Hotkeys.Repo.OpenCommandPalette
@@ -341,6 +360,7 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.Appearance.UseFixedTabWidth
- Text.Preferences.DiffMerge.DiffArgs
- Text.Preferences.DiffMerge.DiffArgs.Tip
@@ -372,11 +392,13 @@ This document shows the translation status of each locale file in the repository
</details>
### ![pt__BR](https://img.shields.io/badge/pt__BR-68.49%25-red)
### ![pt__BR](https://img.shields.io/badge/pt__BR-68.08%25-red)
<details>
<summary>Missing keys in pt_BR.axaml</summary>
- Text.AIAssistant.Use
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.BranchCM.InteractiveRebase.Manually
- Text.BranchTree.AheadBehind
@@ -509,6 +531,7 @@ This document shows the translation status of each locale file in the repository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Hotkeys.Repo.OpenCommandPalette
@@ -555,8 +578,8 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.AI.ReadApiKeyFromEnv
- Text.Preferences.AI.Streaming
- Text.Preferences.Appearance.EditorTabWidth
- Text.Preferences.Appearance.UseAutoHideScrollBars
- Text.Preferences.DiffMerge.DiffArgs
@@ -686,23 +709,9 @@ This document shows the translation status of each locale file in the repository
</details>
### ![ru__RU](https://img.shields.io/badge/ru__RU-99.18%25-yellow)
### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)
<details>
<summary>Missing keys in ru_RU.axaml</summary>
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
- Text.CommandPalette.RevisionFiles
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.Preferences.General.Use24Hours
</details>
### ![ta__IN](https://img.shields.io/badge/ta__IN-70.75%25-red)
### ![ta__IN](https://img.shields.io/badge/ta__IN-70.14%25-red)
<details>
<summary>Missing keys in ta_IN.axaml</summary>
@@ -712,8 +721,10 @@ This document shows the translation status of each locale file in the repository
- Text.AddToIgnore
- Text.AddToIgnore.Pattern
- Text.AddToIgnore.Storage
- Text.AIAssistant.Use
- Text.App.Hide
- Text.App.ShowAll
- Text.Apply.3Way
- Text.Askpass.Passphrase
- Text.Avatar.Load
- Text.Bisect
@@ -814,6 +825,7 @@ This document shows the translation status of each locale file in the repository
- Text.ConfirmRestart.Title
- Text.ConfirmRestart.Message
- Text.CreateBranch.OverwriteExisting
- Text.DealWithLocalChanges.DoNothing
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
@@ -850,6 +862,7 @@ This document shows the translation status of each locale file in the repository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Hotkeys.Repo.OpenCommandPalette
@@ -887,6 +900,7 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.AI.ReadApiKeyFromEnv
- Text.Preferences.Appearance.UseAutoHideScrollBars
- Text.Preferences.DiffMerge.DiffArgs
@@ -994,7 +1008,7 @@ This document shows the translation status of each locale file in the repository
</details>
### ![uk__UA](https://img.shields.io/badge/uk__UA-71.58%25-red)
### ![uk__UA](https://img.shields.io/badge/uk__UA-70.97%25-red)
<details>
<summary>Missing keys in uk_UA.axaml</summary>
@@ -1004,8 +1018,10 @@ This document shows the translation status of each locale file in the repository
- Text.AddToIgnore
- Text.AddToIgnore.Pattern
- Text.AddToIgnore.Storage
- Text.AIAssistant.Use
- Text.App.Hide
- Text.App.ShowAll
- Text.Apply.3Way
- Text.Askpass.Passphrase
- Text.Avatar.Load
- Text.Bisect
@@ -1102,6 +1118,7 @@ This document shows the translation status of each locale file in the repository
- Text.ConfirmRestart.Title
- Text.ConfirmRestart.Message
- Text.CreateBranch.OverwriteExisting
- Text.DealWithLocalChanges.DoNothing
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
@@ -1138,6 +1155,7 @@ This document shows the translation status of each locale file in the repository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Hotkeys.Repo.OpenCommandPalette
@@ -1175,6 +1193,7 @@ This document shows the translation status of each locale file in the repository
- Text.OpenFile
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.AI.ReadApiKeyFromEnv
- Text.Preferences.Appearance.UseAutoHideScrollBars
- Text.Preferences.DiffMerge.DiffArgs

View File

@@ -1 +1 @@
2026.06
2026.07

88
src/AI/Agent.cs Normal file
View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.OpenAI;
using OpenAI;
using OpenAI.Chat;
namespace SourceGit.AI
{
public class Agent
{
public Agent(Service service)
{
_service = service;
}
public async Task GenerateCommitMessageAsync(string repo, string changeList, Action<string> onUpdate, CancellationToken cancellation)
{
var endPoint = new Uri(_service.Server);
var client = _service.Server.Contains("openai.azure.com/", StringComparison.Ordinal)
? new AzureOpenAIClient(endPoint, _service.Credential)
: new OpenAIClient(_service.Credential, new() { Endpoint = endPoint });
var chatClient = client.GetChatClient(_service.Model);
var options = new ChatCompletionOptions() { Tools = { ChatTools.GetDetailChangesInFile } };
var userMessageBuilder = new StringBuilder();
userMessageBuilder
.AppendLine("Generate a commit message (follow the rule of conventional commit message) for given git repository.")
.AppendLine("- Read all given changed files before generating. Only binary files (such as images, audios ...) can be skipped.")
.AppendLine("- Output the conventional commit message (with detail changes in list) directly. Do not explain your output nor introduce your answer.")
.AppendLine(string.IsNullOrEmpty(_service.AdditionalPrompt) ? string.Empty : _service.AdditionalPrompt)
.Append("Reposiory path: ").AppendLine(repo.Quoted())
.AppendLine("Changed files ('A' means added, 'M' means modified, 'D' means deleted, 'T' means type changed, 'R' means renamed, 'C' means copied): ")
.Append(changeList);
var messages = new List<ChatMessage>() { new UserChatMessage(userMessageBuilder.ToString()) };
do
{
ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options, cancellation);
var inProgress = false;
switch (completion.FinishReason)
{
case ChatFinishReason.Stop:
onUpdate?.Invoke(string.Empty);
onUpdate?.Invoke("# Assistant");
if (completion.Content.Count > 0)
onUpdate?.Invoke(completion.Content[0].Text);
else
onUpdate?.Invoke("[No content was generated.]");
onUpdate?.Invoke(string.Empty);
onUpdate?.Invoke("# Token Usage");
onUpdate?.Invoke($"Total: {completion.Usage.TotalTokenCount}. Input: {completion.Usage.InputTokenCount}. Output: {completion.Usage.OutputTokenCount}");
break;
case ChatFinishReason.Length:
throw new Exception("The response was cut off because it reached the maximum length. Consider increasing the max tokens limit.");
case ChatFinishReason.ToolCalls:
{
messages.Add(new AssistantChatMessage(completion));
foreach (var call in completion.ToolCalls)
{
var result = await ChatTools.Process(call, onUpdate);
messages.Add(result);
}
inProgress = true;
break;
}
case ChatFinishReason.ContentFilter:
throw new Exception("Ommitted content due to a content filter flag");
default:
break;
}
if (!inProgress)
break;
} while (true);
}
private readonly Service _service;
}
}

60
src/AI/ChatTools.cs Normal file
View File

@@ -0,0 +1,60 @@
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using OpenAI.Chat;
namespace SourceGit.AI
{
public static class ChatTools
{
public static readonly ChatTool GetDetailChangesInFile = ChatTool.CreateFunctionTool(
"GetDetailChangesInFile",
"Get the detailed changes in the specified file in the specified repository.",
BinaryData.FromBytes(Encoding.UTF8.GetBytes("""
{
"type": "object",
"properties": {
"repo": {
"type": "string",
"description": "The path to the repository."
},
"file": {
"type": "string",
"description": "The path to the file."
},
"originalFile": {
"type": "string",
"description": "The path to the original file when it has been renamed or copied."
}
},
"required": ["repo", "file"]
}
""")), false);
public static async Task<ToolChatMessage> Process(ChatToolCall call, Action<string> output)
{
using var doc = JsonDocument.Parse(call.FunctionArguments);
if (call.FunctionName.Equals(GetDetailChangesInFile.FunctionName))
{
var hasRepo = doc.RootElement.TryGetProperty("repo", out var repoPath);
var hasFile = doc.RootElement.TryGetProperty("file", out var filePath);
var hasOriginalFile = doc.RootElement.TryGetProperty("originalFile", out var originalFilePath);
if (!hasRepo)
throw new ArgumentException("repo", "The repo argument is required");
if (!hasFile)
throw new ArgumentException("file", "The file argument is required");
output?.Invoke($"Read changes in file: {filePath.GetString()}");
var orgFilePath = hasOriginalFile ? originalFilePath.GetString() : string.Empty;
var rs = await new Commands.GetFileChangeForAI(repoPath.GetString(), filePath.GetString(), orgFilePath).ReadAsync();
var message = rs.IsSuccess ? rs.StdOut : string.Empty;
return new ToolChatMessage(call.Id, message);
}
throw new NotSupportedException($"The tool {call.FunctionName} is not supported");
}
}
}

16
src/AI/Service.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.ClientModel;
namespace SourceGit.AI
{
public class Service
{
public string Name { get; set; }
public string Server { get; set; }
public string Model { get; set; }
public string ApiKey { get; set; }
public bool ReadApiKeyFromEnv { get; set; }
public string AdditionalPrompt { get; set; }
public ApiKeyCredential Credential => new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey);
}
}

View File

@@ -6,7 +6,6 @@ using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -286,6 +285,8 @@ namespace SourceGit
else
Models.CommitGraph.SetDefaultPens(overrides.GraphPenThickness);
Native.OS.UseMicaOnWindows11 = overrides.UseMicaOnWindows11;
app.Resources.MergedDictionaries.Add(resDic);
app._themeOverrides = resDic;
}
@@ -486,23 +487,7 @@ namespace SourceGit
using var stream = File.OpenRead(jobsFile);
var collection = JsonSerializer.Deserialize(stream, JsonCodeGen.Default.InteractiveRebaseJobCollection);
using var writer = new StreamWriter(file);
foreach (var job in collection.Jobs)
{
var code = job.Action switch
{
Models.InteractiveRebaseAction.Pick => 'p',
Models.InteractiveRebaseAction.Edit => 'e',
Models.InteractiveRebaseAction.Reword => 'r',
Models.InteractiveRebaseAction.Squash => 's',
Models.InteractiveRebaseAction.Fixup => 'f',
_ => 'd'
};
writer.WriteLine($"{code} {job.SHA}");
}
writer.Flush();
collection.WriteTodoList(file);
exitCode = 0;
return true;
}
@@ -533,27 +518,8 @@ namespace SourceGit
var onto = File.ReadAllText(ontoFile).Trim();
using var stream = File.OpenRead(jobsFile);
var collection = JsonSerializer.Deserialize(stream, JsonCodeGen.Default.InteractiveRebaseJobCollection);
if (!collection.Onto.Equals(onto) || !collection.OrigHead.Equals(origHead))
return true;
var done = File.ReadAllText(doneFile).Trim().Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (done.Length == 0)
return true;
var current = done[^1].Trim();
var match = REG_REBASE_TODO().Match(current);
if (!match.Success)
return true;
var sha = match.Groups[1].Value;
foreach (var job in collection.Jobs)
{
if (job.SHA.StartsWith(sha))
{
File.WriteAllText(file, job.Message);
break;
}
}
if (collection.Onto.StartsWith(onto, StringComparison.OrdinalIgnoreCase) && collection.OrigHead.StartsWith(origHead, StringComparison.OrdinalIgnoreCase))
collection.WriteCommitMessage(doneFile, file);
return true;
}
@@ -804,9 +770,6 @@ namespace SourceGit
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
}
[GeneratedRegex(@"^[a-z]+\s+([a-fA-F0-9]{4,40})(\s+.*)?$")]
private static partial Regex REG_REBASE_TODO();
private Models.IpcChannel _ipcChannel = null;
private ViewModels.Launcher _launcher = null;
private ResourceDictionary _activeLocale = null;

View File

@@ -14,7 +14,7 @@ namespace SourceGit.Commands
builder.Append("clone --progress --verbose ");
if (!string.IsNullOrEmpty(extraArgs))
builder.Append(extraArgs).Append(' ');
builder.Append(url).Append(' ');
builder.Append(url.Quoted()).Append(' ');
if (!string.IsNullOrEmpty(localName))
builder.Append(localName.Quoted());

View File

@@ -12,7 +12,7 @@ namespace SourceGit.Commands
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
private static partial Regex REG_INDICATOR();
[GeneratedRegex(@"^index\s([0-9a-f]{6,40})\.\.([0-9a-f]{6,40})(\s[1-9]{6})?")]
[GeneratedRegex(@"^index\s([0-9a-f]{6,64})\.\.([0-9a-f]{6,64})(\s[1-9]{6})?")]
private static partial Regex REG_HASH_CHANGE();
private const string PREFIX_LFS_NEW = "+version https://git-lfs.github.com/spec/";
@@ -194,6 +194,7 @@ namespace SourceGit.Commands
return;
}
_result.TextDiff.DeletedLines++;
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0);
_deleted.Add(_last);
_oldLine++;
@@ -207,6 +208,7 @@ namespace SourceGit.Commands
return;
}
_result.TextDiff.AddedLines++;
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine);
_added.Add(_last);
_newLine++;

View File

@@ -1,101 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SourceGit.Commands
{
/// <summary>
/// A C# version of https://github.com/anjerodev/commitollama
/// </summary>
public class GenerateCommitMessage
{
public class GetDiffContent : Command
{
public GetDiffContent(string repo, Models.DiffOption opt)
{
WorkingDirectory = repo;
Context = repo;
Args = $"diff --no-color --no-ext-diff --diff-algorithm=minimal {opt}";
}
public async Task<Result> ReadAsync()
{
return await ReadToEndAsync().ConfigureAwait(false);
}
}
public GenerateCommitMessage(Models.OpenAIService service, string repo, List<Models.Change> changes, CancellationToken cancelToken, Action<string> onResponse)
{
_service = service;
_repo = repo;
_changes = changes;
_cancelToken = cancelToken;
_onResponse = onResponse;
}
public async Task ExecAsync()
{
try
{
_onResponse?.Invoke("Waiting for pre-file analyzing to completed...\n\n");
var responseBuilder = new StringBuilder();
var summaryBuilder = new StringBuilder();
foreach (var change in _changes)
{
if (_cancelToken.IsCancellationRequested)
return;
responseBuilder.Append("- ");
summaryBuilder.Append("- ");
var rs = await new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadAsync();
if (rs.IsSuccess)
{
await _service.ChatAsync(
_service.AnalyzeDiffPrompt,
$"Here is the `git diff` output: {rs.StdOut}",
_cancelToken,
update =>
{
responseBuilder.Append(update);
summaryBuilder.Append(update);
_onResponse?.Invoke($"Waiting for pre-file analyzing to completed...\n\n{responseBuilder}");
});
}
responseBuilder.AppendLine();
summaryBuilder.Append("(file: ").Append(change.Path).AppendLine(")");
}
if (_cancelToken.IsCancellationRequested)
return;
var responseBody = responseBuilder.ToString();
var subjectBuilder = new StringBuilder();
await _service.ChatAsync(
_service.GenerateSubjectPrompt,
$"Here are the summaries changes:\n{summaryBuilder}",
_cancelToken,
update =>
{
subjectBuilder.Append(update);
_onResponse?.Invoke($"{subjectBuilder}\n\n{responseBody}");
});
}
catch (Exception e)
{
App.RaiseException(_repo, $"Failed to generate commit message: {e}");
}
}
private Models.OpenAIService _service;
private string _repo;
private List<Models.Change> _changes;
private CancellationToken _cancelToken;
private Action<string> _onResponse;
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Text;
using System.Threading.Tasks;
namespace SourceGit.Commands
{
public class GetFileChangeForAI : Command
{
public GetFileChangeForAI(string repo, string file, string originalFile)
{
WorkingDirectory = repo;
Context = repo;
var builder = new StringBuilder();
builder.Append("diff --no-color --no-ext-diff --diff-algorithm=minimal --cached -- ");
if (!string.IsNullOrEmpty(originalFile) && !file.Equals(originalFile, StringComparison.Ordinal))
builder.Append(originalFile.Quoted()).Append(' ');
builder.Append(file.Quoted());
Args = builder.ToString();
}
public async Task<Result> ReadAsync()
{
return await ReadToEndAsync().ConfigureAwait(false);
}
}
}

View File

@@ -8,11 +8,11 @@ namespace SourceGit.Commands
[GeneratedRegex(@"^\-\s+\-\s+.*$")]
private static partial Regex REG_TEST();
public IsBinary(string repo, string commit, string path)
public IsBinary(string repo, string revision, string path)
{
WorkingDirectory = repo;
Context = repo;
Args = $"diff --no-color --no-ext-diff --numstat {Models.Commit.EmptyTreeSHA1} {commit} -- {path.Quoted()}";
Args = $"diff --no-color --no-ext-diff --numstat {Models.EmptyTreeHash.Guess(revision)} {revision} -- {path.Quoted()}";
RaiseError = false;
}

View File

@@ -24,7 +24,7 @@ namespace SourceGit.Commands
foreach (var line in lines)
{
if (line.Contains(_commit))
outs.Add(line.Substring(0, 40));
outs.Add(line.Substring(0, _commit.Length));
}
}

View File

@@ -6,11 +6,8 @@ namespace SourceGit.Commands
{
public partial class QueryRepositoryStatus : Command
{
[GeneratedRegex(@"ahead\s(\d+)")]
private static partial Regex REG_AHEAD();
[GeneratedRegex(@"behind\s(\d+)")]
private static partial Regex REG_BEHIND();
[GeneratedRegex(@"\+(\d+) \-(\d+)")]
private static partial Regex REG_BRANCH_AB();
public QueryRepositoryStatus(string repo)
{
@@ -20,23 +17,27 @@ namespace SourceGit.Commands
public async Task<Models.RepositoryStatus> GetResultAsync()
{
Args = "branch -l -v --format=\"%(refname:short)%00%(HEAD)%00%(upstream:track,nobracket)\"";
Args = "status --porcelain=v2 -b";
var rs = await ReadToEndAsync().ConfigureAwait(false);
if (!rs.IsSuccess)
return null;
var status = new Models.RepositoryStatus();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var parts = line.Split('\0');
if (parts.Length != 3 || !parts[1].Equals("*", StringComparison.Ordinal))
continue;
var count = lines.Length;
if (count < 2)
return null;
status.CurrentBranch = parts[0];
if (!string.IsNullOrEmpty(parts[2]))
ParseTrackStatus(status, parts[2]);
}
var sha1 = lines[0].Substring(13).Trim(); // Remove "# branch.oid " prefix
var head = lines[1].Substring(14).Trim(); // Remove "# branch.head " prefix
if (head.Equals("(detached)", StringComparison.Ordinal))
status.CurrentBranch = sha1.Length > 10 ? $"({sha1.Substring(0, 10)})" : "-";
else
status.CurrentBranch = head;
if (count == 4 && lines[3].StartsWith("# branch.ab ", StringComparison.Ordinal))
ParseTrackStatus(status, lines[3].Substring(12).Trim());
status.LocalChanges = await new CountLocalChanges(WorkingDirectory, true) { RaiseError = false }
.GetResultAsync()
@@ -47,13 +48,12 @@ namespace SourceGit.Commands
private void ParseTrackStatus(Models.RepositoryStatus status, string input)
{
var aheadMatch = REG_AHEAD().Match(input);
if (aheadMatch.Success)
status.Ahead = int.Parse(aheadMatch.Groups[1].Value);
var behindMatch = REG_BEHIND().Match(input);
if (behindMatch.Success)
status.Behind = int.Parse(behindMatch.Groups[1].Value);
var match = REG_BRANCH_AB().Match(input);
if (match.Success)
{
status.Ahead = int.Parse(match.Groups[1].Value);
status.Behind = int.Parse(match.Groups[2].Value);
}
}
}
}

View File

@@ -6,25 +6,34 @@ namespace SourceGit.Commands
{
public partial class QueryStagedChangesWithAmend : Command
{
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ADMT])\d{0,6}\t(.*)$")]
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{4,64}) [0-9a-f]{4,64} ([ADMT])\d{0,6}\t(.*)$")]
private static partial Regex REG_FORMAT1();
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([RC])\d{0,6}\t(.*\t.*)$")]
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{4,64}) [0-9a-f]{4,64} ([RC])\d{0,6}\t(.*\t.*)$")]
private static partial Regex REG_FORMAT2();
public QueryStagedChangesWithAmend(string repo, string parent)
public QueryStagedChangesWithAmend(string repo)
{
WorkingDirectory = repo;
Context = repo;
Args = $"diff-index --cached -M {parent}";
_parent = parent;
}
public List<Models.Change> GetResult()
{
Args = "show --no-show-signature --format=\"%H %P\" -s HEAD";
var rs = ReadToEnd();
if (!rs.IsSuccess)
return [];
var shas = rs.StdOut.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (shas.Length == 0)
return [];
var parent = shas.Length > 1 ? shas[1] : Models.EmptyTreeHash.Guess(shas[0]);
Args = $"diff-index --cached -M {parent}";
rs = ReadToEnd();
if (!rs.IsSuccess)
return [];
var changes = new List<Models.Change>();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
@@ -39,7 +48,7 @@ namespace SourceGit.Commands
{
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
ParentSHA = _parent,
ParentSHA = parent,
},
};
var type = match.Groups[3].Value;
@@ -58,7 +67,7 @@ namespace SourceGit.Commands
{
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
ParentSHA = _parent,
ParentSHA = parent,
},
};
@@ -84,7 +93,5 @@ namespace SourceGit.Commands
return changes;
}
private readonly string _parent;
}
}

View File

@@ -15,8 +15,6 @@ namespace SourceGit.Models
public class Commit
{
public const string EmptyTreeSHA1 = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
public string SHA { get; set; } = string.Empty;
public User Author { get; set; } = User.Invalid;
public ulong AuthorTime { get; set; } = 0;
@@ -33,6 +31,7 @@ namespace SourceGit.Models
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null;
public bool HasDecorators => Decorators.Count > 0;
public string FirstParentToCompare => Parents.Count > 0 ? $"{SHA}^" : EmptyTreeHash.Guess(SHA);
public string GetFriendlyName()
{

View File

@@ -0,0 +1,9 @@
namespace SourceGit.Models
{
public enum DealWithLocalChanges
{
DoNothing = 0,
StashAndReapply,
Discard,
}
}

View File

@@ -60,8 +60,7 @@ namespace SourceGit.Models
/// <param name="change"></param>
public DiffOption(Commit commit, Change change)
{
var baseRevision = commit.Parents.Count == 0 ? Commit.EmptyTreeSHA1 : $"{commit.SHA}^";
_revisions.Add(baseRevision);
_revisions.Add(commit.FirstParentToCompare);
_revisions.Add(commit.SHA);
_path = change.Path;
_orgPath = change.OriginalPath;
@@ -74,8 +73,7 @@ namespace SourceGit.Models
/// <param name="file"></param>
public DiffOption(Commit commit, string file)
{
var baseRevision = commit.Parents.Count == 0 ? Commit.EmptyTreeSHA1 : $"{commit.SHA}^";
_revisions.Add(baseRevision);
_revisions.Add(commit.FirstParentToCompare);
_revisions.Add(commit.SHA);
_path = file;
}
@@ -88,7 +86,7 @@ namespace SourceGit.Models
{
if (string.IsNullOrEmpty(ver.OriginalPath))
{
_revisions.Add(ver.HasParent ? $"{ver.SHA}^" : Commit.EmptyTreeSHA1);
_revisions.Add(ver.HasParent ? $"{ver.SHA}^" : EmptyTreeHash.Guess(ver.SHA));
_revisions.Add(ver.SHA);
_path = ver.Path;
}
@@ -111,14 +109,14 @@ namespace SourceGit.Models
{
if (start.Change.Index == ChangeState.Deleted)
{
_revisions.Add(Commit.EmptyTreeSHA1);
_revisions.Add(EmptyTreeHash.Guess(end.SHA));
_revisions.Add(end.SHA);
_path = end.Path;
}
else if (end.Change.Index == ChangeState.Deleted)
{
_revisions.Add(start.SHA);
_revisions.Add(Commit.EmptyTreeSHA1);
_revisions.Add(EmptyTreeHash.Guess(start.SHA));
_path = start.Path;
}
else if (!end.Path.Equals(start.Path, StringComparison.Ordinal))

View File

@@ -55,6 +55,8 @@ namespace SourceGit.Models
{
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
public int MaxLineNumber = 0;
public int AddedLines { get; set; } = 0;
public int DeletedLines { get; set; } = 0;
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
{

View File

@@ -0,0 +1,13 @@
namespace SourceGit.Models
{
public static class EmptyTreeHash
{
public static string Guess(string revision)
{
return revision.Length == 40 ? SHA1 : SHA256;
}
private const string SHA1 = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
private const string SHA256 = "6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321";
}
}

View File

@@ -1,4 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace SourceGit.Models
{
@@ -34,10 +37,55 @@ namespace SourceGit.Models
public string Message { get; set; } = string.Empty;
}
public class InteractiveRebaseJobCollection
public partial class InteractiveRebaseJobCollection
{
public string OrigHead { get; set; } = string.Empty;
public string Onto { get; set; } = string.Empty;
public List<InteractiveRebaseJob> Jobs { get; set; } = new List<InteractiveRebaseJob>();
public void WriteTodoList(string todoFile)
{
using var writer = new StreamWriter(todoFile);
foreach (var job in Jobs)
{
var code = job.Action switch
{
InteractiveRebaseAction.Pick => 'p',
InteractiveRebaseAction.Edit => 'e',
InteractiveRebaseAction.Reword => 'r',
InteractiveRebaseAction.Squash => 's',
InteractiveRebaseAction.Fixup => 'f',
_ => 'd'
};
writer.WriteLine($"{code} {job.SHA}");
}
writer.Flush();
}
public void WriteCommitMessage(string doneFile, string msgFile)
{
var done = File.ReadAllText(doneFile).Trim().Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (done.Length == 0)
return;
var current = done[^1].Trim();
var match = REG_REBASE_TODO().Match(current);
if (!match.Success)
return;
var sha = match.Groups[1].Value;
foreach (var job in Jobs)
{
if (job.SHA.StartsWith(sha))
{
File.WriteAllText(msgFile, job.Message);
return;
}
}
}
[GeneratedRegex(@"^[a-z]+\s+([a-fA-F0-9]{4,64})(\s+.*)?$")]
private static partial Regex REG_REBASE_TODO();
}
}

View File

@@ -1,239 +0,0 @@
using System;
using System.ClientModel;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.OpenAI;
using CommunityToolkit.Mvvm.ComponentModel;
using OpenAI;
using OpenAI.Chat;
namespace SourceGit.Models
{
public partial class OpenAIResponse
{
public OpenAIResponse(Action<string> onUpdate)
{
_onUpdate = onUpdate;
}
public void Append(string text)
{
var buffer = text;
if (_thinkTail.Length > 0)
{
_thinkTail.Append(buffer);
buffer = _thinkTail.ToString();
_thinkTail.Clear();
}
buffer = REG_COT().Replace(buffer, "");
var startIdx = buffer.IndexOf('<');
if (startIdx >= 0)
{
if (startIdx > 0)
OnReceive(buffer.Substring(0, startIdx));
var endIdx = buffer.IndexOf('>', startIdx + 1);
if (endIdx <= startIdx)
{
if (buffer.Length - startIdx <= 15)
_thinkTail.Append(buffer.AsSpan(startIdx));
else
OnReceive(buffer.Substring(startIdx));
}
else if (endIdx < startIdx + 15)
{
var tag = buffer.Substring(startIdx + 1, endIdx - startIdx - 1);
if (_thinkTags.Contains(tag))
_thinkTail.Append(buffer.AsSpan(startIdx));
else
OnReceive(buffer.Substring(startIdx));
}
else
{
OnReceive(buffer.Substring(startIdx));
}
}
else
{
OnReceive(buffer);
}
}
public void End()
{
if (_thinkTail.Length > 0)
{
OnReceive(_thinkTail.ToString());
_thinkTail.Clear();
}
}
private void OnReceive(string text)
{
if (!_hasTrimmedStart)
{
text = text.TrimStart();
if (string.IsNullOrEmpty(text))
return;
_hasTrimmedStart = true;
}
_onUpdate?.Invoke(text);
}
[GeneratedRegex(@"<(think|thought|thinking|thought_chain)>.*?</\1>", RegexOptions.Singleline)]
private static partial Regex REG_COT();
private Action<string> _onUpdate = null;
private StringBuilder _thinkTail = new StringBuilder();
private HashSet<string> _thinkTags = ["think", "thought", "thinking", "thought_chain"];
private bool _hasTrimmedStart = false;
}
public class OpenAIService : ObservableObject
{
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public string Server
{
get => _server;
set => SetProperty(ref _server, value);
}
public string ApiKey
{
get => _apiKey;
set => SetProperty(ref _apiKey, value);
}
public bool ReadApiKeyFromEnv
{
get => _readApiKeyFromEnv;
set => SetProperty(ref _readApiKeyFromEnv, value);
}
public string Model
{
get => _model;
set => SetProperty(ref _model, value);
}
public bool Streaming
{
get => _streaming;
set => SetProperty(ref _streaming, value);
}
public string AnalyzeDiffPrompt
{
get => _analyzeDiffPrompt;
set => SetProperty(ref _analyzeDiffPrompt, value);
}
public string GenerateSubjectPrompt
{
get => _generateSubjectPrompt;
set => SetProperty(ref _generateSubjectPrompt, value);
}
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 async Task ChatAsync(string prompt, string question, CancellationToken cancellation, Action<string> onUpdate)
{
var key = _readApiKeyFromEnv ? Environment.GetEnvironmentVariable(_apiKey) : _apiKey;
var endPoint = new Uri(_server);
var credential = new ApiKeyCredential(key);
var client = _server.Contains("openai.azure.com/", StringComparison.Ordinal)
? new AzureOpenAIClient(endPoint, credential)
: new OpenAIClient(credential, new() { Endpoint = endPoint });
var chatClient = client.GetChatClient(_model);
var messages = new List<ChatMessage>()
{
_model.Equals("o1-mini", StringComparison.Ordinal) ? new UserChatMessage(prompt) : new SystemChatMessage(prompt),
new UserChatMessage(question),
};
try
{
var rsp = new OpenAIResponse(onUpdate);
if (_streaming)
{
var updates = chatClient.CompleteChatStreamingAsync(messages, null, cancellation);
await foreach (var update in updates)
{
if (update.ContentUpdate.Count > 0)
rsp.Append(update.ContentUpdate[0].Text);
}
}
else
{
var completion = await chatClient.CompleteChatAsync(messages, null, cancellation);
if (completion.Value.Content.Count > 0)
rsp.Append(completion.Value.Content[0].Text);
}
rsp.End();
}
catch
{
if (!cancellation.IsCancellationRequested)
throw;
}
}
private string _name;
private string _server;
private string _apiKey;
private bool _readApiKeyFromEnv = false;
private string _model;
private bool _streaming = true;
private string _analyzeDiffPrompt;
private string _generateSubjectPrompt;
}
}

View File

@@ -64,14 +64,9 @@ namespace SourceGit.Models
{
url = null;
if (URL.StartsWith("http", StringComparison.Ordinal))
if (URL.StartsWith("http://", StringComparison.Ordinal) || URL.StartsWith("https://", StringComparison.Ordinal))
{
var uri = new Uri(URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL);
if (uri.Port != 80 && uri.Port != 443)
url = $"{uri.Scheme}://{uri.Host}:{uri.Port}{uri.LocalPath}";
else
url = $"{uri.Scheme}://{uri.Host}{uri.LocalPath}";
url = URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL;
return true;
}
@@ -97,7 +92,6 @@ namespace SourceGit.Models
var uri = new Uri(baseURL);
var host = uri.Host;
var route = uri.AbsolutePath.TrimStart('/');
var encodedBranch = HttpUtility.UrlEncode(mergeBranch);
if (host.Contains("github.com", StringComparison.Ordinal))

View File

@@ -10,5 +10,6 @@ namespace SourceGit.Models
public ulong Time { get; set; } = 0;
public string Message { get; set; } = "";
public string Subject => Message.Split('\n', 2)[0].Trim();
public string UntrackedParent => EmptyTreeHash.Guess(SHA);
}
}

View File

@@ -9,6 +9,7 @@ namespace SourceGit.Models
public Dictionary<string, Color> BasicColors { get; set; } = new Dictionary<string, Color>();
public double GraphPenThickness { get; set; } = 2;
public double OpacityForNotMergedCommits { get; set; } = 0.5;
public bool UseMicaOnWindows11 { get; set; } = true;
public List<Color> GraphColors { get; set; } = new List<Color>();
}
}

View File

@@ -107,6 +107,12 @@ namespace SourceGit.Native
set;
} = string.Empty;
public static bool UseMicaOnWindows11
{
get => OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) && _enableMicaOnWindows11;
set => _enableMicaOnWindows11 = value;
}
public static bool UseSystemWindowFrame
{
get => OperatingSystem.IsLinux() && _enableSystemWindowFrame;
@@ -294,5 +300,6 @@ namespace SourceGit.Native
private static IBackend _backend = null;
private static string _gitExecutable = string.Empty;
private static bool _enableSystemWindowFrame = false;
private static bool _enableMicaOnWindows11 = true;
}
}

View File

@@ -24,7 +24,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI-Assistent</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">NEU GENERIEREN</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Verwende AI, um Commit-Nachrichten zu generieren</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Als Commit-Nachricht verwenden</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit minimieren</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Alles anzeigen</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
@@ -113,8 +112,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Warnung: Durch Auschecken eines Commits wird dein HEAD losgelöst (detached)!</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Lokale Änderungen:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Verwerfen</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stashen &amp; wieder anwenden</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Dein aktueller HEAD enthält Commit(s) ohne Verbindung zu einem Branch / Tag. Möchtest du trotzdem fortfahren?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">Die folgenden Submodule müssen aktualisiert werden:{0}Möchtest du sie aktualisieren?</x:String>
@@ -298,8 +295,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Basierend auf:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Erstellten Branch auschecken</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Lokale Änderungen:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Verwerfen</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stashen &amp; wieder anwenden</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Neuer Branch-Name:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Einen Branch-Namen eingeben</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Lokalen Branch erstellen</x:String>
@@ -318,6 +313,9 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">Ohne Anmerkung</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Halte Strg gedrückt, um direkt auszuführen</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Ausschneiden</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Verwerfen</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Nichts tun</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stashen &amp; wieder anwenden</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Deinitialisiere Submodul</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Erzwinge Deinitialisierung, selbst wenn lokale Änderungen enthalten sind.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Submodul:</x:String>
@@ -603,14 +601,11 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Gestern</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Einstellungen</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">System-Prompt für Diff-Analyse</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API-Schlüssel</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">System-Prompt für Erstellung von Commit-Nachricht</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modell</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Name</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Der eingegebene Wert ist der Name der Umgebungsvariable, aus der der API-Schlüssel gelesen wird</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Streaming aktivieren</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">DARSTELLUNG</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Standardschriftart</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Editor-Tab-Breite</x:String>
@@ -680,8 +675,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Remote-Branch:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Lokaler Branch:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Lokale Änderungen:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Verwerfen</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stashen &amp; wieder anwenden</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remote:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Rebase anstatt Merge verwenden</x:String>

View File

@@ -20,10 +20,11 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI Assistant</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GENERATE</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Use AI to generate commit message</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APPLY AS COMMIT MESSAGE</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Use</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Hide SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Show All</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">3-Way Merge</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Patch File:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Select .patch file to apply</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">Ignore whitespace changes</x:String>
@@ -107,10 +108,8 @@
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Warning: By doing a commit checkout, your Head will be detached</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Warning: By doing a commit checkout, HEAD will be detached</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Local Changes:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reapply</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Your current HEAD contains commit(s) not connected to any branches/tags! Do you want to continue?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">The following submodules need to be updated:{0}Do you want to update them?</x:String>
@@ -300,8 +299,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Based On:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Check out the created branch</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Local Changes:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reapply</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">New Branch Name:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Enter branch name.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Create Local Branch</x:String>
@@ -320,6 +317,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">lightweight</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Hold Ctrl to start directly</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Cut</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Discard</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Do Nothing</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reapply</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">De-initialize Submodule</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Force de-init even if it contains local changes.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Submodule:</x:String>
@@ -502,6 +502,7 @@
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Commit staged changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Commit and push staged changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">Stage all changes and commit</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CreateBranch" xml:space="preserve">Create new branch</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Fetch" xml:space="preserve">Fetch, starts directly</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">Dashboard mode (Default)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoToChild" xml:space="preserve">Goto child of selected commit</x:String>
@@ -609,14 +610,12 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Yesterday</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferences</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyze Diff Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Additional Prompt (Use `-` to list your requirements)</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API Key</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Generate Subject Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Model</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Name</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Entered value is the name to load API key from ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Enable Streaming</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APPEARANCE</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Editor Tab Width</x:String>
@@ -687,8 +686,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Remote Branch:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Into:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Local Changes:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reapply</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remote:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Use rebase instead of merge</x:String>
@@ -855,7 +852,7 @@
<x:String x:Key="Text.Stash.Message" xml:space="preserve">Message:</x:String>
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">Optional. Message of this stash</x:String>
<x:String x:Key="Text.Stash.Mode" xml:space="preserve">Mode:</x:String>
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">Only staged changes</x:String>
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">Only staged files</x:String>
<x:String x:Key="Text.Stash.TipForSelectedFiles" xml:space="preserve">Both staged and unstaged changes of selected file(s) will be stashed!!!</x:String>
<x:String x:Key="Text.Stash.Title" xml:space="preserve">Stash Local Changes</x:String>
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">Apply</x:String>

View File

@@ -24,10 +24,11 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Asistente OpenAI</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GENERAR</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Usar OpenAI para generar mensaje de commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APLICAR COMO MENSAJE DE COMMIT</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Usar</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Ocultar SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Mostrar Todo</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Aplicar Parche</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">Merge a 3 vías (3-Way)</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Archivo del Parche:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Seleccionar archivo .patch para aplicar</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">Ignorar cambios de espacios en blanco</x:String>
@@ -113,8 +114,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Advertencia: Al hacer un checkout de commit, tu Head se separará</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Cambios Locales:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Rama:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">¡Tu HEAD actual contiene commit(s) que no están conectados a ningunas ramas/etiquetas! ¿Quieres continuar?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">Los siguientes submódulos necesitan ser actualizados:{0} ¿Quieres actualizarlos?</x:String>
@@ -304,8 +303,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Basado En:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Checkout de la rama creada</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Cambios Locales:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nombre de la Nueva Rama:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Introduzca el nombre de la rama.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Crear Rama Local</x:String>
@@ -324,6 +321,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">ligera</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Mantenga Ctrl para iniciar directamente</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Cortar</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">No Hacer Nada</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Desinicializar Submódulo</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Forzar desinicialización incluso si contiene cambios locales.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Submódulo:</x:String>
@@ -506,6 +506,7 @@
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Commit cambios staged</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Commit y push cambios staged</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">Stage todos los cambios y commit</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CreateBranch" xml:space="preserve">Crear nueva rama</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Fetch" xml:space="preserve">Fetch, empieza directamente</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">Modo Dashboard (Por Defecto)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoToChild" xml:space="preserve">Ir al hijo del commit seleccionado</x:String>
@@ -613,14 +614,12 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Ayer</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferencias</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">OPEN AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Analizar Diff Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Prompt adicional (Usa `-` para listar tus requerimientos)</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Clave API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Generar Subject Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modelo</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nombre</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">El valor ingresado es el nombre de la clave API a cargar desde ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Servidor</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Habilitar Transmisión</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APARIENCIA</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Fuente por defecto</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Ancho de la Pestaña del Editor</x:String>
@@ -653,6 +652,7 @@
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">Mostrar hijos en los detalles de commit</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">Mostrar etiquetas en el gráfico de commit</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">Longitud de la guía del asunto</x:String>
<x:String x:Key="Text.Preferences.General.Use24Hours" xml:space="preserve">24-Horas</x:String>
<x:String x:Key="Text.Preferences.General.UseGitHubStyleAvatar" xml:space="preserve">Generar avatar con estilo por defecto de Github</x:String>
<x:String x:Key="Text.Preferences.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preferences.Git.CRLF" xml:space="preserve">Habilitar Auto CRLF</x:String>
@@ -690,8 +690,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Rama Remota:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">En:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Cambios Locales:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Usar rebase en lugar de merge</x:String>

View File

@@ -23,7 +23,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assistant IA</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GÉNÉRER</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Utiliser l'IA pour générer un message de commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APPLIQUER COMME MESSAGE DE COMMIT</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Masquer SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Tout Afficher</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Appliquer</x:String>
@@ -104,8 +103,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit :</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Avertissement: une récupération vers un commit aboutiera vers un HEAD détaché</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Changements locaux :</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Annuler</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Mettre en stash et réappliquer</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branche :</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Votre HEAD actuel contient un ou plusieurs commits non connectés à une branche/tag ! Voulez-vous continuer ?</x:String>
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Récupérer &amp; Fast-Forward</x:String>
@@ -285,8 +282,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Basé sur :</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Récupérer la branche créée</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Changements locaux :</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Rejeter</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Réappliquer</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nom de la nouvelle branche :</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Entrez le nom de la branche.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Créer une branche locale</x:String>
@@ -305,6 +300,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">léger</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Maintenir Ctrl pour commencer directement</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Couper</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Annuler</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Ne rien faire</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Mettre en stash et réappliquer</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Désinitialiser le sous-module</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Forcer la désinitialisation même s'il contient des modifications locales.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Sous-module :</x:String>
@@ -562,14 +560,11 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Hier</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Préférences</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">IA</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyser Diff Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Clé d'API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Générer le sujet de Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modèle</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nom</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">La valeur saisie est le nom pour charger la clé API depuis l'ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Serveur</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Activer le streaming</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APPARENCE</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Police par défaut</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Largeur de tab dans l'éditeur</x:String>
@@ -633,8 +628,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branche distante :</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Dans :</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Changements locaux :</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Rejeter</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Réappliquer</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Dépôt distant :</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Utiliser rebase au lieu de merge</x:String>

View File

@@ -22,7 +22,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Asisten AI</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">BUAT ULANG</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Gunakan AI untuk membuat pesan commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">TERAPKAN SEBAGAI PESAN COMMIT</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Sembunyikan SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Tampilkan Semua</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
@@ -100,8 +99,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Peringatan: Dengan melakukan checkout commit, Head akan terlepas</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Perubahan Lokal:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Buang</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Terapkan Ulang</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">HEAD saat ini mengandung commit yang tidak terhubung ke branch/tag manapun! Lanjutkan?</x:String>
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Checkout &amp; Fast-Forward</x:String>
@@ -270,8 +267,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Berdasarkan:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Checkout branch yang dibuat</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Perubahan Lokal:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Buang</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Terapkan Ulang</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nama Branch Baru:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Masukkan nama branch.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Buat Branch Lokal</x:String>
@@ -290,6 +285,8 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">lightweight</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Tahan Ctrl untuk memulai langsung</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Potong</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Buang</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Terapkan Ulang</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">De-initialize Submodule</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Paksa de-init meski mengandung perubahan lokal.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Submodule:</x:String>
@@ -537,14 +534,11 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Kemarin</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferensi</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Prompt Analisis Diff</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API Key</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Prompt Generate Subjek</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Model</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nama</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Nilai yang dimasukkan adalah nama untuk memuat API key dari ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Aktifkan Streaming</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">TAMPILAN</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Font Default</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Lebar Tab Editor</x:String>
@@ -608,8 +602,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Remote Branch:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Ke:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Perubahan Lokal:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Buang</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Terapkan Ulang</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remote:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Gunakan rebase alih-alih merge</x:String>

View File

@@ -24,7 +24,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assistente AI</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RIGENERA</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Usa AI per generare il messaggio di commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APPLICA COME MESSAGGIO DI COMMIT</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Nascondi SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Mostra Tutto</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Applica</x:String>
@@ -112,8 +111,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Avviso: Effettuando un checkout del commit, la tua HEAD sarà separata</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Modifiche Locali:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Scarta</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stasha e Ripristina</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Il tuo HEAD attuale contiene commit non connessi ad alcun branch/tag! Sicuro di voler continuare?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">I seguenti sottomoduli devono essere aggiornati:{0}Vuoi aggiornarli?</x:String>
@@ -297,8 +294,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Basato Su:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Checkout del Branch Creato</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Modifiche Locali:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Scarta</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Stasha e Ripristina</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nome Nuovo Branch:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Inserisci il nome del branch.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Crea Branch Locale</x:String>
@@ -317,6 +312,9 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">leggero</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Tieni premuto Ctrl per avviare direttamente</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Taglia</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Scarta</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Non fare nulla</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stash e Ripristina</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Deinizializza Sottomodulo</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Forza deinizializzazione anche se contiene modifiche locali.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Sottomodulo:</x:String>
@@ -599,14 +597,11 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Ieri</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferenze</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Analizza il Prompt Differenza</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Chiave API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Genera Prompt Oggetto</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modello</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nome</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Il valore inserito è il nome per caricare la chiave API da ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Abilita streaming</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">ASPETTO</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Font Predefinito</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Larghezza della Tab Editor</x:String>
@@ -676,8 +671,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch Remoto:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">In:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Modifiche Locali:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Scarta</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Stasha e Riapplica</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Scarica (Recupera e Unisci)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Riallineare anziché unire</x:String>

View File

@@ -24,7 +24,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI アシスタント</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">再生成</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">AI を使用してコミットメッセージを生成</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">コミットメッセージとして適用</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit を隠す</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">すべて表示</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">パッチ</x:String>
@@ -113,8 +112,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">コミット:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">警告: コミットをチェックアウトすると、HEAD が切断されます</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">ローカルの変更:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">破棄</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">スタッシュして再適用</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">ブランチ:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">現在の HEAD には、どのブランチやタグにも繋がっていないコミットが含まれています!それでも続行しますか?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">これらのサブモジュールを更新する必要があります:{0}更新しますか?</x:String>
@@ -299,8 +296,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">派生元:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">作成したブランチにチェックアウト</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">ローカルの変更:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">破棄</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">スタッシュして再適用</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">新しいブランチ名:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">ブランチの名前を入力</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">ローカルブランチを作成</x:String>
@@ -319,6 +314,8 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">軽量</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Ctrl キーを押しながらで直接実行できます</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">切り取り</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">破棄</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">スタッシュして再適用</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">サブモジュールの初期化を解除</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">ローカルの変更の有無に関わらず、強制的に解除</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">サブモジュール:</x:String>
@@ -606,14 +603,11 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">昨日</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">設定</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">差分分析プロンプト</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API キー</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">タイトル生成プロンプト</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">モデル</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">名前</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">この値を環境変数の名前とし、そこから API キーを読み込む</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">サーバー</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">ストリーミングを有効化</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">外観</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">既定のフォント</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">エディターのタブ幅</x:String>
@@ -683,8 +677,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">リモートブランチ:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">プル先:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">ローカルの変更:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">破棄</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">スタッシュして再適用</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">リモート:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">プル (フェッチ &amp; マージ)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">マージではなくリベースを使用</x:String>

View File

@@ -20,7 +20,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI 어시스턴트</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">재생성</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">AI를 사용하여 커밋 메시지 생성</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">커밋 메시지로 적용</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit 숨기기</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">모두 보기</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">패치</x:String>
@@ -97,8 +96,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">커밋:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">경고: 커밋 체크아웃을 하면, HEAD가 분리됩니다(detached)</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">로컬 변경 사항:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">폐기</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">스태시 &amp; 재적용</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">브랜치:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">현재 HEAD에 브랜치/태그에 연결되지 않은 커밋이 있습니다! 계속하시겠습니까?</x:String>
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">체크아웃 &amp; Fast-Forward</x:String>
@@ -269,8 +266,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">기준:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">생성된 브랜치로 체크아웃</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">로컬 변경 사항:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">폐기</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">스태시 &amp; 재적용</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">새 브랜치 이름:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">브랜치 이름을 입력하세요.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">로컬 브랜치 생성</x:String>
@@ -289,6 +284,8 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">경량 태그</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Ctrl을 누른 채 클릭하면 바로 시작합니다</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">잘라내기</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">폐기</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">스태시 &amp; 재적용</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">서브모듈 초기화 해제</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">로컬 변경 사항이 있어도 강제로 초기화 해제합니다.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">서브모듈:</x:String>
@@ -539,14 +536,11 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">어제</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">환경설정</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Diff 분석 프롬프트</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API 키</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">제목 생성 프롬프트</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">모델</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">이름</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">입력된 값은 환경변수(ENV)에서 API 키를 불러올 이름입니다</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">서버</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">스트리밍 활성화</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">모양</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">기본 글꼴</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">에디터 탭 너비</x:String>
@@ -609,8 +603,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">원격 브랜치:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">대상:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">로컬 변경 사항:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">폐기</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">스태시 &amp; 재적용</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">원격:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; 병합)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">병합 대신 리베이스 사용</x:String>

View File

@@ -24,7 +24,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assietente IA</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">Gerar novamente</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Utilizar IA para gerar mensagem de commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APLICAR COMO MENSAGEM DE COMMMIT</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Esconder SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Mostrar Todos</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
@@ -100,8 +99,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Aviso: Ao fazer o checkout de um commit, seu Head ficará desanexado</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Alterações Locais:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-Pick</x:String>
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Adicionar origem à mensagem de commit</x:String>
@@ -201,8 +198,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Baseado Em:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Checar o branch criado</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Alterações Locais:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Guardar &amp; Reaplicar</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nome do Novo Branch:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Insira o nome do branch.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Criar Branch Local</x:String>
@@ -220,6 +215,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">leve</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Pressione Ctrl para iniciar diretamente</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Recortar</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Nada</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Stash &amp; Reaplicar</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">Excluir Branch</x:String>
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">Você está prestes a excluir uma branch remota!!!</x:String>
@@ -419,9 +417,7 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Ontem</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferências</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">INTELIGÊNCIA ARTIFICIAL</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Prompt para Analisar Diff</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Chave da API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Prompt para Gerar Título</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modelo</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nome</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Servidor</x:String>
@@ -476,8 +472,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch Remoto:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Para:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Alterações Locais:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Descartar</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Guardar &amp; Reaplicar</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Puxar (Buscar &amp; Mesclar)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Usar rebase em vez de merge</x:String>

View File

@@ -24,10 +24,11 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Помощник OpenAI</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">ПЕРЕСОЗДАТЬ</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Использовать OpenAI для создания сообщения о ревизии</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">ПРИМЕНИТЬ КАК СООБЩЕНИЕ РЕВИЗИИ</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Использовать</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Скрыть SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Показать все</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Исправить </x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">Трехстороннее слияние</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Файл заплатки:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Выберите файл .patch для применения</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">Игнорировать изменения пробелов</x:String>
@@ -113,8 +114,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Ревизия:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Предупреждение: После переключения ревизии ваша Голова (HEAD) будет отсоединена</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Локальные изменения:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Отклонить</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Отложить и применить повторно</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Ветка:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Ваша текущая ГОЛОВА содержит ревизию(и), не связанные ни с к какими ветками или метками! Вы хотите продолжить?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">Подмодулям требуется обновление:{0}Обновить их?</x:String>
@@ -138,6 +137,10 @@
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">Адрес репозитория:</x:String>
<x:String x:Key="Text.Close" xml:space="preserve">ЗАКРЫТЬ</x:String>
<x:String x:Key="Text.CodeEditor" xml:space="preserve">Редактор</x:String>
<x:String x:Key="Text.CommandPalette.Branches" xml:space="preserve">Ветки</x:String>
<x:String x:Key="Text.CommandPalette.BranchesAndTags" xml:space="preserve">Ветки и метки</x:String>
<x:String x:Key="Text.CommandPalette.RepositoryActions" xml:space="preserve">Пользовательские действия репозитория</x:String>
<x:String x:Key="Text.CommandPalette.RevisionFiles" xml:space="preserve">Ревизия файлов</x:String>
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Переключиться на эту ревизию</x:String>
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Применить эту ревизию (cherry-pick)</x:String>
<x:String x:Key="Text.CommitCM.CherryPickMultiple" xml:space="preserve">Применить несколько ревизий ...</x:String>
@@ -281,6 +284,7 @@
<x:String x:Key="Text.ConfirmEmptyCommit.Continue" xml:space="preserve">ПРОДОЛЖИТЬ</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.NoLocalChanges" xml:space="preserve">Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty)?</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageAllThenCommit" xml:space="preserve">Сформировать всё и зафиксировать ревизию</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageSelectedThenCommit" xml:space="preserve">Сформировать выбранные и зафиксировать</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.WithLocalChanges" xml:space="preserve">Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить всё, а затем зафиксировать ревизию?</x:String>
<x:String x:Key="Text.ConfirmRestart.Title" xml:space="preserve">Требуется перезапуск</x:String>
<x:String x:Key="Text.ConfirmRestart.Message" xml:space="preserve">Вы должны перезапустить приложение после применения изменений.</x:String>
@@ -299,8 +303,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Основан на:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Переключиться на созданную ветку</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Локальные изменения:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Отклонить</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Отложить и применить повторно</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Имя новой ветки:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Введите имя ветки.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Создать локальную ветку</x:String>
@@ -319,6 +321,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">Простой</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Удерживайте Ctrl, чтобы сразу начать</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Вырезать</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Отклонить</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">Ничего не делать</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Отложить и примненить повторно</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Удалить подмодуль</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Принудительно удалить даже если содержит локальные изменения.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Подмодуль:</x:String>
@@ -501,6 +506,7 @@
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Зафиксировать сформированные изменения</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Зафиксировать и выложить сформированные изменения</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">Сформировать все изменения и зафиксировать</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CreateBranch" xml:space="preserve">Создать новую ветку</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Fetch" xml:space="preserve">Извлечь (fetch), запускается сразу</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">Режим доски (по умолчанию)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoToChild" xml:space="preserve">К дочернему выбранной ревизии</x:String>
@@ -523,6 +529,8 @@
<x:String x:Key="Text.Hunk.Stage" xml:space="preserve">Сформировать</x:String>
<x:String x:Key="Text.Hunk.Unstage" xml:space="preserve">Расформировать</x:String>
<x:String x:Key="Text.Init" xml:space="preserve">Создать репозиторий</x:String>
<x:String x:Key="Text.Init.CommandTip" xml:space="preserve">Вы действительно хотите запуститб команду «git init» по этому пути?</x:String>
<x:String x:Key="Text.Init.ErrorMessageTip" xml:space="preserve">Не удалось открыть репозиторий. Причина: </x:String>
<x:String x:Key="Text.Init.Path" xml:space="preserve">Путь:</x:String>
<x:String x:Key="Text.InProgress.CherryPick" xml:space="preserve">Выполняется частичный перенос ревизий (cherry-pick).</x:String>
<x:String x:Key="Text.InProgress.CherryPick.Head" xml:space="preserve">Обрабтка ревизии.</x:String>
@@ -606,14 +614,12 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Вчера</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Параметры</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">ОТКРЫТЬ ИИ</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Запрос на анализ сравнения</x:String>
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Дополнительная подсказка (Для перечисления ваших требований используйте `-`)</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Создать запрос на тему</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Модель</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Имя:</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Введённое значение — это имя для загрузки API-ключа из ENV</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Сервер</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Разрешить потоковую передачу</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">ВИД</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Шрифт по умолчанию</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Редактировать ширину вкладки</x:String>
@@ -646,6 +652,7 @@
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">Показать наследника в деталях комментария</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">Показывать метки на графике</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">Длина темы ревизии</x:String>
<x:String x:Key="Text.Preferences.General.Use24Hours" xml:space="preserve">24-часовой</x:String>
<x:String x:Key="Text.Preferences.General.UseGitHubStyleAvatar" xml:space="preserve">Создать Github-подобный аватар по умолчанию</x:String>
<x:String x:Key="Text.Preferences.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preferences.Git.CRLF" xml:space="preserve">Включить автозавершение CRLF</x:String>
@@ -683,8 +690,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Ветка внешнего репозитория:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">В:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Локальные изменения:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Отклонить</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Отложить и применить повторно</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Внешний репозиторий:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Загрузить (Получить и слить)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Использовать перемещение вместо слияния</x:String>

View File

@@ -19,7 +19,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">செநு உதவியாளர்</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">மறு-உருவாக்கு</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">உறுதிமொழி செய்தியை உருவாக்க செநுவைப் பயன்படுத்து</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">உறுதிமொழி செய்தி என இடு</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">ஒட்டு</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">ஒட்டு கோப்பு:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">.ஒட்டு இடுவதற்கு கோப்பைத் தேர்ந்தெடு</x:String>
@@ -71,8 +70,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">உறுதிமொழி:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">முன்னறிவிப்பு: ஒரு உறுதிமொழி சரிபார்பதன் மூலம், உங்கள் தலை பிரிக்கப்படும்</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">உள்ளக மாற்றங்கள்:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">நிராகரி</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">பதுக்கிவை &amp; மீண்டும் இடு</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">கிளை:</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">கனி பறி</x:String>
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">உறுதிமொழி செய்திக்கு மூலத்தைச் சேர்</x:String>
@@ -182,8 +179,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">இதன் அடிப்படையில்:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">உருவாக்கப்பட்ட கிளையைப் சரிபார்</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">உள்ளக மாற்றங்கள்:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">நிராகரி</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">பதுக்கிவை &amp; மீண்டும் இடு</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">புதிய கிளை பெயர்:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">கிளை பெயரை உள்ளிடவும்.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">உள்ளக கிளையை உருவாக்கு</x:String>
@@ -201,6 +196,8 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">குறைந்தஎடை</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">நேரடியாகத் தொடங்க கட்டுப்பாட்டை அழுத்திப் பிடி</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">வெட்டு</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">நிராகரி</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">பதுக்கிவை &amp; மீண்டும் இடு</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">கிளையை நீக்கு</x:String>
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">கிளை:</x:String>
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">நீங்கள் ஒரு தொலை கிளையை நீக்கப் போகிறீர்கள்!!!</x:String>
@@ -417,13 +414,10 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">நேற்று</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">விருப்பத்தேர்வுகள்</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">செநு</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">வேறுபாடு உடனடியாக பகுப்பாய்வு செய்</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">பநிஇ திறவுகோல்</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">பொருள் உடனடியாக உருவாக்கு</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">மாதிரி</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">பெயர்</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">சேவையகம்</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">ஓடையை இயக்கு</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">தோற்றம்</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">இயல்புநிலை எழுத்துரு</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">திருத்தி தாவல் அகலம்</x:String>
@@ -480,8 +474,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">தொலை கிளை:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">இதனுள்:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">உள்ளக மாற்றங்கள்:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">நிராகரி</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">பதுக்கிவை &amp; மீண்டும் இடு</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">தொலை:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">இழு (எடுத்து ஒன்றிணை)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">ஒன்றிணை என்பதற்குப் பதிலாக மறுதளத்தைப் பயன்படுத்து</x:String>

View File

@@ -19,7 +19,6 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI Асистент</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">ПЕРЕГЕНЕРУВАТИ</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Використати AI для генерації повідомлення коміту</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">ЗАСТОСУВАТИ ЯК ПОВІДОМЛЕННЯ КОМІТУ</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Застосувати</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Файл патчу:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Виберіть файл .patch для застосування</x:String>
@@ -71,8 +70,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Коміт:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Попередження: Перехід на коміт призведе до стану "від'єднаний HEAD"</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Локальні зміни:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Скасувати</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">Сховати та Застосувати</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Гілка:</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-pick</x:String>
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Додати джерело до повідомлення коміту</x:String>
@@ -186,8 +183,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">На основі:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Перейти на створену гілку</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Локальні зміни:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Скасувати</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">Сховати та Застосувати</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Назва нової гілки:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Введіть назву гілки.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Створити локальну гілку</x:String>
@@ -205,6 +200,8 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">легкий</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Утримуйте Ctrl для запуску без діалогу</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Вирізати</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">Скасувати</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">Сховати та Застосувати</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">Видалити гілку</x:String>
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">Гілка:</x:String>
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">Ви збираєтеся видалити віддалену гілку!!!</x:String>
@@ -421,13 +418,10 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Вчора</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">Налаштування</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Промпт для аналізу різниці</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Промпт для генерації теми</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Модель</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Назва</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Сервер</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">Увімкнути потокове відтворення</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">ВИГЛЯД</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Шрифт за замовчуванням</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Ширина табуляції в редакторі</x:String>
@@ -484,8 +478,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Віддалена гілка:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">В:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Локальні зміни:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Скасувати</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">Сховати та Застосувати</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Віддалене сховище:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch &amp; Merge)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Використовувати rebase замість merge</x:String>

View File

@@ -24,10 +24,11 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI助手</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">重新生成</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用AI助手生成提交信息</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">应用本次生成</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">应用所选</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隐藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">显示所有窗口</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">应用补丁(apply)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">尝试三路合并</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">补丁文件 </x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">选择补丁文件</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">忽略空白符号</x:String>
@@ -113,8 +114,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">提交 </x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">注意执行该操作后当前HEAD会变为游离(detached)状态!</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">未提交更改 </x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">贮藏并自动恢复</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">目标分支 </x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">您当前游离的HEAD包含未被任何分支及标签引用的提交是否继续</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">以下子模块需要更新:{0}是否立即更新?</x:String>
@@ -304,8 +303,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">新分支基于 </x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">完成后切换到新分支</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">未提交更改 </x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">贮藏并自动恢复</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">新分支名 </x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">填写分支名称。</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">创建本地分支</x:String>
@@ -324,6 +321,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">轻量标签</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">按住Ctrl键点击将以默认参数运行</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">剪切</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">不做处理</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">贮藏并自动恢复</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">取消初始化子模块</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">强制取消,即使包含本地变更</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">子模块 </x:String>
@@ -506,6 +506,7 @@
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">提交暂存区更改</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">提交暂存区更改并推送</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">自动暂存全部变更并提交</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CreateBranch" xml:space="preserve">新建分支</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Fetch" xml:space="preserve">拉取 (fetch) 远程变更</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">切换左边栏为分支/标签等显示模式(默认)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoToChild" xml:space="preserve">前往选中提交的子提交</x:String>
@@ -613,14 +614,12 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">昨天</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">偏好设置</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyze Diff Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">附加提示词 (请使用 `-` 列出您的要求)</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API密钥</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">Generate Subject Prompt</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">模型</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">配置名称</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">从环境变量填写环境变量名中读取API密钥</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">服务地址</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">启用流式输出</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">外观配置</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">编辑器制表符宽度</x:String>
@@ -691,8 +690,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 </x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 </x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 </x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">贮藏并自动恢复</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">远程 </x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">拉回(拉取并合并)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">使用变基方式合并分支</x:String>

View File

@@ -24,10 +24,11 @@
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI 助理</x:String>
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">重新產生</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用 AI 產生提交訊息</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">套用為提交訊息</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">套用選取</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隱藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">顯示所有</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">套用修補檔 (apply patch)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">嘗試三向合併</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">修補檔:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">選擇修補檔</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">忽略空白符號</x:String>
@@ -113,8 +114,6 @@
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">提交:</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">注意: 執行該操作後,目前 HEAD 會變為分離 (detached) 狀態!</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">未提交變更:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">捨棄變更</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReapply" xml:space="preserve">擱置變更並自動復原</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">目標分支:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">您目前的分離的 HEAD 包含與任何分支/標籤無關的提交! 您要繼續嗎?</x:String>
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">以下子模組需要更新: {0},您要立即更新嗎?</x:String>
@@ -285,7 +284,7 @@
<x:String x:Key="Text.ConfirmEmptyCommit.Continue" xml:space="preserve">確認繼續</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.NoLocalChanges" xml:space="preserve">未包含任何檔案變更! 您是否仍要提交 (--allow-empty)?</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageAllThenCommit" xml:space="preserve">暫存全部變更並提交</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageSelectedThenCommit" xml:space="preserve">僅暫存所選變更提交</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageSelectedThenCommit" xml:space="preserve">僅暫存所選變更提交</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.WithLocalChanges" xml:space="preserve">未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存變更並提交?</x:String>
<x:String x:Key="Text.ConfirmRestart.Title" xml:space="preserve">系統提示</x:String>
<x:String x:Key="Text.ConfirmRestart.Message" xml:space="preserve">您需要重新啟動此應用程式才能套用變更!</x:String>
@@ -304,8 +303,6 @@
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">新分支基於:</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">完成後切換到新分支</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">未提交變更:</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">捨棄變更</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReapply" xml:space="preserve">擱置變更並自動復原</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">新分支名稱:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">輸入分支名稱。</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">建立本機分支</x:String>
@@ -324,6 +321,9 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">輕量標籤</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">按住 Ctrl 鍵將直接以預設參數執行</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">剪下</x:String>
<x:String x:Key="Text.DealWithLocalChanges.Discard" xml:space="preserve">捨棄變更</x:String>
<x:String x:Key="Text.DealWithLocalChanges.DoNothing" xml:space="preserve">不做處理</x:String>
<x:String x:Key="Text.DealWithLocalChanges.StashAndReapply" xml:space="preserve">擱置變更並自動復原</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">取消初始化子模組</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">強制取消,即使包含本機變更</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">子模組: </x:String>
@@ -506,6 +506,7 @@
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">提交暫存區變更</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">提交暫存區變更並推送</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">自動暫存全部變更並提交</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CreateBranch" xml:space="preserve">新增分支</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Fetch" xml:space="preserve">提取 (fetch) 遠端的變更</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">切換左邊欄為分支/標籤等顯示模式 (預設)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoToChild" xml:space="preserve">前往所選提交的子提交</x:String>
@@ -528,8 +529,8 @@
<x:String x:Key="Text.Hunk.Stage" xml:space="preserve">暫存</x:String>
<x:String x:Key="Text.Hunk.Unstage" xml:space="preserve">取消暫存</x:String>
<x:String x:Key="Text.Init" xml:space="preserve">初始化存放庫</x:String>
<x:String x:Key="Text.Init.CommandTip" xml:space="preserve">在該路徑執行 git init 以初始化 git 倉庫?</x:String>
<x:String x:Key="Text.Init.ErrorMessageTip" xml:space="preserve">無法在指定路徑開啟本機存庫。原因</x:String>
<x:String x:Key="Text.Init.CommandTip" xml:space="preserve">您是否要在該路徑執行 git init 以進行初始化?</x:String>
<x:String x:Key="Text.Init.ErrorMessageTip" xml:space="preserve">無法在指定路徑開啟本機存庫。原因: </x:String>
<x:String x:Key="Text.Init.Path" xml:space="preserve">路徑:</x:String>
<x:String x:Key="Text.InProgress.CherryPick" xml:space="preserve">揀選 (cherry-pick) 操作進行中。</x:String>
<x:String x:Key="Text.InProgress.CherryPick.Head" xml:space="preserve">正在處理提交</x:String>
@@ -613,14 +614,12 @@
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">昨天</x:String>
<x:String x:Key="Text.Preferences" xml:space="preserve">偏好設定</x:String>
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.AnalyzeDiffPrompt" xml:space="preserve">分析變更差異提示詞</x:String>
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">附加提示詞 (請使用 '-' 列出您的要求)</x:String>
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API 金鑰</x:String>
<x:String x:Key="Text.Preferences.AI.GenerateSubjectPrompt" xml:space="preserve">產生提交訊息提示詞</x:String>
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">模型</x:String>
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">名稱</x:String>
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">從環境變數中 (輸入環境變數名稱) 讀取 API 金鑰</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">伺服器</x:String>
<x:String x:Key="Text.Preferences.AI.Streaming" xml:space="preserve">啟用串流輸出</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">外觀設定</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">預設字型</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">編輯器 Tab 寬度</x:String>
@@ -653,7 +652,7 @@
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">在提交詳細資訊中顯示後續提交</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">在路線圖中顯示標籤</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">提交標題字數偵測</x:String>
<x:String x:Key="Text.Preferences.General.Use24Hours" xml:space="preserve">24小時制</x:String>
<x:String x:Key="Text.Preferences.General.Use24Hours" xml:space="preserve">24 小時制</x:String>
<x:String x:Key="Text.Preferences.General.UseGitHubStyleAvatar" xml:space="preserve">產生 GitHub 風格的預設頭貼</x:String>
<x:String x:Key="Text.Preferences.Git" xml:space="preserve">Git 設定</x:String>
<x:String x:Key="Text.Preferences.Git.CRLF" xml:space="preserve">自動換行轉換</x:String>
@@ -691,8 +690,6 @@
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支:</x:String>
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本機分支:</x:String>
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交變更:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">捨棄變更</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReapply" xml:space="preserve">擱置變更並自動復原</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">遠端:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">拉取 (提取並合併)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">使用重定基底 (rebase) 合併分支</x:String>
@@ -981,7 +978,7 @@
<x:String x:Key="Text.Workspace" xml:space="preserve">工作區: </x:String>
<x:String x:Key="Text.Workspace.Configure" xml:space="preserve">設定工作區...</x:String>
<x:String x:Key="Text.Worktree" xml:space="preserve">本機工作區</x:String>
<x:String x:Key="Text.Worktree.Branch" xml:space="preserve">关联分支</x:String>
<x:String x:Key="Text.Worktree.Branch" xml:space="preserve">分支</x:String>
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">複製工作區路徑</x:String>
<x:String x:Key="Text.Worktree.Head" xml:space="preserve">最新提交</x:String>
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">鎖定工作區</x:String>

View File

@@ -1443,6 +1443,11 @@
</Style>
<Style Selector="aee|TextArea">
<Setter Property="FocusAdorner">
<FocusAdornerTemplate>
<Border/>
</FocusAdornerTemplate>
</Setter>
<Setter Property="SelectionBorder">
<Setter.Value>
<Pen Brush="{DynamicResource SystemAccentColor}" Thickness="1" />
@@ -1472,7 +1477,7 @@
Name="PART_searchTextBox"
Width="265" Height="28"
Margin="0,0,0,0"
CornerRadius="2"
CornerRadius="3"
Text="{Binding SearchPattern, RelativeSource={RelativeSource TemplatedParent}}">
<TextBox.InnerRightContent>
<StackPanel Orientation="Horizontal">

View File

@@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<AssemblyMetadata Include="BuildDate" Value="$([System.DateTime]::Now.ToString('o'))"/>
<AssemblyMetadata Include="BuildDate" Value="$([System.DateTime]::Now.ToString('o'))" />
</ItemGroup>
<ItemGroup>
@@ -53,14 +53,14 @@
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.12" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.8.0-beta.1" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageReference Include="BitMiracle.LibTiff.NET" Version="2.4.660" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc6.1" />
<PackageReference Include="OpenAI" Version="2.8.0" />
<PackageReference Include="OpenAI" Version="2.9.1" />
<PackageReference Include="Pfim" Version="0.11.4" />
<ProjectReference Include="../depends/AvaloniaEdit/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj"/>
<ProjectReference Include="../depends/AvaloniaEdit/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@@ -22,27 +22,59 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _text, value);
}
public AIAssistant(Repository repo, Models.OpenAIService service, List<Models.Change> changes)
public AIAssistant(Repository repo, AI.Service service, List<Models.Change> changes)
{
_repo = repo;
_service = service;
_changes = changes;
_cancel = new CancellationTokenSource();
Gen();
var builder = new StringBuilder();
foreach (var c in changes)
SerializeChange(c, builder);
_changeList = builder.ToString();
}
public void Regen()
public async Task GenAsync()
{
if (_cancel is { IsCancellationRequested: false })
_cancel.Cancel();
_cancel = new CancellationTokenSource();
Gen();
var agent = new AI.Agent(_service);
var builder = new StringBuilder();
builder.AppendLine("Asking AI to generate commit message...").AppendLine();
Text = builder.ToString();
IsGenerating = true;
try
{
await agent.GenerateCommitMessageAsync(_repo.FullPath, _changeList, message =>
{
builder.AppendLine(message);
Dispatcher.UIThread.Post(() => Text = builder.ToString());
}, _cancel.Token);
}
catch (OperationCanceledException)
{
// Do nothing and leave `IsGenerating` to current (may already changed), so that the UI can update accordingly.
return;
}
catch (Exception e)
{
builder
.AppendLine()
.AppendLine("[ERROR]")
.Append(e.Message);
Text = builder.ToString();
}
IsGenerating = false;
}
public void Apply()
public void Use(string text)
{
_repo.SetCommitMessage(Text);
_repo.SetCommitMessage(text);
}
public void Cancel()
@@ -50,26 +82,30 @@ namespace SourceGit.ViewModels
_cancel?.Cancel();
}
private void Gen()
private void SerializeChange(Models.Change c, StringBuilder builder)
{
Text = string.Empty;
IsGenerating = true;
_cancel = new CancellationTokenSource();
Task.Run(async () =>
var status = c.Index switch
{
await new Commands.GenerateCommitMessage(_service, _repo.FullPath, _changes, _cancel.Token, message =>
{
Dispatcher.UIThread.Post(() => Text = message);
}).ExecAsync().ConfigureAwait(false);
Models.ChangeState.Added => "A",
Models.ChangeState.Modified => "M",
Models.ChangeState.Deleted => "D",
Models.ChangeState.TypeChanged => "T",
Models.ChangeState.Renamed => "R",
Models.ChangeState.Copied => "C",
_ => " ",
};
Dispatcher.UIThread.Post(() => IsGenerating = false);
}, _cancel.Token);
builder.Append(status).Append('\t');
if (c.Index == Models.ChangeState.Renamed || c.Index == Models.ChangeState.Copied)
builder.Append(c.OriginalPath).Append(" -> ").Append(c.Path).AppendLine();
else
builder.Append(c.Path).AppendLine();
}
private readonly Repository _repo = null;
private Models.OpenAIService _service = null;
private List<Models.Change> _changes = null;
private readonly AI.Service _service = null;
private readonly string _changeList = null;
private CancellationTokenSource _cancel = null;
private bool _isGenerating = false;
private string _text = string.Empty;

View File

@@ -26,6 +26,12 @@ namespace SourceGit.ViewModels
set;
}
public bool ThreeWayMerge
{
get;
set;
}
public Apply(Repository repo)
{
_repo = repo;
@@ -49,7 +55,8 @@ namespace SourceGit.ViewModels
var log = _repo.CreateLog("Apply Patch");
Use(log);
var succ = await new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg, null)
var extra = ThreeWayMerge ? "--3way" : string.Empty;
var succ = await new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg, extra)
.Use(log)
.ExecAsync();

View File

@@ -4,35 +4,36 @@ namespace SourceGit.ViewModels
{
public class Checkout : Popup
{
public string Branch
public string BranchName
{
get;
get => _branch.Name;
}
public bool DiscardLocalChanges
public bool HasLocalChanges
{
get => _repo.LocalChangesCount > 0;
}
public Models.DealWithLocalChanges DealWithLocalChanges
{
get;
set;
}
public Checkout(Repository repo, string branch)
public Checkout(Repository repo, Models.Branch branch)
{
_repo = repo;
Branch = branch;
DiscardLocalChanges = false;
}
public override bool CanStartDirectly()
{
return _repo.LocalChangesCount == 0;
_branch = branch;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
}
public override async Task<bool> Sure()
{
using var lockWatcher = _repo.LockWatcher();
ProgressDescription = $"Checkout '{Branch}' ...";
var branchName = BranchName;
ProgressDescription = $"Checkout '{branchName}' ...";
var log = _repo.CreateLog($"Checkout '{Branch}'");
var log = _repo.CreateLog($"Checkout '{branchName}'");
Use(log);
if (_repo.CurrentBranch is { IsDetachedHead: true })
@@ -50,7 +51,13 @@ namespace SourceGit.ViewModels
var succ = false;
var needPopStash = false;
if (!DiscardLocalChanges)
if (DealWithLocalChanges == Models.DealWithLocalChanges.DoNothing)
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(branchName, false);
}
else if (DealWithLocalChanges == Models.DealWithLocalChanges.StashAndReapply)
{
var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync();
if (changes > 0)
@@ -61,16 +68,23 @@ namespace SourceGit.ViewModels
if (!succ)
{
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
return false;
}
needPopStash = true;
}
}
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(Branch, DiscardLocalChanges);
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(branchName, false);
}
else
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(branchName, true);
}
if (succ)
{
@@ -80,21 +94,19 @@ namespace SourceGit.ViewModels
await new Commands.Stash(_repo.FullPath)
.Use(log)
.PopAsync("stash@{0}");
_repo.RefreshAfterCheckoutBranch(_branch);
}
else
{
_repo.MarkWorkingCopyDirtyManually();
}
log.Complete();
var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch);
if (b != null && _repo.HistoryFilterMode == Models.FilterMode.Included)
_repo.SetBranchFilterMode(b, Models.FilterMode.Included, false, false);
_repo.MarkBranchesDirtyManually();
ProgressDescription = "Waiting for branch updated...";
await Task.Delay(400);
return succ;
}
private readonly Repository _repo = null;
private readonly Models.Branch _branch = null;
}
}

View File

@@ -14,7 +14,12 @@ namespace SourceGit.ViewModels
get;
}
public bool DiscardLocalChanges
public bool HasLocalChanges
{
get => _repo.LocalChangesCount > 0;
}
public Models.DealWithLocalChanges DealWithLocalChanges
{
get;
set;
@@ -25,6 +30,7 @@ namespace SourceGit.ViewModels
_repo = repo;
LocalBranch = localBranch;
RemoteBranch = remoteBranch;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
}
public override async Task<bool> Sure()
@@ -50,7 +56,13 @@ namespace SourceGit.ViewModels
var succ = false;
var needPopStash = false;
if (!DiscardLocalChanges)
if (DealWithLocalChanges == Models.DealWithLocalChanges.DoNothing)
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(LocalBranch.Name, RemoteBranch.Head, false, true);
}
else if (DealWithLocalChanges == Models.DealWithLocalChanges.StashAndReapply)
{
var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync();
if (changes > 0)
@@ -61,16 +73,23 @@ namespace SourceGit.ViewModels
if (!succ)
{
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
return false;
}
needPopStash = true;
}
}
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(LocalBranch.Name, RemoteBranch.Head, DiscardLocalChanges, true);
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(LocalBranch.Name, RemoteBranch.Head, false, true);
}
else
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(LocalBranch.Name, RemoteBranch.Head, true, true);
}
if (succ)
{
@@ -80,17 +99,19 @@ namespace SourceGit.ViewModels
await new Commands.Stash(_repo.FullPath)
.Use(log)
.PopAsync("stash@{0}");
LocalBranch.Behind.Clear();
LocalBranch.Head = RemoteBranch.Head;
LocalBranch.CommitterDate = RemoteBranch.CommitterDate;
_repo.RefreshAfterCheckoutBranch(LocalBranch);
}
else
{
_repo.MarkWorkingCopyDirtyManually();
}
log.Complete();
if (_repo.HistoryFilterMode == Models.FilterMode.Included)
_repo.SetBranchFilterMode(LocalBranch, Models.FilterMode.Included, false, false);
_repo.MarkBranchesDirtyManually();
ProgressDescription = "Waiting for branch updated...";
await Task.Delay(400);
return succ;
}

View File

@@ -9,7 +9,12 @@ namespace SourceGit.ViewModels
get;
}
public bool DiscardLocalChanges
public bool HasLocalChanges
{
get => _repo.LocalChangesCount > 0;
}
public Models.DealWithLocalChanges DealWithLocalChanges
{
get;
set;
@@ -19,7 +24,7 @@ namespace SourceGit.ViewModels
{
_repo = repo;
Commit = commit;
DiscardLocalChanges = false;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
}
public override async Task<bool> Sure()
@@ -45,7 +50,13 @@ namespace SourceGit.ViewModels
var succ = false;
var needPop = false;
if (!DiscardLocalChanges)
if (DealWithLocalChanges == Models.DealWithLocalChanges.DoNothing)
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.CommitAsync(Commit.SHA, false);
}
else if (DealWithLocalChanges == Models.DealWithLocalChanges.StashAndReapply)
{
var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync();
if (changes > 0)
@@ -56,16 +67,23 @@ namespace SourceGit.ViewModels
if (!succ)
{
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
return false;
}
needPop = true;
}
}
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.CommitAsync(Commit.SHA, DiscardLocalChanges);
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.CommitAsync(Commit.SHA, false);
}
else
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.CommitAsync(Commit.SHA, true);
}
if (succ)
{

View File

@@ -240,8 +240,13 @@ namespace SourceGit.ViewModels
if (_commit == null)
return;
var baseRevision = _commit.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : _commit.Parents[0];
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo.FullPath, changes, baseRevision, _commit.SHA, saveTo);
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(
_repo.FullPath,
changes,
_commit.FirstParentToCompare,
_commit.SHA,
saveTo);
if (succ)
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
}
@@ -535,8 +540,7 @@ namespace SourceGit.ViewModels
Task.Run(async () =>
{
var parent = _commit.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : $"{_commit.SHA}^";
var cmd = new Commands.CompareRevisions(_repo.FullPath, parent, _commit.SHA) { CancellationToken = token };
var cmd = new Commands.CompareRevisions(_repo.FullPath, _commit.FirstParentToCompare, _commit.SHA) { CancellationToken = token };
var changes = await cmd.ReadAsync().ConfigureAwait(false);
var visible = changes;
if (!string.IsNullOrWhiteSpace(_searchChangeFilter))
@@ -757,7 +761,7 @@ namespace SourceGit.ViewModels
[GeneratedRegex(@"\b(https?://|ftp://)[\w\d\._/\-~%@()+:?&=#!]*[\w\d/]")]
private static partial Regex REG_URL_FORMAT();
[GeneratedRegex(@"\b([0-9a-fA-F]{6,40})\b")]
[GeneratedRegex(@"\b([0-9a-fA-F]{6,64})\b")]
private static partial Regex REG_SHA_FORMAT();
[GeneratedRegex(@"`.*?`")]

View File

@@ -20,7 +20,12 @@ namespace SourceGit.ViewModels
get;
}
public bool DiscardLocalChanges
public bool HasLocalChanges
{
get => _repo.LocalChangesCount > 0;
}
public Models.DealWithLocalChanges DealWithLocalChanges
{
get;
set;
@@ -35,6 +40,7 @@ namespace SourceGit.ViewModels
{
_repo.UIStates.CheckoutBranchOnCreateBranch = value;
OnPropertyChanged();
UpdateOverrideTip();
}
}
}
@@ -44,6 +50,12 @@ namespace SourceGit.ViewModels
get => _repo.IsBare;
}
public string OverrideTip
{
get => _overrideTip;
private set => SetProperty(ref _overrideTip, value);
}
public bool AllowOverwrite
{
get => _allowOverwrite;
@@ -58,30 +70,39 @@ namespace SourceGit.ViewModels
{
_repo = repo;
_baseOnRevision = branch.Head;
_committerDate = branch.CommitterDate;
_head = branch.Head;
if (!branch.IsLocal)
Name = branch.Name;
BasedOn = branch;
DiscardLocalChanges = false;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
UpdateOverrideTip();
}
public CreateBranch(Repository repo, Models.Commit commit)
{
_repo = repo;
_baseOnRevision = commit.SHA;
_committerDate = commit.CommitterTime;
_head = commit.SHA;
BasedOn = commit;
DiscardLocalChanges = false;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
UpdateOverrideTip();
}
public CreateBranch(Repository repo, Models.Tag tag)
{
_repo = repo;
_baseOnRevision = tag.SHA;
_committerDate = tag.CreatorDate;
_head = tag.SHA;
BasedOn = tag;
DiscardLocalChanges = false;
DealWithLocalChanges = Models.DealWithLocalChanges.DoNothing;
UpdateOverrideTip();
}
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx)
@@ -125,11 +146,26 @@ namespace SourceGit.ViewModels
}
}
Models.Branch created = new()
{
Name = _name,
FullName = $"refs/heads/{_name}",
CommitterDate = _committerDate,
Head = _head,
IsLocal = true,
};
bool succ;
if (CheckoutAfterCreated && !_repo.IsBare)
{
var needPopStash = false;
if (!DiscardLocalChanges)
if (DealWithLocalChanges == Models.DealWithLocalChanges.DoNothing)
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(_name, _baseOnRevision, false, _allowOverwrite);
}
else if (DealWithLocalChanges == Models.DealWithLocalChanges.StashAndReapply)
{
var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync();
if (changes > 0)
@@ -140,16 +176,23 @@ namespace SourceGit.ViewModels
if (!succ)
{
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
return false;
}
needPopStash = true;
}
}
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(_name, _baseOnRevision, DiscardLocalChanges, _allowOverwrite);
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(_name, _baseOnRevision, false, _allowOverwrite);
}
else
{
succ = await new Commands.Checkout(_repo.FullPath)
.Use(log)
.BranchAsync(_name, _baseOnRevision, true, _allowOverwrite);
}
if (succ)
{
@@ -168,43 +211,39 @@ namespace SourceGit.ViewModels
.CreateAsync(_baseOnRevision, _allowOverwrite);
}
if (succ && BasedOn is Models.Branch { IsLocal: false } basedOn && _name.Equals(basedOn.Name, StringComparison.Ordinal))
if (succ)
{
await new Commands.Branch(_repo.FullPath, _name)
if (BasedOn is Models.Branch { IsLocal: false } basedOn && _name.Equals(basedOn.Name, StringComparison.Ordinal))
{
await new Commands.Branch(_repo.FullPath, _name)
.Use(log)
.SetUpstreamAsync(basedOn);
created.Upstream = basedOn.FullName;
}
_repo.RefreshAfterCreateBranch(created, CheckoutAfterCreated);
}
else
{
_repo.MarkWorkingCopyDirtyManually();
}
log.Complete();
if (succ && CheckoutAfterCreated)
{
var fake = new Models.Branch() { IsLocal = true, FullName = $"refs/heads/{_name}" };
if (BasedOn is Models.Branch { IsLocal: false } based)
fake.Upstream = based.FullName;
var folderEndIdx = fake.FullName.LastIndexOf('/');
if (folderEndIdx > 10)
_repo.UIStates.ExpandedBranchNodesInSideBar.Add(fake.FullName.Substring(0, folderEndIdx));
if (_repo.HistoryFilterMode == Models.FilterMode.Included)
_repo.SetBranchFilterMode(fake, Models.FilterMode.Included, false, false);
}
_repo.MarkBranchesDirtyManually();
if (CheckoutAfterCreated)
{
ProgressDescription = "Waiting for branch updated...";
await Task.Delay(400);
}
return true;
}
private void UpdateOverrideTip()
{
OverrideTip = CheckoutAfterCreated ? "-B in `git checkout`" : "-f in `git branch`";
}
private readonly Repository _repo = null;
private string _name = null;
private readonly string _baseOnRevision = null;
private readonly ulong _committerDate = 0;
private readonly string _head = string.Empty;
private string _name = null;
private string _overrideTip = "-B";
private bool _allowOverwrite = false;
}
}

View File

@@ -6,10 +6,19 @@ namespace SourceGit.ViewModels
{
public class EditRepositoryNode : Popup
{
public string Id
public string Target
{
get => _id;
set => SetProperty(ref _id, value);
get;
}
public bool IsRepository
{
get;
}
public List<int> Bookmarks
{
get;
}
[Required(ErrorMessage = "Name is required!")]
@@ -19,31 +28,20 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _name, value, true);
}
public List<int> Bookmarks
{
get;
}
public int Bookmark
{
get => _bookmark;
set => SetProperty(ref _bookmark, value);
}
public bool IsRepository
{
get => _isRepository;
set => SetProperty(ref _isRepository, value);
}
public EditRepositoryNode(RepositoryNode node)
{
_node = node;
_id = node.Id;
_name = node.Name;
_isRepository = node.IsRepository;
_bookmark = node.Bookmark;
Target = node.IsRepository ? node.Id : node.Name;
IsRepository = node.IsRepository;
Bookmarks = new List<int>();
for (var i = 0; i < Models.Bookmarks.Brushes.Length; i++)
Bookmarks.Add(i);
@@ -65,9 +63,7 @@ namespace SourceGit.ViewModels
}
private RepositoryNode _node = null;
private string _id = null;
private string _name = null;
private bool _isRepository = false;
private int _bookmark = 0;
}
}

View File

@@ -2,10 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@@ -17,15 +14,27 @@ namespace SourceGit.ViewModels
public bool CanOpenWithDefaultEditor { get; set; } = canOpenWithDefaultEditor;
}
public class FileHistoriesSingleRevisionViewMode
{
public bool IsDiff
{
get;
set;
} = true;
}
public class FileHistoriesSingleRevision : ObservableObject
{
public bool IsDiffMode
{
get => _isDiffMode;
get => _viewMode.IsDiff;
set
{
if (SetProperty(ref _isDiffMode, value))
if (_viewMode.IsDiff != value)
{
_viewMode.IsDiff = value;
RefreshViewContent();
}
}
}
@@ -35,17 +44,24 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _viewContent, value);
}
public FileHistoriesSingleRevision(string repo, Models.FileVersion revision, bool prevIsDiffMode)
public FileHistoriesSingleRevision(string repo, Models.FileVersion revision, FileHistoriesSingleRevisionViewMode viewMode)
{
_repo = repo;
_file = revision.Path;
_revision = revision;
_isDiffMode = prevIsDiffMode;
_viewMode = viewMode;
_viewContent = null;
RefreshViewContent();
}
public void SetRevision(Models.FileVersion revision)
{
_file = revision.Path;
_revision = revision;
RefreshViewContent();
}
public async Task<bool> ResetToSelectedRevisionAsync()
{
return await new Commands.Checkout(_repo)
@@ -72,7 +88,7 @@ namespace SourceGit.ViewModels
private void RefreshViewContent()
{
if (_isDiffMode)
if (_viewMode.IsDiff)
{
ViewContent = new DiffContext(_repo, new(_revision), _viewContent as DiffContext);
return;
@@ -155,7 +171,7 @@ namespace SourceGit.ViewModels
private string _repo = null;
private string _file = null;
private Models.FileVersion _revision = null;
private bool _isDiffMode = false;
private FileHistoriesSingleRevisionViewMode _viewMode = null;
private object _viewContent = null;
}
@@ -226,11 +242,15 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _revisions, value);
}
public AvaloniaList<Models.FileVersion> SelectedRevisions
public List<Models.FileVersion> SelectedRevisions
{
get;
set;
} = [];
get => _selectedRevisions;
set
{
if (SetProperty(ref _selectedRevisions, value))
RefreshViewContent();
}
}
public object ViewContent
{
@@ -257,23 +277,8 @@ namespace SourceGit.ViewModels
{
IsLoading = false;
Revisions = revisions;
if (revisions.Count > 0)
SelectedRevisions.Add(revisions[0]);
});
});
SelectedRevisions.CollectionChanged += (_, _) =>
{
if (_viewContent is FileHistoriesSingleRevision singleRevision)
_prevIsDiffMode = singleRevision.IsDiffMode;
ViewContent = SelectedRevisions.Count switch
{
1 => new FileHistoriesSingleRevision(_repo, SelectedRevisions[0], _prevIsDiffMode),
2 => new FileHistoriesCompareRevisions(_repo, SelectedRevisions[0], SelectedRevisions[1]),
_ => SelectedRevisions.Count,
};
};
}
public void NavigateToCommit(Models.FileVersion revision)
@@ -303,10 +308,35 @@ namespace SourceGit.ViewModels
return msg;
}
private void RefreshViewContent()
{
var count = _selectedRevisions?.Count ?? 0;
if (count == 0)
{
ViewContent = null;
}
else if (count == 1)
{
if (_viewContent is FileHistoriesSingleRevision single)
single.SetRevision(_selectedRevisions[0]);
else
ViewContent = new FileHistoriesSingleRevision(_repo, _selectedRevisions[0], _viewMode);
}
else if (count == 2)
{
ViewContent = new FileHistoriesCompareRevisions(_repo, _selectedRevisions[0], _selectedRevisions[1]);
}
else
{
ViewContent = _selectedRevisions.Count;
}
}
private readonly string _repo = null;
private bool _isLoading = true;
private bool _prevIsDiffMode = true;
private FileHistoriesSingleRevisionViewMode _viewMode = new();
private List<Models.FileVersion> _revisions = null;
private List<Models.FileVersion> _selectedRevisions = [];
private Dictionary<string, string> _fullCommitMessages = new();
private object _viewContent = null;
}

View File

@@ -480,7 +480,7 @@ namespace SourceGit.ViewModels
set;
} = [];
public AvaloniaList<Models.OpenAIService> OpenAIServices
public AvaloniaList<AI.Service> OpenAIServices
{
get;
set;

View File

@@ -38,11 +38,16 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _selectedBranch, value, true);
}
public bool DiscardLocalChanges
public bool HasLocalChanges
{
get => _repo.LocalChangesCount > 0;
}
public Models.DealWithLocalChanges DealWithLocalChanges
{
get;
set;
} = false;
} = Models.DealWithLocalChanges.DoNothing;
public bool UseRebase
{
@@ -111,21 +116,26 @@ namespace SourceGit.ViewModels
var needPopStash = false;
if (changes > 0)
{
if (DiscardLocalChanges)
if (DealWithLocalChanges == Models.DealWithLocalChanges.DoNothing)
{
await Commands.Discard.AllAsync(_repo.FullPath, false, false, log);
// Do nothing, just let the pull command fail and show the error to user
}
else
else if (DealWithLocalChanges == Models.DealWithLocalChanges.StashAndReapply)
{
var succ = await new Commands.Stash(_repo.FullPath).Use(log).PushAsync("PULL_AUTO_STASH", false);
if (!succ)
{
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
return false;
}
needPopStash = true;
}
else
{
await Commands.Discard.AllAsync(_repo.FullPath, false, false, log);
}
}
bool rs = await new Commands.Pull(

View File

@@ -60,17 +60,9 @@ namespace SourceGit.ViewModels
.RenameAsync(_name);
if (succ)
_repo.UIStates.RenameBranchFilter(Target.FullName, _name);
_repo.RefreshAfterRenameBranch(Target, _name);
log.Complete();
_repo.MarkBranchesDirtyManually();
if (isCurrent)
{
ProgressDescription = "Waiting for branch updated...";
await Task.Delay(400);
}
return succ;
}

View File

@@ -788,6 +788,113 @@ namespace SourceGit.ViewModels
return _watcher?.Lock();
}
public void RefreshAfterCreateBranch(Models.Branch created, bool checkout)
{
_watcher?.MarkBranchUpdated();
_watcher?.MarkWorkingCopyUpdated();
_branches.Add(created);
if (checkout)
{
if (_currentBranch.IsDetachedHead)
{
_branches.Remove(_currentBranch);
}
else
{
_currentBranch.IsCurrent = false;
_currentBranch.WorktreePath = null;
}
created.IsCurrent = true;
created.WorktreePath = FullPath;
var folderEndIdx = created.FullName.LastIndexOf('/');
if (folderEndIdx > 10)
_uiStates.ExpandedBranchNodesInSideBar.Add(created.FullName.Substring(0, folderEndIdx));
if (_historyFilterMode == Models.FilterMode.Included)
SetBranchFilterMode(created, Models.FilterMode.Included, false, false);
CurrentBranch = created;
}
List<Models.Branch> locals = [];
foreach (var b in _branches)
{
if (b.IsLocal)
locals.Add(b);
}
var builder = BuildBranchTree(locals, []);
LocalBranchTrees = builder.Locals;
RefreshCommits();
RefreshWorkingCopyChanges();
RefreshWorktrees();
}
public void RefreshAfterCheckoutBranch(Models.Branch checkouted)
{
_watcher?.MarkBranchUpdated();
_watcher?.MarkWorkingCopyUpdated();
if (_currentBranch.IsDetachedHead)
{
_branches.Remove(_currentBranch);
}
else
{
_currentBranch.IsCurrent = false;
_currentBranch.WorktreePath = null;
}
checkouted.IsCurrent = true;
checkouted.WorktreePath = FullPath;
if (_historyFilterMode == Models.FilterMode.Included)
SetBranchFilterMode(checkouted, Models.FilterMode.Included, false, false);
List<Models.Branch> locals = [];
foreach (var b in _branches)
{
if (b.IsLocal)
locals.Add(b);
}
var builder = BuildBranchTree(locals, []);
LocalBranchTrees = builder.Locals;
CurrentBranch = checkouted;
RefreshCommits();
RefreshWorkingCopyChanges();
RefreshWorktrees();
}
public void RefreshAfterRenameBranch(Models.Branch b, string newName)
{
_watcher?.MarkBranchUpdated();
var newFullName = $"refs/heads/{newName}";
_uiStates.RenameBranchFilter(b.FullName, newFullName);
b.Name = newName;
b.FullName = newFullName;
List<Models.Branch> locals = [];
foreach (var branch in _branches)
{
if (branch.IsLocal)
locals.Add(branch);
}
var builder = BuildBranchTree(locals, []);
LocalBranchTrees = builder.Locals;
RefreshCommits();
RefreshWorktrees();
}
public void MarkBranchesDirtyManually()
{
_watcher?.MarkBranchUpdated();
@@ -1219,17 +1326,14 @@ namespace SourceGit.ViewModels
.GetResultAsync()
.ConfigureAwait(false);
if (_workingCopy == null || token.IsCancellationRequested)
return;
changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path));
_workingCopy.SetData(changes, token);
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
_workingCopy.SetData(changes);
LocalChangesCount = changes.Count;
OnPropertyChanged(nameof(InProgressContext));
GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0);
@@ -1304,7 +1408,10 @@ namespace SourceGit.ViewModels
if (branch.IsLocal)
{
await ShowAndStartPopupAsync(new Checkout(this, branch.Name));
if (_workingCopy is { CanSwitchBranchDirectly: true })
await ShowAndStartPopupAsync(new Checkout(this, branch));
else
ShowPopup(new Checkout(this, branch));
}
else
{
@@ -1492,7 +1599,7 @@ namespace SourceGit.ViewModels
log.Complete();
}
public List<Models.OpenAIService> GetPreferredOpenAIServices()
public List<AI.Service> GetPreferredOpenAIServices()
{
var services = Preferences.Instance.OpenAIServices;
if (services == null || services.Count == 0)
@@ -1502,7 +1609,7 @@ namespace SourceGit.ViewModels
return [services[0]];
var preferred = _settings.PreferredOpenAIService;
var all = new List<Models.OpenAIService>();
var all = new List<AI.Service>();
foreach (var service in services)
{
if (service.Name.Equals(preferred, StringComparison.Ordinal))

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -105,6 +106,12 @@ namespace SourceGit.ViewModels
IsQuerying = true;
if (_cancellation is { IsCancellationRequested: false })
_cancellation.Cancel();
_cancellation = new();
var token = _cancellation.Token;
Task.Run(async () =>
{
var result = new List<Models.Commit>();
@@ -158,17 +165,23 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Post(() =>
{
IsQuerying = false;
if (token.IsCancellationRequested)
return;
IsQuerying = false;
if (_repo.IsSearchingCommits)
Results = result;
});
});
}, token);
}
public void EndSearch()
{
if (_cancellation is { IsCancellationRequested: false })
_cancellation.Cancel();
_worktreeFiles = null;
IsQuerying = false;
Suggestions = null;
Results = null;
GC.Collect();
@@ -228,6 +241,7 @@ namespace SourceGit.ViewModels
}
private Repository _repo = null;
private CancellationTokenSource _cancellation = null;
private int _method = (int)Models.CommitSearchMethod.ByMessage;
private string _filter = string.Empty;
private bool _onlySearchCurrentBranch = false;

View File

@@ -63,7 +63,7 @@ namespace SourceGit.ViewModels
var untracked = new List<Models.Change>();
if (value.Parents.Count == 3)
{
untracked = await new Commands.CompareRevisions(_repo.FullPath, Models.Commit.EmptyTreeSHA1, value.Parents[2])
untracked = await new Commands.CompareRevisions(_repo.FullPath, value.UntrackedParent, value.Parents[2])
.ReadAsync()
.ConfigureAwait(false);
@@ -107,7 +107,7 @@ namespace SourceGit.ViewModels
if (value is not { Count: 1 })
DiffContext = null;
else if (_untracked.Contains(value[0]))
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(Models.Commit.EmptyTreeSHA1, _selectedStash.Parents[2], value[0]), _diffContext);
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], value[0]), _diffContext);
else
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, value[0]), _diffContext);
}
@@ -167,16 +167,16 @@ namespace SourceGit.ViewModels
.ConfigureAwait(false);
foreach (var c in changes)
opts.Add(new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, c));
opts.Add(new Models.DiffOption(stash.Parents[0], stash.SHA, c));
if (stash.Parents.Count == 3)
{
var untracked = await new Commands.CompareRevisions(_repo.FullPath, Models.Commit.EmptyTreeSHA1, stash.Parents[2])
var untracked = await new Commands.CompareRevisions(_repo.FullPath, stash.UntrackedParent, stash.Parents[2])
.ReadAsync()
.ConfigureAwait(false);
foreach (var c in untracked)
opts.Add(new Models.DiffOption(Models.Commit.EmptyTreeSHA1, _selectedStash.Parents[2], c));
opts.Add(new Models.DiffOption(stash.UntrackedParent, stash.Parents[2], c));
changes.AddRange(untracked);
}
@@ -190,7 +190,7 @@ namespace SourceGit.ViewModels
{
Models.DiffOption opt;
if (_untracked.Contains(change))
opt = new Models.DiffOption(Models.Commit.EmptyTreeSHA1, _selectedStash.Parents[2], change);
opt = new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], change);
else
opt = new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, change);
@@ -242,7 +242,7 @@ namespace SourceGit.ViewModels
foreach (var c in changes)
{
if (_untracked.Contains(c) && _selectedStash.Parents.Count == 3)
opts.Add(new Models.DiffOption(Models.Commit.EmptyTreeSHA1, _selectedStash.Parents[2], c));
opts.Add(new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], c));
else
opts.Add(new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, c));
}

View File

@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@@ -41,6 +38,12 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _hasUnsolvedConflicts, value);
}
public bool CanSwitchBranchDirectly
{
get;
set;
} = true;
public InProgressContext InProgressContext
{
get => _inProgressContext;
@@ -256,64 +259,67 @@ namespace SourceGit.ViewModels
_commitMessage = string.Empty;
}
public void SetData(List<Models.Change> changes, CancellationToken cancellationToken)
public void SetData(List<Models.Change> changes)
{
if (!IsChanged(_cached, changes))
{
// Just force refresh selected changes.
Dispatcher.UIThread.Invoke(() =>
{
if (cancellationToken.IsCancellationRequested)
return;
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
UpdateInProgressState();
UpdateDetail();
});
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
UpdateInProgressState();
UpdateDetail();
return;
}
var lastSelectedUnstaged = new HashSet<string>();
var lastSelectedStaged = new HashSet<string>();
if (_selectedUnstaged is { Count: > 0 })
{
foreach (var c in _selectedUnstaged)
lastSelectedUnstaged.Add(c.Path);
}
else if (_selectedStaged is { Count: > 0 })
{
foreach (var c in _selectedStaged)
lastSelectedStaged.Add(c.Path);
}
var unstaged = new List<Models.Change>();
var visibleUnstaged = new List<Models.Change>();
var selectedUnstaged = new List<Models.Change>();
var noFilter = string.IsNullOrEmpty(_filter);
var hasConflict = false;
var canSwitchDirectly = true;
foreach (var c in changes)
{
if (c.WorkTree != Models.ChangeState.None)
{
unstaged.Add(c);
hasConflict |= c.IsConflicted;
}
}
var visibleUnstaged = GetVisibleChanges(unstaged);
var selectedUnstaged = new List<Models.Change>();
foreach (var c in visibleUnstaged)
{
if (lastSelectedUnstaged.Contains(c.Path))
selectedUnstaged.Add(c);
if (noFilter || c.Path.Contains(_filter, StringComparison.OrdinalIgnoreCase))
{
visibleUnstaged.Add(c);
if (lastSelectedUnstaged.Contains(c.Path))
selectedUnstaged.Add(c);
}
}
if (!canSwitchDirectly)
continue;
if (c.WorkTree == Models.ChangeState.Untracked || c.Index == Models.ChangeState.Added)
continue;
canSwitchDirectly = false;
}
var staged = GetStagedChanges(changes);
var visibleStaged = GetVisibleChanges(staged);
var selectedStaged = new List<Models.Change>();
foreach (var c in visibleStaged)
if (_selectedStaged is { Count: > 0 })
{
if (lastSelectedStaged.Contains(c.Path))
selectedStaged.Add(c);
var set = new HashSet<string>();
foreach (var c in _selectedStaged)
set.Add(c.Path);
foreach (var c in visibleStaged)
{
if (set.Contains(c.Path))
selectedStaged.Add(c);
}
}
if (selectedUnstaged.Count == 0 && selectedStaged.Count == 0 && hasConflict)
@@ -322,25 +328,20 @@ namespace SourceGit.ViewModels
selectedUnstaged.Add(firstConflict);
}
Dispatcher.UIThread.Invoke(() =>
{
if (cancellationToken.IsCancellationRequested)
return;
_isLoadingData = true;
_cached = changes;
HasUnsolvedConflicts = hasConflict;
CanSwitchBranchDirectly = canSwitchDirectly;
VisibleUnstaged = visibleUnstaged;
VisibleStaged = visibleStaged;
Unstaged = unstaged;
Staged = staged;
SelectedUnstaged = selectedUnstaged;
SelectedStaged = selectedStaged;
_isLoadingData = false;
_isLoadingData = true;
_cached = changes;
HasUnsolvedConflicts = hasConflict;
VisibleUnstaged = visibleUnstaged;
VisibleStaged = visibleStaged;
Unstaged = unstaged;
Staged = staged;
SelectedUnstaged = selectedUnstaged;
SelectedStaged = selectedStaged;
_isLoadingData = false;
UpdateInProgressState();
UpdateDetail();
});
UpdateInProgressState();
UpdateDetail();
}
public async Task StageChangesAsync(List<Models.Change> changes, Models.Change next)
@@ -671,8 +672,11 @@ namespace SourceGit.ViewModels
if (succ)
{
// Do not use property `UseAmend` but manually trigger property changed to avoid refreshing staged changes here.
_useAmend = false;
OnPropertyChanged(nameof(UseAmend));
CommitMessage = string.Empty;
UseAmend = false;
if (autoPush && _repo.Remotes.Count > 0)
{
Models.Branch pushBranch = null;
@@ -737,10 +741,7 @@ namespace SourceGit.ViewModels
private List<Models.Change> GetStagedChanges(List<Models.Change> cached)
{
if (_useAmend)
{
var head = new Commands.QuerySingleCommit(_repo.FullPath, "HEAD").GetResult();
return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : $"{head.SHA}^").GetResult();
}
return new Commands.QueryStagedChangesWithAmend(_repo.FullPath).GetResult();
var rs = new List<Models.Change>();
foreach (var c in cached)

View File

@@ -51,19 +51,12 @@
<v:LoadingIcon Width="14" Height="14"
Margin="0,0,8,0"
IsVisible="{Binding IsGenerating}"/>
<Button Classes="flat"
Height="28"
Margin="0,0,8,0"
Padding="12,0"
Content="{DynamicResource Text.AIAssistant.Use}"
IsEnabled="{Binding !IsGenerating}"
Click="OnApply"/>
<Button Classes="flat"
Height="28"
Padding="12,0"
Content="{DynamicResource Text.AIAssistant.Regen}"
IsEnabled="{Binding !IsGenerating}"
Command="{Binding Regen}"/>
Click="OnRegenClicked"/>
</StackPanel>
</Border>
</Grid>

View File

@@ -1,5 +1,4 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@@ -9,12 +8,29 @@ using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.Document;
using AvaloniaEdit.Editing;
using AvaloniaEdit.Rendering;
using AvaloniaEdit.TextMate;
namespace SourceGit.Views
{
public class AIResponseView : TextEditor
{
public class LineStyleTransformer : DocumentColorizingTransformer
{
protected override void ColorizeLine(DocumentLine line)
{
var content = CurrentContext.Document.GetText(line);
if (content.StartsWith("Read changes in file: ", StringComparison.Ordinal))
{
ChangeLinePart(line.Offset + 22, line.EndOffset, v =>
{
v.TextRunProperties.SetForegroundBrush(Brushes.DeepSkyBlue);
v.TextRunProperties.SetTextDecorations(TextDecorations.Underline);
});
}
}
}
public static readonly StyledProperty<string> ContentProperty =
AvaloniaProperty.Register<AIResponseView, string>(nameof(Content), string.Empty);
@@ -30,8 +46,7 @@ namespace SourceGit.Views
{
IsReadOnly = true;
ShowLineNumbers = false;
WordWrap = true;
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
TextArea.TextView.Margin = new Thickness(4, 0);
@@ -49,6 +64,7 @@ namespace SourceGit.Views
{
_textMate = Models.TextMateHelper.CreateForEditor(this);
Models.TextMateHelper.SetGrammarByFileName(_textMate, "README.md");
TextArea.TextView.LineTransformers.Add(new LineStyleTransformer());
}
}
@@ -77,29 +93,31 @@ namespace SourceGit.Views
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is not ViewModels.AIAssistant vm)
return;
var selected = SelectedText;
if (string.IsNullOrEmpty(selected))
return;
var apply = new MenuItem() { Header = App.Text("AIAssistant.Use") };
apply.Icon = App.CreateMenuIcon("Icons.Check");
apply.Click += (_, ev) =>
{
vm.Use(selected);
ev.Handled = true;
};
var copy = new MenuItem() { Header = App.Text("Copy") };
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += async (_, ev) =>
{
await App.CopyTextAsync(selected);
ev.Handled = true;
};
if (this.FindResource("Icons.Copy") is Geometry geo)
{
copy.Icon = new Avalonia.Controls.Shapes.Path()
{
Width = 10,
Height = 10,
Stretch = Stretch.Uniform,
Data = geo,
};
}
var menu = new ContextMenu();
menu.Items.Add(apply);
menu.Items.Add(copy);
menu.Open(TextArea.TextView);
@@ -117,16 +135,26 @@ namespace SourceGit.Views
InitializeComponent();
}
protected override async void OnOpened(EventArgs e)
{
base.OnOpened(e);
if (DataContext is ViewModels.AIAssistant vm)
await vm.GenAsync();
}
protected override void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);
(DataContext as ViewModels.AIAssistant)?.Cancel();
}
private void OnApply(object sender, RoutedEventArgs e)
private async void OnRegenClicked(object sender, RoutedEventArgs e)
{
(DataContext as ViewModels.AIAssistant)?.Apply();
Close();
if (DataContext is ViewModels.AIAssistant vm)
await vm.GenAsync();
e.Handled = true;
}
}
}

View File

@@ -27,9 +27,16 @@
<TextBox Grid.Row="0" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Remote.Name.Placeholder}"
Text="{Binding Name, Mode=TwoWay}"/>
Text="{Binding Name, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Remote}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -38,9 +45,20 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Remote.URL.Placeholder}"
Text="{Binding Url, Mode=TwoWay}"/>
Text="{Binding Url, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Link}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<v:RemoteProtocolSwitcher Url="{Binding Url, Mode=TwoWay}"/>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -54,6 +72,12 @@
CornerRadius="3"
Watermark="{DynamicResource Text.SSHKey.Placeholder}"
Text="{Binding SSHKey, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Password}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="30" Height="30" Click="SelectSSHKey">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="SourceGit.Views.AddSubmodule"
x:DataType="vm:AddSubmodule">
@@ -25,9 +26,20 @@
<TextBox Grid.Row="0" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.RepositoryURL}"
Text="{Binding Url, Mode=TwoWay}"/>
Text="{Binding Url, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Link}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<v:RemoteProtocolSwitcher Url="{Binding Url, Mode=TwoWay}"/>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -36,9 +48,16 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Submodule.RelativePath.Placeholder}"
Text="{Binding RelativePath, Mode=TwoWay}"/>
Text="{Binding RelativePath, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Folder.Open}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
</TextBox>
<CheckBox Grid.Row="2" Grid.Column="1"
Content="{DynamicResource Text.Submodule.FetchNested}"

View File

@@ -18,7 +18,7 @@
Text="{DynamicResource Text.Apply.Title}"/>
</StackPanel>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32" ColumnDefinitions="120,*">
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32" ColumnDefinitions="120,*">
<TextBlock Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@@ -77,6 +77,11 @@
Content="{DynamicResource Text.Apply.IgnoreWS}"
IsChecked="{Binding IgnoreWhiteSpace, Mode=TwoWay}"
ToolTip.Tip="--ignore-whitespace"/>
<CheckBox Grid.Row="3" Grid.Column="1"
Content="{DynamicResource Text.Apply.3Way}"
IsChecked="{Binding ThreeWayMerge, Mode=TwoWay}"
ToolTip.Tip="--3way"/>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -127,12 +127,16 @@
<!-- Content Area (allows double-clicking) -->
<Grid Grid.Column="1"
Height="24"
Background="Transparent"
ColumnDefinitions="18,*,Auto,Auto"
ColumnDefinitions="18,*,Auto,Auto,Auto"
DoubleTapped="OnDoubleTappedBranchNode">
<!-- Icon -->
<v:BranchTreeNodeIcon Grid.Column="0" IsExpanded="{Binding IsExpanded}"/>
<v:BranchTreeNodeIcon Grid.Column="0"
Width="18" Height="18"
VerticalAlignment="Center"
IsExpanded="{Binding IsExpanded}"/>
<!-- Name -->
<TextBlock Grid.Column="1"
@@ -152,7 +156,7 @@
</Border>
<!-- Tracking status -->
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="3"
VerticalAlignment="Center"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
@@ -160,8 +164,9 @@
Background="{DynamicResource Brush.Badge}"/>
<!-- Filter Mode Switcher -->
<v:FilterModeSwitchButton Grid.Column="3"
Margin="0,0,8,0"
<v:FilterModeSwitchButton Grid.Column="4"
Width="12"
Margin="4,0,8,0"
Mode="{Binding FilterMode}"/>
</Grid>
</Grid>

View File

@@ -887,37 +887,45 @@ namespace SourceGit.Views
}
}
var rename = new MenuItem();
rename.Header = App.Text("BranchCM.Rename", branch.Name);
rename.Icon = App.CreateMenuIcon("Icons.Rename");
rename.Click += (_, e) =>
if (!branch.IsDetachedHead)
{
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.RenameBranch(repo, branch));
e.Handled = true;
};
var editDescription = new MenuItem();
editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name);
editDescription.Icon = App.CreateMenuIcon("Icons.Edit");
editDescription.Click += async (_, e) =>
{
var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description");
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.EditBranchDescription(repo, branch, desc));
e.Handled = true;
};
var editDescription = new MenuItem();
editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name);
editDescription.Icon = App.CreateMenuIcon("Icons.Edit");
editDescription.Click += async (_, e) =>
{
var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description");
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.EditBranchDescription(repo, branch, desc));
e.Handled = true;
};
var rename = new MenuItem();
rename.Header = App.Text("BranchCM.Rename", branch.Name);
rename.Icon = App.CreateMenuIcon("Icons.Rename");
rename.Click += (_, e) =>
{
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.RenameBranch(repo, branch));
e.Handled = true;
};
var delete = new MenuItem();
delete.Header = App.Text("BranchCM.Delete", branch.Name);
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.IsEnabled = !branch.IsCurrent;
delete.Click += (_, e) =>
{
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.DeleteBranch(repo, branch));
e.Handled = true;
};
var delete = new MenuItem();
delete.Header = App.Text("BranchCM.Delete", branch.Name);
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.IsEnabled = !branch.IsCurrent;
delete.Click += (_, e) =>
{
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.DeleteBranch(repo, branch));
e.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(editDescription);
menu.Items.Add(rename);
menu.Items.Add(delete);
}
var createBranch = new MenuItem();
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
@@ -939,10 +947,6 @@ namespace SourceGit.Views
e.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(editDescription);
menu.Items.Add(rename);
menu.Items.Add(delete);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(createBranch);
menu.Items.Add(createTag);

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.ChangeSubmoduleUrl"
x:DataType="vm:ChangeSubmoduleUrl">
@@ -34,9 +35,20 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.RepositoryURL}"
Text="{Binding Url, Mode=TwoWay}"/>
Text="{Binding Url, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Link}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<v:RemoteProtocolSwitcher Url="{Binding Url, Mode=TwoWay}"/>
</TextBox.InnerRightContent>
</TextBox>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Checkout"
x:DataType="vm:Checkout">
@@ -18,33 +19,27 @@
Text="{DynamicResource Text.Checkout}"/>
</StackPanel>
<Grid Margin="0,16,0,0" ColumnDefinitions="140,*">
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto" MinHeight="32"/>
</Grid.RowDefinitions>
<Grid Margin="0,16,0,0" RowDefinitions="32,Auto" ColumnDefinitions="140,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.Target}"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
<Path Width="14" Height="14" Margin="4,0" Data="{StaticResource Icons.Branch}"/>
<TextBlock Text="{Binding Branch}"/>
<TextBlock Text="{Binding BranchName}"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
<WrapPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<RadioButton GroupName="LocalChanges"
Margin="0,0,8,0"
Content="{DynamicResource Text.Checkout.LocalChanges.StashAndReapply}"
IsChecked="{Binding !DiscardLocalChanges, Mode=TwoWay}"/>
<RadioButton GroupName="LocalChanges"
Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"/>
</WrapPanel>
<Border Grid.Row="1" Grid.Column="0"
Height="32"
VerticalAlignment="Top"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
</Border>
<v:DealWithLocalChangesMethod Grid.Row="1" Grid.Column="1"
Method="{Binding DealWithLocalChanges, Mode=TwoWay}"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}"/>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.CheckoutAndFastForward"
x:DataType="vm:CheckoutAndFastForward">
@@ -18,13 +19,7 @@
Text="{DynamicResource Text.Checkout.WithFastForward}"/>
</StackPanel>
<Grid Margin="0,16,0,0" ColumnDefinitions="140,*">
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto" MinHeight="32"/>
</Grid.RowDefinitions>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,Auto" ColumnDefinitions="140,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@@ -55,18 +50,17 @@
<TextBlock Text="{Binding RemoteBranch.FriendlyName}"/>
</StackPanel>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
<WrapPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<RadioButton GroupName="LocalChanges"
Margin="0,0,8,0"
Content="{DynamicResource Text.Checkout.LocalChanges.StashAndReapply}"
IsChecked="{Binding !DiscardLocalChanges, Mode=TwoWay}"/>
<RadioButton GroupName="LocalChanges"
Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"/>
</WrapPanel>
<Border Grid.Row="2" Grid.Column="0"
Height="32"
VerticalAlignment="Top"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
</Border>
<v:DealWithLocalChangesMethod Grid.Row="2" Grid.Column="1"
Method="{Binding DealWithLocalChanges, Mode=TwoWay}"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}"/>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -3,7 +3,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="clr-namespace:SourceGit.Converters"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.CheckoutCommit"
x:DataType="vm:CheckoutCommit">
@@ -18,12 +19,11 @@
Text="{DynamicResource Text.Checkout.Commit}"/>
</StackPanel>
<Grid Margin="0,16,0,0" ColumnDefinitions="Auto,*">
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto" MinHeight="32"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Margin="0,16,0,0" RowDefinitions="32,Auto,Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="140"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -35,18 +35,17 @@
<TextBlock Grid.Column="2" Text="{Binding Commit.Subject}" TextTrimming="CharacterEllipsis"/>
</Grid>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
<WrapPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<RadioButton Content="{DynamicResource Text.Checkout.LocalChanges.StashAndReapply}"
GroupName="LocalChanges"
Margin="0,0,8,0"
IsChecked="{Binding !DiscardLocalChanges, Mode=TwoWay}"/>
<RadioButton Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"
GroupName="LocalChanges"/>
</WrapPanel>
<Border Grid.Row="1" Grid.Column="0"
Height="32"
VerticalAlignment="Top"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
</Border>
<v:DealWithLocalChangesMethod Grid.Row="1" Grid.Column="1"
Method="{Binding DealWithLocalChanges, Mode=TwoWay}"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}"/>
<Grid Grid.Row="2" Grid.Column="1" ColumnDefinitions="Auto,*" Margin="0,6,0,0">
<Path Grid.Column="0"

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Clone"
x:DataType="vm:Clone">
@@ -26,7 +27,18 @@
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding Remote, Mode=TwoWay}"/>
Text="{Binding Remote, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Link}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<v:RemoteProtocolSwitcher Url="{Binding Remote, Mode=TwoWay}"/>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right"
@@ -40,6 +52,13 @@
Watermark="{DynamicResource Text.SSHKey.Placeholder}"
Text="{Binding SSHKey, Mode=TwoWay}"
IsVisible="{Binding UseSSH}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Password}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="28" Height="28" Click="SelectSSHKey">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>

View File

@@ -161,9 +161,6 @@ namespace SourceGit.Views
Brushes.Gray);
context.DrawText(formatted, new Point(4, 2));
var y = PixelSnapHelpers.PixelAlign(6 + formatted.Height, pixelHeight);
context.DrawLine(pen, new Point(0, y), new Point(w, y));
}
return;
@@ -194,6 +191,15 @@ namespace SourceGit.Views
var y = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - view.VerticalOffset + 4;
y = PixelSnapHelpers.PixelAlign(y, pixelHeight);
context.DrawLine(pen, new Point(0, y), new Point(w, y));
var subjectEndTip = new FormattedText(
"SUBJECT END",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(FontFamily, FontStyle.Italic),
10,
Brushes.Gray);
context.DrawText(subjectEndTip, new Point(w - subjectEndTip.WidthIncludingTrailingWhitespace - 6, y + 1));
return;
}
}

View File

@@ -87,7 +87,7 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Optional}"
Text="{Binding Scope, Mode=TwoWay}"/>
@@ -98,7 +98,7 @@
<TextBox Grid.Row="2" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding Description, Mode=TwoWay}"/>
<TextBlock Grid.Row="3" Grid.Column="0"
@@ -114,7 +114,7 @@
ScrollViewer.VerticalScrollBarVisibility="Auto"
VerticalAlignment="Center"
VerticalContentAlignment="Top"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Optional}"
Text="{Binding Detail, Mode=TwoWay}"/>
@@ -131,7 +131,7 @@
ScrollViewer.VerticalScrollBarVisibility="Auto"
VerticalAlignment="Center"
VerticalContentAlignment="Top"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Optional}"
Text="{Binding BreakingChanges, Mode=TwoWay}"/>
@@ -142,7 +142,7 @@
<TextBox Grid.Row="5" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Optional}"
Text="{Binding ClosedIssue, Mode=TwoWay}"/>
</Grid>

View File

@@ -63,30 +63,37 @@
<v:BranchOrTagNameTextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding Name, Mode=TwoWay}"
Watermark="{DynamicResource Text.CreateBranch.Name.Placeholder}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.CreateBranch.LocalChanges}"
IsVisible="{Binding !IsBareRepository}"/>
<Border Grid.Row="2" Grid.Column="1" MinHeight="32" IsVisible="{Binding !IsBareRepository}">
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center">
<RadioButton GroupName="LocalChanges"
Margin="0,0,8,0"
Content="{DynamicResource Text.CreateBranch.LocalChanges.StashAndReapply}"
IsChecked="{Binding !DiscardLocalChanges, Mode=TwoWay}"/>
<RadioButton GroupName="LocalChanges"
Content="{DynamicResource Text.CreateBranch.LocalChanges.Discard}"/>
</WrapPanel>
<Border Grid.Row="2" Grid.Column="0"
Height="32"
VerticalAlignment="Top">
<Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="HasLocalChanges" Mode="OneWay"/>
<Binding Path="CheckoutAfterCreated" Mode="OneWay"/>
</MultiBinding>
</Border.IsVisible>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.CreateBranch.LocalChanges}"/>
</Border>
<v:DealWithLocalChangesMethod Grid.Row="2" Grid.Column="1"
Method="{Binding DealWithLocalChanges, Mode=TwoWay}">
<v:DealWithLocalChangesMethod.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="HasLocalChanges" Mode="OneWay"/>
<Binding Path="CheckoutAfterCreated" Mode="OneWay"/>
</MultiBinding>
</v:DealWithLocalChangesMethod.IsVisible>
</v:DealWithLocalChangesMethod>
<CheckBox Grid.Row="3" Grid.Column="1"
Content="{DynamicResource Text.CreateBranch.OverwriteExisting}"
IsChecked="{Binding AllowOverwrite, Mode=TwoWay}"
ToolTip.Tip="checkout -B or branch -f"/>
ToolTip.Tip="{Binding OverrideTip, Mode=OneWay}"/>
<CheckBox Grid.Row="4" Grid.Column="1"
Content="{DynamicResource Text.CreateBranch.Checkout}"

View File

@@ -51,7 +51,7 @@
<v:BranchOrTagNameTextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding TagName, Mode=TwoWay}"
Watermark="{DynamicResource Text.CreateTag.Name.Placeholder}"/>
@@ -76,7 +76,7 @@
Height="64"
AcceptsReturn="True" AcceptsTab="False"
VerticalAlignment="Center" VerticalContentAlignment="Top"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.CreateTag.Message.Placeholder}"
Text="{Binding Message, Mode=TwoWay}"
IsVisible="{Binding Annotated}"

View File

@@ -0,0 +1,26 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.DealWithLocalChangesMethod"
x:Name="ThisControl">
<StackPanel Orientation="Vertical" Margin="0,4,0,2">
<RadioButton Content="{DynamicResource Text.DealWithLocalChanges.DoNothing}"
x:Name="RadioDoNothing"
Height="24"
Click="OnRadioButtonClicked"
Tag="{Binding Source={x:Static m:DealWithLocalChanges.DoNothing}}"/>
<RadioButton Content="{DynamicResource Text.DealWithLocalChanges.StashAndReapply}"
x:Name="RadioStashAndReapply"
Height="24"
Click="OnRadioButtonClicked"
Tag="{Binding Source={x:Static m:DealWithLocalChanges.StashAndReapply}}"/>
<RadioButton Content="{DynamicResource Text.DealWithLocalChanges.Discard}"
x:Name="RadioDiscard"
Height="24"
Click="OnRadioButtonClicked"
Tag="{Binding Source={x:Static m:DealWithLocalChanges.Discard}}"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,56 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace SourceGit.Views
{
public partial class DealWithLocalChangesMethod : UserControl
{
public static readonly StyledProperty<Models.DealWithLocalChanges> MethodProperty =
AvaloniaProperty.Register<DealWithLocalChangesMethod, Models.DealWithLocalChanges>(nameof(Method), Models.DealWithLocalChanges.DoNothing);
public Models.DealWithLocalChanges Method
{
get => GetValue(MethodProperty);
set => SetValue(MethodProperty, value);
}
public DealWithLocalChangesMethod()
{
InitializeComponent();
UpdateRadioButtons();
}
private void OnRadioButtonClicked(object sender, RoutedEventArgs e)
{
if (sender is RadioButton { Tag: Models.DealWithLocalChanges way })
{
Method = way;
UpdateRadioButtons();
e.Handled = true;
}
}
private void UpdateRadioButtons()
{
switch (Method)
{
case Models.DealWithLocalChanges.DoNothing:
RadioDoNothing.IsChecked = true;
RadioStashAndReapply.IsChecked = false;
RadioDiscard.IsChecked = false;
break;
case Models.DealWithLocalChanges.StashAndReapply:
RadioDoNothing.IsChecked = false;
RadioStashAndReapply.IsChecked = true;
RadioDiscard.IsChecked = false;
break;
default:
RadioDoNothing.IsChecked = false;
RadioStashAndReapply.IsChecked = false;
RadioDiscard.IsChecked = true;
break;
}
}
}
}

View File

@@ -13,7 +13,7 @@
<Grid RowDefinitions="26,*">
<!-- Toolbar -->
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}">
<Grid ColumnDefinitions="Auto,Auto,*,Auto">
<Grid ColumnDefinitions="Auto,Auto,Auto,*,Auto">
<!-- File Icon -->
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.File}" Margin="8,0,0,0"/>
@@ -28,12 +28,35 @@
ToolTip.Tip="{DynamicResource Text.Diff.FileModeChanged}">
<TextBlock FontSize="10" HorizontalAlignment="Center" Margin="4,0" Text="{Binding FileModeChange}" Foreground="{DynamicResource Brush.BadgeFG}"/>
</Border>
<!-- Line Changes Summary -->
<ContentControl Grid.Column="2" Margin="4,0,0,0" Content="{Binding Content, Mode=OneWay}" IsVisible="{Binding IsTextDiff}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:TextDiffContext">
<StackPanel Orientation="Horizontal" Spacing="4">
<TextBlock FontSize="11"
Foreground="Green"
Text="{Binding Data.AddedLines, Mode=OneWay, StringFormat=+{0}}"
IsVisible="{Binding Data.AddedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
<TextBlock FontSize="11"
Foreground="Red"
Text="{Binding Data.DeletedLines, Mode=OneWay, StringFormat=-{0}}"
IsVisible="{Binding Data.DeletedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
</StackPanel>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<!-- Title -->
<TextBlock Grid.Column="2" Margin="4,0,0,0" Text="{Binding Title}" ToolTip.Tip="{Binding Title}" FontSize="11" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="3"
Margin="4,0,0,0"
Text="{Binding Title}"
ToolTip.Tip="{Binding Title}"
FontSize="11"
TextTrimming="CharacterEllipsis"/>
<!-- Toolbar Buttons -->
<StackPanel Grid.Column="3" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center">
<StackPanel Grid.Column="4" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center">
<Button Classes="icon_button"
Width="28"
Click="OnGotoFirstChange"

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="SourceGit.Views.EditRemote"
x:DataType="vm:EditRemote">
@@ -25,9 +26,16 @@
<TextBox Grid.Row="0" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Remote.Name.Placeholder}"
Text="{Binding Name, Mode=TwoWay}"/>
Text="{Binding Name, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Remote}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -36,9 +44,20 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Remote.URL.Placeholder}"
Text="{Binding Url, Mode=TwoWay}"/>
Text="{Binding Url, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Link}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<v:RemoteProtocolSwitcher Url="{Binding Url, Mode=TwoWay}"/>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
@@ -52,6 +71,12 @@
CornerRadius="3"
Watermark="{DynamicResource Text.SSHKey.Placeholder}"
Text="{Binding SSHKey, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="12" Height="12"
Margin="4,0,2,0"
Data="{StaticResource Icons.Password}"
Fill="{DynamicResource Brush.FG1}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="30" Height="30" Click="SelectSSHKey">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>

View File

@@ -24,10 +24,26 @@
IsVisible="{Binding IsRepository}"/>
</StackPanel>
<Grid Height="28" Margin="8,16,0,0" ColumnDefinitions="120,*" >
<TextBlock Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0" Text="{DynamicResource Text.EditRepositoryNode.Target}"/>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding Name, Mode=OneTime}" IsVisible="{Binding !IsRepository}"/>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding Id, Mode=TwoWay}" IsVisible="{Binding IsRepository}"/>
<Grid Height="28" Margin="8,16,0,0" ColumnDefinitions="120,Auto,*" >
<TextBlock Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.EditRepositoryNode.Target}"/>
<Path Grid.Column="1"
Width="12" Height="12"
Margin="0,1,0,0"
Data="{StaticResource Icons.Folder.Open}"
IsVisible="{Binding !IsRepository, Mode=OneWay}"/>
<Path Grid.Column="1"
Width="12" Height="12"
Data="{StaticResource Icons.Repositories}"
IsVisible="{Binding IsRepository, Mode=OneWay}"/>
<TextBlock Grid.Column="2"
Margin="6,0,0,0"
VerticalAlignment="Center"
Text="{Binding Target, Mode=OneWay}"/>
</Grid>
<Grid Height="28" Margin="8,4,0,0" ColumnDefinitions="120,*">
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.EditRepositoryNode.Name}"/>

View File

@@ -59,9 +59,10 @@
BorderThickness="1"
Margin="8,4,4,8"
BorderBrush="{DynamicResource Brush.Border2}"
ItemsSource="{Binding Revisions}"
SelectedItems="{Binding SelectedRevisions, Mode=TwoWay}"
ItemsSource="{Binding Revisions, Mode=OneWay}"
SelectionMode="Multiple"
SelectionChanged="OnRevisionsSelectionChanged"
PropertyChanged="OnRevisionsPropertyChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Styles>

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
@@ -14,6 +16,34 @@ namespace SourceGit.Views
InitializeComponent();
}
private void OnRevisionsPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == ListBox.ItemsSourceProperty &&
sender is ListBox { Items: { Count: > 0 } } listBox)
listBox.SelectedIndex = 0;
}
private void OnRevisionsSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is ListBox listBox && DataContext is ViewModels.FileHistories vm)
{
if (listBox.SelectedItems is { } selected)
{
var revs = new List<Models.FileVersion>();
foreach (var item in listBox.SelectedItems)
{
if (item is Models.FileVersion ver)
revs.Add(ver);
}
vm.SelectedRevisions = revs;
}
else
{
vm.SelectedRevisions = [];
}
}
}
private void OnPressCommitSHA(object sender, PointerPressedEventArgs e)
{
if (sender is TextBlock { DataContext: Models.FileVersion ver } &&

View File

@@ -35,7 +35,7 @@
<TextBox Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.GitFlow.StartPlaceholder}"
Text="{Binding Name, Mode=TwoWay}"/>
</Grid>

View File

@@ -83,7 +83,7 @@
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}"
Margin="0,8"/>
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+H, macOS=⌘+⇧+H}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoHome}" />
@@ -108,26 +108,29 @@
<TextBlock Grid.Row="7" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Alt+Enter, macOS=⌘+⌥+Enter}"/>
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.CommitAndPush}" />
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Down, macOS=⌘+Down}"/>
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Fetch}" />
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+B, macOS=⌘+B}"/>
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.CreateBranch}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+Down, macOS=⌘+⇧+Down}"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Pull}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Down, macOS=⌘+Down}"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Fetch}" />
<TextBlock Grid.Row="10" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+Up, macOS=⌘+⇧+Up}"/>
<TextBlock Grid.Row="10" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Push}" />
<TextBlock Grid.Row="10" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+Down, macOS=⌘+⇧+Down}"/>
<TextBlock Grid.Row="10" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Pull}" />
<TextBlock Grid.Row="11" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+Up, macOS=⌘+⇧+Up}"/>
<TextBlock Grid.Row="11" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Push}" />
<TextBlock Grid.Row="11" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+P, macOS=⌘+⇧+P}"/>
<TextBlock Grid.Row="11" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.OpenCommandPalette}" />
<TextBlock Grid.Row="12" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+P, macOS=⌘+⇧+P}"/>
<TextBlock Grid.Row="12" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.OpenCommandPalette}" />
<TextBlock Grid.Row="12" Grid.Column="0" Classes="bold" Text="{OnPlatform Alt+Down, macOS=⌥+Down}"/>
<TextBlock Grid.Row="12" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoToParent}" />
<TextBlock Grid.Row="13" Grid.Column="0" Classes="bold" Text="{OnPlatform Alt+Down, macOS=⌥+Down}"/>
<TextBlock Grid.Row="13" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoToParent}" />
<TextBlock Grid.Row="13" Grid.Column="0" Classes="bold" Text="{OnPlatform Alt+Up, macOS=⌥+Up}"/>
<TextBlock Grid.Row="13" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoToChild}" />
<TextBlock Grid.Row="14" Grid.Column="0" Classes="bold" Text="{OnPlatform Alt+Up, macOS=⌥+Up}"/>
<TextBlock Grid.Row="14" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoToChild}" />
<TextBlock Grid.Row="14" Grid.Column="0" Classes="bold" Text="F5"/>
<TextBlock Grid.Row="14" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
<TextBlock Grid.Row="15" Grid.Column="0" Classes="bold" Text="F5"/>
<TextBlock Grid.Row="15" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
</Grid>
<TextBlock Text="{DynamicResource Text.Hotkeys.TextEditor}"

View File

@@ -25,7 +25,7 @@
<TextBox Grid.Row="0" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding Master, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0"
@@ -35,7 +35,7 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding Develop, Mode=TwoWay}"/>
<Rectangle Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
@@ -50,7 +50,7 @@
<TextBox Grid.Row="3" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding FeaturePrefix, Mode=TwoWay}"/>
<TextBlock Grid.Row="4" Grid.Column="0"
@@ -60,7 +60,7 @@
<TextBox Grid.Row="4" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding ReleasePrefix, Mode=TwoWay}"/>
<TextBlock Grid.Row="5" Grid.Column="0"
@@ -70,7 +70,7 @@
<TextBox Grid.Row="5" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding HotfixPrefix, Mode=TwoWay}"/>
<TextBlock Grid.Row="6" Grid.Column="0"
@@ -80,7 +80,7 @@
<TextBox Grid.Row="6" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Optional}"
Text="{Binding TagPrefix, Mode=TwoWay}"/>
</Grid>

View File

@@ -62,7 +62,7 @@ namespace SourceGit.Views
InitializeComponent();
PositionChanged += OnPositionChanged;
if (OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000))
if (Native.OS.UseMicaOnWindows11)
{
Background = Brushes.Transparent;
TransparencyLevelHint = [WindowTransparencyLevel.Mica];

View File

@@ -80,6 +80,10 @@ namespace SourceGit.Views
RepoListBox.Focus(NavigationMethod.Directional);
vm.SelectedRepo = vm.VisibleRepos[0];
}
else if (e.Key == Key.Tab)
{
FilterTextBox.Focus(NavigationMethod.Directional);
}
e.Handled = true;
return;

View File

@@ -34,7 +34,7 @@
<TextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Watermark="{DynamicResource Text.Submodule.RelativePath.Placeholder}"
Text="{Binding MoveTo, Mode=TwoWay}"/>
</Grid>

View File

@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="using:SourceGit"
xmlns:ai="using:SourceGit.AI"
xmlns:m="using:SourceGit.Models"
xmlns:c="using:SourceGit.Converters"
xmlns:vm="using:SourceGit.ViewModels"
@@ -822,7 +823,7 @@
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="m:OpenAIService">
<DataTemplate DataType="ai:Service">
<Grid ColumnDefinitions="Auto,*">
<Path Grid.Column="0" Width="14" Height="14" Data="{StaticResource Icons.AIAssist}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="8,0" TextTrimming="CharacterEllipsis"/>
@@ -859,7 +860,7 @@
</ContentControl.Content>
<ContentControl.DataTemplates>
<DataTemplate DataType="m:OpenAIService">
<DataTemplate DataType="ai:Service">
<StackPanel Orientation="Vertical" MaxWidth="680">
<TextBlock Text="{DynamicResource Text.Preferences.AI.Name}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/>
@@ -876,27 +877,14 @@
Content="{DynamicResource Text.Preferences.AI.ReadApiKeyFromEnv}"
IsChecked="{Binding ReadApiKeyFromEnv, Mode=TwoWay}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preferences.AI.AnalyzeDiffPrompt}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preferences.AI.AdditionalPrompt}"/>
<TextBox Height="120"
Margin="0,4,0,0"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding AnalyzeDiffPrompt, Mode=TwoWay}"
Text="{Binding AdditionalPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preferences.AI.GenerateSubjectPrompt}"/>
<TextBox Height="120"
Margin="0,4,0,0"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding GenerateSubjectPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"/>
<CheckBox Margin="0,12,0,0"
Content="{DynamicResource Text.Preferences.AI.Streaming}"
IsChecked="{Binding Streaming, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ContentControl.DataTemplates>

View File

@@ -95,10 +95,10 @@ namespace SourceGit.Views
set;
} = false;
public static readonly StyledProperty<Models.OpenAIService> SelectedOpenAIServiceProperty =
AvaloniaProperty.Register<Preferences, Models.OpenAIService>(nameof(SelectedOpenAIService));
public static readonly StyledProperty<AI.Service> SelectedOpenAIServiceProperty =
AvaloniaProperty.Register<Preferences, AI.Service>(nameof(SelectedOpenAIService));
public Models.OpenAIService SelectedOpenAIService
public AI.Service SelectedOpenAIService
{
get => GetValue(SelectedOpenAIServiceProperty);
set => SetValue(SelectedOpenAIServiceProperty, value);
@@ -397,7 +397,7 @@ namespace SourceGit.Views
private void OnAddOpenAIService(object sender, RoutedEventArgs e)
{
var service = new Models.OpenAIService() { Name = "Unnamed Service" };
var service = new AI.Service() { Name = "Unnamed Service" };
ViewModels.Preferences.Instance.OpenAIServices.Add(service);
SelectedOpenAIService = service;

View File

@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="SourceGit.Views.Pull"
x:DataType="vm:Pull">
@@ -18,15 +19,7 @@
Text="{DynamicResource Text.Pull.Title}"/>
</StackPanel>
<Grid Margin="0,16,0,0" ColumnDefinitions="140,*">
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="32"/>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto" MinHeight="32"/>
<RowDefinition Height="32"/>
</Grid.RowDefinitions>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,Auto,32" ColumnDefinitions="140,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@@ -78,18 +71,17 @@
<TextBlock Text="{Binding Current.Name}"/>
</StackPanel>
<TextBlock Grid.Row="3" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Pull.LocalChanges}"/>
<WrapPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<RadioButton GroupName="LocalChanges"
Margin="0,0,8,0"
Content="{DynamicResource Text.Pull.LocalChanges.StashAndReapply}"
IsChecked="{Binding !DiscardLocalChanges, Mode=TwoWay}"/>
<RadioButton GroupName="LocalChanges"
Content="{DynamicResource Text.Pull.LocalChanges.Discard}"/>
</WrapPanel>
<Border Grid.Row="3" Grid.Column="0"
Height="32"
VerticalAlignment="Top"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.Pull.LocalChanges}"/>
</Border>
<v:DealWithLocalChangesMethod Grid.Row="3" Grid.Column="1"
Method="{Binding DealWithLocalChanges, Mode=TwoWay}"
IsVisible="{Binding HasLocalChanges, Mode=OneWay}"/>
<CheckBox Grid.Row="4" Grid.Column="1"
Content="{DynamicResource Text.Pull.UseRebase}"

View File

@@ -0,0 +1,20 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.RemoteProtocolSwitcher"
x:Name="ThisControl">
<Button Classes="icon_button" Click="OnOpenDropdownMenu">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12"
Text="{Binding #ThisControl.ActiveProtocol, Mode=OneWay}"
Foreground="{DynamicResource Brush.Link}"/>
<Path Width="10" Height="10"
Margin="4,0,0,0"
VerticalAlignment="Center"
Data="M0 0M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"
Fill="{DynamicResource Brush.Link}"/>
</StackPanel>
</Button>
</UserControl>

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace SourceGit.Views
{
public partial class RemoteProtocolSwitcher : UserControl
{
public static readonly StyledProperty<string> UrlProperty =
AvaloniaProperty.Register<RemoteProtocolSwitcher, string>(nameof(Url));
public string Url
{
get => GetValue(UrlProperty);
set => SetValue(UrlProperty, value);
}
public static readonly StyledProperty<string> ActiveProtocolProperty =
AvaloniaProperty.Register<RemoteProtocolSwitcher, string>(nameof(ActiveProtocol));
public string ActiveProtocol
{
get => GetValue(ActiveProtocolProperty);
set => SetValue(ActiveProtocolProperty, value);
}
public RemoteProtocolSwitcher()
{
InitializeComponent();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == UrlProperty)
{
_protocols.Clear();
var url = Url ?? string.Empty;
if (url.StartsWith("https://", StringComparison.Ordinal) && Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
var host = uri.Host;
var route = uri.AbsolutePath.TrimStart('/');
_protocols.Add(url);
_protocols.Add($"git@{host}:{route}");
SetCurrentValue(ActiveProtocolProperty, "HTTPS");
SetCurrentValue(IsVisibleProperty, true);
return;
}
var match = REG_SSH_FORMAT().Match(url);
if (match.Success)
{
var host = match.Groups[1].Value;
var repo = match.Groups[2].Value;
_protocols.Add($"https://{host}/{repo}");
_protocols.Add(url);
SetCurrentValue(ActiveProtocolProperty, "SSH");
SetCurrentValue(IsVisibleProperty, true);
return;
}
SetCurrentValue(IsVisibleProperty, false);
}
}
private void OnOpenDropdownMenu(object sender, RoutedEventArgs e)
{
if (sender is Button btn && _protocols.Count > 0)
{
var menu = new ContextMenu();
menu.Placement = PlacementMode.BottomEdgeAlignedLeft;
foreach (var protocol in _protocols)
{
var dup = protocol;
var item = new MenuItem() { Header = dup };
item.Click += (_, _) => Url = protocol;
menu.Items.Add(item);
}
menu.Open(btn);
}
e.Handled = true;
}
[GeneratedRegex(@"^git@([\w\.\-]+):(.+)$")]
private static partial Regex REG_SSH_FORMAT();
private List<string> _protocols = [];
}
}

View File

@@ -35,7 +35,7 @@
<v:BranchOrTagNameTextBox Grid.Row="1" Grid.Column="1"
Height="26"
VerticalAlignment="Center"
CornerRadius="2"
CornerRadius="3"
Text="{Binding Name, Mode=TwoWay}"
Watermark="{DynamicResource Text.RenameBranch.Name.Placeholder}"/>
</Grid>

Some files were not shown because too many files have changed in this diff Show More