Merge branch 'release/v2025.06'

This commit is contained in:
leo 2025-02-24 09:49:41 +08:00
commit fb6b57bdf5
No known key found for this signature in database
36 changed files with 590 additions and 240 deletions

View file

@ -47,7 +47,7 @@
## Translation Status ## Translation Status
[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.47%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-97.61%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-92.42%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-92.15%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.73%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md) [![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.34%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.74%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.93%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.35%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.67%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.21%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md)
> [!NOTE] > [!NOTE]
> You can find the missing keys in [TRANSLATION.md](TRANSLATION.md) > You can find the missing keys in [TRANSLATION.md](TRANSLATION.md)
@ -132,7 +132,7 @@ For **Linux** users:
## OpenAI ## OpenAI
This software supports using OpenAI or other AI service that has an OpenAI comaptible HTTP API to generate commit message. You need configurate the service in `Preference` window. This software supports using OpenAI or other AI service that has an OpenAI compatible HTTP API to generate commit message. You need configurate the service in `Preference` window.
For `OpenAI`: For `OpenAI`:

View file

@ -1,44 +1,29 @@
### de_DE.axaml: 99.47% ### de_DE.axaml: 99.34%
<details> <details>
<summary>Missing Keys</summary> <summary>Missing Keys</summary>
- Text.BranchCM.CustomAction - Text.BranchUpstreamInvalid
- Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit - Text.Configure.CustomAction.WaitForExit
- Text.Repository.Notifications.Clear - Text.Diff.First
- Text.Diff.Last
- Text.Preferences.AI.Streaming
</details> </details>
### es_ES.axaml: 97.61% ### es_ES.axaml: 99.74%
<details> <details>
<summary>Missing Keys</summary> <summary>Missing Keys</summary>
- Text.AIAssistant.Regen - Text.Diff.First
- Text.AIAssistant.Use - Text.Diff.Last
- Text.ApplyStash
- Text.ApplyStash.DropAfterApply </details>
- Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash ### fr_FR.axaml: 91.93%
- Text.BranchCM.CustomAction
- Text.Clone.RecurseSubmodules
- Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit
- Text.CreateBranch.Name.WarnSpace
- Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository
- Text.Repository.Notifications.Clear
- Text.Stash.AutoRestore
- Text.Stash.AutoRestore.Tip
- Text.WorkingCopy.SignOff
</details>
### fr_FR.axaml: 92.42%
<details> <details>
@ -51,6 +36,7 @@
- Text.ApplyStash.RestoreIndex - Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash - Text.ApplyStash.Stash
- Text.BranchCM.CustomAction - Text.BranchCM.CustomAction
- Text.BranchUpstreamInvalid
- Text.Clone.RecurseSubmodules - Text.Clone.RecurseSubmodules
- Text.Configure.CustomAction.Scope.Branch - Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit - Text.Configure.CustomAction.WaitForExit
@ -58,6 +44,8 @@
- Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository - Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
- Text.InProgress.CherryPick.Head - Text.InProgress.CherryPick.Head
- Text.InProgress.Merge.Operating - Text.InProgress.Merge.Operating
- Text.InProgress.Rebase.StoppedAt - Text.InProgress.Rebase.StoppedAt
@ -67,6 +55,7 @@
- Text.MergeMultiple.CommitChanges - Text.MergeMultiple.CommitChanges
- Text.MergeMultiple.Strategy - Text.MergeMultiple.Strategy
- Text.MergeMultiple.Targets - Text.MergeMultiple.Targets
- Text.Preferences.AI.Streaming
- Text.Preferences.Appearance.FontSize - Text.Preferences.Appearance.FontSize
- Text.Preferences.Appearance.FontSize.Default - Text.Preferences.Appearance.FontSize.Default
- Text.Preferences.Appearance.FontSize.Editor - Text.Preferences.Appearance.FontSize.Editor
@ -104,7 +93,7 @@
</details> </details>
### it_IT.axaml: 97.87% ### it_IT.axaml: 97.35%
<details> <details>
@ -117,19 +106,23 @@
- Text.ApplyStash.RestoreIndex - Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash - Text.ApplyStash.Stash
- Text.BranchCM.CustomAction - Text.BranchCM.CustomAction
- Text.BranchUpstreamInvalid
- Text.Clone.RecurseSubmodules - Text.Clone.RecurseSubmodules
- Text.Configure.CustomAction.Scope.Branch - Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit - Text.Configure.CustomAction.WaitForExit
- Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository - Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
- Text.Preferences.AI.Streaming
- Text.Repository.Notifications.Clear - Text.Repository.Notifications.Clear
- Text.Stash.AutoRestore - Text.Stash.AutoRestore
- Text.Stash.AutoRestore.Tip - Text.Stash.AutoRestore.Tip
</details> </details>
### pt_BR.axaml: 92.15% ### pt_BR.axaml: 91.67%
<details> <details>
@ -143,6 +136,7 @@
- Text.ApplyStash.Stash - Text.ApplyStash.Stash
- Text.BranchCM.CustomAction - Text.BranchCM.CustomAction
- Text.BranchCM.MergeMultiBranches - Text.BranchCM.MergeMultiBranches
- Text.BranchUpstreamInvalid
- Text.Clone.RecurseSubmodules - Text.Clone.RecurseSubmodules
- Text.CommitCM.Merge - Text.CommitCM.Merge
- Text.CommitCM.MergeMultiple - Text.CommitCM.MergeMultiple
@ -156,6 +150,8 @@
- Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository - Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
- Text.Diff.UseBlockNavigation - Text.Diff.UseBlockNavigation
- Text.Fetch.Force - Text.Fetch.Force
- Text.FileCM.ResolveUsing - Text.FileCM.ResolveUsing
@ -169,6 +165,7 @@
- Text.MergeMultiple.CommitChanges - Text.MergeMultiple.CommitChanges
- Text.MergeMultiple.Strategy - Text.MergeMultiple.Strategy
- Text.MergeMultiple.Targets - Text.MergeMultiple.Targets
- Text.Preferences.AI.Streaming
- Text.Preferences.General.DateFormat - Text.Preferences.General.DateFormat
- Text.Preferences.General.ShowChildren - Text.Preferences.General.ShowChildren
- Text.Preferences.Git.SSLVerify - Text.Preferences.Git.SSLVerify
@ -197,14 +194,18 @@
</details> </details>
### ru_RU.axaml: 99.73% ### ru_RU.axaml: 99.21%
<details> <details>
<summary>Missing Keys</summary> <summary>Missing Keys</summary>
- Text.BranchCM.CustomAction - Text.BranchCM.CustomAction
- Text.BranchUpstreamInvalid
- Text.Configure.CustomAction.Scope.Branch - Text.Configure.CustomAction.Scope.Branch
- Text.Diff.First
- Text.Diff.Last
- Text.Preferences.AI.Streaming
</details> </details>

View file

@ -1 +1 @@
2025.05 2025.06

View file

@ -12,7 +12,7 @@ namespace SourceGit.Commands
Args = includeUntracked ? "add ." : "add -u ."; Args = includeUntracked ? "add ." : "add -u .";
} }
public Add(string repo, List<Models.Change> changes) public Add(string repo, List<string> changes)
{ {
WorkingDirectory = repo; WorkingDirectory = repo;
Context = repo; Context = repo;
@ -22,7 +22,7 @@ namespace SourceGit.Commands
foreach (var c in changes) foreach (var c in changes)
{ {
builder.Append(" \""); builder.Append(" \"");
builder.Append(c.Path); builder.Append(c);
builder.Append("\""); builder.Append("\"");
} }
Args = builder.ToString(); Args = builder.ToString();

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using Avalonia.Threading; using Avalonia.Threading;
@ -36,6 +35,8 @@ namespace SourceGit.Commands
{ {
try try
{ {
_onResponse?.Invoke("Waiting for pre-file analyzing to completed...\n\n");
var responseBuilder = new StringBuilder(); var responseBuilder = new StringBuilder();
var summaryBuilder = new StringBuilder(); var summaryBuilder = new StringBuilder();
foreach (var change in _changes) foreach (var change in _changes)
@ -49,18 +50,17 @@ namespace SourceGit.Commands
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd(); var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
if (rs.IsSuccess) if (rs.IsSuccess)
{ {
var hasFirstValidChar = false;
var thinkingBuffer = new StringBuilder();
_service.Chat( _service.Chat(
_service.AnalyzeDiffPrompt, _service.AnalyzeDiffPrompt,
$"Here is the `git diff` output: {rs.StdOut}", $"Here is the `git diff` output: {rs.StdOut}",
_cancelToken, _cancelToken,
update => update =>
ProcessChatResponse(update, ref hasFirstValidChar, thinkingBuffer, {
(responseBuilder, text => responseBuilder.Append(update);
_onResponse?.Invoke( summaryBuilder.Append(update);
$"Waiting for pre-file analyzing to completed...\n\n{text}")),
(summaryBuilder, null))); _onResponse?.Invoke($"Waiting for pre-file analyzing to completed...\n\n{responseBuilder}");
});
} }
responseBuilder.Append("\n"); responseBuilder.Append("\n");
@ -74,15 +74,15 @@ namespace SourceGit.Commands
var responseBody = responseBuilder.ToString(); var responseBody = responseBuilder.ToString();
var subjectBuilder = new StringBuilder(); var subjectBuilder = new StringBuilder();
var hasSubjectFirstValidChar = false;
var subjectThinkingBuffer = new StringBuilder();
_service.Chat( _service.Chat(
_service.GenerateSubjectPrompt, _service.GenerateSubjectPrompt,
$"Here are the summaries changes:\n{summaryBuilder}", $"Here are the summaries changes:\n{summaryBuilder}",
_cancelToken, _cancelToken,
update => update =>
ProcessChatResponse(update, ref hasSubjectFirstValidChar, subjectThinkingBuffer, {
(subjectBuilder, text => _onResponse?.Invoke($"{text}\n\n{responseBody}")))); subjectBuilder.Append(update);
_onResponse?.Invoke($"{subjectBuilder}\n\n{responseBody}");
});
} }
catch (Exception e) catch (Exception e)
{ {
@ -90,67 +90,10 @@ namespace SourceGit.Commands
} }
} }
private void ProcessChatResponse(
string update,
ref bool hasFirstValidChar,
StringBuilder thinkingBuffer,
params (StringBuilder builder, Action<string> callback)[] outputs)
{
if (!hasFirstValidChar)
{
update = update.TrimStart();
if (string.IsNullOrEmpty(update))
return;
if (update.StartsWith("<", StringComparison.Ordinal))
thinkingBuffer.Append(update);
hasFirstValidChar = true;
}
if (thinkingBuffer.Length > 0)
thinkingBuffer.Append(update);
if (thinkingBuffer.Length > 15)
{
var match = REG_COT.Match(thinkingBuffer.ToString());
if (match.Success)
{
update = REG_COT.Replace(thinkingBuffer.ToString(), "").TrimStart();
if (update.Length > 0)
{
foreach (var output in outputs)
output.builder.Append(update);
thinkingBuffer.Clear();
}
return;
}
match = REG_THINK_START.Match(thinkingBuffer.ToString());
if (!match.Success)
{
foreach (var output in outputs)
output.builder.Append(thinkingBuffer);
thinkingBuffer.Clear();
return;
}
}
if (thinkingBuffer.Length == 0)
{
foreach (var output in outputs)
{
output.builder.Append(update);
output.callback?.Invoke(output.builder.ToString());
}
}
}
private Models.OpenAIService _service; private Models.OpenAIService _service;
private string _repo; private string _repo;
private List<Models.Change> _changes; private List<Models.Change> _changes;
private CancellationToken _cancelToken; private CancellationToken _cancelToken;
private Action<string> _onResponse; private Action<string> _onResponse;
private static readonly Regex REG_COT = new(@"^<(think|thought|thinking|thought_chain)>(.*?)</\1>", RegexOptions.Singleline);
private static readonly Regex REG_THINK_START = new(@"^<(think|thought|thinking|thought_chain)>", RegexOptions.Singleline);
} }
} }

