Merge branch 'release/v2025.32'

This commit is contained in:
leo
2025-08-25 10:01:42 +08:00
91 changed files with 1368 additions and 1415 deletions

View File

@@ -6,11 +6,29 @@ This document shows the translation status of each locale file in the repository
### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)
### ![de__DE](https://img.shields.io/badge/de__DE-%E2%88%9A-brightgreen)
### ![de__DE](https://img.shields.io/badge/de__DE-99.65%25-yellow)
### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)
<details>
<summary>Missing keys in de_DE.axaml</summary>
### ![fr__FR](https://img.shields.io/badge/fr__FR-80.37%25-yellow)
- Text.Preferences.General.ShowChangesPageByDefault
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
</details>
### ![es__ES](https://img.shields.io/badge/es__ES-99.65%25-yellow)
<details>
<summary>Missing keys in es_ES.axaml</summary>
- Text.Preferences.General.ShowChangesPageByDefault
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
</details>
### ![fr__FR](https://img.shields.io/badge/fr__FR-80.07%25-yellow)
<details>
<summary>Missing keys in fr_FR.axaml</summary>
@@ -113,7 +131,8 @@ This document shows the translation status of each locale file in the repository
- Text.MoveSubmodule
- Text.MoveSubmodule.MoveTo
- Text.MoveSubmodule.Submodule
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.UseLibsecret
- Text.Pull.RecurseSubmodules
@@ -178,6 +197,8 @@ This document shows the translation status of each locale file in the repository
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.ConfirmCommitWithFilter
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
@@ -188,140 +209,19 @@ This document shows the translation status of each locale file in the repository
</details>
### ![it__IT](https://img.shields.io/badge/it__IT-85.57%25-yellow)
### ![it__IT](https://img.shields.io/badge/it__IT-99.54%25-yellow)
<details>
<summary>Missing keys in it_IT.axaml</summary>
- Text.AddToIgnore
- Text.AddToIgnore.Pattern
- Text.AddToIgnore.Storage
- Text.App.Hide
- Text.App.ShowAll
- Text.Askpass.Passphrase
- Text.Avatar.Load
- Text.BranchCM.ResetToSelectedCommit
- Text.BranchTree.InvalidUpstream
- Text.BranchTree.Remote
- Text.BranchTree.Tracking
- Text.BranchTree.URL
- Text.ChangeSubmoduleUrl
- Text.ChangeSubmoduleUrl.Submodule
- Text.ChangeSubmoduleUrl.URL
- Text.Checkout.WarnLostCommits
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.CopyCommitMessage
- Text.CommitCM.InteractiveRebase
- Text.CommitCM.InteractiveRebase.Drop
- Text.CommitCM.InteractiveRebase.Edit
- Text.CommitCM.InteractiveRebase.Fixup
- Text.CommitCM.InteractiveRebase.Manually
- Text.CommitCM.InteractiveRebase.Reword
- Text.CommitCM.InteractiveRebase.Squash
- Text.CommitCM.PushRevision
- Text.CommitCM.Rebase
- Text.CommitCM.Reset
- Text.CommitDetail.Changes.Count
- Text.CommitDetail.Info.Key
- Text.CommitDetail.Info.Signer
- Text.Configure.CommitMessageTemplate.BuiltinVars
- Text.Configure.CustomAction.Arguments.Tip
- Text.Configure.CustomAction.InputControls
- Text.Configure.CustomAction.InputControls.Edit
- Text.Configure.CustomAction.Scope.Remote
- Text.Configure.CustomAction.Scope.Tag
- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit
- Text.Configure.IssueTracker.Share
- Text.ConfigureCustomActionControls
- Text.ConfigureCustomActionControls.CheckedValue
- Text.ConfigureCustomActionControls.CheckedValue.Tip
- Text.ConfigureCustomActionControls.Description
- Text.ConfigureCustomActionControls.DefaultValue
- Text.ConfigureCustomActionControls.IsFolder
- Text.ConfigureCustomActionControls.Label
- Text.ConfigureCustomActionControls.Options
- Text.ConfigureCustomActionControls.Options.Tip
- Text.ConfigureCustomActionControls.Type
- Text.ConfirmRestart.Title
- Text.ConfirmRestart.Message
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.DeleteMultiTags
- Text.DeleteMultiTags.DeleteFromRemotes
- Text.DeleteMultiTags.Tip
- Text.Diff.Image.Blend
- Text.Diff.Image.SideBySide
- Text.Diff.Image.Swipe
- Text.Diff.New
- Text.Diff.Old
- Text.Diff.Submodule.Deleted
- Text.DirHistories
- Text.Discard.IncludeUntracked
- Text.ExecuteCustomAction.Target
- Text.ExecuteCustomAction.Repository
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.InteractiveRebase.ReorderTip
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.MoveSubmodule
- Text.MoveSubmodule.MoveTo
- Text.MoveSubmodule.Submodule
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.Git.UseLibsecret
- Text.Pull.RecurseSubmodules
- Text.Push.New
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.RemoteCM.CustomAction
- Text.Repository.ClearStashes
- Text.Repository.Dashboard
- Text.Repository.MoreOptions
- Text.Repository.OnlyHighlightCurrentBranchInGraph
- Text.Repository.Search.ByPath
- Text.Repository.ShowDecoratedCommitsOnly
- Text.Repository.ShowFirstParentOnly
- Text.Repository.ShowFlags
- Text.Repository.ShowLostCommits
- Text.Repository.UseRelativeTimeInGraph
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.SetSubmoduleBranch
- Text.SetSubmoduleBranch.Submodule
- Text.SetSubmoduleBranch.Current
- Text.SetSubmoduleBranch.New
- Text.SetSubmoduleBranch.New.Tip
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Branch
- Text.Submodule.CopyBranch
- Text.Submodule.Deinit
- Text.Submodule.Histories
- Text.Submodule.Move
- Text.Submodule.SetBranch
- Text.Submodule.SetURL
- Text.Submodule.Update
- Text.Tag.Tagger
- Text.Tag.Time
- Text.TagCM.Copy.Message
- Text.TagCM.Copy.Name
- Text.TagCM.Copy.Tagger
- Text.TagCM.CopyName
- Text.TagCM.CustomAction
- Text.TagCM.DeleteMultiple
- Text.UpdateSubmodules.UpdateToRemoteTrackingBranch
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.ResetAuthor
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
</details>
### ![ja__JP](https://img.shields.io/badge/ja__JP-80.37%25-yellow)
### ![ja__JP](https://img.shields.io/badge/ja__JP-80.07%25-yellow)
<details>
<summary>Missing keys in ja_JP.axaml</summary>
@@ -425,7 +325,8 @@ This document shows the translation status of each locale file in the repository
- Text.MoveSubmodule
- Text.MoveSubmodule.MoveTo
- Text.MoveSubmodule.Submodule
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.UseLibsecret
- Text.Pull.RecurseSubmodules
@@ -489,6 +390,8 @@ This document shows the translation status of each locale file in the repository
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.ConfirmCommitWithFilter
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
@@ -499,7 +402,7 @@ This document shows the translation status of each locale file in the repository
</details>
### ![pt__BR](https://img.shields.io/badge/pt__BR-73.56%25-red)
### ![pt__BR](https://img.shields.io/badge/pt__BR-73.39%25-red)
<details>
<summary>Missing keys in pt_BR.axaml</summary>
@@ -610,7 +513,6 @@ This document shows the translation status of each locale file in the repository
- Text.Diff.New
- Text.Diff.Old
- Text.Diff.Submodule.Deleted
- Text.Diff.UseBlockNavigation
- Text.DirHistories
- Text.Discard.IncludeUntracked
- Text.ExecuteCustomAction.Target
@@ -641,8 +543,9 @@ This document shows the translation status of each locale file in the repository
- Text.MoveSubmodule.Submodule
- Text.Preferences.AI.Streaming
- Text.Preferences.Appearance.EditorTabWidth
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.General.DateFormat
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.Preferences.General.ShowChildren
- Text.Preferences.General.ShowTagsInGraph
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
@@ -724,6 +627,8 @@ This document shows the translation status of each locale file in the repository
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
- Text.WorkingCopy.CommitToEdit
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.ConfirmCommitWithFilter
@@ -736,18 +641,9 @@ This document shows the translation status of each locale file in the repository
</details>
### ![ru__RU](https://img.shields.io/badge/ru__RU-99.65%25-yellow)
### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)
<details>
<summary>Missing keys in ru_RU.axaml</summary>
- Text.App.Hide
- Text.App.ShowAll
- Text.Configure.CustomAction.Arguments.Tip
</details>
### ![ta__IN](https://img.shields.io/badge/ta__IN-80.48%25-yellow)
### ![ta__IN](https://img.shields.io/badge/ta__IN-80.18%25-yellow)
<details>
<summary>Missing keys in ta_IN.axaml</summary>
@@ -851,7 +747,8 @@ This document shows the translation status of each locale file in the repository
- Text.MoveSubmodule
- Text.MoveSubmodule.MoveTo
- Text.MoveSubmodule.Submodule
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.UseLibsecret
- Text.Pull.RecurseSubmodules
@@ -915,6 +812,8 @@ This document shows the translation status of each locale file in the repository
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
@@ -924,7 +823,7 @@ This document shows the translation status of each locale file in the repository
</details>
### ![uk__UA](https://img.shields.io/badge/uk__UA-81.64%25-yellow)
### ![uk__UA](https://img.shields.io/badge/uk__UA-81.34%25-yellow)
<details>
<summary>Missing keys in uk_UA.axaml</summary>
@@ -1023,7 +922,8 @@ This document shows the translation status of each locale file in the repository
- Text.MoveSubmodule
- Text.MoveSubmodule.MoveTo
- Text.MoveSubmodule.Submodule
- Text.Preferences.Appearance.EnableCompactFolders
- Text.Preferences.General.EnableCompactFolders
- Text.Preferences.General.ShowChangesPageByDefault
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.UseLibsecret
- Text.Pull.RecurseSubmodules
@@ -1086,6 +986,8 @@ This document shows the translation status of each locale file in the repository
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ClearCommitHistories
- Text.WorkingCopy.ClearCommitHistories.Confirm
- Text.WorkingCopy.ConfirmCommitWithDetachedHead
- Text.WorkingCopy.ResetAuthor

View File

@@ -1 +1 @@
2025.31
2025.32

View File

