diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..ad62efb6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,68 @@ +name: Build +on: + workflow_call: +jobs: + build: + strategy: + matrix: + include: + - name : Windows x64 + os: windows-2019 + runtime: win-x64 + - name : Windows ARM64 + os: windows-2019 + runtime: win-arm64 + - name : macOS (Intel) + os: macos-13 + runtime: osx-x64 + - name : macOS (Apple Silicon) + os: macos-latest + runtime: osx-arm64 + - name : Linux + os: ubuntu-20.04 + runtime: linux-x64 + - name : Linux (arm64) + os: ubuntu-20.04 + runtime: linux-arm64 + name: Build ${{ matrix.name }} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Configure arm64 packages + if: ${{ matrix.runtime == 'linux-arm64' }} + run: | + sudo dpkg --add-architecture arm64 + echo 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main restricted + deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted + deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted' \ + | sudo tee /etc/apt/sources.list.d/arm64.list + sudo sed -i -e 's/^deb http/deb [arch=amd64] http/g' /etc/apt/sources.list + sudo sed -i -e 's/^deb mirror/deb [arch=amd64] mirror/g' /etc/apt/sources.list + - name: Install cross-compiling dependencies + if: ${{ matrix.runtime == 'linux-arm64' }} + run: | + sudo apt-get update + sudo apt-get install clang llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64 + - name: Build + run: dotnet build -c Release + - name: Publish + run: dotnet publish src/SourceGit.csproj -c Release -o publish -r ${{ matrix.runtime }} + - name: Rename executable file + if: ${{ startsWith(matrix.runtime, 'linux-') }} + run: mv publish/SourceGit publish/sourcegit + - name: Tar artifact + if: ${{ startsWith(matrix.runtime, 'linux-') || startsWith(matrix.runtime, 'osx-') }} + run: | + tar -cvf "sourcegit.${{ matrix.runtime }}.tar" -C publish . + rm -r publish/* + mv "sourcegit.${{ matrix.runtime }}.tar" publish + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: sourcegit.${{ matrix.runtime }} + path: publish/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87d460c6..50e02dc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,66 +8,22 @@ on: workflow_call: jobs: build: - strategy: - matrix: - include: - - name : Windows x64 - os: windows-2019 - runtime: win-x64 - - name : Windows ARM64 - os: windows-2019 - runtime: win-arm64 - - name : macOS (Intel) - os: macos-13 - runtime: osx-x64 - - name : macOS (Apple Silicon) - os: macos-latest - runtime: osx-arm64 - - name : Linux - os: ubuntu-20.04 - runtime: linux-x64 - - name : Linux (arm64) - os: ubuntu-20.04 - runtime: linux-arm64 - name: Build ${{ matrix.name }} - runs-on: ${{ matrix.os }} + name: Build + uses: ./.github/workflows/build.yml + version: + name: Prepare version string + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} steps: - name: Checkout sources uses: actions/checkout@v4 - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - name: Configure arm64 packages - if: ${{ matrix.runtime == 'linux-arm64' }} - run: | - sudo dpkg --add-architecture arm64 - echo 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main restricted - deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted - deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted' \ - | sudo tee /etc/apt/sources.list.d/arm64.list - sudo sed -i -e 's/^deb http/deb [arch=amd64] http/g' /etc/apt/sources.list - sudo sed -i -e 's/^deb mirror/deb [arch=amd64] mirror/g' /etc/apt/sources.list - - name: Install cross-compiling dependencies - if: ${{ matrix.runtime == 'linux-arm64' }} - run: | - sudo apt-get update - sudo apt-get install clang llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64 - - name: Build - run: dotnet build -c Release - - name: Publish - run: dotnet publish src/SourceGit.csproj -c Release -o publish -r ${{ matrix.runtime }} - - name: Rename executable file - if: ${{ startsWith(matrix.runtime, 'linux-') }} - run: mv publish/SourceGit publish/sourcegit - - name: Tar artifact - if: ${{ startsWith(matrix.runtime, 'linux-') }} - run: | - tar -cvf "sourcegit.${{ matrix.runtime }}.tar" -C publish . - rm -r publish/* - mv "sourcegit.${{ matrix.runtime }}.tar" publish - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: sourcegit.${{ matrix.runtime }} - path: publish + - name: Output version string + id: version + run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT" + package: + needs: [build, version] + name: Package + uses: ./.github/workflows/package.yml + with: + version: ${{ needs.version.outputs.version }} diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 8c0c192d..53affe3d 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -7,12 +7,8 @@ on: required: true type: string jobs: - build: - name: Build - uses: ./.github/workflows/ci.yml windows-portable: name: Package portable Windows app - needs: build runs-on: ubuntu-latest strategy: matrix: @@ -35,10 +31,13 @@ jobs: with: name: package.${{ matrix.runtime }} path: build/sourcegit_*.zip + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} osx-app: name: Package OSX app - needs: build - runs-on: ubuntu-latest + runs-on: macos-latest strategy: matrix: runtime: [osx-x64, osx-arm64] @@ -49,20 +48,26 @@ jobs: uses: actions/download-artifact@v4 with: name: sourcegit.${{ matrix.runtime }} - path: build/SourceGit + path: build - name: Package env: VERSION: ${{ inputs.version }} RUNTIME: ${{ matrix.runtime }} - run: ./build/scripts/package.osx-app.sh + run: | + mkdir build/SourceGit + tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit + ./build/scripts/package.osx-app.sh - name: Upload package artifact uses: actions/upload-artifact@v4 with: name: package.${{ matrix.runtime }} path: build/sourcegit_*.zip + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} linux: name: Package Linux - needs: build runs-on: ubuntu-latest strategy: matrix: @@ -96,3 +101,7 @@ jobs: build/sourcegit-*.AppImage build/sourcegit_*.deb build/sourcegit-*.rpm + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 51e247d2..c19103e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: tags: - v* jobs: + build: + name: Build + uses: ./.github/workflows/build.yml version: name: Prepare version string runs-on: ubuntu-latest @@ -16,13 +19,13 @@ jobs: TAG: ${{ github.ref_name }} run: echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" package: - needs: version + needs: [build, version] name: Package uses: ./.github/workflows/package.yml with: version: ${{ needs.version.outputs.version }} release: - needs: [version, package] + needs: [package, version] name: Release runs-on: ubuntu-latest permissions: @@ -34,7 +37,8 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ github.ref_name }} - run: gh release create "$TAG" -t "Release ${TAG#v}" --notes-from-tag + VERSION: ${{ needs.version.outputs.version }} + run: gh release create "$TAG" -t "Release $VERSION" --notes-from-tag - name: Download artifacts uses: actions/download-artifact@v4 with: @@ -45,5 +49,4 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ github.ref_name }} - VERSION: ${{ needs.version.outputs.version }} run: gh release upload "$TAG" packages/* diff --git a/README.md b/README.md index 50d75259..cbba9388 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,6 @@ For **Linux** users: * `xdg-open` must be installed to support open native file manager. * Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your linux. -* Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI. ## External Tools @@ -123,7 +122,7 @@ This app supports open repository in external tools listed in the table below. * Custom - You can find custom themes from [sourcegit-theme](https://github.com/sourcegit-scm/sourcegit-theme.git) + You can find custom themes from [sourcegit-theme](https://github.com/sourcegit-scm/sourcegit-theme.git). And welcome to share your own themes. ## Contributing diff --git a/VERSION b/VERSION index 21783978..eeb14e9b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.26 \ No newline at end of file +8.27 \ No newline at end of file diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec index 86a7cfdd..289cbe39 100644 --- a/build/resources/rpm/SPECS/build.spec +++ b/build/resources/rpm/SPECS/build.spec @@ -5,8 +5,8 @@ Summary: Open-source & Free Git Gui Client License: MIT URL: https://sourcegit-scm.github.io/ Source: https://github.com/sourcegit-scm/sourcegit/archive/refs/tags/v%_version.tar.gz -Requires: libX11.so.6 -Requires: libSM.so.6 +Requires: libX11 +Requires: libSM %define _build_id_links none @@ -19,7 +19,7 @@ mkdir -p %{buildroot}/%{_bindir} mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/icons cp -f ../../../SourceGit/* %{buildroot}/opt/sourcegit/ -ln -sf ../../opt/sourcegit/sourcegit %{buildroot}/%{_bindir} +ln -rsf %{buildroot}/opt/sourcegit/sourcegit %{buildroot}/%{_bindir} cp -r ../../_common/applications %{buildroot}/%{_datadir} cp -r ../../_common/icons %{buildroot}/%{_datadir} chmod 755 -R %{buildroot}/opt/sourcegit diff --git a/build/scripts/package.linux.sh b/build/scripts/package.linux.sh index 04309018..5eceffd6 100755 --- a/build/scripts/package.linux.sh +++ b/build/scripts/package.linux.sh @@ -60,7 +60,7 @@ mkdir -p resources/deb/usr/bin mkdir -p resources/deb/usr/share/applications mkdir -p resources/deb/usr/share/icons cp -f SourceGit/* resources/deb/opt/sourcegit -ln -sf ../../opt/sourcegit/sourcegit resources/deb/usr/bin +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 diff --git a/build/scripts/package.osx-app.sh b/build/scripts/package.osx-app.sh index 80e66a08..5d908956 100755 --- a/build/scripts/package.osx-app.sh +++ b/build/scripts/package.osx-app.sh @@ -18,5 +18,6 @@ mkdir -p SourceGit.app/Contents/Resources mv SourceGit SourceGit.app/Contents/MacOS cp resources/app/App.icns SourceGit.app/Contents/Resources/App.icns sed "s/SOURCE_GIT_VERSION/$VERSION/g" resources/app/App.plist > SourceGit.app/Contents/Info.plist +rm -rf SourceGit.app/Contents/MacOS/SourceGit.dsym -zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit.app -x "*/*\.dsym/*" +zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit.app diff --git a/src/App.Commands.cs b/src/App.Commands.cs new file mode 100644 index 00000000..55241afc --- /dev/null +++ b/src/App.Commands.cs @@ -0,0 +1,65 @@ +using System; +using System.Windows.Input; +using Avalonia.Controls; + +namespace SourceGit +{ + public partial class App + { + public class SimpleCommand : ICommand + { + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + + public SimpleCommand(Action action) + { + _action = action; + } + + public bool CanExecute(object parameter) => _action != null; + public void Execute(object parameter) => _action?.Invoke(); + + private Action _action = null; + } + + public class ParameterCommand : ICommand + { + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + + public ParameterCommand(Action action) + { + _action = action; + } + + public bool CanExecute(object parameter) => _action != null; + public void Execute(object parameter) => _action?.Invoke(parameter); + + private Action _action = null; + } + + public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); + public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); + public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); + public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => OpenDialog(new Views.About())); + public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => Check4Update(true)); + public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + + public static readonly ParameterCommand CopyTextCommand = new ParameterCommand(param => + { + if (param is TextBlock textBlock) + { + if (textBlock.Inlines is { Count: > 0 } inlines) + CopyText(inlines.Text); + else + CopyText(textBlock.Text); + } + }); + } +} diff --git a/src/App.JsonCodeGen.cs b/src/App.JsonCodeGen.cs index 5d7b114e..8daea4f9 100644 --- a/src/App.JsonCodeGen.cs +++ b/src/App.JsonCodeGen.cs @@ -20,20 +20,6 @@ namespace SourceGit } } - public class FontFamilyConverter : JsonConverter - { - public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var name = reader.GetString(); - return new FontFamily(name); - } - - public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } - public class GridLengthConverter : JsonConverter { public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -54,7 +40,6 @@ namespace SourceGit IgnoreReadOnlyProperties = true, Converters = [ typeof(ColorConverter), - typeof(FontFamilyConverter), typeof(GridLengthConverter), ] )] diff --git a/src/App.axaml b/src/App.axaml index 1f3a66e0..1831f59c 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -13,6 +13,7 @@ + diff --git a/src/App.axaml.cs b/src/App.axaml.cs index a1eaee6a..5dab4836 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -6,7 +6,6 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading.Tasks; -using System.Windows.Input; using Avalonia; using Avalonia.Controls; @@ -15,30 +14,12 @@ using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Fonts; +using Avalonia.Platform.Storage; using Avalonia.Styling; using Avalonia.Threading; namespace SourceGit { - public class SimpleCommand : ICommand - { - public event EventHandler CanExecuteChanged - { - add { } - remove { } - } - - public SimpleCommand(Action action) - { - _action = action; - } - - public bool CanExecute(object parameter) => _action != null; - public void Execute(object parameter) => _action?.Invoke(); - - private Action _action = null; - } - public partial class App : Application { [STAThread] @@ -89,47 +70,37 @@ namespace SourceGit return builder; } - public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => + public override void Initialize() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + AvaloniaXamlLoader.Load(this); - var dialog = new Views.Preference(); - dialog.ShowDialog(toplevel); - }); + var pref = ViewModels.Preference.Instance; + SetLocale(pref.Locale); + SetTheme(pref.Theme, pref.ThemeOverrides); + SetFonts(pref.DefaultFontFamily, pref.MonospaceFontFamily, pref.OnlyUseMonoFontInEditor); + } - public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => + public override void OnFrameworkInitializationCompleted() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + BindingPlugins.DataValidators.RemoveAt(0); - var dialog = new Views.Hotkeys(); - dialog.ShowDialog(toplevel); - }); + if (TryLaunchedAsCoreEditor(desktop)) + return; - public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => + if (TryLaunchedAsAskpass(desktop)) + return; + + TryLaunchedAsNormal(desktop); + } + } + + public static void OpenDialog(Window window) { - Native.OS.OpenInFileManager(Native.OS.DataDir); - }); - - public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => - { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; - - var dialog = new Views.About(); - dialog.ShowDialog(toplevel); - }); - - public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => - { - Check4Update(true); - }); - - public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner}) + window.ShowDialog(owner); + } public static void RaiseException(string context, string message) { @@ -146,7 +117,10 @@ namespace SourceGit public static void SetLocale(string localeKey) { var app = Current as App; - var targetLocale = app?.Resources[localeKey] as ResourceDictionary; + if (app == null) + return; + + var targetLocale = app.Resources[localeKey] as ResourceDictionary; if (targetLocale == null || targetLocale == app._activeLocale) return; @@ -211,12 +185,55 @@ namespace SourceGit } } + public static void SetFonts(string defaultFont, string monospaceFont, bool onlyUseMonospaceFontInEditor) + { + var app = Current as App; + if (app == null) + return; + + if (app._fontsOverrides != null) + { + app.Resources.MergedDictionaries.Remove(app._fontsOverrides); + app._fontsOverrides = null; + } + + var resDic = new ResourceDictionary(); + if (!string.IsNullOrEmpty(defaultFont)) + resDic.Add("Fonts.Default", new FontFamily(defaultFont)); + + if (string.IsNullOrEmpty(monospaceFont)) + { + if (!string.IsNullOrEmpty(defaultFont)) + { + monospaceFont = $"fonts:SourceGit#JetBrains Mono,{defaultFont}"; + resDic.Add("Fonts.Monospace", new FontFamily(monospaceFont)); + } + } + else + { + if (!string.IsNullOrEmpty(defaultFont) && !monospaceFont.Contains(defaultFont, StringComparison.Ordinal)) + monospaceFont = $"{monospaceFont},{defaultFont}"; + + resDic.Add("Fonts.Monospace", new FontFamily(monospaceFont)); + } + + var primary = onlyUseMonospaceFontInEditor ? defaultFont : monospaceFont; + if (!string.IsNullOrEmpty(primary)) + resDic.Add("Fonts.Primary", new FontFamily(primary)); + + if (resDic.Count > 0) + { + app.Resources.MergedDictionaries.Add(resDic); + app._fontsOverrides = resDic; + } + } + public static async void CopyText(string data) { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { if (desktop.MainWindow?.Clipboard is { } clipboard) - await clipboard.SetTextAsync(data); + await clipboard.SetTextAsync(data ?? ""); } } @@ -258,17 +275,74 @@ namespace SourceGit return icon; } - public static TopLevel GetTopLevel() + public static IStorageProvider GetStorageProvider() { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + return desktop.MainWindow?.StorageProvider; + + return null; + } + + public static ViewModels.Launcher GetLauncer() + { + return Current is App app ? app._launcher : null; + } + + public static ViewModels.Repository FindOpenedRepository(string repoPath) + { + if (Current is App app && app._launcher != null) { - return desktop.MainWindow; + foreach (var page in app._launcher.Pages) + { + var id = page.Node.Id.Replace("\\", "/"); + if (id == repoPath && page.Data is ViewModels.Repository repo) + return repo; + } } return null; } - public static void Check4Update(bool manually = false) + public static void Quit(int exitCode) + { + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow?.Close(); + desktop.Shutdown(exitCode); + } + else + { + Environment.Exit(exitCode); + } + } + + private static void LogException(Exception ex) + { + if (ex == null) + return; + + var builder = new StringBuilder(); + builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); + builder.Append("----------------------------\n"); + builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); + builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); + builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); + builder.Append($"Source: {ex.Source}\n"); + builder.Append($"---------------------------\n\n"); + builder.Append(ex.StackTrace); + while (ex.InnerException != null) + { + ex = ex.InnerException; + builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); + builder.Append(ex.StackTrace); + } + + var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); + File.WriteAllText(file, builder.ToString()); + } + + private static void Check4Update(bool manually = false) { Task.Run(async () => { @@ -309,104 +383,11 @@ namespace SourceGit }); } - public static ViewModels.Launcher GetLauncer() - { - return Current is App app ? app._launcher : null; - } - - public static ViewModels.Repository FindOpenedRepository(string repoPath) - { - if (Current is App app && app._launcher != null) - { - foreach (var page in app._launcher.Pages) - { - var id = page.Node.Id.Replace("\\", "/"); - if (id == repoPath && page.Data is ViewModels.Repository repo) - return repo; - } - } - - return null; - } - - public static void Quit(int exitCode) - { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow?.Close(); - desktop.Shutdown(exitCode); - } - else - { - Environment.Exit(exitCode); - } - } - - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - - var pref = ViewModels.Preference.Instance; - - SetLocale(pref.Locale); - SetTheme(pref.Theme, pref.ThemeOverrides); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - BindingPlugins.DataValidators.RemoveAt(0); - - if (TryLaunchedAsCoreEditor(desktop)) - return; - - if (TryLaunchedAsAskpass(desktop)) - return; - - TryLaunchedAsNormal(desktop); - } - } - - private static void LogException(Exception ex) - { - if (ex == null) - return; - - var builder = new StringBuilder(); - builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); - builder.Append("----------------------------\n"); - builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); - builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); - builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); - builder.Append($"Source: {ex.Source}\n"); - builder.Append($"---------------------------\n\n"); - builder.Append(ex.StackTrace); - while (ex.InnerException != null) - { - ex = ex.InnerException; - builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); - builder.Append(ex.StackTrace); - } - - var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); - File.WriteAllText(file, builder.ToString()); - } - private static void ShowSelfUpdateResult(object data) { Dispatcher.UIThread.Post(() => { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop) - { - var dialog = new Views.SelfUpdate() - { - DataContext = new ViewModels.SelfUpdate() { Data = data } - }; - - dialog.Show(desktop.MainWindow); - } + OpenDialog(new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } }); }); } @@ -549,5 +530,6 @@ namespace SourceGit private ViewModels.Launcher _launcher = null; private ResourceDictionary _activeLocale = null; private ResourceDictionary _themeOverrides = null; + private ResourceDictionary _fontsOverrides = null; } } diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs index 31a69eb9..69b859ab 100644 --- a/src/Commands/Push.cs +++ b/src/Commands/Push.cs @@ -4,7 +4,7 @@ namespace SourceGit.Commands { public class Push : Command { - public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool force, bool track, Action onProgress) + public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, Action onProgress) { _outputHandler = onProgress; @@ -16,6 +16,8 @@ namespace SourceGit.Commands if (withTags) Args += "--tags "; + if (checkSubmodules) + Args += "--recurse-submodules=check "; if (track) Args += "-u "; if (force) diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index d58f42f4..bdef9bf8 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -28,8 +28,6 @@ namespace SourceGit.Commands var match = REG_FORMAT().Match(line); if (!match.Success) return; - if (line.EndsWith("/", StringComparison.Ordinal)) - return; // Ignore changes with git-worktree var change = new Models.Change() { Path = match.Groups[2].Value }; var status = match.Groups[1].Value; diff --git a/src/Commands/QueryRefsContainsCommit.cs b/src/Commands/QueryRefsContainsCommit.cs new file mode 100644 index 00000000..e3b73ccc --- /dev/null +++ b/src/Commands/QueryRefsContainsCommit.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace SourceGit.Commands +{ + public class QueryRefsContainsCommit : Command + { + public QueryRefsContainsCommit(string repo, string commit) + { + WorkingDirectory = repo; + RaiseError = false; + Args = $"for-each-ref --format=\"%(refname)\" --contains {commit}"; + } + + public List Result() + { + var rs = new List(); + + var output = ReadToEnd(); + if (!output.IsSuccess) + return rs; + + var lines = output.StdOut.Split('\n'); + foreach (var line in lines) + { + if (line.StartsWith("refs/heads/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/heads/".Length), Type = Models.DecoratorType.LocalBranchHead }); + else if (line.StartsWith("refs/remotes/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/remotes/".Length), Type = Models.DecoratorType.RemoteBranchHead }); + else if (line.StartsWith("refs/tags/", StringComparison.Ordinal)) + rs.Add(new() { Name = line.Substring("refs/tags/".Length), Type = Models.DecoratorType.Tag }); + } + + return rs; + } + } +} diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs index 3e784f60..cf7d84d1 100644 --- a/src/Commands/Stash.cs +++ b/src/Commands/Stash.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using System.IO; +using System.Text; namespace SourceGit.Commands { @@ -19,14 +19,11 @@ namespace SourceGit.Commands public bool Push(List changes, string message) { - var temp = Path.GetTempFileName(); - var stream = new FileStream(temp, FileMode.Create); - var writer = new StreamWriter(stream); - + var pathsBuilder = new StringBuilder(); var needAdd = new List(); foreach (var c in changes) { - writer.WriteLine(c.Path); + pathsBuilder.Append($"\"{c.Path}\" "); if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked) { @@ -44,15 +41,9 @@ namespace SourceGit.Commands needAdd.Clear(); } - writer.Flush(); - stream.Flush(); - writer.Close(); - stream.Close(); - - Args = $"stash push -m \"{message}\" --pathspec-from-file=\"{temp}\""; - var succ = Exec(); - File.Delete(temp); - return succ; + var paths = pathsBuilder.ToString(); + Args = $"stash push -m \"{message}\" -- {paths}"; + return Exec(); } public bool Apply(string name) diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 6f371594..55746a43 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -97,8 +97,16 @@ namespace SourceGit.Models public int Color; } + public enum DotType + { + Default, + Head, + Merge, + } + public class Dot { + public DotType Type; public Point Center; public int Color; } @@ -128,12 +136,13 @@ namespace SourceGit.Models _penCount = colors.Count; } - public static CommitGraph Parse(List commits) + public static CommitGraph Parse(List commits, bool firstParentOnlyEnabled) { double UNIT_WIDTH = 12; double HALF_WIDTH = 6; double UNIT_HEIGHT = 28; double HALF_HEIGHT = 14; + double H_MARGIN = 2; var temp = new CommitGraph(); var unsolved = new List(); @@ -152,7 +161,7 @@ namespace SourceGit.Models offsetY += UNIT_HEIGHT; // Find first curves that links to this commit and marks others that links to this commit ended. - double offsetX = -HALF_WIDTH; + double offsetX = H_MARGIN - HALF_WIDTH; foreach (var l in unsolved) { if (l.Next == commit.SHA) @@ -204,45 +213,54 @@ namespace SourceGit.Models // Calculate link position of this commit. Point position = new Point(offsetX, offsetY); + int dotColor = 0; if (major != null) { major.IsMerged = isMerged; position = new Point(major.LastX, offsetY); - temp.Dots.Add(new Dot() { Center = position, Color = major.Path.Color }); + dotColor = major.Path.Color; } + + Dot anchor = new Dot() { Center = position, Color = dotColor }; + if (commit.IsCurrentHead) + anchor.Type = DotType.Head; + else if (commit.Parents.Count > 1) + anchor.Type = DotType.Merge; else + anchor.Type = DotType.Default; + temp.Dots.Add(anchor); + + // Deal with other parents (the first parent has been processed) + if (!firstParentOnlyEnabled) { - temp.Dots.Add(new Dot() { Center = position, Color = 0 }); - } - - // Deal with parents - for (int j = 1; j < commit.Parents.Count; j++) - { - var parent = commit.Parents[j]; - if (mapUnsolved.TryGetValue(parent, out var value)) + for (int j = 1; j < commit.Parents.Count; j++) { - // Try to change the merge state of linked graph - var l = value; - if (isMerged) - l.IsMerged = true; + var parent = commit.Parents[j]; + if (mapUnsolved.TryGetValue(parent, out var value)) + { + // Try to change the merge state of linked graph + var l = value; + if (isMerged) + l.IsMerged = true; - var link = new Link(); - link.Start = position; - link.End = new Point(l.LastX, offsetY + HALF_HEIGHT); - link.Control = new Point(link.End.X, link.Start.Y); - link.Color = l.Path.Color; + var link = new Link(); + link.Start = position; + link.End = new Point(l.LastX, offsetY + HALF_HEIGHT); + link.Control = new Point(link.End.X, link.Start.Y); + link.Color = l.Path.Color; - temp.Links.Add(link); - } - else - { - offsetX += UNIT_WIDTH; + temp.Links.Add(link); + } + else + { + offsetX += UNIT_WIDTH; - // Create new curve for parent commit that not includes before - var l = new PathHelper(commit.Parents[j], isMerged, colorIdx, position, new Point(offsetX, position.Y + HALF_HEIGHT)); - unsolved.Add(l); - temp.Paths.Add(l.Path); - colorIdx = (colorIdx + 1) % _penCount; + // Create new curve for parent commit that not includes before + var l = new PathHelper(parent, isMerged, colorIdx, position, new Point(offsetX, position.Y + HALF_HEIGHT)); + unsolved.Add(l); + temp.Paths.Add(l.Path); + colorIdx = (colorIdx + 1) % _penCount; + } } } @@ -255,7 +273,7 @@ namespace SourceGit.Models // Margins & merge state (used by datagrid). commit.IsMerged = isMerged; - commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH), 0, 0, 0); + commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH + H_MARGIN) + H_MARGIN, 0, 0, 0); // Clean up ended.Clear(); @@ -271,7 +289,7 @@ namespace SourceGit.Models if (path.Path.Points.Count == 1 && Math.Abs(path.Path.Points[0].Y - endY) < 0.0001) continue; - path.Add((i + 0.5) * UNIT_WIDTH, endY + HALF_HEIGHT, HALF_HEIGHT, true); + path.Add((i + 0.5) * UNIT_WIDTH + H_MARGIN, endY + HALF_HEIGHT, HALF_HEIGHT, true); } unsolved.Clear(); diff --git a/src/Models/Decorator.cs b/src/Models/Decorator.cs index 235101cc..7d985e31 100644 --- a/src/Models/Decorator.cs +++ b/src/Models/Decorator.cs @@ -14,5 +14,6 @@ { public DecoratorType Type { get; set; } = DecoratorType.None; public string Name { get; set; } = ""; + public bool IsTag => Type == DecoratorType.Tag; } } diff --git a/src/Models/Hyperlink.cs b/src/Models/Hyperlink.cs new file mode 100644 index 00000000..81dc980e --- /dev/null +++ b/src/Models/Hyperlink.cs @@ -0,0 +1,29 @@ +namespace SourceGit.Models +{ + public class Hyperlink + { + public int Start { get; set; } = 0; + public int Length { get; set; } = 0; + public string Link { get; set; } = ""; + public bool IsCommitSHA { get; set; } = false; + + public Hyperlink(int start, int length, string link, bool isCommitSHA = false) + { + Start = start; + Length = length; + Link = link; + IsCommitSHA = isCommitSHA; + } + + public bool Intersect(int start, int length) + { + if (start == Start) + return true; + + if (start < Start) + return start + length > Start; + + return start < Start + Length; + } + } +} diff --git a/src/Models/IssueTrackerRule.cs b/src/Models/IssueTrackerRule.cs index 618fa166..29487a16 100644 --- a/src/Models/IssueTrackerRule.cs +++ b/src/Models/IssueTrackerRule.cs @@ -1,29 +1,10 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -using Avalonia.Controls.Documents; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.Models { - public class IssueTrackerMatch - { - public int Start { get; set; } = 0; - public int Length { get; set; } = 0; - public string URL { get; set; } = ""; - public Run Link { get; set; } = null; - - public bool Intersect(int start, int length) - { - if (start == Start) - return true; - - if (start < Start) - return start + length > Start; - - return start < Start + Length; - } - } - public class IssueTrackerRule : ObservableObject { public string Name @@ -65,7 +46,7 @@ namespace SourceGit.Models set => SetProperty(ref _urlTemplate, value); } - public void Matches(List outs, string message) + public void Matches(List outs, string message) { if (_regex == null || string.IsNullOrEmpty(_urlTemplate)) return; @@ -92,17 +73,15 @@ namespace SourceGit.Models if (intersect) continue; - var range = new IssueTrackerMatch(); - range.Start = start; - range.Length = len; - range.URL = _urlTemplate; + var link = _urlTemplate; for (var j = 1; j < match.Groups.Count; j++) { var group = match.Groups[j]; if (group.Success) - range.URL = range.URL.Replace($"${j}", group.Value); + link = link.Replace($"${j}", group.Value); } + var range = new Hyperlink(start, len, link); outs.Add(range); } } diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs index b660c294..51121b5f 100644 --- a/src/Models/Locales.cs +++ b/src/Models/Locales.cs @@ -10,6 +10,7 @@ namespace SourceGit.Models public static readonly List Supported = new List() { new Locale("English", "en_US"), new Locale("Deutsch", "de_DE"), + new Locale("Français", "fr_FR"), new Locale("Português (Brasil)", "pt_BR"), new Locale("简体中文", "zh_CN"), new Locale("繁體中文", "zh_TW"), diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs index 788e00a8..06e60a87 100644 --- a/src/Models/RepositorySettings.cs +++ b/src/Models/RepositorySettings.cs @@ -40,6 +40,12 @@ namespace SourceGit.Models set; } = true; + public bool CheckSubmodulesOnPush + { + get; + set; + } = true; + public bool PushAllTags { get; diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index 9d444dae..bd973f5f 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.Versioning; using Avalonia; -using Avalonia.Media; namespace SourceGit.Native { @@ -37,11 +36,6 @@ namespace SourceGit.Native public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "fonts:SourceGit#JetBrains Mono", - }); - builder.With(new X11PlatformOptions() { EnableIme = true, diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index e034c674..cf7b5b4c 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -6,20 +6,20 @@ using System.Runtime.Versioning; using System.Text; using Avalonia; -using Avalonia.Media; namespace SourceGit.Native { [SupportedOSPlatform("macOS")] internal class MacOS : OS.IBackend { + enum TerminalType + { + Default, + iTerm2, + } + public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "PingFang SC", - }); - builder.With(new MacOSPlatformOptions() { DisableDefaultApplicationMenuItems = true, @@ -28,7 +28,6 @@ namespace SourceGit.Native public string FindGitExecutable() { - // XCode built-in git return File.Exists("/usr/bin/git") ? "/usr/bin/git" : string.Empty; } @@ -52,13 +51,9 @@ namespace SourceGit.Native public void OpenInFileManager(string path, bool select) { if (Directory.Exists(path)) - { Process.Start("open", $"\"{path}\""); - } else if (File.Exists(path)) - { Process.Start("open", $"\"{path}\" -R"); - } } public void OpenTerminal(string workdir) @@ -66,16 +61,32 @@ namespace SourceGit.Native var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir; dir = dir.Replace(" ", "\\ "); - var builder = new StringBuilder(); - builder.AppendLine("on run argv"); - builder.AppendLine(" tell application \"Terminal\""); - builder.AppendLine($" do script \"cd {dir}\""); - builder.AppendLine(" activate"); - builder.AppendLine(" end tell"); - builder.AppendLine("end run"); + var terminal = DetectTerminal(); + var cmdBuilder = new StringBuilder(); + switch (terminal) + { + case TerminalType.iTerm2: + cmdBuilder.AppendLine("on run argv"); + cmdBuilder.AppendLine(" tell application \"iTerm2\""); + cmdBuilder.AppendLine(" create window with default profile"); + cmdBuilder.AppendLine(" tell the current session of the current window"); + cmdBuilder.AppendLine($" write text \"cd {dir}\""); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine("end run"); + break; + default: + cmdBuilder.AppendLine("on run argv"); + cmdBuilder.AppendLine(" tell application \"Terminal\""); + cmdBuilder.AppendLine($" do script \"cd {dir}\""); + cmdBuilder.AppendLine(" activate"); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine("end run"); + break; + } var tmp = Path.GetTempFileName(); - File.WriteAllText(tmp, builder.ToString()); + File.WriteAllText(tmp, cmdBuilder.ToString()); var proc = Process.Start("osascript", $"\"{tmp}\""); if (proc != null) @@ -86,7 +97,15 @@ namespace SourceGit.Native public void OpenWithDefaultEditor(string file) { - Process.Start("open", file); + Process.Start("open", $"\"{file}\""); + } + + private TerminalType DetectTerminal() + { + if (Directory.Exists("/Applications/iTerm.app")) + return TerminalType.iTerm2; + + return TerminalType.Default; } } } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index d8203d13..18326fa6 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -8,7 +8,6 @@ using System.Text; using Avalonia; using Avalonia.Controls; -using Avalonia.Media; namespace SourceGit.Native { @@ -62,12 +61,6 @@ namespace SourceGit.Native public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "Microsoft YaHei UI", - FontFallbacks = [new FontFallback { FontFamily = "Microsoft YaHei" }], - }); - // Fix drop shadow issue on Windows 10 RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX(); v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(); @@ -190,6 +183,7 @@ namespace SourceGit.Native else { fullpath = new DirectoryInfo(path!).FullName; + fullpath += Path.DirectorySeparatorChar; } if (select) @@ -209,7 +203,7 @@ namespace SourceGit.Native public void OpenWithDefaultEditor(string file) { var info = new FileInfo(file); - var start = new ProcessStartInfo("cmd", $"/c start {info.FullName}"); + var start = new ProcessStartInfo("cmd", $"/c start \"\" \"{info.FullName}\""); start.CreateNoWindow = true; Process.Start(start); } diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 7952105c..bbef234a 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -38,6 +38,7 @@ M416 832H128V128h384v192C512 355 541 384 576 384L768 384v32c0 19 13 32 32 32S832 435 832 416v-64c0-6 0-19-6-25l-256-256c-6-6-19-6-25-6H128A64 64 0 0064 128v704C64 867 93 896 129 896h288c19 0 32-13 32-32S435 832 416 832zM576 172 722 320H576V172zM736 512C614 512 512 614 512 736S614 960 736 960s224-102 224-224S858 512 736 512zM576 736C576 646 646 576 736 576c32 0 58 6 83 26l-218 218c-19-26-26-51-26-83zm160 160c-32 0-64-13-96-32l224-224c19 26 32 58 32 96 0 90-70 160-160 160z M896 320c0-19-6-32-19-45l-192-192c-13-13-26-19-45-19H192c-38 0-64 26-64 64v768c0 38 26 64 64 64h640c38 0 64-26 64-64V320zm-256 384H384c-19 0-32-13-32-32s13-32 32-32h256c19 0 32 13 32 32s-13 32-32 32zm166-384H640V128l192 192h-26z M599 425 599 657 425 832 425 425 192 192 832 192Z + m211 611a142 142 0 00-90-4v-190a142 142 0 0090-4v198zm0 262v150h-90v-146a142 142 0 0090-4zm0-723a142 142 0 00-90-4v-146h90zm-51 246a115 115 0 11115-115 115 115 0 01-115 115zm0 461a115 115 0 11115-115 115 115 0 01-115 115zm256-691h563v90h-563zm0 461h563v90h-563zm0-282h422v90h-422zm0 474h422v90h-422z M853 267H514c-4 0-6-2-9-4l-38-66c-13-21-38-36-64-36H171c-41 0-75 34-75 75v555c0 41 34 75 75 75h683c41 0 75-34 75-75V341c0-41-34-75-75-75zm-683-43h233c4 0 6 2 9 4l38 66c13 21 38 36 64 36H853c6 0 11 4 11 11v75h-704V235c0-6 4-11 11-11zm683 576H171c-6 0-11-4-11-11V480h704V789c0 6-4 11-11 11z M1088 227H609L453 78a11 11 0 00-7-3H107a43 43 0 00-43 43v789a43 43 0 0043 43h981a43 43 0 0043-43V270a43 43 0 00-43-43zM757 599c0 5-5 9-10 9h-113v113c0 5-4 9-9 9h-56c-5 0-9-4-9-9V608h-113c-5 0-10-4-10-9V543c0-5 5-9 10-9h113V420c0-5 4-9 9-9h56c5 0 9 4 9 9V533h113c5 0 10 4 10 9v56z M922 450c-6-9-15-13-26-13h-11V341c0-41-34-75-75-75H514c-4 0-6-2-9-4l-38-66c-13-21-38-36-64-36H171c-41 0-75 34-75 75v597c0 6 2 13 6 19 6 9 15 13 26 13h640c13 0 26-9 30-21l128-363c4-11 2-21-4-30zM171 224h233c4 0 6 2 9 4l38 66c13 21 38 36 64 36H811c6 0 11 4 11 11v96H256c-13 0-26 9-30 21l-66 186V235c0-6 4-11 11-11zm574 576H173l105-299h572l-105 299z @@ -55,7 +56,6 @@ M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z M512 64A447 447 0 0064 512c0 248 200 448 448 448s448-200 448-448S760 64 512 64zM218 295h31c54 0 105 19 145 55 13 12 13 31 3 43a35 35 0 01-22 10 36 36 0 01-21-7 155 155 0 00-103-39h-31a32 32 0 01-31-31c0-18 13-31 30-31zm31 433h-31a32 32 0 01-31-31c0-16 13-31 31-31h31A154 154 0 00403 512 217 217 0 01620 295h75l-93-67a33 33 0 01-7-43 33 33 0 0143-7l205 148-205 148a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67H620a154 154 0 00-154 154c0 122-97 220-217 220zm390 118a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67h-75c-52 0-103-19-143-54-12-12-13-31-1-43a30 30 0 0142-3 151 151 0 00102 39h75L602 599a33 33 0 01-7-43 33 33 0 0143-7l205 148-203 151z M922 39H102A65 65 0 0039 106v609a65 65 0 0063 68h94v168a34 34 0 0019 31 30 30 0 0012 3 30 30 0 0022-10l182-192H922a65 65 0 0063-68V106A65 65 0 00922 39zM288 378h479a34 34 0 010 68H288a34 34 0 010-68zm0-135h479a34 34 0 010 68H288a34 34 0 010-68zm0 270h310a34 34 0 010 68H288a34 34 0 010-68z - M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zM139 832V192c0-6 4-11 11-11h331v661H149c-6 0-11-4-11-11zm747 0c0 6-4 11-11 11H544v-661H875c6 0 11 4 11 11v640z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zm-725 64h725c6 0 11 4 11 11v288h-747V192c0-6 4-11 11-11zm725 661H149c-6 0-11-4-11-11V544h747V832c0 6-4 11-11 11z M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z @@ -74,11 +74,13 @@ M896 64H128C96 64 64 96 64 128v768c0 32 32 64 64 64h768c32 0 64-32 64-64V128c0-32-32-64-64-64z m-64 736c0 16-17 32-32 32H224c-18 0-32-12-32-32V224c0-16 16-32 32-32h576c15 0 32 16 32 32v576zM512 384c-71 0-128 57-128 128s57 128 128 128 128-57 128-128-57-128-128-128z M299 811 299 725 384 725 384 811 299 811M469 811 469 725 555 725 555 811 469 811M640 811 640 725 725 725 725 811 640 811M299 640 299 555 384 555 384 640 299 640M469 640 469 555 555 555 555 640 469 640M640 640 640 555 725 555 725 640 640 640M299 469 299 384 384 384 384 469 299 469M469 469 469 384 555 384 555 469 469 469M640 469 640 384 725 384 725 469 640 469M299 299 299 213 384 213 384 299 299 299M469 299 469 213 555 213 555 299 469 299M640 299 640 213 725 213 725 299 640 299Z M683 409v204L1024 308 683 0v191c-413 0-427 526-427 526c117-229 203-307 427-307zm85 492H102V327h153s38-63 114-122H51c-28 0-51 27-51 61v697c0 34 23 61 51 61h768c28 0 51-27 51-61V614l-102 100v187z + M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z M544 85c49 0 90 37 95 85h75a96 96 0 0196 89L811 267a32 32 0 01-28 32L779 299a32 32 0 01-32-28L747 267a32 32 0 00-28-32L715 235h-91a96 96 0 01-80 42H395c-33 0-62-17-80-42L224 235a32 32 0 00-32 28L192 267v576c0 16 12 30 28 32l4 0h128a32 32 0 0132 28l0 4a32 32 0 01-32 32h-128a96 96 0 01-96-89L128 843V267a96 96 0 0189-96L224 171h75a96 96 0 0195-85h150zm256 256a96 96 0 0196 89l0 7v405a96 96 0 01-89 96L800 939h-277a96 96 0 01-96-89L427 843v-405a96 96 0 0189-96L523 341h277zm-256-192H395a32 32 0 000 64h150a32 32 0 100-64z m186 532 287 0 0 287c0 11 9 20 20 20s20-9 20-20l0-287 287 0c11 0 20-9 20-20s-9-20-20-20l-287 0 0-287c0-11-9-20-20-20s-20 9-20 20l0 287-287 0c-11 0-20 9-20 20s9 20 20 20z M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z M592 768h-160c-27 0-48-21-48-48V384h-175c-36 0-53-43-28-68L485 11c15-15 40-15 55 0l304 304c25 25 7 68-28 68H640v336c0 27-21 48-48 48zm432-16v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h272v16c0 62 50 112 112 112h160c62 0 112-50 112-112v-16h272c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z + M672 336l64-89c-13-19-26-45-26-70 0-64 51-115 109-115s109 51 109 115-51 115-109 115c-19 0-38-6-51-13l-64 89c32 38 51 89 51 147 0 70-32 128-77 172l51 64c13-6 32-13 51-13 57 0 109 51 109 115s-51 109-109 109-109-51-109-115c0-26 13-51 26-70L646 707c-32 19-64 26-102 26-121 0-217-102-217-223v-38l-57-13c-19 32-57 57-96 57C116 515 65 464 65 400s51-115 109-115 109 51 109 115v13l57 19C372 349 448 292 538 292c51 0 102 19 134 45z M706 302a289 289 0 00-173 44 27 27 0 1029 46 234 234 0 01125-36c23 0 45 3 66 9 93 28 161 114 161 215C914 704 813 805 687 805H337C211 805 110 704 110 580c0-96 61-178 147-210C282 263 379 183 495 183a245 245 0 01210 119z M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z M1024 64v704h-128v128h-128v128h-768v-704h128v-128h128v-128zM64 960h640v-576h-640zM320 128v64h576v512h64v-576zM192 256v64h576v512h64v-576zM432 688L576 832H480L384 736 288 832H192l144-144L192 544h96L384 640l96-96H576z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 649dc7c9..3752841a 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -67,6 +67,8 @@ Branch Vergleich Bytes ABBRECHEN + Auf diese Revision zurücksetzen + Auf Vorgänger-Revision zurücksetzen ANZEIGE MODUS ÄNDERN Zeige als Datei- und Ordnerliste Zeige als Pfadliste @@ -107,7 +109,7 @@ Commit rückgängig machen Umformulieren Als Patch speichern... - Squash in den Vorgänger + Squash Commits ÄNDERUNGEN Änderungen durchsuchen... DATEIEN @@ -117,6 +119,8 @@ AUTOR GEÄNDERT COMMITTER + Prüfe Refs, die diesen Commit enthalten + COMMIT ENTHALTEN IN Zeigt nur die ersten 100 Änderungen. Alle Änderungen im ÄNDERUNGEN Tab. COMMIT-NACHRICHT VORGÄNGER @@ -125,6 +129,9 @@ Commit-Nachricht Details Repository Einstellungen + COMMIT TEMPLATE + Template Name: + Template Inhalt: Email Adresse Email Adresse GIT @@ -377,14 +384,14 @@ Verwende nur die Monospace-Schriftart im Texteditor Design Design-Anpassungen + Fixe Tab-Breite in Titelleiste + Verwende nativen Fensterrahmen ALLGEMEIN Beim Starten nach Updates suchen Sprache Commit-Historie Zuletzt geöffnete Tabs beim Starten wiederherstellen Längenvorgabe für Commit-Nachrichten - Fixe Tab-Breite in Titelleiste - Sichtbare Vergleichskontextzeilen GIT Remotes automatisch fetchen Auto-Fetch Intervall @@ -428,6 +435,7 @@ Pull (Fetch & Merge) Rebase anstatt Merge verwenden Push + Sicherstellen, dass Submodule gepusht wurden Erzwinge Push Lokaler Branch: Remote: @@ -475,6 +483,7 @@ GEFILTERT: LOKALE BRANCHES Zum HEAD wechseln + Aktiviere '--first-parent' Option Erstelle Branch Öffne in {0} Öffne in externen Tools @@ -590,11 +599,13 @@ Du kannst diese Datei jetzt stagen. COMMIT COMMIT & PUSH + Template/Historie STRG + Enter KONFLIKTE ERKANNT DATEI KONFLIKTE GELÖST NICHT-VERFOLGTE DATEIEN INKLUDIEREN KEINE BISHERIGEN COMMIT-NACHRICHTEN + KEINE COMMIT TEMPLATES GESTAGED UNSTAGEN ALLES UNSTAGEN @@ -602,6 +613,7 @@ STAGEN ALLES STAGEN ALS UNVERÄNDERT ANGENOMMENE ANZEIGEN + Template: ${0}$ Rechtsklick auf selektierte Dateien und wähle die Konfliktlösungen aus. WORKTREE Pfad kopieren diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 57e199ac..67f42eb2 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -107,6 +107,7 @@ Reword Save as Patch... Squash Into Parent + Squash Child Commits to Here CHANGES Search Changes... FILES @@ -116,6 +117,8 @@ AUTHOR CHANGED COMMITTER + Check refs that contains this commit + COMMIT IS CONTAINED BY Shows only the first 100 changes. See all changes on the CHANGES tab. MESSAGE PARENTS @@ -143,6 +146,7 @@ User Name User name for this repository Copy + Copy All Text COPY MESSAGE Copy Path Copy File Name @@ -241,6 +245,8 @@ Use Theirs (checkout --theirs) Use Mine (checkout --ours) File History + CONTENT + CHANGE FILTER Git-Flow Development Branch: @@ -379,14 +385,14 @@ Only use monospace font in text editor Theme Theme Overrides + Use fixed tab width in titlebar + Use native window frame GENERAL Check for updates on startup Language History Commits Restore last opened tab(s) on startup Subject Guide Length - Use fixed tab width in titlebar - Visible Diff Context Lines GIT Fetch remotes automatically Auto Fetch Interval @@ -430,6 +436,7 @@ Pull (Fetch & Merge) Use rebase instead of merge Push + Make sure submodules have been pushed Force push Local Branch: Remote: @@ -477,6 +484,7 @@ FILTERED BY: LOCAL BRANCHES Navigate To HEAD + Enable '--first-parent' Option Create Branch Open In {0} Open In External Tools @@ -524,7 +532,8 @@ Skip This Version Software Update There are currently no updates available. - Squash HEAD Into Parent + Squash Commits + Into: SSH Private Key: Private SSH key store path START diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml new file mode 100644 index 00000000..ac7265c5 --- /dev/null +++ b/src/Resources/Locales/fr_FR.axaml @@ -0,0 +1,626 @@ + + + + + A propos + A propos de SourceGit + • Compilé avec + © 2024 sourcegit-scm + • TextEditor de + + • La police Monospace vient de + • Le code source est disponible sur + Client Git gratuit et open source + Ajouter un Worktree + What to Checkout: + Branche existante + Créer une nouvelle branche + Location: + Chemin vers ce worktree. Relatif supporté. + Nom de branche: + Optionnel. Par défaut le nom du dossier de destination. + Track Branch: + Tracking remote branch + Patch + Erreur + Soulever les erreurs et refuser d'appliquer le patch + Toutes erreurs + Similaire à 'error', mais plus détaillé + Fichier de patch : + Selectionner le fichier .patch à appliquer + Ignorer les espaces blancs + Pas d'avertissement + Désactive l'avertissement des espaces blancs terminaux + Appliquer le patch + Warn + Affiche les avertissements Outputs warnings for a few such errors, mais applique + Espace blanc: + Archive... + Sauvegarder l'archive vers : + Selectionner le chemin de l'archive + Révision: + Archive + SourceGit Askpass + FICHIERS PRÉSUMÉS INCHANGÉS + PAS DE FICHIERS PRÉSUMÉS INCHANGÉS + REMOVE + FICHIER BINAIRE NON SUPPORTÉ !!! + Blame + BLAME SUR CE FICHIER NON SUPPORTÉ !!! + Checkout ${0}$... + Comparer avec la branche + Comparer avec HEAD + Comparer avec le Worktree + Copier le nom de la branche + Supprimer ${0}$... + Supprimer {0} branches sélectionnées + Rejeter tous les changements + Fast-Forward to ${0}$ + Git Flow - Finir ${0}$ + Merger ${0}$ dans ${1}$... + Pull ${0}$ + Pull ${0}$ dans ${1}$... + Push ${0}$ + Rebase ${0}$ sur ${1}$... + Renommer ${0}$... + Définir la branche de suivi + Unset Upstream + Comparer les branches + Octets + ANNULER + Reset vers cette révision + Reset vers la révision parente + CHANGER LE MODE D'AFFICHAGE + Afficher comme liste de dossiers/fichiers + Afficher comme liste de chemins + Afficher comme arborescence + Checkout Branch + Checkout Commit + Avertissement: un checkout vers un commit aboutiera vers un HEAD détaché + Commit : + Branche : + Changement locaux: + Rejeter + Ne rien faire + Stash et Réappliquer + Cherry-Pick ce commit + Commit : + Commit tous les changements + Cherry Pick + Vider les Stashes + Voulez-vous vider tous les stashes. Êtes-vous sûr de vouloir continuer ? + Cloner le dépôt distant + Paramètres supplémentaires : + Arguments additionnels au clônage. Optionnel. + Nom local : + Nom de dépôt. Optionnel. + Dossier parent : + URL du dépôt : + FERMER + Editeur + Cherry-Pick ce commit + Checkout ce commit + Comparer avec HEAD + Comparer avec le Worktree + Copier les infos + Copier le SHA + Rebase interactif de ${0}$ ici + Rebase ${0}$ ici + Reset ${0}$ ici + Annuler le commit + Reformuler + Sauver en tant que patch... + Squash dans le parent + CHANGEMENTS + Chercher des changements... + FICHIERS + Fichiers LFS + Sous-module + INFORMATION + AUTEUR + CHANGÉ + COMMITTER + Afficher seulement les 100 premiers changements. Voir tous les changements dans l'onglet CHANGEMENTS. + MESSAGE + PARENTS + REFS + SHA + Enter le sujet du commit + Description + Repository Configurer + MODÈLE DE COMMIT + Nom de modèle: + Contenu de modèle: + Addresse e-mail + Addresse e-mail + GIT + ISSUE TRACKER + Add Sample Github Rule + Add Sample Jira Rule + Nouvelle règle + Issue Regex Expression: + nom de règle : + URL résultant: + + Please use $1, $2 to access regex groups values. + HTTP Proxy + HTTP proxy used by this repository + Nom d'utilisateur + Nom d'utilisateur pour ce dépôt + Copier + COPIER LE MESSAGE + Copier le chemin + Copier le nom de fichier + Créer une branche... + Basé sur : + + Check out the created branch + Changements locaux : + Rejeter + Ne rien faire + Stash & Réappliquer + Nouveau nom de branche: + Enter le nom de branche. + Créer une branche locale + Créer un tag... + Nouveau tag à : + Signature GPG + Message de tag : + Optionnel. + Nom de tag : + Format Recommendé : v1.0.0-alpha + Push vers tous les arpès création + Créer un nouveau tag + Kind: + annoté + + lightweight + Maintenir Ctrl pour commencer directement + Couper + Supprimer la branche + Branche : + Vous êtes sur le point de supprimer une branche distante !!! + Supprimer également la branche distante ${0}$ + Supprimer plusieurs branches + Vous essayez de supprimer plusieurs branche à la fois. Assurez-vous de revérifier avant de procéder ! + Supprimer Remote + Remote : + Cible : + Confirmer la suppression du groupe + Confirmer la suppression du dépôt + Supprimer le Sous-module + Chemin du sous-module : + Supprimer le tag + Tag: + Delete from remote repositories + DIFF BINAIRE + NOUVEAU + ANCIEN + Copier + Mode de fichier changé + LFS OBJECT CHANGE + Différence suivante + PAS DE CHANGEMENT OU SEULEMENT EN FIN DE LIGNE + Différence précédente + Diff côte-à-côte + SOUS-MODULE + NOUVEAU + Syntax Highlighting + Retour à la ligne + Ouvrir dans l'outil de merge + Réduit le nombre de ligne visibles + Augmente le nombre de ligne visibles + SELECTIONNER UN FICHIER POUR VOIR LES CHANGEMENTS + Afficher les caractères invisibles + Permuter + Ouvrir dans l'outil de merge + Rejeter les changements + Tous les changements dans la copie de travail. + Changements : + {0} changements seront rejetés + Vous ne pouvez pas annuler cette action !!! + Bookmark: + Nouveau nom: + Cible : + Éditer le groupe sélectionné + Éditer le dépôt sélectionné + Fast-Forward (sans checkout) + Fetch + Fetch toutes les branches distantes + Fetch sans les tags + Purger les branches distantes mortes + Remote : + Fetch des changements distants + Présumer inchangé + Rejeter... + Rejeter {0} fichiers... + Rejeter les changements dans les lignes sélectionnées + Ouvrir l'outil de merge externe + Savegarder le patch sous... + Stage + + Stage {0} files + Stage Changes in Selected Line(s) + Stash... + Stash {0} fichiers... + Unstage + Unstage {0} files + Unstage Changes in Selected Line(s) + Use Theirs (checkout --theirs) + Use Mine (checkout --ours) + File History + FILTER + Git-Flow + Development Branch: + Feature: + Feature Prefix: + FLOW - Finish Feature + FLOW - Finish Hotfix + FLOW - Finish Release + Target: + Hotfix: + Hotfix Prefix: + Initialize Git-Flow + Keep branch + Production Branch: + Release: + Release Prefix: + Start Feature... + FLOW - Start Feature + Start Hotfix... + FLOW - Start Hotfix + Enter name + Start Release... + FLOW - Start Release + Version Tag Prefix: + Git LFS + Add Track Pattern... + Pattern is file name + Custom Pattern: + Add Track Pattern to Git LFS + Fetch + Fetch LFS Objects + Run `git lfs fetch` to download Git LFS objects. This does not update the working copy. + Install Git LFS hooks + Show Locks + No Locked Files + Lock + LFS Locks + Unlock + Force Unlock + Prune + Run `git lfs prune` to delete old LFS files from local storage + Pull + Pull LFS Objects + Run `git lfs pull` to download all Git LFS files for current ref & checkout + Push + Push LFS Objects + Push queued large files to the Git LFS endpoint + Remote: + Track files named '{0}' + Track all *{0} files + Histories + Switch Horizontal/Vertical Layout + Switch Curve/Polyline Graph Mode + AUTHOR + GRAPH & SUBJECT + SHA + COMMIT TIME + SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT + CLEAR + SELECTED {0} COMMITS + Référence des raccourcis clavier + GLOBAL + Annuler le popup en cours + Fermer la page en cours + Aller à la page précédente + Aller à la page suivante + Créer une nouvelle page + Ouvrir le dialogue des préférences + DÉPÔT + Commit les changements de l'index + Commit et pousser les changements de l'index + Mode tableau de bord (Défaut) + Forcer le rechargement du dépôt + Ajouter/Retirer les changements sélectionnés de l'index + Recherche de commit + Basculer vers 'Changements' + Basculer vers 'Historique' + Basculer vers 'Stashes' + ÉDITEUR DE TEXTE + Fermer le panneau de recherche + Trouver la prochaine correspondance + Trouver la correspondance précédente + Ouvrir le panneau de recherche + Stage + Retirer de l'index + Rejeter + Initialize Repository + Path: + Invalid repository detected. Run `git init` under this path? + Cherry-Pick in progress. Press 'Abort' to restore original HEAD. + Merge request in progress. Press 'Abort' to restore original HEAD. + Rebase in progress. Press 'Abort' to restore original HEAD. + Revert in progress. Press 'Abort' to restore original HEAD. + Interactive Rebase + Target Branch: + On: + Move Up + Move Down + Source Git + ERROR + NOTICE + Open Main Menu + Merge Branch + Into: + Merge Option: + Source Branch: + Nom : + Git n'a PAS été configuré. Veuillez d'abord le faire dans le menu Préférence. + NOTICE + Ouvrir le dossier AppData + SELECT FOLDER + Open With... + Optional. + Créer un nouvel onglet + Bookmark + Fermer l'onglet + Fermer les autres onglets + Fermer les onglets à droite + Copy Repository Path + Dépôts + Paste + A l'instant + il y a {0} minutes + il y a {0} heures + Hier + il y a {0} jours + Le mois dernier + il y a {0} mois + L'an dernier + il y a {0} ans + Préférences + APPARENCE + Police par défaut + Taille de police par défaut + Police monospace + N'utiliser que des polices monospace pour l'éditeur de texte + Thème + Dérogations de thème + Utiliser des onglets de taille fixe dans la barre de titre + Utiliser un cadre de fenêtre natif + GÉNÉRAL + Vérifier les mises à jour au démarrage + Language + Historique de commits + Restaurer les onglets au démarrage + Guide de longueur du sujet + GIT + Fetch les dépôts distants automatiquement + Intervalle de fetch auto + minute(s) + Activer auto CRLF + Répertoire de clônage par défaut + E-mail utilsateur + E-mail utilsateur global + Chemin d'installation + Shell + Nom d'utilisateur + Nom d'utilisateur global + Version de Git + Cette application requière Git (>= 2.23.0) + SIGNATURE GPG + Signature GPG de commit + Signature GPG de tag + Format GPG + Chemin d'installation du programme + Saisir le chemin d'installation vers le programme GPG + Clé de signature de l'utilisateur + Clé de signature GPG de l'utilisateur + OUTIL DIFF/MERGE + Chemin d'installation + Saisir le chemin d'installation de l'outil diff/merge + Outil + Élaguer une branche distant + Cible : + Élaguer les Worktrees + Élaguer les information de worktree dans `$GIT_DIR/worktrees` + Pull + Branche : + Fetch toutes les branches + Dans : + Changements locaux : + Rejeter + Ne rien faire + Stash & Réappliquer + Fetch sans les tags + Distant : + Pull (Fetch & Merge) + Use rebase instead of merge + Push + Make sure submodules have been pushed + Force push + Local Branch: + Remote: + Push Changes To Remote + Remote Branch: + Set as tracking branch + Push all tags + Push Tag To Remote + Push to all remotes + Remote: + Tag: + Quit + Rebase Current Branch + Stash & reapply local changes + On: + Rebase: + Refresh + Add Remote + Edit Remote + Name: + Remote name + Repository URL: + Remote git repository URL + Copy URL + Delete... + Edit... + Fetch + Open In Browser + Prune + Target: + Confirm to Remove Worktree + Enable `--force` Option + Target: + Rename Branch + New Name: + Unique name for this branch + Branch: + ABORT + Cleanup(GC & Prune) + Run `git gc` command for this repository. + Clear all + Configure this repository + CONTINUE + Ouvrir dans l'explorateur Windows + FILTERED BY: + LOCAL BRANCHES + Navigate To HEAD + Enable '--first-parent' Option + Create Branch + Open In {0} + Open In External Tools + Refresh + REMOTES + ADD REMOTE + RESOLVE + Search Commit + Search By + File + Message + SHA + Author & Committer + Search Branches & Tags + Show Tags as Tree + Statistics + SUBMODULES + ADD SUBMODULE + UPDATE SUBMODULE + TAGS + NEW TAG + Ouvrir dans un terminal + WORKTREES + ADD WORKTREE + PRUNE + Git Repository URL + Reset Current Branch To Revision + Reset Mode: + Move To: + Current Branch: + Reveal in File Explorer + Revert Commit + Commit: + Commit revert changes + Reword Commit Message + Use 'Shift+Enter' to input a new line. 'Enter' is the hotkey of OK button + Running. Please wait... + SAVE + Save As... + Patch has been saved successfully! + Vérifier les mises à jour... + Une nouvelle version du logiciel est disponible : + La vérification de mise à jour à échouée ! + Télécharger + Passer cette version + Mise à jour du logiciel + Il n'y a pas de mise à jour pour le moment. + Squash Commits + SSH Private Key: + Private SSH key store path + START + Stash + Include untracked files + Message: + Optional. Name of this stash + Stash Local Changes + Apply + Drop + Pop + Drop Stash + Drop: + Stashes + CHANGES + STASHES + Statistics + COMMITS + COMMITTER + MONTH + WEEK + YEAR + COMMITS: + COMMITTERS: + SUBMODULES + Add Submodule + Copy Relative Path + Fetch nested submodules + Open Submodule Repository + Relative Path: + Relative folder to store this module. + Delete Submodule + OK + Copy Tag Name + Delete ${0}$... + Push ${0}$... + URL: + Update Submodules + All submodules + Initialize as needed + Recursively + Submodule: + Use --remote option + Warning + Page d'accueil + Créer un groupe + Créer un sous-groupe + Cloner un dépôt + Supprimer + GLISSER / DEPOSER DE DOSSIER SUPPORTÉ. GROUPAGE PERSONNALISÉ SUPPORTÉ. + Éditer + Ouvrir tous les dépôts + Ouvrir un dépôt + Ouvrir le terminal + Chercher des dépôts... + Trier + Changes + Git Ignore + Ignore all *{0} files + Ignore *{0} files in the same folder + Ignore files in the same folder + Ignore this file only + Amend + Auto-Stage + You can stage this file now. + COMMIT + COMMIT & PUSH + Template/Histories + CTRL + Enter + CONFLICTS DETECTED + FILE CONFLICTS ARE RESOLVED + INCLUDE UNTRACKED FILES + NO RECENT INPUT MESSAGES + NO COMMIT TEMPLATES + STAGED + UNSTAGE + UNSTAGE ALL + UNSTAGED + STAGE + STAGE ALL + VIEW ASSUME UNCHANGED + Template: ${0}$ + Right-click the selected file(s), and make your choice to resolve conflicts. + WORKTREE + Copy Path + Lock + Remove + Unlock + diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 637f9a21..7d6f8cee 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -382,14 +382,13 @@ Usar apenas fonte monoespaçada no editor de texto Tema Sobrescrever Tema + Usar largura fixa da aba na barra de título GERAL Verificar atualizações na inicialização Idioma Commits do Histórico Restaurar as últimas abas abertas na inicialização Comprimento do Guia de Assunto - Usar largura fixa da aba na barra de título - Linhas de Contexto de Diferença Visíveis GIT Buscar remotos automaticamente Intervalo de Busca Automática @@ -480,6 +479,7 @@ FILTRADO POR: BRANCHES LOCAIS Navegar para HEAD + Filtro do Primeiro Pai Criar Branch Abrir em {0} Abrir em Ferramentas Externas @@ -527,7 +527,7 @@ Ignorar esta versão Atualização de Software Não há atualizações disponíveis no momento. - Unir HEAD ao Parent + Squash Commits Chave SSH Privada: Caminho para a chave SSH privada INICIAR diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 3aa37333..2fcb2bde 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -110,6 +110,7 @@ 编辑提交信息 另存为补丁 ... 合并此提交到上一个提交 + 合并之后的提交到此处 变更对比 查找变更... 文件列表 @@ -119,6 +120,8 @@ 修改者 变更列表 提交者 + 查看包含此提交的分支/标签 + 本提交已被以下分支/标签包含 仅显示前100项变更。请前往【变更对比】页面查看全部。 提交信息 父提交 @@ -146,6 +149,7 @@ 用户名 应用于本仓库的用户名 复制 + 复制全部文本 复制内容 复制路径 复制文件名 @@ -244,6 +248,8 @@ 使用 THEIRS (checkout --theirs) 使用 MINE (checkout --ours) 文件历史 + 文件内容 + 文件变更 过滤 GIT工作流 开发分支 : @@ -382,14 +388,14 @@ 仅在文本编辑器中使用等宽字体 主题 主题自定义 + 主标签使用固定宽度 + 使用系统默认窗体样式 通用配置 启动时检测软件更新 显示语言 最大历史提交数 启动时恢复上次打开的仓库 SUBJECT字数检测 - 使用固定宽度的标题栏标签 - DIFF上下文行数 GIT配置 启用定时自动拉取远程更新 自动拉取间隔 @@ -433,6 +439,7 @@ 拉回(拉取并合并) 使用变基方式合并分支 推送(push) + 确保子模块变更已推送 启用强制推送 本地分支 : 远程仓库 : @@ -479,6 +486,7 @@ 过滤规则 : 本地分支 定位HEAD + 启用 --first-parent 过滤选项 新建分支 在 {0} 中打开 使用外部工具打开 @@ -526,7 +534,8 @@ 忽略此版本 软件更新 当前已是最新版本。 - 合并HEAD到上一个提交 + 压缩为单个提交 + 合并入: SSH密钥 : SSH密钥文件 开 始 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 682335b7..aad55faf 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -2,206 +2,210 @@ - 關於軟體 - 關於本軟體 + 關於 + 關於 SourceGit • 專案依賴於 © 2024 sourcegit-scm • 文字編輯器使用 • 等寬字型來自於 - • 專案原始碼地址 - 開源免費的Git客戶端 - 新增工作樹 - 檢出分支方式 : + • 專案原始碼網址 + 開源免費的 Git 客戶端 + 新增工作區 + 簽出分支方式: 已有分支 - 創建新分支 - 工作樹路徑 : - 填寫該工作樹的路徑。支援相對路徑。 - 分支名 : - 選填。 預設使用目標資料夾名稱。 - 跟蹤分支 - 設置上游跟蹤分支 - 應用補丁(apply) + 建立新分支 + 工作區路徑: + 填寫該工作區的路徑。支援相對路徑。 + 分支名稱: + 選填。預設使用目標資料夾名稱。 + 追蹤分支 + 設定遠端追蹤分支 + 套用修補檔 (apply patch) 錯誤 - 輸出錯誤,並終止應用補丁 + 輸出錯誤,並中止套用修補檔 更多錯誤 - 與【錯誤】級別相似,但輸出內容更多 - 補丁檔案 : - 選擇補丁檔案 + 與 [錯誤] 級別相似,但輸出更多內容 + 修補檔: + 選擇修補檔 忽略空白符號 忽略 關閉所有警告 - 應用補丁 + 套用修補檔 警告 - 應用補丁,輸出關於空白符的警告 - 空白符號處理 : - 存檔(archive) ... - 存檔檔案路徑: - 選擇存檔檔案的存放路徑 - 指定的提交: - 存檔 + 套用修補檔,輸出關於空白字元的警告 + 空白字元處理: + 封存 (archive)... + 封存檔案路徑: + 選擇封存檔案的儲存路徑 + 指定的提交: + 封存 SourceGit Askpass - 不跟蹤更改的檔案 - 沒有不跟蹤更改的檔案 + 不追蹤變更的檔案 + 沒有不追蹤變更的檔案 移除 - 二進位制檔案不支援該操作!!! - 逐行追溯(blame) - 選中檔案不支援該操作!!! - 檢出(checkout) ${0}$... + 二進位檔案不支援該操作! + 逐行溯源 (blame) + 所選擇的檔案不支援該操作! + 簽出 (checkout) ${0}$... 與其他分支比較 - 與當前HEAD比較 - 與本地工作樹比較 - 複製分支名 + 與目前 HEAD 比較 + 與本機工作區比較 + 複製分支名稱 刪除 ${0}$... - 刪除選中的 {0} 個分支 - 放棄所有更改 - 快進(fast-forward)到 ${0}$ - GIT工作流 - 完成 ${0}$ + 刪除所選的 {0} 個分支 + 捨棄所有變更 + 快轉 (fast-forward) 到 ${0}$ + Git 工作流 - 完成 ${0}$ 合併 ${0}$ 到 ${1}$... - 拉回(pull) ${0}$ - 拉回(pull) ${0}$ 內容至 ${1}$... - 推送(push)${0}$ - 變基(rebase) ${0}$ 分支至 ${1}$... + 拉取 (pull) ${0}$ + 拉取 (pull) ${0}$ 內容至 ${1}$... + 推送 (push) ${0}$ + 重定基底 (rebase) ${0}$ 分支至 ${1}$... 重新命名 ${0}$... 切換上游分支 - 取消追蹤 + 取消設定上游分支 分支比較 位元組 - 取 消 - 重置檔案到該版本 - 重置檔案到上一版本 + 取 消 + 重設檔案為此版本 + 重設檔案到上一版本 切換變更顯示模式 - 檔名+路徑列表模式 + 檔案名稱 + 路徑列表模式 全路徑列表模式 - 檔案目錄樹形結構模式 - 檢出(checkout)分支 - 檢出(checkout)提交 - 注意:執行該操作後,當前HEAD會變為遊離(detached)狀態! - 提交 : - 目標分支 : - 未提交更改 : - 丟棄更改 + 檔案目錄樹狀結構模式 + 簽出 (checkout) 分支 + 簽出 (checkout) 提交 + 注意: 執行該操作後,目前 HEAD 會變為分離 (detached) 狀態! + 提交: + 目標分支: + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 - 挑選(cherry-pick)此提交 - 提交ID : - 提交變化 - 挑選提交 - 丟棄儲藏確認 - 您正在丟棄所有的儲藏,一經操作,無法回退,是否繼續? - 克隆遠端倉庫 - 額外引數 : - 其他克隆引數,選填。 - 本地倉庫名 : - 本地倉庫目錄的名字,選填。 - 父級目錄 : - 遠端倉庫 : + 擱置變更並自動復原 + 揀選 (cherry-pick) 此提交 + 提交編號: + 提交變更 + 揀選提交 + 捨棄擱置變更確認 + 您正在捨棄所有的擱置變更,一經操作便無法復原,是否繼續? + 複製 (clone) 遠端存放庫 + 額外參數: + 其他複製參數,選填。 + 本機存放庫名稱: + 本機存放庫目錄的名稱,選填。 + 父級目錄: + 遠端存放庫: 關閉 - 提交資訊編輯器 - 挑選(cherry-pick)此提交 - 檢出此提交 - 與當前HEAD比較 - 與本地工作樹比較 - 複製簡要資訊 - 複製提交指紋 - 互動式變基(rebase -i) ${0}$ 到此處 - 變基(rebase) ${0}$ 到此處 - 重置(reset) ${0}$ 到此處 - 回滾此提交 - 編輯提交資訊 - 另存為補丁 ... + 提交訊息編輯器 + 揀選 (cherry-pick) 此提交 + 簽出 (checkout) 此提交 + 與目前 HEAD 比較 + 與本機工作區比較 + 複製摘要資訊 + 複製提交編號 + 互動式重定基底 (rebase -i) ${0}$ 到此處 + 重定基底 (rebase) ${0}$ 到此處 + 重設 (reset) ${0}$ 到此處 + 復原此提交 + 編輯提交訊息 + 另存為修補檔 (patch)... 合併此提交到上一個提交 + 合併之後的提交到此處 變更對比 - 查詢變更... + 搜尋變更... 檔案列表 - LFS檔案 + LFS 檔案 子模組 基本資訊 - 修改者 + 作者 變更列表 提交者 - 僅顯示前100項變更。 請前往『變更對比』頁面查看全部。 - 提交資訊 - 父提交 - 相關引用 - 提交指紋 - 填寫提交信息主題 + 檢視包含此提交的分支或標籤 + 本提交包含於以下分支或標籤 + 僅顯示前 100 項變更。請前往 [變更對比] 頁面以瀏覽所有變更。 + 提交訊息 + 前次提交 + 相關參照 + 提交編號 + 填寫提交訊息標題 詳細描述 - 倉庫配置 - 提交資訊範本 - 範本名稱 : - 範本內容 : - 電子郵箱 - 郵箱地址 - GIT配置 - ISSUE追蹤 - 新增匹配Github Issue規則 - 新增匹配Jira規則 - 新增自定義規則 - 匹配ISSUE的正則表達式 : - 規則名 : - 為ISSUE生成的URL連結 : - 可在URL中使用$1,$2等變數填入正則表示式匹配的內容 - HTTP代理 - HTTP網路代理 + 存放庫設定 + 提交訊息範本 + 範本名稱: + 範本內容: + 電子郵件 + 電子郵件地址 + Git 設定 + Issue 追蹤 + 新增符合 Github Issue 規則 + 新增符合 Jira 規則 + 新增自訂規則 + 符合 Issue 的正則表達式: + 規則名稱: + 為 Issue 產生的網址連結: + 可在網址中使用 $1、$2 等變數填入正則表示式相符的內容 + HTTP 代理 + HTTP 網路代理 使用者名稱 - 應用於本倉庫的使用者名稱 + 用於本存放庫的使用者名稱 複製 + 複製全部內容 複製內容 複製路徑 - 複製檔名 - 新建分支 ... - 新分支基於 : + 複製檔案名稱 + 新增分支... + 新分支基於: 完成後切換到新分支 - 未提交更改 : - 丟棄更改 + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 - 新分支名 : - 填寫分支名稱。 - 建立本地分支 - 新建標籤 ... - 標籤位於 : - 使用GPG簽名 - 標籤描述 : + 擱置變更並自動復原 + 新分支名稱: + 輸入分支名稱。 + 建立本機分支 + 新增標籤... + 標籤位於: + 使用 GPG 簽名 + 標籤描述: 選填。 - 標籤名 : - 推薦格式 :v1.0.0-alpha - 推送到所有遠端倉庫 - 新建標籤 - 型別 : + 標籤名稱: + 建議格式: v1.0.0-alpha + 推送到所有遠端存放庫 + 新增標籤 + 類型: 附註標籤 輕量標籤 - 按住Ctrl鍵點擊將以預設參數運行 + 按住 Ctrl 鍵將直接以預設參數執行 剪下 刪除分支確認 - 分支名 : - 您正在刪除遠端上的分支,請務必小心!!! + 分支名稱: + 您正在刪除遠端上的分支,請務必小心! 同時刪除遠端分支 ${0}$ 刪除多個分支 - 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再執行操作! + 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再刪除! 刪除遠端確認 - 遠端名 : - 目標 : - 刪除分組確認 - 刪除倉庫確認 + 遠端名稱: + 目標: + 刪除群組確認 + 刪除存放庫確認 刪除子模組確認 - 子模組路徑 : + 子模組路徑: 刪除標籤確認 - 標籤名 : - 同時刪除遠端倉庫中的此標籤 - 二進位制檔案 - 當前大小 + 標籤名稱: + 同時刪除遠端存放庫中的此標籤 + 二進位檔案 + 目前大小 原始大小 複製 - 檔案許可權已變化 - LFS物件變更 + 檔案權限已變更 + LFS 物件變更 下一個差異 - 沒有變更或僅有換行符差異 + 沒有變更或僅有換行字元差異 上一個差異 - 分列對比 + 並排對比 子模組 新增 - 語法高亮 + 語法上色 自動換行 使用外部合併工具檢視 減少可見的行數 @@ -210,409 +214,415 @@ 顯示隱藏符號 交換比對雙方 使用外部比對工具檢視 - 放棄更改確認 - 所有本地址未提交的修改。 - 變更 : - 總計{0}項選中更改 - 本操作不支援回退,請確認後繼續!!! - 書籤 : - 名稱 : - 目標 : - 編輯分組 - 編輯倉庫 - 快進(fast-forward,無需checkout) - 拉取(fetch) - 拉取所有的遠端倉庫 - 不拉取遠端標籤 + 捨棄變更 + 所有本機未提交的變更。 + 變更: + 將捨棄總計 {0} 項已選取的變更 + 您無法復原此操作,請確認後再繼續! + 書籤: + 名稱: + 目標: + 編輯群組 + 編輯存放庫 + 快進 (fast-forward,無需 checkout) + 提取 (fetch) + 提取所有的遠端存放庫 + 不提取遠端標籤 自動清理遠端已刪除分支 - 遠端倉庫 : - 拉取遠端倉庫內容 - 不跟蹤此檔案的更改 - 放棄更改... - 放棄 {0} 個檔案的更改... - 放棄選中的更改 + 遠端存放庫: + 提取遠端存放庫內容 + 不追蹤此檔案的變更 + 捨棄變更... + 捨棄已選的 {0} 個檔案變更... + 捨棄選取的變更 使用外部合併工具開啟 - 另存為補丁... - 暫存(add) - 暫存(add){0} 個檔案 - 暫存選中的更改 - 儲藏(stash)... - 儲藏(stash)選中的 {0} 個檔案... - 從暫存中移除 + 另存為修補檔 (patch)... + 暫存 (add) + 暫存 (add) 已選的 {0} 個檔案 + 暫存選取的變更 + 擱置變更 (stash)... + 擱置變更 (stash) 所選的 {0} 個檔案... + 取消暫存 從暫存中移除 {0} 個檔案 - 從暫存中移除選中的更改 - 使用 THEIRS (checkout --theirs) - 使用 MINE (checkout --ours) + 取消暫存選取的變更 + 使用對方版本 (checkout --theirs) + 使用我方版本 (checkout --ours) 檔案歷史 - 過濾 - GIT工作流 - 開發分支 : - 特性分支 : - 特性分支名字首 : - 結束特性分支 - 結束脩復分支 - 結束版本分支 - 目標分支 : - 修復分支 : - 修復分支名字首 : - 初始化GIT工作流 + 檔案内容 + 檔案變更 + 篩選 + Git 工作流 + 開發分支: + 功能分支: + 功能分支前置詞: + 完成功能分支 + 完成修復分支 + 完成發行分支 + 目標分支: + 修復分支: + 修復分支前置詞: + 初始化 Git 工作流 保留分支 - 釋出分支 : - 版本分支 : - 版本分支名字首 : - 開始特性分支... - 開始特性分支 + 發行分支: + 版本分支: + 發行分支前置詞: + 開始功能分支... + 開始功能分支 開始修復分支... 開始修復分支 - 輸入分支名 - 開始版本分支... - 開始版本分支 - 版本標籤字首 : + 輸入分支名稱 + 開始發行分支... + 開始發行分支 + 版本標籤前置詞: Git LFS - 添加追蹤檔案規則... - 匹配完整檔案名 - 規則 : - 添加LFS追蹤檔案規則 - 拉取LFS物件 (fetch) - 拉取LFS物件 - 執行`git lfs fetch`命令,下載遠端LFS物件,但不會更新工作副本。 - 啟用Git LFS支援 - 顯示LFS物件鎖 - 沒有鎖定的LFS物件 + 加入追蹤檔案規則... + 符合完整檔案名稱 + 規則: + 加入 LFS 追蹤檔案規則 + 提取 (fetch) + 提取 LFS 物件 + 執行 `git lfs fetch` 以下載遠端 LFS 物件,但不會更新工作副本。 + 啟用 Git LFS 支援 + 顯示 LFS 物件鎖 + 沒有鎖定的 LFS 物件 鎖定 - LFS物件鎖 + LFS 物件鎖 解鎖 強制解鎖 - 精簡本地LFS物件存儲 - 執行`git lfs prune`命令,從本地存儲中精簡當前版本不需要的LFS物件 - 拉回LFS物件 (pull) - 拉回LFS物件 - 執行`git lfs pull`命令,下載遠端LFS物件并更新工作副本。 - 推送 - 推送LFS物件 - 將排隊的大檔推送到Git LFS遠端服務 - 遠端倉庫 : - 跟蹤名為'{0}'的檔案 - 跟蹤所有 *{0} 檔案 + 清理 (prune) + 執行 `git lfs prune` 以從本機中清理目前版本不需要的 LFS 物件 + 拉取 (pull) + 拉取 LFS 物件 + 執行 `git lfs pull` 以下載遠端 LFS 物件並更新工作副本。 + 推送 (push) + 推送 LFS 物件 + 將大型檔案推送到 Git LFS 遠端服務 + 遠端存放庫: + 追蹤名稱為「{0}」的檔案 + 追蹤所有 *{0} 檔案 歷史記錄 切換橫向/縱向顯示 切換曲線/折線顯示 作者 - 路線圖與主題 - 提交指紋 + 路線圖與訊息標題 + 提交編號 提交時間 - 查詢提交指紋、資訊、作者。回車鍵開始,ESC鍵取消 + 搜尋提交編號、訊息、作者。按下 Enter 鍵以搜尋,ESC 鍵取消 清空 - 已選中 {0} 項提交 - 快捷鍵參考 - 全域性快捷鍵 + 已選取 {0} 項提交 + 快速鍵參考 + 全域快速鍵 取消彈出面板 - 關閉當前頁面 + 關閉目前頁面 切換到上一個頁面 切換到下一個頁面 - 新建頁面 + 新增頁面 開啟偏好設定面板 - 倉庫頁面快捷鍵 + 存放庫頁面快速鍵 提交暫存區變更 - 提交暫存區變更併推送 - 切換左邊欄為分支/標籤等顯示模式(預設) - 重新載入倉庫狀態 - 將選中的變更暫存或從暫存列表中移除 + 提交暫存區變更並推送 + 切換左邊欄為分支/標籤等顯示模式 (預設) + 強制重新載入存放庫 + 暫存選取的變更或從暫存列表中移除 切換左邊欄為歷史搜尋模式 - 顯示本地更改 + 顯示本機變更 顯示歷史記錄 - 顯示儲藏列表 - 文字編輯器 - 關閉搜尋 - 定位到下一個匹配搜尋的位置 - 定位到上一個匹配搜尋的位置 - 開啟搜尋 + 顯示擱置變更列表 + 文字編輯器快速鍵 + 關閉搜尋面板 + 前往下一個搜尋相符的位置 + 前往上一個搜尋相符的位置 + 開啟搜尋面板 暫存 - 移出暫存區 - 丟棄 - 初始化新倉庫 - 路徑 : - 選擇目錄不是有效的Git倉庫。是否需要在此目錄執行`git init`操作? - 挑選(Cherry-Pick)操作進行中。點選【終止】回滾到操作前的狀態。 - 合併操作進行中。點選【終止】回滾到操作前的狀態。 - 變基(Rebase)操作進行中。點選【終止】回滾到操作前的狀態。 - 回滾提交操作進行中。點選【終止】回滾到操作前的狀態。 - 互動式變基 - 目標分支 : - 起始提交 : + 取消暫存 + 捨棄 + 初始化存放庫 + 路徑: + 選擇目錄並非有效的 Git 存放庫。是否要在此目錄執行 `git init` 以進行初始化? + 揀選 (cherry-pick) 操作進行中。點選 [中止] 復原到操作前的狀態。 + 合併操作進行中。點選 [中止] 復原到操作前的狀態。 + 重定基底 (rebase) 操作進行中。點選 [中止] 復原到操作前的狀態。 + 復原提交操作進行中。點選 [中止] 復原到操作前的狀態。 + 互動式重定基底 + 目標分支: + 起始提交: 向上移動 向下移動 Source Git - 出錯了 + 發生錯誤 系統提示 主選單 合併分支 - 目標分支 : - 合併方式 : - 合併分支 : - 名稱 : - GIT尚未配置。請開啟【偏好設定】配置GIT路徑。 + 目標分支: + 合併方式: + 合併分支: + 名稱: + 尚未設定 Git。請開啟 [偏好設定] 以設定 Git 路徑。 系統提示 瀏覽程式資料目錄 選擇資料夾 開啟檔案... 選填。 - 新建空白頁 + 新增分頁 設定書籤 - 關閉標籤頁 - 關閉其他標籤頁 - 關閉右側標籤頁 - 複製倉庫路徑 - 新標籤頁 + 關閉分頁 + 關閉其他分頁 + 關閉右側分頁 + 複製存放庫路徑 + 新分頁 貼上 剛剛 - {0}分鐘前 - {0}小時前 + {0} 分鐘前 + {0} 小時前 昨天 - {0}天前 + {0} 天前 上個月 - {0}個月前 + {0} 個月前 一年前 - {0}年前 + {0} 年前 偏好設定 - 外觀配置 + 外觀設定 預設字型 預設字型大小 等寬字型 - 僅在文字編輯器中使用等寬字體 - 主題 - 主題自訂 - 通用配置 - 啟動時檢測軟體更新 + 僅在文字編輯器中使用等寬字型 + 佈景主題 + 自訂主題 + 使用固定寬度的分頁標籤 + 使用系統原生預設視窗樣式 + 一般設定 + 啟動時檢查軟體更新 顯示語言 最大歷史提交數 - 啟動時恢復上次開啟的倉庫 - SUBJECT字數檢測 - 使用固定寬度的標題欄標籤 - DIFF上下文行數 - GIT配置 - 啟用定時自動拉取遠端更新 - 自動拉取間隔 + 啟動時還原上次開啟的存放庫 + 提交標題字數偵測 + Git 設定 + 啟用定時自動提取 (fetch) 遠端更新 + 自動提取間隔 分鐘 自動換行轉換 - 預設克隆路徑 - 郵箱 - 預設GIT使用者郵箱 + 預設複製 (clone) 路徑 + 電子郵件 + 預設 Git 使用者電子郵件 安裝路徑 - 終端Shell + 終端 Shell 使用者名稱 - 預設GIT使用者名稱 + 預設 Git 使用者名稱 Git 版本 - 本軟體要求GIT最低版本為2.23.0 - GPG簽名 + 本軟體要求 Git 最低版本為 2.23.0 + GPG 簽名 啟用提交簽名 啟用標籤簽名 - GPG簽名格式 - 可執行檔案位置 - gpg.exe所在路徑 - 使用者簽名KEY - 輸入簽名提交所使用的KEY + GPG 簽名格式 + 可執行檔案路徑 + 填寫 gpg.exe 所在路徑 + 使用者簽名金鑰 + 填寫簽名提交所使用的金鑰 對比/合併工具 安裝路徑 - 填寫工具可執行檔案所在位置 + 填寫可執行檔案所在路徑 工具 清理遠端已刪除分支 - 目標 : - 清理工作樹 - 清理在`$GIT_DIR/worktrees`中的無效工作樹資訊 - 拉回(pull) - 拉取分支 : - 拉取遠端中的所有分支變更 - 本地分支 : - 未提交更改 : - 丟棄更改 + 目標: + 清理工作區 + 清理在 `$GIT_DIR/worktrees` 中的無效工作區資訊 + 拉取 (pull) + 拉取分支: + 拉取遠端中的所有分支 + 本機分支: + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 + 擱置變更並自動復原 不拉取遠端標籤 - 遠端 : - 拉回(拉取併合並) - 使用變基方式合併分支 - 推送(push) + 遠端: + 拉取 (提取並合併) + 使用重定基底 (rebase) 合併分支 + 推送 (push) + 確保已推送子模組 啟用強制推送 - 本地分支 : - 遠端倉庫 : - 推送到遠端倉庫 - 遠端分支 : - 跟蹤遠端分支 + 本機分支: + 遠端存放庫: + 推送到遠端存放庫 + 遠端分支: + 追蹤遠端分支 同時推送標籤 - 推送標籤到遠端倉庫 - 推送到所有遠端倉庫 - 遠端倉庫 : - 標籤 : - 退出 - 變基(rebase)操作 - 自動儲藏並恢復本地變更 - 目標提交 : - 分支 : + 推送標籤到遠端存放庫 + 推送到所有遠端存放庫 + 遠端存放庫: + 標籤: + 結束 + 重定基底 (rebase) 操作 + 自動擱置變更並復原本機變更 + 目標提交: + 分支: 重新載入 - 新增遠端倉庫 - 編輯遠端倉庫 - 遠端名 : - 唯一遠端名 - 倉庫地址 : - 遠端倉庫的地址 - 複製遠端地址 - 刪除 ... - 編輯 ... - 拉取(fetch)更新 - 在瀏覽器中訪問網址 + 新增遠端存放庫 + 編輯遠端存放庫 + 遠端名稱: + 唯一遠端名稱 + 存放庫網址: + 遠端存放庫的網址 + 複製遠端網址 + 刪除... + 編輯... + 提取 (fetch) 更新 + 在瀏覽器中存取網址 清理遠端已刪除分支 - 刪除工作樹操作確認 - 啟用`--force`選項 - 目標工作樹 : + 目標: + 刪除工作區操作確認 + 啟用 [--force] 選項 + 目標工作區: 分支重新命名 - 新的名稱 : - 新的分支名不能與現有分支名相同 - 分支 : - 終止合併 - 清理本倉庫(GC) - 本操作將執行`git gc`命令。 - 清空過濾規則 - 配置本倉庫 + 新名稱: + 新的分支名稱不能與現有分支名稱相同 + 分支: + 中止 + 清理本存放庫 (GC) + 本操作將執行 `git gc` 命令。 + 清空篩選規則 + 設定本存放庫 下一步 在檔案瀏覽器中開啟 - 過濾規則 : - 本地分支 - 定位HEAD - 新建分支 + 篩選規則: + 本機分支 + 回到 HEAD + 啟用 [--first-parent] 選項 + 新增分支 在 {0} 中開啟 使用外部工具開啟 重新載入 遠端列表 新增遠端 解決衝突 - 查詢提交 - 查詢方式 + 搜尋提交 + 搜尋方式 檔案 - 提交資訊 - 提交指紋 + 提交訊息 + 提交編號 作者及提交者 - 快速查找分支、標籤 + 快速搜尋分支、標籤 以樹型結構展示 提交統計 子模組列表 新增子模組 更新子模組 標籤列表 - 新建標籤 - 在終端中開啟 - 工作樹清單 - 新建工作樹 + 新增標籤 + 在終端機中開啟 + 工作區列表 + 新增工作區 清理 - 遠端倉庫地址 - 重置(reset)當前分支到指定版本 - 重置模式 : - 提交 : - 當前分支 : + 遠端存放庫網址 + 重設目前分支到指定版本 + 重設模式: + 移至提交: + 目前分支: 在檔案瀏覽器中檢視 - 回滾操作確認 - 目標提交 : - 回滾後提交更改 - 編輯提交資訊 - 請使用Shift+Enter換行。Enter鍵已被【確 定】按鈕佔用。 + 復原操作確認 + 目標提交: + 復原後提交變更 + 編輯提交訊息 + 請使用 Shift + Enter 換行。Enter 鍵已被 [確定] 按鈕佔用。 執行操作中,請耐心等待... - 保 存 - 另存為... - 補丁已成功儲存! - 檢測更新... - 檢測到軟體有版本更新: - 獲取最新版本資訊失敗! - 下 載 + 儲存 + 另存新檔... + 修補檔已成功儲存! + 檢查更新... + 軟體有版本更新: + 獲取最新版本資訊失敗! + 下載 忽略此版本 軟體更新 - 當前已是最新版本。 - 合併HEAD到上一個提交 - SSH金鑰 : - SSH金鑰檔案 - 開 始 - 儲藏(stash) - 包含未跟蹤的檔案 - 資訊 : - 選填,用於命名此儲藏 - 儲藏本地變更 - 應用(apply) - 刪除(drop) - 應用並刪除(pop) - 丟棄儲藏確認 - 丟棄儲藏 : - 儲藏列表 + 目前已是最新版本。 + 壓縮為單個提交 + 合併入: + SSH 金鑰: + SSH 金鑰檔案 + 開 始 + 擱置變更 (stash) + 包含未追蹤的檔案 + 擱置變更訊息: + 選填,用於命名此擱置變更 + 擱置變更本機變更 + 套用 (apply) + 刪除 (drop) + 套用並刪除 (pop) + 捨棄擱置變更確認 + 捨棄擱置變更: + 擱置變更 檢視變更 - 儲藏列表 + 擱置變更列表 提交統計 提交次數 提交者 本月 本週 本年 - 提交次數: - 提交者: + 提交次數: + 提交者: 子模組 新增子模組 複製路徑 - 拉取子孫模組 - 開啟倉庫 - 相對倉庫路徑 : - 本地存放的相對路徑。 + 提取子模組 + 開啟存放庫 + 相對存放庫路徑: + 本機存放的相對路徑。 刪除子模組 - 確 定 - 複製標籤名 + 確 定 + 複製標籤名稱 刪除 ${0}$... 推送 ${0}$... - 倉庫地址 : + 存放庫網址: 更新子模組 更新所有子模組 - 啟用『--init』選項 - 啟用『--recursive』選項 - 子模組 : - 啟用『--remote』選項 + 啟用 [--init] 選項 + 啟用 [--recursive] 選項 + 子模組: + 啟用 [--remote] 選項 警告 起始頁 - 新建分組 - 新建子分組 - 克隆遠端倉庫 + 新增群組 + 新增子群組 + 複製 (clone) 遠端存放庫 刪除 - 支援拖放目錄新增。支援自定義分組。 + 支援拖放以新增目錄與自訂群組。 編輯 - 打開所有包含倉庫 - 開啟本地倉庫 - 開啟終端 - 快速查詢倉庫... + 開啟所有包含存放庫 + 開啟本機存放庫 + 開啟終端機 + 快速搜尋存放庫... 排序 - 本地更改 - 添加至 .gitignore 忽略清單 + 本機變更 + 加入至 .gitignore 忽略清單 忽略所有 *{0} 檔案 忽略同路徑下所有 *{0} 檔案 忽略同路徑下所有檔案 忽略本檔案 - 修補(--amend) + 修補 (--amend) 自動暫存 現在您已可將其加入暫存區中 - 提交 - 提交併推送 + 提 交 + 提交並推送 歷史輸入/範本 CTRL + Enter 檢測到衝突 檔案衝突已解決 - 顯示未跟蹤檔案 - 沒有提交資訊記錄 - 沒有可應用的提交資訊範本 + 顯示未追蹤檔案 + 沒有提交訊息記錄 + 沒有可套用的提交訊息範本 已暫存 - 從暫存區移除選中 - 從暫存區移除所有 + 取消暫存選取的檔案 + 取消暫存所有檔案 未暫存 - 暫存選中 - 暫存所有 - 檢視忽略變更檔案 - 範本:${0}$ - 請選中衝突檔案,開啟右鍵選單,選擇合適的解決方式 - 本地工作樹 - 拷贝工作樹路徑 - 鎖定工作樹 - 移除工作樹 - 解除鎖定工作樹 + 暫存選取的檔案 + 暫存所有檔案 + 檢視不追蹤變更的檔案 + 範本: ${0}$ + 請選擇發生衝突的檔案,開啟右鍵選單,選擇合適的解決方式 + 本機工作區 + 複製工作區路徑 + 鎖定工作區 + 移除工作區 + 解除鎖定工作區 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index e9477212..497e9f49 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1,5 +1,6 @@ - - @@ -47,6 +46,7 @@ + @@ -120,9 +120,9 @@ Background="{DynamicResource Brush.Window}" BorderBrush="{DynamicResource Brush.WindowBorder}" BorderThickness="1" - CornerRadius="8"> + CornerRadius="{TemplateBinding CornerRadius}"> - + + @@ -191,9 +192,6 @@ - - + + + + + + + + + + @@ -1105,7 +1075,7 @@ - - - - - - - - + - + + + - - - - - + + - - - - - + + - - + + - - - - - + + - - + + - - - - - - - - - - - - + + + + + + + diff --git a/src/Views/Checkout.axaml b/src/Views/Checkout.axaml index 02ed64b7..eaf2e79e 100644 --- a/src/Views/Checkout.axaml +++ b/src/Views/Checkout.axaml @@ -13,7 +13,12 @@ Classes="bold" Text="{DynamicResource Text.Checkout}"/> - + + + + + + - - + + - + - + diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 434d22d4..a9b9f259 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -3,19 +3,43 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Platform; namespace SourceGit.Views { public class ChromelessWindow : Window { + public bool UseSystemWindowFrame + { + get => OperatingSystem.IsLinux() && ViewModels.Preference.Instance.UseSystemWindowFrame; + } + protected override Type StyleKeyOverride => typeof(Window); public ChromelessWindow() { if (OperatingSystem.IsLinux()) - Classes.Add("custom_window_frame"); - else if (OperatingSystem.IsWindows()) - Classes.Add("fix_maximized_padding"); + { + if (UseSystemWindowFrame) + { + ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.Default; + ExtendClientAreaToDecorationsHint = false; + } + else + { + ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome; + ExtendClientAreaToDecorationsHint = true; + Classes.Add("custom_window_frame"); + } + } + else + { + ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome; + ExtendClientAreaToDecorationsHint = true; + + if (OperatingSystem.IsWindows()) + Classes.Add("fix_maximized_padding"); + } } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index 70da7fcd..8c2bf3cf 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -57,12 +57,16 @@ - + + @@ -94,11 +98,13 @@ + FontFamily="{DynamicResource Fonts.Primary}" + FontSize="11" + VerticalAlignment="Center" + Refs="{Binding Decorators}"/> diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index f7c44f17..9806860d 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -17,6 +17,15 @@ namespace SourceGit.Views set => SetValue(MessageProperty, value); } + public static readonly StyledProperty SupportsContainsInProperty = + AvaloniaProperty.Register(nameof(SupportsContainsIn)); + + public bool SupportsContainsIn + { + get => GetValue(SupportsContainsInProperty); + set => SetValue(SupportsContainsInProperty, value); + } + public static readonly StyledProperty> WebLinksProperty = AvaloniaProperty.Register>(nameof(WebLinks)); @@ -74,6 +83,19 @@ namespace SourceGit.Views e.Handled = true; } + private void OnOpenContainsIn(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.CommitDetail detail && sender is Button button) + { + var tracking = new CommitRelationTracking(detail); + var flyout = new Flyout(); + flyout.Content = tracking; + flyout.ShowAt(button); + } + + e.Handled = true; + } + private void OnParentSHAPressed(object sender, PointerPressedEventArgs e) { if (DataContext is ViewModels.CommitDetail detail && sender is Control { DataContext: string sha }) diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml index 432fa737..8307d650 100644 --- a/src/Views/CommitDetail.axaml +++ b/src/Views/CommitDetail.axaml @@ -21,6 +21,7 @@ diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index c00fb50c..bf19ecc7 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -1,17 +1,21 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Documents; using Avalonia.Input; -using Avalonia.Utilities; +using Avalonia.VisualTree; namespace SourceGit.Views { - public class CommitMessagePresenter : SelectableTextBlock + public partial class CommitMessagePresenter : SelectableTextBlock { + [GeneratedRegex(@"\b([0-9a-fA-F]{8,40})\b")] + private static partial Regex REG_SHA_FORMAT(); + public static readonly StyledProperty MessageProperty = AvaloniaProperty.Register(nameof(Message)); @@ -38,7 +42,7 @@ namespace SourceGit.Views if (change.Property == MessageProperty || change.Property == IssueTrackerRulesProperty) { - Inlines.Clear(); + Inlines!.Clear(); _matches = null; ClearHoveredIssueLink(); @@ -46,16 +50,35 @@ namespace SourceGit.Views if (string.IsNullOrEmpty(message)) return; - var rules = IssueTrackerRules; - if (rules == null || rules.Count == 0) + var matches = new List(); + if (IssueTrackerRules is { Count: > 0 } rules) { - Inlines.Add(new Run(message)); - return; + foreach (var rule in rules) + rule.Matches(matches, message); } - var matches = new List(); - foreach (var rule in rules) - rule.Matches(matches, message); + var shas = REG_SHA_FORMAT().Matches(message); + for (int i = 0; i < shas.Count; i++) + { + var sha = shas[i]; + if (!sha.Success) + continue; + + var start = sha.Index; + var len = sha.Length; + var intersect = false; + foreach (var match in matches) + { + if (match.Intersect(start, len)) + { + intersect = true; + break; + } + } + + if (!intersect) + matches.Add(new Models.Hyperlink(start, len, sha.Groups[1].Value, true)); + } if (matches.Count == 0) { @@ -72,9 +95,9 @@ namespace SourceGit.Views if (match.Start > pos) Inlines.Add(new Run(message.Substring(pos, match.Start - pos))); - match.Link = new Run(message.Substring(match.Start, match.Length)); - match.Link.Classes.Add("issue_link"); - Inlines.Add(match.Link); + var link = new Run(message.Substring(match.Start, match.Length)); + link.Classes.Add(match.IsCommitSHA ? "commit_link" : "issue_link"); + Inlines.Add(link); pos = match.Start + match.Length; } @@ -90,11 +113,10 @@ namespace SourceGit.Views if (e.Pointer.Captured == null && _matches != null) { - var padding = Padding; - var point = e.GetPosition(this) - new Point(padding.Left, padding.Top); - point = new Point( - MathUtilities.Clamp(point.X, 0, Math.Max(TextLayout.WidthIncludingTrailingWhitespace, 0)), - MathUtilities.Clamp(point.Y, 0, Math.Max(TextLayout.Height, 0))); + var point = e.GetPosition(this) - new Point(Padding.Left, Padding.Top); + var x = Math.Min(Math.Max(point.X, 0), Math.Max(TextLayout.WidthIncludingTrailingWhitespace, 0)); + var y = Math.Min(Math.Max(point.Y, 0), Math.Max(TextLayout.Height, 0)); + point = new Point(x, y); var pos = TextLayout.HitTestPoint(point).TextPosition; foreach (var match in _matches) @@ -105,12 +127,15 @@ namespace SourceGit.Views if (match == _lastHover) return; - _lastHover = match; - //_lastHover.Link.Classes.Add("issue_link_hovered"); - SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); - ToolTip.SetTip(this, match.URL); - ToolTip.SetIsOpen(this, true); + + _lastHover = match; + if (!_lastHover.IsCommitSHA) + { + ToolTip.SetTip(this, match.Link); + ToolTip.SetIsOpen(this, true); + } + return; } @@ -123,7 +148,18 @@ namespace SourceGit.Views if (_lastHover != null) { e.Pointer.Capture(null); - Native.OS.OpenBrowser(_lastHover.URL); + + if (_lastHover.IsCommitSHA) + { + var parentView = this.FindAncestorOfType(); + if (parentView is { DataContext: ViewModels.CommitDetail detail }) + detail.NavigateTo(_lastHover.Link); + } + else + { + Native.OS.OpenBrowser(_lastHover.Link); + } + e.Handled = true; return; } @@ -143,12 +179,11 @@ namespace SourceGit.Views { ToolTip.SetTip(this, null); SetCurrentValue(CursorProperty, Cursor.Parse("IBeam")); - //_lastHover.Link.Classes.Remove("issue_link_hovered"); _lastHover = null; } } - private List _matches = null; - private Models.IssueTrackerMatch _lastHover = null; + private List _matches = null; + private Models.Hyperlink _lastHover = null; } } diff --git a/src/Views/CommitRefsPresenter.cs b/src/Views/CommitRefsPresenter.cs index da842182..92be9b98 100644 --- a/src/Views/CommitRefsPresenter.cs +++ b/src/Views/CommitRefsPresenter.cs @@ -14,7 +14,16 @@ namespace SourceGit.Views { public Geometry Icon { get; set; } = null; public FormattedText Label { get; set; } = null; - public bool IsTag { get; set; } = false; + public IBrush LabelBG { get; set; } = null; + } + + public static readonly StyledProperty> RefsProperty = + AvaloniaProperty.Register>(nameof(Refs)); + + public List Refs + { + get => GetValue(RefsProperty); + set => SetValue(RefsProperty, value); } public static readonly StyledProperty FontFamilyProperty = @@ -71,6 +80,15 @@ namespace SourceGit.Views set => SetValue(BranchNameBackgroundProperty, value); } + public static readonly StyledProperty HeadBranchNameBackgroundProperty = + AvaloniaProperty.Register(nameof(HeadBranchNameBackground), Brushes.White); + + public IBrush HeadBranchNameBackground + { + get => GetValue(HeadBranchNameBackgroundProperty); + set => SetValue(HeadBranchNameBackgroundProperty, value); + } + public static readonly StyledProperty TagNameBackgroundProperty = AvaloniaProperty.Register(nameof(TagNameBackground), Brushes.White); @@ -85,7 +103,8 @@ namespace SourceGit.Views AffectsMeasure( FontFamilyProperty, FontSizeProperty, - LabelForegroundProperty); + LabelForegroundProperty, + RefsProperty); AffectsRender( IconBackgroundProperty, @@ -101,8 +120,6 @@ namespace SourceGit.Views var iconFG = IconForeground; var iconBG = IconBackground; - var branchBG = BranchNameBackground; - var tagBG = TagNameBackground; var x = 0.0; foreach (var item in _items) @@ -111,7 +128,7 @@ namespace SourceGit.Views var labelRect = new RoundedRect(new Rect(x + 16, 0, item.Label.Width + 8, 16), new CornerRadius(0, 2, 2, 0)); context.DrawRectangle(iconBG, null, iconRect); - context.DrawRectangle(item.IsTag ? tagBG : branchBG, null, labelRect); + context.DrawRectangle(item.LabelBG, null, labelRect); context.DrawText(item.Label, new Point(x + 20, 8.0 - item.Label.Height * 0.5)); using (context.PushTransform(Matrix.CreateTranslation(x + 4, 4))) @@ -121,25 +138,23 @@ namespace SourceGit.Views } } - protected override void OnDataContextChanged(EventArgs e) - { - base.OnDataContextChanged(e); - InvalidateMeasure(); - } - protected override Size MeasureOverride(Size availableSize) { _items.Clear(); - if (DataContext is Models.Commit commit && commit.HasDecorators) + var refs = Refs; + if (refs != null && refs.Count > 0) { var typeface = new Typeface(FontFamily); var typefaceBold = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold); var labelFG = LabelForeground; + var branchBG = BranchNameBackground; + var headBG = HeadBranchNameBackground; + var tagBG = TagNameBackground; var labelSize = FontSize; var requiredWidth = 0.0; - foreach (var decorator in commit.Decorators) + foreach (var decorator in refs) { var isHead = decorator.Type == Models.DecoratorType.CurrentBranchHead || decorator.Type == Models.DecoratorType.CurrentCommitHead; @@ -152,26 +167,28 @@ namespace SourceGit.Views labelSize, labelFG); - var item = new RenderItem() - { - Label = label, - IsTag = decorator.Type == Models.DecoratorType.Tag, - }; - + var item = new RenderItem() { Label = label }; StreamGeometry geo; switch (decorator.Type) { case Models.DecoratorType.CurrentBranchHead: + item.LabelBG = headBG; + geo = this.FindResource("Icons.Check") as StreamGeometry; + break; case Models.DecoratorType.CurrentCommitHead: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Check") as StreamGeometry; break; case Models.DecoratorType.RemoteBranchHead: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Remote") as StreamGeometry; break; case Models.DecoratorType.Tag: + item.LabelBG = tagBG; geo = this.FindResource("Icons.Tag") as StreamGeometry; break; default: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Branch") as StreamGeometry; break; } diff --git a/src/Views/CommitRelationTracking.axaml b/src/Views/CommitRelationTracking.axaml new file mode 100644 index 00000000..5e3574d8 --- /dev/null +++ b/src/Views/CommitRelationTracking.axaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitRelationTracking.axaml.cs b/src/Views/CommitRelationTracking.axaml.cs new file mode 100644 index 00000000..1e436552 --- /dev/null +++ b/src/Views/CommitRelationTracking.axaml.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; + +using Avalonia.Controls; +using Avalonia.Threading; + +namespace SourceGit.Views +{ + public partial class CommitRelationTracking : UserControl + { + public CommitRelationTracking() + { + InitializeComponent(); + } + + public CommitRelationTracking(ViewModels.CommitDetail detail) + { + InitializeComponent(); + + LoadingIcon.IsVisible = true; + + Task.Run(() => + { + var containsIn = detail.GetRefsContainsThisCommit(); + Dispatcher.UIThread.Invoke(() => + { + Container.ItemsSource = containsIn; + LoadingIcon.IsVisible = false; + }); + }); + } + } +} diff --git a/src/Views/ConfirmRestart.axaml b/src/Views/ConfirmRestart.axaml new file mode 100644 index 00000000..4e0ac386 --- /dev/null +++ b/src/Views/ConfirmRestart.axaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index ace50cc6..3a2c6802 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -9,20 +9,20 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.FileHistories" x:DataType="vm:FileHistories" - x:Name="me" + x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.FileHistory}" MinWidth="1280" MinHeight="720"> - + - + - - + @@ -104,41 +111,122 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + @@ -329,74 +303,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index a83d24bd..eb3158cd 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -63,11 +63,6 @@ namespace SourceGit.Views FontFamilyProperty, ForegroundProperty, CountProperty); - - AffectsRender( - ForegroundProperty, - BackgroundProperty, - CountProperty); } public override void Render(DrawingContext context) @@ -98,6 +93,7 @@ namespace SourceGit.Views _label = null; } + InvalidateVisual(); return _label != null ? new Size(_label.Width + 18, 18) : new Size(0, 0); } @@ -200,7 +196,7 @@ namespace SourceGit.Views private void OnSubmoduleContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Submodule submodule } grid && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Submodule submodule } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForSubmodule(submodule.Path); grid.OpenContextMenu(menu); @@ -211,7 +207,7 @@ namespace SourceGit.Views private void OnDoubleTappedSubmodule(object sender, TappedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Submodule submodule } && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Submodule submodule } && DataContext is ViewModels.Repository repo) { repo.OpenSubmodule(submodule.Path); } @@ -221,7 +217,7 @@ namespace SourceGit.Views private void OnWorktreeContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Worktree worktree } grid && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Worktree worktree } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForWorktree(worktree); grid.OpenContextMenu(menu); @@ -232,7 +228,7 @@ namespace SourceGit.Views private void OnDoubleTappedWorktree(object sender, TappedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Worktree worktree } && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Worktree worktree } && DataContext is ViewModels.Repository repo) { repo.OpenWorktree(worktree); } @@ -242,7 +238,7 @@ namespace SourceGit.Views private void OnLeftSidebarDataGridPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e) { - if (e.Property == DataGrid.ItemsSourceProperty || e.Property == DataGrid.IsVisibleProperty) + if (e.Property == ListBox.ItemsSourceProperty || e.Property == ListBox.IsVisibleProperty) UpdateLeftSidebarLayout(); } @@ -266,8 +262,8 @@ namespace SourceGit.Views var remoteBranchRows = vm.IsRemoteGroupExpanded ? RemoteBranchTree.Rows.Count : 0; var desiredBranches = (localBranchRows + remoteBranchRows) * 24.0; var desiredTag = vm.IsTagGroupExpanded ? 24.0 * TagsList.Rows : 0; - var desiredSubmodule = vm.IsSubmoduleGroupExpanded ? SubmoduleList.RowHeight * vm.Submodules.Count : 0; - var desiredWorktree = vm.IsWorktreeGroupExpanded ? WorktreeList.RowHeight * vm.Worktrees.Count : 0; + var desiredSubmodule = vm.IsSubmoduleGroupExpanded ? 24.0 * vm.Submodules.Count : 0; + var desiredWorktree = vm.IsWorktreeGroupExpanded ? 24.0 * vm.Worktrees.Count : 0; var desiredOthers = desiredTag + desiredSubmodule + desiredWorktree; var hasOverflow = (desiredBranches + desiredOthers > leftHeight); diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index 5ecaee22..f5d16fd4 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -8,14 +8,15 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.RepositoryConfigure" x:DataType="vm:RepositoryConfigure" + x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.Configure}" Width="600" SizeToContent="Height" CanResize="False" WindowStartupLocation="CenterOwner"> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs new file mode 100644 index 00000000..bca6a082 --- /dev/null +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class RevisionFileContentViewer : UserControl + { + public RevisionFileContentViewer() + { + InitializeComponent(); + } + } +} + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index aef71e19..0165ccab 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -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:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -28,56 +27,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/Views/SelfUpdate.axaml b/src/Views/SelfUpdate.axaml index f19c6969..c768a891 100644 --- a/src/Views/SelfUpdate.axaml +++ b/src/Views/SelfUpdate.axaml @@ -9,6 +9,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.SelfUpdate" x:DataType="vm:SelfUpdate" + x:Name="ThisControl" Title="{DynamicResource Text.SelfUpdate.Title}" Icon="/App.ico" SizeToContent="WidthAndHeight" @@ -16,7 +17,7 @@ WindowStartupLocation="CenterOwner"> - + - - - - - - - - + + - - + diff --git a/src/Views/StandaloneCommitMessageEditor.axaml b/src/Views/StandaloneCommitMessageEditor.axaml index feee7f1f..d6b961d5 100644 --- a/src/Views/StandaloneCommitMessageEditor.axaml +++ b/src/Views/StandaloneCommitMessageEditor.axaml @@ -5,6 +5,7 @@ xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.StandaloneCommitMessageEditor" + x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.CodeEditor}" Width="800" @@ -13,7 +14,7 @@ WindowStartupLocation="CenterScreen"> - + - + - - - @@ -70,7 +57,8 @@ - diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 23d31ab4..9bb20aca 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -282,6 +282,7 @@ namespace SourceGit.Views var tags = Tags; if (tags == null || tags.Count == 0) { + Rows = 0; Content = null; return; } diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index b49faa7d..1ab75ef6 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -22,7 +22,7 @@ AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}" DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" - FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" + FontFamily="{DynamicResource Fonts.Monospace}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -43,7 +43,7 @@ AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}" DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" - FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" + FontFamily="{DynamicResource Fonts.Monospace}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -63,7 +63,7 @@ AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}" DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}" IndicatorForeground="{DynamicResource Brush.FG2}" - FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" + FontFamily="{DynamicResource Fonts.Monospace}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}" ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}" diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 464339d8..b2459127 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -104,7 +104,7 @@ namespace SourceGit.Views if (string.IsNullOrEmpty(lineNumber)) continue; - var y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop) - view.VerticalOffset; + var y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.LineMiddle) - view.VerticalOffset; var txt = new FormattedText( lineNumber, CultureInfo.CurrentCulture, @@ -112,7 +112,7 @@ namespace SourceGit.Views typeface, presenter.FontSize, presenter.Foreground); - context.DrawText(txt, new Point(Bounds.Width - txt.Width, y)); + context.DrawText(txt, new Point(Bounds.Width - txt.Width, y - txt.Height * 0.5)); } } } @@ -392,7 +392,7 @@ namespace SourceGit.Views _lineStyleTransformer = new LineStyleTransformer(this); - TextArea.TextView.Margin = new Thickness(4, 0); + TextArea.TextView.Margin = new Thickness(2, 0); TextArea.TextView.Options.EnableHyperlinks = false; TextArea.TextView.Options.EnableEmailHyperlinks = false; diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index b926bada..0e84b790 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -4,13 +4,16 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:c="using:SourceGit.Converters" xmlns:vm="using:SourceGit.ViewModels" + xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Welcome" x:DataType="vm:Welcome"> + + Text="{Binding SearchFilter, Mode=TwoWay}" + v:AutoFocusBehaviour.IsEnabled="True"> + + + + + @@ -31,14 +44,48 @@ - - + + + + + + + + + + + + + + + @@ -46,21 +93,14 @@ - + - - - - - - - + + - + + - - - + - - - + + + + typeof(ToggleButton); + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && + DataContext is ViewModels.RepositoryNode { IsRepository: false } node) + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } + + e.Handled = true; + } + } + public partial class Welcome : UserControl { public Welcome() @@ -15,9 +33,29 @@ namespace SourceGit.Views InitializeComponent(); } + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (!e.Handled) + { + if (e.Key == Key.Down && ViewModels.Welcome.Instance.Rows.Count > 0) + { + TreeContainer.SelectedIndex = 0; + TreeContainer.Focus(NavigationMethod.Directional); + e.Handled = true; + } + else if (e.Key == Key.Escape) + { + ViewModels.Welcome.Instance.ClearSearchFilter(); + e.Handled = true; + } + } + } + private void SetupTreeViewDragAndDrop(object sender, RoutedEventArgs _) { - if (sender is TreeView view) + if (sender is ListBox view) { DragDrop.SetAllowDrop(view, true); view.AddHandler(DragDrop.DragOverEvent, DragOverTreeView); @@ -35,11 +73,30 @@ namespace SourceGit.Views } } + private void OnTreeViewKeyDown(object sender, KeyEventArgs e) + { + if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node && e.Key == Key.Enter) + { + if (node.IsRepository) + { + var parent = this.FindAncestorOfType(); + if (parent is { DataContext: ViewModels.Launcher launcher }) + launcher.OpenRepositoryInTab(node, null); + } + else + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } + + e.Handled = true; + } + } + private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is Grid grid) + if (sender is Grid { DataContext: ViewModels.RepositoryNode node } grid) { - var menu = ViewModels.Welcome.Instance.CreateContextMenu(grid.DataContext as ViewModels.RepositoryNode); + var menu = ViewModels.Welcome.Instance.CreateContextMenu(node); grid.OpenContextMenu(menu); e.Handled = true; } @@ -195,16 +252,21 @@ namespace SourceGit.Views private void OnDoubleTappedTreeNode(object sender, TappedEventArgs e) { - var grid = sender as Grid; - var to = grid?.DataContext as ViewModels.RepositoryNode; - if (to is not { IsRepository: true }) - return; + if (sender is Grid { DataContext: ViewModels.RepositoryNode node }) + { + if (node.IsRepository) + { + var parent = this.FindAncestorOfType(); + if (parent is { DataContext: ViewModels.Launcher launcher }) + launcher.OpenRepositoryInTab(node, null); + } + else + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } - var parent = this.FindAncestorOfType(); - if (parent?.DataContext is ViewModels.Launcher launcher) - launcher.OpenRepositoryInTab(to, null); - - e.Handled = true; + e.Handled = true; + } } private void OpenOrInitRepository(string path, ViewModels.RepositoryNode parent = null) @@ -226,6 +288,8 @@ namespace SourceGit.Views var normalizedPath = root.Replace("\\", "/"); var node = ViewModels.Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, parent, true); + ViewModels.Welcome.Instance.Refresh(); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; launcher?.OpenRepositoryInTab(node, launcher.ActivePage); } diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 2f8de643..6c5e21d0 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -56,6 +56,8 @@ namespace SourceGit.Views var normalizedPath = root.Replace("\\", "/"); var node = ViewModels.Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, parent, false); + ViewModels.Welcome.Instance.Refresh(); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; launcher?.OpenRepositoryInTab(node, launcher.ActivePage); }