View file

@ -25,11 +25,22 @@ namespace SourceGit.Commands
return branches; return branches;
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries); var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var remoteBranches = new HashSet<string>();
foreach (var line in lines) foreach (var line in lines)
{ {
var b = ParseLine(line); var b = ParseLine(line);
if (b != null) if (b != null)
{
branches.Add(b); branches.Add(b);
if (!b.IsLocal)
remoteBranches.Add(b.FullName);
}
}
foreach (var b in branches)
{
if (b.IsLocal && !string.IsNullOrEmpty(b.Upstream))
b.IsUpsteamGone = !remoteBranches.Contains(b.Upstream);
} }
return branches; return branches;
@ -75,6 +86,7 @@ namespace SourceGit.Commands
branch.Head = parts[1]; branch.Head = parts[1];
branch.IsCurrent = parts[2] == "*"; branch.IsCurrent = parts[2] == "*";
branch.Upstream = parts[3]; branch.Upstream = parts[3];
branch.IsUpsteamGone = false;
if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal)) if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result(); branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result();

View file

@ -1,4 +1,5 @@
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Media;
namespace SourceGit.Converters namespace SourceGit.Converters
{ {
@ -6,5 +7,8 @@ namespace SourceGit.Converters
{ {
public static readonly FuncValueConverter<bool, double> ToPageTabWidth = public static readonly FuncValueConverter<bool, double> ToPageTabWidth =
new FuncValueConverter<bool, double>(x => x ? 200 : double.NaN); new FuncValueConverter<bool, double>(x => x ? 200 : double.NaN);
public static readonly FuncValueConverter<bool, FontWeight> IsBoldToFontWeight =
new FuncValueConverter<bool, FontWeight>(x => x ? FontWeight.Bold : FontWeight.Normal);
} }
} }

View file

@ -34,6 +34,7 @@ namespace SourceGit.Models
public string Upstream { get; set; } public string Upstream { get; set; }
public BranchTrackStatus TrackStatus { get; set; } public BranchTrackStatus TrackStatus { get; set; }
public string Remote { get; set; } public string Remote { get; set; }
public bool IsUpsteamGone { get; set; }
public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}"; public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}";
} }

View file

