diff --git a/README.md b/README.md index d63886ed..aacf222e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ * Supports Windows/macOS/Linux * Opensource/Free * Fast -* Deutsch/English/Español/Français/Italiano/Português/Русский/简体中文/繁體中文 +* Deutsch/English/Español/Français/Italiano/Português/Русский/简体中文/繁體中文/日本語 * Built-in light/dark themes * Customize theme * Visual commit graph diff --git a/TRANSLATION.md b/TRANSLATION.md index a96f0fc6..b344e6a3 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,13 +6,15 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.92%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.65%25-yellow)
Missing keys in de_DE.axaml - Text.BranchUpstreamInvalid - Text.Configure.CustomAction.WaitForExit +- Text.Configure.IssueTracker.AddSampleAzure +- Text.CopyFullPath - Text.Diff.First - Text.Diff.Last - Text.Preferences.AI.Streaming @@ -22,27 +24,31 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.Preferences.General.ShowTagsInGraph - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-%E2%88%9A-brightgreen) -### ![it__IT](https://img.shields.io/badge/it__IT-99.87%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-99.73%25-yellow)
Missing keys in it_IT.axaml +- Text.CopyFullPath - Text.Preferences.General.ShowTagsInGraph
-### ![pt__BR](https://img.shields.io/badge/pt__BR-91.12%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-99.73%25-yellow) + +
+Missing keys in ja_JP.axaml + +- Text.Repository.FilterCommits +- Text.Repository.Tags.OrderByNameDes + +
+ +### ![pt__BR](https://img.shields.io/badge/pt__BR-90.98%25-yellow)
Missing keys in pt_BR.axaml @@ -65,6 +71,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.WaitForExit - Text.Configure.IssueTracker.AddSampleGiteeIssue - Text.Configure.IssueTracker.AddSampleGiteePullRequest +- Text.CopyFullPath - Text.CreateBranch.Name.WarnSpace - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup diff --git a/VERSION b/VERSION index e78345d1..8283ecf3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.10 \ No newline at end of file +2025.12 \ No newline at end of file diff --git a/build/resources/deb/DEBIAN/control b/build/resources/deb/DEBIAN/control index f553db8b..71786b43 100755 --- a/build/resources/deb/DEBIAN/control +++ b/build/resources/deb/DEBIAN/control @@ -1,7 +1,8 @@ Package: sourcegit -Version: 8.23 +Version: 2025.10 Priority: optional Depends: libx11-6, libice6, libsm6, libicu | libicu76 | libicu74 | libicu72 | libicu71 | libicu70 | libicu69 | libicu68 | libicu67 | libicu66 | libicu65 | libicu63 | libicu60 | libicu57 | libicu55 | libicu52, xdg-utils Architecture: amd64 +Installed-Size: 60440 Maintainer: longshuang@msn.cn Description: Open-source & Free Git GUI Client diff --git a/build/scripts/package.linux.sh b/build/scripts/package.linux.sh index 5abb058b..1b4adbdc 100755 --- a/build/scripts/package.linux.sh +++ b/build/scripts/package.linux.sh @@ -56,8 +56,15 @@ cp -f SourceGit/* resources/deb/opt/sourcegit ln -rsf resources/deb/opt/sourcegit/sourcegit resources/deb/usr/bin cp -r resources/_common/applications resources/deb/usr/share cp -r resources/_common/icons resources/deb/usr/share -sed -i -e "s/^Version:.*/Version: $VERSION/" -e "s/^Architecture:.*/Architecture: $arch/" resources/deb/DEBIAN/control -dpkg-deb --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb" +# Calculate installed size in KB +installed_size=$(du -sk resources/deb | cut -f1) +# Update the control file +sed -i -e "s/^Version:.*/Version: $VERSION/" \ + -e "s/^Architecture:.*/Architecture: $arch/" \ + -e "s/^Installed-Size:.*/Installed-Size: $installed_size/" \ + resources/deb/DEBIAN/control +# Build deb package with gzip compression +dpkg-deb -Zgzip --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb" rpmbuild -bb --target="$target" resources/rpm/SPECS/build.spec --define "_topdir $(pwd)/resources/rpm" --define "_version $VERSION" mv "resources/rpm/RPMS/$target/sourcegit-$VERSION-1.$target.rpm" ./ diff --git a/src/App.Commands.cs b/src/App.Commands.cs index 85a75829..3733587a 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Input; using Avalonia.Controls; @@ -37,16 +37,15 @@ namespace SourceGit } } - public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences())); - public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys())); - public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir)); - public static readonly Command OpenAboutCommand = new Command(_ => OpenDialog(new Views.About())); - public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true)); - public static readonly Command QuitCommand = new Command(_ => Quit(0)); - public static readonly Command CopyTextBlockCommand = new Command(p => + public static readonly Command OpenPreferencesCommand = new(_ => OpenDialog(new Views.Preferences())); + public static readonly Command OpenHotkeysCommand = new(_ => OpenDialog(new Views.Hotkeys())); + public static readonly Command OpenAppDataDirCommand = new(_ => Native.OS.OpenInFileManager(Native.OS.DataDir)); + public static readonly Command OpenAboutCommand = new(_ => OpenDialog(new Views.About())); + public static readonly Command CheckForUpdateCommand = new(_ => (Current as App)?.Check4Update(true)); + public static readonly Command QuitCommand = new(_ => Quit(0)); + public static readonly Command CopyTextBlockCommand = new(p => { - var textBlock = p as TextBlock; - if (textBlock == null) + if (p is not TextBlock textBlock) return; if (textBlock.Inlines is { Count: > 0 } inlines) diff --git a/src/App.axaml b/src/App.axaml index 76d4baa8..fdfa8e07 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -20,6 +20,7 @@ + diff --git a/src/App.axaml.cs b/src/App.axaml.cs index af5e6177..f6a5fb22 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -37,7 +37,6 @@ namespace SourceGit TaskScheduler.UnobservedTaskException += (_, e) => { - LogException(e.Exception); e.SetObserved(); }; @@ -284,10 +283,12 @@ namespace SourceGit public static Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { - var icon = new Avalonia.Controls.Shapes.Path(); - icon.Width = 12; - icon.Height = 12; - icon.Stretch = Stretch.Uniform; + var icon = new Avalonia.Controls.Shapes.Path + { + Width = 12, + Height = 12, + Stretch = Stretch.Uniform + }; var geo = Current?.FindResource(key) as StreamGeometry; if (geo != null) @@ -430,7 +431,7 @@ namespace SourceGit if (!File.Exists(doneFile)) return true; - var done = File.ReadAllText(doneFile).Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + var done = File.ReadAllText(doneFile).Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries); if (done.Length > collection.Jobs.Count) return true; @@ -560,8 +561,22 @@ namespace SourceGit foreach (var part in parts) { var t = part.Trim(); - if (!string.IsNullOrEmpty(t)) - trimmed.Add(t); + if (string.IsNullOrEmpty(t)) + continue; + + // Collapse multiple spaces into single space + var prevChar = '\0'; + var sb = new StringBuilder(); + + foreach (var c in t) + { + if (c == ' ' && prevChar == ' ') + continue; + sb.Append(c); + prevChar = c; + } + + trimmed.Add(sb.ToString()); } return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty; diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs index e311a88e..c3206767 100644 --- a/src/Commands/CompareRevisions.cs +++ b/src/Commands/CompareRevisions.cs @@ -6,8 +6,10 @@ namespace SourceGit.Commands { public partial class CompareRevisions : Command { - [GeneratedRegex(@"^([MADRC])\s+(.+)$")] + [GeneratedRegex(@"^([MADC])\s+(.+)$")] private static partial Regex REG_FORMAT(); + [GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")] + private static partial Regex REG_RENAME_FORMAT(); public CompareRevisions(string repo, string start, string end) { @@ -38,7 +40,17 @@ namespace SourceGit.Commands { var match = REG_FORMAT().Match(line); if (!match.Success) + { + match = REG_RENAME_FORMAT().Match(line); + if (match.Success) + { + var renamed = new Models.Change() { Path = match.Groups[1].Value }; + renamed.Set(Models.ChangeState.Renamed); + _changes.Add(renamed); + } + return; + } var change = new Models.Change() { Path = match.Groups[2].Value }; var status = match.Groups[1].Value; @@ -57,10 +69,6 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Deleted); _changes.Add(change); break; - case 'R': - change.Set(Models.ChangeState.Renamed); - _changes.Add(change); - break; case 'C': change.Set(Models.ChangeState.Copied); _changes.Add(change); diff --git a/src/Commands/QueryStashChanges.cs b/src/Commands/QueryStashChanges.cs index 7fc27ea3..92240569 100644 --- a/src/Commands/QueryStashChanges.cs +++ b/src/Commands/QueryStashChanges.cs @@ -9,8 +9,10 @@ namespace SourceGit.Commands /// public partial class QueryStashChanges : Command { - [GeneratedRegex(@"^([MADRC])\s+(.+)$")] + [GeneratedRegex(@"^([MADC])\s+(.+)$")] private static partial Regex REG_FORMAT(); + [GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")] + private static partial Regex REG_RENAME_FORMAT(); public QueryStashChanges(string repo, string stash) { @@ -31,7 +33,17 @@ namespace SourceGit.Commands { var match = REG_FORMAT().Match(line); if (!match.Success) + { + match = REG_RENAME_FORMAT().Match(line); + if (match.Success) + { + var renamed = new Models.Change() { Path = match.Groups[1].Value }; + renamed.Set(Models.ChangeState.Renamed); + outs.Add(renamed); + } + continue; + } var change = new Models.Change() { Path = match.Groups[2].Value }; var status = match.Groups[1].Value; @@ -50,10 +62,6 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Deleted); outs.Add(change); break; - case 'R': - change.Set(Models.ChangeState.Renamed); - outs.Add(change); - break; case 'C': change.Set(Models.ChangeState.Copied); outs.Add(change); diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs index 7acfdf38..7d1a269b 100644 --- a/src/Commands/Stash.cs +++ b/src/Commands/Stash.cs @@ -82,7 +82,7 @@ namespace SourceGit.Commands public bool Pop(string name) { - Args = $"stash pop -q \"{name}\""; + Args = $"stash pop -q --index \"{name}\""; return Exec(); } diff --git a/src/Converters/DoubleConverters.cs b/src/Converters/DoubleConverters.cs index 5b7c0a03..c9bd9890 100644 --- a/src/Converters/DoubleConverters.cs +++ b/src/Converters/DoubleConverters.cs @@ -4,16 +4,12 @@ namespace SourceGit.Converters { public static class DoubleConverters { - public static readonly FuncValueConverter Increase = - new FuncValueConverter(v => v + 1.0); + public static readonly FuncValueConverter Increase = new(v => v + 1.0); - public static readonly FuncValueConverter Decrease = - new FuncValueConverter(v => v - 1.0); + public static readonly FuncValueConverter Decrease = new(v => v - 1.0); - public static readonly FuncValueConverter ToPercentage = - new FuncValueConverter(v => (v * 100).ToString("F3") + "%"); + public static readonly FuncValueConverter ToPercentage = new(v => $"{v * 100:F3}%"); - public static readonly FuncValueConverter OneMinusToPercentage = - new FuncValueConverter(v => ((1.0 - v) * 100).ToString("F3") + "%"); + public static readonly FuncValueConverter OneMinusToPercentage = new(v => $"{(1.0 - v) * 100:F3}%"); } } diff --git a/src/Converters/FilterModeConverters.cs b/src/Converters/FilterModeConverters.cs index c486af5e..12ca1c9b 100644 --- a/src/Converters/FilterModeConverters.cs +++ b/src/Converters/FilterModeConverters.cs @@ -6,17 +6,14 @@ namespace SourceGit.Converters public static class FilterModeConverters { public static readonly FuncValueConverter ToBorderBrush = - new FuncValueConverter(v => + new(v => { - switch (v) + return v switch { - case Models.FilterMode.Included: - return Brushes.Green; - case Models.FilterMode.Excluded: - return Brushes.Red; - default: - return Brushes.Transparent; - } + Models.FilterMode.Included => Brushes.Green, + Models.FilterMode.Excluded => Brushes.Red, + _ => Brushes.Transparent, + }; }); } } diff --git a/src/Converters/IntConverters.cs b/src/Converters/IntConverters.cs index f21c5d24..c2542b2a 100644 --- a/src/Converters/IntConverters.cs +++ b/src/Converters/IntConverters.cs @@ -7,32 +7,24 @@ namespace SourceGit.Converters { public static class IntConverters { - public static readonly FuncValueConverter IsGreaterThanZero = - new FuncValueConverter(v => v > 0); + public static readonly FuncValueConverter IsGreaterThanZero = new(v => v > 0); - public static readonly FuncValueConverter IsGreaterThanFour = - new FuncValueConverter(v => v > 4); + public static readonly FuncValueConverter IsGreaterThanFour = new(v => v > 4); - public static readonly FuncValueConverter IsZero = - new FuncValueConverter(v => v == 0); + public static readonly FuncValueConverter IsZero = new(v => v == 0); - public static readonly FuncValueConverter IsOne = - new FuncValueConverter(v => v == 1); + public static readonly FuncValueConverter IsOne = new(v => v == 1); - public static readonly FuncValueConverter IsNotOne = - new FuncValueConverter(v => v != 1); + public static readonly FuncValueConverter IsNotOne = new(v => v != 1); - public static readonly FuncValueConverter IsSubjectLengthBad = - new FuncValueConverter(v => v > ViewModels.Preferences.Instance.SubjectGuideLength); + public static readonly FuncValueConverter IsSubjectLengthBad = new(v => v > ViewModels.Preferences.Instance.SubjectGuideLength); - public static readonly FuncValueConverter IsSubjectLengthGood = - new FuncValueConverter(v => v <= ViewModels.Preferences.Instance.SubjectGuideLength); + public static readonly FuncValueConverter IsSubjectLengthGood = new(v => v <= ViewModels.Preferences.Instance.SubjectGuideLength); - public static readonly FuncValueConverter ToTreeMargin = - new FuncValueConverter(v => new Thickness(v * 16, 0, 0, 0)); + public static readonly FuncValueConverter ToTreeMargin = new(v => new Thickness(v * 16, 0, 0, 0)); public static readonly FuncValueConverter ToBookmarkBrush = - new FuncValueConverter(bookmark => + new(bookmark => { if (bookmark == 0) return Application.Current?.FindResource("Brush.FG1") as IBrush; diff --git a/src/Converters/InteractiveRebaseActionConverters.cs b/src/Converters/InteractiveRebaseActionConverters.cs index dbd183bd..7478e3d0 100644 --- a/src/Converters/InteractiveRebaseActionConverters.cs +++ b/src/Converters/InteractiveRebaseActionConverters.cs @@ -6,46 +6,34 @@ namespace SourceGit.Converters public static class InteractiveRebaseActionConverters { public static readonly FuncValueConverter ToIconBrush = - new FuncValueConverter(v => + new(v => { - switch (v) + return v switch { - case Models.InteractiveRebaseAction.Pick: - return Brushes.Green; - case Models.InteractiveRebaseAction.Edit: - return Brushes.Orange; - case Models.InteractiveRebaseAction.Reword: - return Brushes.Orange; - case Models.InteractiveRebaseAction.Squash: - return Brushes.LightGray; - case Models.InteractiveRebaseAction.Fixup: - return Brushes.LightGray; - default: - return Brushes.Red; - } + Models.InteractiveRebaseAction.Pick => Brushes.Green, + Models.InteractiveRebaseAction.Edit => Brushes.Orange, + Models.InteractiveRebaseAction.Reword => Brushes.Orange, + Models.InteractiveRebaseAction.Squash => Brushes.LightGray, + Models.InteractiveRebaseAction.Fixup => Brushes.LightGray, + _ => Brushes.Red, + }; }); public static readonly FuncValueConverter ToName = - new FuncValueConverter(v => + new(v => { - switch (v) + return v switch { - case Models.InteractiveRebaseAction.Pick: - return "Pick"; - case Models.InteractiveRebaseAction.Edit: - return "Edit"; - case Models.InteractiveRebaseAction.Reword: - return "Reword"; - case Models.InteractiveRebaseAction.Squash: - return "Squash"; - case Models.InteractiveRebaseAction.Fixup: - return "Fixup"; - default: - return "Drop"; - } + Models.InteractiveRebaseAction.Pick => "Pick", + Models.InteractiveRebaseAction.Edit => "Edit", + Models.InteractiveRebaseAction.Reword => "Reword", + Models.InteractiveRebaseAction.Squash => "Squash", + Models.InteractiveRebaseAction.Fixup => "Fixup", + _ => "Drop", + }; }); public static readonly FuncValueConverter CanEditMessage = - new FuncValueConverter(v => v == Models.InteractiveRebaseAction.Reword || v == Models.InteractiveRebaseAction.Squash); + new(v => v == Models.InteractiveRebaseAction.Reword || v == Models.InteractiveRebaseAction.Squash); } } diff --git a/src/Converters/ListConverters.cs b/src/Converters/ListConverters.cs index 81cac8b7..9c379e6b 100644 --- a/src/Converters/ListConverters.cs +++ b/src/Converters/ListConverters.cs @@ -8,18 +8,18 @@ namespace SourceGit.Converters public static class ListConverters { public static readonly FuncValueConverter ToCount = - new FuncValueConverter(v => v == null ? " (0)" : $" ({v.Count})"); + new(v => v == null ? " (0)" : $" ({v.Count})"); public static readonly FuncValueConverter IsNullOrEmpty = - new FuncValueConverter(v => v == null || v.Count == 0); + new(v => v == null || v.Count == 0); public static readonly FuncValueConverter IsNotNullOrEmpty = - new FuncValueConverter(v => v != null && v.Count > 0); + new(v => v != null && v.Count > 0); public static readonly FuncValueConverter, List> Top100Changes = - new FuncValueConverter, List>(v => (v == null || v.Count < 100) ? v : v.GetRange(0, 100)); + new(v => (v == null || v.Count < 100) ? v : v.GetRange(0, 100)); public static readonly FuncValueConverter IsOnlyTop100Shows = - new FuncValueConverter(v => v != null && v.Count > 100); + new(v => v != null && v.Count > 100); } } diff --git a/src/Converters/PathConverters.cs b/src/Converters/PathConverters.cs index dd7cfa49..bd945304 100644 --- a/src/Converters/PathConverters.cs +++ b/src/Converters/PathConverters.cs @@ -22,7 +22,7 @@ namespace SourceGit.Converters var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (v.StartsWith(home, StringComparison.Ordinal)) - return "~" + v.Substring(prefixLen); + return $"~{v.AsSpan()[prefixLen..]}"; return v; }); diff --git a/src/Converters/StringConverters.cs b/src/Converters/StringConverters.cs index 5e4608c5..f90e18a4 100644 --- a/src/Converters/StringConverters.cs +++ b/src/Converters/StringConverters.cs @@ -21,7 +21,7 @@ namespace SourceGit.Converters } } - public static readonly ToLocaleConverter ToLocale = new ToLocaleConverter(); + public static readonly ToLocaleConverter ToLocale = new(); public class ToThemeConverter : IValueConverter { @@ -46,7 +46,7 @@ namespace SourceGit.Converters } } - public static readonly ToThemeConverter ToTheme = new ToThemeConverter(); + public static readonly ToThemeConverter ToTheme = new(); public class FormatByResourceKeyConverter : IValueConverter { @@ -62,24 +62,24 @@ namespace SourceGit.Converters } } - public static readonly FormatByResourceKeyConverter FormatByResourceKey = new FormatByResourceKeyConverter(); + public static readonly FormatByResourceKeyConverter FormatByResourceKey = new(); public static readonly FuncValueConverter ToShortSHA = - new FuncValueConverter(v => v == null ? string.Empty : (v.Length > 10 ? v.Substring(0, 10) : v)); + new(v => v == null ? string.Empty : (v.Length > 10 ? v.Substring(0, 10) : v)); public static readonly FuncValueConverter TrimRefsPrefix = - new FuncValueConverter(v => + new(v => { if (v == null) return string.Empty; if (v.StartsWith("refs/heads/", StringComparison.Ordinal)) - return v.Substring(11); + return v[11..]; if (v.StartsWith("refs/remotes/", StringComparison.Ordinal)) - return v.Substring(13); + return v[13..]; return v; }); public static readonly FuncValueConverter ContainsSpaces = - new FuncValueConverter(v => v != null && v.Contains(' ')); + new(v => v != null && v.Contains(' ')); } } diff --git a/src/Models/DateTimeFormat.cs b/src/Models/DateTimeFormat.cs index 4e71a74f..4e8aa550 100644 --- a/src/Models/DateTimeFormat.cs +++ b/src/Models/DateTimeFormat.cs @@ -32,17 +32,17 @@ namespace SourceGit.Models public static readonly List Supported = new List { - new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss"), - new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss"), - new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"), - new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy HH:mm:ss"), - new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy HH:mm:ss"), - new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy HH:mm:ss"), - new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss"), - new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy HH:mm:ss"), - new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy HH:mm:ss"), - new DateTimeFormat("MMM d yyyy", "MMM d yyyy HH:mm:ss"), - new DateTimeFormat("d MMM yyyy", "d MMM yyyy HH:mm:ss"), + new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd, HH:mm:ss"), + new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd, HH:mm:ss"), + new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd, HH:mm:ss"), + new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy, HH:mm:ss"), + new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy, HH:mm:ss"), + new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy, HH:mm:ss"), + new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy, HH:mm:ss"), + new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy, HH:mm:ss"), + new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy, HH:mm:ss"), + new DateTimeFormat("MMM d yyyy", "MMM d yyyy, HH:mm:ss"), + new DateTimeFormat("d MMM yyyy", "d MMM yyyy, HH:mm:ss"), }; private static readonly DateTime _example = new DateTime(2025, 1, 31, 8, 0, 0, DateTimeKind.Local); diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs index d5e1534c..802e88ef 100644 --- a/src/Models/Locales.cs +++ b/src/Models/Locales.cs @@ -17,6 +17,7 @@ namespace SourceGit.Models new Locale("Русский", "ru_RU"), new Locale("简体中文", "zh_CN"), new Locale("繁體中文", "zh_TW"), + new Locale("日本語", "ja_JP"), }; public Locale(string name, string key) diff --git a/src/Native/OS.cs b/src/Native/OS.cs index f11d1e7f..320b5208 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -162,6 +162,15 @@ namespace SourceGit.Native _backend.OpenWithDefaultEditor(file); } + public static string GetAbsPath(string root, string sub) + { + var fullpath = Path.Combine(root, sub); + if (OperatingSystem.IsWindows()) + return fullpath.Replace('/', '\\'); + + return fullpath; + } + private static void UpdateGitVersion() { if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable)) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 7144ef43..bbfe4545 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -186,7 +186,6 @@ Kopieren Kopiere gesamten Text Pfad kopieren - Dateinamen kopieren Branch erstellen... Basierend auf: Erstellten Branch auschecken @@ -650,7 +649,6 @@ Lokale Änderungen stashen Anwenden Entfernen - Anwenden und entfernen Stash entfernen Entfernen: Stashes diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index f24d6c65..61140173 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -160,6 +160,7 @@ Add Sample GitLab Issue Rule Add Sample GitLab Merge Request Rule Add Sample Jira Rule + Add Sample Azure DevOps Rule New Rule Issue Regex Expression: Rule Name: @@ -185,7 +186,7 @@ Copy Copy All Text Copy Path - Copy File Name + Copy Full Path Create Branch... Based On: Check out the created branch @@ -654,7 +655,6 @@ Stash Local Changes Apply Drop - Pop Save as Patch... Drop Stash Drop: @@ -719,6 +719,7 @@ Trigger click event Commit (Edit) Stage all changes and commit + You have staged {0} file(s) but only {1} file(s) displayed ({2} files are filtered out). Do you want to continue? Empty commit detected! Do you want to continue (--allow-empty)? CONFLICTS DETECTED FILE CONFLICTS ARE RESOLVED diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index e7e953e5..8b4c350f 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -161,6 +161,7 @@ Añadir Regla de Ejemplo para Pull Requests de Gitee Añadir Regla de Ejemplo para Github Añadir Regla de Ejemplo para Jira + Añadir Regla de Ejemplo para Azure DevOps Añadir Regla de Ejemplo para Incidencias de GitLab Añadir Regla de Ejemplo para Merge Requests de GitLab Nueva Regla @@ -188,7 +189,7 @@ Copiar Copiar Todo el Texto Copiar Ruta - Copiar Nombre del Archivo + Copiar Ruta Completa Crear Rama... Basado En: Checkout de la rama creada @@ -475,6 +476,7 @@ Commits en el historial Mostrar hora del autor en lugar de la hora del commit en el gráfico Mostrar hijos en los detalles de commit + Mostrar etiquetas en el gráfico de commit Longitud de la guía del asunto GIT Habilitar Auto CRLF @@ -604,7 +606,7 @@ Por Fecha de Creación Por Nombre (Ascendiente) Por Nombre (Descendiente) - Sort + Ordenar Abrir en Terminal Usar tiempo relativo en las historias WORKTREES @@ -657,7 +659,6 @@ Stash Cambios Locales Aplicar Eliminar - Pop Guardar como Parche... Eliminar Stash Eliminar: diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 19c0859c..bf238aa2 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -16,13 +16,19 @@ Suivre la branche : Suivi de la branche distante Assistant IA + RE-GÉNERER Utiliser l'IA pour générer un message de commit + APPLIQUER COMME MESSAGE DE COMMIT Appliquer Fichier de patch : Selectionner le fichier .patch à appliquer Ignorer les changements d'espaces blancs Appliquer le patch Espaces blancs : + Appliquer le Stash + Supprimer après application + Rétablir les changements de l'index + Stash: Archiver... Enregistrer l'archive sous : Sélectionnez le chemin du fichier d'archive @@ -39,6 +45,7 @@ Comparer avec HEAD Comparer avec le worktree Copier le nom de la branche + Action personnalisée Supprimer ${0}$... Supprimer {0} branches sélectionnées Rejeter tous les changements @@ -54,6 +61,7 @@ Renommer ${0}$... Définir la branche de suivi... Comparer les branches + Branche amont invalide ! Octets ANNULER Réinitialiser à la révision parente @@ -92,6 +100,7 @@ Récupérer ce commit Cherry-Pick ce commit Cherry-Pick ... + Checkout Commit Comparer avec HEAD Comparer avec le worktree Copier les informations @@ -150,9 +159,10 @@ Ajouter une règle d'exemple Gitee Ajouter une règle d'exemple pour Pull Request Gitee Ajouter une règle d'exemple Github - Ajouter une règle d'exemple Jira Ajouter une règle d'exemple pour Incidents GitLab Ajouter une règle d'exemple pour Merge Request GitLab + Ajouter une règle d'exemple Jira + Ajouter une règle d'exemple Azure DevOps Nouvelle règle Issue Regex Expression: Nom de règle : @@ -177,8 +187,8 @@ Type de Changement : Copier Copier tout le texte - Copier le nom de fichier Copier le chemin + Copier le chemin complet Créer une branche... Basé sur : Récupérer la branche créée @@ -187,6 +197,7 @@ Stash & Réappliquer Nom de la nouvelle branche : Entrez le nom de la branche. + Les espaces seront remplacés par des tirets. Créer une branche locale Créer un tag... Nouveau tag à : @@ -583,13 +594,20 @@ Passer cette version Mise à jour du logiciel Il n'y a pas de mise à jour pour le moment. + Définir la branche de suivi + Branche : + Escamoter la branche amont + Branche amont: Copier le SHA - Squash Commits + Aller à + Squash les commits Dans : Clé privée SSH : Chemin du magasin de clés privées SSH START Stash + Auto-restauration après le stash + Vos fichiers de travail sont inchangés, mais un stash a été sauvegardé. Inclure les fichiers non-suivis Garder les fichiers indexés Message : @@ -599,7 +617,7 @@ Stash les changements locaux Appliquer Effacer - Extraire + Sauver comme Patch... Effacer le Stash Effacer : Stashes diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index d002dfef..85038d9e 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -161,6 +161,7 @@ Aggiungi una regola di esempio per un Pull Request Gitee Aggiungi una regola di esempio per GitHub Aggiungi una regola di esempio per Jira + Aggiungi una regola di esempio per Azure DevOps Aggiungi una regola di esempio per Issue GitLab Aggiungi una regola di esempio per una Merge Request GitLab Nuova Regola @@ -188,7 +189,6 @@ Copia Copia Tutto il Testo Copia Percorso - Copia Nome File Crea Branch... Basato Su: Checkout del Branch Creato @@ -658,7 +658,6 @@ Stasha Modifiche Locali Applica Elimina - Estrai Salva come Patch... Elimina Stash Elimina: diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml new file mode 100644 index 00000000..21255221 --- /dev/null +++ b/src/Resources/Locales/ja_JP.axaml @@ -0,0 +1,747 @@ + + + + + 概要 + SourceGitについて + オープンソース & フリーなGit GUIクライアント + ワークツリーを追加 + チェックアウトする内容: + 既存のブランチ + 新しいブランチを作成 + 場所: + ワークツリーのパスを入力してください。相対パスも使用することができます。 + ブランチの名前: + 任意。デフォルトでは宛先フォルダ名が使用されます。 + 追跡するブランチ: + 追跡中のリモートブランチ + OpenAI アシスタント + 再生成 + OpenAIを使用してコミットメッセージを生成 + コミットメッセージとして適用 + 適用 + パッチファイル: + 適用する .patchファイルを選択 + 空白文字の変更を無視 + パッチを適用 + 空白文字: + スタッシュを適用 + 適用後に削除 + インデックスの変更を復元 + スタッシュ: + アーカイブ... + アーカイブの保存先: + アーカイブファイルのパスを選択 + リビジョン: + アーカイブ + SourceGit Askpass + 変更されていないとみなされるファイル + 変更されていないとみなされるファイルはありません + 削除 + バイナリファイルはサポートされていません!!! + Blame + BLAMEではこのファイルはサポートされていません!!! + ${0}$ をチェックアウトする... + HEADと比較 + ワークツリーと比較 + ブランチ名をコピー + カスタムアクション + ${0}$を削除... + 選択中の{0}個のブランチを削除 + すべての変更を破棄 + ${0}$ へ早送りする + ${0}$ から ${1}$ へフェッチする + Git Flow - Finish ${0}$ + ${0}$ を ${1}$ にマージする... + 選択中の{0}個のブランチを現在のブランチにマージする + ${0}$ をプルする + ${0}$ を ${1}$ にプルする... + ${0}$ をプッシュする + ${0}$ を ${1}$ でリベースする... + ${0}$ をリネームする... + トラッキングブランチを設定... + ブランチの比較 + 無効な上流ブランチ! + バイト + キャンセル + このリビジョンにリセット + 親リビジョンにリセット + コミットメッセージを生成 + 変更表示の切り替え + ファイルとディレクトリのリストを表示 + パスのリストを表示 + ファイルシステムのツリーを表示 + ブランチをチェックアウト + コミットをチェックアウト + 警告: コミットをチェックアウトするとHEADが切断されます + コミット: + ブランチ: + ローカルの変更: + 破棄 + スタッシュして再適用 + チェリーピック + ソースをコミットメッセージに追加 + コミット(複数可): + すべての変更をコミット + メインライン: + 通常、マージをチェリーピックすることはできません。どちらのマージ元をメインラインとして扱うべきかが分からないためです。このオプションを使用すると、指定した親に対して変更を再適用する形でチェリーピックを実行できます。 + スタッシュをクリア + すべてのスタッシュをクリアします。続行しますか? + リモートリポジトリをクローン + 追加の引数: + リポジトリをクローンする際の追加パラメータ(任意)。 + ローカル名: + リポジトリの名前(任意)。 + 親フォルダ: + サブモジュールを初期化して更新 + リポジトリのURL: + 閉じる + エディタ + このコミットをチェリーピック + チェリーピック... + コミットをチェックアウト + HEADと比較 + ワークツリーと比較 + 情報をコピー + SHAをコピー + カスタムアクション + ${0}$ ブランチをここにインタラクティブリベース + ${0}$ にマージ + マージ... + ${0}$ をここにリベース + ${0}$ ブランチをここにリセット + コミットを戻す + 書き直す + パッチとして保存... + 親にスカッシュ + 子コミットをここにスカッシュ + 変更 + 変更を検索... + ファイル + LFSファイル + ファイルを検索... + サブモジュール + コミットの情報 + 著者 + 変更 + + コミッター + このコミットを含む参照を確認 + コミットが含まれるか確認 + 最初の100件の変更のみが表示されています。すべての変更は'変更'タブで確認できます。 + メッセージ + + 参照 + SHA + ブラウザで開く + コミットのタイトルを入力 + 説明 + リポジトリの設定 + コミットテンプレート + テンプレート名: + テンプレート内容: + カスタムアクション + 引数: + ${REPO} - リポジトリのパス; ${BRANCH} - 選択中のブランチ; ${SHA} - 選択中のコミットのSHA + 実行ファイル: + 名前: + スコープ: + ブランチ + コミット + リポジトリ + アクションの終了を待機 + Eメールアドレス + Eメールアドレス + GIT + 自動的にリモートからフェッチ 間隔: + 分(s) + リモートの初期値 + ISSUEトラッカー + サンプルのGitee Issueルールを追加 + サンプルのGiteeプルリクエストルールを追加 + サンプルのGithubルールを追加 + サンプルのGitLab Issueルールを追加 + サンプルのGitLabマージリクエストルールを追加 + サンプルのJiraルールを追加 + サンプルのAzure DevOpsルールを追加 + 新しいルール + Issueの正規表現: + ルール名: + リザルトURL: + 正規表現のグループ値に$1, $2を使用してください。 + AI + 優先するサービス: + 優先するサービスが設定されている場合、SourceGitはこのリポジトリでのみそれを使用します。そうでない場合で複数サービスが利用できる場合は、そのうちの1つを選択するためのコンテキストメニューが表示されます。 + HTTP プロキシ + このリポジトリで使用するHTTPプロキシ + ユーザー名 + このリポジトリにおけるユーザー名 + ワークスペース + + 起動時にタブを復元 + Conventional Commitヘルパー + 破壊的変更: + 閉じたIssue: + 詳細な変更: + スコープ: + 短い説明: + 変更の種類: + コピー + すべてのテキストをコピー + パスをコピー + 絶対パスをコピー + ブランチを作成... + 派生元: + 作成したブランチにチェックアウト + ローカルの変更: + 破棄 + スタッシュして再適用 + 新しいブランチの名前: + ブランチの名前を入力 + スペースはダッシュに置き換えられます。 + ローカルブランチを作成 + タグを作成... + 付与されるコミット: + GPG署名を使用 + タグメッセージ: + 任意。 + タグの名前: + 推奨フォーマット: v1.0.0-alpha + 作成後にすべてのリモートにプッシュ + 新しいタグを作成 + 種類: + 注釈付き + 軽量 + Ctrlキーを押しながらクリックで実行 + 切り取り + ブランチを削除 + ブランチ: + リモートブランチを削除しようとしています!!! + もしリモートブランチを削除する場合、${0}$も削除します。 + 複数のブランチを削除 + 一度に複数のブランチを削除しようとしています! 操作を行う前に再度確認してください! + リモートを削除 + リモート: + パス: + 対象: + すべての子ノードがリストから削除されます。 + グループを削除 + これはリストからのみ削除され、ディスクには保存されません! + リポジトリを削除 + サブモジュールを削除 + サブモジュールのパス: + タグを削除 + タグ: + リモートリポジトリから削除 + バイナリの差分 + NEW + OLD + コピー + ファイルモードが変更されました + 先頭の差分 + 空白の変更を無視 + 最後の差分 + LFSオブジェクトの変更 + 次の差分 + 変更がない、もしくはEOLの変更のみ + 前の差分 + パッチとして保存 + 隠されたシンボルを表示 + 差分の分割表示 + サブモジュール + 新規 + スワップ + シンタックスハイライト + 行の折り返し + ブロックナビゲーションを有効化 + マージツールで開く + すべての行を表示 + 表示する行数を減らす + 表示する行数を増やす + ファイルを選択すると、変更内容が表示されます + マージツールで開く + 変更を破棄 + ワーキングディレクトリのすべての変更を破棄 + 変更: + 無視したファイルを含める + {0}個の変更を破棄します。 + この操作を元に戻すことはできません!!! + ブックマーク: + 新しい名前: + 対象: + 選択中のグループを編集 + 選択中のリポジトリを編集 + カスタムアクションを実行 + アクション名: + (チェックアウトせずに)ブランチを早送りする + フェッチ + すべてのリモートをフェッチ + ローカル参照を強制的に上書き + タグなしでフェッチ + リモート: + リモートの変更をフェッチ + 変更されていないとみなされる + 破棄... + {0}個のファイルを破棄... + 選択された行の変更を破棄 + 外部マージツールで開く + ${0}$ を使用して解決 + パッチとして保存... + ステージ + {0}個のファイルをステージ... + 選択された行の変更をステージ + スタッシュ... + {0}個のファイルをスタッシュ... + アンステージ + {0}個のファイルをアンステージ... + 選択された行の変更をアンステージ + 相手の変更を使用 (checkout --theirs) + 自分の変更を使用 (checkout --ours) + ファイルの履歴 + コンテンツ + 変更 + Git-Flow + 開発ブランチ: + Feature: + Feature プレフィックス: + FLOW - Finish Feature + FLOW - Finish Hotfix + FLOW - Finish Release + 対象: + Hotfix: + Hotfix プレフィックス: + Git-Flowを初期化 + ブランチを保持 + プロダクション ブランチ: + Release: + Release プレフィックス: + Start Feature... + FLOW - Start Feature + Start Hotfix... + FLOW - Start Hotfix + 名前を入力 + Start Release... + FLOW - Start Release + Versionタグ プレフィックス: + Git LFS + トラックパターンを追加... + パターンをファイル名として扱う + カスタム パターン: + Git LFSにトラックパターンを追加 + フェッチ + LFSオブジェクトをフェッチ + `git lfs fetch`を実行して、Git LFSオブジェクトをダウンロードします。ワーキングコピーは更新されません。 + Git LFSフックをインストール + ロックを表示 + ロックされているファイルはありません + ロック + 私のロックのみ表示 + LFSロック + ロック解除 + 強制的にロック解除 + 削除 + `git lfs prune`を実行して、ローカルの保存領域から古いLFSファイルを削除します。 + プル + LFSオブジェクトをプル + `git lfs pull`を実行して、現在の参照とチェックアウトのすべてのGit LFSファイルをダウンロードします。 + プッシュ + LFSオブジェクトをプッシュ + キュー内の大容量ファイルをGit LFSエンドポイントにプッシュします。 + リモート: + {0}という名前のファイルをトラック + すべての*{0}ファイルをトラック + 履歴 + 著者 + 著者時間 + グラフ & コミットのタイトル + SHA + 日時 + {0} コミットを選択しました + 'Ctrl'キーまたは'Shift'キーを押すと、複数のコミットを選択できます。 + ⌘ または ⇧ キーを押して複数のコミットを選択します。 + TIPS: + キーボードショートカットを確認 + 総合 + 現在のポップアップをキャンセル + 新しくリポジトリをクローン + 現在のページを閉じる + 前のページに移動 + 次のページに移動 + 新しいページを作成 + 設定ダイアログを開く + リポジトリ + ステージ済みの変更をコミット + ステージ済みの変更をコミットしてプッシュ + 全ての変更をステージしてコミット + 選択中のコミットから新たなブランチを作成 + 選択した変更を破棄 + 直接フェッチを実行 + ダッシュボードモード (初期値) + 直接プルを実行 + 直接プッシュを実行 + 現在のリポジトリを強制的に再読み込み + 選択中の変更をステージ/アンステージ + コミット検索モード + '変更'に切り替える + '履歴'に切り替える + 'スタッシュ'に切り替える + テキストエディタ + 検索パネルを閉じる + 次のマッチを検索 + 前のマッチを検索 + 検索パネルを開く + ステージ + アンステージ + 破棄 + リポジトリの初期化 + パス: + チェリーピックが進行中です。'中止'を押すと元のHEADが復元されます。 + コミットを処理中 + マージリクエストが進行中です。'中止'を押すと元のHEADが復元されます。 + マージ中 + リベースが進行中です。'中止'を押すと元のHEADが復元されます。 + 停止しました + 元に戻す処理が進行中です。'中止'を押すと元のHEADが復元されます。 + コミットを元に戻しています + インタラクティブ リベース + 対象のブランチ: + On: + ブラウザで開く + リンクをコピー + エラー + 通知 + ブランチのマージ + 宛先: + マージオプション: + ソースブランチ: + マージ (複数) + すべての変更をコミット + マージ戦略: + 対象: + リポジトリノードの移動 + 親ノードを選択: + 名前: + Gitが設定されていません。まず[設定]に移動して設定を行ってください。 + アプリケーションデータのディレクトリを開く + 外部ツールで開く... + 任意。 + 新しいページを開く + ブックマーク + タブを閉じる + 他のタブを閉じる + 右のタブを閉じる + リポジトリパスをコピー + リポジトリ + 貼り付け + たった今 + {0} 分前 + 1 時間前 + {0} 時間前 + 昨日 + {0} 日前 + 先月 + {0} ヶ月前 + 昨年 + {0} 年前 + 設定 + AI + 差分分析プロンプト + APIキー + タイトル生成プロンプト + モデル + 名前 + サーバー + ストリーミングを有効化 + 外観 + デフォルトのフォント + エディタのタブ幅 + フォントサイズ + デフォルト + エディタ + 等幅フォント + テキストエディタでは等幅フォントのみを使用する + テーマ + テーマの上書き + タイトルバーの固定タブ幅を使用 + ネイティブウィンドウフレームを使用 + 差分/マージ ツール + インストール パス + 差分/マージ ツールのパスを入力 + ツール + 総合 + 起動時にアップデートを確認 + 日時のフォーマット + 言語 + コミット履歴 + グラフにコミット時間の代わりに著者の時間を表示する + コミット詳細に子コミットを表示 + コミットグラフにタグを表示 + コミットタイトル枠の大きさ + GIT + 自動CRLFを有効化 + デフォルトのクローンディレクトリ + ユーザー Eメールアドレス + グローバルgitのEメールアドレス + フェッチ時に--pruneを有効化 + インストール パス + HTTP SSL 検証を有効にする + ユーザー名 + グローバルのgitユーザー名 + Gitバージョン + Git (>= 2.23.0) はこのアプリで必要です + GPG 署名 + コミットにGPG署名を行う + タグにGPG署名を行う + GPGフォーマット + プログラムのインストールパス + インストールされたgpgプログラムのパスを入力 + ユーザー署名キー + ユーザーのGPG署名キー + 統合 + シェル/ターミナル + シェル/ターミナル + パス + リモートを削除 + 対象: + 作業ツリーを削除 + `$GIT_DIR/worktrees` の作業ツリー情報を削除 + プル + ブランチ: + すべてのブランチをフェッチ + 宛先: + ローカルの変更: + 破棄 + スタッシュして再適用 + タグなしでフェッチ + リモート: + プル (フェッチ & マージ) + マージの代わりにリベースを使用 + プッシュ + サブモジュールがプッシュされていることを確認 + 強制的にプッシュ + ローカル ブランチ: + リモート: + 変更をリモートにプッシュ + リモート ブランチ: + 追跡ブランチとして設定 + すべてのタグをプッシュ + リモートにタグをプッシュ + すべてのリモートにプッシュ + リモート: + タグ: + 終了 + 現在のブランチをリベース + ローカルの変更をスタッシュして再適用 + On: + リベース: + 更新 + リモートを追加 + リモートを編集 + 名前: + リモートの名前 + リポジトリのURL: + リモートのgitリポジトリのURL + URLをコピー + 削除... + 編集... + フェッチ + ブラウザで開く + 削除 + ワークツリーの削除を確認 + `--force` オプションを有効化 + 対象: + ブランチの名前を編集 + 新しい名前: + このブランチにつける一意な名前 + ブランチ: + 中止 + リモートから変更を自動取得中... + クリーンアップ(GC & Prune) + このリポジトリに対して`git gc`コマンドを実行します。 + すべてのフィルターをクリア + リポジトリの設定 + 続ける + カスタムアクション + カスタムアクションがありません + `--reflog` オプションを有効化 + ファイルブラウザーで開く + ブランチ/タグ/サブモジュールを検索 + 解除 + コミットグラフで非表示 + コミットグラフでフィルター + `--first-parent` オプションを有効化 + レイアウト + 水平 + 垂直 + コミットの並び順 + 日時 + トポロジカルソート + ローカル ブランチ + HEADに移動 + ブランチを作成 + 通知をクリア + グラフで現在のブランチを強調表示 + {0} で開く + 外部ツールで開く + 更新 + リモート + リモートを追加 + コミットを検索 + 著者 + コミッター + ファイル + メッセージ + SHA + 現在のブランチ + タグをツリーとして表示 + スキップ + 統計 + サブモジュール + サブモジュールを追加 + サブモジュールを更新 + タグ + 新しいタグを作成 + 作成者日時 + 名前 (昇順) + 名前 (降順) + ソート + ターミナルで開く + 履歴に相対時間を使用 + ワークツリー + ワークツリーを追加 + 削除 + GitリポジトリのURL + 現在のブランチをリビジョンにリセット + リセットモード: + 移動先: + 現在のブランチ: + ファイルエクスプローラーで表示 + コミットを戻す + コミット: + コミットの変更を戻す + コミットメッセージを書き直す + 改行には'Shift+Enter'キーを使用します。 'Enter"はOKボタンのホットキーとして機能します。 + 実行中です。しばらくお待ちください... + 保存 + 名前を付けて保存... + パッチが正常に保存されました! + リポジトリをスキャン + ルートディレクトリ: + 更新を確認 + 新しいバージョンのソフトウェアが利用可能です: + 更新の確認に失敗しました! + ダウンロード + このバージョンをスキップ + ソフトウェアの更新 + 利用可能なアップデートはありません + トラッキングブランチを設定 + ブランチ: + 上流ブランチを解除 + 上流ブランチ: + SHAをコピー + Go to + スカッシュコミット + 宛先: + SSH プライベートキー: + プライベートSSHキーストアのパス + スタート + スタッシュ + スタッシュ後に自動で復元 + 作業ファイルは変更されず、スタッシュが保存されます。 + 追跡されていないファイルを含める + ステージされたファイルを保持 + メッセージ: + オプション. このスタッシュの名前を入力 + ステージされた変更のみ + 選択したファイルの、ステージされた変更とステージされていない変更の両方がスタッシュされます!!! + ローカルの変更をスタッシュ + 適用 + 破棄 + パッチとして保存 + スタッシュを破棄 + 破棄: + スタッシュ + 変更 + スタッシュ + 統計 + コミット + コミッター + 月間 + 週間 + コミット: + 著者: + 概要 + サブモジュール + サブモジュールを追加 + 相対パスをコピー + ネストされたサブモジュールを取得する + サブモジュールのリポジトリを開く + 相対パス: + このモジュールを保存するフォルダの相対パス + サブモジュールを削除 + OK + タグ名をコピー + タグメッセージをコピー + ${0}$ を削除... + ${0}$ を ${1}$ にマージ... + ${0}$ をプッシュ... + URL: + サブモジュールを更新 + すべてのサブモジュール + 必要に応じて初期化 + 再帰的に更新 + サブモジュール: + --remoteオプションを使用 + 警告 + ようこそ + グループを作成 + サブグループを作成 + リポジトリをクローンする + 削除 + ドラッグ & ドロップでフォルダを追加できます. グループを作成したり、変更したりできます。 + 編集 + 別のグループに移動 + すべてのリポジトリを開く + リポジトリを開く + ターミナルを開く + デフォルトのクローンディレクトリ内のリポジトリを再スキャン + リポジトリを検索... + ソート + 変更 + Git Ignore + すべての*{0}ファイルを無視 + 同じフォルダ内の*{0}ファイルを無視 + 同じフォルダ内のファイルを無視 + このファイルのみを無視 + Amend + このファイルを今すぐステージできます。 + コミット + コミットしてプッシュ + メッセージのテンプレート/履歴 + クリックイベントをトリガー + コミット (Edit) + すべての変更をステージしてコミット + 空のコミットが検出されました。続行しますか? (--allow-empty) + 競合が検出されました + ファイルの競合は解決されました + 追跡されていないファイルを含める + 最近の入力メッセージはありません + コミットテンプレートはありません + サインオフ + ステージしたファイル + ステージを取り消し + すべてステージを取り消し + 未ステージのファイル + ステージへ移動 + すべてステージへ移動 + 変更されていないとみなしたものを表示 + テンプレート: ${0}$ + 選択したファイルを右クリックし、競合を解決する操作を選択してください。 + ワークスペース: + ワークスペースを設定... + ワークツリー + パスをコピー + ロック + 削除 + ロック解除 + diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index e811cf3e..4ee6cdbc 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -168,6 +168,7 @@ RASTREADOR DE PROBLEMAS Adicionar Regra de Exemplo do Github Adicionar Regra de Exemplo do Jira + Adicionar Regra de Exemplo do Azure DevOps Adicionar Regra de Exemplo do GitLab Adicionar regra de exemplo de Merge Request do GitLab Nova Regra @@ -195,7 +196,6 @@ Copiar Copiar todo o texto Copiar Caminho - Copiar Nome do Arquivo Criar Branch... Baseado Em: Checar o branch criado @@ -619,7 +619,6 @@ Guardar Alterações Locais Aplicar Descartar - Pop Descartar Stash Descartar: Stashes diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 707c9ed9..2ea274d1 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -161,6 +161,7 @@ Добавить пример правила запроса скачивания из Gitea Добавить пример правила для Git Добавить пример правила Jira + Добавить пример правила Azure DevOps Добавить пример правила выдачи GitLab Добавить пример правила запроса на слияние в GitLab Новое правило @@ -189,7 +190,7 @@ Копировать Копировать весь текст Копировать путь - Копировать имя файла + Копировать полный путь Создать ветку... Основан на: Проверить созданную ветку @@ -212,7 +213,7 @@ Вид: С примечаниями Простой - Удерживайте Ctrl, чтобы начать сразу + Удерживайте Ctrl, чтобы сразу начать Вырезать Удалить ветку Ветка: @@ -330,7 +331,7 @@ Добавить шаблон отслеживания в LFS Git Извлечь Извлечь объекты LFS - Запустить «git lfs fetch», чтобы загрузить объекты LFS Git. При этом рабочая копия не обновляется. + Запустить (git lfs fetch), чтобы загрузить объекты LFS Git. При этом рабочая копия не обновляется. Установить перехват LFS Git Показывать блокировки Нет заблокированных файлов @@ -340,10 +341,10 @@ Разблокировать Принудительно разблокировать Обрезать - Запустить «git lfs prune», чтобы удалить старые файлы LFS из локального хранилища + Запустить (git lfs prune), чтобы удалить старые файлы LFS из локального хранилища Забрать Забрать объекты LFS - Запустить «git lfs pull», чтобы загрузить все файлы LFS Git для текущей ссылки и проверить + Запустить (git lfs pull), чтобы загрузить все файлы LFS Git для текущей ссылки и проверить Выложить Выложить объекты LFS Отправляйте большие файлы, помещенные в очередь, в конечную точку LFS Git @@ -557,7 +558,7 @@ Отказ Автоматическое извлечение изменений с внешних репозиторий... Очистить (Сбор мусора и удаление) - Запустить команду «git gc» для данного репозитория. + Запустить команду (git gc) для данного репозитория. Очистить всё Настройка репозитория ПРОДОЛЖИТЬ @@ -659,7 +660,6 @@ Отложить локальные изменения Принять Отбросить - Применить Сохранить как заплатку... Отбросить тайник Отбросить: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 93d08bd0..fec5c780 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -157,6 +157,7 @@ 分钟 默认远程 ISSUE追踪 + 新增匹配Azure DevOps规则 新增匹配Gitee议题规则 新增匹配Gitee合并请求规则 新增匹配Github Issue规则 @@ -188,7 +189,7 @@ 复制 复制全部文本 复制路径 - 复制文件名 + 复制完整路径 新建分支 ... 新分支基于 : 完成后切换到新分支 @@ -658,7 +659,6 @@ 贮藏本地变更 应用(apply) 删除(drop) - 应用并删除(pop) 另存为补丁... 丢弃贮藏确认 丢弃贮藏 : @@ -723,6 +723,7 @@ 触发点击事件 提交(修改原始提交) 自动暂存所有变更并提交 + 当前有 {0} 个文件在暂存区中,但仅显示了 {1} 个文件({2} 个文件被过滤掉了),是否继续提交? 提交未包含变更文件!是否继续(--allow-empty)? 检测到冲突 文件冲突已解决 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index d5a7a77c..66bcc33a 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -157,6 +157,7 @@ 分鐘 預設遠端存放庫 Issue 追蹤 + 新增符合 Azure DevOps 規則 新增符合 Gitee 議題規則 新增符合 Gitee 合併請求規則 新增符合 GitHub Issue 規則 @@ -188,7 +189,7 @@ 複製 複製全部內容 複製路徑 - 複製檔案名稱 + 複製完整路徑 新增分支... 新分支基於: 完成後切換到新分支 @@ -657,7 +658,6 @@ 擱置本機變更 套用 (apply) 刪除 (drop) - 套用並刪除 (pop) 另存為修補檔 (patch)... 捨棄擱置變更確認 捨棄擱置變更: @@ -722,6 +722,7 @@ 觸發點擊事件 提交 (修改原始提交) 自動暫存全部變更並提交 + 您已暫存 {0} 檔案,但只顯示 {1} 檔案 ({2} 檔案被篩選器隱藏)。您要繼續嗎? 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)? 檢測到衝突 檔案衝突已解決 diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 3578d59f..852c9e34 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -41,17 +41,17 @@ - - - - - + + + + + - + - + diff --git a/src/ViewModels/BranchCompare.cs b/src/ViewModels/BranchCompare.cs index b3c0009c..4edb978c 100644 --- a/src/ViewModels/BranchCompare.cs +++ b/src/ViewModels/BranchCompare.cs @@ -163,15 +163,15 @@ namespace SourceGit.ViewModels }; menu.Items.Add(copyPath); - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo, change.Path)); e.Handled = true; }; - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); return menu; } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 34ac8308..d04e674b 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -425,17 +425,17 @@ namespace SourceGit.ViewModels ev.Handled = true; }; - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, change.Path)); e.Handled = true; }; menu.Items.Add(copyPath); - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); return menu; } @@ -562,17 +562,17 @@ namespace SourceGit.ViewModels ev.Handled = true; }; - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(file.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, file.Path)); e.Handled = true; }; menu.Items.Add(copyPath); - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); return menu; } diff --git a/src/ViewModels/ConfirmCommit.cs b/src/ViewModels/ConfirmCommit.cs new file mode 100644 index 00000000..cea56948 --- /dev/null +++ b/src/ViewModels/ConfirmCommit.cs @@ -0,0 +1,26 @@ +using System; + +namespace SourceGit.ViewModels +{ + public class ConfirmCommit + { + public string Message + { + get; + private set; + } + + public ConfirmCommit(string message, Action onSure) + { + Message = message; + _onSure = onSure; + } + + public void Continue() + { + _onSure?.Invoke(); + } + + private Action _onSure; + } +} diff --git a/src/ViewModels/ConfirmCommitWithoutFiles.cs b/src/ViewModels/ConfirmCommitWithoutFiles.cs deleted file mode 100644 index 3249fba8..00000000 --- a/src/ViewModels/ConfirmCommitWithoutFiles.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace SourceGit.ViewModels -{ - public class ConfirmCommitWithoutFiles - { - public ConfirmCommitWithoutFiles(WorkingCopy wc, bool autoPush) - { - _wc = wc; - _autoPush = autoPush; - } - - public void Continue() - { - _wc.CommitWithoutFiles(_autoPush); - } - - private readonly WorkingCopy _wc; - private bool _autoPush; - } -} diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 06479394..8df914fb 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -48,7 +48,7 @@ namespace SourceGit.ViewModels { _ignoreIndexChange = true; - Pages = new AvaloniaList(); + Pages = []; AddNewTab(); var pref = Preferences.Instance; @@ -205,8 +205,7 @@ namespace SourceGit.ViewModels return; } - if (page == null) - page = _activePage; + page ??= _activePage; var removeIdx = Pages.IndexOf(page); var activeIdx = Pages.IndexOf(_activePage); @@ -239,7 +238,7 @@ namespace SourceGit.ViewModels CloseRepositoryInTab(one); } - Pages = new AvaloniaList { ActivePage }; + Pages = [ActivePage]; ActiveWorkspace.ActiveIdx = 0; OnPropertyChanged(nameof(Pages)); @@ -275,22 +274,16 @@ namespace SourceGit.ViewModels if (!Path.Exists(node.Id)) { - var ctx = page == null ? ActivePage.Node.Id : page.Node.Id; - App.RaiseException(ctx, "Repository does NOT exists any more. Please remove it."); + App.RaiseException(node.Id, "Repository does NOT exists any more. Please remove it."); return; } var isBare = new Commands.IsBareRepository(node.Id).Result(); - var gitDir = node.Id; - if (!isBare) + var gitDir = isBare ? node.Id : GetRepositoryGitDir(node.Id); + if (string.IsNullOrEmpty(gitDir)) { - gitDir = new Commands.QueryGitDir(node.Id).Result(); - if (string.IsNullOrEmpty(gitDir)) - { - var ctx = page == null ? ActivePage.Node.Id : page.Node.Id; - App.RaiseException(ctx, "Given path is not a valid git repository!"); - return; - } + App.RaiseException(node.Id, "Given path is not a valid git repository!"); + return; } var repo = new Repository(isBare, node.Id, gitDir); @@ -366,9 +359,11 @@ namespace SourceGit.ViewModels var icon = App.CreateMenuIcon(workspace.IsActive ? "Icons.Check" : "Icons.Workspace"); icon.Fill = workspace.Brush; - var item = new MenuItem(); - item.Header = workspace.Name; - item.Icon = icon; + var item = new MenuItem + { + Header = workspace.Name, + Icon = icon + }; item.Click += (_, e) => { if (!workspace.IsActive) @@ -382,8 +377,10 @@ namespace SourceGit.ViewModels menu.Items.Add(new MenuItem() { Header = "-" }); - var configure = new MenuItem(); - configure.Header = App.Text("Workspace.Configure"); + var configure = new MenuItem + { + Header = App.Text("Workspace.Configure") + }; configure.Click += (_, e) => { App.OpenDialog(new Views.ConfigureWorkspace() { DataContext = new ConfigureWorkspace() }); @@ -400,9 +397,11 @@ namespace SourceGit.ViewModels return null; var menu = new ContextMenu(); - var close = new MenuItem(); - close.Header = App.Text("PageTabBar.Tab.Close"); - close.InputGesture = KeyGesture.Parse(OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W"); + var close = new MenuItem + { + Header = App.Text("PageTabBar.Tab.Close"), + InputGesture = KeyGesture.Parse(OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W") + }; close.Click += (_, e) => { CloseTab(page); @@ -410,8 +409,10 @@ namespace SourceGit.ViewModels }; menu.Items.Add(close); - var closeOthers = new MenuItem(); - closeOthers.Header = App.Text("PageTabBar.Tab.CloseOther"); + var closeOthers = new MenuItem + { + Header = App.Text("PageTabBar.Tab.CloseOther") + }; closeOthers.Click += (_, e) => { CloseOtherTabs(); @@ -419,8 +420,10 @@ namespace SourceGit.ViewModels }; menu.Items.Add(closeOthers); - var closeRight = new MenuItem(); - closeRight.Header = App.Text("PageTabBar.Tab.CloseRight"); + var closeRight = new MenuItem + { + Header = App.Text("PageTabBar.Tab.CloseRight") + }; closeRight.Click += (_, e) => { CloseRightTabs(); @@ -430,9 +433,11 @@ namespace SourceGit.ViewModels if (page.Node.IsRepository) { - var bookmark = new MenuItem(); - bookmark.Header = App.Text("PageTabBar.Tab.Bookmark"); - bookmark.Icon = App.CreateMenuIcon("Icons.Bookmark"); + var bookmark = new MenuItem + { + Header = App.Text("PageTabBar.Tab.Bookmark"), + Icon = App.CreateMenuIcon("Icons.Bookmark") + }; for (int i = 0; i < Models.Bookmarks.Supported.Count; i++) { @@ -442,8 +447,10 @@ namespace SourceGit.ViewModels icon.Fill = Models.Bookmarks.Brushes[i]; var dupIdx = i; - var setter = new MenuItem(); - setter.Header = icon; + var setter = new MenuItem + { + Header = icon + }; setter.Click += (_, e) => { page.Node.Bookmark = dupIdx; @@ -454,9 +461,11 @@ namespace SourceGit.ViewModels menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(bookmark); - var copyPath = new MenuItem(); - copyPath.Header = App.Text("PageTabBar.Tab.CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + var copyPath = new MenuItem + { + Header = App.Text("PageTabBar.Tab.CopyPath"), + Icon = App.CreateMenuIcon("Icons.Copy") + }; copyPath.Click += (_, e) => { page.CopyPath(); @@ -469,6 +478,37 @@ namespace SourceGit.ViewModels return menu; } + private string GetRepositoryGitDir(string repo) + { + var fullpath = Path.Combine(repo, ".git"); + if (Directory.Exists(fullpath)) + { + if (Directory.Exists(Path.Combine(fullpath, "refs")) && + Directory.Exists(Path.Combine(fullpath, "objects")) && + File.Exists(Path.Combine(fullpath, "HEAD"))) + return fullpath; + + return null; + } + + if (File.Exists(fullpath)) + { + var redirect = File.ReadAllText(fullpath).Trim(); + if (redirect.StartsWith("gitdir: ", StringComparison.Ordinal)) + redirect = redirect.Substring(8); + + if (!Path.IsPathRooted(redirect)) + redirect = Path.GetFullPath(Path.Combine(repo, redirect)); + + if (Directory.Exists(redirect)) + return redirect; + + return null; + } + + return new Commands.QueryGitDir(repo).Result(); + } + private void SwitchWorkspace(Workspace to) { foreach (var one in Pages) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 6ea41e04..6b1e439e 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -2513,30 +2513,37 @@ namespace SourceGit.ViewModels private void AutoFetchImpl(object sender) { - if (!_settings.EnableAutoFetch || _isAutoFetching) - return; - - var lockFile = Path.Combine(_gitDir, "index.lock"); - if (File.Exists(lockFile)) - return; - - var now = DateTime.Now; - var desire = _lastFetchTime.AddMinutes(_settings.AutoFetchInterval); - if (desire > now) - return; - - var remotes = new List(); - lock (_lockRemotes) + try { - foreach (var remote in _remotes) - remotes.Add(remote.Name); - } + if (!_settings.EnableAutoFetch || _isAutoFetching) + return; - Dispatcher.UIThread.Invoke(() => IsAutoFetching = true); - foreach (var remote in remotes) - new Commands.Fetch(_fullpath, remote, false, false, null) { RaiseError = false }.Exec(); - _lastFetchTime = DateTime.Now; - Dispatcher.UIThread.Invoke(() => IsAutoFetching = false); + var lockFile = Path.Combine(_gitDir, "index.lock"); + if (File.Exists(lockFile)) + return; + + var now = DateTime.Now; + var desire = _lastFetchTime.AddMinutes(_settings.AutoFetchInterval); + if (desire > now) + return; + + var remotes = new List(); + lock (_lockRemotes) + { + foreach (var remote in _remotes) + remotes.Add(remote.Name); + } + + Dispatcher.UIThread.Invoke(() => IsAutoFetching = true); + foreach (var remote in remotes) + new Commands.Fetch(_fullpath, remote, false, false, null) { RaiseError = false }.Exec(); + _lastFetchTime = DateTime.Now; + Dispatcher.UIThread.Invoke(() => IsAutoFetching = false); + } + catch + { + // DO nothing, but prevent `System.AggregateException` + } } private string _fullpath = string.Empty; diff --git a/src/ViewModels/RepositoryConfigure.cs b/src/ViewModels/RepositoryConfigure.cs index cf23b6d8..3f590758 100644 --- a/src/ViewModels/RepositoryConfigure.cs +++ b/src/ViewModels/RepositoryConfigure.cs @@ -203,6 +203,11 @@ namespace SourceGit.ViewModels SelectedIssueTrackerRule = _repo.Settings.AddIssueTracker("Jira Tracker", "PROJ-(\\d+)", "https://jira.yourcompany.com/browse/PROJ-$1"); } + public void AddSampleAzureWorkItemTracker() + { + SelectedIssueTrackerRule = _repo.Settings.AddIssueTracker("Azure DevOps Tracker", "#(\\d+)", "https://dev.azure.com/yourcompany/workspace/_workitems/edit/$1"); + } + public void AddSampleGitLabIssueTracker() { var link = "https://gitlab.com/username/repository/-/issues/$1"; diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs index 77a408e0..3b5717a6 100644 --- a/src/ViewModels/RevisionCompare.cs +++ b/src/ViewModels/RevisionCompare.cs @@ -183,15 +183,15 @@ namespace SourceGit.ViewModels }; menu.Items.Add(copyPath); - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo, change.Path)); e.Handled = true; }; - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); return menu; } diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index 77ed5551..e69d9bb5 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -251,12 +251,12 @@ namespace SourceGit.ViewModels ev.Handled = true; }; - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, change.Path)); e.Handled = true; }; @@ -267,7 +267,7 @@ namespace SourceGit.ViewModels menu.Items.Add(resetToThisRevision); menu.Items.Add(new MenuItem { Header = "-" }); menu.Items.Add(copyPath); - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); return menu; } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index f9ddb288..a0933ea3 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -99,17 +99,18 @@ namespace SourceGit.ViewModels } } - public string UnstagedFilter + public string Filter { - get => _unstagedFilter; + get => _filter; set { - if (SetProperty(ref _unstagedFilter, value)) + if (SetProperty(ref _filter, value)) { if (_isLoadingData) return; - VisibleUnstaged = GetVisibleUnstagedChanges(_unstaged); + VisibleUnstaged = GetVisibleChanges(_unstaged); + VisibleStaged = GetVisibleChanges(_staged); SelectedUnstaged = []; } } @@ -133,6 +134,12 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _staged, value); } + public List VisibleStaged + { + get => _visibleStaged; + private set => SetProperty(ref _visibleStaged, value); + } + public List SelectedUnstaged { get => _selectedUnstaged; @@ -216,6 +223,9 @@ namespace SourceGit.ViewModels _visibleUnstaged.Clear(); OnPropertyChanged(nameof(VisibleUnstaged)); + _visibleStaged.Clear(); + OnPropertyChanged(nameof(VisibleStaged)); + _unstaged.Clear(); OnPropertyChanged(nameof(Unstaged)); @@ -269,7 +279,7 @@ namespace SourceGit.ViewModels } } - var visibleUnstaged = GetVisibleUnstagedChanges(unstaged); + var visibleUnstaged = GetVisibleChanges(unstaged); var selectedUnstaged = new List(); foreach (var c in visibleUnstaged) { @@ -278,8 +288,10 @@ namespace SourceGit.ViewModels } var staged = GetStagedChanges(); + + var visibleStaged = GetVisibleChanges(staged); var selectedStaged = new List(); - foreach (var c in staged) + foreach (var c in visibleStaged) { if (lastSelectedStaged.Contains(c.Path)) selectedStaged.Add(c); @@ -290,6 +302,7 @@ namespace SourceGit.ViewModels _isLoadingData = true; HasUnsolvedConflicts = hasConflict; VisibleUnstaged = visibleUnstaged; + VisibleStaged = visibleStaged; Unstaged = unstaged; Staged = staged; SelectedUnstaged = selectedUnstaged; @@ -337,7 +350,7 @@ namespace SourceGit.ViewModels public void UnstageAll() { - UnstageChanges(_staged, null); + UnstageChanges(_visibleStaged, null); } public void Discard(List changes) @@ -346,9 +359,9 @@ namespace SourceGit.ViewModels _repo.ShowPopup(new Discard(_repo, changes)); } - public void ClearUnstagedFilter() + public void ClearFilter() { - UnstagedFilter = string.Empty; + Filter = string.Empty; } public async void UseTheirs(List changes) @@ -538,11 +551,6 @@ namespace SourceGit.ViewModels DoCommit(false, true, false); } - public void CommitWithoutFiles(bool autoPush) - { - DoCommit(false, autoPush, true); - } - public ContextMenu CreateContextMenuForUnstagedChanges() { if (_selectedUnstaged == null || _selectedUnstaged.Count == 0) @@ -903,15 +911,15 @@ namespace SourceGit.ViewModels }; menu.Items.Add(copy); - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, change.Path)); e.Handled = true; }; - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); } else { @@ -1270,17 +1278,17 @@ namespace SourceGit.ViewModels e.Handled = true; }; - var copyFileName = new MenuItem(); - copyFileName.Header = App.Text("CopyFileName"); - copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFileName.Click += (_, e) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Click += (_, e) => { - App.CopyText(Path.GetFileName(change.Path)); + App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, change.Path)); e.Handled = true; }; menu.Items.Add(copyPath); - menu.Items.Add(copyFileName); + menu.Items.Add(copyFullPath); } else { @@ -1472,16 +1480,16 @@ namespace SourceGit.ViewModels return menu; } - private List GetVisibleUnstagedChanges(List unstaged) + private List GetVisibleChanges(List changes) { - if (string.IsNullOrEmpty(_unstagedFilter)) - return unstaged; + if (string.IsNullOrEmpty(_filter)) + return changes; var visible = new List(); - foreach (var c in unstaged) + foreach (var c in changes) { - if (c.Path.Contains(_unstagedFilter, StringComparison.OrdinalIgnoreCase)) + if (c.Path.Contains(_filter, StringComparison.OrdinalIgnoreCase)) visible.Add(c); } @@ -1599,7 +1607,8 @@ namespace SourceGit.ViewModels private async void UnstageChanges(List changes, Models.Change next) { - if (changes.Count == 0) + var count = changes.Count; + if (count == 0) return; // Use `_selectedStaged` instead of `SelectedStaged` to avoid UI refresh. @@ -1611,16 +1620,15 @@ namespace SourceGit.ViewModels { await Task.Run(() => new Commands.UnstageChangesForAmend(_repo.FullPath, changes).Exec()); } - else if (changes.Count == _staged.Count) + else if (count == _staged.Count) { await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec()); } else { - for (int i = 0; i < changes.Count; i += 10) + for (int i = 0; i < count; i += 10) { - var count = Math.Min(10, changes.Count - i); - var step = changes.GetRange(i, count); + var step = changes.GetRange(i, Math.Min(10, count - i)); await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Exec()); } } @@ -1642,7 +1650,7 @@ namespace SourceGit.ViewModels DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), _detailContext as DiffContext); } - private void DoCommit(bool autoStage, bool autoPush, bool allowEmpty) + private void DoCommit(bool autoStage, bool autoPush, bool allowEmpty = false, bool confirmWithFilter = false) { if (!_repo.CanCreatePopup()) { @@ -1650,6 +1658,20 @@ namespace SourceGit.ViewModels return; } + if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && !confirmWithFilter) + { + var confirmMessage = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count); + App.OpenDialog(new Views.ConfirmCommit() + { + DataContext = new ConfirmCommit(confirmMessage, () => + { + DoCommit(autoStage, autoPush, allowEmpty, true); + }) + }); + + return; + } + if (string.IsNullOrWhiteSpace(_commitMessage)) { App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!"); @@ -1660,9 +1682,13 @@ namespace SourceGit.ViewModels { if ((autoStage && _count == 0) || (!autoStage && _staged.Count == 0)) { - App.OpenDialog(new Views.ConfirmCommitWithoutFiles() + var confirmMessage = App.Text("WorkingCopy.ConfirmCommitWithoutFiles"); + App.OpenDialog(new Views.ConfirmCommit() { - DataContext = new ConfirmCommitWithoutFiles(this, autoPush) + DataContext = new ConfirmCommit(confirmMessage, () => + { + DoCommit(autoStage, autoPush, true, confirmWithFilter); + }) }); return; @@ -1729,11 +1755,12 @@ namespace SourceGit.ViewModels private List _unstaged = []; private List _visibleUnstaged = []; private List _staged = []; + private List _visibleStaged = []; private List _selectedUnstaged = []; private List _selectedStaged = []; private int _count = 0; private object _detailContext = null; - private string _unstagedFilter = string.Empty; + private string _filter = string.Empty; private string _commitMessage = string.Empty; private bool _hasUnsolvedConflicts = false; diff --git a/src/Views/ConfirmCommitWithoutFiles.axaml b/src/Views/ConfirmCommit.axaml similarity index 92% rename from src/Views/ConfirmCommitWithoutFiles.axaml rename to src/Views/ConfirmCommit.axaml index 4c9fd1c8..77bbdb30 100644 --- a/src/Views/ConfirmCommitWithoutFiles.axaml +++ b/src/Views/ConfirmCommit.axaml @@ -5,8 +5,8 @@ xmlns:v="using:SourceGit.Views" xmlns:vm="using:SourceGit.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="SourceGit.Views.ConfirmCommitWithoutFiles" - x:DataType="vm:ConfirmCommitWithoutFiles" + x:Class="SourceGit.Views.ConfirmCommit" + x:DataType="vm:ConfirmCommit" x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.Warn}" @@ -38,7 +38,7 @@ - + diff --git a/src/Views/ConfirmCommitWithoutFiles.axaml.cs b/src/Views/ConfirmCommit.axaml.cs similarity index 57% rename from src/Views/ConfirmCommitWithoutFiles.axaml.cs rename to src/Views/ConfirmCommit.axaml.cs index 342600fc..1cf770cb 100644 --- a/src/Views/ConfirmCommitWithoutFiles.axaml.cs +++ b/src/Views/ConfirmCommit.axaml.cs @@ -2,20 +2,16 @@ using Avalonia.Interactivity; namespace SourceGit.Views { - public partial class ConfirmCommitWithoutFiles : ChromelessWindow + public partial class ConfirmCommit : ChromelessWindow { - public ConfirmCommitWithoutFiles() + public ConfirmCommit() { InitializeComponent(); } private void Sure(object _1, RoutedEventArgs _2) { - if (DataContext is ViewModels.ConfirmCommitWithoutFiles vm) - { - vm.Continue(); - } - + (DataContext as ViewModels.ConfirmCommit)?.Continue(); Close(); } diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 4fd8909d..96340d1e 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -195,7 +195,7 @@ - + + diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index 4d79135d..94dd0c30 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -19,13 +19,46 @@ + + + + + + + + + + + + + + - + @@ -75,40 +108,8 @@ - - - - - - - - - - - - - - - - + @@ -155,15 +156,16 @@ ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/> - +