diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4117364..bcb32580 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,14 +19,25 @@ jobs: os: macos-latest runtime: osx-arm64 - name : Linux - os: ubuntu-20.04 + os: ubuntu-latest runtime: linux-x64 + container: ubuntu:20.04 - name : Linux (arm64) - os: ubuntu-20.04 + os: ubuntu-latest runtime: linux-arm64 + container: ubuntu:20.04 name: Build ${{ matrix.name }} runs-on: ${{ matrix.os }} + container: ${{ matrix.container || '' }} steps: + - name: Install common CLI tools + if: ${{ startsWith(matrix.runtime, 'linux-') }} + run: | + export DEBIAN_FRONTEND=noninteractive + ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + apt-get update + apt-get install -y sudo + sudo apt-get install -y curl wget git unzip zip libicu66 tzdata clang - name: Checkout sources uses: actions/checkout@v4 - name: Setup .NET @@ -47,7 +58,7 @@ jobs: if: ${{ matrix.runtime == 'linux-arm64' }} run: | sudo apt-get update - sudo apt-get install clang llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64 + sudo apt-get install -y llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64 - name: Build run: dotnet build -c Release - name: Publish diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 074c9544..2dfc97fd 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -9,10 +9,10 @@ on: jobs: windows: name: Package Windows - runs-on: ubuntu-latest + runs-on: windows-2019 strategy: matrix: - runtime: [win-x64, win-arm64] + runtime: [ win-x64, win-arm64 ] steps: - name: Checkout sources uses: actions/checkout@v4 @@ -22,6 +22,7 @@ jobs: name: sourcegit.${{ matrix.runtime }} path: build/SourceGit - name: Package + shell: bash env: VERSION: ${{ inputs.version }} RUNTIME: ${{ matrix.runtime }} @@ -69,6 +70,7 @@ jobs: linux: name: Package Linux runs-on: ubuntu-latest + container: ubuntu:20.04 strategy: matrix: runtime: [linux-x64, linux-arm64] @@ -77,9 +79,10 @@ jobs: uses: actions/checkout@v4 - name: Download package dependencies run: | - sudo add-apt-repository universe - sudo apt-get update - sudo apt-get install desktop-file-utils rpm libfuse2 + export DEBIAN_FRONTEND=noninteractive + ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + apt-get update + apt-get install -y curl wget git dpkg-dev fakeroot tzdata zip unzip desktop-file-utils rpm libfuse2 file build-essential binutils - name: Download build uses: actions/download-artifact@v4 with: @@ -89,6 +92,7 @@ jobs: env: VERSION: ${{ inputs.version }} RUNTIME: ${{ matrix.runtime }} + APPIMAGE_EXTRACT_AND_RUN: 1 run: | mkdir build/SourceGit tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit diff --git a/README.md b/README.md index dae76783..4086a641 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ ## Translation Status -[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.21%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.80%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-%E2%88%9A-brightgreen)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.53%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.07%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md) +[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.08%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.68%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-99.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.41%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md) > [!NOTE] > You can find the missing keys in [TRANSLATION.md](TRANSLATION.md) diff --git a/SourceGit.sln b/SourceGit.sln index cf761abd..624322f8 100644 --- a/SourceGit.sln +++ b/SourceGit.sln @@ -60,6 +60,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DEBIAN", "DEBIAN", "{F101849D-BDB7-40D4-A516-751150C3CCFC}" ProjectSection(SolutionItems) = preProject build\resources\deb\DEBIAN\control = build\resources\deb\DEBIAN\control + build\resources\deb\DEBIAN\preinst = build\resources\deb\DEBIAN\preinst + build\resources\deb\DEBIAN\prerm = build\resources\deb\DEBIAN\prerm EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rpm", "rpm", "{9BA0B044-0CC9-46F8-B551-204F149BF45D}" diff --git a/TRANSLATION.md b/TRANSLATION.md index 716ddd3a..a5abbbba 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -1,4 +1,4 @@ -### de_DE.axaml: 99.21% +### de_DE.axaml: 99.08%
@@ -9,21 +9,22 @@ - Text.Diff.First - Text.Diff.Last - Text.Preferences.AI.Streaming +- Text.Preferences.Appearance.EditorTabWidth - Text.StashCM.SaveAsPatch
-### es_ES.axaml: 99.87% +### es_ES.axaml: 100.00%
Missing Keys -- Text.StashCM.SaveAsPatch +
-### fr_FR.axaml: 91.80% +### fr_FR.axaml: 91.68%
@@ -56,6 +57,7 @@ - Text.MergeMultiple.Strategy - Text.MergeMultiple.Targets - Text.Preferences.AI.Streaming +- Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.Appearance.FontSize - Text.Preferences.Appearance.FontSize.Default - Text.Preferences.Appearance.FontSize.Editor @@ -94,17 +96,17 @@
-### it_IT.axaml: 100.00% +### it_IT.axaml: 99.87%
Missing Keys - +- Text.Preferences.Appearance.EditorTabWidth
-### pt_BR.axaml: 91.53% +### pt_BR.axaml: 91.41%
@@ -148,6 +150,7 @@ - Text.MergeMultiple.Strategy - Text.MergeMultiple.Targets - Text.Preferences.AI.Streaming +- Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.General.DateFormat - Text.Preferences.General.ShowChildren - Text.Preferences.Git.SSLVerify @@ -177,19 +180,13 @@
-### ru_RU.axaml: 99.07% +### ru_RU.axaml: 100.00%
Missing Keys -- Text.BranchCM.CustomAction -- Text.BranchUpstreamInvalid -- Text.Configure.CustomAction.Scope.Branch -- Text.Diff.First -- Text.Diff.Last -- Text.Preferences.AI.Streaming -- Text.StashCM.SaveAsPatch +
diff --git a/VERSION b/VERSION index a241ab9b..a75bd422 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.07 \ No newline at end of file +2025.08 \ No newline at end of file diff --git a/build/resources/deb/DEBIAN/preinst b/build/resources/deb/DEBIAN/preinst new file mode 100755 index 00000000..a93f8090 --- /dev/null +++ b/build/resources/deb/DEBIAN/preinst @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see http://www.debian.org/doc/debian-policy/ + +case "$1" in + install|upgrade) + # Check if SourceGit is running and stop it + if pgrep -f '/opt/sourcegit/sourcegit' > /dev/null; then + echo "Stopping running SourceGit instance..." + pkill -f '/opt/sourcegit/sourcegit' || true + # Give the process a moment to terminate + sleep 1 + fi + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/build/resources/deb/DEBIAN/prerm b/build/resources/deb/DEBIAN/prerm new file mode 100755 index 00000000..c2c9e4f0 --- /dev/null +++ b/build/resources/deb/DEBIAN/prerm @@ -0,0 +1,35 @@ +#!/bin/sh + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + remove|upgrade|deconfigure) + if pgrep -f '/opt/sourcegit/sourcegit' > /dev/null; then + echo "Stopping running SourceGit instance..." + pkill -f '/opt/sourcegit/sourcegit' || true + # Give the process a moment to terminate + sleep 1 + fi + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/build/scripts/package.windows.sh b/build/scripts/package.windows.sh index 6bd3879b..1a8f99c1 100755 --- a/build/scripts/package.windows.sh +++ b/build/scripts/package.windows.sh @@ -9,4 +9,8 @@ cd build rm -rf SourceGit/*.pdb -zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then + powershell -Command "Compress-Archive -Path SourceGit\\* -DestinationPath \"sourcegit_$VERSION.$RUNTIME.zip\" -Force" +else + zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit +fi diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 95b396de..25e32323 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -445,10 +445,14 @@ namespace SourceGit var file = args[1]; if (!File.Exists(file)) + { desktop.Shutdown(-1); - else - desktop.MainWindow = new Views.StandaloneCommitMessageEditor(file); + return true; + } + var editor = new Views.StandaloneCommitMessageEditor(); + editor.SetFile(file); + desktop.MainWindow = editor; return true; } @@ -461,7 +465,9 @@ namespace SourceGit var args = desktop.Args; if (args?.Length > 0) { - desktop.MainWindow = new Views.Askpass(args[0]); + var askpass = new Views.Askpass(); + askpass.TxtDescription.Text = args[0]; + desktop.MainWindow = askpass; return true; } diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index da971e58..8ae6350f 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -68,6 +68,18 @@ namespace SourceGit.Commands return; } + if (line.StartsWith("deleted file mode ", StringComparison.Ordinal)) + { + _result.OldMode = line.Substring(18); + return; + } + + if (line.StartsWith("new file mode ", StringComparison.Ordinal)) + { + _result.NewMode = line.Substring(14); + return; + } + if (_result.IsBinary) return; diff --git a/src/Commands/IsCommitSHA.cs b/src/Commands/IsCommitSHA.cs new file mode 100644 index 00000000..1b0c50e3 --- /dev/null +++ b/src/Commands/IsCommitSHA.cs @@ -0,0 +1,17 @@ +namespace SourceGit.Commands +{ + public class IsCommitSHA : Command + { + public IsCommitSHA(string repo, string hash) + { + WorkingDirectory = repo; + Args = $"cat-file -t {hash}"; + } + + public bool Result() + { + var rs = ReadToEnd(); + return rs.IsSuccess && rs.StdOut.Trim().Equals("commit"); + } + } +} diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs index a4efa4b6..35a6289a 100644 --- a/src/Commands/Pull.cs +++ b/src/Commands/Pull.cs @@ -11,10 +11,11 @@ namespace SourceGit.Commands Context = repo; TraitErrorAsOutput = true; SSHKey = new Config(repo).Get($"remote.{remote}.sshkey"); - Args = "pull --verbose --progress --tags "; + Args = "pull --verbose --progress "; if (useRebase) - Args += "--rebase "; + Args += "--rebase=true "; + if (noTags) Args += "--no-tags "; diff --git a/src/Commands/QueryCommitsWithFullMessage.cs b/src/Commands/QueryCommitsForInteractiveRebase.cs similarity index 86% rename from src/Commands/QueryCommitsWithFullMessage.cs rename to src/Commands/QueryCommitsForInteractiveRebase.cs index c15cdbe1..232d86e5 100644 --- a/src/Commands/QueryCommitsWithFullMessage.cs +++ b/src/Commands/QueryCommitsForInteractiveRebase.cs @@ -3,18 +3,18 @@ using System.Collections.Generic; namespace SourceGit.Commands { - public class QueryCommitsWithFullMessage : Command + public class QueryCommitsForInteractiveRebase : Command { - public QueryCommitsWithFullMessage(string repo, string args) + public QueryCommitsForInteractiveRebase(string repo, string on) { _boundary = $"----- BOUNDARY OF COMMIT {Guid.NewGuid()} -----"; WorkingDirectory = repo; Context = repo; - Args = $"log --date-order --no-show-signature --decorate=full --pretty=format:\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_boundary}\" {args}"; + Args = $"log --date-order --no-show-signature --decorate=full --pretty=format:\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_boundary}\" {on}..HEAD"; } - public List Result() + public List Result() { var rs = ReadToEnd(); if (!rs.IsSuccess) @@ -29,7 +29,7 @@ namespace SourceGit.Commands switch (nextPartIdx) { case 0: - _current = new Models.CommitWithMessage(); + _current = new Models.InteractiveCommit(); _current.Commit.SHA = line; _commits.Add(_current); break; @@ -52,7 +52,7 @@ namespace SourceGit.Commands _current.Commit.CommitterTime = ulong.Parse(line); break; default: - var boundary = rs.StdOut.IndexOf(_boundary, end + 1); + var boundary = rs.StdOut.IndexOf(_boundary, end + 1, StringComparison.Ordinal); if (boundary > end) { _current.Message = rs.StdOut.Substring(start, boundary - start - 1); @@ -88,8 +88,8 @@ namespace SourceGit.Commands _current.Commit.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries)); } - private List _commits = new List(); - private Models.CommitWithMessage _current = null; + private List _commits = []; + private Models.InteractiveCommit _current = null; private string _boundary = ""; } } diff --git a/src/Commands/QueryRevisionByRefName.cs b/src/Commands/QueryRevisionByRefName.cs new file mode 100644 index 00000000..7fb4ecfa --- /dev/null +++ b/src/Commands/QueryRevisionByRefName.cs @@ -0,0 +1,21 @@ +namespace SourceGit.Commands +{ + public class QueryRevisionByRefName : Command + { + public QueryRevisionByRefName(string repo, string refname) + { + WorkingDirectory = repo; + Context = repo; + Args = $"rev-parse {refname}"; + } + + public string Result() + { + var rs = ReadToEnd(); + if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut)) + return rs.StdOut.Trim(); + + return null; + } + } +} diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index f015130a..5c48b0c0 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -112,9 +112,9 @@ namespace SourceGit.Models } } - public class CommitWithMessage + public class CommitFullMessage { - public Commit Commit { get; set; } = new Commit(); - public string Message { get; set; } = ""; + public string Message { get; set; } = string.Empty; + public List Links { get; set; } = []; } } diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index e0ae82e0..88992e10 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -681,6 +681,18 @@ namespace SourceGit.Models public TextDiff TextDiff { get; set; } = null; public LFSDiff LFSDiff { get; set; } = null; - public string FileModeChange => string.IsNullOrEmpty(OldMode) ? string.Empty : $"{OldMode} → {NewMode}"; + public string FileModeChange + { + get + { + if (string.IsNullOrEmpty(OldMode) && string.IsNullOrEmpty(NewMode)) + return string.Empty; + + var oldDisplay = string.IsNullOrEmpty(OldMode) ? "0" : OldMode; + var newDisplay = string.IsNullOrEmpty(NewMode) ? "0" : NewMode; + + return $"{oldDisplay} → {newDisplay}"; + } + } } } diff --git a/src/Models/InteractiveRebase.cs b/src/Models/InteractiveRebase.cs index 0980587a..691aadeb 100644 --- a/src/Models/InteractiveRebase.cs +++ b/src/Models/InteractiveRebase.cs @@ -12,6 +12,12 @@ namespace SourceGit.Models Drop, } + public class InteractiveCommit + { + public Commit Commit { get; set; } = new Commit(); + public string Message { get; set; } = string.Empty; + } + public class InteractiveRebaseJob { public string SHA { get; set; } = string.Empty; diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs index f1f5265f..8cc1be2a 100644 --- a/src/Models/RevisionFile.cs +++ b/src/Models/RevisionFile.cs @@ -29,6 +29,6 @@ namespace SourceGit.Models public class RevisionSubmodule { public Commit Commit { get; set; } = null; - public string FullMessage { get; set; } = string.Empty; + public CommitFullMessage FullMessage { get; set; } = null; } } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 5208a269..5df3ca71 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -464,6 +464,7 @@ Enable Streaming APPEARANCE Default Font + Editor Tab Width Font Size Default Editor diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index bf635321..4073f512 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -24,17 +24,17 @@ RE-GENERAR Usar OpenAI para generar mensaje de commit APLICAR CÓMO MENSAJE DE COMMIT - Aplicar Patch + Aplicar Parche Error - Genera errores y se niega a aplicar el patch + Genera errores y se niega a aplicar el parche Error Todo Similar a 'error', pero muestra más - Archivo Patch: + Archivo del Parche: Seleccionar archivo .patch para aplicar Ignorar cambios de espacios en blanco Sin Advertencia Desactiva la advertencia de espacios en blanco al final - Aplicar Patch + Aplicar Parche Advertencia Genera advertencias para algunos de estos errores, pero aplica Espacios en Blanco: @@ -126,7 +126,7 @@ Reset ${0}$ hasta Aquí Revertir Commit Reescribir - Guardar como Patch... + Guardar como Parche... Squash en Parent Squash Commits Hijos hasta Aquí CAMBIOS @@ -259,7 +259,7 @@ Siguiente Diferencia SIN CAMBIOS O SOLO CAMBIOS DE EOL Diferencia Anterior - Guardar como Patch + Guardar como Parche Mostrar símbolos ocultos Diferencia Lado a Lado SUBMÓDULO @@ -300,7 +300,7 @@ Descartar Cambios en Línea(s) Seleccionada(s) Abrir Herramienta de Merge Externa Resolver usando ${0}$ - Guardar Como Patch... + Guardar como Parche... Stage Stage {0} archivos Stage Cambios en Línea(s) Seleccionada(s) @@ -468,6 +468,7 @@ Activar Transmisión APARIENCIA Fuente por defecto + Ancho de la Pestaña del Editor Tamaño de fuente Por defecto Editor @@ -638,7 +639,7 @@ Ejecutando. Por favor espera... GUARDAR Guardar Como... - ¡El patch se ha guardado exitosamente! + ¡El parche se ha guardado exitosamente! Escanear Repositorios Directorio Raíz: Buscar Actualizaciones... @@ -672,6 +673,7 @@ Aplicar Eliminar Pop + Guardar como Parche... Eliminar Stash Eliminar: Stashes diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 4468650b..b15a769b 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -20,21 +20,21 @@ Имя целевого каталога по умолчанию. (необязательно) Отслеживание ветки: Отслеживание внешней ветки - OpenAI Ассистент + Помощник OpenAI ПЕРЕСОЗДАТЬ Использовать OpenAI для создания сообщения о ревизии ПРИМЕНИТЬ КАК СООБЩЕНИЕ РЕВИЗИИ Исправить Ошибка - Выдает ошибки и отказывается применять исправление + Выдает ошибки и отказывается применять заплатку Все ошибки Аналогично «ошибке», но показывает больше - Файл исправлений: + Файл заплатки: Выберите файл .patch для применения Игнорировать изменения пробелов Нет предупреждений Отключить предупреждения о пробелах в конце - Применить исправление + Применить заплатку Предупреждать Выдавать предупреждения о нескольких таких ошибках, но применять Пробел: @@ -58,6 +58,7 @@ Сравнить с ГОЛОВОЙ (HEAD) Сравнить с рабочим каталогом Копировать имя ветки + Изменить действие Удалить ${0}$... Удалить выбранные {0} ветки Отклонить все изменения. @@ -73,6 +74,7 @@ Переименовать ${0}$... Отслеживать ветку... Сравнение веток + Недопустимая основная ветка! Байты ОТМЕНА Сбросить эту ревизию @@ -105,7 +107,7 @@ Локальное имя: Имя репозитория. (необязательно). Родительский каталог: - Инициализировать и обновить подмодуль + Создать и обновить подмодуль Адрес репозитория: ЗАКРЫТЬ Редактор @@ -119,7 +121,7 @@ Пользовательское действие Интерактивное перемещение (rebase -i) ${0}$ сюда Влить в ${0}$ - Влить ветки... + Влить ... Переместить ${0}$ сюда Сбросить ${0}$ сюда Отменить ревизию @@ -137,7 +139,7 @@ АВТОР ИЗМЕНЁННЫЙ ДОЧЕРНИЙ - РЕВИЗОР(ИСПОЛНИТЕЛЬ) + РЕВИЗОР (ИСПОЛНИТЕЛЬ) Найти все ветки с этой ревизией ВЕТКИ С ЭТОЙ РЕВИЗИЕЙ Отображаются только первые 100 изменений. Смотрите все изменения на вкладке ИЗМЕНЕНИЯ. @@ -158,6 +160,7 @@ Исполняемый файл: Имя: Диапазон: + Ветка Ревизия Репозиторий Ждать для выполения выхода @@ -250,13 +253,15 @@ СТАРЫЙ Копировать Режим файла изменён + Первое различие Игнорировать изменение пробелов + Последнее различие Показывать скрытые символы ИЗМЕНЕНИЕ ОБЪЕКТА LFS Следующее различие НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL Предыдущее различие - Сохранить как исправление + Сохранить как заплатку Различие бок о бок ПОДМОДУЛЬ НОВЫЙ @@ -286,7 +291,7 @@ Быстрая перемотка вперёд (без проверки) Извлечь Извлечь все внешние репозитории - Разрешить опцию '--force' + Разрешить опцию (--force) Извлечь без меток Внешний репозиторий: Извлечь внешние изменения @@ -296,7 +301,7 @@ Отменить изменения в выбранной(ых) строке(ах) Открыть расширенный инструмент слияния Взять версию ${0}$ - Сохранить как patch-файл... + Сохранить как файл заплатки... Подготовить Подготовленные {0} файлы Подготовленные изменения в выбранной(ых) строке(ах) @@ -320,7 +325,7 @@ Цель: Исправление: Префикс исправлений: - Инициализировать Git-поток + Создать Git-поток Держать ветку Производственная ветка: Выпуск: @@ -403,7 +408,7 @@ Подготовить Снять из подготовленных Отклонить - Инициализировать репозиторий + Создать репозиторий Путь: Выполняется частичный перенос ревизий (cherry-pick). Обрабтка ревизии. @@ -460,10 +465,12 @@ Модель Имя: Сервер + Разрешить потоковую передачу ВИД Шрифт по умолчанию Размер шрифта По умолчанию + Редактировать ширину вкладки Редактор Моноширный шрифт В текстовом редакторе используется только моноширный шрифт @@ -488,7 +495,7 @@ Каталог клонирования по умолчанию Электроная почта пользователя Общая электроная почта пользователя git - Разрешить '--prune' при скачивании + Разрешить (--prune) при скачивании Путь установки Разрешить верификацию HTTP SSL Имя пользователя @@ -555,7 +562,7 @@ Открыть в браузере Удалить Подтвердить удаление рабочего каталога - Включить опцию --force + Включить опцию (--force) Цель: Переименовать ветку Новое имя: @@ -578,7 +585,7 @@ Скрыть в графе ревизии Фильтр в графе ревизии ОТФИЛЬТРОВАНО: - Включить опцию --first-parent + Включить опцию (--first-parent) РАСПОЛОЖЕНИЕ Горизонтально Вертикально @@ -633,7 +640,7 @@ Запуск. Подождите пожалуйста... СОХРАНИТЬ Сохранить как... - Исправление успешно сохранено! + Заплатка успешно сохранена! Сканирование репозиторий Корневой каталог: Копировать SHA @@ -667,6 +674,7 @@ Принять Отбросить Применить + Сохранить как заплатку... Отбросить тайник Отбросить: Отложенные @@ -674,7 +682,7 @@ ОТЛОЖЕННЫЕ Статистика РЕВИЗИИ - РЕВИЗОРЫ(ИСПОЛНИТЕЛИ) + РЕВИЗОРЫ (ИСПОЛНИТЕЛИ) МЕСЯЦ НЕДЕЛЯ РЕВИЗИИ: @@ -697,10 +705,10 @@ Сетевой адрес: Обновление подмодулей Все подмодули - Инициализировать по необходимости + Создавать по необходимости Рекурсивно Подмодуль: - Использовать опцию '--remote' + Использовать опцию (--remote) Предупреждение Приветствие Создать группу diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 0e8b9f7d..72b434f9 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -467,6 +467,7 @@ 启用流式输出 外观配置 缺省字体 + 编辑器制表符宽度 字体大小 默认 代码编辑器 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index f4cfad08..bc9991f6 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -467,6 +467,7 @@ 啟用串流輸出 外觀設定 預設字型 + 編輯器制表符寬度 字型大小 預設 程式碼 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 9b43d8af..38321356 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1299,6 +1299,8 @@ + + - + - + - + - + - + + + + + + + + + + + + + - - - + + - - + + + + - - - - + + + + - - - - + + + + - - + + - - - + + - + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index e3140191..b8c09b35 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -112,8 +112,19 @@ namespace SourceGit.Views // We should clear all unhandled key modifiers. _unhandledModifiers = KeyModifiers.None; + // Check for AltGr (which is detected as Ctrl+Alt) + bool isAltGr = e.KeyModifiers.HasFlag(KeyModifiers.Control) && + e.KeyModifiers.HasFlag(KeyModifiers.Alt); + + // Skip hotkey processing if AltGr is pressed + if (isAltGr) + { + base.OnKeyDown(e); + return; + } + // Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar) - if (!OperatingSystem.IsMacOS() && e.KeyModifiers == (KeyModifiers.Control | KeyModifiers.Shift) && e.Key == Key.P) + if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: (KeyModifiers.Control | KeyModifiers.Shift), Key: Key.P }) { App.OpenDialog(new Preferences()); e.Handled = true; @@ -243,13 +254,13 @@ namespace SourceGit.Views { _unhandledModifiers = e.KeyModifiers; - if (!_unhandledModifiers.HasFlag(KeyModifiers.Alt) && (e.Key == Key.LeftAlt || e.Key == Key.RightAlt)) + if (!_unhandledModifiers.HasFlag(KeyModifiers.Alt) && e.Key is Key.LeftAlt or Key.RightAlt) _unhandledModifiers |= KeyModifiers.Alt; - if (!_unhandledModifiers.HasFlag(KeyModifiers.Control) && (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)) + if (!_unhandledModifiers.HasFlag(KeyModifiers.Control) && e.Key is Key.LeftCtrl or Key.RightCtrl) _unhandledModifiers |= KeyModifiers.Control; - if (!_unhandledModifiers.HasFlag(KeyModifiers.Shift) && (e.Key == Key.LeftShift || e.Key == Key.RightShift)) + if (!_unhandledModifiers.HasFlag(KeyModifiers.Shift) && e.Key is Key.LeftShift or Key.RightShift) _unhandledModifiers |= KeyModifiers.Shift; } } diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index 3bdd150a..12679e50 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -148,7 +148,7 @@ - + + Minimum="10" Maximum="18" Increment="0.5" + Height="28" + Padding="4" + BorderThickness="1" BorderBrush="{DynamicResource Brush.Border1}" + CornerRadius="3" + Value="{Binding DefaultFontSize, Mode=TwoWay}"> @@ -218,10 +218,23 @@ + + + + + - @@ -232,16 +245,16 @@ - - - @@ -63,7 +64,7 @@ - + diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs index c74f2db2..16f4fc83 100644 --- a/src/Views/RevisionFileContentViewer.axaml.cs +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -15,6 +15,15 @@ namespace SourceGit.Views { public class RevisionTextFileView : TextEditor { + public static readonly StyledProperty TabWidthProperty = + AvaloniaProperty.Register(nameof(TabWidth), 4); + + public int TabWidth + { + get => GetValue(TabWidthProperty); + set => SetValue(TabWidthProperty, value); + } + protected override Type StyleKeyOverride => typeof(TextEditor); public RevisionTextFileView() : base(new TextArea(), new TextDocument()) @@ -22,13 +31,16 @@ namespace SourceGit.Views IsReadOnly = true; ShowLineNumbers = true; WordWrap = false; + + Options.IndentationSize = TabWidth; + Options.EnableHyperlinks = false; + Options.EnableEmailHyperlinks = false; + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; VerticalScrollBarVisibility = ScrollBarVisibility.Auto; TextArea.LeftMargins[0].Margin = new Thickness(8, 0); TextArea.TextView.Margin = new Thickness(4, 0); - TextArea.TextView.Options.EnableHyperlinks = false; - TextArea.TextView.Options.EnableEmailHyperlinks = false; } protected override void OnLoaded(RoutedEventArgs e) @@ -69,6 +81,16 @@ namespace SourceGit.Views } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == TabWidthProperty) + { + Options.IndentationSize = TabWidth; + } + } + private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e) { var selected = SelectedText; diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index 3d5cff38..6847b14b 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -44,7 +44,7 @@ + IsOpen="{Binding RevisionFileSearchSuggestion, Converter={x:Static c:ListConverters.IsNotNullOrEmpty}}"> 0) { SearchSuggestionBox.Focus(NavigationMethod.Tab); SearchSuggestionBox.SelectedIndex = 0; @@ -33,12 +33,7 @@ namespace SourceGit.Views } else if (e.Key == Key.Escape) { - if (vm.IsRevisionFileSearchSuggestionOpen) - { - vm.RevisionFileSearchSuggestion.Clear(); - vm.IsRevisionFileSearchSuggestionOpen = false; - } - + vm.CancelRevisionFileSuggestions(); e.Handled = true; } } @@ -57,7 +52,7 @@ namespace SourceGit.Views if (e.Key == Key.Escape) { - vm.RevisionFileSearchSuggestion.Clear(); + vm.CancelRevisionFileSuggestions(); e.Handled = true; } else if (e.Key == Key.Enter && SearchSuggestionBox.SelectedItem is string content) diff --git a/src/Views/StandaloneCommitMessageEditor.axaml.cs b/src/Views/StandaloneCommitMessageEditor.axaml.cs index 6833fb33..8a49ee74 100644 --- a/src/Views/StandaloneCommitMessageEditor.axaml.cs +++ b/src/Views/StandaloneCommitMessageEditor.axaml.cs @@ -9,18 +9,14 @@ namespace SourceGit.Views { public StandaloneCommitMessageEditor() { - _file = string.Empty; - DataContext = this; InitializeComponent(); } - public StandaloneCommitMessageEditor(string file) + public void SetFile(string file) { _file = file; - DataContext = this; - InitializeComponent(); - var content = File.ReadAllText(file).ReplaceLineEndings("\n"); + var content = File.ReadAllText(file).ReplaceLineEndings("\n").Trim(); var firstLineEnd = content.IndexOf('\n'); if (firstLineEnd == -1) { @@ -29,7 +25,7 @@ namespace SourceGit.Views else { Editor.SubjectEditor.Text = content.Substring(0, firstLineEnd); - Editor.DescriptionEditor.Text = content.Substring(firstLineEnd + 1); + Editor.DescriptionEditor.Text = content.Substring(firstLineEnd + 1).Trim(); } } @@ -41,12 +37,16 @@ namespace SourceGit.Views private void SaveAndClose(object _1, RoutedEventArgs _2) { - File.WriteAllText(_file, Editor.Text); - _exitCode = 0; + if (!string.IsNullOrEmpty(_file)) + { + File.WriteAllText(_file, Editor.Text); + _exitCode = 0; + } + Close(); } - private readonly string _file; + private string _file = string.Empty; private int _exitCode = -1; } } diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index 3ea7af14..7ea6b44b 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -27,6 +27,7 @@ IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}" + TabWidth="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorTabWidth}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}" WordWrap="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableDiffViewWordWrap}" ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -59,6 +60,7 @@ IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}" + TabWidth="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorTabWidth}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}" WordWrap="False" ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}" @@ -81,6 +83,7 @@ IndicatorForeground="{DynamicResource Brush.FG2}" FontFamily="{DynamicResource Fonts.Monospace}" FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}" + TabWidth="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorTabWidth}" UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}" WordWrap="False" ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}" diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index dccbb9b2..925c7622 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -60,7 +60,7 @@ namespace SourceGit.Views public class ThemedTextDiffPresenter : TextEditor { - public class VerticalSeperatorMargin : AbstractMargin + public class VerticalSeparatorMargin : AbstractMargin { public override void Render(DrawingContext context) { @@ -475,6 +475,15 @@ namespace SourceGit.Views set => SetValue(ShowHiddenSymbolsProperty, value); } + public static readonly StyledProperty TabWidthProperty = + AvaloniaProperty.Register(nameof(TabWidth), 4); + + public int TabWidth + { + get => GetValue(TabWidthProperty); + set => SetValue(TabWidthProperty, value); + } + public static readonly StyledProperty EnableChunkSelectionProperty = AvaloniaProperty.Register(nameof(EnableChunkSelection)); @@ -519,12 +528,13 @@ namespace SourceGit.Views ShowLineNumbers = false; BorderThickness = new Thickness(0); + Options.IndentationSize = TabWidth; + Options.EnableHyperlinks = false; + Options.EnableEmailHyperlinks = false; + _lineStyleTransformer = new LineStyleTransformer(this); TextArea.TextView.Margin = new Thickness(2, 0); - TextArea.TextView.Options.EnableHyperlinks = false; - TextArea.TextView.Options.EnableEmailHyperlinks = false; - TextArea.TextView.BackgroundRenderers.Add(new LineBackgroundRenderer(this)); TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); } @@ -734,10 +744,14 @@ namespace SourceGit.Views } else if (change.Property == ShowHiddenSymbolsProperty) { - var val = change.NewValue is true; + var val = ShowHiddenSymbols; Options.ShowTabs = val; Options.ShowSpaces = val; } + else if (change.Property == TabWidthProperty) + { + Options.IndentationSize = TabWidth; + } else if (change.Property == FileNameProperty) { Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); @@ -1007,8 +1021,8 @@ namespace SourceGit.Views if (startPosition.Location > endPosition.Location) (startPosition, endPosition) = (endPosition, startPosition); - var startIdx = Math.Min(startPosition.Line - 1, lines.Count - 1); - var endIdx = Math.Min(endPosition.Line - 1, lines.Count - 1); + var startIdx = startPosition.Line - 1; + var endIdx = endPosition.Line - 1; if (startIdx == endIdx) { @@ -1025,25 +1039,35 @@ namespace SourceGit.Views } var builder = new StringBuilder(); - for (var i = startIdx; i <= endIdx; i++) + for (var i = startIdx; i <= endIdx && i <= lines.Count - 1; i++) { var line = lines[i]; if (line.Type == Models.TextDiffLineType.Indicator || line.Type == Models.TextDiffLineType.None) continue; + // The first selected line (partial selection) if (i == startIdx && startPosition.Column > 1) { builder.AppendLine(line.Content.Substring(startPosition.Column - 1)); continue; } - if (i == endIdx && endPosition.Column < line.Content.Length) + // The selection range is larger than original source. + if (i == lines.Count - 1 && i < endIdx) { - builder.AppendLine(line.Content.Substring(0, endPosition.Column)); - continue; + builder.Append(line.Content); + break; } + // For the last line (selection range is within original source) + if (i == endIdx) + { + builder.Append(endPosition.Column - 1 < line.Content.Length ? line.Content.Substring(0, endPosition.Column - 1) : line.Content); + break; + } + + // Other lines. builder.AppendLine(line.Content); } @@ -1061,9 +1085,9 @@ namespace SourceGit.Views public CombinedTextDiffPresenter() : base(new TextArea(), new TextDocument()) { TextArea.LeftMargins.Add(new LineNumberMargin(false, true)); - TextArea.LeftMargins.Add(new VerticalSeperatorMargin()); + TextArea.LeftMargins.Add(new VerticalSeparatorMargin()); TextArea.LeftMargins.Add(new LineNumberMargin(false, false)); - TextArea.LeftMargins.Add(new VerticalSeperatorMargin()); + TextArea.LeftMargins.Add(new VerticalSeparatorMargin()); TextArea.LeftMargins.Add(new LineModifyTypeMargin()); } @@ -1262,7 +1286,7 @@ namespace SourceGit.Views public SingleSideTextDiffPresenter() : base(new TextArea(), new TextDocument()) { TextArea.LeftMargins.Add(new LineNumberMargin(true, false)); - TextArea.LeftMargins.Add(new VerticalSeperatorMargin()); + TextArea.LeftMargins.Add(new VerticalSeparatorMargin()); TextArea.LeftMargins.Add(new LineModifyTypeMargin()); } diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index ebf26a96..e5728df2 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -211,7 +211,12 @@ - + @@ -225,18 +230,15 @@ FontSize="11" VerticalAlignment="Center" UseGraphColor="False"/> - + - - - - - - - - @@ -397,7 +399,8 @@ Width="0" Height="0" Background="Transparent" Command="{Binding CommitWithAutoStage}" - HotKey="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}"/> + HotKey="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}" + IsEnabled="{Binding !IsCommitting}"/>