@ -1,5 +1,8 @@
using System; using System;
using System.ClientModel; using System.ClientModel;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using Azure.AI.OpenAI; using Azure.AI.OpenAI;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -8,6 +11,91 @@ using OpenAI.Chat;
namespace SourceGit.Models 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('<', StringComparison.Ordinal);
if (startIdx >= 0)
{
if (startIdx > 0)
OnReceive(buffer.Substring(0, startIdx));
var endIdx = buffer.IndexOf(">", startIdx + 1, StringComparison.Ordinal);
if (endIdx <= startIdx)
{
if (buffer.Length - startIdx <= 15)
_thinkTail.Append(buffer.Substring(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.Substring(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 class OpenAIService : ObservableObject
{ {
public string Name public string Name
@ -42,6 +130,12 @@ namespace SourceGit.Models
set => SetProperty(ref _model, value); set => SetProperty(ref _model, value);
} }
public bool Streaming
{
get => _streaming;
set => SetProperty(ref _streaming, value);
}
public string AnalyzeDiffPrompt public string AnalyzeDiffPrompt
{ {
get => _analyzeDiffPrompt; get => _analyzeDiffPrompt;
@ -89,32 +183,47 @@ namespace SourceGit.Models
public void Chat(string prompt, string question, CancellationToken cancellation, Action<string> onUpdate) public void Chat(string prompt, string question, CancellationToken cancellation, Action<string> onUpdate)
{ {
Uri server = new(Server); var server = new Uri(_server);
ApiKeyCredential key = new(ApiKey); var key = new ApiKeyCredential(_apiKey);
ChatClient client = null; var client = null as ChatClient;
if (Server.Contains("openai.azure.com/", StringComparison.Ordinal)) if (_server.Contains("openai.azure.com/", StringComparison.Ordinal))
{ {
var azure = new AzureOpenAIClient(server, key); var azure = new AzureOpenAIClient(server, key);
client = azure.GetChatClient(Model); client = azure.GetChatClient(_model);
} }
else else
{ {
var openai = new OpenAIClient(key, new() { Endpoint = server }); var openai = new OpenAIClient(key, new() { Endpoint = server });
client = openai.GetChatClient(Model); client = openai.GetChatClient(_model);
} }
var messages = new List<ChatMessage>();
messages.Add(_model.Equals("o1-mini", StringComparison.Ordinal) ? new UserChatMessage(prompt) : new SystemChatMessage(prompt));
messages.Add(new UserChatMessage(question));
try try
{ {
var updates = client.CompleteChatStreaming([ var rsp = new OpenAIResponse(onUpdate);
_model.Equals("o1-mini", StringComparison.Ordinal) ? new UserChatMessage(prompt) : new SystemChatMessage(prompt),
new UserChatMessage(question),
], null, cancellation);
foreach (var update in updates) if (_streaming)
{ {
if (update.ContentUpdate.Count > 0) var updates = client.CompleteChatStreaming(messages, null, cancellation);
onUpdate.Invoke(update.ContentUpdate[0].Text);
foreach (var update in updates)
{
if (update.ContentUpdate.Count > 0)
rsp.Append(update.ContentUpdate[0].Text);
}
} }
else
{
var completion = client.CompleteChat(messages, null, cancellation);
if (completion.Value.Content.Count > 0)
rsp.Append(completion.Value.Content[0].Text);
}
rsp.End();
} }
catch catch
{ {
@ -127,6 +236,7 @@ namespace SourceGit.Models
private string _server; private string _server;
private string _apiKey; private string _apiKey;
private string _model; private string _model;
private bool _streaming = true;
private string _analyzeDiffPrompt; private string _analyzeDiffPrompt;
private string _generateSubjectPrompt; private string _generateSubjectPrompt;
} }

View file

@ -104,6 +104,18 @@ namespace SourceGit.Models
set; set;
} = false; } = false;
public bool PushToRemoteWhenCreateTag
{
get;
set;
} = true;
public bool PushToRemoteWhenDeleteTag
{
get;
set;
} = false;
public DealWithLocalChanges DealWithLocalChangesOnCreateBranch public DealWithLocalChanges DealWithLocalChangesOnCreateBranch
{ {
get; get;

View file

@ -57,6 +57,7 @@ namespace SourceGit.Models
new ShellOrTerminal("mate-terminal", "MATE Terminal", "mate-terminal"), new ShellOrTerminal("mate-terminal", "MATE Terminal", "mate-terminal"),
new ShellOrTerminal("foot", "Foot", "foot"), new ShellOrTerminal("foot", "Foot", "foot"),
new ShellOrTerminal("wezterm", "WezTerm", "wezterm"), new ShellOrTerminal("wezterm", "WezTerm", "wezterm"),
new ShellOrTerminal("ptyxis", "Ptyxis", "ptyxis"),
new ShellOrTerminal("custom", "Custom", ""), new ShellOrTerminal("custom", "Custom", ""),
}; };
} }

View file

@ -5,9 +5,11 @@
<StreamGeometry x:Key="Icons.Binary">M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z</StreamGeometry> <StreamGeometry x:Key="Icons.Binary">M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z</StreamGeometry>
<StreamGeometry x:Key="Icons.Blame">M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z</StreamGeometry> <StreamGeometry x:Key="Icons.Blame">M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z</StreamGeometry>
<StreamGeometry x:Key="Icons.Bookmark">M832 64H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V96c0-18-14-32-32-32zM736 596 624 502 506 596V131h230v318z</StreamGeometry> <StreamGeometry x:Key="Icons.Bookmark">M832 64H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V96c0-18-14-32-32-32zM736 596 624 502 506 596V131h230v318z</StreamGeometry>
<StreamGeometry x:Key="Icons.Bottom">M509 546 780 275 871 366 509 728 147 366 238 275zM509 728h-362v128h724v-128z</StreamGeometry>
<StreamGeometry x:Key="Icons.Branch">M757 226a143 143 0 00-55 276 96 96 0 01-88 59h-191a187 187 0 00-96 27V312a143 143 0 10-96 0v399a143 143 0 10103 2 96 96 0 0188-59h191a191 191 0 00187-151 143 143 0 00-43-279zM280 130a48 48 0 110 96 48 48 0 010-96zm0 764a48 48 0 110-96 48 48 0 010 96zM757 417a48 48 0 110-96 48 48 0 010 96z</StreamGeometry> <StreamGeometry x:Key="Icons.Branch">M757 226a143 143 0 00-55 276 96 96 0 01-88 59h-191a187 187 0 00-96 27V312a143 143 0 10-96 0v399a143 143 0 10103 2 96 96 0 0188-59h191a191 191 0 00187-151 143 143 0 00-43-279zM280 130a48 48 0 110 96 48 48 0 010-96zm0 764a48 48 0 110-96 48 48 0 010 96zM757 417a48 48 0 110-96 48 48 0 010 96z</StreamGeometry>
<StreamGeometry x:Key="Icons.Branch.Add">M896 128h-64V64c0-35-29-64-64-64s-64 29-64 64v64h-64c-35 0-64 29-64 64s29 64 64 64h64v64c0 35 29 64 64 64s64-29 64-64V256h64c35 0 64-29 64-64s-29-64-64-64zm-204 307C673 481 628 512 576 512H448c-47 0-90 13-128 35V372C394 346 448 275 448 192c0-106-86-192-192-192S64 86 64 192c0 83 54 154 128 180v280c-74 26-128 97-128 180c0 106 86 192 192 192s192-86 192-192c0-67-34-125-84-159c22-20 52-33 84-33h128c122 0 223-85 249-199c-19 4-37 7-57 7c-26 0-51-5-76-13zM256 128c35 0 64 29 64 64s-29 64-64 64s-64-29-64-64s29-64 64-64zm0 768c-35 0-64-29-64-64s29-64 64-64s64 29 64 64s-29 64-64 64z</StreamGeometry> <StreamGeometry x:Key="Icons.Branch.Add">M896 128h-64V64c0-35-29-64-64-64s-64 29-64 64v64h-64c-35 0-64 29-64 64s29 64 64 64h64v64c0 35 29 64 64 64s64-29 64-64V256h64c35 0 64-29 64-64s-29-64-64-64zm-204 307C673 481 628 512 576 512H448c-47 0-90 13-128 35V372C394 346 448 275 448 192c0-106-86-192-192-192S64 86 64 192c0 83 54 154 128 180v280c-74 26-128 97-128 180c0 106 86 192 192 192s192-86 192-192c0-67-34-125-84-159c22-20 52-33 84-33h128c122 0 223-85 249-199c-19 4-37 7-57 7c-26 0-51-5-76-13zM256 128c35 0 64 29 64 64s-29 64-64 64s-64-29-64-64s29-64 64-64zm0 768c-35 0-64-29-64-64s29-64 64-64s64 29 64 64s-29 64-64 64z</StreamGeometry>
<StreamGeometry x:Key="Icons.Check">M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z</StreamGeometry> <StreamGeometry x:Key="Icons.Check">M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z</StreamGeometry>
<StreamGeometry x:Key="Icons.CheckCircled">M512 32C246 32 32 250 32 512s218 480 480 480 480-218 480-480S774 32 512 32zm269 381L496 698c-26 26-61 26-83 0L243 528c-26-26-26-61 0-83s61-26 83 0l128 128 240-240c26-26 61-26 83 0 26 19 26 54 3 80z</StreamGeometry>
<StreamGeometry x:Key="Icons.Changes">M747 467c29 0 56 4 82 12v-363c0-47-38-84-84-84H125c-47 0-84 38-84 84v707c0 47 38 84 84 84h375a287 287 0 01-43-152c0-160 129-289 289-289zm-531-250h438c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm0 179h263c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm131 247h-131c-19 0-34-15-34-34s15-34 34-34h131c19 0 34 15 34 34s-15 34-34 34zM747 521c-130 0-236 106-236 236S617 992 747 992s236-106 236-236S877 521 747 521zm11 386v-65h-130c-12 0-22-10-22-22s10-22 22-22h260l-130 108zm108-192H606l130-108v65h130c12 0 22 10 22 22s-10 22-22 22z</StreamGeometry> <StreamGeometry x:Key="Icons.Changes">M747 467c29 0 56 4 82 12v-363c0-47-38-84-84-84H125c-47 0-84 38-84 84v707c0 47 38 84 84 84h375a287 287 0 01-43-152c0-160 129-289 289-289zm-531-250h438c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm0 179h263c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm131 247h-131c-19 0-34-15-34-34s15-34 34-34h131c19 0 34 15 34 34s-15 34-34 34zM747 521c-130 0-236 106-236 236S617 992 747 992s236-106 236-236S877 521 747 521zm11 386v-65h-130c-12 0-22-10-22-22s10-22 22-22h260l-130 108zm108-192H606l130-108v65h130c12 0 22 10 22 22s-10 22-22 22z</StreamGeometry>
<StreamGeometry x:Key="Icons.CherryPick">M529 511c115 0 212 79 239 185h224a62 62 0 017 123l-7 0-224 0a247 247 0 01-479 0H65a62 62 0 01-7-123l7-0h224a247 247 0 01239-185zm0 124a124 124 0 100 247 124 124 0 000-247zm0-618c32 0 58 24 61 55l0 7V206c89 11 165 45 225 103a74 74 0 0122 45l0 9v87a62 62 0 01-123 7l-0-7v-65l-6-4c-43-33-97-51-163-53l-17-0c-74 0-133 18-180 54l-6 4v65a62 62 0 01-55 61l-7 0a62 62 0 01-61-55l-0-7V362c0-20 8-39 23-53 60-58 135-92 224-103V79c0-34 28-62 62-62z</StreamGeometry> <StreamGeometry x:Key="Icons.CherryPick">M529 511c115 0 212 79 239 185h224a62 62 0 017 123l-7 0-224 0a247 247 0 01-479 0H65a62 62 0 01-7-123l7-0h224a247 247 0 01239-185zm0 124a124 124 0 100 247 124 124 0 000-247zm0-618c32 0 58 24 61 55l0 7V206c89 11 165 45 225 103a74 74 0 0122 45l0 9v87a62 62 0 01-123 7l-0-7v-65l-6-4c-43-33-97-51-163-53l-17-0c-74 0-133 18-180 54l-6 4v65a62 62 0 01-55 61l-7 0a62 62 0 01-61-55l-0-7V362c0-20 8-39 23-53 60-58 135-92 224-103V79c0-34 28-62 62-62z</StreamGeometry>
<StreamGeometry x:Key="Icons.CircleDown">M512 926c-229 0-414-186-414-414S283 98 512 98s414 186 414 414-186 414-414 414zm0-73c189 0 341-153 341-341S701 171 512 171 171 323 171 512s153 341 341 341zm-6-192L284 439l52-52 171 171 171-171L728 439l-222 222z</StreamGeometry> <StreamGeometry x:Key="Icons.CircleDown">M512 926c-229 0-414-186-414-414S283 98 512 98s414 186 414 414-186 414-414 414zm0-73c189 0 341-153 341-341S701 171 512 171 171 323 171 512s153 341 341 341zm-6-192L284 439l52-52 171 171 171-171L728 439l-222 222z</StreamGeometry>
@ -118,6 +120,7 @@
<StreamGeometry x:Key="Icons.Tags">M996 452 572 28A96 96 0 00504 0H96C43 0 0 43 0 96v408a96 96 0 0028 68l424 424c37 37 98 37 136 0l408-408c37-37 37-98 0-136zM224 320c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm1028 268L844 996c-37 37-98 37-136 0l-1-1L1055 647c34-34 53-79 53-127s-19-93-53-127L663 0h97a96 96 0 0168 28l424 424c37 37 37 98 0 136z</StreamGeometry> <StreamGeometry x:Key="Icons.Tags">M996 452 572 28A96 96 0 00504 0H96C43 0 0 43 0 96v408a96 96 0 0028 68l424 424c37 37 98 37 136 0l408-408c37-37 37-98 0-136zM224 320c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm1028 268L844 996c-37 37-98 37-136 0l-1-1L1055 647c34-34 53-79 53-127s-19-93-53-127L663 0h97a96 96 0 0168 28l424 424c37 37 37 98 0 136z</StreamGeometry>
<StreamGeometry x:Key="Icons.Target">M765 118 629 239l-16 137-186 160 54 59 183-168 144 4 136-129 47-43-175-12L827 67zM489 404c-66 0-124 55-124 125s54 121 124 121c66 0 120-55 120-121H489l23-121c-8-4-16-4-23-4zM695 525c0 114-93 207-206 207s-206-94-206-207 93-207 206-207c16 0 27 0 43 4l43-207c-27-4-54-8-85-8-229 0-416 188-416 419s187 419 416 419c225 0 408-180 416-403v-12l-210-4z</StreamGeometry> <StreamGeometry x:Key="Icons.Target">M765 118 629 239l-16 137-186 160 54 59 183-168 144 4 136-129 47-43-175-12L827 67zM489 404c-66 0-124 55-124 125s54 121 124 121c66 0 120-55 120-121H489l23-121c-8-4-16-4-23-4zM695 525c0 114-93 207-206 207s-206-94-206-207 93-207 206-207c16 0 27 0 43 4l43-207c-27-4-54-8-85-8-229 0-416 188-416 419s187 419 416 419c225 0 408-180 416-403v-12l-210-4z</StreamGeometry>
<StreamGeometry x:Key="Icons.Terminal">M144 112h736c18 0 32 14 32 32v736c0 18-14 32-32 32H144c-18 0-32-14-32-32V144c0-18 14-32 32-32zm112 211v72a9 9 0 003 7L386 509 259 615a9 9 0 00-3 7v72a9 9 0 0015 7L493 516a9 9 0 000-14l-222-186a9 9 0 00-15 7zM522 624a10 10 0 00-10 10v60a10 10 0 0010 10h237a10 10 0 0010-10v-60a10 10 0 00-10-10H522z</StreamGeometry> <StreamGeometry x:Key="Icons.Terminal">M144 112h736c18 0 32 14 32 32v736c0 18-14 32-32 32H144c-18 0-32-14-32-32V144c0-18 14-32 32-32zm112 211v72a9 9 0 003 7L386 509 259 615a9 9 0 00-3 7v72a9 9 0 0015 7L493 516a9 9 0 000-14l-222-186a9 9 0 00-15 7zM522 624a10 10 0 00-10 10v60a10 10 0 0010 10h237a10 10 0 0010-10v-60a10 10 0 00-10-10H522z</StreamGeometry>
<StreamGeometry x:Key="Icons.Top">M170 831 513 489 855 831 960 726 512 278 64 726 170 831zM512 278h448v-128h-896v128h448z</StreamGeometry>
<StreamGeometry x:Key="Icons.Track">M897 673v13c0 51-42 93-93 93h-10c-1 0-2 0-2 0H220c-23 0-42 19-42 42v13c0 23 19 42 42 42h552c14 0 26 12 26 26 0 14-12 26-26 26H220c-51 0-93-42-93-93v-13c0-51 42-93 93-93h20c1-0 2-0 2-0h562c23 0 42-19 42-42v-13c0-11-5-22-13-29-8-7-17-11-28-10H660c-14 0-26-12-26-26 0-14 12-26 26-26h144c24-1 47 7 65 24 18 17 29 42 29 67zM479 98c-112 0-203 91-203 203 0 44 14 85 38 118l132 208c15 24 50 24 66 0l133-209c23-33 37-73 37-117 0-112-91-203-203-203zm0 327c-68 0-122-55-122-122s55-122 122-122 122 55 122 122-55 122-122 122z</StreamGeometry> <StreamGeometry x:Key="Icons.Track">M897 673v13c0 51-42 93-93 93h-10c-1 0-2 0-2 0H220c-23 0-42 19-42 42v13c0 23 19 42 42 42h552c14 0 26 12 26 26 0 14-12 26-26 26H220c-51 0-93-42-93-93v-13c0-51 42-93 93-93h20c1-0 2-0 2-0h562c23 0 42-19 42-42v-13c0-11-5-22-13-29-8-7-17-11-28-10H660c-14 0-26-12-26-26 0-14 12-26 26-26h144c24-1 47 7 65 24 18 17 29 42 29 67zM479 98c-112 0-203 91-203 203 0 44 14 85 38 118l132 208c15 24 50 24 66 0l133-209c23-33 37-73 37-117 0-112-91-203-203-203zm0 327c-68 0-122-55-122-122s55-122 122-122 122 55 122 122-55 122-122 122z</StreamGeometry>
<StreamGeometry x:Key="Icons.Tree">M912 800a48 48 0 1 1 0 96h-416a48 48 0 1 1 0-96h416z m-704-704A112 112 0 0 1 256 309.184V480h80a48 48 0 0 1 0 96H256v224h81.664a48 48 0 1 1 0 96H256a96 96 0 0 1-96-96V309.248A112 112 0 0 1 208 96z m704 384a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z m0-320a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z</StreamGeometry> <StreamGeometry x:Key="Icons.Tree">M912 800a48 48 0 1 1 0 96h-416a48 48 0 1 1 0-96h416z m-704-704A112 112 0 0 1 256 309.184V480h80a48 48 0 0 1 0 96H256v224h81.664a48 48 0 1 1 0 96H256a96 96 0 0 1-96-96V309.248A112 112 0 0 1 208 96z m704 384a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z m0-320a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z</StreamGeometry>
<StreamGeometry x:Key="Icons.TriangleLeft">M30 0 30 30 0 15z</StreamGeometry> <StreamGeometry x:Key="Icons.TriangleLeft">M30 0 30 30 0 15z</StreamGeometry>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

@ -59,6 +59,7 @@
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Mit HEAD vergleichen</x:String> <x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Mit HEAD vergleichen</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Mit Worktree vergleichen</x:String> <x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Mit Worktree vergleichen</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Branch-Namen kopieren</x:String> <x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Branch-Namen kopieren</x:String>
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">Benutzerdefinierte Aktion</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Lösche ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Lösche ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Lösche alle ausgewählten {0} Branches</x:String> <x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Lösche alle ausgewählten {0} Branches</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Alle Änderungen verwerfen</x:String> <x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Alle Änderungen verwerfen</x:String>
@ -159,6 +160,7 @@
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Ausführbare Datei:</x:String> <x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Ausführbare Datei:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Name:</x:String> <x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Name:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Geltungsbereich:</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Geltungsbereich:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Branch" xml:space="preserve">Branch</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repository</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repository</x:String>
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Email Adresse</x:String> <x:String x:Key="Text.Configure.Email" xml:space="preserve">Email Adresse</x:String>
@ -586,6 +588,7 @@
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOKALE BRANCHES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOKALE BRANCHES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Zum HEAD wechseln</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Zum HEAD wechseln</x:String>
<x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Erstelle Branch</x:String> <x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Erstelle Branch</x:String>
<x:String x:Key="Text.Repository.Notifications.Clear" xml:space="preserve">BENACHRICHTIGUNGEN LÖSCHEN</x:String>
<x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInHistories" xml:space="preserve">Nur aktuellen Branch im Graphen hervorheben</x:String> <x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInHistories" xml:space="preserve">Nur aktuellen Branch im Graphen hervorheben</x:String>
<x:String x:Key="Text.Repository.OpenIn" xml:space="preserve">Öffne in {0}</x:String> <x:String x:Key="Text.Repository.OpenIn" xml:space="preserve">Öffne in {0}</x:String>
<x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Öffne in externen Tools</x:String> <x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Öffne in externen Tools</x:String>
@ -710,7 +713,7 @@
<x:String x:Key="Text.Welcome.OpenAllInNode" xml:space="preserve">Öffne alle Repositories</x:String> <x:String x:Key="Text.Welcome.OpenAllInNode" xml:space="preserve">Öffne alle Repositories</x:String>
<x:String x:Key="Text.Welcome.OpenOrInit" xml:space="preserve">Öffne Repository</x:String> <x:String x:Key="Text.Welcome.OpenOrInit" xml:space="preserve">Öffne Repository</x:String>
<x:String x:Key="Text.Welcome.OpenTerminal" xml:space="preserve">Öffne Terminal</x:String> <x:String x:Key="Text.Welcome.OpenTerminal" xml:space="preserve">Öffne Terminal</x:String>
<x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Klon Standardordner erneut nach Repositories durchsuchen</x:String> <x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Klon Standardordner erneut nach Repositories durchsuchen</x:String>
<x:String x:Key="Text.Welcome.Search" xml:space="preserve">Suche Repositories...</x:String> <x:String x:Key="Text.Welcome.Search" xml:space="preserve">Suche Repositories...</x:String>
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sortieren</x:String> <x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sortieren</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">Änderungen</x:String> <x:String x:Key="Text.WorkingCopy" xml:space="preserve">Änderungen</x:String>

View file

@ -72,6 +72,7 @@
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Rename ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Rename ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Set Tracking Branch...</x:String> <x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Set Tracking Branch...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">Branch Compare</x:String> <x:String x:Key="Text.BranchCompare" xml:space="preserve">Branch Compare</x:String>
<x:String x:Key="Text.BranchUpstreamInvalid" xml:space="preserve">Invalid upstream!</x:String>
<x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String> <x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String> <x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Reset to This Revision</x:String> <x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Reset to This Revision</x:String>
@ -249,7 +250,9 @@
<x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">OLD</x:String> <x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">OLD</x:String>
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">Copy</x:String> <x:String x:Key="Text.Diff.Copy" xml:space="preserve">Copy</x:String>
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">File Mode Changed</x:String> <x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">File Mode Changed</x:String>
<x:String x:Key="Text.Diff.First" xml:space="preserve">First Difference</x:String>
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Ignore Whitespace Change</x:String> <x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Ignore Whitespace Change</x:String>
<x:String x:Key="Text.Diff.Last" xml:space="preserve">Last Difference</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS OBJECT CHANGE</x:String> <x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS OBJECT CHANGE</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Next Difference</x:String> <x:String x:Key="Text.Diff.Next" xml:space="preserve">Next Difference</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NO CHANGES OR ONLY EOL CHANGES</x:String> <x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NO CHANGES OR ONLY EOL CHANGES</x:String>
@ -459,6 +462,7 @@
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Model</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.Name" xml:space="preserve">Name</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</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" 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.DefaultFont" xml:space="preserve">Default Font</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Font Size</x:String> <x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Font Size</x:String>

View file

@ -22,7 +22,9 @@
<x:String x:Key="Text.AddWorktree.Tracking" xml:space="preserve">Rama de Seguimiento:</x:String> <x:String x:Key="Text.AddWorktree.Tracking" xml:space="preserve">Rama de Seguimiento:</x:String>
<x:String x:Key="Text.AddWorktree.Tracking.Toggle" xml:space="preserve">Seguimiento de rama remota</x:String> <x:String x:Key="Text.AddWorktree.Tracking.Toggle" xml:space="preserve">Seguimiento de rama remota</x:String>
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Asistente OpenAI</x:String> <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.Tip" xml:space="preserve">Usar OpenAI para generar mensaje de commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APLICAR CÓMO MENSAJE DE COMMIT</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Aplicar Patch</x:String> <x:String x:Key="Text.Apply" xml:space="preserve">Aplicar Patch</x:String>
<x:String x:Key="Text.Apply.Error" xml:space="preserve">Error</x:String> <x:String x:Key="Text.Apply.Error" xml:space="preserve">Error</x:String>
<x:String x:Key="Text.Apply.Error.Desc" xml:space="preserve">Genera errores y se niega a aplicar el patch</x:String> <x:String x:Key="Text.Apply.Error.Desc" xml:space="preserve">Genera errores y se niega a aplicar el patch</x:String>
@ -37,6 +39,10 @@
<x:String x:Key="Text.Apply.Warn" xml:space="preserve">Advertencia</x:String> <x:String x:Key="Text.Apply.Warn" xml:space="preserve">Advertencia</x:String>
<x:String x:Key="Text.Apply.Warn.Desc" xml:space="preserve">Genera advertencias para algunos de estos errores, pero aplica</x:String> <x:String x:Key="Text.Apply.Warn.Desc" xml:space="preserve">Genera advertencias para algunos de estos errores, pero aplica</x:String>
<x:String x:Key="Text.Apply.WS" xml:space="preserve">Espacios en Blanco:</x:String> <x:String x:Key="Text.Apply.WS" xml:space="preserve">Espacios en Blanco:</x:String>
<x:String x:Key="Text.ApplyStash" xml:space="preserve">Aplicar Stash</x:String>
<x:String x:Key="Text.ApplyStash.DropAfterApply" xml:space="preserve">Borrar después de aplicar</x:String>
<x:String x:Key="Text.ApplyStash.RestoreIndex" xml:space="preserve">Restaurar los cambios del índice</x:String>
<x:String x:Key="Text.ApplyStash.Stash" xml:space="preserve">Stash:</x:String>
<x:String x:Key="Text.Archive" xml:space="preserve">Archivar...</x:String> <x:String x:Key="Text.Archive" xml:space="preserve">Archivar...</x:String>
<x:String x:Key="Text.Archive.File" xml:space="preserve">Guardar Archivo en:</x:String> <x:String x:Key="Text.Archive.File" xml:space="preserve">Guardar Archivo en:</x:String>
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">Seleccionar ruta del archivo</x:String> <x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">Seleccionar ruta del archivo</x:String>
@ -53,6 +59,7 @@
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Comparar con HEAD</x:String> <x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Comparar con HEAD</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Comparar con Worktree</x:String> <x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Comparar con Worktree</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copiar Nombre de Rama</x:String> <x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copiar Nombre de Rama</x:String>
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">Acción personalizada</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Eliminar ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Eliminar ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Eliminar {0} ramas seleccionadas</x:String> <x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Eliminar {0} ramas seleccionadas</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Descartar todos los cambios</x:String> <x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Descartar todos los cambios</x:String>
@ -68,6 +75,7 @@
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Renombrar ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Renombrar ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Establecer Rama de Seguimiento...</x:String> <x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Establecer Rama de Seguimiento...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">Comparar Ramas</x:String> <x:String x:Key="Text.BranchCompare" xml:space="preserve">Comparar Ramas</x:String>
<x:String x:Key="Text.BranchUpstreamInvalid" xml:space="preserve">¡Upstream inválido!</x:String>
<x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String> <x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCELAR</x:String> <x:String x:Key="Text.Cancel" xml:space="preserve">CANCELAR</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Resetear a Esta Revisión</x:String> <x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Resetear a Esta Revisión</x:String>
@ -100,6 +108,7 @@
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">Nombre Local:</x:String> <x:String x:Key="Text.Clone.LocalName" xml:space="preserve">Nombre Local:</x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">Nombre del repositorio. Opcional.</x:String> <x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">Nombre del repositorio. Opcional.</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Carpeta Padre:</x:String> <x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Carpeta Padre:</x:String>
<x:String x:Key="Text.Clone.RecurseSubmodules" xml:space="preserve">Inicializar y actualizar submodulos</x:String>
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">URL del Repositorio:</x:String> <x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">URL del Repositorio:</x:String>
<x:String x:Key="Text.Close" xml:space="preserve">CERRAR</x:String> <x:String x:Key="Text.Close" xml:space="preserve">CERRAR</x:String>
<x:String x:Key="Text.CodeEditor" xml:space="preserve">Editor</x:String> <x:String x:Key="Text.CodeEditor" xml:space="preserve">Editor</x:String>
@ -152,8 +161,10 @@
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Archivo Ejecutable:</x:String> <x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Archivo Ejecutable:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Nombre:</x:String> <x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Nombre:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Alcance:</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Alcance:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Branch" xml:space="preserve">Rama</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Commit" xml:space="preserve">Commit</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repositorio</x:String> <x:String x:Key="Text.Configure.CustomAction.Scope.Repository" xml:space="preserve">Repositorio</x:String>
<x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">Esperar la acción de salida</x:String>
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Dirección de Email</x:String> <x:String x:Key="Text.Configure.Email" xml:space="preserve">Dirección de Email</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Dirección de email</x:String> <x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Dirección de email</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String> <x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
@ -202,6 +213,7 @@
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">Stash &amp; Reaplicar</x:String> <x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" 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" 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.Name.Placeholder" xml:space="preserve">Introduzca el nombre de la rama.</x:String>
<x:String x:Key="Text.CreateBranch.Name.WarnSpace" xml:space="preserve">Los espacios serán reemplazados con guiones.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Crear Rama Local</x:String> <x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Crear Rama Local</x:String>
<x:String x:Key="Text.CreateTag" xml:space="preserve">Crear Etiqueta...</x:String> <x:String x:Key="Text.CreateTag" xml:space="preserve">Crear Etiqueta...</x:String>
<x:String x:Key="Text.CreateTag.BasedOn" xml:space="preserve">Nueva Etiqueta En:</x:String> <x:String x:Key="Text.CreateTag.BasedOn" xml:space="preserve">Nueva Etiqueta En:</x:String>
@ -225,8 +237,11 @@
<x:String x:Key="Text.DeleteMultiBranch.Tip" xml:space="preserve">Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de revisar antes de tomar acción!</x:String> <x:String x:Key="Text.DeleteMultiBranch.Tip" xml:space="preserve">Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de revisar antes de tomar acción!</x:String>
<x:String x:Key="Text.DeleteRemote" xml:space="preserve">Eliminar Remoto</x:String> <x:String x:Key="Text.DeleteRemote" xml:space="preserve">Eliminar Remoto</x:String>
<x:String x:Key="Text.DeleteRemote.Remote" xml:space="preserve">Remoto:</x:String> <x:String x:Key="Text.DeleteRemote.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.Path" xml:space="preserve">Ruta:</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.Target" xml:space="preserve">Destino:</x:String> <x:String x:Key="Text.DeleteRepositoryNode.Target" xml:space="preserve">Destino:</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TipForGroup" xml:space="preserve">Todos los hijos serán removidos de la lista.</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TitleForGroup" xml:space="preserve">Confirmar Eliminación de Grupo</x:String> <x:String x:Key="Text.DeleteRepositoryNode.TitleForGroup" xml:space="preserve">Confirmar Eliminación de Grupo</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TipForRepository" xml:space="preserve">¡Esto solo lo removera de la lista, no del disco!</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TitleForRepository" xml:space="preserve">Confirmar Eliminación de Repositorio</x:String> <x:String x:Key="Text.DeleteRepositoryNode.TitleForRepository" xml:space="preserve">Confirmar Eliminación de Repositorio</x:String>
<x:String x:Key="Text.DeleteSubmodule" xml:space="preserve">Eliminar Submódulo</x:String> <x:String x:Key="Text.DeleteSubmodule" xml:space="preserve">Eliminar Submódulo</x:String>
<x:String x:Key="Text.DeleteSubmodule.Path" xml:space="preserve">Ruta del Submódulo:</x:String> <x:String x:Key="Text.DeleteSubmodule.Path" xml:space="preserve">Ruta del Submódulo:</x:String>
@ -449,6 +464,7 @@
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modelo</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.Name" xml:space="preserve">Nombre</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Servidor</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">Activar Transmisión</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APARIENCIA</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.DefaultFont" xml:space="preserve">Fuente por defecto</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Tamaño de fuente</x:String> <x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Tamaño de fuente</x:String>
@ -576,6 +592,7 @@
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">RAMAS LOCALES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">RAMAS LOCALES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar a HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar a HEAD</x:String>
<x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Crear Rama</x:String> <x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Crear Rama</x:String>
<x:String x:Key="Text.Repository.Notifications.Clear" xml:space="preserve">LIMPIAR NOTIFICACIONES</x:String>
<x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInHistories" xml:space="preserve">Resaltar solo la rama actual en el gráfico</x:String> <x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInHistories" xml:space="preserve">Resaltar solo la rama actual en el gráfico</x:String>
<x:String x:Key="Text.Repository.OpenIn" xml:space="preserve">Abrir en {0}</x:String> <x:String x:Key="Text.Repository.OpenIn" xml:space="preserve">Abrir en {0}</x:String>
<x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Abrir en Herramientas Externas</x:String> <x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Abrir en Herramientas Externas</x:String>
@ -642,6 +659,8 @@
<x:String x:Key="Text.SSHKey.Placeholder" xml:space="preserve">Ruta de almacenamiento de la clave privada SSH</x:String> <x:String x:Key="Text.SSHKey.Placeholder" xml:space="preserve">Ruta de almacenamiento de la clave privada SSH</x:String>
<x:String x:Key="Text.Start" xml:space="preserve">INICIAR</x:String> <x:String x:Key="Text.Start" xml:space="preserve">INICIAR</x:String>
<x:String x:Key="Text.Stash" xml:space="preserve">Stash</x:String> <x:String x:Key="Text.Stash" xml:space="preserve">Stash</x:String>
<x:String x:Key="Text.Stash.AutoRestore" xml:space="preserve">Restaurar automáticamente después del stashing</x:String>
<x:String x:Key="Text.Stash.AutoRestore.Tip" xml:space="preserve">Tus archivos de trabajo permanecen sin cambios, pero se guarda un stash.</x:String>
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">Incluir archivos no rastreados</x:String> <x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">Incluir archivos no rastreados</x:String>
<x:String x:Key="Text.Stash.KeepIndex" xml:space="preserve">Mantener archivos staged</x:String> <x:String x:Key="Text.Stash.KeepIndex" xml:space="preserve">Mantener archivos staged</x:String>
<x:String x:Key="Text.Stash.Message" xml:space="preserve">Mensaje:</x:String> <x:String x:Key="Text.Stash.Message" xml:space="preserve">Mensaje:</x:String>
@ -721,6 +740,7 @@
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUIR ARCHIVOS NO RASTREADOS</x:String> <x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUIR ARCHIVOS NO RASTREADOS</x:String>
<x:String x:Key="Text.WorkingCopy.NoCommitHistories" xml:space="preserve">NO HAY MENSAJES DE ENTRADA RECIENTES</x:String> <x:String x:Key="Text.WorkingCopy.NoCommitHistories" xml:space="preserve">NO HAY MENSAJES DE ENTRADA RECIENTES</x:String>
<x:String x:Key="Text.WorkingCopy.NoCommitTemplates" xml:space="preserve">NO HAY PLANTILLAS DE COMMIT</x:String> <x:String x:Key="Text.WorkingCopy.NoCommitTemplates" xml:space="preserve">NO HAY PLANTILLAS DE COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.SignOff" xml:space="preserve">Firmar</x:String>
<x:String x:Key="Text.WorkingCopy.Staged" xml:space="preserve">STAGED</x:String> <x:String x:Key="Text.WorkingCopy.Staged" xml:space="preserve">STAGED</x:String>
<x:String x:Key="Text.WorkingCopy.Staged.Unstage" xml:space="preserve">UNSTAGE</x:String> <x:String x:Key="Text.WorkingCopy.Staged.Unstage" xml:space="preserve">UNSTAGE</x:String>
<x:String x:Key="Text.WorkingCopy.Staged.UnstageAll" xml:space="preserve">UNSTAGE TODO</x:String> <x:String x:Key="Text.WorkingCopy.Staged.UnstageAll" xml:space="preserve">UNSTAGE TODO</x:String>

View file

@ -75,6 +75,7 @@
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重命名 ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重命名 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切换上游分支 ...</x:String> <x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切换上游分支 ...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比较</x:String> <x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比较</x:String>
<x:String x:Key="Text.BranchUpstreamInvalid" xml:space="preserve">跟踪的上游分支不存在或已删除!</x:String>
<x:String x:Key="Text.Bytes" xml:space="preserve">字节</x:String> <x:String x:Key="Text.Bytes" xml:space="preserve">字节</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String> <x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重置文件到该版本</x:String> <x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重置文件到该版本</x:String>
@ -252,7 +253,9 @@
<x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">原始大小</x:String> <x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">原始大小</x:String>
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">复制</x:String> <x:String x:Key="Text.Diff.Copy" xml:space="preserve">复制</x:String>
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">文件权限已变化</x:String> <x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">文件权限已变化</x:String>
<x:String x:Key="Text.Diff.First" xml:space="preserve">首个差异</x:String>
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">忽略空白符号变化</x:String> <x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">忽略空白符号变化</x:String>
<x:String x:Key="Text.Diff.Last" xml:space="preserve">最后一个差异</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS对象变更</x:String> <x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS对象变更</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">下一个差异</x:String> <x:String x:Key="Text.Diff.Next" xml:space="preserve">下一个差异</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">没有变更或仅有换行符差异</x:String> <x:String x:Key="Text.Diff.NoChange" xml:space="preserve">没有变更或仅有换行符差异</x:String>
@ -462,6 +465,7 @@
<x:String x:Key="Text.Preferences.AI.Model" 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.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.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" 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.DefaultFont" xml:space="preserve">缺省字体</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">字体大小</x:String> <x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">字体大小</x:String>

View file

@ -75,6 +75,7 @@
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重新命名 ${0}$...</x:String> <x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重新命名 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切換上游分支...</x:String> <x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切換上游分支...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比較</x:String> <x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比較</x:String>
<x:String x:Key="Text.BranchUpstreamInvalid" xml:space="preserve">追蹤上游分支不存在或已刪除!</x:String>
<x:String x:Key="Text.Bytes" xml:space="preserve">位元組</x:String> <x:String x:Key="Text.Bytes" xml:space="preserve">位元組</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String> <x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重設檔案為此版本</x:String> <x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重設檔案為此版本</x:String>
@ -252,7 +253,9 @@
<x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">原始大小</x:String> <x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">原始大小</x:String>
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">複製</x:String> <x:String x:Key="Text.Diff.Copy" xml:space="preserve">複製</x:String>
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">檔案權限已變更</x:String> <x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">檔案權限已變更</x:String>
<x:String x:Key="Text.Diff.First" xml:space="preserve">第一個差異</x:String>
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">忽略空白符號變化</x:String> <x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">忽略空白符號變化</x:String>
<x:String x:Key="Text.Diff.Last" xml:space="preserve">最後一個差異</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS 物件變更</x:String> <x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS 物件變更</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">下一個差異</x:String> <x:String x:Key="Text.Diff.Next" xml:space="preserve">下一個差異</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">沒有變更或僅有換行字元差異</x:String> <x:String x:Key="Text.Diff.NoChange" xml:space="preserve">沒有變更或僅有換行字元差異</x:String>
@ -456,12 +459,13 @@
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} 年前</x:String> <x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} 年前</x:String>
<x:String x:Key="Text.Preferences" 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" xml:space="preserve">AI</x:String>
<x:String x:Key="Text.Preferences.AI.Server" 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">API 金鑰</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.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.Name" 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.Server" 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.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" 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.DefaultFont" xml:space="preserve">預設字型</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">字型大小</x:String> <x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">字型大小</x:String>

View file

@ -48,10 +48,10 @@
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.4" Condition="'$(Configuration)' == 'Debug'" /> <PackageReference Include="Avalonia.Diagnostics" Version="11.2.4" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" /> <PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" /> <PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.1" /> <PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc5.1" /> <PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc5.4" />
<PackageReference Include="OpenAI" Version="2.2.0-beta.1" /> <PackageReference Include="OpenAI" Version="2.2.0-beta.2" />
<PackageReference Include="TextMateSharp" Version="1.0.66" /> <PackageReference Include="TextMateSharp" Version="1.0.66" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.66" /> <PackageReference Include="TextMateSharp.Grammars" Version="1.0.66" />
</ItemGroup> </ItemGroup>

View file

@ -101,12 +101,12 @@ namespace SourceGit.ViewModels
return (_current >= 0 && _current < Blocks.Count) ? Blocks[_current] : null; return (_current >= 0 && _current < Blocks.Count) ? Blocks[_current] : null;
} }
public Block GotoNext() public Block GotoFirst()
{ {
if (Blocks.Count == 0) if (Blocks.Count == 0)
return null; return null;
Current = (_current + 1) % Blocks.Count; Current = 0;
return Blocks[_current]; return Blocks[_current];
} }
@ -115,7 +115,29 @@ namespace SourceGit.ViewModels
if (Blocks.Count == 0) if (Blocks.Count == 0)
return null; return null;
Current = _current == -1 ? Blocks.Count - 1 : (_current - 1 + Blocks.Count) % Blocks.Count; if (_current == -1)
Current = 0;
else if (_current > 0)
Current = _current - 1;
return Blocks[_current];
}
public Block GotoNext()
{
if (Blocks.Count == 0)
return null;
if (_current < Blocks.Count - 1)
Current = _current + 1;
return Blocks[_current];
}
public Block GotoLast()
{
if (Blocks.Count == 0)
return null;
Current = Blocks.Count - 1;
return Blocks[_current]; return Blocks[_current];
} }