@@ -20,10 +20,10 @@ mkdir -p %{buildroot}/opt/sourcegit
mkdir -p %{buildroot}/%{_bindir}
mkdir -p %{buildroot}/usr/share/applications
mkdir -p %{buildroot}/usr/share/icons
cp -f ../../../SourceGit/* %{buildroot}/opt/sourcegit/
cp -f %{_topdir}/../../SourceGit/* %{buildroot}/opt/sourcegit/
ln -rsf %{buildroot}/opt/sourcegit/sourcegit %{buildroot}/%{_bindir}
cp -r ../../_common/applications %{buildroot}/%{_datadir}
cp -r ../../_common/icons %{buildroot}/%{_datadir}
cp -r %{_topdir}/../_common/applications %{buildroot}/%{_datadir}
cp -r %{_topdir}/../_common/icons %{buildroot}/%{_datadir}
chmod 755 -R %{buildroot}/opt/sourcegit
chmod 755 %{buildroot}/%{_datadir}/applications/sourcegit.desktop

View File

@@ -608,10 +608,10 @@ namespace SourceGit
try
{
// Fetch latest release information.
using var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) };
var data = await client.GetStringAsync("https://sourcegit-scm.github.io/data/version.json");
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
// Parse JSON into Models.Version.
var data = await client.GetStringAsync("https://sourcegit-scm.github.io/data/version.json");
var ver = JsonSerializer.Deserialize(data, JsonCodeGen.Default.Version);
if (ver == null)
return;

View File

@@ -48,8 +48,8 @@ namespace SourceGit.Commands
proc.OutputDataReceived += (_, e) => HandleOutput(e.Data, errs);
proc.ErrorDataReceived += (_, e) => HandleOutput(e.Data, errs);
Process dummy = null;
var dummyProcLock = new object();
var captured = new CapturedProcess() { Process = proc };
var capturedLock = new object();
try
{
proc.Start();
@@ -57,13 +57,12 @@ namespace SourceGit.Commands
// Not safe, please only use `CancellationToken` in readonly commands.
if (CancellationToken.CanBeCanceled)
{
dummy = proc;
CancellationToken.Register(() =>
{
lock (dummyProcLock)
lock (capturedLock)
{
if (dummy is { HasExited: false })
dummy.Kill();
if (captured is { Process: { HasExited: false } })
captured.Process.Kill();
}
});
}
@@ -89,12 +88,9 @@ namespace SourceGit.Commands
HandleOutput(e.Message, errs);
}
if (dummy != null)
lock (capturedLock)
{
lock (dummyProcLock)
{
dummy = null;
}
captured.Process = null;
}
Log?.AppendLine(string.Empty);
@@ -116,7 +112,8 @@ namespace SourceGit.Commands
protected Result ReadToEnd()
{
using var proc = new Process() { StartInfo = CreateGitStartInfo(true) };
using var proc = new Process();
proc.StartInfo = CreateGitStartInfo(true);
try
{
@@ -138,7 +135,8 @@ namespace SourceGit.Commands
protected async Task<Result> ReadToEndAsync()
{
using var proc = new Process() { StartInfo = CreateGitStartInfo(true) };
using var proc = new Process();
proc.StartInfo = CreateGitStartInfo(true);
try
{
@@ -245,6 +243,11 @@ namespace SourceGit.Commands
errs.Add(line);
}
private class CapturedProcess
{
public Process Process { get; set; } = null;
}
[GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS();
}

View File

@@ -34,7 +34,7 @@ namespace SourceGit.Commands
}
}
private readonly string _tmpFile = string.Empty;
private readonly string _message = string.Empty;
private readonly string _tmpFile;
private readonly string _message;
}
}

View File

@@ -20,11 +20,7 @@ namespace SourceGit.Commands
public Diff(string repo, Models.DiffOption opt, int unified, bool ignoreWhitespace)
{
_result.TextDiff = new Models.TextDiff()
{
Repo = repo,
Option = opt,
};
_result.TextDiff = new Models.TextDiff() { Option = opt };
WorkingDirectory = repo;
Context = repo;

View File

@@ -94,8 +94,7 @@ namespace SourceGit.Commands
branch.IsLocal = true;
}
ulong committerDate = 0;
ulong.TryParse(parts[1], out committerDate);
ulong.TryParse(parts[1], out var committerDate);
branch.FullName = refName;
branch.CommitterDate = committerDate;

View File

@@ -21,7 +21,7 @@ namespace SourceGit.Commands
var stream = new MemoryStream();
try
{
using var proc = Process.Start(starter);
using var proc = Process.Start(starter)!;
await proc.StandardOutput.BaseStream.CopyToAsync(stream).ConfigureAwait(false);
await proc.WaitForExitAsync().ConfigureAwait(false);
}
@@ -49,7 +49,7 @@ namespace SourceGit.Commands
var stream = new MemoryStream();
try
{
using var proc = Process.Start(starter);
using var proc = Process.Start(starter)!;
await proc.StandardInput.WriteLineAsync("version https://git-lfs.github.com/spec/v1").ConfigureAwait(false);
await proc.StandardInput.WriteLineAsync($"oid sha256:{oid}").ConfigureAwait(false);
await proc.StandardInput.WriteLineAsync($"size {size}").ConfigureAwait(false);

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Threading.Tasks;
namespace SourceGit.Commands
{
@@ -16,12 +15,6 @@ namespace SourceGit.Commands
return Parse(ReadToEnd());
}
public async Task<string> GetResultAsync()
{
var rs = await ReadToEndAsync().ConfigureAwait(false);
return Parse(rs);
}
private string Parse(Result rs)
{
if (!rs.IsSuccess)

View File

@@ -34,8 +34,7 @@ namespace SourceGit.Commands
if (!string.IsNullOrEmpty(message) && message.Equals(name, StringComparison.Ordinal))
message = null;
ulong creactorDate = 0;
ulong.TryParse(subs[5], out creactorDate);
ulong.TryParse(subs[5], out var creatorDate);
tags.Add(new Models.Tag()
{
@@ -43,7 +42,7 @@ namespace SourceGit.Commands
IsAnnotated = subs[1].Equals("tag", StringComparison.Ordinal),
SHA = string.IsNullOrEmpty(subs[3]) ? subs[2] : subs[3],
Creator = Models.User.FindOrAdd(subs[4]),
CreatorDate = creactorDate,
CreatorDate = creatorDate,
Message = message,
});
}

View File

@@ -62,7 +62,7 @@ namespace SourceGit.Commands
try
{
using var proc = Process.Start(starter);
using var proc = Process.Start(starter)!;
await proc.StandardOutput.BaseStream.CopyToAsync(writer).ConfigureAwait(false);
await proc.WaitForExitAsync().ConfigureAwait(false);
return proc.ExitCode == 0;

View File

@@ -9,7 +9,7 @@ namespace SourceGit.Commands
{
public static async Task RunAsync(string repo, string revision, string file, string saveTo)
{
var dir = Path.GetDirectoryName(saveTo);
var dir = Path.GetDirectoryName(saveTo) ?? string.Empty;
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
@@ -42,7 +42,7 @@ namespace SourceGit.Commands
{
try
{
using var proc = Process.Start(starter);
using var proc = Process.Start(starter)!;
if (input != null)
{

View File

@@ -63,7 +63,7 @@ namespace SourceGit.Commands
try
{
using var proc = Process.Start(starter);
using var proc = Process.Start(starter)!;
await proc.StandardInput.WriteAsync(_patchBuilder.ToString());
proc.StandardInput.Close();
@@ -83,7 +83,7 @@ namespace SourceGit.Commands
}
}
private string _repo = "";
private StringBuilder _patchBuilder = new StringBuilder();
private readonly string _repo;
private readonly StringBuilder _patchBuilder = new();
}
}

View File

@@ -7,9 +7,6 @@ namespace SourceGit.Converters
{
public static class ListConverters
{
public static readonly FuncValueConverter<IList, string> Count =
new FuncValueConverter<IList, string>(v => v == null ? "0" : $"{v.Count}");
public static readonly FuncValueConverter<IList, string> ToCount =
new FuncValueConverter<IList, string>(v => v == null ? "(0)" : $"({v.Count})");

View File

@@ -190,9 +190,6 @@ namespace SourceGit.Models
image = Bitmap.DecodeToWidth(stream, 128);
}
if (image == null)
return;
_resources[email] = image;
lock (_synclock)

View File

@@ -6,12 +6,7 @@ using Avalonia.Media;
namespace SourceGit.Models
{
public record CommitGraphLayout(double startY, double clipWidth, double rowHeight)
{
public double StartY { get; set; } = startY;
public double ClipWidth { get; set; } = clipWidth;
public double RowHeight { get; set; } = rowHeight;
}
public record CommitGraphLayout(double StartY, double ClipWidth, double RowHeight);
public class CommitGraph
{

View File

@@ -5,8 +5,8 @@ namespace SourceGit.Models
{
public class CommitLink
{
public string Name { get; set; } = null;
public string URLPrefix { get; set; } = null;
public string Name { get; } = null;
public string URLPrefix { get; } = null;
public CommitLink(string name, string prefix)
{

View File

@@ -1,8 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Media.Imaging;
namespace SourceGit.Models
@@ -62,12 +60,9 @@ namespace SourceGit.Models
public partial class TextDiff
{
public string File { get; set; } = string.Empty;
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
public Vector ScrollOffset { get; set; } = Vector.Zero;
public int MaxLineNumber = 0;
public string Repo { get; set; } = null;
public DiffOption Option { get; set; } = null;
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
public int MaxLineNumber = 0;
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
{
@@ -148,7 +143,8 @@ namespace SourceGit.Models
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
var fileGuid = isTracked ? fileBlobGuid : "00000000";
using var writer = new StreamWriter(output) { NewLine = "\n" };
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
if (!revert && !isTracked)
writer.WriteLine("new file mode 100644");
@@ -192,7 +188,8 @@ namespace SourceGit.Models
{
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
using var writer = new StreamWriter(output) { NewLine = "\n" };
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
writer.WriteLine($"index 00000000...{fileTreeGuid} 100644");
writer.WriteLine($"--- a/{orgFile}");
@@ -305,7 +302,8 @@ namespace SourceGit.Models
{
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
using var writer = new StreamWriter(output) { NewLine = "\n" };
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
writer.WriteLine($"index 00000000...{fileTreeGuid} 100644");
writer.WriteLine($"--- a/{orgFile}");

View File

@@ -80,7 +80,7 @@ namespace SourceGit.Models
if (!_cancellationTokenSource.IsCancellationRequested)
{
var line = await reader.ReadToEndAsync(_cancellationTokenSource.Token);
MessageReceived?.Invoke(line?.Trim());
MessageReceived?.Invoke(line.Trim());
}
_server.Disconnect();

View File

@@ -6,6 +6,8 @@ namespace SourceGit.Models
{
public static int Compare(string s1, string s2)
{
var comparer = StringComparer.InvariantCultureIgnoreCase;
int len1 = s1.Length;
int len2 = s2.Length;
@@ -20,7 +22,7 @@ namespace SourceGit.Models
bool isDigit1 = char.IsDigit(c1);
bool isDigit2 = char.IsDigit(c2);
if (isDigit1 != isDigit2)
return c1.CompareTo(c2);
return comparer.Compare(c1.ToString(), c2.ToString());
int subLen1 = 1;
while (marker1 + subLen1 < len1 && char.IsDigit(s1[marker1 + subLen1]) == isDigit1)
@@ -40,7 +42,7 @@ namespace SourceGit.Models
if (isDigit1)
result = (subLen1 == subLen2) ? string.CompareOrdinal(sub1, sub2) : (subLen1 - subLen2);
else
result = string.Compare(sub1, sub2, StringComparison.OrdinalIgnoreCase);
result = comparer.Compare(sub1, sub2);
if (result != 0)
return result;

View File

@@ -285,7 +285,7 @@ namespace SourceGit.Models
switch (c)
{
case ESCAPE:
// allow to escape only }
// allow to escape only right-brace
if (Peek() == VARIABLE_END)
{
esc = true;

View File

@@ -82,19 +82,19 @@ namespace SourceGit.Models
}
}
public void MarkBranchDirtyManually()
public void MarkBranchUpdated()
{
_updateBranch = DateTime.Now.ToFileTime() - 1;
_updateBranch = 0;
}
public void MarkTagDirtyManually()
public void MarkTagUpdated()
{
_updateTags = DateTime.Now.ToFileTime() - 1;
_updateTags = 0;
}
public void MarkWorkingCopyDirtyManually()
public void MarkWorkingCopyUpdated()
{
_updateWC = DateTime.Now.ToFileTime() - 1;
_updateWC = 0;
}
public void Dispose()

View File

@@ -110,7 +110,7 @@ namespace SourceGit.Native
}
else
{
throw new Exception("Platform unsupported!!!");
throw new PlatformNotSupportedException();
}
}
@@ -124,7 +124,7 @@ namespace SourceGit.Native
if (OperatingSystem.IsWindows())
{
var execFile = Process.GetCurrentProcess().MainModule!.FileName;
var portableDir = Path.Combine(Path.GetDirectoryName(execFile), "data");
var portableDir = Path.Combine(Path.GetDirectoryName(execFile)!, "data");
if (Directory.Exists(portableDir))
{
DataDir = portableDir;
@@ -250,7 +250,7 @@ namespace SourceGit.Native
try
{
using var proc = Process.Start(start);
using var proc = Process.Start(start)!;
var rs = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
if (proc.ExitCode == 0 && !string.IsNullOrWhiteSpace(rs))

View File

@@ -17,6 +17,7 @@ namespace SourceGit.Native
[SupportedOSPlatform("windows")]
internal class Windows : OS.IBackend
{
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
public int left;
@@ -393,7 +394,7 @@ namespace SourceGit.Native
try
{
using var proc = Process.Start(startInfo);
using var proc = Process.Start(startInfo)!;
var output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();

View File

@@ -21,7 +21,6 @@
<StreamGeometry x:Key="Icons.Clone">M1280 704c0 141-115 256-256 256H288C129 960 0 831 0 672c0-126 80-232 192-272A327 327 0 01192 384c0-177 143-320 320-320 119 0 222 64 277 160C820 204 857 192 896 192c106 0 192 86 192 192 0 24-5 48-13 69C1192 477 1280 580 1280 704zm-493-128H656V352c0-18-14-32-32-32h-96c-18 0-32 14-32 32v224h-131c-29 0-43 34-23 55l211 211c12 12 33 12 45 0l211-211c20-20 6-55-23-55z</StreamGeometry>
<StreamGeometry x:Key="Icons.Close">M523 398 918 3l113 113-396 396 397 397-113 113-397-397-397 397-113-113 397-397L14 116l113-113 396 396z</StreamGeometry>
<StreamGeometry x:Key="Icons.Code">M853 102H171C133 102 102 133 102 171v683C102 891 133 922 171 922h683C891 922 922 891 922 853V171C922 133 891 102 853 102zM390 600l-48 48L205 512l137-137 48 48L301 512l88 88zM465 819l-66-18L559 205l66 18L465 819zm218-171L634 600 723 512l-88-88 48-48L819 512 683 649z</StreamGeometry>
<StreamGeometry x:Key="Icons.CodeBlock">M684 736 340 736l0-53 344 1-0 53zM552 565l-213-2 0-53 212 2-0 53zM684 392 340 392l0-53 344 1-0 53zM301 825c-45 0-78-9-100-27-22-18-33-43-33-75v-116c0-22-4-37-12-45-7-9-20-13-40-13v-61c19 0 32-4 40-12 8-9 12-24 12-46v-116c0-32 11-57 33-75 22-18 56-27 100-27h24v61h-24a35 35 0 00-27 12 41 41 0 00-11 29v116c0 35-10 60-31 75a66 66 0 01-31 14c11 2 22 6 31 14 20 17 31 42 31 75v116c0 12 4 22 11 29 7 8 16 12 27 12h24v61h-24zM701 764h24c10 0 19-4 27-12a41 41 0 0011-29v-116c0-33 10-58 31-75 9-7 19-12 31-14a66 66 0 01-31-14c-20-15-31-40-31-75v-116a41 41 0 00-11-29 35 35 0 00-27-12h-24v-61h24c45 0 78 9 100 27 22 18 33 43 33 75v116c0 22 4 37 11 46 8 8 21 12 40 12v61c-19 0-33 4-40 13-7 8-11 23-11 45v116c0 32-11 57-33 75-22 18-55 27-100 27h-24v-61z</StreamGeometry>
<StreamGeometry x:Key="Icons.ColorPicker">M128 854h768v86H128zM390 797c13 13 29 19 48 19s35-6 45-19l291-288c26-22 26-64 0-90L435 83l-61 61L426 192l-272 269c-22 22-22 64 0 90l237 246zm93-544 211 211-32 32H240l243-243zM707 694c0 48 38 86 86 86 48 0 86-38 86-86 0-22-10-45-26-61L794 576l-61 61c-13 13-26 35-26 58z</StreamGeometry>
<StreamGeometry x:Key="Icons.Commit">M0 512M1024 512M512 0M512 1024M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688</StreamGeometry>
<StreamGeometry x:Key="Icons.CommitMessageGenerator">M796 561a5 5 0 014 7l-39 90a5 5 0 004 7h100a5 5 0 014 8l-178 247a5 5 0 01-9-4l32-148a5 5 0 00-5-6h-89a5 5 0 01-4-7l86-191a5 5 0 014-3h88zM731 122a73 73 0 0173 73v318a54 54 0 00-8-1H731V195H244v634h408l-16 73H244a73 73 0 01-73-73V195a73 73 0 0173-73h488zm-219 366v73h-195v-73h195zm146-146v73H317v-73h341z</StreamGeometry>

View File

@@ -333,7 +333,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Seiten wechseln</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Hervorhebung</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Zeilenumbruch</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Aktiviere Block-Navigation</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Öffne in Merge Tool</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Alle Zeilen anzeigen</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Weniger Zeilen anzeigen</x:String>
@@ -541,7 +540,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">DARSTELLUNG</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Standardschriftart</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Editor Tab Breite</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" xml:space="preserve">Aktiviere kompakte Ordner im Änderungsbaum</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Schriftgröße</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Default" xml:space="preserve">Standard</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">Texteditor</x:String>
@@ -558,6 +556,7 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">ALLGEMEIN</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">Beim Starten nach Updates suchen</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">Datumsformat</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">Aktiviere kompakte Ordner im Änderungsbaum</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">Sprache</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">Commit-Historie</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">Zeige Autor Zeitpunkt anstatt Commit Zeitpunkt</x:String>

View File

@@ -328,7 +328,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Swap</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Highlighting</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Line Word Wrap</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Enable Block-Navigation</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Open in Merge Tool</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Show All Lines</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Decrease Number of Visible Lines</x:String>
@@ -536,7 +535,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APPEARANCE</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Editor Tab Width</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" xml:space="preserve">Enable compact folders in changes tree</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.Default" xml:space="preserve">Default</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">Editor</x:String>
@@ -553,9 +551,11 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">GENERAL</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">Check for updates on startup</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">Date Format</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">Enable compact folders in changes tree</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">Language</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">History Commits</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">Show author time instead of commit time in graph</x:String>
<x:String x:Key="Text.Preferences.General.ShowChangesPageByDefault" xml:space="preserve">Show `LOCAL CHANGES` page by default</x:String>
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">Show children in the commit details</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">Show tags in commit graph</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">Subject Guide Length</x:String>
@@ -839,6 +839,8 @@
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.SingleFile" xml:space="preserve">Ignore this file only</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Amend</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">You can stage this file now.</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories" xml:space="preserve">Clear History</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories.Confirm" xml:space="preserve">Are you sure you want to clear all commit message history? This action cannot be undone.</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Template/History</x:String>

View File

@@ -332,7 +332,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Intercambiar</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Resaltado de Sintaxis</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Ajuste de Línea</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Habilitar navegación en bloque</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Abrir en Herramienta de Merge</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Mostrar Todas las Líneas</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Disminuir Número de Líneas Visibles</x:String>
@@ -540,7 +539,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APARIENCIA</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Fuente por defecto</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Ancho de la Pestaña del Editor</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" xml:space="preserve">Habilitar carpetas compactas en el árbol de cambios</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.Default" xml:space="preserve">Por defecto</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">Editor</x:String>
@@ -557,6 +555,7 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">GENERAL</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">Buscar actualizaciones al iniciar</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">Formato de Fecha</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">Habilitar carpetas compactas en el árbol de cambios</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">Idioma</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">Commits en el historial</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">Mostrar hora del autor en lugar de la hora del commit en el gráfico</x:String>

View File

@@ -242,7 +242,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Permuter</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Coloration syntaxique</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Retour à la ligne</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Activer la navigation par blocs</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Ouvrir dans l'outil de fusion</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Voir toutes les lignes</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Réduit le nombre de ligne visibles</x:String>

View File

@@ -6,6 +6,9 @@
<x:String x:Key="Text.About" xml:space="preserve">Informazioni</x:String>
<x:String x:Key="Text.About.Menu" xml:space="preserve">Informazioni su SourceGit</x:String>
<x:String x:Key="Text.About.SubTitle" xml:space="preserve">Client GUI Git open source e gratuito</x:String>
<x:String x:Key="Text.AddToIgnore" xml:space="preserve">Aggiungi file a Ignora</x:String>
<x:String x:Key="Text.AddToIgnore.Pattern" xml:space="preserve">Pattern:</x:String>
<x:String x:Key="Text.AddToIgnore.Storage" xml:space="preserve">File di storage:</x:String>
<x:String x:Key="Text.AddWorktree" xml:space="preserve">Aggiungi Worktree</x:String>
<x:String x:Key="Text.AddWorktree.Location" xml:space="preserve">Posizione:</x:String>
<x:String x:Key="Text.AddWorktree.Location.Placeholder" xml:space="preserve">Percorso per questo worktree. Supportato il percorso relativo.</x:String>
@@ -20,6 +23,8 @@
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RIGENERA</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Usa AI per generare il messaggio di commit</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">APPLICA COME MESSAGGIO DI COMMIT</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Nascondi SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Mostra Tutto</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Applica</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">File Patch:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Seleziona file .patch da applicare</x:String>
@@ -36,8 +41,10 @@
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">Revisione:</x:String>
<x:String x:Key="Text.Archive.Title" xml:space="preserve">Archivia</x:String>
<x:String x:Key="Text.Askpass" xml:space="preserve">Richiedi Password SourceGit</x:String>
<x:String x:Key="Text.Askpass.Passphrase" xml:space="preserve">Inserisci passphrase:</x:String>
<x:String x:Key="Text.AssumeUnchanged" xml:space="preserve">FILE ASSUNTI COME INVARIATI</x:String>
<x:String x:Key="Text.AssumeUnchanged.Empty" xml:space="preserve">NESSUN FILE ASSUNTO COME INVARIATO</x:String>
<x:String x:Key="Text.Avatar.Load" xml:space="preserve">Carico l'Immagine...</x:String>
<x:String x:Key="Text.Avatar.Refetch" xml:space="preserve">Aggiorna</x:String>
<x:String x:Key="Text.BinaryNotSupported" xml:space="preserve">FILE BINARIO NON SUPPORTATO!!!</x:String>
<x:String x:Key="Text.Bisect" xml:space="preserve">Biseca</x:String>
@@ -66,8 +73,13 @@
<x:String x:Key="Text.BranchCM.Push" xml:space="preserve">Invia ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">Riallinea ${0}$ su ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Rinomina ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.ResetToSelectedCommit" xml:space="preserve">Resetta ${0}$ a ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Imposta Branch di Tracciamento...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">Confronto Branch</x:String>
<x:String x:Key="Text.BranchTree.InvalidUpstream" xml:space="preserve">Invalido</x:String>
<x:String x:Key="Text.BranchTree.Remote" xml:space="preserve">REMOTO</x:String>
<x:String x:Key="Text.BranchTree.Tracking" xml:space="preserve">TRACCIAMENTO</x:String>
<x:String x:Key="Text.BranchTree.URL" xml:space="preserve">URL</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">ANNULLA</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Ripristina la Revisione Padre</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Ripristina Questa Revisione</x:String>
@@ -76,6 +88,9 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Mostra come elenco di file e cartelle</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Mostra come elenco di percorsi</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Mostra come albero del filesystem</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">Cambia l'URL del Sottomodulo</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">Sottomodulo:</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">URL:</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
@@ -85,6 +100,9 @@
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">Stasha e Ripristina</x:String>
<x:String x:Key="Text.Checkout.RecurseSubmodules" xml:space="preserve">Aggiorna tutti i sottomoduli</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Checkout.WarnLostCommits" xml:space="preserve">Il tuo HEAD attuale contiene commit non connessi ad alcun branch/tag! Sicuro di voler continuare?</x:String>
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Checkout &amp; Avanzamento Veloce</x:String>
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">Avanzamento Veloce verso:</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry Pick</x:String>
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Aggiungi sorgente al messaggio di commit</x:String>
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit(s):</x:String>
@@ -109,17 +127,29 @@
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Confronta con HEAD</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Confronta con Worktree</x:String>
<x:String x:Key="Text.CommitCM.CopyAuthor" xml:space="preserve">Autore</x:String>
<x:String x:Key="Text.CommitCM.CopyCommitMessage" xml:space="preserve">Messaggio</x:String>
<x:String x:Key="Text.CommitCM.CopyCommitter" xml:space="preserve">Committer</x:String>
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitCM.CopySubject" xml:space="preserve">Oggetto</x:String>
<x:String x:Key="Text.CommitCM.CustomAction" xml:space="preserve">Azione Personalizzata</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Rebase Interattivo</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Drop" xml:space="preserve">Scarta...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Edit" xml:space="preserve">Modifica...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Fixup" xml:space="preserve">Correggi nel Genitore...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Manually" xml:space="preserve">Ribasare interattivamente ${0}$ su ${1}$</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Reword" xml:space="preserve">Riformula...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Squash" xml:space="preserve">Compatta nel Genitore...</x:String>
<x:String x:Key="Text.CommitCM.Merge" xml:space="preserve">Unisci a ${0}$</x:String>
<x:String x:Key="Text.CommitCM.MergeMultiple" xml:space="preserve">Unisci ...</x:String>
<x:String x:Key="Text.CommitCM.PushRevision" xml:space="preserve">Invia ${0}$ a ${1}$</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Ribasa ${0}$ su ${1}$</x:String>
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">Resetta ${0}$ su ${1}$</x:String>
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Annulla Commit</x:String>
<x:String x:Key="Text.CommitCM.Reword" xml:space="preserve">Modifica</x:String>
<x:String x:Key="Text.CommitCM.SaveAsPatch" xml:space="preserve">Salva come Patch...</x:String>
<x:String x:Key="Text.CommitCM.Squash" xml:space="preserve">Compatta nel Genitore</x:String>
<x:String x:Key="Text.CommitDetail.Changes" xml:space="preserve">MODIFICHE</x:String>
<x:String x:Key="Text.CommitDetail.Changes.Count" xml:space="preserve">file modificati</x:String>
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Cerca Modifiche...</x:String>
<x:String x:Key="Text.CommitDetail.Files" xml:space="preserve">FILE</x:String>
<x:String x:Key="Text.CommitDetail.Files.LFS" xml:space="preserve">File LFS</x:String>
@@ -132,26 +162,42 @@
<x:String x:Key="Text.CommitDetail.Info.ContainsIn" xml:space="preserve">Controlla i riferimenti che contengono questo commit</x:String>
<x:String x:Key="Text.CommitDetail.Info.ContainsIn.Title" xml:space="preserve">IL COMMIT È CONTENUTO DA</x:String>
<x:String x:Key="Text.CommitDetail.Info.GotoChangesPage" xml:space="preserve">Mostra solo le prime 100 modifiche. Vedi tutte le modifiche nella scheda MODIFICHE.</x:String>
<x:String x:Key="Text.CommitDetail.Info.Key" xml:space="preserve">Chiave:</x:String>
<x:String x:Key="Text.CommitDetail.Info.Message" xml:space="preserve">MESSAGGIO</x:String>
<x:String x:Key="Text.CommitDetail.Info.Parents" xml:space="preserve">GENITORI</x:String>
<x:String x:Key="Text.CommitDetail.Info.Refs" xml:space="preserve">RIFERIMENTI</x:String>
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitDetail.Info.Signer" xml:space="preserve">Firmatario:</x:String>
<x:String x:Key="Text.CommitDetail.Info.WebLinks" xml:space="preserve">Apri nel Browser</x:String>
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">Descrizione</x:String>
<x:String x:Key="Text.CommitMessageTextBox.SubjectCount" xml:space="preserve">OGGETTO</x:String>
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">Inserisci l'oggetto del commit</x:String>
<x:String x:Key="Text.Configure" xml:space="preserve">Configura Repository</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate" xml:space="preserve">TEMPLATE DI COMMIT</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.BuiltinVars" xml:space="preserve">Puoi usare ${files_num}, ${branch_name}, ${files} e ${files:N} dove N è il numero massimo di percorsi di file da mostrare.</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">Contenuto Template:</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.Name" xml:space="preserve">Nome Template:</x:String>
<x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">AZIONE PERSONALIZZATA</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">Argomenti:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">Parametri integrati:
${REPO} Percorso del repository
${REMOTE} Remoto selezionato o remoto del branch selezionato
${BRANCH} Branch selezionato, senza la parte ${REMOTE} per i branch remoti
${BRANCH_FRIENDLY_NAME} Nome amichevole del branch selezionato, contiene la parte ${REMOTE} per i branch remoti
${SHA} Hash del commit selezionato
${TAG} Tag selezionato
$1, $2 ... Valori dei controlli di input</x:String>
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">File Eseguibile:</x:String>
<x:String x:Key="Text.Configure.CustomAction.InputControls" xml:space="preserve">Controlli di Input:</x:String>
<x:String x:Key="Text.Configure.CustomAction.InputControls.Edit" xml:space="preserve">Modifica</x:String>
<x:String x:Key="Text.Configure.CustomAction.Name" xml:space="preserve">Nome:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Scope" xml:space="preserve">Ambito:</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.Remote" xml:space="preserve">Remoto</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.Tag" xml:space="preserve">Tag</x:String>
<x:String x:Key="Text.Configure.CustomAction.WaitForExit" xml:space="preserve">Attendi la fine dell'azione</x:String>
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Indirizzo Email</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Indirizzo email</x:String>
@@ -162,6 +208,7 @@
<x:String x:Key="Text.Configure.Git.PreferredMergeMode" xml:space="preserve">Modalità di Merge Preferita</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">TRACCIAMENTO ISSUE</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleAzure" xml:space="preserve">Aggiungi una regola di esempio per Azure DevOps</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit" xml:space="preserve">Aggiungi regola per Gerrit Change-Id Commit</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGiteeIssue" xml:space="preserve">Aggiungi una regola di esempio per un Issue Gitee</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGiteePullRequest" xml:space="preserve">Aggiungi una regola di esempio per un Pull Request Gitee</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGitHub" xml:space="preserve">Aggiungi una regola di esempio per GitHub</x:String>
@@ -171,6 +218,7 @@
<x:String x:Key="Text.Configure.IssueTracker.NewRule" xml:space="preserve">Nuova Regola</x:String>
<x:String x:Key="Text.Configure.IssueTracker.Regex" xml:space="preserve">Espressione Regex Issue:</x:String>
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Nome Regola:</x:String>
<x:String x:Key="Text.Configure.IssueTracker.Share" xml:space="preserve">Condividi questa regola nel file .issuetracker</x:String>
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">URL Risultato:</x:String>
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">Utilizza $1, $2 per accedere ai valori dei gruppi regex.</x:String>
<x:String x:Key="Text.Configure.OpenAI" xml:space="preserve">AI</x:String>
@@ -180,6 +228,16 @@
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">Proxy HTTP usato da questo repository</x:String>
<x:String x:Key="Text.Configure.User" xml:space="preserve">Nome Utente</x:String>
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">Nome utente per questo repository</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls" xml:space="preserve">Modifica Controlli Azione Personalizzata</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.CheckedValue" xml:space="preserve">Valore Selezionato:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.CheckedValue.Tip" xml:space="preserve">Quando selezionato, questo valore sarà usato negli argomenti della riga di comando</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Description" xml:space="preserve">Descrizione:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.DefaultValue" xml:space="preserve">Predefinito:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.IsFolder" xml:space="preserve">È una Cartella:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Label" xml:space="preserve">Etichetta:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options" xml:space="preserve">Opzioni:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options.Tip" xml:space="preserve">Usa '|' come delimitatore per le opzioni</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Type" xml:space="preserve">Tipo:</x:String>
<x:String x:Key="Text.ConfigureWorkspace" xml:space="preserve">Spazi di Lavoro</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Color" xml:space="preserve">Colore</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Name" xml:space="preserve">Nome</x:String>
@@ -188,6 +246,8 @@
<x:String x:Key="Text.ConfirmEmptyCommit.NoLocalChanges" xml:space="preserve">Trovato un commit vuoto! Vuoi procedere (--allow-empty)?</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.StageAllThenCommit" xml:space="preserve">STAGE DI TUTTO E COMMITTA</x:String>
<x:String x:Key="Text.ConfirmEmptyCommit.WithLocalChanges" xml:space="preserve">Trovato un commit vuoto! Vuoi procedere (--allow-empty) o fare lo stage di tutto e committare?</x:String>
<x:String x:Key="Text.ConfirmRestart.Title" xml:space="preserve">Riavvio Necessario</x:String>
<x:String x:Key="Text.ConfirmRestart.Message" xml:space="preserve">È necessario riavviare l'applicazione per applicare le modifiche.</x:String>
<x:String x:Key="Text.ConventionalCommit" xml:space="preserve">Guida Commit Convenzionali</x:String>
<x:String x:Key="Text.ConventionalCommit.BreakingChanges" xml:space="preserve">Modifica Sostanziale:</x:String>
<x:String x:Key="Text.ConventionalCommit.ClosedIssue" xml:space="preserve">Issue Chiusa:</x:String>
@@ -209,6 +269,7 @@
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Inserisci il nome del branch.</x:String>
<x:String x:Key="Text.CreateBranch.Name.WarnSpace" xml:space="preserve">Gli spazi verranno rimpiazzati con dei trattini.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Crea Branch Locale</x:String>
<x:String x:Key="Text.CreateBranch.OverwriteExisting" xml:space="preserve">Sovrascrivi branch esistente</x:String>
<x:String x:Key="Text.CreateTag" xml:space="preserve">Crea Tag...</x:String>
<x:String x:Key="Text.CreateTag.BasedOn" xml:space="preserve">Nuovo Tag Su:</x:String>
<x:String x:Key="Text.CreateTag.GPGSign" xml:space="preserve">Firma con GPG</x:String>
@@ -223,12 +284,18 @@
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">leggero</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Tieni premuto Ctrl per avviare direttamente</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Taglia</x:String>
<x:String x:Key="Text.DeinitSubmodule" xml:space="preserve">Deinizializza Sottomodulo</x:String>
<x:String x:Key="Text.DeinitSubmodule.Force" xml:space="preserve">Forza deinizializzazione anche se contiene modifiche locali.</x:String>
<x:String x:Key="Text.DeinitSubmodule.Path" xml:space="preserve">Sottomodulo:</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">Elimina Branch</x:String>
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">Stai per eliminare un branch remoto!!!</x:String>
<x:String x:Key="Text.DeleteBranch.WithTrackingRemote" xml:space="preserve">Elimina anche il branch remoto ${0}$</x:String>
<x:String x:Key="Text.DeleteMultiBranch" xml:space="preserve">Elimina Branch Multipli</x:String>
<x:String x:Key="Text.DeleteMultiBranch.Tip" xml:space="preserve">Stai per eliminare più branch contemporaneamente. Controlla attentamente prima di procedere!</x:String>
<x:String x:Key="Text.DeleteMultiTags" xml:space="preserve">Elimina Tag Multipli</x:String>
<x:String x:Key="Text.DeleteMultiTags.DeleteFromRemotes" xml:space="preserve">Eliminali dai remoti</x:String>
<x:String x:Key="Text.DeleteMultiTags.Tip" xml:space="preserve">Stai cercando di eliminare più tag contemporaneamente. Assicurati di controllare attentamente prima di procedere!</x:String>
<x:String x:Key="Text.DeleteRemote" xml:space="preserve">Elimina 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">Percorso:</x:String>
@@ -246,29 +313,36 @@
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">Modalità File Modificata</x:String>
<x:String x:Key="Text.Diff.First" xml:space="preserve">Prima differenza</x:String>
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Ignora Modifiche agli Spazi</x:String>
<x:String x:Key="Text.Diff.Image.Blend" xml:space="preserve">FUSIONE</x:String>
<x:String x:Key="Text.Diff.Image.SideBySide" xml:space="preserve">AFFIANCATI</x:String>
<x:String x:Key="Text.Diff.Image.Swipe" xml:space="preserve">SCORRIMENTO</x:String>
<x:String x:Key="Text.Diff.Last" xml:space="preserve">Ultima differenza</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">MODIFICA OGGETTO LFS</x:String>
<x:String x:Key="Text.Diff.New" xml:space="preserve">NUOVO</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Differenza Successiva</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NESSUNA MODIFICA O SOLO CAMBIAMENTI DI FINE LINEA</x:String>
<x:String x:Key="Text.Diff.Old" xml:space="preserve">VECCHIO</x:String>
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">Differenza Precedente</x:String>
<x:String x:Key="Text.Diff.SaveAsPatch" xml:space="preserve">Salva come Patch</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Mostra Simboli Nascosti</x:String>
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">Diff Affiancato</x:String>
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">SOTTOMODULO</x:String>
<x:String x:Key="Text.Diff.Submodule.Deleted" xml:space="preserve">ELIMINATO</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">NUOVO</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Scambia</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Evidenziazione Sintassi</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Avvolgimento delle Parole</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Abilita la navigazione a blocchi</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Apri nello Strumento di Merge</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Mostra Tutte le Righe</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Diminuisci Numero di Righe Visibili</x:String>
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">Aumenta Numero di Righe Visibili</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SELEZIONA UN FILE PER VISUALIZZARE LE MODIFICHE</x:String>
<x:String x:Key="Text.DirHistories" xml:space="preserve">Cronologia Cartella</x:String>
<x:String x:Key="Text.Discard" xml:space="preserve">Scarta Modifiche</x:String>
<x:String x:Key="Text.Discard.All" xml:space="preserve">Tutte le modifiche locali nella copia di lavoro.</x:String>
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Modifiche:</x:String>
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">Includi file ignorati</x:String>
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">Includi file non tracciati</x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">Un totale di {0} modifiche saranno scartate</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">Questa azione non può essere annullata!!!</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">Segnalibro:</x:String>
@@ -276,6 +350,8 @@
<x:String x:Key="Text.EditRepositoryNode.Target" xml:space="preserve">Destinazione:</x:String>
<x:String x:Key="Text.EditRepositoryNode.TitleForGroup" xml:space="preserve">Modifica Gruppo Selezionato</x:String>
<x:String x:Key="Text.EditRepositoryNode.TitleForRepository" xml:space="preserve">Modifica Repository Selezionato</x:String>
<x:String x:Key="Text.ExecuteCustomAction.Target" xml:space="preserve">Destinazione:</x:String>
<x:String x:Key="Text.ExecuteCustomAction.Repository" xml:space="preserve">Questo repository</x:String>
<x:String x:Key="Text.Fetch" xml:space="preserve">Recupera</x:String>
<x:String x:Key="Text.Fetch.AllRemotes" xml:space="preserve">Recupera da tutti i remoti</x:String>
<x:String x:Key="Text.Fetch.Force" xml:space="preserve">Forza la sovrascrittura dei riferimenti locali</x:String>
@@ -368,6 +444,8 @@
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">Vai alla pagina precedente</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Crea una nuova pagina</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">Apri la finestra delle preferenze</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchWorkspace" xml:space="preserve">Cambia workspace attivo</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">Cambia scheda attiva</x:String>
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">REPOSITORY</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Committa le modifiche in tsage</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Committa e invia le modifiche in stage</x:String>
@@ -403,12 +481,16 @@
<x:String x:Key="Text.InteractiveRebase" xml:space="preserve">Riallinea Interattivamente</x:String>
<x:String x:Key="Text.InteractiveRebase.AutoStash" xml:space="preserve">Stasha e Riapplica modifiche locali</x:String>
<x:String x:Key="Text.InteractiveRebase.On" xml:space="preserve">Su:</x:String>
<x:String x:Key="Text.InteractiveRebase.ReorderTip" xml:space="preserve">Trascina per riordinare i commit</x:String>
<x:String x:Key="Text.InteractiveRebase.Target" xml:space="preserve">Branch di destinazione:</x:String>
<x:String x:Key="Text.IssueLinkCM.CopyLink" xml:space="preserve">Copia il Link</x:String>
<x:String x:Key="Text.IssueLinkCM.OpenInBrowser" xml:space="preserve">Apri nel Browser</x:String>
<x:String x:Key="Text.Launcher.Error" xml:space="preserve">ERRORE</x:String>
<x:String x:Key="Text.Launcher.Info" xml:space="preserve">AVVISO</x:String>
<x:String x:Key="Text.Launcher.Workspaces" xml:space="preserve">Workspaces</x:String>
<x:String x:Key="Text.Launcher.Pages" xml:space="preserve">Schede</x:String>
<x:String x:Key="Text.Merge" xml:space="preserve">Unisci Branch</x:String>
<x:String x:Key="Text.Merge.Edit" xml:space="preserve">Personalizza messaggio di merge</x:String>
<x:String x:Key="Text.Merge.Into" xml:space="preserve">In:</x:String>
<x:String x:Key="Text.Merge.Mode" xml:space="preserve">Opzione di Unione:</x:String>
<x:String x:Key="Text.Merge.Source" xml:space="preserve">Sorgente:</x:String>
@@ -416,6 +498,9 @@
<x:String x:Key="Text.MergeMultiple.CommitChanges" xml:space="preserve">Commit di tutte le modifiche</x:String>
<x:String x:Key="Text.MergeMultiple.Strategy" xml:space="preserve">Strategia:</x:String>
<x:String x:Key="Text.MergeMultiple.Targets" xml:space="preserve">Obiettivi:</x:String>
<x:String x:Key="Text.MoveSubmodule" xml:space="preserve">Sposta Sottomodulo</x:String>
<x:String x:Key="Text.MoveSubmodule.MoveTo" xml:space="preserve">Sposta Verso:</x:String>
<x:String x:Key="Text.MoveSubmodule.Submodule" xml:space="preserve">Sottomodulo:</x:String>
<x:String x:Key="Text.MoveRepositoryNode" xml:space="preserve">Sposta Nodo Repository</x:String>
<x:String x:Key="Text.MoveRepositoryNode.Target" xml:space="preserve">Seleziona nodo padre per:</x:String>
<x:String x:Key="Text.Name" xml:space="preserve">Nome:</x:String>
@@ -486,6 +571,7 @@
<x:String x:Key="Text.Preferences.Git.Invalid" xml:space="preserve">Questa applicazione richiede Git (&gt;= 2.25.1)</x:String>
<x:String x:Key="Text.Preferences.Git.Path" xml:space="preserve">Percorso Installazione</x:String>
<x:String x:Key="Text.Preferences.Git.SSLVerify" xml:space="preserve">Abilita la verifica HTTP SSL</x:String>
<x:String x:Key="Text.Preferences.Git.UseLibsecret" xml:space="preserve">Usa git-credential-libsecret invece di git-credential-manager</x:String>
<x:String x:Key="Text.Preferences.Git.User" xml:space="preserve">Nome Utente</x:String>
<x:String x:Key="Text.Preferences.Git.User.Placeholder" xml:space="preserve">Nome utente Git globale</x:String>
<x:String x:Key="Text.Preferences.Git.Version" xml:space="preserve">Versione di Git</x:String>
@@ -511,6 +597,7 @@
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Modifiche Locali:</x:String>
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Scarta</x:String>
<x:String x:Key="Text.Pull.LocalChanges.StashAndReply" xml:space="preserve">Stasha e Riapplica</x:String>
<x:String x:Key="Text.Pull.RecurseSubmodules" xml:space="preserve">Aggiorna tutti i sottomoduli</x:String>
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Scarica (Recupera e Unisci)</x:String>
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Riallineare anziché unire</x:String>
@@ -518,10 +605,13 @@
<x:String x:Key="Text.Push.CheckSubmodules" xml:space="preserve">Assicurati che i sottomoduli siano stati inviati</x:String>
<x:String x:Key="Text.Push.Force" xml:space="preserve">Forza l'invio</x:String>
<x:String x:Key="Text.Push.Local" xml:space="preserve">Branch Locale:</x:String>
<x:String x:Key="Text.Push.New" xml:space="preserve">NUOVO</x:String>
<x:String x:Key="Text.Push.Remote" xml:space="preserve">Remoto:</x:String>
<x:String x:Key="Text.Push.Revision" xml:space="preserve">Revisione:</x:String>
<x:String x:Key="Text.Push.Revision.Title" xml:space="preserve">Invia Revisione Al Remoto</x:String>
<x:String x:Key="Text.Push.Title" xml:space="preserve">Invia modifiche al remoto</x:String>
<x:String x:Key="Text.Push.To" xml:space="preserve">Branch Remoto:</x:String>
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Imposta come branch di tracking</x:String>
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Imposta come branch di tracciamento</x:String>
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">Invia tutti i tag</x:String>
<x:String x:Key="Text.PushTag" xml:space="preserve">Invia Tag al Remoto</x:String>
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">Invia a tutti i remoti</x:String>
@@ -538,6 +628,7 @@
<x:String x:Key="Text.Remote.URL" xml:space="preserve">URL del Repository:</x:String>
<x:String x:Key="Text.Remote.URL.Placeholder" xml:space="preserve">URL del repository Git remoto</x:String>
<x:String x:Key="Text.RemoteCM.CopyURL" xml:space="preserve">Copia URL</x:String>
<x:String x:Key="Text.RemoteCM.CustomAction" xml:space="preserve">Azione Personalizzata</x:String>
<x:String x:Key="Text.RemoteCM.Delete" xml:space="preserve">Elimina...</x:String>
<x:String x:Key="Text.RemoteCM.Edit" xml:space="preserve">Modifica...</x:String>
<x:String x:Key="Text.RemoteCM.Fetch" xml:space="preserve">Recupera</x:String>
@@ -558,10 +649,12 @@
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">Pulizia (GC e Potatura)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Esegui il comando `git gc` per questo repository.</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Cancella tutto</x:String>
<x:String x:Key="Text.Repository.ClearStashes" xml:space="preserve">Cancella</x:String>
<x:String x:Key="Text.Repository.Configure" xml:space="preserve">Configura questo repository</x:String>
<x:String x:Key="Text.Repository.Continue" xml:space="preserve">CONTINUA</x:String>
<x:String x:Key="Text.Repository.CustomActions" xml:space="preserve">Azioni Personalizzate</x:String>
<x:String x:Key="Text.Repository.CustomActions.Empty" xml:space="preserve">Nessuna Azione Personalizzata</x:String>
<x:String x:Key="Text.Repository.Dashboard" xml:space="preserve">Dashboard</x:String>
<x:String x:Key="Text.Repository.DiscardAll" xml:space="preserve">Scarta tutte le modifiche</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Apri nell'Esplora File</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Cerca Branch/Tag/Sottomodulo</x:String>
@@ -576,9 +669,11 @@
<x:String x:Key="Text.Repository.HistoriesOrder.ByDate" xml:space="preserve">Per data del commit</x:String>
<x:String x:Key="Text.Repository.HistoriesOrder.Topo" xml:space="preserve">Topologicamente</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">BRANCH LOCALI</x:String>
<x:String x:Key="Text.Repository.MoreOptions" xml:space="preserve">Altre opzioni...</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Vai a HEAD</x:String>
<x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Crea Branch</x:String>
<x:String x:Key="Text.Repository.Notifications.Clear" xml:space="preserve">CANCELLA LE NOTIFICHE</x:String>
<x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInGraph" xml:space="preserve">Evidenzia solo il branch corrente</x:String>
<x:String x:Key="Text.Repository.OpenIn" xml:space="preserve">Apri in {0}</x:String>
<x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Apri in Strumenti Esterni</x:String>
<x:String x:Key="Text.Repository.Remotes" xml:space="preserve">REMOTI</x:String>
@@ -588,8 +683,13 @@
<x:String x:Key="Text.Repository.Search.ByCommitter" xml:space="preserve">Committer</x:String>
<x:String x:Key="Text.Repository.Search.ByContent" xml:space="preserve">Contenuto</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Messaggio</x:String>
<x:String x:Key="Text.Repository.Search.ByPath" xml:space="preserve">Percorso</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Branch Corrente</x:String>
<x:String x:Key="Text.Repository.ShowDecoratedCommitsOnly" xml:space="preserve">Solo commit decorati</x:String>
<x:String x:Key="Text.Repository.ShowFirstParentOnly" xml:space="preserve">Solo primo genitore</x:String>
<x:String x:Key="Text.Repository.ShowFlags" xml:space="preserve">MOSTRA FLAG</x:String>
<x:String x:Key="Text.Repository.ShowLostCommits" xml:space="preserve">Mostra commit persi</x:String>
<x:String x:Key="Text.Repository.ShowSubmodulesAsTree" xml:space="preserve">Mostra i Sottomoduli Come Albero</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Mostra Tag come Albero</x:String>
<x:String x:Key="Text.Repository.Skip" xml:space="preserve">SALTA</x:String>
@@ -603,6 +703,7 @@
<x:String x:Key="Text.Repository.Tags.OrderByName" xml:space="preserve">Per nome</x:String>
<x:String x:Key="Text.Repository.Tags.Sort" xml:space="preserve">Ordina</x:String>
<x:String x:Key="Text.Repository.Terminal" xml:space="preserve">Apri nel Terminale</x:String>
<x:String x:Key="Text.Repository.UseRelativeTimeInGraph" xml:space="preserve">Usa tempo relativo</x:String>
<x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">Visualizza i Log</x:String>
<x:String x:Key="Text.Repository.Visit" xml:space="preserve">Visita '{0}' nel Browser</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">WORKTREE</x:String>
@@ -613,6 +714,9 @@
<x:String x:Key="Text.Reset.Mode" xml:space="preserve">Modalità Reset:</x:String>
<x:String x:Key="Text.Reset.MoveTo" xml:space="preserve">Sposta a:</x:String>
<x:String x:Key="Text.Reset.Target" xml:space="preserve">Branch Corrente:</x:String>
<x:String x:Key="Text.ResetWithoutCheckout" xml:space="preserve">Resetta Branch (Senza Checkout)</x:String>
<x:String x:Key="Text.ResetWithoutCheckout.MoveTo" xml:space="preserve">Sposta Verso:</x:String>
<x:String x:Key="Text.ResetWithoutCheckout.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.RevealFile" xml:space="preserve">Mostra nell'Esplora File</x:String>
<x:String x:Key="Text.Revert" xml:space="preserve">Ripristina Commit</x:String>
<x:String x:Key="Text.Revert.Commit" xml:space="preserve">Commit:</x:String>
@@ -631,6 +735,11 @@
<x:String x:Key="Text.SelfUpdate.IgnoreThisVersion" xml:space="preserve">Salta questa versione</x:String>
<x:String x:Key="Text.SelfUpdate.Title" xml:space="preserve">Aggiornamento Software</x:String>
<x:String x:Key="Text.SelfUpdate.UpToDate" xml:space="preserve">Non ci sono aggiornamenti disponibili.</x:String>
<x:String x:Key="Text.SetSubmoduleBranch" xml:space="preserve">Imposta Branch del Sottomodulo</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Submodule" xml:space="preserve">Sottomodulo:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.Current" xml:space="preserve">Attuale:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New" xml:space="preserve">Cambia In:</x:String>
<x:String x:Key="Text.SetSubmoduleBranch.New.Tip" xml:space="preserve">Opzionale. Imposta al valore predefinito quando è vuoto.</x:String>
<x:String x:Key="Text.SetUpstream" xml:space="preserve">Imposta il Branch </x:String>
<x:String x:Key="Text.SetUpstream.Local" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.SetUpstream.Unset" xml:space="preserve">Rimuovi upstream</x:String>
@@ -646,10 +755,12 @@
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">Includi file non tracciati</x:String>
<x:String x:Key="Text.Stash.Message" xml:space="preserve">Messaggio:</x:String>
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">Opzionale. Informazioni di questo stash</x:String>
<x:String x:Key="Text.Stash.Mode" xml:space="preserve">Modalità:</x:String>
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">Solo modifiche in stage</x:String>
<x:String x:Key="Text.Stash.TipForSelectedFiles" xml:space="preserve">Sia le modifiche in stage che quelle non in stage dei file selezionati saranno stashate!!!</x:String>
<x:String x:Key="Text.Stash.Title" xml:space="preserve">Stasha Modifiche Locali</x:String>
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">Applica</x:String>
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">Copia Messaggio</x:String>
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">Elimina</x:String>
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">Salva come Patch...</x:String>
<x:String x:Key="Text.StashDropConfirm" xml:space="preserve">Elimina Stash</x:String>
@@ -665,20 +776,36 @@
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMIT:</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SOTTOMODULI</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">Aggiungi Sottomodulo</x:String>
<x:String x:Key="Text.Submodule.Branch" xml:space="preserve">BRANCH</x:String>
<x:String x:Key="Text.Submodule.CopyBranch" xml:space="preserve">Branch</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">Percorso Relativo</x:String>
<x:String x:Key="Text.Submodule.Deinit" xml:space="preserve">Deinizializza</x:String>
<x:String x:Key="Text.Submodule.FetchNested" xml:space="preserve">Recupera sottomoduli annidati</x:String>
<x:String x:Key="Text.Submodule.Histories" xml:space="preserve">Cronologia</x:String>
<x:String x:Key="Text.Submodule.Move" xml:space="preserve">Sposta</x:String>
<x:String x:Key="Text.Submodule.Open" xml:space="preserve">Apri Repository del Sottomodulo</x:String>
<x:String x:Key="Text.Submodule.RelativePath" xml:space="preserve">Percorso Relativo:</x:String>
<x:String x:Key="Text.Submodule.RelativePath.Placeholder" xml:space="preserve">Cartella relativa per memorizzare questo modulo.</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">Elimina Sottomodulo</x:String>
<x:String x:Key="Text.Submodule.SetBranch" xml:space="preserve">Imposta Branch</x:String>
<x:String x:Key="Text.Submodule.SetURL" xml:space="preserve">Cambia URL</x:String>
<x:String x:Key="Text.Submodule.Status" xml:space="preserve">STATO</x:String>
<x:String x:Key="Text.Submodule.Status.Modified" xml:space="preserve">modificato</x:String>
<x:String x:Key="Text.Submodule.Status.NotInited" xml:space="preserve">non inizializzato</x:String>
<x:String x:Key="Text.Submodule.Status.RevisionChanged" xml:space="preserve">revisione cambiata</x:String>
<x:String x:Key="Text.Submodule.Status.Unmerged" xml:space="preserve">non unito</x:String>
<x:String x:Key="Text.Submodule.Update" xml:space="preserve">Aggiorna</x:String>
<x:String x:Key="Text.Submodule.URL" xml:space="preserve">URL</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">OK</x:String>
<x:String x:Key="Text.Tag.Tagger" xml:space="preserve">AUTORE TAG</x:String>
<x:String x:Key="Text.Tag.Time" xml:space="preserve">DATA</x:String>
<x:String x:Key="Text.TagCM.Copy.Message" xml:space="preserve">Messaggio</x:String>
<x:String x:Key="Text.TagCM.Copy.Name" xml:space="preserve">Nome</x:String>
<x:String x:Key="Text.TagCM.Copy.Tagger" xml:space="preserve">Autore</x:String>
<x:String x:Key="Text.TagCM.CopyName" xml:space="preserve">Copia Nome Tag</x:String>
<x:String x:Key="Text.TagCM.CustomAction" xml:space="preserve">Azione Personalizzata</x:String>
<x:String x:Key="Text.TagCM.Delete" xml:space="preserve">Elimina ${0}$...</x:String>
<x:String x:Key="Text.TagCM.DeleteMultiple" xml:space="preserve">Elimina i {0} tag selezionati...</x:String>
<x:String x:Key="Text.TagCM.Merge" xml:space="preserve">Unisci ${0}$ in ${1}$...</x:String>
<x:String x:Key="Text.TagCM.Push" xml:space="preserve">Invia ${0}$...</x:String>
<x:String x:Key="Text.UpdateSubmodules" xml:space="preserve">Aggiorna Sottomoduli</x:String>
@@ -686,6 +813,7 @@
<x:String x:Key="Text.UpdateSubmodules.Init" xml:space="preserve">Inizializza se necessario</x:String>
<x:String x:Key="Text.UpdateSubmodules.Recursive" xml:space="preserve">Ricorsivamente</x:String>
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">Sottomodulo:</x:String>
<x:String x:Key="Text.UpdateSubmodules.UpdateToRemoteTrackingBranch" xml:space="preserve">Aggiorna al branch di tracciamento remoto del sottomodulo</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">URL:</x:String>
<x:String x:Key="Text.ViewLogs" xml:space="preserve">Log</x:String>
<x:String x:Key="Text.ViewLogs.Clear" xml:space="preserve">CANCELLA TUTTO</x:String>
@@ -709,6 +837,7 @@
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore" xml:space="preserve">Git Ignore</x:String>
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.Extension" xml:space="preserve">Ignora tutti i file *{0}</x:String>
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.ExtensionInSameFolder" xml:space="preserve">Ignora i file *{0} nella stessa cartella</x:String>
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.InFolder" xml:space="preserve">Ignora file non tracciati in questa cartella</x:String>
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.SingleFile" xml:space="preserve">Ignora solo questo file</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Modifica</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">Puoi aggiungere in stage questo file ora.</x:String>
@@ -718,6 +847,7 @@
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Attiva evento click</x:String>
<x:String x:Key="Text.WorkingCopy.CommitToEdit" xml:space="preserve">Commit (Modifica)</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Stage di tutte le modifiche e fai il commit</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithDetachedHead" xml:space="preserve">Stai creando un commit su un HEAD distaccato. Vuoi continuare?</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithFilter" xml:space="preserve">Hai stageato {0} file ma solo {1} file mostrati ({2} file sono stati filtrati). Vuoi procedere?</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLITTI RILEVATI</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.OpenExternalMergeTool" xml:space="preserve">APRI STRUMENTO DI MERGE ESTERNO</x:String>
@@ -728,6 +858,7 @@
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUDI FILE NON TRACCIATI</x:String>
<x:String x:Key="Text.WorkingCopy.NoCommitHistories" xml:space="preserve">NESSUN MESSAGGIO RECENTE INSERITO</x:String>
<x:String x:Key="Text.WorkingCopy.NoCommitTemplates" xml:space="preserve">NESSUN TEMPLATE DI COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.ResetAuthor" xml:space="preserve">Reimposta Autore</x:String>
<x:String x:Key="Text.WorkingCopy.SignOff" xml:space="preserve">SignOff</x:String>
<x:String x:Key="Text.WorkingCopy.Staged" xml:space="preserve">IN STAGE</x:String>
<x:String x:Key="Text.WorkingCopy.Staged.Unstage" xml:space="preserve">RIMUOVI DA STAGE</x:String>

View File

@@ -241,7 +241,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">スワップ</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">シンタックスハイライト</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">行の折り返し</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">ブロックナビゲーションを有効化</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">マージツールで開く</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">すべての行を表示</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">表示する行数を減らす</x:String>

View File

@@ -23,6 +23,8 @@
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">ПЕРЕСОЗДАТЬ</x:String>
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Использовать OpenAI для создания сообщения о ревизии</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">ПРИМЕНИТЬ КАК СООБЩЕНИЕ РЕВИЗИИ</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Скрыть SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Показать все</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Исправить </x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Файл заплатки:</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Выберите файл .patch для применения</x:String>
@@ -177,6 +179,15 @@
<x:String x:Key="Text.Configure.CommitMessageTemplate.Name" xml:space="preserve">Название:</x:String>
<x:String x:Key="Text.Configure.CustomAction" xml:space="preserve">ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments" xml:space="preserve">Аргументы:</x:String>
<x:String x:Key="Text.Configure.CustomAction.Arguments.Tip" xml:space="preserve">Built-in parameters:
${REPO} Путь репозитория
${REMOTE} Выбранная удаённая ветка
${BRANCH} Выбранная ветка, без ${REMOTE} удалённых веток
${BRANCH_FRIENDLY_NAME} Понятное имя выбранной ветки, содержащую ${REMOTE} удалённые ветки
${SHA} Хеш выбранной ревизии
${TAG} Выбранная метка
$1, $2 ... Ввод управляющих значений</x:String>
<x:String x:Key="Text.Configure.CustomAction.Executable" xml:space="preserve">Исполняемый файл:</x:String>
<x:String x:Key="Text.Configure.CustomAction.InputControls" xml:space="preserve">Элементы управления вводом:</x:String>
<x:String x:Key="Text.Configure.CustomAction.InputControls.Edit" xml:space="preserve">Редактор</x:String>
@@ -321,7 +332,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Обмен</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Подсветка синтаксиса </x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Перенос слов в строке</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Разрешить навигацию по блокам</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Открыть в инструменте слияния </x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Показывать все строки</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Меньше видимых строк</x:String>
@@ -529,7 +539,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">ВИД</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Шрифт по умолчанию</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Редактировать ширину вкладки</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" 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.Default" xml:space="preserve">По умолчанию</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">Редактор</x:String>
@@ -546,9 +555,11 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">ОСНОВНЫЕ</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">Проверить обновления при старте</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">Формат даты</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">Включить компактные каталоги в дереве изменений</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">Язык</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">Максимальная длина истории</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">Показывать время автора вместо времени ревизии на графике</x:String>
<x:String x:Key="Text.Preferences.General.ShowChangesPageByDefault" xml:space="preserve">Показывать вкладку «ЛОКАЛЬНЫЕ ИЗМЕНЕНИЯ» по умолчанию</x:String>
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">Показать наследника в деталях комментария</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">Показывать метки на графике</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">Длина темы ревизии</x:String>
@@ -832,6 +843,8 @@
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.SingleFile" xml:space="preserve">Игнорировать только эти файлы</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Изменить</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">Теперь вы можете сформировать этот файл.</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories" xml:space="preserve">Очистить историю</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories.Confirm" xml:space="preserve">Вы действительно хотите очистить всю историю сообщений ревизии? Данное действие нельзя отменить.</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">ЗАФИКСИРОВАТЬ</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">ЗАФИКСИРОВАТЬ и ОТПРАВИТЬ</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Шаблон/Истории</x:String>

View File

@@ -241,7 +241,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">இடமாற்று</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">தொடரியல் சிறப்பம்சமாக்கல்</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">வரி சொல் மடக்கு</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">தடுப்பு-வழிசெலுத்தலை இயக்கு</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">ஒன்றிணை கருவியில் திற</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">அனைத்து வரிகளையும் காட்டு</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">தெரியும் வரிகளின் எண்ணிக்கையைக் குறை</x:String>

View File

@@ -246,7 +246,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Поміняти місцями</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Підсвітка синтаксису</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Перенос слів</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Увімкнути навігацію блоками</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Відкрити в інструменті злиття</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Показати всі рядки</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Зменшити кількість видимих рядків</x:String>

View File

@@ -332,7 +332,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交换比对双方</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">语法高亮</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自动换行</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">启用基于变更块的跳转</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合并工具查看</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">显示完整文件</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">减少可见的行数</x:String>
@@ -540,7 +539,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">外观配置</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">编辑器制表符宽度</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" 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.Default" xml:space="preserve">默认</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">代码编辑器</x:String>
@@ -557,9 +555,11 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">通用配置</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">启动时检测软件更新</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">日期时间格式</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">在变更列表树中启用紧凑文件夹模式</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">显示语言</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">最大历史提交数</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">在提交路线图中显示修改时间而非提交时间</x:String>
<x:String x:Key="Text.Preferences.General.ShowChangesPageByDefault" xml:space="preserve">默认显示【本地更改】页</x:String>
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">在提交详情页中显示子提交列表</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">在提交路线图中显示标签</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">SUBJECT字数检测</x:String>
@@ -843,6 +843,8 @@
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.SingleFile" xml:space="preserve">忽略本文件</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">修补</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">现在您已可将其加入暂存区中</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories" xml:space="preserve">清空历史提交信息</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories.Confirm" xml:space="preserve">您确定要清空所有的历史提交信息记录吗(执行操作后无法撤回)?</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交并推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">历史输入/模板</x:String>

View File

@@ -332,7 +332,6 @@
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交換比對雙方</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">語法上色</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自動換行</x:String>
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">區塊切換上/下一個差異</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合併工具檢視</x:String>
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">顯示檔案的全部內容</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">減少可見的行數</x:String>
@@ -540,7 +539,6 @@
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">外觀設定</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">預設字型</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">編輯器 Tab 寬度</x:String>
<x:String x:Key="Text.Preferences.Appearance.EnableCompactFolders" 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.Default" xml:space="preserve">預設</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize.Editor" xml:space="preserve">程式碼</x:String>
@@ -557,9 +555,11 @@
<x:String x:Key="Text.Preferences.General" xml:space="preserve">一般設定</x:String>
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">啟動時檢查軟體更新</x:String>
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">日期時間格式</x:String>
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">在變更樹中啟用精簡文件夾顯示模式</x:String>
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">顯示語言</x:String>
<x:String x:Key="Text.Preferences.General.MaxHistoryCommits" xml:space="preserve">最大歷史提交數</x:String>
<x:String x:Key="Text.Preferences.General.ShowAuthorTime" xml:space="preserve">在提交路線圖中顯示修改時間而非提交時間</x:String>
<x:String x:Key="Text.Preferences.General.ShowChangesPageByDefault" xml:space="preserve">預設顯示「本機變更」頁面</x:String>
<x:String x:Key="Text.Preferences.General.ShowChildren" xml:space="preserve">在提交詳細資訊中顯示後續提交</x:String>
<x:String x:Key="Text.Preferences.General.ShowTagsInGraph" xml:space="preserve">在路線圖中顯示標籤</x:String>
<x:String x:Key="Text.Preferences.General.SubjectGuideLength" xml:space="preserve">提交標題字數偵測</x:String>
@@ -843,6 +843,8 @@
<x:String x:Key="Text.WorkingCopy.AddToGitIgnore.SingleFile" xml:space="preserve">忽略本檔案</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">修補</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">現在您已可將其加入暫存區中</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories" xml:space="preserve">清除提交訊息歷史</x:String>
<x:String x:Key="Text.WorkingCopy.ClearCommitHistories.Confirm" xml:space="preserve">您確定要清除所有提交訊息記錄嗎 (動作無法撤銷) ?</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提 交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交並推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">歷史輸入/範本</x:String>

View File

@@ -740,6 +740,7 @@
</Style>
<Style Selector="ContextMenu">
<Setter Property="MaxWidth" Value="1024"/>
<Setter Property="HorizontalOffset" Value="-4"/>
<Setter Property="VerticalOffset" Value="-4"/>
<Setter Property="Template">

View File

@@ -47,7 +47,7 @@
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.4" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.3.0" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.5" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.3.0-beta.1" />
<PackageReference Include="BitMiracle.LibTiff.NET" Version="2.4.660" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc5.4" />

View File

@@ -35,7 +35,7 @@ namespace SourceGit.ViewModels
var file = StorageFile.GetFullPath(_repo.FullPath, _repo.GitDir);
if (!File.Exists(file))
{
await File.WriteAllLinesAsync(file, [_pattern]);
await File.WriteAllLinesAsync(file!, [_pattern]);
}
else
{

View File

@@ -1,63 +1,36 @@
using System.Collections.Generic;
using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class BlockNavigation : ObservableObject
{
public class Block
public record Block(int Start, int End)
{
public int Start { get; set; } = 0;
public int End { get; set; } = 0;
public Block(int start, int end)
{
Start = start;
End = end;
}
public bool IsInRange(int line)
{
return line >= Start && line <= End;
}
}
public AvaloniaList<Block> Blocks
{
get;
} = [];
public int Current
{
get => _current;
private set => SetProperty(ref _current, value);
}
public string Indicator
{
get
{
if (Blocks.Count == 0)
if (_blocks.Count == 0)
return "-/-";
if (_current >= 0 && _current < Blocks.Count)
return $"{_current + 1}/{Blocks.Count}";
if (_current >= 0 && _current < _blocks.Count)
return $"{_current + 1}/{_blocks.Count}";
return $"-/{Blocks.Count}";
return $"-/{_blocks.Count}";
}
}
public BlockNavigation(object context)
public BlockNavigation(List<Models.TextDiffLine> lines)
{
Blocks.Clear();
Current = -1;
var lines = new List<Models.TextDiffLine>();
if (context is Models.TextDiff combined)
lines = combined.Lines;
else if (context is TwoSideTextDiff twoSide)
lines = twoSide.Old;
_blocks.Clear();
_current = -1;
if (lines.Count == 0)
return;
@@ -91,54 +64,89 @@ namespace SourceGit.ViewModels
if (!isNewBlock)
blocks.Add(new Block(blockStartIdx, lines.Count - 1));
Blocks.AddRange(blocks);
_blocks.AddRange(blocks);
}
public Block GetCurrentBlock()
{
return (_current >= 0 && _current < Blocks.Count) ? Blocks[_current] : null;
if (_current >= 0 && _current < _blocks.Count)
return _blocks[_current];
return null;
}
public Block GotoFirst()
{
if (Blocks.Count == 0)
if (_blocks.Count == 0)
return null;
Current = 0;
return Blocks[_current];
_current = 0;
OnPropertyChanged(nameof(Indicator));
return _blocks[_current];
}
public Block GotoPrev()
{
if (Blocks.Count == 0)
if (_blocks.Count == 0)
return null;
if (_current == -1)
Current = 0;
_current = 0;
else if (_current > 0)
Current = _current - 1;
return Blocks[_current];
_current--;
OnPropertyChanged(nameof(Indicator));
return _blocks[_current];
}
public Block GotoNext()
{
if (Blocks.Count == 0)
if (_blocks.Count == 0)
return null;
if (_current < Blocks.Count - 1)
Current = _current + 1;
return Blocks[_current];
if (_current < _blocks.Count - 1)
_current++;
OnPropertyChanged(nameof(Indicator));
return _blocks[_current];
}
public Block GotoLast()
{
if (Blocks.Count == 0)
if (_blocks.Count == 0)
return null;
Current = Blocks.Count - 1;
return Blocks[_current];
_current = _blocks.Count - 1;
OnPropertyChanged(nameof(Indicator));
return _blocks[_current];
}
private int _current = -1;
public void UpdateByCaretPosition(int caretLine)
{
if (_current >= 0 && _current < _blocks.Count)
{
var block = _blocks[_current];
if (block.IsInRange(caretLine))
return;
}
_current = -1;
for (var i = 0; i < _blocks.Count; i++)
{
var block = _blocks[i];
if (block.Start > caretLine)
break;
_current = i;
if (block.End >= caretLine)
break;
}
OnPropertyChanged(nameof(Indicator));
}
private int _current;
private readonly List<Block> _blocks = [];
}
}

View File

@@ -252,8 +252,8 @@ namespace SourceGit.ViewModels
SortNodesByTime(node.Children);
}
private readonly Models.BranchSortMode _localSortMode = Models.BranchSortMode.Name;
private readonly Models.BranchSortMode _remoteSortMode = Models.BranchSortMode.Name;
private readonly Models.BranchSortMode _localSortMode;
private readonly Models.BranchSortMode _remoteSortMode;
private readonly HashSet<string> _expanded = new HashSet<string>();
}
}

View File

@@ -1,9 +1,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@@ -24,7 +22,42 @@ namespace SourceGit.ViewModels
{
Preferences.Instance.IgnoreWhitespaceChangesInDiff = value;
OnPropertyChanged();
LoadDiffContent();
LoadContent();
}
}
}
public bool ShowEntireFile
{
get => Preferences.Instance.UseFullTextDiff;
set
{
if (value != Preferences.Instance.UseFullTextDiff)
{
Preferences.Instance.UseFullTextDiff = value;
OnPropertyChanged();
if (Content is TextDiffContext ctx)
{
ctx.Data.File = string.Empty; // Just to ignore both previous `ScrollOffset` and `BlockNavigation`
LoadContent();
}
}
}
}
public bool UseSideBySide
{
get => Preferences.Instance.UseSideBySideDiff;
set
{
if (value != Preferences.Instance.UseSideBySideDiff)
{
Preferences.Instance.UseSideBySideDiff = value;
OnPropertyChanged();
if (Content is TextDiffContext ctx && ctx.IsSideBySide() != value)
Content = ctx.SwitchMode();
}
}
}
@@ -72,25 +105,19 @@ namespace SourceGit.ViewModels
else
Title = $"{_option.OrgPath} → {_option.Path}";
LoadDiffContent();
}
public void ToggleFullTextDiff()
{
Preferences.Instance.UseFullTextDiff = !Preferences.Instance.UseFullTextDiff;
LoadDiffContent();
LoadContent();
}
public void IncrUnified()
{
UnifiedLines = _unifiedLines + 1;
LoadDiffContent();
LoadContent();
}
public void DecrUnified()
{
UnifiedLines = Math.Max(4, _unifiedLines - 1);
LoadDiffContent();
LoadContent();
}
public void OpenExternalMergeTool()
@@ -98,7 +125,27 @@ namespace SourceGit.ViewModels
new Commands.DiffTool(_repo, _option).Open();
}
private void LoadDiffContent()
public void CheckSettings()
{
if (Content is TextDiffContext ctx)
{
if ((ShowEntireFile && _info.UnifiedLines != _entireFileLine) ||
(!ShowEntireFile && _info.UnifiedLines == _entireFileLine) ||
(IgnoreWhitespace != _info.IgnoreWhitespace))
{
LoadContent();
return;
}
if (ctx.IsSideBySide() != UseSideBySide)
{
ctx = ctx.SwitchMode();
Content = ctx;
}
}
}
private void LoadContent()
{
if (_option.Path.EndsWith('/'))
{
@@ -109,7 +156,7 @@ namespace SourceGit.ViewModels
Task.Run(async () =>
{
var numLines = Preferences.Instance.UseFullTextDiff ? 999999999 : _unifiedLines;
var numLines = Preferences.Instance.UseFullTextDiff ? _entireFileLine : _unifiedLines;
var ignoreWhitespace = Preferences.Instance.IgnoreWhitespaceChangesInDiff;
var latest = await new Commands.Diff(_repo, _option, numLines, ignoreWhitespace)
@@ -228,12 +275,22 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Post(() =>
{
if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File)
cur.ScrollOffset = old.ScrollOffset;
FileModeChange = latest.FileModeChange;
Content = rs;
IsTextDiff = rs is Models.TextDiff;
if (rs is Models.TextDiff cur)
{
IsTextDiff = true;
if (Preferences.Instance.UseSideBySideDiff)
Content = new TwoSideTextDiff(cur, _content as TwoSideTextDiff);
else
Content = new CombinedTextDiff(cur, _content as CombinedTextDiff);
}
else
{
IsTextDiff = false;
Content = rs;
}
});
});
}
@@ -279,6 +336,7 @@ namespace SourceGit.ViewModels
}
}
private readonly int _entireFileLine = 999999999;
private readonly string _repo;
private readonly Models.DiffOption _option = null;
private string _fileModeChange = string.Empty;

View File

@@ -38,7 +38,6 @@ namespace SourceGit.ViewModels
public CommitDetail Detail
{
get => _detail;
private set => SetProperty(ref _detail, value);
}
public DirHistories(Repository repo, string dir, string revision = null)

View File

@@ -15,9 +15,9 @@ namespace SourceGit.ViewModels
public class CustomActionControlTextBox : ICustomActionControlParameter
{
public string Label { get; set; } = string.Empty;
public string Placeholder { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
public string Label { get; set; }
public string Placeholder { get; set; }
public string Text { get; set; }
public CustomActionControlTextBox(string label, string placeholder, string defaultValue)
{
@@ -31,9 +31,9 @@ namespace SourceGit.ViewModels
public class CustomActionControlPathSelector : ObservableObject, ICustomActionControlParameter
{
public string Label { get; set; } = string.Empty;
public string Placeholder { get; set; } = string.Empty;
public bool IsFolder { get; set; } = false;
public string Label { get; set; }
public string Placeholder { get; set; }
public bool IsFolder { get; set; }
public string Path
{
@@ -51,14 +51,14 @@ namespace SourceGit.ViewModels
public string GetValue() => _path;
private string _path = string.Empty;
private string _path;
}
public class CustomActionControlCheckBox : ICustomActionControlParameter
{
public string Label { get; set; } = string.Empty;
public string ToolTip { get; set; } = string.Empty;
public string CheckedValue { get; set; } = string.Empty;
public string Label { get; set; }
public string ToolTip { get; set; }
public string CheckedValue { get; set; }
public bool IsChecked { get; set; }
public CustomActionControlCheckBox(string label, string tooltip, string checkedValue, bool isChecked)
@@ -74,8 +74,8 @@ namespace SourceGit.ViewModels
public class CustomActionControlComboBox : ObservableObject, ICustomActionControlParameter
{
public string Label { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Label { get; set; }
public string Description { get; set; }
public List<string> Options { get; set; } = [];
public string Value
@@ -227,8 +227,8 @@ namespace SourceGit.ViewModels
start.StandardErrorEncoding = Encoding.UTF8;
start.WorkingDirectory = _repo.FullPath;
using var proc = new Process() { StartInfo = start };
var builder = new StringBuilder();
using var proc = new Process();
proc.StartInfo = start;
proc.OutputDataReceived += (_, e) =>
{
@@ -236,6 +236,7 @@ namespace SourceGit.ViewModels
log?.AppendLine(e.Data);
};
var builder = new StringBuilder();
proc.ErrorDataReceived += (_, e) =>
{
if (e.Data != null)

View File

@@ -13,8 +13,7 @@ namespace SourceGit.ViewModels
public bool IsFetchAllRemoteVisible
{
get;
private set;
} = true;
}
public bool FetchAllRemotes
{

View File

@@ -11,18 +11,13 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public record InteractiveRebasePrefill(string sha, Models.InteractiveRebaseAction action)
{
public string SHA { get; } = sha;
public Models.InteractiveRebaseAction Action { get; } = action;
}
public record InteractiveRebasePrefill(string SHA, Models.InteractiveRebaseAction Action);
public class InteractiveRebaseItem : ObservableObject
{
public Models.Commit Commit
{
get;
private set;
}
public bool CanSquashOrFixup
@@ -88,7 +83,6 @@ namespace SourceGit.ViewModels
public Models.Commit On
{
get;
private set;
}
public bool AutoStash
@@ -111,7 +105,6 @@ namespace SourceGit.ViewModels
public AvaloniaList<InteractiveRebaseItem> Items
{
get;
private set;
} = [];
public InteractiveRebaseItem SelectedItem
@@ -127,7 +120,6 @@ namespace SourceGit.ViewModels
public CommitDetail DetailContext
{
get;
private set;
}
public InteractiveRebase(Repository repo, Models.Commit on, InteractiveRebasePrefill prefill = null)
@@ -151,7 +143,7 @@ namespace SourceGit.ViewModels
list.Add(new InteractiveRebaseItem(c.Commit, c.Message, i < commits.Count - 1));
}
InteractiveRebaseItem selected = list.Count > 0 ? list[0] : null;
var selected = list.Count > 0 ? list[0] : null;
if (prefill != null)
{
var item = list.Find(x => x.Commit.SHA.Equals(prefill.SHA, StringComparison.Ordinal));

View File

@@ -119,6 +119,12 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _layout, value);
}
public bool ShowLocalChangesByDefault
{
get;
set;
} = false;
public int MaxHistoryCommits
{
get => _maxHistoryCommits;
@@ -255,12 +261,6 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _useFullTextDiff, value);
}
public bool UseBlockNavigationInDiffView
{
get => _useBlockNavigationInDiffView;
set => SetProperty(ref _useBlockNavigationInDiffView, value);
}
public int LFSImageActiveIdx
{
get => _lfsImageActiveIdx;
@@ -701,7 +701,7 @@ namespace SourceGit.ViewModels
private double _defaultFontSize = 13;
private double _editorFontSize = 13;
private int _editorTabWidth = 4;
private LayoutInfo _layout = new LayoutInfo();
private LayoutInfo _layout = new();
private int _maxHistoryCommits = 20000;
private int _subjectGuideLength = 50;
@@ -722,7 +722,6 @@ namespace SourceGit.ViewModels
private bool _enableDiffViewWordWrap = false;
private bool _showHiddenSymbolsInDiffView = false;
private bool _useFullTextDiff = false;
private bool _useBlockNavigationInDiffView = false;
private int _lfsImageActiveIdx = 0;
private bool _enableCompactFoldersInChangesTree = false;

View File

@@ -22,25 +22,12 @@ namespace SourceGit.ViewModels
public string FullPath
{
get => _fullpath;
set
{
if (value != null)
{
var normalized = value.Replace('\\', '/').TrimEnd('/');
SetProperty(ref _fullpath, normalized);
}
else
{
SetProperty(ref _fullpath, null);
}
}
get;
}
public string GitDir
{
get => _gitDir;
set => SetProperty(ref _gitDir, value);
get;
}
public Models.RepositorySettings Settings
@@ -52,7 +39,7 @@ namespace SourceGit.ViewModels
{
get;
set;
} = new Models.GitFlow();
} = new();
public Models.FilterMode HistoriesFilterMode
{
@@ -104,7 +91,7 @@ namespace SourceGit.ViewModels
public Models.HistoryShowFlags HistoryShowFlags
{
get => _settings.HistoryShowFlags;
set
private set
{
if (value != _settings.HistoryShowFlags)
{
@@ -494,36 +481,34 @@ namespace SourceGit.ViewModels
public AvaloniaList<Models.IssueTracker> IssueTrackers
{
get;
private set;
} = new AvaloniaList<Models.IssueTracker>();
} = [];
public AvaloniaList<CommandLog> Logs
{
get;
private set;
} = new AvaloniaList<CommandLog>();
} = [];
public Repository(bool isBare, string path, string gitDir)
{
IsBare = isBare;
FullPath = path;
GitDir = gitDir;
FullPath = path.Replace('\\', '/').TrimEnd('/');
GitDir = gitDir.Replace('\\', '/').TrimEnd('/');
var commonDirFile = Path.Combine(_gitDir, "commondir");
_isWorktree = _gitDir.Replace('\\', '/').IndexOf("/worktrees/", StringComparison.Ordinal) > 0 &&
File.Exists(commonDirFile);
var commonDirFile = Path.Combine(GitDir, "commondir");
_isWorktree = GitDir.IndexOf("/worktrees/", StringComparison.Ordinal) > 0 &&
File.Exists(commonDirFile);
if (_isWorktree)
{
var commonDir = File.ReadAllText(commonDirFile).Trim();
if (!Path.IsPathRooted(commonDir))
commonDir = new DirectoryInfo(Path.Combine(_gitDir, commonDir)).FullName;
commonDir = new DirectoryInfo(Path.Combine(GitDir, commonDir)).FullName;
_gitCommonDir = commonDir;
}
else
{
_gitCommonDir = _gitDir;
_gitCommonDir = GitDir;
}
}
@@ -549,11 +534,11 @@ namespace SourceGit.ViewModels
try
{
_watcher = new Models.Watcher(this, _fullpath, _gitCommonDir);
_watcher = new Models.Watcher(this, FullPath, _gitCommonDir);
}
catch (Exception ex)
{
App.RaiseException(string.Empty, $"Failed to start watcher for repository: '{_fullpath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}");
App.RaiseException(string.Empty, $"Failed to start watcher for repository: '{FullPath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}");
}
if (_settings.HistoriesFilters.Count > 0)
@@ -562,14 +547,22 @@ namespace SourceGit.ViewModels
_historiesFilterMode = Models.FilterMode.None;
_histories = new Histories(this);
_workingCopy = new WorkingCopy(this);
_workingCopy = new WorkingCopy(this) { CommitMessage = _settings.LastCommitMessage };
_stashesPage = new StashesPage(this);
_selectedView = _histories;
_selectedViewIndex = 0;
_workingCopy.CommitMessage = _settings.LastCommitMessage;
if (Preferences.Instance.ShowLocalChangesByDefault)
{
_selectedView = _workingCopy;
_selectedViewIndex = 1;
}
else
{
_selectedView = _histories;
_selectedViewIndex = 0;
}
_lastFetchTime = DateTime.Now;
_autoFetchTimer = new Timer(AutoFetchInBackground, null, 5000, 5000);
_autoFetchTimer = new Timer(FetchInBackground, null, 5000, 5000);
RefreshAll();
}
@@ -585,6 +578,17 @@ namespace SourceGit.ViewModels
JsonSerializer.Serialize(stream, _settings, JsonCodeGen.Default.RepositorySettings);
}
if (_cancellationRefreshBranches is { IsCancellationRequested: false })
_cancellationRefreshBranches.Cancel();
if (_cancellationRefreshTags is { IsCancellationRequested: false })
_cancellationRefreshTags.Cancel();
if (_cancellationRefreshWorkingCopyChanges is { IsCancellationRequested: false })
_cancellationRefreshWorkingCopyChanges.Cancel();
if (_cancellationRefreshCommits is { IsCancellationRequested: false })
_cancellationRefreshCommits.Cancel();
if (_cancellationRefreshStashes is { IsCancellationRequested: false })
_cancellationRefreshStashes.Cancel();
_autoFetchTimer.Dispose();
_autoFetchTimer = null;
@@ -669,7 +673,7 @@ namespace SourceGit.ViewModels
public bool IsLFSEnabled()
{
var path = Path.Combine(_fullpath, ".git", "hooks", "pre-push");
var path = Path.Combine(FullPath, ".git", "hooks", "pre-push");
if (!File.Exists(path))
return false;
@@ -680,9 +684,9 @@ namespace SourceGit.ViewModels
public async Task InstallLFSAsync()
{
var log = CreateLog("Install LFS");
var succ = await new Commands.LFS(_fullpath).Use(log).InstallAsync();
var succ = await new Commands.LFS(FullPath).Use(log).InstallAsync();
if (succ)
App.SendNotification(_fullpath, "LFS enabled successfully!");
App.SendNotification(FullPath, "LFS enabled successfully!");
log.Complete();
}
@@ -690,12 +694,12 @@ namespace SourceGit.ViewModels
public async Task<bool> TrackLFSFileAsync(string pattern, bool isFilenameMode)
{
var log = CreateLog("Track LFS");
var succ = await new Commands.LFS(_fullpath)
var succ = await new Commands.LFS(FullPath)
.Use(log)
.TrackAsync(pattern, isFilenameMode);
if (succ)
App.SendNotification(_fullpath, $"Tracking successfully! Pattern: {pattern}");
App.SendNotification(FullPath, $"Tracking successfully! Pattern: {pattern}");
log.Complete();
return succ;
@@ -704,12 +708,12 @@ namespace SourceGit.ViewModels
public async Task<bool> LockLFSFileAsync(string remote, string path)
{
var log = CreateLog("Lock LFS File");
var succ = await new Commands.LFS(_fullpath)
var succ = await new Commands.LFS(FullPath)
.Use(log)
.LockAsync(remote, path);
if (succ)
App.SendNotification(_fullpath, $"Lock file successfully! File: {path}");
App.SendNotification(FullPath, $"Lock file successfully! File: {path}");
log.Complete();
return succ;
@@ -718,12 +722,12 @@ namespace SourceGit.ViewModels
public async Task<bool> UnlockLFSFileAsync(string remote, string path, bool force, bool notify)
{
var log = CreateLog("Unlock LFS File");
var succ = await new Commands.LFS(_fullpath)
var succ = await new Commands.LFS(FullPath)
.Use(log)
.UnlockAsync(remote, path, force);
if (succ && notify)
App.SendNotification(_fullpath, $"Unlock file successfully! File: {path}");
App.SendNotification(FullPath, $"Unlock file successfully! File: {path}");
log.Complete();
return succ;
@@ -790,7 +794,7 @@ namespace SourceGit.ViewModels
IssueTrackers.AddRange(issuetrackers);
});
var config = await new Commands.Config(_fullpath).ReadAllAsync().ConfigureAwait(false);
var config = await new Commands.Config(FullPath).ReadAllAsync().ConfigureAwait(false);
_hasAllowedSignersFile = config.TryGetValue("gpg.ssh.allowedSignersFile", out var allowedSignersFile) && !string.IsNullOrEmpty(allowedSignersFile);
if (config.TryGetValue("gitflow.branch.master", out var masterName))
@@ -813,7 +817,7 @@ namespace SourceGit.ViewModels
if (_remotes.Count == 0)
{
App.RaiseException(_fullpath, "No remotes added to this repository!!!");
App.RaiseException(FullPath, "No remotes added to this repository!!!");
return;
}
@@ -830,13 +834,13 @@ namespace SourceGit.ViewModels
if (_remotes.Count == 0)
{
App.RaiseException(_fullpath, "No remotes added to this repository!!!");
App.RaiseException(FullPath, "No remotes added to this repository!!!");
return;
}
if (_currentBranch == null)
{
App.RaiseException(_fullpath, "Can NOT find current branch!!!");
App.RaiseException(FullPath, "Can NOT find current branch!!!");
return;
}
@@ -854,13 +858,13 @@ namespace SourceGit.ViewModels
if (_remotes.Count == 0)
{
App.RaiseException(_fullpath, "No remotes added to this repository!!!");
App.RaiseException(FullPath, "No remotes added to this repository!!!");
return;
}
if (_currentBranch == null)
{
App.RaiseException(_fullpath, "Can NOT find current branch!!!");
App.RaiseException(FullPath, "Can NOT find current branch!!!");
return;
}
@@ -925,13 +929,13 @@ namespace SourceGit.ViewModels
if (method == Models.CommitSearchMethod.BySHA)
{
var isCommitSHA = await new Commands.IsCommitSHA(_fullpath, _searchCommitFilter)
var isCommitSHA = await new Commands.IsCommitSHA(FullPath, _searchCommitFilter)
.GetResultAsync()
.ConfigureAwait(false);
if (isCommitSHA)
{
var commit = await new Commands.QuerySingleCommit(_fullpath, _searchCommitFilter)
var commit = await new Commands.QuerySingleCommit(FullPath, _searchCommitFilter)
.GetResultAsync()
.ConfigureAwait(false);
visible.Add(commit);
@@ -939,7 +943,7 @@ namespace SourceGit.ViewModels
}
else
{
visible = await new Commands.QueryCommits(_fullpath, _searchCommitFilter, method, _onlySearchCommitsInCurrentBranch)
visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, _onlySearchCommitsInCurrentBranch)
.GetResultAsync()
.ConfigureAwait(false);
}
@@ -959,38 +963,24 @@ namespace SourceGit.ViewModels
public void MarkBranchesDirtyManually()
{
if (_watcher == null)
{
RefreshBranches();
RefreshCommits();
RefreshWorkingCopyChanges();
RefreshWorktrees();
}
else
{
_watcher.MarkBranchDirtyManually();
}
_watcher?.MarkBranchUpdated();
RefreshBranches();
RefreshCommits();
RefreshWorkingCopyChanges();
RefreshWorktrees();
}
public void MarkTagsDirtyManually()
{
if (_watcher == null)
{
RefreshTags();
RefreshCommits();
}
else
{
_watcher.MarkTagDirtyManually();
}
_watcher?.MarkTagUpdated();
RefreshTags();
RefreshCommits();
}
public void MarkWorkingCopyDirtyManually()
{
if (_watcher == null)
RefreshWorkingCopyChanges();
else
_watcher.MarkWorkingCopyDirtyManually();
_watcher?.MarkWorkingCopyUpdated();
RefreshWorkingCopyChanges();
}
public void MarkFetched()
@@ -1125,17 +1115,20 @@ namespace SourceGit.ViewModels
public async Task StashAllAsync(bool autoStart)
{
await _workingCopy?.StashAllAsync(autoStart);
if (_workingCopy != null)
await _workingCopy.StashAllAsync(autoStart);
}
public async Task SkipMergeAsync()
{
await _workingCopy?.SkipMergeAsync();
if (_workingCopy != null)
await _workingCopy.SkipMergeAsync();
}
public async Task AbortMergeAsync()
{
await _workingCopy?.AbortMergeAsync();
if (_workingCopy != null)
await _workingCopy.AbortMergeAsync();
}
public List<(Models.CustomAction, CustomActionContextMenuLabel)> GetCustomActions(Models.CustomActionScope scope)
@@ -1164,14 +1157,14 @@ namespace SourceGit.ViewModels
var log = CreateLog($"Bisect({subcmd})");
var succ = await new Commands.Bisect(_fullpath, subcmd).Use(log).ExecAsync();
var succ = await new Commands.Bisect(FullPath, subcmd).Use(log).ExecAsync();
log.Complete();
var head = await new Commands.QueryRevisionByRefName(_fullpath, "HEAD").GetResultAsync();
var head = await new Commands.QueryRevisionByRefName(FullPath, "HEAD").GetResultAsync();
if (!succ)
App.RaiseException(_fullpath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
App.RaiseException(FullPath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
else if (log.Content.Contains("is the first bad commit"))
App.SendNotification(_fullpath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
App.SendNotification(FullPath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
MarkBranchesDirtyManually();
NavigateToCommit(head, true);
@@ -1181,21 +1174,30 @@ namespace SourceGit.ViewModels
public bool MayHaveSubmodules()
{
var modulesFile = Path.Combine(_fullpath, ".gitmodules");
var modulesFile = Path.Combine(FullPath, ".gitmodules");
var info = new FileInfo(modulesFile);
return info.Exists && info.Length > 20;
}
public void RefreshBranches()
{
if (_cancellationRefreshBranches is { IsCancellationRequested: false })
_cancellationRefreshBranches.Cancel();
_cancellationRefreshBranches = new CancellationTokenSource();
var token = _cancellationRefreshBranches.Token;
Task.Run(async () =>
{
var branches = await new Commands.QueryBranches(_fullpath).GetResultAsync().ConfigureAwait(false);
var remotes = await new Commands.QueryRemotes(_fullpath).GetResultAsync().ConfigureAwait(false);
var branches = await new Commands.QueryBranches(FullPath).GetResultAsync().ConfigureAwait(false);
var remotes = await new Commands.QueryRemotes(FullPath).GetResultAsync().ConfigureAwait(false);
var builder = BuildBranchTree(branches, remotes);
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
Remotes = remotes;
Branches = branches;
CurrentBranch = branches.Find(x => x.IsCurrent);
@@ -1216,58 +1218,64 @@ namespace SourceGit.ViewModels
var hasPendingPullOrPush = CurrentBranch?.TrackStatus.IsVisible ?? false;
GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasPendingPullOrPush, !hasPendingPullOrPush);
});
});
}, token);
}
public void RefreshWorktrees()
{
Task.Run(async () =>
{
var worktrees = await new Commands.Worktree(_fullpath).ReadAllAsync().ConfigureAwait(false);
if (worktrees.Count > 0)
var worktrees = await new Commands.Worktree(FullPath).ReadAllAsync().ConfigureAwait(false);
if (worktrees.Count == 0)
{
var cleaned = new List<Models.Worktree>();
var normalizedGitDir = _gitDir.Replace('\\', '/');
foreach (var worktree in worktrees)
{
if (worktree.FullPath.Equals(_fullpath, StringComparison.Ordinal) ||
worktree.FullPath.Equals(normalizedGitDir, StringComparison.Ordinal))
continue;
cleaned.Add(worktree);
}
Dispatcher.UIThread.Invoke(() =>
{
Worktrees = cleaned;
});
Dispatcher.UIThread.Invoke(() => Worktrees = worktrees);
return;
}
else
var cleaned = new List<Models.Worktree>();
foreach (var worktree in worktrees)
{
Dispatcher.UIThread.Invoke(() =>
{
Worktrees = worktrees;
});
if (worktree.FullPath.Equals(FullPath, StringComparison.Ordinal) ||
worktree.FullPath.Equals(GitDir, StringComparison.Ordinal))
continue;
cleaned.Add(worktree);
}
Dispatcher.UIThread.Invoke(() => Worktrees = cleaned);
});
}
public void RefreshTags()
{
if (_cancellationRefreshTags is { IsCancellationRequested: false })
_cancellationRefreshTags.Cancel();
_cancellationRefreshTags = new CancellationTokenSource();
var token = _cancellationRefreshTags.Token;
Task.Run(async () =>
{
var tags = await new Commands.QueryTags(_fullpath).GetResultAsync().ConfigureAwait(false);
var tags = await new Commands.QueryTags(FullPath).GetResultAsync().ConfigureAwait(false);
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
Tags = tags;
VisibleTags = BuildVisibleTags();
});
});
}, token);
}
public void RefreshCommits()
{
if (_cancellationRefreshCommits is { IsCancellationRequested: false })
_cancellationRefreshCommits.Cancel();
_cancellationRefreshCommits = new CancellationTokenSource();
var token = _cancellationRefreshCommits.Token;
Task.Run(async () =>
{
await Dispatcher.UIThread.InvokeAsync(() => _histories.IsLoading = true);
@@ -1295,11 +1303,14 @@ namespace SourceGit.ViewModels
else
builder.Append(filters);
var commits = await new Commands.QueryCommits(_fullpath, builder.ToString()).GetResultAsync().ConfigureAwait(false);
var commits = await new Commands.QueryCommits(FullPath, builder.ToString()).GetResultAsync().ConfigureAwait(false);
var graph = Models.CommitGraph.Parse(commits, _settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly));
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
if (_histories != null)
{
_histories.IsLoading = false;
@@ -1314,7 +1325,7 @@ namespace SourceGit.ViewModels
_navigateToCommitDelayed = string.Empty;
});
});
}, token);
}
public void RefreshSubmodules()
@@ -1335,7 +1346,7 @@ namespace SourceGit.ViewModels
Task.Run(async () =>
{
var submodules = await new Commands.QuerySubmodules(_fullpath).GetResultAsync().ConfigureAwait(false);
var submodules = await new Commands.QuerySubmodules(FullPath).GetResultAsync().ConfigureAwait(false);
_watcher?.SetSubmodules(submodules);
Dispatcher.UIThread.Invoke(() =>
@@ -1379,25 +1390,34 @@ namespace SourceGit.ViewModels
if (IsBare)
return;
if (_cancellationRefreshWorkingCopyChanges is { IsCancellationRequested: false })
_cancellationRefreshWorkingCopyChanges.Cancel();
_cancellationRefreshWorkingCopyChanges = new CancellationTokenSource();
var token = _cancellationRefreshWorkingCopyChanges.Token;
Task.Run(async () =>
{
var changes = await new Commands.QueryLocalChanges(_fullpath, _settings.IncludeUntrackedInLocalChanges)
var changes = await new Commands.QueryLocalChanges(FullPath, _settings.IncludeUntrackedInLocalChanges)
.GetResultAsync()
.ConfigureAwait(false);
if (_workingCopy == null)
if (_workingCopy == null || token.IsCancellationRequested)
return;
changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path));
_workingCopy.SetData(changes);
_workingCopy.SetData(changes, token);
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
LocalChangesCount = changes.Count;
OnPropertyChanged(nameof(InProgressContext));
GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0);
});
});
}, token);
}
public void RefreshStashes()
@@ -1405,17 +1425,26 @@ namespace SourceGit.ViewModels
if (IsBare)
return;
if (_cancellationRefreshStashes is { IsCancellationRequested: false })
_cancellationRefreshStashes.Cancel();
_cancellationRefreshStashes = new CancellationTokenSource();
var token = _cancellationRefreshStashes.Token;
Task.Run(async () =>
{
var stashes = await new Commands.QueryStashes(_fullpath).GetResultAsync().ConfigureAwait(false);
var stashes = await new Commands.QueryStashes(FullPath).GetResultAsync().ConfigureAwait(false);
Dispatcher.UIThread.Invoke(() =>
{
if (token.IsCancellationRequested)
return;
if (_stashesPage != null)
_stashesPage.Stashes = stashes;
StashesCount = stashes.Count;
});
});
}, token);
}
public void ToggleHistoryShowFlag(Models.HistoryShowFlags flag)
@@ -1430,7 +1459,7 @@ namespace SourceGit.ViewModels
{
if (_currentBranch == null)
{
App.RaiseException(_fullpath, "Git cannot create a branch before your first commit.");
App.RaiseException(FullPath, "Git cannot create a branch before your first commit.");
return;
}
@@ -1486,9 +1515,9 @@ namespace SourceGit.ViewModels
public async Task CheckoutTagAsync(Models.Tag tag)
{
var c = await new Commands.QuerySingleCommit(_fullpath, tag.SHA).GetResultAsync();
if (c != null)
await _histories?.CheckoutBranchByCommitAsync(c);
var c = await new Commands.QuerySingleCommit(FullPath, tag.SHA).GetResultAsync();
if (c != null && _histories != null)
await _histories.CheckoutBranchByCommitAsync(c);
}
public async Task CompareBranchWithWorktreeAsync(Models.Branch branch)
@@ -1497,9 +1526,9 @@ namespace SourceGit.ViewModels
{
SelectedSearchedCommit = null;
var target = await new Commands.QuerySingleCommit(_fullpath, branch.Head).GetResultAsync();
var target = await new Commands.QuerySingleCommit(FullPath, branch.Head).GetResultAsync();
_histories.AutoSelectedCommit = null;
_histories.DetailContext = new RevisionCompare(_fullpath, target, null);
_histories.DetailContext = new RevisionCompare(FullPath, target, null);
}
}
@@ -1525,7 +1554,7 @@ namespace SourceGit.ViewModels
{
if (_currentBranch == null)
{
App.RaiseException(_fullpath, "Git cannot create a branch before your first commit.");
App.RaiseException(FullPath, "Git cannot create a branch before your first commit.");
return;
}
@@ -1569,7 +1598,7 @@ namespace SourceGit.ViewModels
if (selfPage == null)
return;
var root = Path.GetFullPath(Path.Combine(_fullpath, submodule));
var root = Path.GetFullPath(Path.Combine(FullPath, submodule));
var normalizedPath = root.Replace('\\', '/').TrimEnd('/');
var node = Preferences.Instance.FindNode(normalizedPath) ??
@@ -1614,7 +1643,7 @@ namespace SourceGit.ViewModels
{
SetWatcherEnabled(false);
var log = CreateLog("Lock Worktree");
var succ = await new Commands.Worktree(_fullpath).Use(log).LockAsync(worktree.FullPath);
var succ = await new Commands.Worktree(FullPath).Use(log).LockAsync(worktree.FullPath);
if (succ)
worktree.IsLocked = true;
log.Complete();
@@ -1625,7 +1654,7 @@ namespace SourceGit.ViewModels
{
SetWatcherEnabled(false);
var log = CreateLog("Unlock Worktree");
var succ = await new Commands.Worktree(_fullpath).Use(log).UnlockAsync(worktree.FullPath);
var succ = await new Commands.Worktree(FullPath).Use(log).UnlockAsync(worktree.FullPath);
if (succ)
worktree.IsLocked = false;
log.Complete();
@@ -1668,7 +1697,7 @@ namespace SourceGit.ViewModels
public async Task<bool> SaveCommitAsPatchAsync(Models.Commit commit, string folder, int index = 0)
{
var ignore_chars = new HashSet<char> { '/', '\\', ':', ',', '*', '?', '\"', '<', '>', '|', '`', '$', '^', '%', '[', ']', '+', '-' };
var ignoredChars = new HashSet<char> { '/', '\\', ':', ',', '*', '?', '\"', '<', '>', '|', '`', '$', '^', '%', '[', ']', '+', '-' };
var builder = new StringBuilder();
builder.Append(index.ToString("D4"));
builder.Append('-');
@@ -1677,7 +1706,7 @@ namespace SourceGit.ViewModels
var len = 0;
foreach (var c in chars)
{
if (!ignore_chars.Contains(c))
if (!ignoredChars.Contains(c))
{
if (c == ' ' || c == '\t')
builder.Append('-');
@@ -1694,7 +1723,7 @@ namespace SourceGit.ViewModels
var saveTo = Path.Combine(folder, builder.ToString());
var log = CreateLog("Save Commit as Patch");
var succ = await new Commands.FormatPatch(_fullpath, commit.SHA, saveTo).Use(log).ExecAsync();
var succ = await new Commands.FormatPatch(FullPath, commit.SHA, saveTo).Use(log).ExecAsync();
log.Complete();
return succ;
}
@@ -1707,7 +1736,7 @@ namespace SourceGit.ViewModels
foreach (var page in launcher.Pages)
{
if (page.Node.Id.Equals(_fullpath))
if (page.Node.Id.Equals(FullPath))
return page;
}
@@ -1716,7 +1745,7 @@ namespace SourceGit.ViewModels
private Commands.IssueTracker CreateIssueTrackerCommand(bool shared)
{
return new Commands.IssueTracker(_fullpath, shared ? $"{_fullpath}/.issuetracker" : null);
return new Commands.IssueTracker(FullPath, shared ? $"{FullPath}/.issuetracker" : null);
}
private BranchTreeNode.Builder BuildBranchTree(List<Models.Branch> branches, List<Models.Remote> remotes)
@@ -1884,6 +1913,9 @@ namespace SourceGit.ViewModels
private BranchTreeNode FindBranchNode(List<BranchTreeNode> nodes, string path)
{
if (string.IsNullOrEmpty(path))
return null;
foreach (var node in nodes)
{
if (node.Path.Equals(path, StringComparison.Ordinal))
@@ -1923,7 +1955,7 @@ namespace SourceGit.ViewModels
Task.Run(async () =>
{
_worktreeFiles = await new Commands.QueryRevisionFileNames(_fullpath, "HEAD")
_worktreeFiles = await new Commands.QueryRevisionFileNames(FullPath, "HEAD")
.GetResultAsync()
.ConfigureAwait(false);
@@ -1959,11 +1991,11 @@ namespace SourceGit.ViewModels
MatchedFilesForSearching = matched;
}
private void AutoFetchInBackground(object sender)
private void FetchInBackground(object sender)
{
Dispatcher.UIThread.Post(async () =>
Dispatcher.UIThread.Invoke(async Task () =>
{
if (_settings == null || !_settings.EnableAutoFetch)
if (_settings is not { EnableAutoFetch: true })
return;
if (!CanCreatePopup())
@@ -1972,7 +2004,7 @@ namespace SourceGit.ViewModels
return;
}
var lockFile = Path.Combine(_gitDir, "index.lock");
var lockFile = Path.Combine(GitDir, "index.lock");
if (File.Exists(lockFile))
return;
@@ -1981,16 +2013,16 @@ namespace SourceGit.ViewModels
if (desire > now)
return;
IsAutoFetching = true;
var remotes = new List<string>();
foreach (var r in _remotes)
remotes.Add(r.Name);
IsAutoFetching = true;
if (_settings.FetchAllRemotes)
{
foreach (var remote in remotes)
await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync();
await new Commands.Fetch(FullPath, remote, false, false) { RaiseError = false }.RunAsync();
}
else if (remotes.Count > 0)
{
@@ -1998,7 +2030,7 @@ namespace SourceGit.ViewModels
remotes.Find(x => x.Equals(_settings.DefaultRemote, StringComparison.Ordinal)) :
remotes[0];
await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync();
await new Commands.Fetch(FullPath, remote, false, false) { RaiseError = false }.RunAsync();
}
_lastFetchTime = DateTime.Now;
@@ -2006,10 +2038,8 @@ namespace SourceGit.ViewModels
});
}
private string _fullpath = string.Empty;
private string _gitDir = string.Empty;
private string _gitCommonDir = string.Empty;
private bool _isWorktree = false;
private readonly bool _isWorktree = false;
private readonly string _gitCommonDir = null;
private Models.RepositorySettings _settings = null;
private Models.FilterMode _historiesFilterMode = Models.FilterMode.None;
private bool _hasAllowedSignersFile = false;
@@ -2030,23 +2060,24 @@ namespace SourceGit.ViewModels
private int _searchCommitFilterType = (int)Models.CommitSearchMethod.ByMessage;
private bool _onlySearchCommitsInCurrentBranch = false;
private string _searchCommitFilter = string.Empty;
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
private List<Models.Commit> _searchedCommits = [];
private Models.Commit _selectedSearchedCommit = null;
private bool _requestingWorktreeFiles = false;
private List<string> _worktreeFiles = null;
private List<string> _matchedFilesForSearching = null;
private string _filter = string.Empty;
private List<Models.Remote> _remotes = new List<Models.Remote>();
private List<Models.Branch> _branches = new List<Models.Branch>();
private List<Models.Remote> _remotes = [];
private List<Models.Branch> _branches = [];
private Models.Branch _currentBranch = null;
private List<BranchTreeNode> _localBranchTrees = new List<BranchTreeNode>();
private List<BranchTreeNode> _remoteBranchTrees = new List<BranchTreeNode>();
private List<Models.Worktree> _worktrees = new List<Models.Worktree>();
private List<Models.Tag> _tags = new List<Models.Tag>();
private List<BranchTreeNode> _localBranchTrees = [];
private List<BranchTreeNode> _remoteBranchTrees = [];
private List<Models.Worktree> _worktrees = [];
private List<Models.Tag> _tags = [];
private object _visibleTags = null;
private List<Models.Submodule> _submodules = new List<Models.Submodule>();
private List<Models.Submodule> _submodules = [];
private object _visibleSubmodules = null;
private string _navigateToCommitDelayed = string.Empty;
private bool _isAutoFetching = false;
private Timer _autoFetchTimer = null;
@@ -2055,6 +2086,10 @@ namespace SourceGit.ViewModels
private Models.BisectState _bisectState = Models.BisectState.None;
private bool _isBisectCommandRunning = false;
private string _navigateToCommitDelayed = string.Empty;
private CancellationTokenSource _cancellationRefreshBranches = null;
private CancellationTokenSource _cancellationRefreshTags = null;
private CancellationTokenSource _cancellationRefreshWorkingCopyChanges = null;
private CancellationTokenSource _cancellationRefreshCommits = null;
private CancellationTokenSource _cancellationRefreshStashes = null;
}
}

View File

@@ -8,11 +8,6 @@ namespace SourceGit.ViewModels
{
public class RevisionCompare : ObservableObject, IDisposable
{
public string RepositoryPath
{
get => _repo;
}
public bool IsLoading
{
get => _isLoading;
@@ -127,6 +122,11 @@ namespace SourceGit.ViewModels
Refresh();
}
public string GetAbsPath(string path)
{
return Native.OS.GetAbsPath(_repo, path);
}
public void SaveAsPatch(string saveTo)
{
Task.Run(async () =>

View File

@@ -9,11 +9,6 @@ namespace SourceGit.ViewModels
{
public class StashesPage : ObservableObject, IDisposable
{
public string RepositoryPath
{
get => _repo.FullPath;
}
public List<Models.Stash> Stashes
{
get => _stashes;
@@ -146,6 +141,11 @@ namespace SourceGit.ViewModels
SearchFilter = string.Empty;
}
public string GetAbsPath(string path)
{
return Native.OS.GetAbsPath(_repo.FullPath, path);
}
public void Apply(Models.Stash stash)
{
if (_repo.CanCreatePopup())

View File

@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public record TextDiffSelectedChunk(double Y, double Height, int StartIdx, int EndIdx, bool Combined, bool IsOldSide)
{
public static bool IsChanged(TextDiffSelectedChunk oldValue, TextDiffSelectedChunk newValue)
{
if (newValue == null)
return oldValue != null;
if (oldValue == null)
return true;
return Math.Abs(newValue.Y - oldValue.Y) > 0.001 ||
Math.Abs(newValue.Height - oldValue.Height) > 0.001 ||
newValue.StartIdx != oldValue.StartIdx ||
newValue.EndIdx != oldValue.EndIdx ||
newValue.Combined != oldValue.Combined ||
newValue.IsOldSide != oldValue.IsOldSide;
}
}
public record TextDiffDisplayRange(int Start, int End)
{
}
public class TextDiffContext : ObservableObject
{
public Models.TextDiff Data => _data;
public string File => _data.File;
public bool IsUnstaged => _data.Option.IsUnstaged;
public bool EnableChunkOption => _data.Option.WorkingCopyChange != null;
public Vector ScrollOffset
{
get => _scrollOffset;
set => SetProperty(ref _scrollOffset, value);
}
public BlockNavigation BlockNavigation
{
get => _blockNavigation;
set => SetProperty(ref _blockNavigation, value);
}
public TextDiffDisplayRange DisplayRange
{
get => _displayRange;
set => SetProperty(ref _displayRange, value);
}
public TextDiffSelectedChunk SelectedChunk
{
get => _selectedChunk;
set => SetProperty(ref _selectedChunk, value);
}
public (int, int) FindRangeByIndex(List<Models.TextDiffLine> lines, int lineIdx)
{
var startIdx = -1;
var endIdx = -1;
var normalLineCount = 0;
var modifiedLineCount = 0;
for (int i = lineIdx; i >= 0; i--)
{
var line = lines[i];
if (line.Type == Models.TextDiffLineType.Indicator)
{
startIdx = i;
break;
}
if (line.Type == Models.TextDiffLineType.Normal)
{
normalLineCount++;
if (normalLineCount >= 2)
{
startIdx = i;
break;
}
}
else
{
normalLineCount = 0;
modifiedLineCount++;
}
}
normalLineCount = lines[lineIdx].Type == Models.TextDiffLineType.Normal ? 1 : 0;
for (int i = lineIdx + 1; i < lines.Count; i++)
{
var line = lines[i];
if (line.Type == Models.TextDiffLineType.Indicator)
{
endIdx = i;
break;
}
if (line.Type == Models.TextDiffLineType.Normal)
{
normalLineCount++;
if (normalLineCount >= 2)
{
endIdx = i;
break;
}
}
else
{
normalLineCount = 0;
modifiedLineCount++;
}
}
if (endIdx == -1)
endIdx = lines.Count - 1;
return modifiedLineCount > 0 ? (startIdx, endIdx) : (-1, -1);
}
public virtual bool IsSideBySide()
{
return false;
}
public virtual TextDiffContext SwitchMode()
{
return null;
}
protected Models.TextDiff _data = null;
protected Vector _scrollOffset = Vector.Zero;
protected BlockNavigation _blockNavigation = null;
private TextDiffDisplayRange _displayRange = null;
private TextDiffSelectedChunk _selectedChunk = null;
}
public class CombinedTextDiff : TextDiffContext
{
public CombinedTextDiff(Models.TextDiff diff, CombinedTextDiff previous = null)
{
_data = diff;
_blockNavigation = new BlockNavigation(_data.Lines);
if (previous != null && previous.File.Equals(File, StringComparison.Ordinal))
_scrollOffset = previous.ScrollOffset;
}
public override TextDiffContext SwitchMode()
{
return new TwoSideTextDiff(_data);
}
}
public class TwoSideTextDiff : TextDiffContext
{
public List<Models.TextDiffLine> Old { get; } = [];
public List<Models.TextDiffLine> New { get; } = [];
public TwoSideTextDiff(Models.TextDiff diff, TwoSideTextDiff previous = null)
{
_data = diff;
foreach (var line in diff.Lines)
{
switch (line.Type)
{
case Models.TextDiffLineType.Added:
New.Add(line);
break;
case Models.TextDiffLineType.Deleted:
Old.Add(line);
break;
default:
FillEmptyLines();
Old.Add(line);
New.Add(line);
break;
}
}
FillEmptyLines();
_blockNavigation = new BlockNavigation(Old);
if (previous != null && previous.File.Equals(File, StringComparison.Ordinal))
_scrollOffset = previous._scrollOffset;
}
public override bool IsSideBySide()
{
return true;
}
public override TextDiffContext SwitchMode()
{
return new CombinedTextDiff(_data);
}
public void ConvertsToCombinedRange(ref int startLine, ref int endLine, bool isOldSide)
{
endLine = Math.Min(endLine, _data.Lines.Count - 1);
var oneSide = isOldSide ? Old : New;
var firstContentLine = -1;
for (int i = startLine; i <= endLine; i++)
{
var line = oneSide[i];
if (line.Type != Models.TextDiffLineType.None)
{
firstContentLine = i;
break;
}
}
if (firstContentLine < 0)
return;
var endContentLine = -1;
for (int i = Math.Min(endLine, oneSide.Count - 1); i >= startLine; i--)
{
var line = oneSide[i];
if (line.Type != Models.TextDiffLineType.None)
{
endContentLine = i;
break;
}
}
if (endContentLine < 0)
return;
var firstContent = oneSide[firstContentLine];
var endContent = oneSide[endContentLine];
startLine = _data.Lines.IndexOf(firstContent);
endLine = _data.Lines.IndexOf(endContent);
}
private void FillEmptyLines()
{
if (Old.Count < New.Count)
{
int diff = New.Count - Old.Count;
for (int i = 0; i < diff; i++)
Old.Add(new Models.TextDiffLine());
}
else if (Old.Count > New.Count)
{
int diff = Old.Count - New.Count;
for (int i = 0; i < diff; i++)
New.Add(new Models.TextDiffLine());
}
}
}
}

View File

@@ -1,109 +0,0 @@
using System;
using System.Collections.Generic;
using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class TwoSideTextDiff : ObservableObject
{
public string File { get; set; }
public List<Models.TextDiffLine> Old { get; set; } = new List<Models.TextDiffLine>();
public List<Models.TextDiffLine> New { get; set; } = new List<Models.TextDiffLine>();
public int MaxLineNumber = 0;
public Vector SyncScrollOffset
{
get => _syncScrollOffset;
set => SetProperty(ref _syncScrollOffset, value);
}
public TwoSideTextDiff(Models.TextDiff diff, TwoSideTextDiff previous = null)
{
File = diff.File;
MaxLineNumber = diff.MaxLineNumber;
foreach (var line in diff.Lines)
{
switch (line.Type)
{
case Models.TextDiffLineType.Added:
New.Add(line);
break;
case Models.TextDiffLineType.Deleted:
Old.Add(line);
break;
default:
FillEmptyLines();
Old.Add(line);
New.Add(line);
break;
}
}
FillEmptyLines();
if (previous != null && previous.File == File)
_syncScrollOffset = previous._syncScrollOffset;
}
public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide)
{
endLine = Math.Min(endLine, combined.Lines.Count - 1);
var oneSide = isOldSide ? Old : New;
var firstContentLine = -1;
for (int i = startLine; i <= endLine; i++)
{
var line = oneSide[i];
if (line.Type != Models.TextDiffLineType.None)
{
firstContentLine = i;
break;
}
}
if (firstContentLine < 0)
return;
var endContentLine = -1;
for (int i = Math.Min(endLine, oneSide.Count - 1); i >= startLine; i--)
{
var line = oneSide[i];
if (line.Type != Models.TextDiffLineType.None)
{
endContentLine = i;
break;
}
}
if (endContentLine < 0)
return;
var firstContent = oneSide[firstContentLine];
var endContent = oneSide[endContentLine];
startLine = combined.Lines.IndexOf(firstContent);
endLine = combined.Lines.IndexOf(endContent);
}
private void FillEmptyLines()
{
if (Old.Count < New.Count)
{
int diff = New.Count - Old.Count;
for (int i = 0; i < diff; i++)
Old.Add(new Models.TextDiffLine());
}
else if (Old.Count > New.Count)
{
int diff = Old.Count - New.Count;
for (int i = 0; i < diff; i++)
New.Add(new Models.TextDiffLine());
}
}
private Vector _syncScrollOffset = Vector.Zero;
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Threading;
@@ -246,15 +247,17 @@ namespace SourceGit.ViewModels
_commitMessage = string.Empty;
}
public void SetData(List<Models.Change> changes)
public void SetData(List<Models.Change> changes, CancellationToken cancellationToken)
{
if (!IsChanged(_cached, changes))
{
// Just force refresh selected changes.
Dispatcher.UIThread.Invoke(() =>
{
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
if (cancellationToken.IsCancellationRequested)
return;
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
UpdateDetail();
UpdateInProgressState();
});
@@ -308,6 +311,9 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(() =>
{
if (cancellationToken.IsCancellationRequested)
return;
_isLoadingData = true;
HasUnsolvedConflicts = hasConflict;
VisibleUnstaged = visibleUnstaged;
@@ -417,7 +423,7 @@ namespace SourceGit.ViewModels
public async Task SaveChangesToPatchAsync(List<Models.Change> changes, bool isUnstaged, string saveTo)
{
var succ = await Commands.SaveChangesAsPatch.ProcessLocalChangesAsync(_repo.FullPath, _selectedUnstaged, isUnstaged, saveTo);
var succ = await Commands.SaveChangesAsPatch.ProcessLocalChangesAsync(_repo.FullPath, changes, isUnstaged, saveTo);
if (succ)
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
}
@@ -606,6 +612,13 @@ namespace SourceGit.ViewModels
CommitMessage = tmpl.Apply(_repo.CurrentBranch, _staged);
}
public async Task ClearCommitMessageHistoryAsync()
{
var sure = await App.AskConfirmAsync(App.Text("WorkingCopy.ClearCommitHistories.Confirm"));
if (sure)
_repo.Settings.CommitMessages.Clear();
}
public async Task CommitAsync(bool autoStage, bool autoPush, Models.CommitCheckPassed checkPassed = Models.CommitCheckPassed.None)
{
if (string.IsNullOrWhiteSpace(_commitMessage))

View File

@@ -80,6 +80,7 @@
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding RemoteBranches}"
IsTextSearchEnabled="True"
SelectedItem="{Binding SelectedTrackingBranch, Mode=TwoWay}"
IsVisible="{Binding SetTrackingBranch, Mode=OneWay}">
<ComboBox.ItemTemplate>

View File

@@ -1,4 +1,5 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;

View File

@@ -128,7 +128,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Loading Status Icon -->

View File

@@ -64,10 +64,10 @@ namespace SourceGit.Views
copyFullPath.Header = App.Text("CopyFullPath");
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
copyFullPath.Click += async (_, e) =>
copyFullPath.Click += async (_, ev) =>
{
await App.CopyTextAsync(Native.OS.GetAbsPath(repo, change.Path));
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(copyFullPath);
menu.Open(view);
@@ -83,5 +83,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.BranchCompare vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -96,7 +96,7 @@
IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsVisible="{Binding !IsBranch}"/>
<!-- Content Area (allows double-click) -->
<!-- Content Area (allows double-clicking) -->
<Grid Grid.Column="1"
Background="Transparent"
ColumnDefinitions="18,*,Auto,Auto"

View File

@@ -33,7 +33,7 @@ namespace SourceGit.Views
{
protected override Type StyleKeyOverride => typeof(ListBox);
protected override async void OnKeyDown(KeyEventArgs e)
protected override void OnKeyDown(KeyEventArgs e)
{
if (SelectedItems is [ViewModels.ChangeTreeNode node])
{
@@ -43,36 +43,6 @@ namespace SourceGit.Views
this.FindAncestorOfType<ChangeCollectionView>()?.ToggleNodeIsExpanded(node);
e.Handled = true;
}
else if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
var path = node.FullPath;
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
do
{
var repoView = this.FindAncestorOfType<Repository>();
if (repoView is { DataContext: ViewModels.Repository repo })
{
path = Native.OS.GetAbsPath(repo.FullPath, path);
break;
}
var branchCompareView = this.FindAncestorOfType<BranchCompare>();
if (branchCompareView is { DataContext: ViewModels.BranchCompare branchCompare })
{
path = branchCompare.GetAbsPath(path);
break;
}
// NOTE: if there is another window uses ChangeCollectionView, add it here!
} while (false);
}
await App.CopyTextAsync(path);
e.Handled = true;
}
}
if (!e.Handled && e.Key != Key.Space && e.Key != Key.Enter)
@@ -110,7 +80,7 @@ namespace SourceGit.Views
}
public static readonly StyledProperty<bool> EnableCompactFoldersProperty =
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(EnableCompactFolders), false);
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(EnableCompactFolders));
public bool EnableCompactFolders
{

View File

@@ -30,12 +30,7 @@ namespace SourceGit.Views
public void BeginMoveWindow(object _, PointerPressedEventArgs e)
{
if (e.ClickCount == 1)
{
if (OperatingSystem.IsMacOS())
e.Pointer.Capture(this);
BeginMoveDrag(e);
}
e.Handled = true;
}

View File

@@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.CommitChanges"
x:DataType="vm:CommitDetail">
@@ -51,7 +50,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Summary -->
@@ -59,7 +59,7 @@
<TextBlock Margin="4,0,0,0"
Foreground="{DynamicResource Brush.FG2}"
HorizontalAlignment="Left" VerticalAlignment="Center">
<Run Text="{Binding Changes, Converter={x:Static c:ListConverters.Count}, Mode=OneWay}" FontWeight="Bold"/>
<Run Text="{Binding Changes.Count, Mode=OneWay}" FontWeight="Bold"/>
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
</TextBlock>
</Border>

View File

@@ -1,4 +1,7 @@
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.VisualTree;
namespace SourceGit.Views
@@ -14,7 +17,7 @@ namespace SourceGit.Views
{
e.Handled = true;
if (sender is not ChangeCollectionView view || DataContext is not ViewModels.CommitDetail vm)
if (sender is not ChangeCollectionView view)
return;
var detailView = this.FindAncestorOfType<CommitDetail>();
@@ -36,5 +39,26 @@ namespace SourceGit.Views
menu.Open(view);
}
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.CommitDetail vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -17,7 +17,7 @@ namespace SourceGit.Views
public ContextMenu CreateChangeContextMenuByFolder(ChangeTreeNode node, List<Models.Change> changes)
{
if (DataContext is not ViewModels.CommitDetail { Repository: ViewModels.Repository repo, Commit: Models.Commit commit } vm)
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)
return null;
var fullPath = Native.OS.GetAbsPath(repo.FullPath, node.FullPath);
@@ -98,7 +98,7 @@ namespace SourceGit.Views
public ContextMenu CreateChangeContextMenu(Models.Change change)
{
if (DataContext is not ViewModels.CommitDetail { Repository: ViewModels.Repository repo, Commit: Models.Commit commit } vm)
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)
return null;
var openWithMerger = new MenuItem();
@@ -111,6 +111,16 @@ namespace SourceGit.Views
ev.Handled = true;
};
var openWith = new MenuItem();
openWith.Header = App.Text("OpenWith");
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
openWith.IsEnabled = change.Index != Models.ChangeState.Deleted;
openWith.Click += async (_, ev) =>
{
await vm.OpenRevisionFileWithDefaultEditorAsync(change.Path);
ev.Handled = true;
};
var fullPath = Native.OS.GetAbsPath(repo.FullPath, change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
@@ -167,6 +177,7 @@ namespace SourceGit.Views
var menu = new ContextMenu();
menu.Items.Add(openWithMerger);
menu.Items.Add(openWith);
menu.Items.Add(explore);
menu.Items.Add(new MenuItem { Header = "-" });
menu.Items.Add(history);
@@ -293,24 +304,30 @@ namespace SourceGit.Views
private async void OnCommitListKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is ViewModels.CommitDetail detail &&
sender is ListBox { SelectedItem: Models.Change change } &&
if (DataContext is not ViewModels.CommitDetail vm)
return;
if (sender is not ListBox { SelectedItem: Models.Change change })
return;
if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
if (e.Key == Key.C)
{
var path = change.Path;
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
path = detail.GetAbsPath(path);
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
await App.CopyTextAsync(path);
e.Handled = true;
}
else if (e.Key == Key.D && e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
detail.OpenChangeInMergeTool(change);
e.Handled = true;
}
e.Handled = true;
return;
}
if (e.Key == Key.D &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
vm.OpenChangeInMergeTool(change);
e.Handled = true;
}
}
@@ -327,7 +344,7 @@ namespace SourceGit.Views
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.CommitDetail detail && sender is Grid { DataContext: Models.Change change } grid)
if (sender is Grid { DataContext: Models.Change change } grid)
CreateChangeContextMenu(change)?.Open(grid);
e.Handled = true;
}

View File

@@ -179,6 +179,7 @@ namespace SourceGit.Views
{
var repo = vm.Repository;
var menu = new ContextMenu();
menu.MaxWidth = 480;
var gitTemplate = await new Commands.Config(repo.FullPath).GetAsync("commit.template");
var templateCount = repo.Settings.CommitTemplates.Count;
@@ -199,10 +200,10 @@ namespace SourceGit.Views
var item = new MenuItem();
item.Header = App.Text("WorkingCopy.UseCommitTemplate", template.Name);
item.Icon = App.CreateMenuIcon("Icons.Code");
item.Click += (_, e) =>
item.Click += (_, ev) =>
{
vm.ApplyCommitMessageTemplate(template);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(item);
}
@@ -224,11 +225,11 @@ namespace SourceGit.Views
var gitTemplateItem = new MenuItem();
gitTemplateItem.Header = App.Text("WorkingCopy.UseCommitTemplate", friendlyName);
gitTemplateItem.Icon = App.CreateMenuIcon("Icons.Code");
gitTemplateItem.Click += (_, e) =>
gitTemplateItem.Click += (_, ev) =>
{
if (File.Exists(gitTemplate))
vm.CommitMessage = File.ReadAllText(gitTemplate);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(gitTemplateItem);
}
@@ -261,14 +262,27 @@ namespace SourceGit.Views
var item = new MenuItem();
item.Header = header;
item.Icon = App.CreateMenuIcon("Icons.Histories");
item.Click += (_, e) =>
item.Click += (_, ev) =>
{
vm.CommitMessage = dup;
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(item);
}
menu.Items.Add(new MenuItem() { Header = "-" });
var clearHistoryItem = new MenuItem();
clearHistoryItem.Header = App.Text("WorkingCopy.ClearCommitHistories");
clearHistoryItem.Icon = App.CreateMenuIcon("Icons.Clear");
clearHistoryItem.Click += async (_, ev) =>
{
await vm.ClearCommitMessageHistoryAsync();
ev.Handled = true;
};
menu.Items.Add(clearHistoryItem);
}
menu.Placement = PlacementMode.TopEdgeAlignedLeft;
@@ -309,10 +323,10 @@ namespace SourceGit.Views
var dup = service;
var item = new MenuItem();
item.Header = service.Name;
item.Click += async (_, e) =>
item.Click += async (_, ev) =>
{
await App.ShowDialog(new ViewModels.AIAssistant(repo, dup, vm.Staged, t => vm.CommitMessage = t));
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(item);

View File

@@ -37,6 +37,7 @@
<Button Classes="icon_button"
Width="28"
Click="OnGotoFirstChange"
IsVisible="{Binding IsTextDiff}"
HotKey="{OnPlatform Ctrl+Alt+Home, macOS=⌘+Alt+Home}">
<ToolTip.Tip>
<TextBlock>
@@ -45,12 +46,6 @@
<Run Text="{OnPlatform Ctrl+Alt+Home, macOS=⌘+⌥+Home}" FontSize="11" Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"/>
</TextBlock>
</ToolTip.Tip>
<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>
@@ -69,16 +64,13 @@
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
</Button>
<Border>
<Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsTextDiff"/>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseBlockNavigationInDiffView" Mode="OneWay"/>
</MultiBinding>
</Border.IsVisible>
<TextBlock x:Name="BlockNavigationIndicator" Classes="primary" Margin="0,0,0,0" FontSize="11" Text="-/-"/>
</Border>
<ContentControl Content="{Binding Content, Mode=OneWay}" IsVisible="{Binding IsTextDiff}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:TextDiffContext">
<TextBlock Classes="primary" Margin="0,0,0,0" FontSize="11" Text="{Binding BlockNavigation.Indicator}"/>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<Button Classes="icon_button"
Width="28"
@@ -98,6 +90,7 @@
<Button Classes="icon_button"
Width="28"
Click="OnGotoLastChange"
IsVisible="{Binding IsTextDiff}"
HotKey="{OnPlatform Ctrl+Alt+End, macOS=⌘+Alt+End}">
<ToolTip.Tip>
<TextBlock>
@@ -106,31 +99,15 @@
<Run Text="{OnPlatform Ctrl+Alt+End, macOS=⌘+⌥+End}" FontSize="11" Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"/>
</TextBlock>
</ToolTip.Tip>
<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"
Width="28"
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseBlockNavigationInDiffView, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.UseBlockNavigation}">
<Path Width="13" Height="13" Data="{StaticResource Icons.CodeBlock}" Margin="0,3,0,0"/>
</ToggleButton>
<Button Classes="icon_button"
Width="28"
Command="{Binding IncrUnified}"
IsVisible="{Binding IsTextDiff}"
IsEnabled="{Binding ShowEntireFile, Mode=OneWay, Converter={x:Static BoolConverters.Not}}"
ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Incr}">
<Button.IsEnabled>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseFullTextDiff" Mode="OneWay" Converter="{x:Static BoolConverters.Not}"/>
</Button.IsEnabled>
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Lines.Incr}"/>
</Button>
@@ -142,7 +119,7 @@
<Button.IsEnabled>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="UnifiedLines" Mode="OneWay" Converter="{x:Static c:IntConverters.IsGreaterThanFour}"/>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseFullTextDiff" Mode="OneWay" Converter="{x:Static BoolConverters.Not}"/>
<Binding Path="ShowEntireFile" Mode="OneWay" Converter="{x:Static BoolConverters.Not}"/>
</MultiBinding>
</Button.IsEnabled>
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Lines.Decr}"/>
@@ -150,8 +127,7 @@
<ToggleButton Classes="line_path"
Width="28"
Click="OnUseFullTextDiffClicked"
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseFullTextDiff, Mode=OneWay}"
IsChecked="{Binding ShowEntireFile, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.All}">
<Path Width="13" Height="13" Data="{StaticResource Icons.Lines.All}" Margin="0,3,0,0"/>
@@ -172,8 +148,8 @@
ToolTip.Tip="{DynamicResource Text.Diff.ToggleWordWrap}">
<ToggleButton.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsTextDiff"/>
<Binding Source="{x:Static vm:Preferences.Instance}" Path="UseSideBySideDiff" Mode="OneWay" Converter="{x:Static BoolConverters.Not}"/>
<Binding Path="IsTextDiff" Mode="OneWay"/>
<Binding Path="UseSideBySide" Mode="OneWay" Converter="{x:Static BoolConverters.Not}"/>
</MultiBinding>
</ToggleButton.IsVisible>
@@ -197,7 +173,7 @@
<ToggleButton Classes="line_path"
Width="28" Height="18"
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSideBySideDiff, Mode=TwoWay}"
IsChecked="{Binding UseSideBySide, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Layout}" Margin="0,2,0,0"/>
@@ -361,10 +337,8 @@
</DataTemplate>
<!-- Text Diff -->
<DataTemplate DataType="m:TextDiff">
<v:TextDiffView UseSideBySideDiff="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
UseBlockNavigation="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseBlockNavigationInDiffView, Mode=OneWay}"
BlockNavigationChanged="OnBlockNavigationChanged"/>
<DataTemplate DataType="vm:TextDiffContext">
<v:TextDiffView SelectedChunk="{Binding SelectedChunk, Mode=OneWay}"/>
</DataTemplate>
<!-- Empty or only EOL changes -->

View File

@@ -1,4 +1,3 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
@@ -12,50 +11,35 @@ namespace SourceGit.Views
InitializeComponent();
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
if (DataContext is ViewModels.DiffContext vm)
vm.CheckSettings();
}
private void OnGotoFirstChange(object _, RoutedEventArgs e)
{
this.FindDescendantOfType<TextDiffView>()?.GotoFirstChange();
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoFirstChange();
e.Handled = true;
}
private void OnGotoPrevChange(object _, RoutedEventArgs e)
{
this.FindDescendantOfType<TextDiffView>()?.GotoPrevChange();
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoPrevChange();
e.Handled = true;
}
private void OnGotoNextChange(object _, RoutedEventArgs e)
{
this.FindDescendantOfType<TextDiffView>()?.GotoNextChange();
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoNextChange();
e.Handled = true;
}
private void OnGotoLastChange(object _, RoutedEventArgs e)
{
this.FindDescendantOfType<TextDiffView>()?.GotoLastChange();
e.Handled = true;
}
private void OnBlockNavigationChanged(object sender, RoutedEventArgs e)
{
if (sender is TextDiffView textDiff)
BlockNavigationIndicator.Text = textDiff.BlockNavigation?.Indicator ?? string.Empty;
}
private void OnUseFullTextDiffClicked(object sender, RoutedEventArgs e)
{
var textDiffView = this.FindDescendantOfType<TextDiffView>();
var presenter = textDiffView?.FindDescendantOfType<ThemedTextDiffPresenter>();
if (presenter == null)
return;
if (presenter.DataContext is Models.TextDiff combined)
combined.ScrollOffset = Vector.Zero;
else if (presenter.DataContext is ViewModels.TwoSideTextDiff twoSides)
twoSides.File = string.Empty; // Just to reset `SyncScrollOffset` without affect UI refresh.
(DataContext as ViewModels.DiffContext)?.ToggleFullTextDiff();
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoLastChange();
e.Handled = true;
}
}

View File

@@ -188,9 +188,9 @@ namespace SourceGit.Views
SetCurrentValue(IsScrollToTopVisibleProperty, startY >= rowHeight);
var clipWidth = dataGrid.Columns[0].ActualWidth - 4;
if (_lastGraphStartY != startY ||
_lastGraphClipWidth != clipWidth ||
_lastGraphRowHeight != rowHeight)
if (Math.Abs(_lastGraphStartY - startY) > 0.01 ||
Math.Abs(_lastGraphClipWidth - clipWidth) > 0.01 ||
Math.Abs(_lastGraphRowHeight - rowHeight) > 0.01)
{
_lastGraphStartY = startY;
_lastGraphClipWidth = clipWidth;
@@ -292,8 +292,6 @@ namespace SourceGit.Views
repo.ShowPopup(new ViewModels.CreateTag(repo, commit));
e.Handled = true;
}
return;
}
}
}
@@ -775,7 +773,7 @@ namespace SourceGit.Views
{
var folder = selected[0];
var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder.Path.ToString();
await repo.SaveCommitAsPatchAsync(commit, folderPath, 0);
await repo.SaveCommitAsPatchAsync(commit, folderPath);
}
}
catch (Exception exception)

View File

@@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters"
xmlns:v="using:SourceGit.Views"

View File

@@ -169,9 +169,6 @@ namespace SourceGit.Views
private void OnButtonActionClicked(object sender, RoutedEventArgs e)
{
if (DataContext is not ViewModels.InteractiveRebase vm)
return;
if (sender is not Button { DataContext: ViewModels.InteractiveRebaseItem item } button)
return;

View File

@@ -328,12 +328,12 @@ namespace SourceGit.Views
var item = new MenuItem();
item.Header = workspace.Name;
item.Icon = icon;
item.Click += (_, e) =>
item.Click += (_, ev) =>
{
if (!workspace.IsActive)
launcher.SwitchWorkspace(workspace);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(item);
@@ -343,10 +343,10 @@ namespace SourceGit.Views
var configure = new MenuItem();
configure.Header = App.Text("Workspace.Configure");
configure.Click += async (_, e) =>
configure.Click += async (_, ev) =>
{
await App.ShowDialog(new ViewModels.ConfigureWorkspace());
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(configure);
menu.Open(btn);

View File

@@ -261,28 +261,28 @@ namespace SourceGit.Views
var close = new MenuItem();
close.Header = App.Text("PageTabBar.Tab.Close");
close.Tag = OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W";
close.Click += (_, e) =>
close.Click += (_, ev) =>
{
vm.CloseTab(page);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(close);
var closeOthers = new MenuItem();
closeOthers.Header = App.Text("PageTabBar.Tab.CloseOther");
closeOthers.Click += (_, e) =>
closeOthers.Click += (_, ev) =>
{
vm.CloseOtherTabs();
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(closeOthers);
var closeRight = new MenuItem();
closeRight.Header = App.Text("PageTabBar.Tab.CloseRight");
closeRight.Click += (_, e) =>
closeRight.Click += (_, ev) =>
{
vm.CloseRightTabs();
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(closeRight);
@@ -302,10 +302,10 @@ namespace SourceGit.Views
var dupIdx = i;
var setter = new MenuItem();
setter.Header = icon;
setter.Click += (_, e) =>
setter.Click += (_, ev) =>
{
page.Node.Bookmark = dupIdx;
e.Handled = true;
ev.Handled = true;
};
bookmark.Items.Add(setter);
}
@@ -315,10 +315,10 @@ namespace SourceGit.Views
var copyPath = new MenuItem();
copyPath.Header = App.Text("PageTabBar.Tab.CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyPath.Click += async (_, e) =>
copyPath.Click += async (_, ev) =>
{
await page.CopyPathAsync();
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
@@ -429,7 +429,7 @@ namespace SourceGit.Views
}
private bool _pressedTab = false;
private Point _pressedTabPosition = new Point();
private Point _pressedTabPosition = new();
private bool _startDragTab = false;
}
}

View File

@@ -46,7 +46,7 @@
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preferences.General}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preferences.General.Locale}"
HorizontalAlignment="Right"
@@ -133,21 +133,31 @@
</Grid>
<CheckBox Grid.Row="5" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.ShowChangesPageByDefault}"
IsChecked="{Binding ShowLocalChangesByDefault, Mode=TwoWay}"/>
<CheckBox Grid.Row="6" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.ShowAuthorTime}"
IsChecked="{Binding ShowAuthorTimeInGraph, Mode=TwoWay}"/>
<CheckBox Grid.Row="6" Grid.Column="1"
<CheckBox Grid.Row="7" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.ShowTagsInGraph}"
IsChecked="{Binding ShowTagsInGraph, Mode=TwoWay}"/>
<CheckBox Grid.Row="7" Grid.Column="1"
<CheckBox Grid.Row="8" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.ShowChildren}"
IsChecked="{Binding ShowChildren, Mode=TwoWay}"/>
<CheckBox Grid.Row="8" Grid.Column="1"
<CheckBox Grid.Row="9" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.EnableCompactFolders}"
IsChecked="{Binding EnableCompactFoldersInChangesTree, Mode=TwoWay}"/>
<CheckBox Grid.Row="10" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.General.Check4UpdatesOnStartup}"
IsVisible="{x:Static s:App.IsCheckForUpdateCommandVisible}"
@@ -159,7 +169,7 @@
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preferences.Appearance}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preferences.Appearance.Theme}"
HorizontalAlignment="Right"
@@ -266,11 +276,6 @@
IsChecked="{Binding UseFixedTabWidth, Mode=TwoWay}"/>
<CheckBox Grid.Row="8" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.Appearance.EnableCompactFolders}"
IsChecked="{Binding EnableCompactFoldersInChangesTree, Mode=TwoWay}"/>
<CheckBox Grid.Row="9" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preferences.Appearance.UseNativeWindowFrame}"
IsChecked="{Binding UseSystemWindowFrame, Mode=OneTime}"

View File

@@ -46,6 +46,7 @@
Margin="0,0,8,0"
Text="{DynamicResource Text.Pull.Branch}"/>
<ComboBox Grid.Row="1" Grid.Column="1"
x:Name="RemoteBranchesComboBox"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding RemoteBranches}"
@@ -60,12 +61,6 @@
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerTheme>
<ControlTheme TargetType="ComboBoxItem" x:DataType="m:Branch" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="TextSearch.Text" Value="{Binding Name}"/>
</ControlTheme>
</ComboBox.ItemContainerTheme>
</ComboBox>
<TextBlock Grid.Row="2" Grid.Column="0"

View File

@@ -1,4 +1,6 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace SourceGit.Views
{
@@ -7,6 +9,7 @@ namespace SourceGit.Views
public Pull()
{
InitializeComponent();
TextSearch.SetTextBinding(RemoteBranchesComboBox, new Binding("Name"));
}
}
}

View File

@@ -18,6 +18,7 @@
Margin="0,0,8,0"
Text="{DynamicResource Text.Push.Local}"/>
<ComboBox Grid.Row="0" Grid.Column="1"
x:Name="LocalBranchesComboBox"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding LocalBranches}"
@@ -32,12 +33,6 @@
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerTheme>
<ControlTheme TargetType="ComboBoxItem" x:DataType="m:Branch" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="TextSearch.Text" Value="{Binding Name}"/>
</ControlTheme>
</ComboBox.ItemContainerTheme>
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="0"
@@ -64,6 +59,7 @@
Margin="0,0,8,0"
Text="{DynamicResource Text.Push.To}"/>
<ComboBox Grid.Row="2" Grid.Column="1"
x:Name="RemoteBranchesComboBox"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding RemoteBranches}"
@@ -85,12 +81,6 @@
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerTheme>
<ControlTheme TargetType="ComboBoxItem" x:DataType="m:Branch" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="TextSearch.Text" Value="{Binding Name}"/>
</ControlTheme>
</ComboBox.ItemContainerTheme>
</ComboBox>
<CheckBox Grid.Row="3" Grid.Column="1"

View File

@@ -1,4 +1,6 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace SourceGit.Views
{
@@ -7,6 +9,8 @@ namespace SourceGit.Views
public Push()
{
InitializeComponent();
TextSearch.SetTextBinding(LocalBranchesComboBox, new Binding("Name"));
TextSearch.SetTextBinding(RemoteBranchesComboBox, new Binding("Name"));
}
}
}

View File

@@ -229,10 +229,10 @@ namespace SourceGit.Views
var copy = new MenuItem();
copy.Header = App.Text("Worktree.CopyPath");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += async (_, e) =>
copy.Click += async (_, ev) =>
{
await App.CopyTextAsync(worktree.FullPath);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copy);
@@ -459,10 +459,10 @@ namespace SourceGit.Views
reflog.Tag = "--reflog";
if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.Reflog))
reflog.Icon = App.CreateMenuIcon("Icons.Check");
reflog.Click += (_, e) =>
reflog.Click += (_, ev) =>
{
repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.Reflog);
e.Handled = true;
ev.Handled = true;
};
var firstParentOnly = new MenuItem();
@@ -470,10 +470,10 @@ namespace SourceGit.Views
firstParentOnly.Tag = "--first-parent";
if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly))
firstParentOnly.Icon = App.CreateMenuIcon("Icons.Check");
firstParentOnly.Click += (_, e) =>
firstParentOnly.Click += (_, ev) =>
{
repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.FirstParentOnly);
e.Handled = true;
ev.Handled = true;
};
var simplifyByDecoration = new MenuItem();
@@ -481,10 +481,10 @@ namespace SourceGit.Views
simplifyByDecoration.Tag = "--simplify-by-decoration";
if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration))
simplifyByDecoration.Icon = App.CreateMenuIcon("Icons.Check");
simplifyByDecoration.Click += (_, e) =>
simplifyByDecoration.Click += (_, ev) =>
{
repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.SimplifyByDecoration);
e.Handled = true;
ev.Handled = true;
};
var order = new MenuItem();

View File

@@ -16,7 +16,7 @@ namespace SourceGit.Views
InitializeComponent();
}
private void OpenWithExternalTools(object sender, RoutedEventArgs e)
private void OpenWithExternalTools(object sender, RoutedEventArgs ev)
{
if (sender is Button button && DataContext is ViewModels.Repository repo)
{
@@ -100,7 +100,7 @@ namespace SourceGit.Views
}
menu.Open(button);
e.Handled = true;
ev.Handled = true;
}
}
@@ -185,7 +185,7 @@ namespace SourceGit.Views
}
}
private void OpenGitFlowMenu(object sender, RoutedEventArgs e)
private void OpenGitFlowMenu(object sender, RoutedEventArgs ev)
{
if (DataContext is ViewModels.Repository repo && sender is Control control)
{
@@ -248,10 +248,10 @@ namespace SourceGit.Views
menu.Open(control);
}
e.Handled = true;
ev.Handled = true;
}
private void OpenGitLFSMenu(object sender, RoutedEventArgs e)
private void OpenGitLFSMenu(object sender, RoutedEventArgs ev)
{
if (DataContext is ViewModels.Repository repo && sender is Control control)
{
@@ -387,7 +387,7 @@ namespace SourceGit.Views
menu.Open(control);
}
e.Handled = true;
ev.Handled = true;
}
private async void StartBisect(object sender, RoutedEventArgs e)
@@ -415,7 +415,7 @@ namespace SourceGit.Views
}
}
private void OpenCustomActionMenu(object sender, RoutedEventArgs e)
private void OpenCustomActionMenu(object sender, RoutedEventArgs ev)
{
if (DataContext is ViewModels.Repository repo && sender is Control control)
{
@@ -448,7 +448,7 @@ namespace SourceGit.Views
menu.Open(control);
}
e.Handled = true;
ev.Handled = true;
}
private async void OpenGitLogs(object sender, RoutedEventArgs e)

View File

@@ -100,7 +100,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Loading Status Icon -->

View File

@@ -20,9 +20,8 @@ namespace SourceGit.Views
if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: 1 } selected } vm &&
sender is ChangeCollectionView view)
{
var repo = vm.RepositoryPath;
var change = selected[0];
var changeFullPath = Native.OS.GetAbsPath(repo, change.Path);
var changeFullPath = vm.GetAbsPath(change.Path);
var menu = new ContextMenu();
var openWithMerger = new MenuItem();
@@ -65,13 +64,13 @@ namespace SourceGit.Views
copyFullPath.Header = App.Text("CopyFullPath");
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
copyFullPath.Click += async (_, e) =>
copyFullPath.Click += async (_, ev) =>
{
await App.CopyTextAsync(changeFullPath);
e.Handled = true;
ev.Handled = true;
};
menu.Items.Add(copyFullPath);
menu?.Open(view);
menu.Open(view);
}
e.Handled = true;
@@ -105,5 +104,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.RevisionCompare vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
@@ -24,7 +25,8 @@ namespace SourceGit.Views
DataContext is ViewModels.RevisionFileTreeNode { IsFolder: true } node)
{
var tree = this.FindAncestorOfType<RevisionFileTreeView>();
await tree?.ToggleNodeIsExpandedAsync(node);
if (tree != null)
await tree.ToggleNodeIsExpandedAsync(node);
}
e.Handled = true;
@@ -117,7 +119,8 @@ namespace SourceGit.Views
(node.IsExpanded && e.Key == Key.Left) || (!node.IsExpanded && e.Key == Key.Right))
{
var tree = this.FindAncestorOfType<RevisionFileTreeView>();
await tree?.ToggleNodeIsExpandedAsync(node);
if (tree != null)
await tree.ToggleNodeIsExpandedAsync(node);
e.Handled = true;
}
else if (e.Key == Key.C &&
@@ -330,7 +333,7 @@ namespace SourceGit.Views
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.CommitDetail { Repository: ViewModels.Repository repo, Commit: Models.Commit commit } vm &&
if (DataContext is ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm &&
sender is Grid { DataContext: ViewModels.RevisionFileTreeNode { Backend: { } obj } } grid)
{
var menu = obj.Type switch

View File

@@ -26,6 +26,7 @@
Margin="0,0,8,0"
Text="{DynamicResource Text.SetUpstream.Upstream}"/>
<ComboBox Grid.Row="1" Grid.Column="1"
x:Name="RemoteBranchesComboBox"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding RemoteBranches}"
@@ -40,12 +41,6 @@
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerTheme>
<ControlTheme TargetType="ComboBoxItem" x:DataType="m:Branch" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="TextSearch.Text" Value="{Binding Name}"/>
</ControlTheme>
</ComboBox.ItemContainerTheme>
</ComboBox>
<CheckBox Grid.Row="2" Grid.Column="1"

View File

@@ -1,4 +1,6 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace SourceGit.Views
{
@@ -7,6 +9,7 @@ namespace SourceGit.Views
public SetUpstream()
{
InitializeComponent();
TextSearch.SetTextBinding(RemoteBranchesComboBox, new Binding("Name"));
}
}
}

View File

@@ -131,7 +131,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding Changes}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
</Grid>

View File

@@ -71,7 +71,7 @@ namespace SourceGit.Views
var patch = new MenuItem();
patch.Header = App.Text("StashCM.SaveAsPatch");
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) =>
patch.Click += async (_, ev) =>
{
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
if (storageProvider == null)
@@ -86,7 +86,7 @@ namespace SourceGit.Views
if (storageFile != null)
await vm.SaveStashAsPathAsync(stash, storageFile.Path.LocalPath);
e.Handled = true;
ev.Handled = true;
};
var copy = new MenuItem();
@@ -126,8 +126,8 @@ namespace SourceGit.Views
if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: 1 } selected } vm &&
sender is ChangeCollectionView view)
{
var repo = vm.RepositoryPath;
var change = selected[0];
var fullPath = vm.GetAbsPath(change.Path);
var openWithMerger = new MenuItem();
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
@@ -139,7 +139,6 @@ namespace SourceGit.Views
ev.Handled = true;
};
var fullPath = Native.OS.GetAbsPath(repo, change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = App.CreateMenuIcon("Icons.Explore");
@@ -173,10 +172,10 @@ namespace SourceGit.Views
copyFullPath.Header = App.Text("CopyFullPath");
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
copyFullPath.Click += async (_, e) =>
copyFullPath.Click += async (_, ev) =>
{
await App.CopyTextAsync(fullPath);
e.Handled = true;
ev.Handled = true;
};
var menu = new ContextMenu();
@@ -192,5 +191,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.StashesPage vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -280,10 +280,10 @@ namespace SourceGit.Views
var item = new MenuItem();
item.Icon = App.CreateMenuIcon("Icons.Action");
item.Header = label;
item.Click += async (_, e) =>
item.Click += async (_, ev) =>
{
await repo.ExecCustomActionAsync(dup, tag);
e.Handled = true;
ev.Handled = true;
};
custom.Items.Add(item);
@@ -340,12 +340,12 @@ namespace SourceGit.Views
var deleteMultiple = new MenuItem();
deleteMultiple.Header = App.Text("TagCM.DeleteMultiple", selected.Count);
deleteMultiple.Icon = App.CreateMenuIcon("Icons.Clear");
deleteMultiple.Click += (_, e) =>
deleteMultiple.Click += (_, ev) =>
{
if (repo.CanCreatePopup())
repo.ShowPopup(new ViewModels.DeleteMultipleTags(repo, selected));
e.Handled = true;
ev.Handled = true;
};
var menu = new ContextMenu();

View File

@@ -2,20 +2,18 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.TextDiffView"
x:Name="ThisControl"
x:DataType="vm:TextDiffContext"
Background="{DynamicResource Brush.Contents}">
<Grid>
<ContentControl x:Name="Editor">
<ContentControl Content="{Binding}">
<ContentControl.DataTemplates>
<DataTemplate DataType="m:TextDiff">
<DataTemplate DataType="vm:CombinedTextDiff">
<Grid ColumnDefinitions="*,1,8">
<v:CombinedTextDiffPresenter Grid.Column="0"
x:Name="CombinedPresenter"
FileName="{Binding File}"
Foreground="{DynamicResource Brush.FG1}"
LineBrush="{DynamicResource Brush.Border2}"
@@ -31,14 +29,14 @@
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableDiffViewWordWrap}"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
EnableChunkSelection="{Binding EnableChunkOption}"
SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/>
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
<v:TextDiffViewMinimap Grid.Column="2"
DisplayRange="{Binding #CombinedPresenter.DisplayRange}"
DisplayRange="{Binding DisplayRange, Mode=OneWay}"
AddedLineBrush="{DynamicResource Brush.Diff.AddedBG}"
DeletedLineBrush="{DynamicResource Brush.Diff.DeletedBG}"/>
</Grid>
@@ -47,7 +45,6 @@
<DataTemplate DataType="vm:TwoSideTextDiff">
<Grid ColumnDefinitions="*,1,*,1,12">
<v:SingleSideTextDiffPresenter Grid.Column="0"
x:Name="LeftSidePresenter"
IsOld="True"
FileName="{Binding File}"
Foreground="{DynamicResource Brush.FG1}"
@@ -64,9 +61,9 @@
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="False"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
EnableChunkSelection="{Binding EnableChunkOption, Mode=OneWay}"
SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/>
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
@@ -87,14 +84,14 @@
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="False"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
EnableChunkSelection="{Binding EnableChunkOption, Mode=OneWay}"
SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}"
BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/>
<Rectangle Grid.Column="3" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
<v:TextDiffViewMinimap Grid.Column="4"
DisplayRange="{Binding #LeftSidePresenter.DisplayRange}"
DisplayRange="{Binding DisplayRange, Mode=OneWay}"
AddedLineBrush="{DynamicResource Brush.Diff.AddedBG}"
DeletedLineBrush="{DynamicResource Brush.Diff.DeletedBG}"/>
</Grid>
@@ -103,7 +100,7 @@
</ContentControl>
<StackPanel x:Name="Popup" IsVisible="False" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" Effect="drop-shadow(0 0 8 #80000000)">
<Button Classes="flat" Click="OnStageChunk" HotKey="{OnPlatform Ctrl+S, macOS=⌘+S}" IsVisible="{Binding #ThisControl.IsUnstagedChange}">
<Button Classes="flat" Click="OnStageChunk" HotKey="{OnPlatform Ctrl+S, macOS=⌘+S}" IsVisible="{Binding IsUnstaged}">
<TextBlock>
<Run Text="{DynamicResource Text.Hunk.Stage}"/>
<Run Text=" "/>
@@ -114,7 +111,7 @@
</TextBlock>
</Button>
<Button Classes="flat" Click="OnUnstageChunk" HotKey="{OnPlatform Ctrl+U, macOS=⌘+U}" IsVisible="{Binding #ThisControl.IsUnstagedChange, Converter={x:Static BoolConverters.Not}}">
<Button Classes="flat" Click="OnUnstageChunk" HotKey="{OnPlatform Ctrl+U, macOS=⌘+U}" IsVisible="{Binding IsUnstaged, Converter={x:Static BoolConverters.Not}}">
<TextBlock>
<Run Text="{DynamicResource Text.Hunk.Unstage}"/>
<Run Text=" "/>
@@ -125,7 +122,7 @@
</TextBlock>
</Button>
<Button Classes="flat" Margin="8,0,0,0" HotKey="{OnPlatform Ctrl+D, macOS=⌘+D}" Click="OnDiscardChunk" IsVisible="{Binding #ThisControl.IsUnstagedChange}">
<Button Classes="flat" Margin="8,0,0,0" HotKey="{OnPlatform Ctrl+D, macOS=⌘+D}" Click="OnDiscardChunk" IsVisible="{Binding IsUnstaged}">
<TextBlock>
<Run Text="{DynamicResource Text.Hunk.Discard}"/>
<Run Text=" "/>

View File

@@ -5,6 +5,7 @@ using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
@@ -23,41 +24,6 @@ using AvaloniaEdit.Utils;
namespace SourceGit.Views
{
public class TextDiffViewChunk
{
public double Y { get; set; } = 0.0;
public double Height { get; set; } = 0.0;
public int StartIdx { get; set; } = 0;
public int EndIdx { get; set; } = 0;
public bool Combined { get; set; } = true;
public bool IsOldSide { get; set; } = false;
public bool ShouldReplace(TextDiffViewChunk old)
{
if (old == null)
return true;
return Math.Abs(Y - old.Y) > 0.001 ||
Math.Abs(Height - old.Height) > 0.001 ||
StartIdx != old.StartIdx ||
EndIdx != old.EndIdx ||
Combined != old.Combined ||
IsOldSide != old.IsOldSide;
}
}
public record TextDiffViewRange
{
public int StartIdx { get; set; } = 0;
public int EndIdx { get; set; } = 0;
public TextDiffViewRange(int startIdx, int endIdx)
{
StartIdx = startIdx;
EndIdx = endIdx;
}
}
public class ThemedTextDiffPresenter : TextEditor
{
public class VerticalSeparatorMargin : AbstractMargin
@@ -134,13 +100,12 @@ namespace SourceGit.Views
protected override Size MeasureOverride(Size availableSize)
{
var presenter = this.FindAncestorOfType<ThemedTextDiffPresenter>();
if (presenter == null)
if (presenter is not { DataContext: ViewModels.TextDiffContext ctx })
return new Size(32, 0);
var maxLineNumber = presenter.GetMaxLineNumber();
var typeface = TextView.CreateTypeface();
var test = new FormattedText(
$"{maxLineNumber}",
$"{ctx.Data.MaxLineNumber}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
@@ -155,8 +120,8 @@ namespace SourceGit.Views
InvalidateMeasure();
}
private bool _usePresenter = false;
private bool _isOld = false;
private readonly bool _usePresenter = false;
private readonly bool _isOld = false;
}
public class LineModifyTypeMargin : AbstractMargin
@@ -256,8 +221,8 @@ namespace SourceGit.Views
return;
var changeBlock = _presenter.BlockNavigation?.GetCurrentBlock();
Brush changeBlockBG = new SolidColorBrush(Colors.Gray, 0.25);
Pen changeBlockFG = new Pen(Brushes.Gray);
var changeBlockBG = new SolidColorBrush(Colors.Gray, 0.25);
var changeBlockFG = new Pen(Brushes.Gray);
var lines = _presenter.GetLines();
var width = textView.Bounds.Width;
@@ -341,7 +306,7 @@ namespace SourceGit.Views
};
}
private ThemedTextDiffPresenter _presenter = null;
private readonly ThemedTextDiffPresenter _presenter = null;
}
public class LineStyleTransformer(ThemedTextDiffPresenter presenter) : DocumentColorizingTransformer
@@ -482,24 +447,15 @@ namespace SourceGit.Views
set => SetValue(EnableChunkSelectionProperty, value);
}
public static readonly StyledProperty<TextDiffViewChunk> SelectedChunkProperty =
AvaloniaProperty.Register<ThemedTextDiffPresenter, TextDiffViewChunk>(nameof(SelectedChunk));
public static readonly StyledProperty<ViewModels.TextDiffSelectedChunk> SelectedChunkProperty =
AvaloniaProperty.Register<ThemedTextDiffPresenter, ViewModels.TextDiffSelectedChunk>(nameof(SelectedChunk));
public TextDiffViewChunk SelectedChunk
public ViewModels.TextDiffSelectedChunk SelectedChunk
{
get => GetValue(SelectedChunkProperty);
set => SetValue(SelectedChunkProperty, value);
}
public static readonly StyledProperty<TextDiffViewRange> DisplayRangeProperty =
AvaloniaProperty.Register<ThemedTextDiffPresenter, TextDiffViewRange>(nameof(DisplayRange), new TextDiffViewRange(0, 0));
public TextDiffViewRange DisplayRange
{
get => GetValue(DisplayRangeProperty);
set => SetValue(DisplayRangeProperty, value);
}
public static readonly StyledProperty<ViewModels.BlockNavigation> BlockNavigationProperty =
AvaloniaProperty.Register<ThemedTextDiffPresenter, ViewModels.BlockNavigation>(nameof(BlockNavigation));
@@ -534,19 +490,23 @@ namespace SourceGit.Views
return [];
}
public virtual int GetMaxLineNumber()
{
return 0;
}
public virtual void UpdateSelectedChunk(double y)
{
}
public virtual void GotoFirstChange()
{
var blockNavigation = BlockNavigation;
var prev = blockNavigation?.GotoFirst();
var first = BlockNavigation?.GotoFirst();
if (first != null)
{
TextArea.Caret.Line = first.Start;
ScrollToLine(first.Start);
}
}
public virtual void GotoPrevChange()
{
var prev = BlockNavigation?.GotoPrev();
if (prev != null)
{
TextArea.Caret.Line = prev.Start;
@@ -554,106 +514,19 @@ namespace SourceGit.Views
}
}
public virtual void GotoPrevChange()
{
var blockNavigation = BlockNavigation;
if (blockNavigation != null)
{
var prev = blockNavigation.GotoPrev();
if (prev != null)
{
TextArea.Caret.Line = prev.Start;
ScrollToLine(prev.Start);
}
return;
}
var firstLineIdx = DisplayRange.StartIdx;
if (firstLineIdx <= 1)
return;
var lines = GetLines();
var firstLineType = lines[firstLineIdx].Type;
var prevLineType = lines[firstLineIdx - 1].Type;
var isChangeFirstLine = firstLineType != Models.TextDiffLineType.Normal && firstLineType != Models.TextDiffLineType.Indicator;
var isChangePrevLine = prevLineType != Models.TextDiffLineType.Normal && prevLineType != Models.TextDiffLineType.Indicator;
if (isChangeFirstLine && isChangePrevLine)
{
for (var i = firstLineIdx - 2; i >= 0; i--)
{
var prevType = lines[i].Type;
if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator)
{
ScrollToLine(i + 2);
return;
}
}
}
var findChange = false;
for (var i = firstLineIdx - 1; i >= 0; i--)
{
var prevType = lines[i].Type;
if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator)
{
if (findChange)
{
ScrollToLine(i + 2);
return;
}
}
else if (!findChange)
{
findChange = true;
}
}
}
public virtual void GotoNextChange()
{
var blockNavigation = BlockNavigation;
if (blockNavigation != null)
var next = BlockNavigation?.GotoNext();
if (next != null)
{
var next = blockNavigation.GotoNext();
if (next != null)
{
TextArea.Caret.Line = next.Start;
ScrollToLine(next.Start);
}
return;
}
var lines = GetLines();
var lastLineIdx = DisplayRange.EndIdx;
if (lastLineIdx >= lines.Count - 1)
return;
var lastLineType = lines[lastLineIdx].Type;
var findNormalLine = lastLineType == Models.TextDiffLineType.Normal || lastLineType == Models.TextDiffLineType.Indicator;
for (var idx = lastLineIdx + 1; idx < lines.Count; idx++)
{
var nextType = lines[idx].Type;
if (nextType is Models.TextDiffLineType.None or Models.TextDiffLineType.Added or Models.TextDiffLineType.Deleted)
{
if (findNormalLine)
{
ScrollToLine(idx + 1);
return;
}
}
else if (!findNormalLine)
{
findNormalLine = true;
}
TextArea.Caret.Line = next.Start;
ScrollToLine(next.Start);
}
}
public virtual void GotoLastChange()
{
var blockNavigation = BlockNavigation;
var next = blockNavigation?.GotoLast();
var next = BlockNavigation?.GotoLast();
if (next != null)
{
TextArea.Caret.Line = next.Start;
@@ -683,6 +556,7 @@ namespace SourceGit.Views
{
base.OnLoaded(e);
TextArea.Caret.PositionChanged += OnTextAreaCaretPositionChanged;
TextArea.TextView.ContextRequested += OnTextViewContextRequested;
TextArea.TextView.PointerEntered += OnTextViewPointerChanged;
TextArea.TextView.PointerMoved += OnTextViewPointerChanged;
@@ -701,6 +575,7 @@ namespace SourceGit.Views
TextArea.RemoveHandler(KeyDownEvent, OnTextAreaKeyDown);
TextArea.Caret.PositionChanged -= OnTextAreaCaretPositionChanged;
TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
TextArea.TextView.PointerEntered -= OnTextViewPointerChanged;
TextArea.TextView.PointerMoved -= OnTextViewPointerChanged;
@@ -746,12 +621,6 @@ namespace SourceGit.Views
}
else if (change.Property == BlockNavigationProperty)
{
if (change.OldValue is ViewModels.BlockNavigation oldValue)
oldValue.PropertyChanged -= OnBlockNavigationPropertyChanged;
if (change.NewValue is ViewModels.BlockNavigation newValue)
newValue.PropertyChanged += OnBlockNavigationPropertyChanged;
TextArea?.TextView?.Redraw();
}
}
@@ -771,10 +640,9 @@ namespace SourceGit.Views
base.OnKeyDown(e);
}
private void OnBlockNavigationPropertyChanged(object _1, PropertyChangedEventArgs e)
private void OnTextAreaCaretPositionChanged(object sender, EventArgs e)
{
if (e.PropertyName == "Current")
TextArea?.TextView?.Redraw();
BlockNavigation?.UpdateByCaretPosition(TextArea?.Caret?.Line ?? 0);
}
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
@@ -850,9 +718,15 @@ namespace SourceGit.Views
private void OnTextViewVisualLinesChanged(object sender, EventArgs e)
{
if (DataContext is not ViewModels.TextDiffContext ctx)
return;
if (ctx.IsSideBySide() && !IsOld)
return;
if (!TextArea.TextView.VisualLinesValid)
{
SetCurrentValue(DisplayRangeProperty, new TextDiffViewRange(0, 0));
ctx.DisplayRange = null;
return;
}
@@ -873,89 +747,15 @@ namespace SourceGit.Views
start = index;
}
SetCurrentValue(DisplayRangeProperty, new TextDiffViewRange(start, start + count));
ctx.DisplayRange = new ViewModels.TextDiffDisplayRange(start, start + count);
}
protected void TrySetChunk(TextDiffViewChunk chunk)
protected void TrySetChunk(ViewModels.TextDiffSelectedChunk chunk)
{
var old = SelectedChunk;
if (chunk == null)
{
if (old != null)
SetCurrentValue(SelectedChunkProperty, null);
return;
}
if (chunk.ShouldReplace(old))
if (ViewModels.TextDiffSelectedChunk.IsChanged(SelectedChunk, chunk))
SetCurrentValue(SelectedChunkProperty, chunk);
}
protected (int, int) FindRangeByIndex(List<Models.TextDiffLine> lines, int lineIdx)
{
var startIdx = -1;
var endIdx = -1;
var normalLineCount = 0;
var modifiedLineCount = 0;
for (int i = lineIdx; i >= 0; i--)
{
var line = lines[i];
if (line.Type == Models.TextDiffLineType.Indicator)
{
startIdx = i;
break;
}
if (line.Type == Models.TextDiffLineType.Normal)
{
normalLineCount++;
if (normalLineCount >= 2)
{
startIdx = i;
break;
}
}
else
{
normalLineCount = 0;
modifiedLineCount++;
}
}
normalLineCount = lines[lineIdx].Type == Models.TextDiffLineType.Normal ? 1 : 0;
for (int i = lineIdx + 1; i < lines.Count; i++)
{
var line = lines[i];
if (line.Type == Models.TextDiffLineType.Indicator)
{
endIdx = i;
break;
}
if (line.Type == Models.TextDiffLineType.Normal)
{
normalLineCount++;
if (normalLineCount >= 2)
{
endIdx = i;
break;
}
}
else
{
normalLineCount = 0;
modifiedLineCount++;
}
}
if (endIdx == -1)
endIdx = lines.Count - 1;
return modifiedLineCount > 0 ? (startIdx, endIdx) : (-1, -1);
}
private void UpdateTextMate()
{
if (UseSyntaxHighlighting)
@@ -1072,21 +872,14 @@ namespace SourceGit.Views
public override List<Models.TextDiffLine> GetLines()
{
if (DataContext is Models.TextDiff diff)
if (DataContext is ViewModels.CombinedTextDiff { Data: { } diff })
return diff.Lines;
return [];
}
public override int GetMaxLineNumber()
{
if (DataContext is Models.TextDiff diff)
return diff.MaxLineNumber;
return 0;
}
public override void UpdateSelectedChunk(double y)
{
if (DataContext is not Models.TextDiff diff)
if (DataContext is not ViewModels.CombinedTextDiff { Data: { } diff } combined)
return;
var view = TextArea.TextView;
@@ -1134,15 +927,7 @@ namespace SourceGit.Views
endLine.GetTextLineVisualYPosition(endLine.TextLines[^1], VisualYPosition.TextBottom) - view.VerticalOffset :
view.Bounds.Height;
TrySetChunk(new TextDiffViewChunk()
{
Y = rectStartY,
Height = rectEndY - rectStartY,
StartIdx = startIdx,
EndIdx = endIdx,
Combined = true,
IsOldSide = false,
});
TrySetChunk(new(rectStartY, rectEndY - rectStartY, startIdx, endIdx, true, false));
}
else
{
@@ -1170,7 +955,7 @@ namespace SourceGit.Views
return;
}
var (startIdx, endIdx) = FindRangeByIndex(diff.Lines, lineIdx);
var (startIdx, endIdx) = combined.FindRangeByIndex(diff.Lines, lineIdx);
if (startIdx == -1)
{
TrySetChunk(null);
@@ -1187,15 +972,7 @@ namespace SourceGit.Views
endLine.GetTextLineVisualYPosition(endLine.TextLines[^1], VisualYPosition.TextBottom) - view.VerticalOffset :
view.Bounds.Height;
TrySetChunk(new TextDiffViewChunk()
{
Y = rectStartY,
Height = rectEndY - rectStartY,
StartIdx = startIdx,
EndIdx = endIdx,
Combined = true,
IsOldSide = false,
});
TrySetChunk(new(rectStartY, rectEndY - rectStartY, startIdx, endIdx, true, false));
}
}
@@ -1223,10 +1000,10 @@ namespace SourceGit.Views
{
base.OnDataContextChanged(e);
if (DataContext is Models.TextDiff textDiff)
if (DataContext is ViewModels.CombinedTextDiff { Data: { } diff })
{
var builder = new StringBuilder();
foreach (var line in textDiff.Lines)
foreach (var line in diff.Lines)
{
if (line.Content.Length > 10000)
{
@@ -1279,35 +1056,28 @@ namespace SourceGit.Views
return [];
}
public override int GetMaxLineNumber()
{
if (DataContext is ViewModels.TwoSideTextDiff diff)
return diff.MaxLineNumber;
return 0;
}
public override void GotoFirstChange()
{
base.GotoFirstChange();
DirectSyncScrollOffset();
SyncScrollOffset();
}
public override void GotoPrevChange()
{
base.GotoPrevChange();
DirectSyncScrollOffset();
SyncScrollOffset();
}
public override void GotoNextChange()
{
base.GotoNextChange();
DirectSyncScrollOffset();
SyncScrollOffset();
}
public override void GotoLastChange()
{
base.GotoLastChange();
DirectSyncScrollOffset();
SyncScrollOffset();
}
public override void UpdateSelectedChunk(double y)
@@ -1315,10 +1085,6 @@ namespace SourceGit.Views
if (DataContext is not ViewModels.TwoSideTextDiff diff)
return;
var parent = this.FindAncestorOfType<TextDiffView>();
if (parent == null)
return;
var view = TextArea.TextView;
var lines = IsOld ? diff.Old : diff.New;
var selection = TextArea.Selection;
@@ -1365,22 +1131,10 @@ namespace SourceGit.Views
endLine.GetTextLineVisualYPosition(endLine.TextLines[^1], VisualYPosition.TextBottom) - view.VerticalOffset :
view.Bounds.Height;
diff.ConvertsToCombinedRange(parent.DataContext as Models.TextDiff, ref startIdx, ref endIdx, IsOld);
TrySetChunk(new TextDiffViewChunk()
{
Y = rectStartY,
Height = rectEndY - rectStartY,
StartIdx = startIdx,
EndIdx = endIdx,
Combined = false,
IsOldSide = IsOld,
});
return;
diff.ConvertsToCombinedRange(ref startIdx, ref endIdx, IsOld);
TrySetChunk(new(rectStartY, rectEndY - rectStartY, startIdx, endIdx, false, IsOld));
}
if (this.FindAncestorOfType<TextDiffView>()?.DataContext is Models.TextDiff textDiff)
else
{
var lineIdx = -1;
foreach (var line in view.VisualLines)
@@ -1406,7 +1160,7 @@ namespace SourceGit.Views
return;
}
var (startIdx, endIdx) = FindRangeByIndex(lines, lineIdx);
var (startIdx, endIdx) = diff.FindRangeByIndex(lines, lineIdx);
if (startIdx == -1)
{
TrySetChunk(null);
@@ -1423,15 +1177,21 @@ namespace SourceGit.Views
endLine.GetTextLineVisualYPosition(endLine.TextLines[^1], VisualYPosition.TextBottom) - view.VerticalOffset :
view.Bounds.Height;
TrySetChunk(new TextDiffViewChunk()
{
Y = rectStartY,
Height = rectEndY - rectStartY,
StartIdx = textDiff.Lines.IndexOf(lines[startIdx]),
EndIdx = endIdx == lines.Count - 1 ? textDiff.Lines.Count - 1 : textDiff.Lines.IndexOf(lines[endIdx]),
Combined = true,
IsOldSide = false,
});
diff.ConvertsToCombinedRange(ref startIdx, ref endIdx, IsOld);
TrySetChunk(new(rectStartY, rectEndY - rectStartY, startIdx, endIdx, true, false));
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == BlockNavigationProperty)
{
if (change.OldValue is ViewModels.BlockNavigation oldValue)
oldValue.PropertyChanged -= OnBlockNavigationPropertyChanged;
if (change.NewValue is ViewModels.BlockNavigation newValue)
newValue.PropertyChanged += OnBlockNavigationPropertyChanged;
}
}
@@ -1443,7 +1203,7 @@ namespace SourceGit.Views
if (_scrollViewer != null)
{
_scrollViewer.ScrollChanged += OnTextViewScrollChanged;
_scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.OneWay));
_scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("ScrollOffset", BindingMode.OneWay));
}
}
@@ -1498,22 +1258,28 @@ namespace SourceGit.Views
if (_scrollViewer == null || DataContext is not ViewModels.TwoSideTextDiff diff)
return;
if (diff.SyncScrollOffset.NearlyEquals(_scrollViewer.Offset))
if (diff.ScrollOffset.NearlyEquals(_scrollViewer.Offset))
return;
if (IsPointerOver || !e.OffsetDelta.NearlyEquals(Vector.Zero))
{
diff.SyncScrollOffset = _scrollViewer.Offset;
diff.ScrollOffset = _scrollViewer.Offset;
if (!TextArea.TextView.IsPointerOver)
TrySetChunk(null);
}
}
private void DirectSyncScrollOffset()
private void OnBlockNavigationPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ("Indicator".Equals(e.PropertyName, StringComparison.Ordinal))
TextArea?.TextView?.Redraw();
}
private void SyncScrollOffset()
{
if (_scrollViewer is not null && DataContext is ViewModels.TwoSideTextDiff diff)
diff.SyncScrollOffset = _scrollViewer.Offset;
diff.ScrollOffset = _scrollViewer.Offset;
}
private ScrollViewer _scrollViewer = null;
@@ -1539,10 +1305,10 @@ namespace SourceGit.Views
set => SetValue(DeletedLineBrushProperty, value);
}
public static readonly StyledProperty<TextDiffViewRange> DisplayRangeProperty =
AvaloniaProperty.Register<TextDiffViewMinimap, TextDiffViewRange>(nameof(DisplayRange), new TextDiffViewRange(0, 0));
public static readonly StyledProperty<ViewModels.TextDiffDisplayRange> DisplayRangeProperty =
AvaloniaProperty.Register<TextDiffViewMinimap, ViewModels.TextDiffDisplayRange>(nameof(DisplayRange));
public TextDiffViewRange DisplayRange
public ViewModels.TextDiffDisplayRange DisplayRange
{
get => GetValue(DisplayRangeProperty);
set => SetValue(DisplayRangeProperty, value);
@@ -1576,18 +1342,19 @@ namespace SourceGit.Views
RenderSingleSide(context, twoSideDiff.Old, 0, halfWidth);
RenderSingleSide(context, twoSideDiff.New, halfWidth, halfWidth);
}
else if (DataContext is Models.TextDiff diff)
else if (DataContext is ViewModels.CombinedTextDiff combined)
{
total = diff.Lines.Count;
RenderSingleSide(context, diff.Lines, 0, Bounds.Width);
var data = combined.Data;
total = data.Lines.Count;
RenderSingleSide(context, data.Lines, 0, Bounds.Width);
}
var range = DisplayRange;
if (range.EndIdx == 0)
if (range == null || range.End == 0)
return;
var startY = range.StartIdx / (total * 1.0) * Bounds.Height;
var endY = range.EndIdx / (total * 1.0) * Bounds.Height;
var startY = range.Start / (total * 1.0) * Bounds.Height;
var endY = range.End / (total * 1.0) * Bounds.Height;
var color = DisplayRangeColor;
var brush = new SolidColorBrush(color, 0.2);
var pen = new Pen(color.ToUInt32());
@@ -1639,189 +1406,53 @@ namespace SourceGit.Views
public partial class TextDiffView : UserControl
{
public static readonly StyledProperty<bool> UseSideBySideDiffProperty =
AvaloniaProperty.Register<TextDiffView, bool>(nameof(UseSideBySideDiff));
public static readonly StyledProperty<ViewModels.TextDiffSelectedChunk> SelectedChunkProperty =
AvaloniaProperty.Register<TextDiffView, ViewModels.TextDiffSelectedChunk>(nameof(SelectedChunk));
public bool UseSideBySideDiff
{
get => GetValue(UseSideBySideDiffProperty);
set => SetValue(UseSideBySideDiffProperty, value);
}
public static readonly StyledProperty<TextDiffViewChunk> SelectedChunkProperty =
AvaloniaProperty.Register<TextDiffView, TextDiffViewChunk>(nameof(SelectedChunk));
public TextDiffViewChunk SelectedChunk
public ViewModels.TextDiffSelectedChunk SelectedChunk
{
get => GetValue(SelectedChunkProperty);
set => SetValue(SelectedChunkProperty, value);
}
public static readonly StyledProperty<bool> IsUnstagedChangeProperty =
AvaloniaProperty.Register<TextDiffView, bool>(nameof(IsUnstagedChange));
public bool IsUnstagedChange
{
get => GetValue(IsUnstagedChangeProperty);
set => SetValue(IsUnstagedChangeProperty, value);
}
public static readonly StyledProperty<bool> EnableChunkSelectionProperty =
AvaloniaProperty.Register<TextDiffView, bool>(nameof(EnableChunkSelection));
public bool EnableChunkSelection
{
get => GetValue(EnableChunkSelectionProperty);
set => SetValue(EnableChunkSelectionProperty, value);
}
public static readonly StyledProperty<bool> UseBlockNavigationProperty =
AvaloniaProperty.Register<TextDiffView, bool>(nameof(UseBlockNavigation));
public bool UseBlockNavigation
{
get => GetValue(UseBlockNavigationProperty);
set => SetValue(UseBlockNavigationProperty, value);
}
public static readonly StyledProperty<ViewModels.BlockNavigation> BlockNavigationProperty =
AvaloniaProperty.Register<TextDiffView, ViewModels.BlockNavigation>(nameof(BlockNavigation));
public ViewModels.BlockNavigation BlockNavigation
{
get => GetValue(BlockNavigationProperty);
set => SetValue(BlockNavigationProperty, value);
}
public static readonly RoutedEvent<RoutedEventArgs> BlockNavigationChangedEvent =
RoutedEvent.Register<TextDiffView, RoutedEventArgs>(nameof(BlockNavigationChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
public event EventHandler<RoutedEventArgs> BlockNavigationChanged
{
add { AddHandler(BlockNavigationChangedEvent, value); }
remove { RemoveHandler(BlockNavigationChangedEvent, value); }
}
static TextDiffView()
{
UseSideBySideDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
{
v.RefreshContent(v.DataContext as Models.TextDiff, false);
});
UseBlockNavigationProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
{
v.RefreshBlockNavigation();
});
SelectedChunkProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
{
var chunk = v.SelectedChunk;
if (chunk == null)
{
v.Popup.IsVisible = false;
return;
}
var top = chunk.Y + (chunk.Height >= 36 ? 8 : 2);
var right = (chunk.Combined || !chunk.IsOldSide) ? 26 : (v.Bounds.Width * 0.5f) + 26;
v.Popup.Margin = new Thickness(0, top, right, 0);
v.Popup.IsVisible = true;
});
}
public TextDiffView()
{
InitializeComponent();
}
public void GotoFirstChange()
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoFirstChange();
TryRaiseBlockNavigationChanged();
}
base.OnPropertyChanged(change);
public void GotoPrevChange()
{
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoPrevChange();
TryRaiseBlockNavigationChanged();
}
public void GotoNextChange()
{
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoNextChange();
TryRaiseBlockNavigationChanged();
}
public void GotoLastChange()
{
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoLastChange();
TryRaiseBlockNavigationChanged();
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
RefreshContent(DataContext as Models.TextDiff);
if (change.Property == SelectedChunkProperty)
{
if (SelectedChunk is { } chunk)
{
var top = chunk.Y + (chunk.Height >= 36 ? 8 : 2);
var right = (chunk.Combined || !chunk.IsOldSide) ? 26 : (Bounds.Width * 0.5f) + 26;
Popup.Margin = new Thickness(0, top, right, 0);
Popup.IsVisible = true;
}
else
{
Popup.IsVisible = false;
}
}
}
protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
if (SelectedChunk != null)
SetCurrentValue(SelectedChunkProperty, null);
}
private void RefreshContent(Models.TextDiff diff, bool keepScrollOffset = true)
{
if (SelectedChunk != null)
SetCurrentValue(SelectedChunkProperty, null);
if (diff == null)
{
Editor.Content = null;
GC.Collect();
return;
}
if (UseSideBySideDiff)
{
var previousContent = Editor.Content as ViewModels.TwoSideTextDiff;
Editor.Content = new ViewModels.TwoSideTextDiff(diff, keepScrollOffset ? previousContent : null);
}
else
{
if (!keepScrollOffset)
diff.ScrollOffset = Vector.Zero;
Editor.Content = diff;
}
RefreshBlockNavigation();
IsUnstagedChange = diff.Option.IsUnstaged;
EnableChunkSelection = diff.Option.WorkingCopyChange != null;
}
private void RefreshBlockNavigation()
{
if (UseBlockNavigation)
BlockNavigation = new ViewModels.BlockNavigation(Editor.Content);
else
BlockNavigation = null;
TryRaiseBlockNavigationChanged();
if (DataContext is ViewModels.TextDiffContext ctx)
ctx.SelectedChunk = null;
}
private async void OnStageChunk(object _1, RoutedEventArgs _2)
{
var chunk = SelectedChunk;
if (chunk == null)
if (DataContext is not ViewModels.TextDiffContext { SelectedChunk: { } chunk, Data: { } diff })
return;
var diff = DataContext as Models.TextDiff;
var change = diff?.Option.WorkingCopyChange;
var change = diff.Option.WorkingCopyChange;
if (change == null)
return;
@@ -1830,7 +1461,6 @@ namespace SourceGit.Views
return;
var repoView = this.FindAncestorOfType<Repository>();
if (repoView?.DataContext is not ViewModels.Repository repo)
return;
@@ -1849,16 +1479,16 @@ namespace SourceGit.Views
}
else if (chunk.Combined)
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(diff.Repo, change.Path).GetResultAsync();
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelection(change, treeGuid, selection, false, tmpFile);
}
else
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(diff.Repo, change.Path).GetResultAsync();
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, false, chunk.IsOldSide, tmpFile);
}
await new Commands.Apply(diff.Repo, tmpFile, true, "nowarn", "--cache --index").ExecAsync();
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--cache --index").ExecAsync();
File.Delete(tmpFile);
}
@@ -1868,13 +1498,10 @@ namespace SourceGit.Views
private async void OnUnstageChunk(object _1, RoutedEventArgs _2)
{
var chunk = SelectedChunk;
if (chunk == null)
if (DataContext is not ViewModels.TextDiffContext { SelectedChunk: { } chunk, Data: { } diff })
return;
var diff = DataContext as Models.TextDiff;
var change = diff?.Option.WorkingCopyChange;
var change = diff.Option.WorkingCopyChange;
if (change == null)
return;
@@ -1883,7 +1510,6 @@ namespace SourceGit.Views
return;
var repoView = this.FindAncestorOfType<Repository>();
if (repoView?.DataContext is not ViewModels.Repository repo)
return;
@@ -1898,7 +1524,7 @@ namespace SourceGit.Views
}
else
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(diff.Repo, change.Path).GetResultAsync();
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
var tmpFile = Path.GetTempFileName();
if (change.Index == Models.ChangeState.Added)
diff.GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile);
@@ -1907,7 +1533,7 @@ namespace SourceGit.Views
else
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
await new Commands.Apply(diff.Repo, tmpFile, true, "nowarn", "--cache --index --reverse").ExecAsync();
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--cache --index --reverse").ExecAsync();
File.Delete(tmpFile);
}
@@ -1917,13 +1543,10 @@ namespace SourceGit.Views
private async void OnDiscardChunk(object _1, RoutedEventArgs _2)
{
var chunk = SelectedChunk;
if (chunk == null)
if (DataContext is not ViewModels.TextDiffContext { SelectedChunk: { } chunk, Data: { } diff })
return;
var diff = DataContext as Models.TextDiff;
var change = diff?.Option.WorkingCopyChange;
var change = diff.Option.WorkingCopyChange;
if (change == null)
return;
@@ -1951,27 +1574,21 @@ namespace SourceGit.Views
}
else if (chunk.Combined)
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(diff.Repo, change.Path).GetResultAsync();
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile);
}
else
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(diff.Repo, change.Path).GetResultAsync();
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
}
await new Commands.Apply(diff.Repo, tmpFile, true, "nowarn", "--reverse").ExecAsync();
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--reverse").ExecAsync();
File.Delete(tmpFile);
}
repo.MarkWorkingCopyDirtyManually();
repo.SetWatcherEnabled(true);
}
private void TryRaiseBlockNavigationChanged()
{
if (UseBlockNavigation)
RaiseEvent(new RoutedEventArgs(BlockNavigationChangedEvent));
}
}
}

View File

@@ -101,7 +101,7 @@ namespace SourceGit.Views
}
}
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e)
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs ev)
{
if (sender is Grid { DataContext: ViewModels.RepositoryNode node } grid)
{
@@ -204,7 +204,7 @@ namespace SourceGit.Views
menu.Open(grid);
}
e.Handled = true;
ev.Handled = true;
}
private void OnPointerPressedTreeNode(object sender, PointerPressedEventArgs e)

View File

@@ -113,6 +113,18 @@ namespace SourceGit.Views
vm.OpenWithDefaultEditor(vm.SelectedUnstaged[0]);
e.Handled = true;
}
else if (e.Key is Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
vm.SelectedUnstaged is { Count: 1 })
{
var change = vm.SelectedUnstaged[0];
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
@@ -134,6 +146,18 @@ namespace SourceGit.Views
vm.OpenWithDefaultEditor(vm.SelectedStaged[0]);
e.Handled = true;
}
else if (e.Key is Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
vm.SelectedStaged is { Count: 1 })
{
var change = vm.SelectedStaged[0];
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
@@ -415,7 +439,7 @@ namespace SourceGit.Views
}
else
{
var isRooted = change.Path.IndexOf('/') <= 0;
var isRooted = change.Path!.IndexOf('/') <= 0;
var singleFile = new MenuItem();
singleFile.Header = App.Text("WorkingCopy.AddToGitIgnore.SingleFile");
singleFile.Click += (_, e) =>
@@ -1150,7 +1174,7 @@ namespace SourceGit.Views
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) =>
{
var storageProvider = TopLevel.GetTopLevel(this).StorageProvider;
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
if (storageProvider == null)
return;