View file

@ -1,9 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia; using Avalonia;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
@ -45,6 +42,11 @@ namespace SourceGit.ViewModels
get => Backend is Models.Branch { IsCurrent: true }; get => Backend is Models.Branch { IsCurrent: true };
} }
public bool ShowUpstreamGoneTip
{
get => Backend is Models.Branch { IsUpsteamGone: true };
}
public string Tooltip public string Tooltip
{ {
get => Backend is Models.Branch b ? b.FriendlyName : null; get => Backend is Models.Branch b ? b.FriendlyName : null;

View file

@ -39,11 +39,11 @@ namespace SourceGit.ViewModels
set; set;
} = false; } = false;
public bool PushToAllRemotes public bool PushToRemotes
{ {
get; get => _repo.Settings.PushToRemoteWhenCreateTag;
set; set => _repo.Settings.PushToRemoteWhenCreateTag = value;
} = true; }
public CreateTag(Repository repo, Models.Branch branch) public CreateTag(Repository repo, Models.Branch branch)
{ {
@ -82,6 +82,7 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false); _repo.SetWatcherEnabled(false);
ProgressDescription = "Create tag..."; ProgressDescription = "Create tag...";
var remotes = PushToRemotes ? _repo.Remotes : null;
return Task.Run(() => return Task.Run(() =>
{ {
bool succ; bool succ;
@ -90,9 +91,9 @@ namespace SourceGit.ViewModels
else else
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn); succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn);
if (succ && PushToAllRemotes) if (succ && remotes != null)
{ {
foreach (var remote in _repo.Remotes) foreach (var remote in remotes)
{ {
SetProgressDescription($"Pushing tag to remote {remote.Name} ..."); SetProgressDescription($"Pushing tag to remote {remote.Name} ...");
new Commands.Push(_repo.FullPath, remote.Name, _tagName, false).Exec(); new Commands.Push(_repo.FullPath, remote.Name, _tagName, false).Exec();

View file

@ -10,17 +10,16 @@ namespace SourceGit.ViewModels
private set; private set;
} }
public bool ShouldPushToRemote public bool PushToRemotes
{ {
get; get => _repo.Settings.PushToRemoteWhenDeleteTag;
set; set => _repo.Settings.PushToRemoteWhenDeleteTag = value;
} }
public DeleteTag(Repository repo, Models.Tag tag) public DeleteTag(Repository repo, Models.Tag tag)
{ {
_repo = repo; _repo = repo;
Target = tag; Target = tag;
ShouldPushToRemote = true;
View = new Views.DeleteTag() { DataContext = this }; View = new Views.DeleteTag() { DataContext = this };
} }
@ -29,9 +28,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false); _repo.SetWatcherEnabled(false);
ProgressDescription = $"Deleting tag '{Target.Name}' ..."; ProgressDescription = $"Deleting tag '{Target.Name}' ...";
var remotes = PushToRemotes ? _repo.Remotes : null;
return Task.Run(() => return Task.Run(() =>
{ {
var remotes = ShouldPushToRemote ? _repo.Remotes : null;
var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes); var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes);
CallUIThread(() => CallUIThread(() =>
{ {

View file

@ -38,7 +38,24 @@ namespace SourceGit.ViewModels
{ {
_repo = repo; _repo = repo;
_fetchAllRemotes = preferedRemote == null; _fetchAllRemotes = preferedRemote == null;
SelectedRemote = preferedRemote != null ? preferedRemote : _repo.Remotes[0];
if (preferedRemote != null)
{
SelectedRemote = preferedRemote;
}
else if (!string.IsNullOrEmpty(_repo.Settings.DefaultRemote))
{
var def = _repo.Remotes.Find(r => r.Name == _repo.Settings.DefaultRemote);
if (def != null)
SelectedRemote = def;
else
SelectedRemote = _repo.Remotes[0];
}
else
{
SelectedRemote = _repo.Remotes[0];
}
View = new Views.Fetch() { DataContext = this }; View = new Views.Fetch() { DataContext = this };
} }

View file

@ -1161,6 +1161,10 @@ namespace SourceGit.ViewModels
public void OpenSubmodule(string submodule) public void OpenSubmodule(string submodule)
{ {
var selfPage = GetOwnerPage();
if (selfPage == null)
return;
var root = Path.GetFullPath(Path.Combine(_fullpath, submodule)); var root = Path.GetFullPath(Path.Combine(_fullpath, submodule));
var normalizedPath = root.Replace("\\", "/"); var normalizedPath = root.Replace("\\", "/");
@ -1171,12 +1175,12 @@ namespace SourceGit.ViewModels
{ {
Id = normalizedPath, Id = normalizedPath,
Name = Path.GetFileName(normalizedPath), Name = Path.GetFileName(normalizedPath),
Bookmark = 0, Bookmark = selfPage.Node.Bookmark,
IsRepository = true, IsRepository = true,
}; };
} }
App.GetLauncer()?.OpenRepositoryInTab(node, null); App.GetLauncer().OpenRepositoryInTab(node, null);
} }
public void AddWorktree() public void AddWorktree()

View file

@ -60,6 +60,12 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _httpProxy, value); set => SetProperty(ref _httpProxy, value);
} }
public bool EnablePruneOnFetch
{
get;
set;
}
public bool EnableAutoFetch public bool EnableAutoFetch
{ {
get => _repo.Settings.EnableAutoFetch; get => _repo.Settings.EnableAutoFetch;
@ -153,6 +159,8 @@ namespace SourceGit.ViewModels
GPGUserSigningKey = signingKey; GPGUserSigningKey = signingKey;
if (_cached.TryGetValue("http.proxy", out var proxy)) if (_cached.TryGetValue("http.proxy", out var proxy))
HttpProxy = proxy; HttpProxy = proxy;
if (_cached.TryGetValue("fetch.prune", out var prune))
EnablePruneOnFetch = (prune == "true");
} }
public void ClearHttpProxy() public void ClearHttpProxy()
@ -286,6 +294,7 @@ namespace SourceGit.ViewModels
SetIfChanged("tag.gpgsign", GPGTagSigningEnabled ? "true" : "false", "false"); SetIfChanged("tag.gpgsign", GPGTagSigningEnabled ? "true" : "false", "false");
SetIfChanged("user.signingkey", GPGUserSigningKey, ""); SetIfChanged("user.signingkey", GPGUserSigningKey, "");
SetIfChanged("http.proxy", HttpProxy, ""); SetIfChanged("http.proxy", HttpProxy, "");
SetIfChanged("fetch.prune", EnablePruneOnFetch ? "true" : "false", "false");
} }
private void SetIfChanged(string key, string value, string defValue) private void SetIfChanged(string key, string value, string defValue)

View file

@ -109,7 +109,7 @@ namespace SourceGit.ViewModels
if (_isLoadingData) if (_isLoadingData)
return; return;
VisibleUnstaged = GetVisibleUnstagedChanges(); VisibleUnstaged = GetVisibleUnstagedChanges(_unstaged);
SelectedUnstaged = []; SelectedUnstaged = [];
} }
} }
@ -214,9 +214,11 @@ namespace SourceGit.ViewModels
OnPropertyChanged(nameof(SelectedStaged)); OnPropertyChanged(nameof(SelectedStaged));
_visibleUnstaged.Clear(); _visibleUnstaged.Clear();
_unstaged.Clear();
OnPropertyChanged(nameof(VisibleUnstaged)); OnPropertyChanged(nameof(VisibleUnstaged));
_unstaged.Clear();
OnPropertyChanged(nameof(Unstaged));
_staged.Clear(); _staged.Clear();
OnPropertyChanged(nameof(Staged)); OnPropertyChanged(nameof(Staged));
@ -231,25 +233,10 @@ namespace SourceGit.ViewModels
// Just force refresh selected changes. // Just force refresh selected changes.
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.Invoke(() =>
{ {
if (_selectedUnstaged.Count == 1)
SetDetail(_selectedUnstaged[0], true);
else if (_selectedStaged.Count == 1)
SetDetail(_selectedStaged[0], false);
else
SetDetail(null, false);
var inProgress = null as InProgressContext;
if (File.Exists(Path.Combine(_repo.GitDir, "CHERRY_PICK_HEAD")))
inProgress = new CherryPickInProgress(_repo);
else if (Directory.Exists(Path.Combine(_repo.GitDir, "rebase-merge")) || Directory.Exists(Path.Combine(_repo.GitDir, "rebase-apply")))
inProgress = new RebaseInProgress(_repo);
else if (File.Exists(Path.Combine(_repo.GitDir, "REVERT_HEAD")))
inProgress = new RevertInProgress(_repo);
else if (File.Exists(Path.Combine(_repo.GitDir, "MERGE_HEAD")))
inProgress = new MergeInProgress(_repo);
HasUnsolvedConflicts = _cached.Find(x => x.IsConflit) != null; HasUnsolvedConflicts = _cached.Find(x => x.IsConflit) != null;
InProgressContext = inProgress;
UpdateDetail();
UpdateInProgressState();
}); });
return; return;
@ -282,9 +269,7 @@ namespace SourceGit.ViewModels
} }
} }
_unstaged = unstaged; var visibleUnstaged = GetVisibleUnstagedChanges(unstaged);
var visibleUnstaged = GetVisibleUnstagedChanges();
var selectedUnstaged = new List<Models.Change>(); var selectedUnstaged = new List<Models.Change>();
foreach (var c in visibleUnstaged) foreach (var c in visibleUnstaged)
{ {
@ -305,37 +290,14 @@ namespace SourceGit.ViewModels
_isLoadingData = true; _isLoadingData = true;
HasUnsolvedConflicts = hasConflict; HasUnsolvedConflicts = hasConflict;
VisibleUnstaged = visibleUnstaged; VisibleUnstaged = visibleUnstaged;
Unstaged = unstaged;
Staged = staged; Staged = staged;
SelectedUnstaged = selectedUnstaged; SelectedUnstaged = selectedUnstaged;
SelectedStaged = selectedStaged; SelectedStaged = selectedStaged;
_isLoadingData = false; _isLoadingData = false;
if (selectedUnstaged.Count == 1) UpdateDetail();
SetDetail(selectedUnstaged[0], true); UpdateInProgressState();
else if (selectedStaged.Count == 1)
SetDetail(selectedStaged[0], false);
else
SetDetail(null, false);
var inProgress = null as InProgressContext;
if (File.Exists(Path.Combine(_repo.GitDir, "CHERRY_PICK_HEAD")))
inProgress = new CherryPickInProgress(_repo);
else if (Directory.Exists(Path.Combine(_repo.GitDir, "rebase-merge")) || Directory.Exists(Path.Combine(_repo.GitDir, "rebase-apply")))
inProgress = new RebaseInProgress(_repo);
else if (File.Exists(Path.Combine(_repo.GitDir, "REVERT_HEAD")))
inProgress = new RevertInProgress(_repo);
else if (File.Exists(Path.Combine(_repo.GitDir, "MERGE_HEAD")))
inProgress = new MergeInProgress(_repo);
InProgressContext = inProgress;
// Try to load merge message from MERGE_MSG
if (string.IsNullOrEmpty(_commitMessage))
{
var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG");
if (File.Exists(mergeMsgFile))
CommitMessage = File.ReadAllText(mergeMsgFile);
}
}); });
} }
@ -391,38 +353,80 @@ namespace SourceGit.ViewModels
public async void UseTheirs(List<Models.Change> changes) public async void UseTheirs(List<Models.Change> changes)
{ {
_repo.SetWatcherEnabled(false);
var files = new List<string>(); var files = new List<string>();
var needStage = new List<string>();
foreach (var change in changes) foreach (var change in changes)
{ {
if (change.IsConflit) if (!change.IsConflit)
continue;
if (change.WorkTree == Models.ChangeState.Deleted)
{
var fullpath = Path.Combine(_repo.FullPath, change.Path);
if (File.Exists(fullpath))
File.Delete(fullpath);
needStage.Add(change.Path);
}
else
{
files.Add(change.Path); files.Add(change.Path);
}
} }
_repo.SetWatcherEnabled(false); if (files.Count > 0)
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
if (succ)
{ {
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec()); var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
if (succ)
needStage.AddRange(files);
} }
if (needStage.Count > 0)
await Task.Run(() => new Commands.Add(_repo.FullPath, needStage).Exec());
_repo.MarkWorkingCopyDirtyManually(); _repo.MarkWorkingCopyDirtyManually();
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);
} }
public async void UseMine(List<Models.Change> changes) public async void UseMine(List<Models.Change> changes)
{ {
_repo.SetWatcherEnabled(false);
var files = new List<string>(); var files = new List<string>();
var needStage = new List<string>();
foreach (var change in changes) foreach (var change in changes)
{ {
if (change.IsConflit) if (!change.IsConflit)
continue;
if (change.Index == Models.ChangeState.Deleted)
{
var fullpath = Path.Combine(_repo.FullPath, change.Path);
if (File.Exists(fullpath))
File.Delete(fullpath);
needStage.Add(change.Path);
}
else
{
files.Add(change.Path); files.Add(change.Path);
}
} }
_repo.SetWatcherEnabled(false); if (files.Count > 0)
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
if (succ)
{ {
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec()); var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
if (succ)
needStage.AddRange(files);
} }
if (needStage.Count > 0)
await Task.Run(() => new Commands.Add(_repo.FullPath, needStage).Exec());
_repo.MarkWorkingCopyDirtyManually(); _repo.MarkWorkingCopyDirtyManually();
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);
} }
@ -1456,14 +1460,14 @@ namespace SourceGit.ViewModels
} }
} }
private List<Models.Change> GetVisibleUnstagedChanges() private List<Models.Change> GetVisibleUnstagedChanges(List<Models.Change> unstaged)
{ {
if (string.IsNullOrEmpty(_unstagedFilter)) if (string.IsNullOrEmpty(_unstagedFilter))
return _unstaged; return unstaged;
var visible = new List<Models.Change>(); var visible = new List<Models.Change>();
foreach (var c in _unstaged) foreach (var c in unstaged)
{ {
if (c.Path.Contains(_unstagedFilter, StringComparison.OrdinalIgnoreCase)) if (c.Path.Contains(_unstagedFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c); visible.Add(c);
@ -1487,9 +1491,61 @@ namespace SourceGit.ViewModels
return rs; return rs;
} }
private void UpdateDetail()
{
if (_selectedUnstaged.Count == 1)
SetDetail(_selectedUnstaged[0], true);
else if (_selectedStaged.Count == 1)
SetDetail(_selectedStaged[0], false);
else
SetDetail(null, false);
}
private void UpdateInProgressState()
{
if (string.IsNullOrEmpty(_commitMessage))
{
var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG");
if (File.Exists(mergeMsgFile))
CommitMessage = File.ReadAllText(mergeMsgFile);
}
if (File.Exists(Path.Combine(_repo.GitDir, "CHERRY_PICK_HEAD")))
{
InProgressContext = new CherryPickInProgress(_repo);
}
else if (Directory.Exists(Path.Combine(_repo.GitDir, "rebase-merge")) || Directory.Exists(Path.Combine(_repo.GitDir, "rebase-apply")))
{
var rebasing = new RebaseInProgress(_repo);
InProgressContext = rebasing;
if (string.IsNullOrEmpty(_commitMessage))
{
var rebaseMsgFile = Path.Combine(_repo.GitDir, "rebase-merge", "message");
if (File.Exists(rebaseMsgFile))
CommitMessage = File.ReadAllText(rebaseMsgFile);
else if (rebasing.StoppedAt != null)
CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, rebasing.StoppedAt.SHA).Result();
}
}
else if (File.Exists(Path.Combine(_repo.GitDir, "REVERT_HEAD")))
{
InProgressContext = new RevertInProgress(_repo);
}
else if (File.Exists(Path.Combine(_repo.GitDir, "MERGE_HEAD")))
{
InProgressContext = new MergeInProgress(_repo);
}
else
{
InProgressContext = null;
}
}
private async void StageChanges(List<Models.Change> changes, Models.Change next) private async void StageChanges(List<Models.Change> changes, Models.Change next)
{ {
if (changes.Count == 0) var count = changes.Count;
if (count == 0)
return; return;
// Use `_selectedUnstaged` instead of `SelectedUnstaged` to avoid UI refresh. // Use `_selectedUnstaged` instead of `SelectedUnstaged` to avoid UI refresh.
@ -1497,7 +1553,7 @@ namespace SourceGit.ViewModels
IsStaging = true; IsStaging = true;
_repo.SetWatcherEnabled(false); _repo.SetWatcherEnabled(false);
if (changes.Count == _unstaged.Count) if (count == _unstaged.Count)
{ {
await Task.Run(() => new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Exec()); await Task.Run(() => new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Exec());
} }
@ -1514,10 +1570,13 @@ namespace SourceGit.ViewModels
} }
else else
{ {
for (int i = 0; i < changes.Count; i += 10) var paths = new List<string>();
foreach (var c in changes)
paths.Add(c.Path);
for (int i = 0; i < count; i += 10)
{ {
var count = Math.Min(10, changes.Count - i); var step = paths.GetRange(i, Math.Min(10, count - i));
var step = changes.GetRange(i, count);
await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec()); await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec());
} }
} }

View file

@ -57,17 +57,21 @@
IsExpanded="{Binding IsExpanded}"/> IsExpanded="{Binding IsExpanded}"/>
<!-- Name --> <!-- Name -->
<Grid Grid.Column="1" ColumnDefinitions="Auto,*"> <TextBlock Grid.Column="1"
<Border Grid.Column="0" Margin="0,0,4,0" Background="Green" Height="16" CornerRadius="8" VerticalAlignment="Center" IsVisible="{Binding IsCurrent}"> Classes="primary"
<TextBlock Text="HEAD" Classes="primary" Margin="8,0" Foreground="#FFDDDDDD" FontSize="10" VerticalAlignment="Center"/> Text="{Binding Name}"
</Border> FontWeight="{Binding IsCurrent, Converter={x:Static c:BoolConverters.IsBoldToFontWeight}}"
TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="1"
Classes="primary" <!-- Upstream invalid tip -->
Text="{Binding Name}" <Border Grid.Column="2"
TextTrimming="CharacterEllipsis"/> Width="12" Height="12"
</Grid> Margin="8,0"
Background="Transparent"
ToolTip.Tip="{DynamicResource Text.BranchUpstreamInvalid}"
IsVisible="{Binding ShowUpstreamGoneTip}">
<Path Data="{StaticResource Icons.Error}" Fill="DarkOrange"/>
</Border>
<!-- Tracking status --> <!-- Tracking status -->
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2" <v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"

View file

@ -52,31 +52,31 @@ namespace SourceGit.Views
if (node.Backend is Models.Remote) if (node.Backend is Models.Remote)
{ {
CreateContent(new Thickness(0, 0, 0, 0), "Icons.Remote"); CreateContent(new Thickness(0, 0, 0, 0), "Icons.Remote", false);
} }
else if (node.Backend is Models.Branch branch) else if (node.Backend is Models.Branch branch)
{ {
if (branch.IsCurrent) if (branch.IsCurrent)
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Check"); CreateContent(new Thickness(0, 0, 0, 0), "Icons.CheckCircled", true);
else else
CreateContent(new Thickness(2, 0, 0, 0), "Icons.Branch"); CreateContent(new Thickness(2, 0, 0, 0), "Icons.Branch", false);
} }
else else
{ {
if (node.IsExpanded) if (node.IsExpanded)
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder.Open"); CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder.Open", false);
else else
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder"); CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder", false);
} }
} }
private void CreateContent(Thickness margin, string iconKey) private void CreateContent(Thickness margin, string iconKey, bool highlight)
{ {
var geo = this.FindResource(iconKey) as StreamGeometry; var geo = this.FindResource(iconKey) as StreamGeometry;
if (geo == null) if (geo == null)
return; return;
Content = new Path() var path = new Path()
{ {
Width = 12, Width = 12,
Height = 12, Height = 12,
@ -85,6 +85,11 @@ namespace SourceGit.Views
Margin = margin, Margin = margin,
Data = geo, Data = geo,
}; };
if (highlight)
path.Fill = Brushes.Green;
Content = path;
} }
} }

View file

@ -84,7 +84,7 @@
<CheckBox Grid.Row="5" Grid.Column="1" <CheckBox Grid.Row="5" Grid.Column="1"
Content="{DynamicResource Text.CreateTag.PushToAllRemotes}" Content="{DynamicResource Text.CreateTag.PushToAllRemotes}"
IsChecked="{Binding PushToAllRemotes, Mode=TwoWay}"/> IsChecked="{Binding PushToRemotes, Mode=TwoWay}"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View file

@ -23,7 +23,7 @@
<CheckBox Grid.Row="1" Grid.Column="1" <CheckBox Grid.Row="1" Grid.Column="1"
Content="{DynamicResource Text.DeleteTag.WithRemote}" Content="{DynamicResource Text.DeleteTag.WithRemote}"
IsChecked="{Binding ShouldPushToRemote, Mode=TwoWay}"/> IsChecked="{Binding PushToRemotes, Mode=TwoWay}"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View file

@ -34,6 +34,20 @@
<!-- Toolbar Buttons --> <!-- Toolbar Buttons -->
<StackPanel Grid.Column="3" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Grid.Column="3" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center">
<Button Classes="icon_button"
Width="28"
Click="OnGotoFirstChange"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.First}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsTextDiff"/>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseBlockNavigationInDiffView" Mode="OneWay"/>
</MultiBinding>
</Button.IsVisible>
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Top}"/>
</Button>
<Button Classes="icon_button" <Button Classes="icon_button"
Width="28" Width="28"
Click="OnGotoPrevChange" Click="OnGotoPrevChange"
@ -61,6 +75,20 @@
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Down}"/> <Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Down}"/>
</Button> </Button>
<Button Classes="icon_button"
Width="28"
Click="OnGotoLastChange"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.Last}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsTextDiff"/>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseBlockNavigationInDiffView" Mode="OneWay"/>
</MultiBinding>
</Button.IsVisible>
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Bottom}"/>
</Button>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="28" Width="28"
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseBlockNavigationInDiffView, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseBlockNavigationInDiffView, Mode=TwoWay}"

View file

@ -11,6 +11,13 @@ namespace SourceGit.Views
InitializeComponent(); InitializeComponent();
} }
private void OnGotoFirstChange(object _, RoutedEventArgs e)
{
var textDiff = this.FindDescendantOfType<TextDiffView>();
textDiff?.GotoFirstChange();
e.Handled = true;
}
private void OnGotoPrevChange(object _, RoutedEventArgs e) private void OnGotoPrevChange(object _, RoutedEventArgs e)
{ {
var textDiff = this.FindDescendantOfType<TextDiffView>(); var textDiff = this.FindDescendantOfType<TextDiffView>();
@ -24,5 +31,12 @@ namespace SourceGit.Views
textDiff?.GotoNextChange(); textDiff?.GotoNextChange();
e.Handled = true; e.Handled = true;
} }
private void OnGotoLastChange(object _, RoutedEventArgs e)
{
var textDiff = this.FindDescendantOfType<TextDiffView>();
textDiff?.GotoLastChange();
e.Handled = true;
}
} }
} }

View file

@ -616,6 +616,10 @@
Text="{Binding GenerateSubjectPrompt, Mode=TwoWay}" Text="{Binding GenerateSubjectPrompt, Mode=TwoWay}"
AcceptsReturn="true" AcceptsReturn="true"
TextWrapping="Wrap"/> TextWrapping="Wrap"/>
<CheckBox Margin="0,12,0,0"
Content="{DynamicResource Text.Preferences.AI.Streaming}"
IsChecked="{Binding Streaming, Mode=TwoWay}"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ContentControl.DataTemplates> </ContentControl.DataTemplates>

View file

@ -44,7 +44,7 @@
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.Git}"/> <TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.Git}"/>
</TabItem.Header> </TabItem.Header>
<Grid Margin="16,4,16,8" RowDefinitions="32,32,32,32,32,32,32,32" ColumnDefinitions="Auto,*"> <Grid Margin="16,4,16,8" RowDefinitions="32,32,32,32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0" <TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0" Margin="0,0,8,0"
@ -119,7 +119,11 @@
Content="{DynamicResource Text.Preferences.GPG.TagEnabled}" Content="{DynamicResource Text.Preferences.GPG.TagEnabled}"
IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/> IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/>
<StackPanel Grid.Row="7" Grid.Column="1" Orientation="Horizontal"> <CheckBox Grid.Row="7" Grid.Column="1"
Content="{DynamicResource Text.Preferences.Git.EnablePruneOnFetch}"
IsChecked="{Binding EnablePruneOnFetch, Mode=TwoWay}"/>
<StackPanel Grid.Row="8" Grid.Column="1" Orientation="Horizontal">
<CheckBox x:Name="AutoFetchCheckBox" <CheckBox x:Name="AutoFetchCheckBox"
Content="{DynamicResource Text.Configure.Git.AutoFetch}" Content="{DynamicResource Text.Configure.Git.AutoFetch}"
IsChecked="{Binding EnableAutoFetch, Mode=TwoWay}"/> IsChecked="{Binding EnableAutoFetch, Mode=TwoWay}"/>
@ -340,7 +344,7 @@
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.CustomAction}"/> <TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.CustomAction}"/>
</TabItem.Header> </TabItem.Header>
<Grid ColumnDefinitions="200,*" Height="300" Margin="0,8,0,16"> <Grid ColumnDefinitions="200,*" Height="340" Margin="0,8,0,16">
<Border Grid.Column="0" <Border Grid.Column="0"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
Background="{DynamicResource Brush.Contents}"> Background="{DynamicResource Brush.Contents}">

View file

@ -543,6 +543,21 @@ namespace SourceGit.Views
{ {
} }
public void GotoFirstChange()
{
var blockNavigation = BlockNavigation;
if (blockNavigation != null)
{
var prev = blockNavigation.GotoFirst();
if (prev != null)
{
TextArea.Caret.Line = prev.Start;
ScrollToLine(prev.Start);
}
}
// NOTE: Not implemented (button hidden) for non-block navigation.
}
public void GotoPrevChange() public void GotoPrevChange()
{ {
var blockNavigation = BlockNavigation; var blockNavigation = BlockNavigation;
@ -641,6 +656,21 @@ namespace SourceGit.Views
} }
} }
public void GotoLastChange()
{
var blockNavigation = BlockNavigation;
if (blockNavigation != null)
{
var next = blockNavigation.GotoLast();
if (next != null)
{
TextArea.Caret.Line = next.Start;
ScrollToLine(next.Start);
}
}
// NOTE: Not implemented (button hidden) for non-block navigation.
}
public override void Render(DrawingContext context) public override void Render(DrawingContext context)
{ {
base.Render(context); base.Render(context);
@ -1682,6 +1712,19 @@ namespace SourceGit.Views
InitializeComponent(); InitializeComponent();
} }
public void GotoFirstChange()
{
var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>();
if (presenter == null)
return;
presenter.GotoFirstChange();
if (presenter is SingleSideTextDiffPresenter singleSide)
singleSide.ForceSyncScrollOffset();
BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty;
}
public void GotoPrevChange() public void GotoPrevChange()
{ {
var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>(); var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>();
@ -1708,6 +1751,19 @@ namespace SourceGit.Views
BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty; BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty;
} }
public void GotoLastChange()
{
var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>();
if (presenter == null)
return;
presenter.GotoLastChange();
if (presenter is SingleSideTextDiffPresenter singleSide)
singleSide.ForceSyncScrollOffset();
BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty;
}
protected override void OnDataContextChanged(EventArgs e) protected override void OnDataContextChanged(EventArgs e)
{ {
base.OnDataContextChanged(e); base.OnDataContextChanged(e);
@ -1796,7 +1852,7 @@ namespace SourceGit.Views
if (!selection.HasLeftChanges) if (!selection.HasLeftChanges)
{ {
new Commands.Add(repo.FullPath, [change]).Exec(); new Commands.Add(repo.FullPath, [change.Path]).Exec();
} }
else else
{ {