diff --git a/.editorconfig b/.editorconfig
index 22c741b9..f3c9a7bf 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -71,20 +71,20 @@ dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# private static fields should have s_ prefix
dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion
-dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style
-dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.required_modifiers = static
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private
@@ -93,14 +93,14 @@ dotnet_naming_style.private_static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
-dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
+dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
-dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
+dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# use accessibility modifiers
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bcb32580..12792cf6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -58,7 +58,7 @@ jobs:
if: ${{ matrix.runtime == 'linux-arm64' }}
run: |
sudo apt-get update
- sudo apt-get install -y llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64
+ sudo apt-get install -y llvm gcc-aarch64-linux-gnu
- name: Build
run: dotnet build -c Release
- name: Publish
diff --git a/.github/workflows/localization-check.yml b/.github/workflows/localization-check.yml
index cc5201ab..8dcd61c8 100644
--- a/.github/workflows/localization-check.yml
+++ b/.github/workflows/localization-check.yml
@@ -4,7 +4,6 @@ on:
branches: [ develop ]
paths:
- 'src/Resources/Locales/**'
- - 'README.md'
workflow_dispatch:
workflow_call:
@@ -32,8 +31,8 @@ jobs:
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ -n "$(git status --porcelain)" ]; then
- git add README.md TRANSLATION.md
- git commit -m 'doc: Update translation status and missing keys'
+ git add TRANSLATION.md src/Resources/Locales/*.axaml
+ git commit -m 'doc: Update translation status and sort locale files'
git push
else
echo "No changes to commit"
diff --git a/.gitignore b/.gitignore
index 0c66b11e..e686a534 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,5 @@ build/*.deb
build/*.rpm
build/*.AppImage
SourceGit.app/
+build.command
+src/Properties/launchSettings.json
diff --git a/README.md b/README.md
index c932ec8a..f9ba3072 100644
--- a/README.md
+++ b/README.md
@@ -11,15 +11,15 @@
* Supports Windows/macOS/Linux
* Opensource/Free
* Fast
-* Deutsch/English/Español/Français/Italiano/Português/Русский/简体中文/繁體中文
+* Deutsch/English/Español/Français/Italiano/Português/Русский/Українська/简体中文/繁體中文/日本語/தமிழ் (Tamil)
* Built-in light/dark themes
* Customize theme
* Visual commit graph
* Supports SSH access with each remote
* GIT commands with GUI
* Clone/Fetch/Pull/Push...
- * Merge/Rebase/Reset/Revert/Amend/Cherry-pick...
- * Amend/Reword
+ * Merge/Rebase/Reset/Revert/Cherry-pick...
+ * Amend/Reword/Squash
* Interactive rebase
* Branches
* Remotes
@@ -35,11 +35,14 @@
* Revision Diffs
* Branch Diff
* Image Diff - Side-By-Side/Swipe/Blend
+* Git command logs
* Search commits
* GitFlow
* Git LFS
+* Bisect
* Issue Link
* Workspace
+* Custom Action
* Using AI to generate commit message (C# port of [anjerodev/commitollama](https://github.com/anjerodev/commitollama))
> [!WARNING]
@@ -47,16 +50,13 @@
## Translation Status
-[](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md) [](TRANSLATION.md)
-
-> [!NOTE]
-> You can find the missing keys in [TRANSLATION.md](TRANSLATION.md)
+You can find the current translation status in [TRANSLATION.md](https://github.com/sourcegit-scm/sourcegit/blob/develop/TRANSLATION.md)
## How to Use
-**To use this tool, you need to install Git(>=2.23.0) first.**
+**To use this tool, you need to install Git(>=2.25.1) first.**
-You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
+You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [GitHub Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
@@ -93,7 +93,7 @@ For **macOS** users:
brew tap ybeapps/homebrew-sourcegit
brew install --cask --no-quarantine sourcegit
```
-* If you want to install `SourceGit.app` from Github Release manually, you need run following command to make sure it works:
+* If you want to install `SourceGit.app` from GitHub Release manually, you need run following command to make sure it works:
```shell
sudo xattr -cr /Applications/SourceGit.app
```
@@ -201,3 +201,7 @@ dotnet run --project src/SourceGit.csproj
Thanks to all the people who contribute.
[](https://github.com/sourcegit-scm/sourcegit/graphs/contributors)
+
+## Third-Party Components
+
+For detailed license information, see [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md).
diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md
new file mode 100644
index 00000000..2338263c
--- /dev/null
+++ b/THIRD-PARTY-LICENSES.md
@@ -0,0 +1,86 @@
+# Third-Party Licenses
+
+This project incorporates components from the following third parties:
+
+## Packages
+
+### AvaloniaUI
+
+- **Source**: https://github.com/AvaloniaUI/Avalonia
+- **Version**: 11.2.5
+- **License**: MIT License
+- **License Link**: https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
+
+### AvaloniaEdit
+
+- **Source**: https://github.com/AvaloniaUI/AvaloniaEdit
+- **Version**: 11.2.0
+- **License**: MIT License
+- **License Link**: https://github.com/AvaloniaUI/AvaloniaEdit/blob/master/LICENSE
+
+### LiveChartsCore.SkiaSharpView.Avalonia
+
+- **Source**: https://github.com/beto-rodriguez/LiveCharts2
+- **Version**: 2.0.0-rc5.4
+- **License**: MIT License
+- **License Link**: https://github.com/beto-rodriguez/LiveCharts2/blob/master/LICENSE
+
+### TextMateSharp
+
+- **Source**: https://github.com/danipen/TextMateSharp
+- **Version**: 1.0.66
+- **License**: MIT License
+- **License Link**: https://github.com/danipen/TextMateSharp/blob/master/LICENSE.md
+
+### OpenAI .NET SDK
+
+- **Source**: https://github.com/openai/openai-dotnet
+- **Version**: 2.2.0-beta2
+- **License**: MIT License
+- **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE
+
+### Azure.AI.OpenAI
+
+- **Source**: https://github.com/Azure/azure-sdk-for-net
+- **Version**: 2.2.0-beta2
+- **License**: MIT License
+- **License Link**: https://github.com/Azure/azure-sdk-for-net/blob/main/LICENSE.txt
+
+## Fonts
+
+### JetBrainsMono
+
+- **Source**: https://github.com/JetBrains/JetBrainsMono
+- **Commit**: v2.304
+- **License**: SIL Open Font License, Version 1.1
+- **License Link**: https://github.com/JetBrains/JetBrainsMono/blob/v2.304/OFL.txt
+
+## Grammar Files
+
+### haxe-TmLanguage
+
+- **Source**: https://github.com/vshaxe/haxe-TmLanguage
+- **Commit**: ddad8b4c6d0781ac20be0481174ec1be772c5da5
+- **License**: MIT License
+- **License Link**: https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/LICENSE.md
+
+### coc-toml
+
+- **Source**: https://github.com/kkiyama117/coc-toml
+- **Commit**: aac3e0c65955c03314b2733041b19f903b7cc447
+- **License**: MIT License
+- **License Link**: https://github.com/kkiyama117/coc-toml/blob/aac3e0c65955c03314b2733041b19f903b7cc447/LICENSE
+
+### eclipse-buildship
+
+- **Source**: https://github.com/eclipse/buildship
+- **Commit**: 6bb773e7692f913dec27105129ebe388de34e68b
+- **License**: Eclipse Public License 1.0
+- **License Link**: https://github.com/eclipse-buildship/buildship/blob/6bb773e7692f913dec27105129ebe388de34e68b/README.md
+
+### vscode-jsp-lang
+
+- **Source**: https://github.com/samuel-weinhardt/vscode-jsp-lang
+- **Commit**: 0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355
+- **License**: MIT License
+- **License Link**: https://github.com/samuel-weinhardt/vscode-jsp-lang/blob/0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355/LICENSE
diff --git a/TRANSLATION.md b/TRANSLATION.md
index e3f9d2a1..1fa3259d 100644
--- a/TRANSLATION.md
+++ b/TRANSLATION.md
@@ -1,116 +1,228 @@
-### de_DE.axaml: 99.07%
+# Translation Status
+This document shows the translation status of each locale file in the repository.
+
+## Details
+
+### 
+
+### 
+
+### 
-Missing Keys
+Missing keys in es_ES.axaml
-- Text.BranchUpstreamInvalid
-- Text.Configure.CustomAction.WaitForExit
-- Text.Diff.First
-- Text.Diff.Last
-- Text.Preferences.AI.Streaming
-- Text.Preferences.Appearance.EditorTabWidth
-- Text.StashCM.SaveAsPatch
+- Text.CommitCM.PushRevision
+- Text.Merge.Edit
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.WorkingCopy.AddToGitIgnore.InFolder
-### es_ES.axaml: 100.00%
-
+### 
-Missing Keys
-
+Missing keys in fr_FR.axaml
+- Text.Avatar.Load
+- Text.Bisect
+- Text.Bisect.Abort
+- Text.Bisect.Bad
+- Text.Bisect.Detecting
+- Text.Bisect.Good
+- Text.Bisect.Skip
+- Text.Bisect.WaitingForRange
+- Text.BranchCM.ResetToSelectedCommit
+- Text.Checkout.RecurseSubmodules
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
+- Text.CommitCM.CopyAuthor
+- Text.CommitCM.CopyCommitter
+- Text.CommitCM.CopySubject
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
+- Text.CommitMessageTextBox.SubjectCount
+- Text.Configure.Git.PreferredMergeMode
+- Text.ConfirmEmptyCommit.Continue
+- Text.ConfirmEmptyCommit.NoLocalChanges
+- Text.ConfirmEmptyCommit.StageAllThenCommit
+- Text.ConfirmEmptyCommit.WithLocalChanges
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
+- Text.Diff.Submodule.Deleted
+- Text.GitFlow.FinishWithPush
+- Text.GitFlow.FinishWithSquash
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
+- Text.Preferences.Git.IgnoreCRAtEOLInDiff
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.BranchSort
+- Text.Repository.BranchSort.ByCommitterDate
+- Text.Repository.BranchSort.ByName
+- Text.Repository.ClearStashes
+- Text.Repository.Search.ByContent
+- Text.Repository.ShowSubmodulesAsTree
+- Text.Repository.ViewLogs
+- Text.Repository.Visit
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.Submodule.Deinit
+- Text.Submodule.Status
+- Text.Submodule.Status.Modified
+- Text.Submodule.Status.NotInited
+- Text.Submodule.Status.RevisionChanged
+- Text.Submodule.Status.Unmerged
+- Text.Submodule.URL
+- Text.ViewLogs
+- Text.ViewLogs.Clear
+- Text.ViewLogs.CopyLog
+- Text.ViewLogs.Delete
+- Text.WorkingCopy.AddToGitIgnore.InFolder
+- Text.WorkingCopy.ConfirmCommitWithFilter
+- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
+- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
+- Text.WorkingCopy.Conflicts.UseMine
+- Text.WorkingCopy.Conflicts.UseTheirs
+- Text.WorkingCopy.ResetAuthor
-### fr_FR.axaml: 91.66%
-
+### 
-Missing Keys
+Missing keys in it_IT.axaml
-- Text.AIAssistant.Regen
-- Text.AIAssistant.Use
-- Text.ApplyStash
-- Text.ApplyStash.DropAfterApply
-- Text.ApplyStash.RestoreIndex
-- Text.ApplyStash.Stash
-- Text.BranchCM.CustomAction
-- Text.BranchUpstreamInvalid
-- Text.Clone.RecurseSubmodules
-- Text.Configure.CustomAction.Scope.Branch
-- Text.Configure.CustomAction.WaitForExit
-- Text.CreateBranch.Name.WarnSpace
-- Text.DeleteRepositoryNode.Path
-- Text.DeleteRepositoryNode.TipForGroup
-- Text.DeleteRepositoryNode.TipForRepository
-- Text.Diff.First
-- Text.Diff.Last
-- Text.InProgress.CherryPick.Head
-- Text.InProgress.Merge.Operating
-- Text.InProgress.Rebase.StoppedAt
-- Text.InProgress.Revert.Head
-- Text.Merge.Source
-- Text.MergeMultiple
-- Text.MergeMultiple.CommitChanges
-- 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
-- Text.Preferences.General.DateFormat
-- Text.Preferences.General.ShowChildren
-- Text.Preferences.Git.SSLVerify
-- Text.Repository.CustomActions
+- Text.Avatar.Load
+- Text.BranchCM.ResetToSelectedCommit
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
+- Text.Diff.Submodule.Deleted
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.ClearStashes
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.Submodule.Deinit
+- Text.WorkingCopy.AddToGitIgnore.InFolder
+- Text.WorkingCopy.ResetAuthor
+
+
+
+### 
+
+
+Missing keys in ja_JP.axaml
+
+- Text.Avatar.Load
+- Text.Bisect
+- Text.Bisect.Abort
+- Text.Bisect.Bad
+- Text.Bisect.Detecting
+- Text.Bisect.Good
+- Text.Bisect.Skip
+- Text.Bisect.WaitingForRange
+- Text.BranchCM.CompareWithCurrent
+- Text.BranchCM.ResetToSelectedCommit
+- Text.Checkout.RecurseSubmodules
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
+- Text.CommitCM.CopyAuthor
+- Text.CommitCM.CopyCommitter
+- Text.CommitCM.CopySubject
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
+- Text.CommitMessageTextBox.SubjectCount
+- Text.Configure.Git.PreferredMergeMode
+- Text.ConfirmEmptyCommit.Continue
+- Text.ConfirmEmptyCommit.NoLocalChanges
+- Text.ConfirmEmptyCommit.StageAllThenCommit
+- Text.ConfirmEmptyCommit.WithLocalChanges
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
+- Text.Diff.Submodule.Deleted
+- Text.GitFlow.FinishWithPush
+- Text.GitFlow.FinishWithSquash
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
+- Text.Preferences.Git.IgnoreCRAtEOLInDiff
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.BranchSort
+- Text.Repository.BranchSort.ByCommitterDate
+- Text.Repository.BranchSort.ByName
+- Text.Repository.ClearStashes
- Text.Repository.FilterCommits
-- Text.Repository.FilterCommits.Default
-- Text.Repository.FilterCommits.Exclude
-- Text.Repository.FilterCommits.Include
-- Text.Repository.HistoriesLayout
-- Text.Repository.HistoriesLayout.Horizontal
-- Text.Repository.HistoriesLayout.Vertical
-- Text.Repository.HistoriesOrder
-- Text.Repository.HistoriesOrder.ByDate
-- Text.Repository.HistoriesOrder.Topo
-- Text.Repository.Notifications.Clear
-- Text.Repository.Skip
-- Text.Repository.Tags.OrderByCreatorDate
-- Text.Repository.Tags.OrderByNameAsc
-- Text.Repository.Tags.OrderByNameDes
-- Text.Repository.Tags.Sort
-- Text.Repository.UseRelativeTimeInHistories
-- Text.ScanRepositories
-- Text.SetUpstream
-- Text.SetUpstream.Local
-- Text.SetUpstream.Unset
-- Text.SetUpstream.Upstream
-- Text.SHALinkCM.NavigateTo
-- Text.Stash.AutoRestore
-- Text.Stash.AutoRestore.Tip
-- Text.StashCM.SaveAsPatch
-- Text.WorkingCopy.CommitToEdit
-- Text.WorkingCopy.SignOff
+- Text.Repository.Search.ByContent
+- Text.Repository.ShowSubmodulesAsTree
+- Text.Repository.ViewLogs
+- Text.Repository.Visit
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.Submodule.Deinit
+- Text.Submodule.Status
+- Text.Submodule.Status.Modified
+- Text.Submodule.Status.NotInited
+- Text.Submodule.Status.RevisionChanged
+- Text.Submodule.Status.Unmerged
+- Text.Submodule.URL
+- Text.ViewLogs
+- Text.ViewLogs.Clear
+- Text.ViewLogs.CopyLog
+- Text.ViewLogs.Delete
+- Text.WorkingCopy.AddToGitIgnore.InFolder
+- Text.WorkingCopy.ConfirmCommitWithFilter
+- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
+- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
+- Text.WorkingCopy.Conflicts.UseMine
+- Text.WorkingCopy.Conflicts.UseTheirs
+- Text.WorkingCopy.ResetAuthor
-### it_IT.axaml: 99.87%
-
+### 
-Missing Keys
-
-- Text.Preferences.Appearance.EditorTabWidth
-
-
-
-### pt_BR.axaml: 91.39%
-
-
-
-Missing Keys
+Missing keys in pt_BR.axaml
- Text.AIAssistant.Regen
- Text.AIAssistant.Use
@@ -118,32 +230,69 @@
- Text.ApplyStash.DropAfterApply
- Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash
+- Text.Avatar.Load
+- Text.Bisect
+- Text.Bisect.Abort
+- Text.Bisect.Bad
+- Text.Bisect.Detecting
+- Text.Bisect.Good
+- Text.Bisect.Skip
+- Text.Bisect.WaitingForRange
- Text.BranchCM.CustomAction
- Text.BranchCM.MergeMultiBranches
+- Text.BranchCM.ResetToSelectedCommit
- Text.BranchUpstreamInvalid
+- Text.Checkout.RecurseSubmodules
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
- Text.Clone.RecurseSubmodules
+- Text.CommitCM.CopyAuthor
+- Text.CommitCM.CopyCommitter
+- Text.CommitCM.CopySubject
- Text.CommitCM.Merge
- Text.CommitCM.MergeMultiple
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
- Text.CommitDetail.Files.Search
- Text.CommitDetail.Info.Children
+- Text.CommitMessageTextBox.SubjectCount
- Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit
+- Text.Configure.Git.PreferredMergeMode
- Text.Configure.IssueTracker.AddSampleGiteeIssue
- Text.Configure.IssueTracker.AddSampleGiteePullRequest
+- Text.ConfirmEmptyCommit.Continue
+- Text.ConfirmEmptyCommit.NoLocalChanges
+- Text.ConfirmEmptyCommit.StageAllThenCommit
+- Text.ConfirmEmptyCommit.WithLocalChanges
+- Text.CopyFullPath
- Text.CreateBranch.Name.WarnSpace
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
- Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
+- Text.Diff.Submodule.Deleted
- Text.Diff.UseBlockNavigation
- Text.Fetch.Force
- Text.FileCM.ResolveUsing
+- Text.GitFlow.FinishWithPush
+- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.Clone
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.InProgress.CherryPick.Head
- Text.InProgress.Merge.Operating
- Text.InProgress.Rebase.StoppedAt
- Text.InProgress.Revert.Head
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
- Text.Merge.Source
- Text.MergeMultiple
- Text.MergeMultiple.CommitChanges
@@ -153,7 +302,16 @@
- Text.Preferences.Appearance.EditorTabWidth
- Text.Preferences.General.DateFormat
- Text.Preferences.General.ShowChildren
+- Text.Preferences.General.ShowTagsInGraph
+- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.SSLVerify
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.BranchSort
+- Text.Repository.BranchSort.ByCommitterDate
+- Text.Repository.BranchSort.ByName
+- Text.Repository.ClearStashes
- Text.Repository.FilterCommits
- Text.Repository.HistoriesLayout
- Text.Repository.HistoriesLayout.Horizontal
@@ -161,51 +319,201 @@
- Text.Repository.HistoriesOrder
- Text.Repository.Notifications.Clear
- Text.Repository.OnlyHighlightCurrentBranchInHistories
+- Text.Repository.Search.ByContent
+- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.Skip
- Text.Repository.Tags.OrderByCreatorDate
-- Text.Repository.Tags.OrderByNameAsc
-- Text.Repository.Tags.OrderByNameDes
+- Text.Repository.Tags.OrderByName
- Text.Repository.Tags.Sort
- Text.Repository.UseRelativeTimeInHistories
+- Text.Repository.ViewLogs
+- Text.Repository.Visit
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
- Text.SetUpstream
- Text.SetUpstream.Local
- Text.SetUpstream.Unset
- Text.SetUpstream.Upstream
- Text.SHALinkCM.NavigateTo
-- Text.Stash.AutoRestore
-- Text.Stash.AutoRestore.Tip
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
- Text.StashCM.SaveAsPatch
+- Text.Submodule.Deinit
+- Text.Submodule.Status
+- Text.Submodule.Status.Modified
+- Text.Submodule.Status.NotInited
+- Text.Submodule.Status.RevisionChanged
+- Text.Submodule.Status.Unmerged
+- Text.Submodule.URL
+- Text.ViewLogs
+- Text.ViewLogs.Clear
+- Text.ViewLogs.CopyLog
+- Text.ViewLogs.Delete
+- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.CommitToEdit
+- Text.WorkingCopy.ConfirmCommitWithFilter
+- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
+- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
+- Text.WorkingCopy.Conflicts.UseMine
+- Text.WorkingCopy.Conflicts.UseTheirs
+- Text.WorkingCopy.ResetAuthor
- Text.WorkingCopy.SignOff
-### ru_RU.axaml: 100.00%
+### 
+### 
-Missing Keys
-
+Missing keys in ta_IN.axaml
+- Text.Avatar.Load
+- Text.Bisect
+- Text.Bisect.Abort
+- Text.Bisect.Bad
+- Text.Bisect.Detecting
+- Text.Bisect.Good
+- Text.Bisect.Skip
+- Text.Bisect.WaitingForRange
+- Text.BranchCM.CompareWithCurrent
+- Text.BranchCM.ResetToSelectedCommit
+- Text.Checkout.RecurseSubmodules
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
+- Text.CommitCM.CopyAuthor
+- Text.CommitCM.CopyCommitter
+- Text.CommitCM.CopySubject
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
+- Text.CommitMessageTextBox.SubjectCount
+- Text.Configure.Git.PreferredMergeMode
+- Text.ConfirmEmptyCommit.Continue
+- Text.ConfirmEmptyCommit.NoLocalChanges
+- Text.ConfirmEmptyCommit.StageAllThenCommit
+- Text.ConfirmEmptyCommit.WithLocalChanges
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
+- Text.Diff.Submodule.Deleted
+- Text.GitFlow.FinishWithPush
+- Text.GitFlow.FinishWithSquash
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
+- Text.Preferences.Git.IgnoreCRAtEOLInDiff
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.BranchSort
+- Text.Repository.BranchSort.ByCommitterDate
+- Text.Repository.BranchSort.ByName
+- Text.Repository.ClearStashes
+- Text.Repository.Search.ByContent
+- Text.Repository.ShowSubmodulesAsTree
+- Text.Repository.ViewLogs
+- Text.Repository.Visit
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.Submodule.Deinit
+- Text.Submodule.Status
+- Text.Submodule.Status.Modified
+- Text.Submodule.Status.NotInited
+- Text.Submodule.Status.RevisionChanged
+- Text.Submodule.Status.Unmerged
+- Text.Submodule.URL
+- Text.UpdateSubmodules.Target
+- Text.ViewLogs
+- Text.ViewLogs.Clear
+- Text.ViewLogs.CopyLog
+- Text.ViewLogs.Delete
+- Text.WorkingCopy.AddToGitIgnore.InFolder
+- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
+- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
+- Text.WorkingCopy.Conflicts.UseMine
+- Text.WorkingCopy.Conflicts.UseTheirs
+- Text.WorkingCopy.ResetAuthor
-### zh_CN.axaml: 100.00%
-
+### 
-Missing Keys
-
+Missing keys in uk_UA.axaml
+- Text.Avatar.Load
+- Text.Bisect
+- Text.Bisect.Abort
+- Text.Bisect.Bad
+- Text.Bisect.Detecting
+- Text.Bisect.Good
+- Text.Bisect.Skip
+- Text.Bisect.WaitingForRange
+- Text.BranchCM.ResetToSelectedCommit
+- Text.Checkout.RecurseSubmodules
+- Text.Checkout.WithFastForward
+- Text.Checkout.WithFastForward.Upstream
+- Text.CommitCM.CopyAuthor
+- Text.CommitCM.CopyCommitter
+- Text.CommitCM.CopySubject
+- Text.CommitCM.PushRevision
+- Text.CommitDetail.Changes.Count
+- Text.CommitMessageTextBox.SubjectCount
+- Text.ConfigureWorkspace.Name
+- Text.CreateBranch.OverwriteExisting
+- Text.DeinitSubmodule
+- Text.DeinitSubmodule.Force
+- Text.DeinitSubmodule.Path
+- Text.Diff.Submodule.Deleted
+- Text.GitFlow.FinishWithPush
+- Text.GitFlow.FinishWithSquash
+- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
+- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
+- Text.Merge.Edit
+- Text.Preferences.Git.IgnoreCRAtEOLInDiff
+- Text.Pull.RecurseSubmodules
+- Text.Push.Revision
+- Text.Push.Revision.Title
+- Text.Repository.BranchSort
+- Text.Repository.BranchSort.ByCommitterDate
+- Text.Repository.BranchSort.ByName
+- Text.Repository.ClearStashes
+- Text.Repository.Search.ByContent
+- Text.Repository.ShowSubmodulesAsTree
+- Text.Repository.ViewLogs
+- Text.Repository.Visit
+- Text.ResetWithoutCheckout
+- Text.ResetWithoutCheckout.MoveTo
+- Text.ResetWithoutCheckout.Target
+- Text.Stash.Mode
+- Text.StashCM.CopyMessage
+- Text.Submodule.Deinit
+- Text.Submodule.Status
+- Text.Submodule.Status.Modified
+- Text.Submodule.Status.NotInited
+- Text.Submodule.Status.RevisionChanged
+- Text.Submodule.Status.Unmerged
+- Text.Submodule.URL
+- Text.ViewLogs
+- Text.ViewLogs.Clear
+- Text.ViewLogs.CopyLog
+- Text.ViewLogs.Delete
+- Text.WorkingCopy.AddToGitIgnore.InFolder
+- Text.WorkingCopy.ResetAuthor
-### zh_TW.axaml: 100.00%
+### 
-
-
-Missing Keys
-
-
-
-
+### 
\ No newline at end of file
diff --git a/VERSION b/VERSION
index 23993bfb..11692c6b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2025.09
\ No newline at end of file
+2025.23
\ No newline at end of file
diff --git a/build/README.md b/build/README.md
index b4358a55..17305edf 100644
--- a/build/README.md
+++ b/build/README.md
@@ -12,4 +12,4 @@
dotnet publish -c Release -r $RUNTIME_IDENTIFIER -o $DESTINATION_FOLDER src/SourceGit.csproj
```
> [!NOTE]
-> Please replace the `$RUNTIME_IDENTIFIER` with one of `win-x64`,`win-arm64`,`linux-x64`,`linux-arm64`,`osx-x64`,`osx-arm64`, and replece the `$DESTINATION_FOLDER` with the real path that will store the output executable files.
+> Please replace the `$RUNTIME_IDENTIFIER` with one of `win-x64`,`win-arm64`,`linux-x64`,`linux-arm64`,`osx-x64`,`osx-arm64`, and replace the `$DESTINATION_FOLDER` with the real path that will store the output executable files.
diff --git a/build/resources/deb/DEBIAN/control b/build/resources/deb/DEBIAN/control
index f553db8b..71786b43 100755
--- a/build/resources/deb/DEBIAN/control
+++ b/build/resources/deb/DEBIAN/control
@@ -1,7 +1,8 @@
Package: sourcegit
-Version: 8.23
+Version: 2025.10
Priority: optional
Depends: libx11-6, libice6, libsm6, libicu | libicu76 | libicu74 | libicu72 | libicu71 | libicu70 | libicu69 | libicu68 | libicu67 | libicu66 | libicu65 | libicu63 | libicu60 | libicu57 | libicu55 | libicu52, xdg-utils
Architecture: amd64
+Installed-Size: 60440
Maintainer: longshuang@msn.cn
Description: Open-source & Free Git GUI Client
diff --git a/build/scripts/localization-check.js b/build/scripts/localization-check.js
index ed89a5e8..8d636b5b 100644
--- a/build/scripts/localization-check.js
+++ b/build/scripts/localization-check.js
@@ -6,7 +6,6 @@ const repoRoot = path.join(__dirname, '../../');
const localesDir = path.join(repoRoot, 'src/Resources/Locales');
const enUSFile = path.join(localesDir, 'en_US.axaml');
const outputFile = path.join(repoRoot, 'TRANSLATION.md');
-const readmeFile = path.join(repoRoot, 'README.md');
const parser = new xml2js.Parser();
@@ -15,49 +14,70 @@ async function parseXml(filePath) {
return parser.parseStringPromise(data);
}
+async function filterAndSortTranslations(localeData, enUSKeys, enUSData) {
+ const strings = localeData.ResourceDictionary['x:String'];
+ // Remove keys that don't exist in English file
+ const filtered = strings.filter(item => enUSKeys.has(item.$['x:Key']));
+
+ // Sort based on the key order in English file
+ const enUSKeysArray = enUSData.ResourceDictionary['x:String'].map(item => item.$['x:Key']);
+ filtered.sort((a, b) => {
+ const aIndex = enUSKeysArray.indexOf(a.$['x:Key']);
+ const bIndex = enUSKeysArray.indexOf(b.$['x:Key']);
+ return aIndex - bIndex;
+ });
+
+ return filtered;
+}
+
async function calculateTranslationRate() {
const enUSData = await parseXml(enUSFile);
const enUSKeys = new Set(enUSData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
-
- const translationRates = [];
- const badges = [];
-
const files = (await fs.readdir(localesDir)).filter(file => file !== 'en_US.axaml' && file.endsWith('.axaml'));
- // Add en_US badge first
- badges.push(`[](TRANSLATION.md)`);
+ const lines = [];
+
+ lines.push('# Translation Status');
+ lines.push('This document shows the translation status of each locale file in the repository.');
+ lines.push(`## Details`);
+ lines.push(`### `);
for (const file of files) {
+ const locale = file.replace('.axaml', '').replace('_', '__');
const filePath = path.join(localesDir, file);
const localeData = await parseXml(filePath);
const localeKeys = new Set(localeData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
-
const missingKeys = [...enUSKeys].filter(key => !localeKeys.has(key));
- const translationRate = ((enUSKeys.size - missingKeys.length) / enUSKeys.size) * 100;
- translationRates.push(`### ${file}: ${translationRate.toFixed(2)}%\n`);
- translationRates.push(`\nMissing Keys
\n\n${missingKeys.map(key => `- ${key}`).join('\n')}\n\n `);
+ // Sort and clean up extra translations
+ const sortedAndCleaned = await filterAndSortTranslations(localeData, enUSKeys, enUSData);
+ localeData.ResourceDictionary['x:String'] = sortedAndCleaned;
- // Add badges
- const locale = file.replace('.axaml', '').replace('_', '__');
- if (translationRate === 100) {
- badges.push(`[](TRANSLATION.md)`);
+ // Save the updated file
+ const builder = new xml2js.Builder({
+ headless: true,
+ renderOpts: { pretty: true, indent: ' ' }
+ });
+ let xmlStr = builder.buildObject(localeData);
+
+ // Add an empty line before the first x:String
+ xmlStr = xmlStr.replace(' 0) {
+ const progress = ((enUSKeys.size - missingKeys.length) / enUSKeys.size) * 100;
+ const badgeColor = progress >= 75 ? 'yellow' : 'red';
+
+ lines.push(`### }%25-${badgeColor})`);
+ lines.push(`\nMissing keys in ${file}
\n\n${missingKeys.map(key => `- ${key}`).join('\n')}\n\n `)
} else {
- const badgeColor = translationRate >= 75 ? 'yellow' : 'red';
- badges.push(`[}%25-${badgeColor})](TRANSLATION.md)`);
- }
+ lines.push(`### `);
+ }
}
- console.log(translationRates.join('\n\n'));
-
- await fs.writeFile(outputFile, translationRates.join('\n\n') + '\n', 'utf8');
-
- // Update README.md
- let readmeContent = await fs.readFile(readmeFile, 'utf8');
- const badgeSection = `## Translation Status\n\n${badges.join(' ')}`;
- console.log(badgeSection);
- readmeContent = readmeContent.replace(/## Translation Status\n\n.*\n\n/, badgeSection + '\n\n');
- await fs.writeFile(readmeFile, readmeContent, 'utf8');
+ const content = lines.join('\n\n');
+ console.log(content);
+ await fs.writeFile(outputFile, content, 'utf8');
}
calculateTranslationRate().catch(err => console.error(err));
diff --git a/build/scripts/package.linux.sh b/build/scripts/package.linux.sh
index 5abb058b..1b4adbdc 100755
--- a/build/scripts/package.linux.sh
+++ b/build/scripts/package.linux.sh
@@ -56,8 +56,15 @@ cp -f SourceGit/* resources/deb/opt/sourcegit
ln -rsf resources/deb/opt/sourcegit/sourcegit resources/deb/usr/bin
cp -r resources/_common/applications resources/deb/usr/share
cp -r resources/_common/icons resources/deb/usr/share
-sed -i -e "s/^Version:.*/Version: $VERSION/" -e "s/^Architecture:.*/Architecture: $arch/" resources/deb/DEBIAN/control
-dpkg-deb --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb"
+# Calculate installed size in KB
+installed_size=$(du -sk resources/deb | cut -f1)
+# Update the control file
+sed -i -e "s/^Version:.*/Version: $VERSION/" \
+ -e "s/^Architecture:.*/Architecture: $arch/" \
+ -e "s/^Installed-Size:.*/Installed-Size: $installed_size/" \
+ resources/deb/DEBIAN/control
+# Build deb package with gzip compression
+dpkg-deb -Zgzip --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb"
rpmbuild -bb --target="$target" resources/rpm/SPECS/build.spec --define "_topdir $(pwd)/resources/rpm" --define "_version $VERSION"
mv "resources/rpm/RPMS/$target/sourcegit-$VERSION-1.$target.rpm" ./
diff --git a/src/App.Commands.cs b/src/App.Commands.cs
index 85a75829..22e9fb51 100644
--- a/src/App.Commands.cs
+++ b/src/App.Commands.cs
@@ -37,10 +37,10 @@ namespace SourceGit
}
}
- public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences()));
- public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys()));
+ public static readonly Command OpenPreferencesCommand = new Command(_ => ShowWindow(new Views.Preferences(), false));
+ public static readonly Command OpenHotkeysCommand = new Command(_ => ShowWindow(new Views.Hotkeys(), false));
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
- public static readonly Command OpenAboutCommand = new Command(_ => OpenDialog(new Views.About()));
+ public static readonly Command OpenAboutCommand = new Command(_ => ShowWindow(new Views.About(), false));
public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true));
public static readonly Command QuitCommand = new Command(_ => Quit(0));
public static readonly Command CopyTextBlockCommand = new Command(p =>
diff --git a/src/App.axaml b/src/App.axaml
index 76d4baa8..186022d5 100644
--- a/src/App.axaml
+++ b/src/App.axaml
@@ -16,10 +16,13 @@
+
+
+
@@ -32,7 +35,7 @@
-
+
diff --git a/src/App.axaml.cs b/src/App.axaml.cs
index f59d35db..6b42700d 100644
--- a/src/App.axaml.cs
+++ b/src/App.axaml.cs
@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -37,7 +38,6 @@ namespace SourceGit
TaskScheduler.UnobservedTaskException += (_, e) =>
{
- LogException(e.Exception);
e.SetObserved();
};
@@ -78,7 +78,7 @@ namespace SourceGit
return builder;
}
- private static void LogException(Exception ex)
+ public static void LogException(Exception ex)
{
if (ex == null)
return;
@@ -105,10 +105,44 @@ namespace SourceGit
#endregion
#region Utility Functions
- public static void OpenDialog(Window window)
+ public static void ShowWindow(object data, bool showAsDialog)
{
- if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
- window.ShowDialog(owner);
+ var impl = (Views.ChromelessWindow target, bool isDialog) =>
+ {
+ if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
+ {
+ if (isDialog)
+ target.ShowDialog(owner);
+ else
+ target.Show(owner);
+ }
+ else
+ {
+ target.Show();
+ }
+ };
+
+ if (data is Views.ChromelessWindow window)
+ {
+ impl(window, showAsDialog);
+ return;
+ }
+
+ var dataTypeName = data.GetType().FullName;
+ if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
+ return;
+
+ var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
+ var viewType = Type.GetType(viewTypeName);
+ if (viewType == null || !viewType.IsSubclassOf(typeof(Views.ChromelessWindow)))
+ return;
+
+ window = Activator.CreateInstance(viewType) as Views.ChromelessWindow;
+ if (window != null)
+ {
+ window.DataContext = data;
+ impl(window, showAsDialog);
+ }
}
public static void RaiseException(string context, string message)
@@ -267,7 +301,7 @@ namespace SourceGit
return await clipboard.GetTextAsync();
}
}
- return default;
+ return null;
}
public static string Text(string key, params object[] args)
@@ -289,8 +323,7 @@ namespace SourceGit
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
- var geo = Current?.FindResource(key) as StreamGeometry;
- if (geo != null)
+ if (Current?.FindResource(key) is StreamGeometry geo)
icon.Data = geo;
return icon;
@@ -304,7 +337,7 @@ namespace SourceGit
return null;
}
- public static ViewModels.Launcher GetLauncer()
+ public static ViewModels.Launcher GetLauncher()
{
return Current is App app ? app._launcher : null;
}
@@ -342,13 +375,42 @@ namespace SourceGit
{
BindingPlugins.DataValidators.RemoveAt(0);
+ // Disable tooltip if window is not active.
+ ToolTip.ToolTipOpeningEvent.AddClassHandler((c, e) =>
+ {
+ var topLevel = TopLevel.GetTopLevel(c);
+ if (topLevel is not Window { IsActive: true })
+ e.Cancel = true;
+ });
+
if (TryLaunchAsCoreEditor(desktop))
return;
if (TryLaunchAsAskpass(desktop))
return;
- TryLaunchAsNormal(desktop);
+ _ipcChannel = new Models.IpcChannel();
+ if (!_ipcChannel.IsFirstInstance)
+ {
+ var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty;
+ if (!string.IsNullOrEmpty(arg))
+ {
+ if (arg.StartsWith('"') && arg.EndsWith('"'))
+ arg = arg.Substring(1, arg.Length - 2).Trim();
+
+ if (arg.Length > 0 && !Path.IsPathFullyQualified(arg))
+ arg = Path.GetFullPath(arg);
+ }
+
+ _ipcChannel.SendToFirstInstance(arg);
+ Environment.Exit(0);
+ }
+ else
+ {
+ _ipcChannel.MessageReceived += TryOpenRepository;
+ desktop.Exit += (_, _) => _ipcChannel.Dispose();
+ TryLaunchAsNormal(desktop);
+ }
}
}
#endregion
@@ -421,21 +483,37 @@ namespace SourceGit
return true;
var gitDir = Path.GetDirectoryName(file)!;
- var jobsFile = Path.Combine(gitDir, "sourcegit_rebase_jobs.json");
- if (!File.Exists(jobsFile))
- return true;
-
- var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
+ var origHeadFile = Path.Combine(gitDir, "rebase-merge", "orig-head");
+ var ontoFile = Path.Combine(gitDir, "rebase-merge", "onto");
var doneFile = Path.Combine(gitDir, "rebase-merge", "done");
- if (!File.Exists(doneFile))
+ var jobsFile = Path.Combine(gitDir, "sourcegit_rebase_jobs.json");
+ if (!File.Exists(ontoFile) || !File.Exists(origHeadFile) || !File.Exists(doneFile) || !File.Exists(jobsFile))
return true;
- var done = File.ReadAllText(doneFile).Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
- if (done.Length > collection.Jobs.Count)
+ var origHead = File.ReadAllText(origHeadFile).Trim();
+ var onto = File.ReadAllText(ontoFile).Trim();
+ var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
+ if (!collection.Onto.Equals(onto) || !collection.OrigHead.Equals(origHead))
return true;
- var job = collection.Jobs[done.Length - 1];
- File.WriteAllText(file, job.Message);
+ var done = File.ReadAllText(doneFile).Trim().Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ if (done.Length == 0)
+ return true;
+
+ var current = done[^1].Trim();
+ var match = REG_REBASE_TODO().Match(current);
+ if (!match.Success)
+ return true;
+
+ var sha = match.Groups[1].Value;
+ foreach (var job in collection.Jobs)
+ {
+ if (job.SHA.StartsWith(sha))
+ {
+ File.WriteAllText(file, job.Message);
+ break;
+ }
+ }
return true;
}
@@ -443,7 +521,7 @@ namespace SourceGit
private bool TryLaunchAsCoreEditor(IClassicDesktopStyleApplicationLifetime desktop)
{
var args = desktop.Args;
- if (args == null || args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal))
+ if (args is not { Length: > 1 } || !args[0].Equals("--core-editor", StringComparison.Ordinal))
return false;
var file = args[1];
@@ -453,8 +531,8 @@ namespace SourceGit
return true;
}
- var editor = new Views.StandaloneCommitMessageEditor();
- editor.SetFile(file);
+ var editor = new Views.CommitMessageEditor();
+ editor.AsStandalone(file);
desktop.MainWindow = editor;
return true;
}
@@ -479,23 +557,54 @@ namespace SourceGit
private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
{
- Native.OS.SetupEnternalTools();
+ Native.OS.SetupExternalTools();
Models.AvatarManager.Instance.Start();
string startupRepo = null;
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))
startupRepo = desktop.Args[0];
+ var pref = ViewModels.Preferences.Instance;
+ pref.SetCanModify();
+
_launcher = new ViewModels.Launcher(startupRepo);
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
+ desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
#if !DISABLE_UPDATE_DETECTION
- var pref = ViewModels.Preferences.Instance;
if (pref.ShouldCheck4UpdateOnStartup())
Check4Update();
#endif
}
+ private void TryOpenRepository(string repo)
+ {
+ if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo))
+ {
+ var test = new Commands.QueryRepositoryRootPath(repo).ReadToEnd();
+ if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ var node = ViewModels.Preferences.Instance.FindOrAddNodeByRepositoryPath(test.StdOut.Trim(), null, false);
+ ViewModels.Welcome.Instance.Refresh();
+ _launcher?.OpenRepositoryInTab(node, null);
+
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher wnd })
+ wnd.BringToTop();
+ });
+
+ return;
+ }
+ }
+
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher launcher })
+ launcher.BringToTop();
+ });
+ }
+
private void Check4Update(bool manually = false)
{
Task.Run(async () =>
@@ -532,7 +641,7 @@ namespace SourceGit
catch (Exception e)
{
if (manually)
- ShowSelfUpdateResult(e);
+ ShowSelfUpdateResult(new Models.SelfUpdateFailed(e));
}
});
}
@@ -541,11 +650,7 @@ namespace SourceGit
{
Dispatcher.UIThread.Post(() =>
{
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
- {
- var dialog = new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } };
- dialog.ShowDialog(owner);
- }
+ ShowWindow(new ViewModels.SelfUpdate() { Data = data }, true);
});
}
@@ -560,13 +665,39 @@ namespace SourceGit
foreach (var part in parts)
{
var t = part.Trim();
- if (!string.IsNullOrEmpty(t))
- trimmed.Add(t);
+ if (string.IsNullOrEmpty(t))
+ continue;
+
+ // Collapse multiple spaces into single space
+ var prevChar = '\0';
+ var sb = new StringBuilder();
+
+ foreach (var c in t)
+ {
+ if (c == ' ' && prevChar == ' ')
+ continue;
+ sb.Append(c);
+ prevChar = c;
+ }
+
+ var name = sb.ToString();
+ if (name.Contains('#', StringComparison.Ordinal))
+ {
+ if (!name.Equals("fonts:Inter#Inter", StringComparison.Ordinal) &&
+ !name.Equals("fonts:SourceGit#JetBrains Mono", StringComparison.Ordinal))
+ continue;
+ }
+
+ trimmed.Add(name);
}
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
}
+ [GeneratedRegex(@"^[a-z]+\s+([a-fA-F0-9]{4,40})(\s+.*)?$")]
+ private static partial Regex REG_REBASE_TODO();
+
+ private Models.IpcChannel _ipcChannel = null;
private ViewModels.Launcher _launcher = null;
private ResourceDictionary _activeLocale = null;
private ResourceDictionary _themeOverrides = null;
diff --git a/src/App.manifest b/src/App.manifest
index b3bc3bdf..11a2ff11 100644
--- a/src/App.manifest
+++ b/src/App.manifest
@@ -1,7 +1,7 @@
diff --git a/src/Commands/Add.cs b/src/Commands/Add.cs
index b2aa803d..210eb4b2 100644
--- a/src/Commands/Add.cs
+++ b/src/Commands/Add.cs
@@ -1,7 +1,4 @@
-using System.Collections.Generic;
-using System.Text;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Add : Command
{
@@ -12,20 +9,11 @@ namespace SourceGit.Commands
Args = includeUntracked ? "add ." : "add -u .";
}
- public Add(string repo, List changes)
+ public Add(string repo, Models.Change change)
{
WorkingDirectory = repo;
Context = repo;
-
- var builder = new StringBuilder();
- builder.Append("add --");
- foreach (var c in changes)
- {
- builder.Append(" \"");
- builder.Append(c);
- builder.Append("\"");
- }
- Args = builder.ToString();
+ Args = $"add -- \"{change.Path}\"";
}
public Add(string repo, string pathspecFromFile)
diff --git a/src/Commands/Archive.cs b/src/Commands/Archive.cs
index d4f6241c..5e0919f7 100644
--- a/src/Commands/Archive.cs
+++ b/src/Commands/Archive.cs
@@ -1,23 +1,12 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Archive : Command
{
- public Archive(string repo, string revision, string saveTo, Action outputHandler)
+ public Archive(string repo, string revision, string saveTo)
{
WorkingDirectory = repo;
Context = repo;
Args = $"archive --format=zip --verbose --output=\"{saveTo}\" {revision}";
- TraitErrorAsOutput = true;
- _outputHandler = outputHandler;
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler;
}
}
diff --git a/src/Commands/AssumeUnchanged.cs b/src/Commands/AssumeUnchanged.cs
index 1898122a..28f78280 100644
--- a/src/Commands/AssumeUnchanged.cs
+++ b/src/Commands/AssumeUnchanged.cs
@@ -1,75 +1,14 @@
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
- public partial class AssumeUnchanged
+ public class AssumeUnchanged : Command
{
- [GeneratedRegex(@"^(\w)\s+(.+)$")]
- private static partial Regex REG_PARSE();
-
- class ViewCommand : Command
+ public AssumeUnchanged(string repo, string file, bool bAdd)
{
- public ViewCommand(string repo)
- {
- WorkingDirectory = repo;
- Args = "ls-files -v";
- RaiseError = false;
- }
+ var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
- public List Result()
- {
- Exec();
- return _outs;
- }
-
- protected override void OnReadline(string line)
- {
- var match = REG_PARSE().Match(line);
- if (!match.Success)
- return;
-
- if (match.Groups[1].Value == "h")
- {
- _outs.Add(match.Groups[2].Value);
- }
- }
-
- private readonly List _outs = new List();
+ WorkingDirectory = repo;
+ Context = repo;
+ Args = $"update-index {mode} -- \"{file}\"";
}
-
- class ModCommand : Command
- {
- public ModCommand(string repo, string file, bool bAdd)
- {
- var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
-
- WorkingDirectory = repo;
- Context = repo;
- Args = $"update-index {mode} -- \"{file}\"";
- }
- }
-
- public AssumeUnchanged(string repo)
- {
- _repo = repo;
- }
-
- public List View()
- {
- return new ViewCommand(_repo).Result();
- }
-
- public void Add(string file)
- {
- new ModCommand(_repo, file, true).Exec();
- }
-
- public void Remove(string file)
- {
- new ModCommand(_repo, file, false).Exec();
- }
-
- private readonly string _repo;
}
}
diff --git a/src/Commands/Bisect.cs b/src/Commands/Bisect.cs
new file mode 100644
index 00000000..a3bf1a97
--- /dev/null
+++ b/src/Commands/Bisect.cs
@@ -0,0 +1,13 @@
+namespace SourceGit.Commands
+{
+ public class Bisect : Command
+ {
+ public Bisect(string repo, string subcmd)
+ {
+ WorkingDirectory = repo;
+ Context = repo;
+ RaiseError = false;
+ Args = $"bisect {subcmd}";
+ }
+ }
+}
diff --git a/src/Commands/Blame.cs b/src/Commands/Blame.cs
index 291249be..1fc51fa4 100644
--- a/src/Commands/Blame.cs
+++ b/src/Commands/Blame.cs
@@ -21,10 +21,17 @@ namespace SourceGit.Commands
public Models.BlameData Result()
{
- var succ = Exec();
- if (!succ)
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
+ return _result;
+
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
{
- return new Models.BlameData();
+ ParseLine(line);
+
+ if (_result.IsBinary)
+ break;
}
if (_needUnifyCommitSHA)
@@ -42,14 +49,9 @@ namespace SourceGit.Commands
return _result;
}
- protected override void OnReadline(string line)
+ private void ParseLine(string line)
{
- if (_result.IsBinary)
- return;
- if (string.IsNullOrEmpty(line))
- return;
-
- if (line.IndexOf('\0', StringComparison.Ordinal) >= 0)
+ if (line.Contains('\0', StringComparison.Ordinal))
{
_result.IsBinary = true;
_result.LineInfos.Clear();
@@ -87,7 +89,7 @@ namespace SourceGit.Commands
private readonly Models.BlameData _result = new Models.BlameData();
private readonly StringBuilder _content = new StringBuilder();
- private readonly string _dateFormat = Models.DateTimeFormat.Actived.DateOnly;
+ private readonly string _dateFormat = Models.DateTimeFormat.Active.DateOnly;
private string _lastSHA = string.Empty;
private bool _needUnifyCommitSHA = false;
private int _minSHALen = 64;
diff --git a/src/Commands/Branch.cs b/src/Commands/Branch.cs
index 2dc8a98d..0d1b1f8f 100644
--- a/src/Commands/Branch.cs
+++ b/src/Commands/Branch.cs
@@ -1,4 +1,6 @@
-namespace SourceGit.Commands
+using System.Text;
+
+namespace SourceGit.Commands
{
public static class Branch
{
@@ -11,29 +13,40 @@
return cmd.ReadToEnd().StdOut.Trim();
}
- public static bool Create(string repo, string name, string basedOn)
+ public static bool Create(string repo, string name, string basedOn, bool force, Models.ICommandLog log)
{
+ var builder = new StringBuilder();
+ builder.Append("branch ");
+ if (force)
+ builder.Append("-f ");
+ builder.Append(name);
+ builder.Append(" ");
+ builder.Append(basedOn);
+
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
- cmd.Args = $"branch {name} {basedOn}";
+ cmd.Args = builder.ToString();
+ cmd.Log = log;
return cmd.Exec();
}
- public static bool Rename(string repo, string name, string to)
+ public static bool Rename(string repo, string name, string to, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -M {name} {to}";
+ cmd.Log = log;
return cmd.Exec();
}
- public static bool SetUpstream(string repo, string name, string upstream)
+ public static bool SetUpstream(string repo, string name, string upstream, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
+ cmd.Log = log;
if (string.IsNullOrEmpty(upstream))
cmd.Args = $"branch {name} --unset-upstream";
@@ -43,25 +56,27 @@
return cmd.Exec();
}
- public static bool DeleteLocal(string repo, string name)
+ public static bool DeleteLocal(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -D {name}";
+ cmd.Log = log;
return cmd.Exec();
}
- public static bool DeleteRemote(string repo, string remote, string name)
+ public static bool DeleteRemote(string repo, string remote, string name, Models.ICommandLog log)
{
bool exists = new Remote(repo).HasBranch(remote, name);
if (exists)
- return new Push(repo, remote, $"refs/heads/{name}", true).Exec();
+ return new Push(repo, remote, $"refs/heads/{name}", true) { Log = log }.Exec();
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -D -r {remote}/{name}";
+ cmd.Log = log;
return cmd.Exec();
}
}
diff --git a/src/Commands/Checkout.cs b/src/Commands/Checkout.cs
index 306d62ff..d2876740 100644
--- a/src/Commands/Checkout.cs
+++ b/src/Commands/Checkout.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
@@ -12,19 +11,37 @@ namespace SourceGit.Commands
Context = repo;
}
- public bool Branch(string branch, Action onProgress)
+ public bool Branch(string branch, bool force)
{
- Args = $"checkout --recurse-submodules --progress {branch}";
- TraitErrorAsOutput = true;
- _outputHandler = onProgress;
+ var builder = new StringBuilder();
+ builder.Append("checkout --progress ");
+ if (force)
+ builder.Append("--force ");
+ builder.Append(branch);
+
+ Args = builder.ToString();
return Exec();
}
- public bool Branch(string branch, string basedOn, Action onProgress)
+ public bool Branch(string branch, string basedOn, bool force, bool allowOverwrite)
{
- Args = $"checkout --recurse-submodules --progress -b {branch} {basedOn}";
- TraitErrorAsOutput = true;
- _outputHandler = onProgress;
+ var builder = new StringBuilder();
+ builder.Append("checkout --progress ");
+ if (force)
+ builder.Append("--force ");
+ builder.Append(allowOverwrite ? "-B " : "-b ");
+ builder.Append(branch);
+ builder.Append(" ");
+ builder.Append(basedOn);
+
+ Args = builder.ToString();
+ return Exec();
+ }
+
+ public bool Commit(string commitId, bool force)
+ {
+ var option = force ? "--force" : string.Empty;
+ Args = $"checkout {option} --detach --progress {commitId}";
return Exec();
}
@@ -61,20 +78,5 @@ namespace SourceGit.Commands
Args = $"checkout --no-overlay {revision} -- \"{file}\"";
return Exec();
}
-
- public bool Commit(string commitId, Action onProgress)
- {
- Args = $"checkout --detach --progress {commitId}";
- TraitErrorAsOutput = true;
- _outputHandler = onProgress;
- return Exec();
- }
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private Action _outputHandler;
}
}
diff --git a/src/Commands/Clean.cs b/src/Commands/Clean.cs
index a10e5873..6ed74999 100644
--- a/src/Commands/Clean.cs
+++ b/src/Commands/Clean.cs
@@ -1,31 +1,12 @@
-using System.Collections.Generic;
-using System.Text;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Clean : Command
{
- public Clean(string repo, bool includeIgnored)
+ public Clean(string repo)
{
WorkingDirectory = repo;
Context = repo;
- Args = includeIgnored ? "clean -qfdx" : "clean -qfd";
- }
-
- public Clean(string repo, List files)
- {
- var builder = new StringBuilder();
- builder.Append("clean -qfd --");
- foreach (var f in files)
- {
- builder.Append(" \"");
- builder.Append(f);
- builder.Append("\"");
- }
-
- WorkingDirectory = repo;
- Context = repo;
- Args = builder.ToString();
+ Args = "clean -qfdx";
}
}
}
diff --git a/src/Commands/Clone.cs b/src/Commands/Clone.cs
index f2faed14..efec264b 100644
--- a/src/Commands/Clone.cs
+++ b/src/Commands/Clone.cs
@@ -1,16 +1,11 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Clone : Command
{
- private readonly Action _notifyProgress;
-
- public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs, Action ouputHandler)
+ public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs)
{
Context = ctx;
WorkingDirectory = path;
- TraitErrorAsOutput = true;
SSHKey = sshKey;
Args = "clone --progress --verbose ";
@@ -21,13 +16,6 @@ namespace SourceGit.Commands
if (!string.IsNullOrEmpty(localName))
Args += localName;
-
- _notifyProgress = ouputHandler;
- }
-
- protected override void OnReadline(string line)
- {
- _notifyProgress?.Invoke(line);
}
}
}
diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs
index 0fef1235..975922fc 100644
--- a/src/Commands/Command.cs
+++ b/src/Commands/Command.cs
@@ -32,45 +32,18 @@ namespace SourceGit.Commands
public string SSHKey { get; set; } = string.Empty;
public string Args { get; set; } = string.Empty;
public bool RaiseError { get; set; } = true;
- public bool TraitErrorAsOutput { get; set; } = false;
+ public Models.ICommandLog Log { get; set; } = null;
public bool Exec()
{
+ Log?.AppendLine($"$ git {Args}\n");
+
var start = CreateGitStartInfo();
var errs = new List();
var proc = new Process() { StartInfo = start };
- proc.OutputDataReceived += (_, e) =>
- {
- if (e.Data != null)
- OnReadline(e.Data);
- };
-
- proc.ErrorDataReceived += (_, e) =>
- {
- if (string.IsNullOrEmpty(e.Data))
- {
- errs.Add(string.Empty);
- return;
- }
-
- if (TraitErrorAsOutput)
- OnReadline(e.Data);
-
- // Ignore progress messages
- if (e.Data.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal))
- return;
- if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal))
- return;
- if (e.Data.StartsWith("remote: Compressing objects:", StringComparison.Ordinal))
- return;
- if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal))
- return;
- if (REG_PROGRESS().IsMatch(e.Data))
- return;
-
- errs.Add(e.Data);
- };
+ proc.OutputDataReceived += (_, e) => HandleOutput(e.Data, errs);
+ proc.ErrorDataReceived += (_, e) => HandleOutput(e.Data, errs);
var dummy = null as Process;
var dummyProcLock = new object();
@@ -97,6 +70,7 @@ namespace SourceGit.Commands
if (RaiseError)
Dispatcher.UIThread.Post(() => App.RaiseException(Context, e.Message));
+ Log?.AppendLine(string.Empty);
return false;
}
@@ -114,6 +88,7 @@ namespace SourceGit.Commands
int exitCode = proc.ExitCode;
proc.Close();
+ Log?.AppendLine(string.Empty);
if (!CancellationToken.IsCancellationRequested && exitCode != 0)
{
@@ -162,11 +137,6 @@ namespace SourceGit.Commands
return rs;
}
- protected virtual void OnReadline(string line)
- {
- // Implemented by derived class
- }
-
private ProcessStartInfo CreateGitStartInfo()
{
var start = new ProcessStartInfo();
@@ -222,6 +192,28 @@ namespace SourceGit.Commands
return start;
}
+ private void HandleOutput(string line, List errs)
+ {
+ line ??= string.Empty;
+ Log?.AppendLine(line);
+
+ // Lines to hide in error message.
+ if (line.Length > 0)
+ {
+ if (line.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal) ||
+ line.StartsWith("remote: Counting objects:", StringComparison.Ordinal) ||
+ line.StartsWith("remote: Compressing objects:", StringComparison.Ordinal) ||
+ line.StartsWith("Filtering content:", StringComparison.Ordinal) ||
+ line.StartsWith("hint:", StringComparison.Ordinal))
+ return;
+
+ if (REG_PROGRESS().IsMatch(line))
+ return;
+ }
+
+ errs.Add(line);
+ }
+
[GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS();
}
diff --git a/src/Commands/Commit.cs b/src/Commands/Commit.cs
index cb086793..1585e7e3 100644
--- a/src/Commands/Commit.cs
+++ b/src/Commands/Commit.cs
@@ -4,19 +4,18 @@ namespace SourceGit.Commands
{
public class Commit : Command
{
- public Commit(string repo, string message, bool amend, bool signOff)
+ public Commit(string repo, string message, bool signOff, bool amend, bool resetAuthor)
{
_tmpFile = Path.GetTempFileName();
File.WriteAllText(_tmpFile, message);
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
Args = $"commit --allow-empty --file=\"{_tmpFile}\"";
- if (amend)
- Args += " --amend --no-edit";
if (signOff)
Args += " --signoff";
+ if (amend)
+ Args += resetAuthor ? " --amend --reset-author --no-edit" : " --amend --no-edit";
}
public bool Run()
@@ -35,6 +34,6 @@ namespace SourceGit.Commands
return succ;
}
- private string _tmpFile = string.Empty;
+ private readonly string _tmpFile;
}
}
diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs
index 8a6f2832..c88e087a 100644
--- a/src/Commands/CompareRevisions.cs
+++ b/src/Commands/CompareRevisions.cs
@@ -6,8 +6,10 @@ namespace SourceGit.Commands
{
public partial class CompareRevisions : Command
{
- [GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")]
+ [GeneratedRegex(@"^([MADC])\s+(.+)$")]
private static partial Regex REG_FORMAT();
+ [GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")]
+ private static partial Regex REG_RENAME_FORMAT();
public CompareRevisions(string repo, string start, string end)
{
@@ -29,16 +31,33 @@ namespace SourceGit.Commands
public List Result()
{
- Exec();
- _changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal));
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
+ return _changes;
+
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ ParseLine(line);
+
+ _changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path));
return _changes;
}
- protected override void OnReadline(string line)
+ private void ParseLine(string line)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
+ {
+ match = REG_RENAME_FORMAT().Match(line);
+ if (match.Success)
+ {
+ var renamed = new Models.Change() { Path = match.Groups[1].Value };
+ renamed.Set(Models.ChangeState.Renamed);
+ _changes.Add(renamed);
+ }
+
return;
+ }
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
@@ -57,10 +76,6 @@ namespace SourceGit.Commands
change.Set(Models.ChangeState.Deleted);
_changes.Add(change);
break;
- case 'R':
- change.Set(Models.ChangeState.Renamed);
- _changes.Add(change);
- break;
case 'C':
change.Set(Models.ChangeState.Copied);
_changes.Add(change);
diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs
index 2fb83325..49e8fcb7 100644
--- a/src/Commands/Config.cs
+++ b/src/Commands/Config.cs
@@ -29,7 +29,7 @@ namespace SourceGit.Commands
var rs = new Dictionary();
if (output.IsSuccess)
{
- var lines = output.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+ var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var idx = line.IndexOf('=', StringComparison.Ordinal);
diff --git a/src/Commands/CountLocalChangesWithoutUntracked.cs b/src/Commands/CountLocalChangesWithoutUntracked.cs
index 7ab9a54a..a704f313 100644
--- a/src/Commands/CountLocalChangesWithoutUntracked.cs
+++ b/src/Commands/CountLocalChangesWithoutUntracked.cs
@@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = "--no-optional-locks status -uno --ignore-submodules=dirty --porcelain";
+ Args = "--no-optional-locks status -uno --ignore-submodules=all --porcelain";
}
public int Result()
@@ -16,7 +16,7 @@ namespace SourceGit.Commands
var rs = ReadToEnd();
if (rs.IsSuccess)
{
- var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
return lines.Length;
}
diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs
index 8ae6350f..6af0a3cc 100644
--- a/src/Commands/Diff.cs
+++ b/src/Commands/Diff.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
@@ -28,34 +28,48 @@ namespace SourceGit.Commands
Context = repo;
if (ignoreWhitespace)
- Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --ignore-all-space --unified={unified} {opt}";
+ Args = $"diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}";
+ else if (Models.DiffOption.IgnoreCRAtEOL)
+ Args = $"diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}";
else
- Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}";
+ Args = $"diff --no-ext-diff --patch --unified={unified} {opt}";
}
public Models.DiffResult Result()
{
- Exec();
+ var rs = ReadToEnd();
+ var start = 0;
+ var end = rs.StdOut.IndexOf('\n', start);
+ while (end > 0)
+ {
+ var line = rs.StdOut.Substring(start, end - start);
+ ParseLine(line);
- if (_result.IsBinary || _result.IsLFS)
+ start = end + 1;
+ end = rs.StdOut.IndexOf('\n', start);
+ }
+
+ if (start < rs.StdOut.Length)
+ ParseLine(rs.StdOut.Substring(start));
+
+ if (_result.IsBinary || _result.IsLFS || _result.TextDiff.Lines.Count == 0)
{
_result.TextDiff = null;
}
else
{
ProcessInlineHighlights();
-
- if (_result.TextDiff.Lines.Count == 0)
- _result.TextDiff = null;
- else
- _result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
+ _result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
}
return _result;
}
- protected override void OnReadline(string line)
+ private void ParseLine(string line)
{
+ if (_result.IsBinary)
+ return;
+
if (line.StartsWith("old mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(9);
@@ -80,9 +94,6 @@ namespace SourceGit.Commands
return;
}
- if (_result.IsBinary)
- return;
-
if (_result.IsLFS)
{
var ch = line[0];
@@ -94,7 +105,7 @@ namespace SourceGit.Commands
}
else if (line.StartsWith("-size ", StringComparison.Ordinal))
{
- _result.LFSDiff.Old.Size = long.Parse(line.Substring(6));
+ _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
}
else if (ch == '+')
@@ -105,12 +116,12 @@ namespace SourceGit.Commands
}
else if (line.StartsWith("+size ", StringComparison.Ordinal))
{
- _result.LFSDiff.New.Size = long.Parse(line.Substring(6));
+ _result.LFSDiff.New.Size = long.Parse(line.AsSpan(6));
}
}
else if (line.StartsWith(" size ", StringComparison.Ordinal))
{
- _result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.Substring(6));
+ _result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
return;
}
@@ -140,7 +151,8 @@ namespace SourceGit.Commands
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
- _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
+ _result.TextDiff.Lines.Add(_last);
}
}
else
@@ -148,7 +160,8 @@ namespace SourceGit.Commands
if (line.Length == 0)
{
ProcessInlineHighlights();
- _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine);
+ _result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
return;
@@ -164,7 +177,8 @@ namespace SourceGit.Commands
return;
}
- _deleted.Add(new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0);
+ _deleted.Add(_last);
_oldLine++;
}
else if (ch == '+')
@@ -176,7 +190,8 @@ namespace SourceGit.Commands
return;
}
- _added.Add(new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine);
+ _added.Add(_last);
_newLine++;
}
else if (ch != '\\')
@@ -187,7 +202,8 @@ namespace SourceGit.Commands
{
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
- _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
+ _result.TextDiff.Lines.Add(_last);
}
else
{
@@ -198,11 +214,16 @@ namespace SourceGit.Commands
return;
}
- _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine));
+ _last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine);
+ _result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
}
}
+ else if (line.Equals("\\ No newline at end of file", StringComparison.Ordinal))
+ {
+ _last.NoNewLineEndOfFile = true;
+ }
}
}
@@ -253,6 +274,7 @@ namespace SourceGit.Commands
private readonly Models.DiffResult _result = new Models.DiffResult();
private readonly List _deleted = new List();
private readonly List _added = new List();
+ private Models.TextDiffLine _last = null;
private int _oldLine = 0;
private int _newLine = 0;
}
diff --git a/src/Commands/Discard.cs b/src/Commands/Discard.cs
index a279bb84..f36ca6c9 100644
--- a/src/Commands/Discard.cs
+++ b/src/Commands/Discard.cs
@@ -1,39 +1,95 @@
using System;
using System.Collections.Generic;
+using System.IO;
+
+using Avalonia.Threading;
namespace SourceGit.Commands
{
public static class Discard
{
- public static void All(string repo, bool includeIgnored)
+ ///
+ /// Discard all local changes (unstaged & staged)
+ ///
+ ///
+ ///
+ ///
+ public static void All(string repo, bool includeIgnored, Models.ICommandLog log)
{
- new Restore(repo).Exec();
- new Clean(repo, includeIgnored).Exec();
+ var changes = new QueryLocalChanges(repo).Result();
+ try
+ {
+ foreach (var c in changes)
+ {
+ if (c.WorkTree == Models.ChangeState.Untracked ||
+ c.WorkTree == Models.ChangeState.Added ||
+ c.Index == Models.ChangeState.Added ||
+ c.Index == Models.ChangeState.Renamed)
+ {
+ var fullPath = Path.Combine(repo, c.Path);
+ if (Directory.Exists(fullPath))
+ Directory.Delete(fullPath, true);
+ else
+ File.Delete(fullPath);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
+ });
+ }
+
+ new Reset(repo, "HEAD", "--hard") { Log = log }.Exec();
+
+ if (includeIgnored)
+ new Clean(repo) { Log = log }.Exec();
}
- public static void Changes(string repo, List changes)
+ ///
+ /// Discard selected changes (only unstaged).
+ ///
+ ///
+ ///
+ ///
+ public static void Changes(string repo, List changes, Models.ICommandLog log)
{
- var needClean = new List();
- var needCheckout = new List();
+ var restores = new List();
- foreach (var c in changes)
+ try
{
- if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added)
- needClean.Add(c.Path);
- else
- needCheckout.Add(c.Path);
+ foreach (var c in changes)
+ {
+ if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added)
+ {
+ var fullPath = Path.Combine(repo, c.Path);
+ if (Directory.Exists(fullPath))
+ Directory.Delete(fullPath, true);
+ else
+ File.Delete(fullPath);
+ }
+ else
+ {
+ restores.Add(c.Path);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
+ });
}
- for (int i = 0; i < needClean.Count; i += 10)
+ if (restores.Count > 0)
{
- var count = Math.Min(10, needClean.Count - i);
- new Clean(repo, needClean.GetRange(i, count)).Exec();
- }
-
- for (int i = 0; i < needCheckout.Count; i += 10)
- {
- var count = Math.Min(10, needCheckout.Count - i);
- new Restore(repo, needCheckout.GetRange(i, count), "--worktree --recurse-submodules").Exec();
+ var pathSpecFile = Path.GetTempFileName();
+ File.WriteAllLines(pathSpecFile, restores);
+ new Restore(repo, pathSpecFile, false) { Log = log }.Exec();
+ File.Delete(pathSpecFile);
}
}
}
diff --git a/src/Commands/ExecuteCustomAction.cs b/src/Commands/ExecuteCustomAction.cs
index 000c8fd1..e59bc068 100644
--- a/src/Commands/ExecuteCustomAction.cs
+++ b/src/Commands/ExecuteCustomAction.cs
@@ -27,7 +27,7 @@ namespace SourceGit.Commands
}
}
- public static void RunAndWait(string repo, string file, string args, Action outputHandler)
+ public static void RunAndWait(string repo, string file, string args, Models.ICommandLog log)
{
var start = new ProcessStartInfo();
start.FileName = file;
@@ -40,20 +40,22 @@ namespace SourceGit.Commands
start.StandardErrorEncoding = Encoding.UTF8;
start.WorkingDirectory = repo;
+ log?.AppendLine($"$ {file} {args}\n");
+
var proc = new Process() { StartInfo = start };
var builder = new StringBuilder();
proc.OutputDataReceived += (_, e) =>
{
if (e.Data != null)
- outputHandler?.Invoke(e.Data);
+ log?.AppendLine(e.Data);
};
proc.ErrorDataReceived += (_, e) =>
{
if (e.Data != null)
{
- outputHandler?.Invoke(e.Data);
+ log?.AppendLine(e.Data);
builder.AppendLine(e.Data);
}
};
diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs
index 06ae8cb6..edf2a6dd 100644
--- a/src/Commands/Fetch.cs
+++ b/src/Commands/Fetch.cs
@@ -1,15 +1,11 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Fetch : Command
{
- public Fetch(string repo, string remote, bool noTags, bool force, Action outputHandler)
+ public Fetch(string repo, string remote, bool noTags, bool force)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "fetch --progress --verbose ";
@@ -24,21 +20,12 @@ namespace SourceGit.Commands
Args += remote;
}
- public Fetch(string repo, Models.Branch local, Models.Branch remote, Action outputHandler)
+ public Fetch(string repo, Models.Branch local, Models.Branch remote)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote.Remote}.sshkey");
Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}";
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler;
}
}
diff --git a/src/Commands/FormatPatch.cs b/src/Commands/FormatPatch.cs
index b3ec2e4a..bf850d60 100644
--- a/src/Commands/FormatPatch.cs
+++ b/src/Commands/FormatPatch.cs
@@ -6,6 +6,7 @@
{
WorkingDirectory = repo;
Context = repo;
+ Editor = EditorType.None;
Args = $"format-patch {commit} -1 --output=\"{saveTo}\"";
}
}
diff --git a/src/Commands/GC.cs b/src/Commands/GC.cs
index 393b915e..0b27f487 100644
--- a/src/Commands/GC.cs
+++ b/src/Commands/GC.cs
@@ -1,23 +1,12 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class GC : Command
{
- public GC(string repo, Action outputHandler)
+ public GC(string repo)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
Args = "gc --prune=now";
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler;
}
}
diff --git a/src/Commands/GitFlow.cs b/src/Commands/GitFlow.cs
index 5e05ed83..1d33fa3a 100644
--- a/src/Commands/GitFlow.cs
+++ b/src/Commands/GitFlow.cs
@@ -1,52 +1,12 @@
-using System;
-using System.Collections.Generic;
-
+using System.Text;
using Avalonia.Threading;
namespace SourceGit.Commands
{
public static class GitFlow
{
- public class BranchDetectResult
+ public static bool Init(string repo, string master, string develop, string feature, string release, string hotfix, string version, Models.ICommandLog log)
{
- public bool IsGitFlowBranch { get; set; } = false;
- public string Type { get; set; } = string.Empty;
- public string Prefix { get; set; } = string.Empty;
- }
-
- public static bool IsEnabled(string repo, List branches)
- {
- var localBrancheNames = new HashSet();
- foreach (var branch in branches)
- {
- if (branch.IsLocal)
- localBrancheNames.Add(branch.Name);
- }
-
- var config = new Config(repo).ListAll();
- if (!config.TryGetValue("gitflow.branch.master", out string master) || !localBrancheNames.Contains(master))
- return false;
-
- if (!config.TryGetValue("gitflow.branch.develop", out string develop) || !localBrancheNames.Contains(develop))
- return false;
-
- return config.ContainsKey("gitflow.prefix.feature") &&
- config.ContainsKey("gitflow.prefix.release") &&
- config.ContainsKey("gitflow.prefix.hotfix");
- }
-
- public static bool Init(string repo, List branches, string master, string develop, string feature, string release, string hotfix, string version)
- {
- var current = branches.Find(x => x.IsCurrent);
-
- var masterBranch = branches.Find(x => x.Name == master);
- if (masterBranch == null && current != null)
- Branch.Create(repo, master, current.Head);
-
- var devBranch = branches.Find(x => x.Name == develop);
- if (devBranch == null && current != null)
- Branch.Create(repo, develop, current.Head);
-
var config = new Config(repo);
config.Set("gitflow.branch.master", master);
config.Set("gitflow.branch.develop", develop);
@@ -61,104 +21,72 @@ namespace SourceGit.Commands
init.WorkingDirectory = repo;
init.Context = repo;
init.Args = "flow init -d";
+ init.Log = log;
return init.Exec();
}
- public static string GetPrefix(string repo, string type)
+ public static bool Start(string repo, Models.GitFlowBranchType type, string name, Models.ICommandLog log)
{
- return new Config(repo).Get($"gitflow.prefix.{type}");
- }
-
- public static BranchDetectResult DetectType(string repo, List branches, string branch)
- {
- var rs = new BranchDetectResult();
- var localBrancheNames = new HashSet();
- foreach (var b in branches)
- {
- if (b.IsLocal)
- localBrancheNames.Add(b.Name);
- }
-
- var config = new Config(repo).ListAll();
- if (!config.TryGetValue("gitflow.branch.master", out string master) || !localBrancheNames.Contains(master))
- return rs;
-
- if (!config.TryGetValue("gitflow.branch.develop", out string develop) || !localBrancheNames.Contains(develop))
- return rs;
-
- if (!config.TryGetValue("gitflow.prefix.feature", out var feature) ||
- !config.TryGetValue("gitflow.prefix.release", out var release) ||
- !config.TryGetValue("gitflow.prefix.hotfix", out var hotfix))
- return rs;
-
- if (branch.StartsWith(feature, StringComparison.Ordinal))
- {
- rs.IsGitFlowBranch = true;
- rs.Type = "feature";
- rs.Prefix = feature;
- }
- else if (branch.StartsWith(release, StringComparison.Ordinal))
- {
- rs.IsGitFlowBranch = true;
- rs.Type = "release";
- rs.Prefix = release;
- }
- else if (branch.StartsWith(hotfix, StringComparison.Ordinal))
- {
- rs.IsGitFlowBranch = true;
- rs.Type = "hotfix";
- rs.Prefix = hotfix;
- }
-
- return rs;
- }
-
- public static bool Start(string repo, string type, string name)
- {
- if (!SUPPORTED_BRANCH_TYPES.Contains(type))
- {
- Dispatcher.UIThread.Post(() =>
- {
- App.RaiseException(repo, "Bad branch type!!!");
- });
-
- return false;
- }
-
var start = new Command();
start.WorkingDirectory = repo;
start.Context = repo;
- start.Args = $"flow {type} start {name}";
+
+ switch (type)
+ {
+ case Models.GitFlowBranchType.Feature:
+ start.Args = $"flow feature start {name}";
+ break;
+ case Models.GitFlowBranchType.Release:
+ start.Args = $"flow release start {name}";
+ break;
+ case Models.GitFlowBranchType.Hotfix:
+ start.Args = $"flow hotfix start {name}";
+ break;
+ default:
+ Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, "Bad git-flow branch type!!!"));
+ return false;
+ }
+
+ start.Log = log;
return start.Exec();
}
- public static bool Finish(string repo, string type, string name, bool keepBranch)
+ public static bool Finish(string repo, Models.GitFlowBranchType type, string name, bool squash, bool push, bool keepBranch, Models.ICommandLog log)
{
- if (!SUPPORTED_BRANCH_TYPES.Contains(type))
- {
- Dispatcher.UIThread.Post(() =>
- {
- App.RaiseException(repo, "Bad branch type!!!");
- });
+ var builder = new StringBuilder();
+ builder.Append("flow ");
- return false;
+ switch (type)
+ {
+ case Models.GitFlowBranchType.Feature:
+ builder.Append("feature");
+ break;
+ case Models.GitFlowBranchType.Release:
+ builder.Append("release");
+ break;
+ case Models.GitFlowBranchType.Hotfix:
+ builder.Append("hotfix");
+ break;
+ default:
+ Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, "Bad git-flow branch type!!!"));
+ return false;
}
- var option = keepBranch ? "-k" : string.Empty;
+ builder.Append(" finish ");
+ if (squash)
+ builder.Append("--squash ");
+ if (push)
+ builder.Append("--push ");
+ if (keepBranch)
+ builder.Append("-k ");
+ builder.Append(name);
+
var finish = new Command();
finish.WorkingDirectory = repo;
finish.Context = repo;
- finish.Args = $"flow {type} finish {option} {name}";
+ finish.Args = builder.ToString();
+ finish.Log = log;
return finish.Exec();
}
-
- private static readonly List SUPPORTED_BRANCH_TYPES = new List()
- {
- "feature",
- "release",
- "bugfix",
- "hotfix",
- "support",
- };
}
}
diff --git a/src/Commands/GitIgnore.cs b/src/Commands/GitIgnore.cs
index e666eba6..8b351f5e 100644
--- a/src/Commands/GitIgnore.cs
+++ b/src/Commands/GitIgnore.cs
@@ -8,7 +8,14 @@ namespace SourceGit.Commands
{
var file = Path.Combine(repo, ".gitignore");
if (!File.Exists(file))
+ {
File.WriteAllLines(file, [pattern]);
+ return;
+ }
+
+ var org = File.ReadAllText(file);
+ if (!org.EndsWith('\n'))
+ File.AppendAllLines(file, ["", pattern]);
else
File.AppendAllLines(file, [pattern]);
}
diff --git a/src/Commands/IsBinary.cs b/src/Commands/IsBinary.cs
index de59b5a4..af8f54bb 100644
--- a/src/Commands/IsBinary.cs
+++ b/src/Commands/IsBinary.cs
@@ -11,7 +11,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {commit} --numstat -- \"{path}\"";
+ Args = $"diff {Models.Commit.EmptyTreeSHA1} {commit} --numstat -- \"{path}\"";
RaiseError = false;
}
diff --git a/src/Commands/IsConflictResolved.cs b/src/Commands/IsConflictResolved.cs
index 13bc12ac..9b243451 100644
--- a/src/Commands/IsConflictResolved.cs
+++ b/src/Commands/IsConflictResolved.cs
@@ -10,5 +10,10 @@
Context = repo;
Args = $"diff -a --ignore-cr-at-eol --check {opt}";
}
+
+ public bool Result()
+ {
+ return ReadToEnd().IsSuccess;
+ }
}
}
diff --git a/src/Commands/LFS.cs b/src/Commands/LFS.cs
index 2b7d1de4..18d2ba93 100644
--- a/src/Commands/LFS.cs
+++ b/src/Commands/LFS.cs
@@ -10,23 +10,15 @@ namespace SourceGit.Commands
[GeneratedRegex(@"^(.+)\s+([\w.]+)\s+\w+:(\d+)$")]
private static partial Regex REG_LOCK();
- class SubCmd : Command
+ private class SubCmd : Command
{
- public SubCmd(string repo, string args, Action onProgress)
+ public SubCmd(string repo, string args, Models.ICommandLog log)
{
WorkingDirectory = repo;
Context = repo;
Args = args;
- TraitErrorAsOutput = true;
- _outputHandler = onProgress;
+ Log = log;
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler;
}
public LFS(string repo)
@@ -44,35 +36,35 @@ namespace SourceGit.Commands
return content.Contains("git lfs pre-push");
}
- public bool Install()
+ public bool Install(Models.ICommandLog log)
{
- return new SubCmd(_repo, "lfs install --local", null).Exec();
+ return new SubCmd(_repo, "lfs install --local", log).Exec();
}
- public bool Track(string pattern, bool isFilenameMode = false)
+ public bool Track(string pattern, bool isFilenameMode, Models.ICommandLog log)
{
var opt = isFilenameMode ? "--filename" : "";
- return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", null).Exec();
+ return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", log).Exec();
}
- public void Fetch(string remote, Action outputHandler)
+ public void Fetch(string remote, Models.ICommandLog log)
{
- new SubCmd(_repo, $"lfs fetch {remote}", outputHandler).Exec();
+ new SubCmd(_repo, $"lfs fetch {remote}", log).Exec();
}
- public void Pull(string remote, Action outputHandler)
+ public void Pull(string remote, Models.ICommandLog log)
{
- new SubCmd(_repo, $"lfs pull {remote}", outputHandler).Exec();
+ new SubCmd(_repo, $"lfs pull {remote}", log).Exec();
}
- public void Push(string remote, Action outputHandler)
+ public void Push(string remote, Models.ICommandLog log)
{
- new SubCmd(_repo, $"lfs push {remote}", outputHandler).Exec();
+ new SubCmd(_repo, $"lfs push {remote}", log).Exec();
}
- public void Prune(Action outputHandler)
+ public void Prune(Models.ICommandLog log)
{
- new SubCmd(_repo, "lfs prune", outputHandler).Exec();
+ new SubCmd(_repo, "lfs prune", log).Exec();
}
public List Locks(string remote)
@@ -82,7 +74,7 @@ namespace SourceGit.Commands
var rs = cmd.ReadToEnd();
if (rs.IsSuccess)
{
- var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_LOCK().Match(line);
@@ -101,21 +93,21 @@ namespace SourceGit.Commands
return locks;
}
- public bool Lock(string remote, string file)
+ public bool Lock(string remote, string file, Models.ICommandLog log)
{
- return new SubCmd(_repo, $"lfs lock --remote={remote} \"{file}\"", null).Exec();
+ return new SubCmd(_repo, $"lfs lock --remote={remote} \"{file}\"", log).Exec();
}
- public bool Unlock(string remote, string file, bool force)
+ public bool Unlock(string remote, string file, bool force, Models.ICommandLog log)
{
var opt = force ? "-f" : "";
- return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} \"{file}\"", null).Exec();
+ return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} \"{file}\"", log).Exec();
}
- public bool Unlock(string remote, long id, bool force)
+ public bool Unlock(string remote, long id, bool force, Models.ICommandLog log)
{
var opt = force ? "-f" : "";
- return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} --id={id}", null).Exec();
+ return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} --id={id}", log).Exec();
}
private readonly string _repo;
diff --git a/src/Commands/Merge.cs b/src/Commands/Merge.cs
index bd1f3653..32898593 100644
--- a/src/Commands/Merge.cs
+++ b/src/Commands/Merge.cs
@@ -1,26 +1,30 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
{
public class Merge : Command
{
- public Merge(string repo, string source, string mode, Action outputHandler)
+ public Merge(string repo, string source, string mode, bool edit)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
- Args = $"merge --progress {source} {mode}";
+ Editor = EditorType.CoreEditor;
+
+ var builder = new StringBuilder();
+ builder.Append("merge --progress ");
+ builder.Append(edit ? "--edit " : "--no-edit ");
+ builder.Append(source);
+ builder.Append(' ');
+ builder.Append(mode);
+
+ Args = builder.ToString();
}
- public Merge(string repo, List targets, bool autoCommit, string strategy, Action outputHandler)
+ public Merge(string repo, List targets, bool autoCommit, string strategy)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
var builder = new StringBuilder();
builder.Append("merge --progress ");
@@ -37,12 +41,5 @@ namespace SourceGit.Commands
Args = builder.ToString();
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler = null;
}
}
diff --git a/src/Commands/MergeTool.cs b/src/Commands/MergeTool.cs
index d3478d59..fc6d0d75 100644
--- a/src/Commands/MergeTool.cs
+++ b/src/Commands/MergeTool.cs
@@ -13,15 +13,18 @@ namespace SourceGit.Commands
cmd.Context = repo;
cmd.RaiseError = true;
+ // NOTE: If no names are specified, 'git mergetool' will run the merge tool program on every file with merge conflicts.
+ var fileArg = string.IsNullOrEmpty(file) ? "" : $"\"{file}\"";
+
if (toolType == 0)
{
- cmd.Args = $"mergetool \"{file}\"";
+ cmd.Args = $"mergetool {fileArg}";
return cmd.Exec();
}
if (!File.Exists(toolPath))
{
- Dispatcher.UIThread.Post(() => App.RaiseException(repo, $"Can NOT found external merge tool in '{toolPath}'!"));
+ Dispatcher.UIThread.Post(() => App.RaiseException(repo, $"Can NOT find external merge tool in '{toolPath}'!"));
return false;
}
@@ -32,7 +35,7 @@ namespace SourceGit.Commands
return false;
}
- cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.Cmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit \"{file}\"";
+ cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.Cmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit {fileArg}";
return cmd.Exec();
}
@@ -51,7 +54,7 @@ namespace SourceGit.Commands
if (!File.Exists(toolPath))
{
- Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, $"Can NOT found external diff tool in '{toolPath}'!"));
+ Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, $"Can NOT find external diff tool in '{toolPath}'!"));
return false;
}
diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs
index 35a6289a..698fbfce 100644
--- a/src/Commands/Pull.cs
+++ b/src/Commands/Pull.cs
@@ -1,32 +1,18 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Pull : Command
{
- public Pull(string repo, string remote, string branch, bool useRebase, bool noTags, Action outputHandler)
+ public Pull(string repo, string remote, string branch, bool useRebase)
{
- _outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "pull --verbose --progress ";
if (useRebase)
Args += "--rebase=true ";
- if (noTags)
- Args += "--no-tags ";
-
Args += $"{remote} {branch}";
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler;
}
}
diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs
index dc81f606..8a5fe33c 100644
--- a/src/Commands/Push.cs
+++ b/src/Commands/Push.cs
@@ -1,16 +1,11 @@
-using System;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Push : Command
{
- public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, Action onProgress)
+ public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force)
{
- _outputHandler = onProgress;
-
WorkingDirectory = repo;
Context = repo;
- TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "push --progress --verbose ";
@@ -38,12 +33,5 @@ namespace SourceGit.Commands
Args += $"{remote} {refname}";
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private readonly Action _outputHandler = null;
}
}
diff --git a/src/Commands/QueryAssumeUnchangedFiles.cs b/src/Commands/QueryAssumeUnchangedFiles.cs
new file mode 100644
index 00000000..b5c23b0b
--- /dev/null
+++ b/src/Commands/QueryAssumeUnchangedFiles.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace SourceGit.Commands
+{
+ public partial class QueryAssumeUnchangedFiles : Command
+ {
+ [GeneratedRegex(@"^(\w)\s+(.+)$")]
+ private static partial Regex REG_PARSE();
+
+ public QueryAssumeUnchangedFiles(string repo)
+ {
+ WorkingDirectory = repo;
+ Args = "ls-files -v";
+ RaiseError = false;
+ }
+
+ public List Result()
+ {
+ var outs = new List();
+ var rs = ReadToEnd();
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ var match = REG_PARSE().Match(line);
+ if (!match.Success)
+ continue;
+
+ if (match.Groups[1].Value == "h")
+ outs.Add(match.Groups[2].Value);
+ }
+
+ return outs;
+ }
+ }
+}
diff --git a/src/Commands/QueryBranches.cs b/src/Commands/QueryBranches.cs
index 44438cef..f268d709 100644
--- a/src/Commands/QueryBranches.cs
+++ b/src/Commands/QueryBranches.cs
@@ -14,18 +14,20 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = "branch -l --all -v --format=\"%(refname)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
+ Args = "branch -l --all -v --format=\"%(refname)%00%(committerdate:unix)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
}
- public List Result()
+ public List Result(out int localBranchesCount)
{
+ localBranchesCount = 0;
+
var branches = new List();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return branches;
- var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
- var remoteBranches = new HashSet();
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ var remoteHeads = new Dictionary();
foreach (var line in lines)
{
var b = ParseLine(line);
@@ -33,14 +35,27 @@ namespace SourceGit.Commands
{
branches.Add(b);
if (!b.IsLocal)
- remoteBranches.Add(b.FullName);
+ remoteHeads.Add(b.FullName, b.Head);
+ else
+ localBranchesCount++;
}
}
foreach (var b in branches)
{
if (b.IsLocal && !string.IsNullOrEmpty(b.Upstream))
- b.IsUpsteamGone = !remoteBranches.Contains(b.Upstream);
+ {
+ if (remoteHeads.TryGetValue(b.Upstream, out var upstreamHead))
+ {
+ b.IsUpstreamGone = false;
+ b.TrackStatus ??= new QueryTrackStatus(WorkingDirectory, b.Head, upstreamHead).Result();
+ }
+ else
+ {
+ b.IsUpstreamGone = true;
+ b.TrackStatus ??= new Models.BranchTrackStatus();
+ }
+ }
}
return branches;
@@ -49,7 +64,7 @@ namespace SourceGit.Commands
private Models.Branch ParseLine(string line)
{
var parts = line.Split('\0');
- if (parts.Length != 5)
+ if (parts.Length != 6)
return null;
var branch = new Models.Branch();
@@ -83,14 +98,16 @@ namespace SourceGit.Commands
}
branch.FullName = refName;
- branch.Head = parts[1];
- branch.IsCurrent = parts[2] == "*";
- branch.Upstream = parts[3];
- branch.IsUpsteamGone = false;
+ branch.CommitterDate = ulong.Parse(parts[1]);
+ branch.Head = parts[2];
+ branch.IsCurrent = parts[3] == "*";
+ branch.Upstream = parts[4];
+ branch.IsUpstreamGone = false;
- if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal))
- branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result();
- else
+ if (!branch.IsLocal ||
+ string.IsNullOrEmpty(branch.Upstream) ||
+ string.IsNullOrEmpty(parts[5]) ||
+ parts[5].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new Models.BranchTrackStatus();
return branch;
diff --git a/src/Commands/QueryCommitChildren.cs b/src/Commands/QueryCommitChildren.cs
index 6a6ed909..4e99ce7a 100644
--- a/src/Commands/QueryCommitChildren.cs
+++ b/src/Commands/QueryCommitChildren.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace SourceGit.Commands
{
@@ -9,22 +10,26 @@ namespace SourceGit.Commands
WorkingDirectory = repo;
Context = repo;
_commit = commit;
- Args = $"rev-list -{max} --parents --branches --remotes --ancestry-path={commit} ^{commit}";
+ Args = $"rev-list -{max} --parents --branches --remotes --ancestry-path ^{commit}";
}
public List Result()
{
- Exec();
- return _lines;
- }
+ var rs = ReadToEnd();
+ var outs = new List();
+ if (rs.IsSuccess)
+ {
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ if (line.Contains(_commit))
+ outs.Add(line.Substring(0, 40));
+ }
+ }
- protected override void OnReadline(string line)
- {
- if (line.Contains(_commit))
- _lines.Add(line.Substring(0, 40));
+ return outs;
}
private string _commit;
- private List _lines = new List();
}
}
diff --git a/src/Commands/QueryCommitFullMessage.cs b/src/Commands/QueryCommitFullMessage.cs
index c8f1867d..36b6d1c7 100644
--- a/src/Commands/QueryCommitFullMessage.cs
+++ b/src/Commands/QueryCommitFullMessage.cs
@@ -6,7 +6,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"show --no-show-signature --pretty=format:%B -s {sha}";
+ Args = $"show --no-show-signature --format=%B -s {sha}";
}
public string Result()
diff --git a/src/Commands/QueryCommitSignInfo.cs b/src/Commands/QueryCommitSignInfo.cs
index 5c81cf57..133949af 100644
--- a/src/Commands/QueryCommitSignInfo.cs
+++ b/src/Commands/QueryCommitSignInfo.cs
@@ -7,7 +7,7 @@
WorkingDirectory = repo;
Context = repo;
- const string baseArgs = "show --no-show-signature --pretty=format:\"%G?%n%GS%n%GK\" -s";
+ const string baseArgs = "show --no-show-signature --format=%G?%n%GS%n%GK -s";
const string fakeSignersFileArg = "-c gpg.ssh.allowedSignersFile=/dev/null";
Args = $"{(useFakeSignersFile ? fakeSignersFileArg : string.Empty)} {baseArgs} {sha}";
}
@@ -18,7 +18,7 @@
if (!rs.IsSuccess)
return null;
- var raw = rs.StdOut.Trim();
+ var raw = rs.StdOut.Trim().ReplaceLineEndings("\n");
if (raw.Length <= 1)
return null;
@@ -29,7 +29,6 @@
Signer = lines[1],
Key = lines[2]
};
-
}
}
}
diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs
index 312c068f..9e1d9918 100644
--- a/src/Commands/QueryCommits.cs
+++ b/src/Commands/QueryCommits.cs
@@ -10,7 +10,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"log --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {limits}";
+ Args = $"log --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {limits}";
_findFirstMerged = needFindHead;
}
@@ -26,16 +26,12 @@ namespace SourceGit.Commands
{
search += $"-i --committer=\"{filter}\"";
}
- else if (method == Models.CommitSearchMethod.ByFile)
- {
- search += $"-- \"{filter}\"";
- }
- else
+ else if (method == Models.CommitSearchMethod.ByMessage)
{
var argsBuilder = new StringBuilder();
argsBuilder.Append(search);
- var words = filter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ var words = filter.Split([' ', '\t', '\r'], StringSplitOptions.RemoveEmptyEntries);
foreach (var word in words)
{
var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal);
@@ -45,10 +41,18 @@ namespace SourceGit.Commands
search = argsBuilder.ToString();
}
+ else if (method == Models.CommitSearchMethod.ByFile)
+ {
+ search += $"-- \"{filter}\"";
+ }
+ else
+ {
+ search = $"-G\"{filter}\"";
+ }
WorkingDirectory = repo;
Context = repo;
- Args = $"log -1000 --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%s " + search;
+ Args = $"log -1000 --date-order --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {search}";
_findFirstMerged = false;
}
@@ -124,7 +128,7 @@ namespace SourceGit.Commands
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
var rs = ReadToEnd();
- var shas = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+ var shas = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (shas.Length == 0)
return;
diff --git a/src/Commands/QueryCommitsForInteractiveRebase.cs b/src/Commands/QueryCommitsForInteractiveRebase.cs
index 232d86e5..9f238319 100644
--- a/src/Commands/QueryCommitsForInteractiveRebase.cs
+++ b/src/Commands/QueryCommitsForInteractiveRebase.cs
@@ -11,7 +11,7 @@ namespace SourceGit.Commands
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}\" {on}..HEAD";
+ Args = $"log --date-order --no-show-signature --decorate=full --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()
@@ -90,6 +90,6 @@ namespace SourceGit.Commands
private List _commits = [];
private Models.InteractiveCommit _current = null;
- private string _boundary = "";
+ private readonly string _boundary;
}
}
diff --git a/src/Commands/QueryFileContent.cs b/src/Commands/QueryFileContent.cs
index f887859c..83d0a575 100644
--- a/src/Commands/QueryFileContent.cs
+++ b/src/Commands/QueryFileContent.cs
@@ -35,5 +35,39 @@ namespace SourceGit.Commands
return stream;
}
+
+ public static Stream FromLFS(string repo, string oid, long size)
+ {
+ var starter = new ProcessStartInfo();
+ starter.WorkingDirectory = repo;
+ starter.FileName = Native.OS.GitExecutable;
+ starter.Arguments = $"lfs smudge";
+ starter.UseShellExecute = false;
+ starter.CreateNoWindow = true;
+ starter.WindowStyle = ProcessWindowStyle.Hidden;
+ starter.RedirectStandardInput = true;
+ starter.RedirectStandardOutput = true;
+
+ var stream = new MemoryStream();
+ try
+ {
+ var proc = new Process() { StartInfo = starter };
+ proc.Start();
+ proc.StandardInput.WriteLine("version https://git-lfs.github.com/spec/v1");
+ proc.StandardInput.WriteLine($"oid sha256:{oid}");
+ proc.StandardInput.WriteLine($"size {size}");
+ proc.StandardOutput.BaseStream.CopyTo(stream);
+ proc.WaitForExit();
+ proc.Close();
+
+ stream.Position = 0;
+ }
+ catch (Exception e)
+ {
+ App.RaiseException(repo, $"Failed to query file content: {e}");
+ }
+
+ return stream;
+ }
}
}
diff --git a/src/Commands/QueryFileSize.cs b/src/Commands/QueryFileSize.cs
index 9016d826..30af7715 100644
--- a/src/Commands/QueryFileSize.cs
+++ b/src/Commands/QueryFileSize.cs
@@ -16,9 +16,6 @@ namespace SourceGit.Commands
public long Result()
{
- if (_result != 0)
- return _result;
-
var rs = ReadToEnd();
if (rs.IsSuccess)
{
@@ -29,7 +26,5 @@ namespace SourceGit.Commands
return 0;
}
-
- private readonly long _result = 0;
}
}
diff --git a/src/Commands/QueryGitCommonDir.cs b/src/Commands/QueryGitCommonDir.cs
new file mode 100644
index 00000000..1076243e
--- /dev/null
+++ b/src/Commands/QueryGitCommonDir.cs
@@ -0,0 +1,26 @@
+using System.IO;
+
+namespace SourceGit.Commands
+{
+ public class QueryGitCommonDir : Command
+ {
+ public QueryGitCommonDir(string workDir)
+ {
+ WorkingDirectory = workDir;
+ Args = "rev-parse --git-common-dir";
+ RaiseError = false;
+ }
+
+ public string Result()
+ {
+ var rs = ReadToEnd().StdOut;
+ if (string.IsNullOrEmpty(rs))
+ return null;
+
+ rs = rs.Trim();
+ if (Path.IsPathRooted(rs))
+ return rs;
+ return Path.GetFullPath(Path.Combine(WorkingDirectory, rs));
+ }
+ }
+}
diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs
index 9458e5f5..788ed617 100644
--- a/src/Commands/QueryLocalChanges.cs
+++ b/src/Commands/QueryLocalChanges.cs
@@ -1,6 +1,9 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Text.RegularExpressions;
+using Avalonia.Threading;
+
namespace SourceGit.Commands
{
public partial class QueryLocalChanges : Command
@@ -18,139 +21,145 @@ namespace SourceGit.Commands
public List Result()
{
- Exec();
- return _changes;
- }
-
- protected override void OnReadline(string line)
- {
- var match = REG_FORMAT().Match(line);
- if (!match.Success)
- return;
-
- var change = new Models.Change() { Path = match.Groups[2].Value };
- var status = match.Groups[1].Value;
-
- switch (status)
+ var outs = new List();
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
{
- case " M":
- change.Set(Models.ChangeState.None, Models.ChangeState.Modified);
- break;
- case " T":
- change.Set(Models.ChangeState.None, Models.ChangeState.TypeChanged);
- break;
- case " A":
- change.Set(Models.ChangeState.None, Models.ChangeState.Added);
- break;
- case " D":
- change.Set(Models.ChangeState.None, Models.ChangeState.Deleted);
- break;
- case " R":
- change.Set(Models.ChangeState.None, Models.ChangeState.Renamed);
- break;
- case " C":
- change.Set(Models.ChangeState.None, Models.ChangeState.Copied);
- break;
- case "M":
- change.Set(Models.ChangeState.Modified);
- break;
- case "MM":
- change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified);
- break;
- case "MT":
- change.Set(Models.ChangeState.Modified, Models.ChangeState.TypeChanged);
- break;
- case "MD":
- change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted);
- break;
- case "T":
- change.Set(Models.ChangeState.TypeChanged);
- break;
- case "TM":
- change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Modified);
- break;
- case "TT":
- change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.TypeChanged);
- break;
- case "TD":
- change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Deleted);
- break;
- case "A":
- change.Set(Models.ChangeState.Added);
- break;
- case "AM":
- change.Set(Models.ChangeState.Added, Models.ChangeState.Modified);
- break;
- case "AT":
- change.Set(Models.ChangeState.Added, Models.ChangeState.TypeChanged);
- break;
- case "AD":
- change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted);
- break;
- case "D":
- change.Set(Models.ChangeState.Deleted);
- break;
- case "R":
- change.Set(Models.ChangeState.Renamed);
- break;
- case "RM":
- change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified);
- break;
- case "RT":
- change.Set(Models.ChangeState.Renamed, Models.ChangeState.TypeChanged);
- break;
- case "RD":
- change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted);
- break;
- case "C":
- change.Set(Models.ChangeState.Copied);
- break;
- case "CM":
- change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified);
- break;
- case "CT":
- change.Set(Models.ChangeState.Copied, Models.ChangeState.TypeChanged);
- break;
- case "CD":
- change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted);
- break;
- case "DR":
- change.Set(Models.ChangeState.Deleted, Models.ChangeState.Renamed);
- break;
- case "DC":
- change.Set(Models.ChangeState.Deleted, Models.ChangeState.Copied);
- break;
- case "DD":
- change.Set(Models.ChangeState.Deleted, Models.ChangeState.Deleted);
- break;
- case "AU":
- change.Set(Models.ChangeState.Added, Models.ChangeState.Unmerged);
- break;
- case "UD":
- change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Deleted);
- break;
- case "UA":
- change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Added);
- break;
- case "DU":
- change.Set(Models.ChangeState.Deleted, Models.ChangeState.Unmerged);
- break;
- case "AA":
- change.Set(Models.ChangeState.Added, Models.ChangeState.Added);
- break;
- case "UU":
- change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Unmerged);
- break;
- case "??":
- change.Set(Models.ChangeState.Untracked, Models.ChangeState.Untracked);
- break;
- default:
- return;
+ Dispatcher.UIThread.Post(() => App.RaiseException(Context, rs.StdErr));
+ return outs;
}
- _changes.Add(change);
- }
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ var match = REG_FORMAT().Match(line);
+ if (!match.Success)
+ continue;
- private readonly List _changes = new List();
+ var change = new Models.Change() { Path = match.Groups[2].Value };
+ var status = match.Groups[1].Value;
+
+ switch (status)
+ {
+ case " M":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Modified);
+ break;
+ case " T":
+ change.Set(Models.ChangeState.None, Models.ChangeState.TypeChanged);
+ break;
+ case " A":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Added);
+ break;
+ case " D":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Deleted);
+ break;
+ case " R":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Renamed);
+ break;
+ case " C":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Copied);
+ break;
+ case "M":
+ change.Set(Models.ChangeState.Modified);
+ break;
+ case "MM":
+ change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified);
+ break;
+ case "MT":
+ change.Set(Models.ChangeState.Modified, Models.ChangeState.TypeChanged);
+ break;
+ case "MD":
+ change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted);
+ break;
+ case "T":
+ change.Set(Models.ChangeState.TypeChanged);
+ break;
+ case "TM":
+ change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Modified);
+ break;
+ case "TT":
+ change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.TypeChanged);
+ break;
+ case "TD":
+ change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Deleted);
+ break;
+ case "A":
+ change.Set(Models.ChangeState.Added);
+ break;
+ case "AM":
+ change.Set(Models.ChangeState.Added, Models.ChangeState.Modified);
+ break;
+ case "AT":
+ change.Set(Models.ChangeState.Added, Models.ChangeState.TypeChanged);
+ break;
+ case "AD":
+ change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted);
+ break;
+ case "D":
+ change.Set(Models.ChangeState.Deleted);
+ break;
+ case "R":
+ change.Set(Models.ChangeState.Renamed);
+ break;
+ case "RM":
+ change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified);
+ break;
+ case "RT":
+ change.Set(Models.ChangeState.Renamed, Models.ChangeState.TypeChanged);
+ break;
+ case "RD":
+ change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted);
+ break;
+ case "C":
+ change.Set(Models.ChangeState.Copied);
+ break;
+ case "CM":
+ change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified);
+ break;
+ case "CT":
+ change.Set(Models.ChangeState.Copied, Models.ChangeState.TypeChanged);
+ break;
+ case "CD":
+ change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted);
+ break;
+ case "DD":
+ change.ConflictReason = Models.ConflictReason.BothDeleted;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "AU":
+ change.ConflictReason = Models.ConflictReason.AddedByUs;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "UD":
+ change.ConflictReason = Models.ConflictReason.DeletedByThem;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "UA":
+ change.ConflictReason = Models.ConflictReason.AddedByThem;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "DU":
+ change.ConflictReason = Models.ConflictReason.DeletedByUs;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "AA":
+ change.ConflictReason = Models.ConflictReason.BothAdded;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "UU":
+ change.ConflictReason = Models.ConflictReason.BothModified;
+ change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
+ break;
+ case "??":
+ change.Set(Models.ChangeState.None, Models.ChangeState.Untracked);
+ break;
+ }
+
+ if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None)
+ outs.Add(change);
+ }
+
+ return outs;
+ }
}
}
diff --git a/src/Commands/QueryRefsContainsCommit.cs b/src/Commands/QueryRefsContainsCommit.cs
index 82e9b341..cabe1b50 100644
--- a/src/Commands/QueryRefsContainsCommit.cs
+++ b/src/Commands/QueryRefsContainsCommit.cs
@@ -20,7 +20,7 @@ namespace SourceGit.Commands
if (!output.IsSuccess)
return rs;
- var lines = output.StdOut.Split('\n');
+ var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.EndsWith("/HEAD", StringComparison.Ordinal))
diff --git a/src/Commands/QueryRemotes.cs b/src/Commands/QueryRemotes.cs
index b5b41b4a..7afec74d 100644
--- a/src/Commands/QueryRemotes.cs
+++ b/src/Commands/QueryRemotes.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
@@ -17,27 +18,31 @@ namespace SourceGit.Commands
public List Result()
{
- Exec();
- return _loaded;
- }
+ var outs = new List();
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
+ return outs;
- protected override void OnReadline(string line)
- {
- var match = REG_REMOTE().Match(line);
- if (!match.Success)
- return;
-
- var remote = new Models.Remote()
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
{
- Name = match.Groups[1].Value,
- URL = match.Groups[2].Value,
- };
+ var match = REG_REMOTE().Match(line);
+ if (!match.Success)
+ continue;
- if (_loaded.Find(x => x.Name == remote.Name) != null)
- return;
- _loaded.Add(remote);
+ var remote = new Models.Remote()
+ {
+ Name = match.Groups[1].Value,
+ URL = match.Groups[2].Value,
+ };
+
+ if (outs.Find(x => x.Name == remote.Name) != null)
+ continue;
+
+ outs.Add(remote);
+ }
+
+ return outs;
}
-
- private readonly List _loaded = new List();
}
}
diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs
index 1e1c9ea4..35289ec5 100644
--- a/src/Commands/QuerySingleCommit.cs
+++ b/src/Commands/QuerySingleCommit.cs
@@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"show --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s -s {sha}";
+ Args = $"show --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s -s {sha}";
}
public Models.Commit Result()
diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs
index f5c9659f..78980401 100644
--- a/src/Commands/QueryStagedChangesWithAmend.cs
+++ b/src/Commands/QueryStagedChangesWithAmend.cs
@@ -6,87 +6,87 @@ namespace SourceGit.Commands
{
public partial class QueryStagedChangesWithAmend : Command
{
- [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMTUX])\d{0,6}\t(.*)$")]
+ [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMT])\d{0,6}\t(.*)$")]
private static partial Regex REG_FORMAT1();
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} R\d{0,6}\t(.*\t.*)$")]
private static partial Regex REG_FORMAT2();
- public QueryStagedChangesWithAmend(string repo)
+ public QueryStagedChangesWithAmend(string repo, string parent)
{
WorkingDirectory = repo;
Context = repo;
- Args = "diff-index --cached -M HEAD^";
+ Args = $"diff-index --cached -M {parent}";
+ _parent = parent;
}
public List Result()
{
var rs = ReadToEnd();
- if (rs.IsSuccess)
+ if (!rs.IsSuccess)
+ return [];
+
+ var changes = new List();
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
{
- var changes = new List();
- var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
- foreach (var line in lines)
+ var match = REG_FORMAT2().Match(line);
+ if (match.Success)
{
- var match = REG_FORMAT2().Match(line);
- if (match.Success)
+ var change = new Models.Change()
{
- var change = new Models.Change()
+ Path = match.Groups[3].Value,
+ DataForAmend = new Models.ChangeDataForAmend()
{
- Path = match.Groups[3].Value,
- DataForAmend = new Models.ChangeDataForAmend()
- {
- FileMode = match.Groups[1].Value,
- ObjectHash = match.Groups[2].Value,
- },
- };
- change.Set(Models.ChangeState.Renamed);
- changes.Add(change);
- continue;
- }
-
- match = REG_FORMAT1().Match(line);
- if (match.Success)
- {
- var change = new Models.Change()
- {
- Path = match.Groups[4].Value,
- DataForAmend = new Models.ChangeDataForAmend()
- {
- FileMode = match.Groups[1].Value,
- ObjectHash = match.Groups[2].Value,
- },
- };
-
- var type = match.Groups[3].Value;
- switch (type)
- {
- case "A":
- change.Set(Models.ChangeState.Added);
- break;
- case "C":
- change.Set(Models.ChangeState.Copied);
- break;
- case "D":
- change.Set(Models.ChangeState.Deleted);
- break;
- case "M":
- change.Set(Models.ChangeState.Modified);
- break;
- case "T":
- change.Set(Models.ChangeState.TypeChanged);
- break;
- case "U":
- change.Set(Models.ChangeState.Unmerged);
- break;
- }
- changes.Add(change);
- }
+ FileMode = match.Groups[1].Value,
+ ObjectHash = match.Groups[2].Value,
+ ParentSHA = _parent,
+ },
+ };
+ change.Set(Models.ChangeState.Renamed);
+ changes.Add(change);
+ continue;
}
- return changes;
+ match = REG_FORMAT1().Match(line);
+ if (match.Success)
+ {
+ var change = new Models.Change()
+ {
+ Path = match.Groups[4].Value,
+ DataForAmend = new Models.ChangeDataForAmend()
+ {
+ FileMode = match.Groups[1].Value,
+ ObjectHash = match.Groups[2].Value,
+ ParentSHA = _parent,
+ },
+ };
+
+ var type = match.Groups[3].Value;
+ switch (type)
+ {
+ case "A":
+ change.Set(Models.ChangeState.Added);
+ break;
+ case "C":
+ change.Set(Models.ChangeState.Copied);
+ break;
+ case "D":
+ change.Set(Models.ChangeState.Deleted);
+ break;
+ case "M":
+ change.Set(Models.ChangeState.Modified);
+ break;
+ case "T":
+ change.Set(Models.ChangeState.TypeChanged);
+ break;
+ }
+ changes.Add(change);
+ }
}
- return [];
+ return changes;
}
+
+ private readonly string _parent;
}
}
diff --git a/src/Commands/QueryStashes.cs b/src/Commands/QueryStashes.cs
index ccf601c5..2b8987b6 100644
--- a/src/Commands/QueryStashes.cs
+++ b/src/Commands/QueryStashes.cs
@@ -9,52 +9,60 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = "stash list --pretty=format:%H%n%P%n%ct%n%gd%n%s";
+ Args = $"stash list -z --no-show-signature --format=\"%H%n%P%n%ct%n%gd%n%B\"";
}
public List Result()
{
- Exec();
- return _stashes;
- }
+ var outs = new List();
+ var rs = ReadToEnd();
+ if (!rs.IsSuccess)
+ return outs;
- protected override void OnReadline(string line)
- {
- switch (_nextLineIdx)
+ var items = rs.StdOut.Split('\0', StringSplitOptions.RemoveEmptyEntries);
+ foreach (var item in items)
{
- case 0:
- _current = new Models.Stash() { SHA = line };
- _stashes.Add(_current);
- break;
- case 1:
- ParseParent(line);
- break;
- case 2:
- _current.Time = ulong.Parse(line);
- break;
- case 3:
- _current.Name = line;
- break;
- case 4:
- _current.Message = line;
- break;
+ var current = new Models.Stash();
+
+ var nextPartIdx = 0;
+ var start = 0;
+ var end = item.IndexOf('\n', start);
+ while (end > 0 && nextPartIdx < 4)
+ {
+ var line = item.Substring(start, end - start);
+
+ switch (nextPartIdx)
+ {
+ case 0:
+ current.SHA = line;
+ break;
+ case 1:
+ if (line.Length > 6)
+ current.Parents.AddRange(line.Split(' ', StringSplitOptions.RemoveEmptyEntries));
+ break;
+ case 2:
+ current.Time = ulong.Parse(line);
+ break;
+ case 3:
+ current.Name = line;
+ break;
+ }
+
+ nextPartIdx++;
+
+ start = end + 1;
+ if (start >= item.Length - 1)
+ break;
+
+ end = item.IndexOf('\n', start);
+ }
+
+ if (start < item.Length)
+ current.Message = item.Substring(start);
+
+ outs.Add(current);
}
-
- _nextLineIdx++;
- if (_nextLineIdx > 4)
- _nextLineIdx = 0;
+ return outs;
}
-
- private void ParseParent(string data)
- {
- if (data.Length < 8)
- return;
-
- _current.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries));
- }
-
- private readonly List _stashes = new List();
- private Models.Stash _current = null;
- private int _nextLineIdx = 0;
}
}
diff --git a/src/Commands/QuerySubmodules.cs b/src/Commands/QuerySubmodules.cs
index 1ceccf78..663c0ea0 100644
--- a/src/Commands/QuerySubmodules.cs
+++ b/src/Commands/QuerySubmodules.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
@@ -6,12 +7,12 @@ namespace SourceGit.Commands
{
public partial class QuerySubmodules : Command
{
- [GeneratedRegex(@"^[\-\+ ][0-9a-f]+\s(.*)\s\(.*\)$")]
- private static partial Regex REG_FORMAT1();
- [GeneratedRegex(@"^[\-\+ ][0-9a-f]+\s(.*)$")]
- private static partial Regex REG_FORMAT2();
- [GeneratedRegex(@"^\s?[\w\?]{1,4}\s+(.+)$")]
+ [GeneratedRegex(@"^([U\-\+ ])([0-9a-f]+)\s(.*?)(\s\(.*\))?$")]
private static partial Regex REG_FORMAT_STATUS();
+ [GeneratedRegex(@"^\s?[\w\?]{1,4}\s+(.+)$")]
+ private static partial Regex REG_FORMAT_DIRTY();
+ [GeneratedRegex(@"^submodule\.(\S*)\.(\w+)=(.*)$")]
+ private static partial Regex REG_FORMAT_MODULE_INFO();
public QuerySubmodules(string repo)
{
@@ -25,52 +26,117 @@ namespace SourceGit.Commands
var submodules = new List();
var rs = ReadToEnd();
- var builder = new StringBuilder();
- var lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ var map = new Dictionary();
+ var needCheckLocalChanges = false;
foreach (var line in lines)
{
- var match = REG_FORMAT1().Match(line);
+ var match = REG_FORMAT_STATUS().Match(line);
if (match.Success)
{
- var path = match.Groups[1].Value;
- builder.Append($"\"{path}\" ");
- submodules.Add(new Models.Submodule() { Path = path });
- continue;
- }
+ var stat = match.Groups[1].Value;
+ var sha = match.Groups[2].Value;
+ var path = match.Groups[3].Value;
- match = REG_FORMAT2().Match(line);
- if (match.Success)
- {
- var path = match.Groups[1].Value;
- builder.Append($"\"{path}\" ");
- submodules.Add(new Models.Submodule() { Path = path });
+ var module = new Models.Submodule() { Path = path, SHA = sha };
+ switch (stat[0])
+ {
+ case '-':
+ module.Status = Models.SubmoduleStatus.NotInited;
+ break;
+ case '+':
+ module.Status = Models.SubmoduleStatus.RevisionChanged;
+ break;
+ case 'U':
+ module.Status = Models.SubmoduleStatus.Unmerged;
+ break;
+ default:
+ module.Status = Models.SubmoduleStatus.Normal;
+ needCheckLocalChanges = true;
+ break;
+ }
+
+ map.Add(path, module);
+ submodules.Add(module);
}
}
if (submodules.Count > 0)
{
- Args = $"--no-optional-locks status -uno --porcelain -- {builder}";
+ Args = "config --file .gitmodules --list";
+ rs = ReadToEnd();
+ if (rs.IsSuccess)
+ {
+ var modules = new Dictionary();
+ lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ var match = REG_FORMAT_MODULE_INFO().Match(line);
+ if (match.Success)
+ {
+ var name = match.Groups[1].Value;
+ var key = match.Groups[2].Value;
+ var val = match.Groups[3].Value;
+
+ if (!modules.TryGetValue(name, out var m))
+ {
+ m = new ModuleInfo();
+ modules.Add(name, m);
+ }
+
+ if (key.Equals("path", StringComparison.Ordinal))
+ m.Path = val;
+ else if (key.Equals("url", StringComparison.Ordinal))
+ m.URL = val;
+ }
+ }
+
+ foreach (var kv in modules)
+ {
+ if (map.TryGetValue(kv.Value.Path, out var m))
+ m.URL = kv.Value.URL;
+ }
+ }
+ }
+
+ if (needCheckLocalChanges)
+ {
+ var builder = new StringBuilder();
+ foreach (var kv in map)
+ {
+ if (kv.Value.Status == Models.SubmoduleStatus.Normal)
+ {
+ builder.Append('"');
+ builder.Append(kv.Key);
+ builder.Append("\" ");
+ }
+ }
+
+ Args = $"--no-optional-locks status --porcelain -- {builder}";
rs = ReadToEnd();
if (!rs.IsSuccess)
return submodules;
- var dirty = new HashSet();
- lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
+ lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
- var match = REG_FORMAT_STATUS().Match(line);
+ var match = REG_FORMAT_DIRTY().Match(line);
if (match.Success)
{
var path = match.Groups[1].Value;
- dirty.Add(path);
+ if (map.TryGetValue(path, out var m))
+ m.Status = Models.SubmoduleStatus.Modified;
}
}
-
- foreach (var submodule in submodules)
- submodule.IsDirty = dirty.Contains(submodule.Path);
}
return submodules;
}
+
+ private class ModuleInfo
+ {
+ public string Path { get; set; } = string.Empty;
+ public string URL { get; set; } = string.Empty;
+ }
}
}
diff --git a/src/Commands/QueryTags.cs b/src/Commands/QueryTags.cs
index 73f63d8b..896d555e 100644
--- a/src/Commands/QueryTags.cs
+++ b/src/Commands/QueryTags.cs
@@ -11,7 +11,7 @@ namespace SourceGit.Commands
Context = repo;
WorkingDirectory = repo;
- Args = $"tag -l --format=\"{_boundary}%(refname)%00%(objectname)%00%(*objectname)%00%(creatordate:unix)%00%(contents:subject)%0a%0a%(contents:body)\"";
+ Args = $"tag -l --format=\"{_boundary}%(refname)%00%(objecttype)%00%(objectname)%00%(*objectname)%00%(creatordate:unix)%00%(contents:subject)%0a%0a%(contents:body)\"";
}
public List Result()
@@ -24,17 +24,22 @@ namespace SourceGit.Commands
var records = rs.StdOut.Split(_boundary, StringSplitOptions.RemoveEmptyEntries);
foreach (var record in records)
{
- var subs = record.Split('\0', StringSplitOptions.None);
- if (subs.Length != 5)
+ var subs = record.Split('\0');
+ if (subs.Length != 6)
continue;
- var message = subs[4].Trim();
+ var name = subs[0].Substring(10);
+ var message = subs[5].Trim();
+ if (!string.IsNullOrEmpty(message) && message.Equals(name, StringComparison.Ordinal))
+ message = null;
+
tags.Add(new Models.Tag()
{
- Name = subs[0].Substring(10),
- SHA = string.IsNullOrEmpty(subs[2]) ? subs[1] : subs[2],
- CreatorDate = ulong.Parse(subs[3]),
- Message = string.IsNullOrEmpty(message) ? null : message,
+ Name = name,
+ IsAnnotated = subs[1].Equals("tag", StringComparison.Ordinal),
+ SHA = string.IsNullOrEmpty(subs[3]) ? subs[2] : subs[3],
+ CreatorDate = ulong.Parse(subs[4]),
+ Message = message,
});
}
diff --git a/src/Commands/QueryTrackStatus.cs b/src/Commands/QueryTrackStatus.cs
index 0bf9746f..e7e1f1c9 100644
--- a/src/Commands/QueryTrackStatus.cs
+++ b/src/Commands/QueryTrackStatus.cs
@@ -19,7 +19,7 @@ namespace SourceGit.Commands
if (!rs.IsSuccess)
return status;
- var lines = rs.StdOut.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line[0] == '>')
diff --git a/src/Commands/QueryUpdatableSubmodules.cs b/src/Commands/QueryUpdatableSubmodules.cs
new file mode 100644
index 00000000..03f4a24d
--- /dev/null
+++ b/src/Commands/QueryUpdatableSubmodules.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace SourceGit.Commands
+{
+ public partial class QueryUpdatableSubmodules : Command
+ {
+ [GeneratedRegex(@"^([U\-\+ ])([0-9a-f]+)\s(.*?)(\s\(.*\))?$")]
+ private static partial Regex REG_FORMAT_STATUS();
+
+ public QueryUpdatableSubmodules(string repo)
+ {
+ WorkingDirectory = repo;
+ Context = repo;
+ Args = "submodule status";
+ }
+
+ public List Result()
+ {
+ var submodules = new List();
+ var rs = ReadToEnd();
+
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ var match = REG_FORMAT_STATUS().Match(line);
+ if (match.Success)
+ {
+ var stat = match.Groups[1].Value;
+ var path = match.Groups[3].Value;
+ if (!stat.StartsWith(' '))
+ submodules.Add(path);
+ }
+ }
+
+ return submodules;
+ }
+ }
+}
diff --git a/src/Commands/Reset.cs b/src/Commands/Reset.cs
index da272135..6a54533b 100644
--- a/src/Commands/Reset.cs
+++ b/src/Commands/Reset.cs
@@ -1,33 +1,7 @@
-using System.Collections.Generic;
-using System.Text;
-
-namespace SourceGit.Commands
+namespace SourceGit.Commands
{
public class Reset : Command
{
- public Reset(string repo)
- {
- WorkingDirectory = repo;
- Context = repo;
- Args = "reset";
- }
-
- public Reset(string repo, List changes)
- {
- WorkingDirectory = repo;
- Context = repo;
-
- var builder = new StringBuilder();
- builder.Append("reset --");
- foreach (var c in changes)
- {
- builder.Append(" \"");
- builder.Append(c.Path);
- builder.Append("\"");
- }
- Args = builder.ToString();
- }
-
public Reset(string repo, string revision, string mode)
{
WorkingDirectory = repo;
diff --git a/src/Commands/Restore.cs b/src/Commands/Restore.cs
index 7a363543..663ea975 100644
--- a/src/Commands/Restore.cs
+++ b/src/Commands/Restore.cs
@@ -1,29 +1,52 @@
-using System.Collections.Generic;
-using System.Text;
+using System.Text;
namespace SourceGit.Commands
{
public class Restore : Command
{
- public Restore(string repo)
+ ///
+ /// Only used for single staged change.
+ ///
+ ///
+ ///
+ public Restore(string repo, Models.Change stagedChange)
{
WorkingDirectory = repo;
Context = repo;
- Args = "restore . --source=HEAD --staged --worktree --recurse-submodules";
+
+ var builder = new StringBuilder();
+ builder.Append("restore --staged -- \"");
+ builder.Append(stagedChange.Path);
+ builder.Append('"');
+
+ if (stagedChange.Index == Models.ChangeState.Renamed)
+ {
+ builder.Append(" \"");
+ builder.Append(stagedChange.OriginalPath);
+ builder.Append('"');
+ }
+
+ Args = builder.ToString();
}
- public Restore(string repo, List files, string extra)
+ ///
+ /// Restore changes given in a path-spec file.
+ ///
+ ///
+ ///
+ ///
+ public Restore(string repo, string pathspecFile, bool isStaged)
{
WorkingDirectory = repo;
Context = repo;
- StringBuilder builder = new StringBuilder();
+ var builder = new StringBuilder();
builder.Append("restore ");
- if (!string.IsNullOrEmpty(extra))
- builder.Append(extra).Append(" ");
- builder.Append("--");
- foreach (var f in files)
- builder.Append(' ').Append('"').Append(f).Append('"');
+ builder.Append(isStaged ? "--staged " : "--worktree --recurse-submodules ");
+ builder.Append("--pathspec-from-file=\"");
+ builder.Append(pathspecFile);
+ builder.Append('"');
+
Args = builder.ToString();
}
}
diff --git a/src/Commands/SaveRevisionFile.cs b/src/Commands/SaveRevisionFile.cs
index 99e89093..64e8f8a5 100644
--- a/src/Commands/SaveRevisionFile.cs
+++ b/src/Commands/SaveRevisionFile.cs
@@ -10,15 +10,15 @@ namespace SourceGit.Commands
{
public static void Run(string repo, string revision, string file, string saveTo)
{
+ var dir = Path.GetDirectoryName(saveTo);
+ if (!Directory.Exists(dir))
+ Directory.CreateDirectory(dir);
+
var isLFSFiltered = new IsLFSFiltered(repo, revision, file).Result();
if (isLFSFiltered)
{
- var tmpFile = saveTo + ".tmp";
- if (ExecCmd(repo, $"show {revision}:\"{file}\"", tmpFile))
- {
- ExecCmd(repo, $"lfs smudge", saveTo, tmpFile);
- }
- File.Delete(tmpFile);
+ var pointerStream = QueryFileContent.Run(repo, revision, file);
+ ExecCmd(repo, $"lfs smudge", saveTo, pointerStream);
}
else
{
@@ -26,7 +26,7 @@ namespace SourceGit.Commands
}
}
- private static bool ExecCmd(string repo, string args, string outputFile, string inputFile = null)
+ private static void ExecCmd(string repo, string args, string outputFile, Stream input = null)
{
var starter = new ProcessStartInfo();
starter.WorkingDirectory = repo;
@@ -45,27 +45,11 @@ namespace SourceGit.Commands
{
var proc = new Process() { StartInfo = starter };
proc.Start();
-
- if (inputFile != null)
- {
- using (StreamReader sr = new StreamReader(inputFile))
- {
- while (true)
- {
- var line = sr.ReadLine();
- if (line == null)
- break;
- proc.StandardInput.WriteLine(line);
- }
- }
- }
-
+ if (input != null)
+ proc.StandardInput.Write(new StreamReader(input).ReadToEnd());
proc.StandardOutput.BaseStream.CopyTo(sw);
proc.WaitForExit();
- var rs = proc.ExitCode == 0;
proc.Close();
-
- return rs;
}
catch (Exception e)
{
@@ -73,7 +57,6 @@ namespace SourceGit.Commands
{
App.RaiseException(repo, "Save file failed: " + e.Message);
});
- return false;
}
}
}
diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs
index 7acfdf38..7d1a269b 100644
--- a/src/Commands/Stash.cs
+++ b/src/Commands/Stash.cs
@@ -82,7 +82,7 @@ namespace SourceGit.Commands
public bool Pop(string name)
{
- Args = $"stash pop -q \"{name}\"";
+ Args = $"stash pop -q --index \"{name}\"";
return Exec();
}
diff --git a/src/Commands/Statistics.cs b/src/Commands/Statistics.cs
index 511c43e8..e11c1740 100644
--- a/src/Commands/Statistics.cs
+++ b/src/Commands/Statistics.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace SourceGit.Commands
{
@@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
- Args = $"log --date-order --branches --remotes -{max} --pretty=format:\"%ct$%aN\"";
+ Args = $"log --date-order --branches --remotes -{max} --format=%ct$%aN±%aE";
}
public Models.Statistics Result()
@@ -40,7 +40,7 @@ namespace SourceGit.Commands
if (dateEndIdx == -1)
return;
- var dateStr = line.Substring(0, dateEndIdx);
+ var dateStr = line.AsSpan(0, dateEndIdx);
if (double.TryParse(dateStr, out var date))
statistics.AddCommit(line.Substring(dateEndIdx + 1), date);
}
diff --git a/src/Commands/Submodule.cs b/src/Commands/Submodule.cs
index 9a273703..025d035a 100644
--- a/src/Commands/Submodule.cs
+++ b/src/Commands/Submodule.cs
@@ -1,4 +1,5 @@
-using System;
+using System.Collections.Generic;
+using System.Text;
namespace SourceGit.Commands
{
@@ -10,10 +11,9 @@ namespace SourceGit.Commands
Context = repo;
}
- public bool Add(string url, string relativePath, bool recursive, Action outputHandler)
+ public bool Add(string url, string relativePath, bool recursive)
{
- _outputHandler = outputHandler;
- Args = $"submodule add {url} \"{relativePath}\"";
+ Args = $"-c protocol.file.allow=always submodule add \"{url}\" \"{relativePath}\"";
if (!Exec())
return false;
@@ -29,38 +29,38 @@ namespace SourceGit.Commands
}
}
- public bool Update(string module, bool init, bool recursive, bool useRemote, Action outputHandler)
+ public bool Update(List modules, bool init, bool recursive, bool useRemote = false)
{
- Args = "submodule update";
+ var builder = new StringBuilder();
+ builder.Append("submodule update");
if (init)
- Args += " --init";
+ builder.Append(" --init");
if (recursive)
- Args += " --recursive";
+ builder.Append(" --recursive");
if (useRemote)
- Args += " --remote";
- if (!string.IsNullOrEmpty(module))
- Args += $" -- \"{module}\"";
+ builder.Append(" --remote");
+ if (modules.Count > 0)
+ {
+ builder.Append(" --");
+ foreach (var module in modules)
+ builder.Append($" \"{module}\"");
+ }
- _outputHandler = outputHandler;
+ Args = builder.ToString();
return Exec();
}
- public bool Delete(string relativePath)
+ public bool Deinit(string module, bool force)
{
- Args = $"submodule deinit -f \"{relativePath}\"";
- if (!Exec())
- return false;
-
- Args = $"rm -rf \"{relativePath}\"";
+ Args = force ? $"submodule deinit -f -- \"{module}\"" : $"submodule deinit -- \"{module}\"";
return Exec();
}
- protected override void OnReadline(string line)
+ public bool Delete(string module)
{
- _outputHandler?.Invoke(line);
+ Args = $"rm -rf \"{module}\"";
+ return Exec();
}
-
- private Action _outputHandler;
}
}
diff --git a/src/Commands/Tag.cs b/src/Commands/Tag.cs
index 23dbb11c..017afea0 100644
--- a/src/Commands/Tag.cs
+++ b/src/Commands/Tag.cs
@@ -1,57 +1,51 @@
-using System.Collections.Generic;
-using System.IO;
+using System.IO;
namespace SourceGit.Commands
{
public static class Tag
{
- public static bool Add(string repo, string name, string basedOn)
+ public static bool Add(string repo, string name, string basedOn, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
- cmd.Args = $"tag {name} {basedOn}";
+ cmd.Args = $"tag --no-sign {name} {basedOn}";
+ cmd.Log = log;
return cmd.Exec();
}
- public static bool Add(string repo, string name, string basedOn, string message, bool sign)
+ public static bool Add(string repo, string name, string basedOn, string message, bool sign, Models.ICommandLog log)
{
var param = sign ? "--sign -a" : "--no-sign -a";
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag {param} {name} {basedOn} ";
+ cmd.Log = log;
if (!string.IsNullOrEmpty(message))
{
string tmp = Path.GetTempFileName();
File.WriteAllText(tmp, message);
cmd.Args += $"-F \"{tmp}\"";
- }
- else
- {
- cmd.Args += $"-m {name}";
+
+ var succ = cmd.Exec();
+ File.Delete(tmp);
+ return succ;
}
+ cmd.Args += $"-m {name}";
return cmd.Exec();
}
- public static bool Delete(string repo, string name, List remotes)
+ public static bool Delete(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag --delete {name}";
- if (!cmd.Exec())
- return false;
-
- if (remotes != null)
- {
- foreach (var r in remotes)
- new Push(repo, r.Name, $"refs/tags/{name}", true).Exec();
- }
-
- return true;
+ cmd.Log = log;
+ return cmd.Exec();
}
}
}
diff --git a/src/Commands/UnstageChangesForAmend.cs b/src/Commands/UnstageChangesForAmend.cs
index c930f136..19def067 100644
--- a/src/Commands/UnstageChangesForAmend.cs
+++ b/src/Commands/UnstageChangesForAmend.cs
@@ -23,13 +23,11 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.OriginalPath);
- _patchBuilder.Append("\n");
}
else if (c.Index == Models.ChangeState.Added)
{
_patchBuilder.Append("0 0000000000000000000000000000000000000000\t");
_patchBuilder.Append(c.Path);
- _patchBuilder.Append("\n");
}
else if (c.Index == Models.ChangeState.Deleted)
{
@@ -37,7 +35,6 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.Path);
- _patchBuilder.Append("\n");
}
else
{
@@ -46,8 +43,9 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.Path);
- _patchBuilder.Append("\n");
}
+
+ _patchBuilder.Append("\n");
}
}
diff --git a/src/Commands/UpdateRef.cs b/src/Commands/UpdateRef.cs
deleted file mode 100644
index ba1b3d2f..00000000
--- a/src/Commands/UpdateRef.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-
-namespace SourceGit.Commands
-{
- public class UpdateRef : Command
- {
- public UpdateRef(string repo, string refName, string toRevision, Action outputHandler)
- {
- _outputHandler = outputHandler;
-
- WorkingDirectory = repo;
- Context = repo;
- Args = $"update-ref {refName} {toRevision}";
- }
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private Action _outputHandler;
- }
-}
diff --git a/src/Commands/Worktree.cs b/src/Commands/Worktree.cs
index b73b8573..1198a443 100644
--- a/src/Commands/Worktree.cs
+++ b/src/Commands/Worktree.cs
@@ -21,7 +21,7 @@ namespace SourceGit.Commands
var last = null as Models.Worktree;
if (rs.IsSuccess)
{
- var lines = rs.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+ var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.StartsWith("worktree ", StringComparison.Ordinal))
@@ -56,7 +56,7 @@ namespace SourceGit.Commands
return worktrees;
}
- public bool Add(string fullpath, string name, bool createNew, string tracking, Action outputHandler)
+ public bool Add(string fullpath, string name, bool createNew, string tracking)
{
Args = "worktree add ";
@@ -78,14 +78,12 @@ namespace SourceGit.Commands
else if (!string.IsNullOrEmpty(name) && !createNew)
Args += name;
- _outputHandler = outputHandler;
return Exec();
}
- public bool Prune(Action outputHandler)
+ public bool Prune()
{
Args = "worktree prune -v";
- _outputHandler = outputHandler;
return Exec();
}
@@ -101,22 +99,14 @@ namespace SourceGit.Commands
return Exec();
}
- public bool Remove(string fullpath, bool force, Action outputHandler)
+ public bool Remove(string fullpath, bool force)
{
if (force)
Args = $"worktree remove -f \"{fullpath}\"";
else
Args = $"worktree remove \"{fullpath}\"";
- _outputHandler = outputHandler;
return Exec();
}
-
- protected override void OnReadline(string line)
- {
- _outputHandler?.Invoke(line);
- }
-
- private Action _outputHandler = null;
}
}
diff --git a/src/Converters/ListConverters.cs b/src/Converters/ListConverters.cs
index 81cac8b7..6f3ae98b 100644
--- a/src/Converters/ListConverters.cs
+++ b/src/Converters/ListConverters.cs
@@ -7,8 +7,11 @@ namespace SourceGit.Converters
{
public static class ListConverters
{
+ public static readonly FuncValueConverter Count =
+ new FuncValueConverter(v => v == null ? "0" : $"{v.Count}");
+
public static readonly FuncValueConverter ToCount =
- new FuncValueConverter(v => v == null ? " (0)" : $" ({v.Count})");
+ new FuncValueConverter(v => v == null ? "(0)" : $"({v.Count})");
public static readonly FuncValueConverter IsNullOrEmpty =
new FuncValueConverter(v => v == null || v.Count == 0);
diff --git a/src/Converters/ObjectConverters.cs b/src/Converters/ObjectConverters.cs
new file mode 100644
index 00000000..f7c57764
--- /dev/null
+++ b/src/Converters/ObjectConverters.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace SourceGit.Converters
+{
+ public static class ObjectConverters
+ {
+ public class IsTypeOfConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value == null || parameter == null)
+ return false;
+
+ return value.GetType().IsAssignableTo((Type)parameter);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return new NotImplementedException();
+ }
+ }
+
+ public static readonly IsTypeOfConverter IsTypeOf = new IsTypeOfConverter();
+ }
+}
diff --git a/src/Converters/PathConverters.cs b/src/Converters/PathConverters.cs
index dd7cfa49..ac1e61e5 100644
--- a/src/Converters/PathConverters.cs
+++ b/src/Converters/PathConverters.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using Avalonia.Data.Converters;
@@ -22,7 +22,7 @@ namespace SourceGit.Converters
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length;
if (v.StartsWith(home, StringComparison.Ordinal))
- return "~" + v.Substring(prefixLen);
+ return $"~{v.AsSpan(prefixLen)}";
return v;
});
diff --git a/src/Converters/StringConverters.cs b/src/Converters/StringConverters.cs
index 5e4608c5..bcadfae9 100644
--- a/src/Converters/StringConverters.cs
+++ b/src/Converters/StringConverters.cs
@@ -81,5 +81,8 @@ namespace SourceGit.Converters
public static readonly FuncValueConverter ContainsSpaces =
new FuncValueConverter(v => v != null && v.Contains(' '));
+
+ public static readonly FuncValueConverter IsNotNullOrWhitespace =
+ new FuncValueConverter(v => v != null && v.Trim().Length > 0);
}
}
diff --git a/src/Models/ApplyWhiteSpaceMode.cs b/src/Models/ApplyWhiteSpaceMode.cs
index 6fbce0b2..aad45f57 100644
--- a/src/Models/ApplyWhiteSpaceMode.cs
+++ b/src/Models/ApplyWhiteSpaceMode.cs
@@ -2,14 +2,22 @@
{
public class ApplyWhiteSpaceMode
{
+ public static readonly ApplyWhiteSpaceMode[] Supported =
+ [
+ new ApplyWhiteSpaceMode("No Warn", "Turns off the trailing whitespace warning", "nowarn"),
+ new ApplyWhiteSpaceMode("Warn", "Outputs warnings for a few such errors, but applies", "warn"),
+ new ApplyWhiteSpaceMode("Error", "Raise errors and refuses to apply the patch", "error"),
+ new ApplyWhiteSpaceMode("Error All", "Similar to 'error', but shows more", "error-all"),
+ ];
+
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public ApplyWhiteSpaceMode(string n, string d, string a)
{
- Name = App.Text(n);
- Desc = App.Text(d);
+ Name = n;
+ Desc = d;
Arg = a;
}
}
diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs
index 9f0bceaf..bc9fbe95 100644
--- a/src/Models/AvatarManager.cs
+++ b/src/Models/AvatarManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -17,7 +17,7 @@ namespace SourceGit.Models
{
public interface IAvatarHost
{
- void OnAvatarResourceChanged(string email);
+ void OnAvatarResourceChanged(string email, Bitmap image);
}
public partial class AvatarManager
@@ -26,10 +26,7 @@ namespace SourceGit.Models
{
get
{
- if (_instance == null)
- _instance = new AvatarManager();
-
- return _instance;
+ return _instance ??= new AvatarManager();
}
}
@@ -38,7 +35,7 @@ namespace SourceGit.Models
[GeneratedRegex(@"^(?:(\d+)\+)?(.+?)@.+\.github\.com$")]
private static partial Regex REG_GITHUB_USER_EMAIL();
- private object _synclock = new object();
+ private readonly Lock _synclock = new();
private string _storePath;
private List _avatars = new List();
private Dictionary _resources = new Dictionary();
@@ -119,7 +116,7 @@ namespace SourceGit.Models
Dispatcher.UIThread.InvokeAsync(() =>
{
_resources[email] = img;
- NotifyResourceChanged(email);
+ NotifyResourceChanged(email, img);
});
}
@@ -144,14 +141,13 @@ namespace SourceGit.Models
if (_defaultAvatars.Contains(email))
return null;
- if (_resources.ContainsKey(email))
- _resources.Remove(email);
+ _resources.Remove(email);
var localFile = Path.Combine(_storePath, GetEmailHash(email));
if (File.Exists(localFile))
File.Delete(localFile);
- NotifyResourceChanged(email);
+ NotifyResourceChanged(email, null);
}
else
{
@@ -179,13 +175,40 @@ namespace SourceGit.Models
lock (_synclock)
{
- if (!_requesting.Contains(email))
- _requesting.Add(email);
+ _requesting.Add(email);
}
return null;
}
+ public void SetFromLocal(string email, string file)
+ {
+ try
+ {
+ Bitmap image = null;
+
+ using (var stream = File.OpenRead(file))
+ {
+ image = Bitmap.DecodeToWidth(stream, 128);
+ }
+
+ if (image == null)
+ return;
+
+ _resources[email] = image;
+
+ _requesting.Remove(email);
+
+ var store = Path.Combine(_storePath, GetEmailHash(email));
+ File.Copy(file, store, true);
+ NotifyResourceChanged(email, image);
+ }
+ catch
+ {
+ // ignore
+ }
+ }
+
private void LoadDefaultAvatar(string key, string img)
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/{img}", UriKind.RelativeOrAbsolute));
@@ -196,19 +219,17 @@ namespace SourceGit.Models
private string GetEmailHash(string email)
{
var lowered = email.ToLower(CultureInfo.CurrentCulture).Trim();
- var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(lowered));
- var builder = new StringBuilder();
+ var hash = MD5.HashData(Encoding.Default.GetBytes(lowered));
+ var builder = new StringBuilder(hash.Length * 2);
foreach (var c in hash)
builder.Append(c.ToString("x2"));
return builder.ToString();
}
- private void NotifyResourceChanged(string email)
+ private void NotifyResourceChanged(string email, Bitmap image)
{
foreach (var avatar in _avatars)
- {
- avatar.OnAvatarResourceChanged(email);
- }
+ avatar.OnAvatarResourceChanged(email, image);
}
}
}
diff --git a/src/Models/Bisect.cs b/src/Models/Bisect.cs
new file mode 100644
index 00000000..2ed8beb2
--- /dev/null
+++ b/src/Models/Bisect.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+
+namespace SourceGit.Models
+{
+ public enum BisectState
+ {
+ None = 0,
+ WaitingForRange,
+ Detecting,
+ }
+
+ [Flags]
+ public enum BisectCommitFlag
+ {
+ None = 0,
+ Good = 1 << 0,
+ Bad = 1 << 1,
+ }
+
+ public class Bisect
+ {
+ public HashSet Bads
+ {
+ get;
+ set;
+ } = [];
+
+ public HashSet Goods
+ {
+ get;
+ set;
+ } = [];
+ }
+}
diff --git a/src/Models/Branch.cs b/src/Models/Branch.cs
index 2d0ae5b2..7146da3f 100644
--- a/src/Models/Branch.cs
+++ b/src/Models/Branch.cs
@@ -23,10 +23,17 @@ namespace SourceGit.Models
}
}
+ public enum BranchSortMode
+ {
+ Name = 0,
+ CommitterDate,
+ }
+
public class Branch
{
public string Name { get; set; }
public string FullName { get; set; }
+ public ulong CommitterDate { get; set; }
public string Head { get; set; }
public bool IsLocal { get; set; }
public bool IsCurrent { get; set; }
@@ -34,7 +41,7 @@ namespace SourceGit.Models
public string Upstream { get; set; }
public BranchTrackStatus TrackStatus { get; set; }
public string Remote { get; set; }
- public bool IsUpsteamGone { get; set; }
+ public bool IsUpstreamGone { get; set; }
public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}";
}
diff --git a/src/Models/Change.cs b/src/Models/Change.cs
index 36fe20ac..129678be 100644
--- a/src/Models/Change.cs
+++ b/src/Models/Change.cs
@@ -18,14 +18,27 @@ namespace SourceGit.Models
Deleted,
Renamed,
Copied,
- Unmerged,
- Untracked
+ Untracked,
+ Conflicted,
+ }
+
+ public enum ConflictReason
+ {
+ None,
+ BothDeleted,
+ AddedByUs,
+ DeletedByThem,
+ AddedByThem,
+ DeletedByUs,
+ BothAdded,
+ BothModified,
}
public class ChangeDataForAmend
{
public string FileMode { get; set; } = "";
public string ObjectHash { get; set; } = "";
+ public string ParentSHA { get; set; } = "";
}
public class Change
@@ -35,20 +48,14 @@ namespace SourceGit.Models
public string Path { get; set; } = "";
public string OriginalPath { get; set; } = "";
public ChangeDataForAmend DataForAmend { get; set; } = null;
+ public ConflictReason ConflictReason { get; set; } = ConflictReason.None;
- public bool IsConflit
- {
- get
- {
- if (Index == ChangeState.Unmerged || WorkTree == ChangeState.Unmerged)
- return true;
- if (Index == ChangeState.Added && WorkTree == ChangeState.Added)
- return true;
- if (Index == ChangeState.Deleted && WorkTree == ChangeState.Deleted)
- return true;
- return false;
- }
- }
+ public bool IsConflicted => WorkTree == ChangeState.Conflicted;
+ public string ConflictMarker => CONFLICT_MARKERS[(int)ConflictReason];
+ public string ConflictDesc => CONFLICT_DESCS[(int)ConflictReason];
+
+ public string WorkTreeDesc => TYPE_DESCS[(int)WorkTree];
+ public string IndexDesc => TYPE_DESCS[(int)Index];
public void Set(ChangeState index, ChangeState workTree = ChangeState.None)
{
@@ -76,8 +83,44 @@ namespace SourceGit.Models
if (Path[0] == '"')
Path = Path.Substring(1, Path.Length - 2);
+
if (!string.IsNullOrEmpty(OriginalPath) && OriginalPath[0] == '"')
OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2);
}
+
+ private static readonly string[] TYPE_DESCS =
+ [
+ "Unknown",
+ "Modified",
+ "Type Changed",
+ "Added",
+ "Deleted",
+ "Renamed",
+ "Copied",
+ "Untracked",
+ "Conflict"
+ ];
+ private static readonly string[] CONFLICT_MARKERS =
+ [
+ string.Empty,
+ "DD",
+ "AU",
+ "UD",
+ "UA",
+ "DU",
+ "AA",
+ "UU"
+ ];
+ private static readonly string[] CONFLICT_DESCS =
+ [
+ string.Empty,
+ "Both deleted",
+ "Added by us",
+ "Deleted by them",
+ "Added by them",
+ "Deleted by us",
+ "Both added",
+ "Both modified"
+ ];
}
}
diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs
index 0bad8376..f0f4b39b 100644
--- a/src/Models/Commit.cs
+++ b/src/Models/Commit.cs
@@ -13,10 +13,14 @@ namespace SourceGit.Models
ByCommitter,
ByMessage,
ByFile,
+ ByContent,
}
public class Commit
{
+ // As retrieved by: git mktree Parents { get; set; } = new List();
- public List Decorators { get; set; } = new List();
+ public List Parents { get; set; } = new();
+ public List Decorators { get; set; } = new();
public bool HasDecorators => Decorators.Count > 0;
- public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime);
- public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime);
- public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateOnly);
- public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateOnly);
+ public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime);
+ public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime);
+ public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);
+ public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);
public bool IsMerged { get; set; } = false;
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
@@ -45,7 +49,7 @@ namespace SourceGit.Models
public int Color { get; set; } = 0;
public double Opacity => IsMerged ? 1 : OpacityForNotMerged;
public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular;
- public Thickness Margin { get; set; } = new Thickness(0);
+ public Thickness Margin { get; set; } = new(0);
public IBrush Brush => CommitGraph.Pens[Color].Brush;
public void ParseDecorators(string data)
@@ -109,7 +113,7 @@ namespace SourceGit.Models
if (l.Type != r.Type)
return (int)l.Type - (int)r.Type;
else
- return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
+ return NumericSort.Compare(l.Name, r.Name);
});
}
}
@@ -117,6 +121,6 @@ namespace SourceGit.Models
public class CommitFullMessage
{
public string Message { get; set; } = string.Empty;
- public List Links { get; set; } = [];
+ public InlineElementCollector Inlines { get; set; } = new();
}
}
diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs
index 77209751..01488656 100644
--- a/src/Models/CommitGraph.cs
+++ b/src/Models/CommitGraph.cs
@@ -64,8 +64,8 @@ namespace SourceGit.Models
{
const double unitWidth = 12;
const double halfWidth = 6;
- const double unitHeight = 28;
- const double halfHeight = 14;
+ const double unitHeight = 1;
+ const double halfHeight = 0.5;
var temp = new CommitGraph();
var unsolved = new List();
diff --git a/src/Models/CommitLink.cs b/src/Models/CommitLink.cs
index 955779a8..2891e5d6 100644
--- a/src/Models/CommitLink.cs
+++ b/src/Models/CommitLink.cs
@@ -1,8 +1,49 @@
-namespace SourceGit.Models
+using System;
+using System.Collections.Generic;
+
+namespace SourceGit.Models
{
public class CommitLink
{
public string Name { get; set; } = null;
public string URLPrefix { get; set; } = null;
+
+ public CommitLink(string name, string prefix)
+ {
+ Name = name;
+ URLPrefix = prefix;
+ }
+
+ public static List Get(List remotes)
+ {
+ var outs = new List();
+
+ foreach (var remote in remotes)
+ {
+ if (remote.TryGetVisitURL(out var url))
+ {
+ var trimmedUrl = url.AsSpan();
+ if (url.EndsWith(".git"))
+ trimmedUrl = url.AsSpan(0, url.Length - 4);
+
+ if (url.StartsWith("https://github.com/", StringComparison.Ordinal))
+ outs.Add(new($"Github ({trimmedUrl.Slice(19)})", $"{url}/commit/"));
+ else if (url.StartsWith("https://gitlab.", StringComparison.Ordinal))
+ outs.Add(new($"GitLab ({trimmedUrl.Slice(trimmedUrl.Slice(15).IndexOf('/') + 16)})", $"{url}/-/commit/"));
+ else if (url.StartsWith("https://gitee.com/", StringComparison.Ordinal))
+ outs.Add(new($"Gitee ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
+ else if (url.StartsWith("https://bitbucket.org/", StringComparison.Ordinal))
+ outs.Add(new($"BitBucket ({trimmedUrl.Slice(22)})", $"{url}/commits/"));
+ else if (url.StartsWith("https://codeberg.org/", StringComparison.Ordinal))
+ outs.Add(new($"Codeberg ({trimmedUrl.Slice(21)})", $"{url}/commit/"));
+ else if (url.StartsWith("https://gitea.org/", StringComparison.Ordinal))
+ outs.Add(new($"Gitea ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
+ else if (url.StartsWith("https://git.sr.ht/", StringComparison.Ordinal))
+ outs.Add(new($"sourcehut ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
+ }
+ }
+
+ return outs;
+ }
}
}
diff --git a/src/Models/CommitTemplate.cs b/src/Models/CommitTemplate.cs
index 56e1992c..3f331543 100644
--- a/src/Models/CommitTemplate.cs
+++ b/src/Models/CommitTemplate.cs
@@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.Models
{
- public partial class CommitTemplate : ObservableObject
+ public class CommitTemplate : ObservableObject
{
public string Name
{
diff --git a/src/Models/ConventionalCommitType.cs b/src/Models/ConventionalCommitType.cs
index 4fb61d87..531a16c0 100644
--- a/src/Models/ConventionalCommitType.cs
+++ b/src/Models/ConventionalCommitType.cs
@@ -4,23 +4,28 @@ namespace SourceGit.Models
{
public class ConventionalCommitType
{
- public string Type { get; set; } = string.Empty;
- public string Description { get; set; } = string.Empty;
+ public string Name { get; set; }
+ public string Type { get; set; }
+ public string Description { get; set; }
- public static readonly List Supported = new List()
- {
- new ConventionalCommitType("feat", "Adding a new feature"),
- new ConventionalCommitType("fix", "Fixing a bug"),
- new ConventionalCommitType("docs", "Updating documentation"),
- new ConventionalCommitType("style", "Elements or code styles without changing the code logic"),
- new ConventionalCommitType("test", "Adding or updating tests"),
- new ConventionalCommitType("chore", "Making changes to the build process or auxiliary tools and libraries"),
- new ConventionalCommitType("revert", "Undoing a previous commit"),
- new ConventionalCommitType("refactor", "Restructuring code without changing its external behavior")
- };
+ public static readonly List Supported = [
+ new("Features", "feat", "Adding a new feature"),
+ new("Bug Fixes", "fix", "Fixing a bug"),
+ new("Work In Progress", "wip", "Still being developed and not yet complete"),
+ new("Reverts", "revert", "Undoing a previous commit"),
+ new("Code Refactoring", "refactor", "Restructuring code without changing its external behavior"),
+ new("Performance Improvements", "perf", "Improves performance"),
+ new("Builds", "build", "Changes that affect the build system or external dependencies"),
+ new("Continuous Integrations", "ci", "Changes to CI configuration files and scripts"),
+ new("Documentations", "docs", "Updating documentation"),
+ new("Styles", "style", "Elements or code styles without changing the code logic"),
+ new("Tests", "test", "Adding or updating tests"),
+ new("Chores", "chore", "Other changes that don't modify src or test files"),
+ ];
- public ConventionalCommitType(string type, string description)
+ public ConventionalCommitType(string name, string type, string description)
{
+ Name = name;
Type = type;
Description = description;
}
diff --git a/src/Models/Count.cs b/src/Models/Count.cs
new file mode 100644
index 00000000..d48b0c08
--- /dev/null
+++ b/src/Models/Count.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace SourceGit.Models
+{
+ public class Count : IDisposable
+ {
+ public int Value { get; set; } = 0;
+
+ public Count(int value)
+ {
+ Value = value;
+ }
+
+ public void Dispose()
+ {
+ // Ignore
+ }
+ }
+}
diff --git a/src/Models/DateTimeFormat.cs b/src/Models/DateTimeFormat.cs
index 4e71a74f..16276c40 100644
--- a/src/Models/DateTimeFormat.cs
+++ b/src/Models/DateTimeFormat.cs
@@ -25,24 +25,24 @@ namespace SourceGit.Models
set;
} = 0;
- public static DateTimeFormat Actived
+ public static DateTimeFormat Active
{
get => Supported[ActiveIndex];
}
public static readonly List Supported = new List
{
- new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss"),
- new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss"),
- new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"),
- new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy HH:mm:ss"),
- new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy HH:mm:ss"),
- new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy HH:mm:ss"),
- new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss"),
- new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy HH:mm:ss"),
- new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy HH:mm:ss"),
- new DateTimeFormat("MMM d yyyy", "MMM d yyyy HH:mm:ss"),
- new DateTimeFormat("d MMM yyyy", "d MMM yyyy HH:mm:ss"),
+ new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd, HH:mm:ss"),
+ new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd, HH:mm:ss"),
+ new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd, HH:mm:ss"),
+ new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy, HH:mm:ss"),
+ new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy, HH:mm:ss"),
+ new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy, HH:mm:ss"),
+ new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy, HH:mm:ss"),
+ new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy, HH:mm:ss"),
+ new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy, HH:mm:ss"),
+ new DateTimeFormat("MMM d yyyy", "MMM d yyyy, HH:mm:ss"),
+ new DateTimeFormat("d MMM yyyy", "d MMM yyyy, HH:mm:ss"),
};
private static readonly DateTime _example = new DateTime(2025, 1, 31, 8, 0, 0, DateTimeKind.Local);
diff --git a/src/Models/DealWithChangesAfterStashing.cs b/src/Models/DealWithChangesAfterStashing.cs
new file mode 100644
index 00000000..63889c96
--- /dev/null
+++ b/src/Models/DealWithChangesAfterStashing.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+
+namespace SourceGit.Models
+{
+ public class DealWithChangesAfterStashing
+ {
+ public string Label { get; set; }
+ public string Desc { get; set; }
+
+ public static readonly List Supported = [
+ new ("Discard", "All (or selected) changes will be discarded"),
+ new ("Keep Index", "Staged changes are left intact"),
+ new ("Keep All", "All (or selected) changes are left intact"),
+ ];
+
+ public DealWithChangesAfterStashing(string label, string desc)
+ {
+ Label = label;
+ Desc = desc;
+ }
+ }
+}
diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs
index 98387e7f..69f93980 100644
--- a/src/Models/DiffOption.cs
+++ b/src/Models/DiffOption.cs
@@ -5,6 +5,15 @@ namespace SourceGit.Models
{
public class DiffOption
{
+ ///
+ /// Enable `--ignore-cr-at-eol` by default?
+ ///
+ public static bool IgnoreCRAtEOL
+ {
+ get;
+ set;
+ } = true;
+
public Change WorkingCopyChange => _workingCopyChange;
public bool IsUnstaged => _isUnstaged;
public List Revisions => _revisions;
@@ -40,7 +49,7 @@ namespace SourceGit.Models
else
{
if (change.DataForAmend != null)
- _extra = "--cached HEAD^";
+ _extra = $"--cached {change.DataForAmend.ParentSHA}";
else
_extra = "--cached";
@@ -56,7 +65,7 @@ namespace SourceGit.Models
///
public DiffOption(Commit commit, Change change)
{
- var baseRevision = commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : $"{commit.SHA}^";
+ var baseRevision = commit.Parents.Count == 0 ? Commit.EmptyTreeSHA1 : $"{commit.SHA}^";
_revisions.Add(baseRevision);
_revisions.Add(commit.SHA);
_path = change.Path;
@@ -70,7 +79,7 @@ namespace SourceGit.Models
///
public DiffOption(Commit commit, string file)
{
- var baseRevision = commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : $"{commit.SHA}^";
+ var baseRevision = commit.Parents.Count == 0 ? Commit.EmptyTreeSHA1 : $"{commit.SHA}^";
_revisions.Add(baseRevision);
_revisions.Add(commit.SHA);
_path = file;
@@ -115,6 +124,6 @@ namespace SourceGit.Models
private readonly string _path;
private readonly string _orgPath = string.Empty;
private readonly string _extra = string.Empty;
- private readonly List _revisions = new List();
+ private readonly List _revisions = [];
}
}
diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs
index 88992e10..71ed2d9e 100644
--- a/src/Models/DiffResult.cs
+++ b/src/Models/DiffResult.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
@@ -16,11 +16,10 @@ namespace SourceGit.Models
Deleted,
}
- public class TextInlineRange
+ public class TextInlineRange(int p, int n)
{
- public int Start { get; set; }
- public int End { get; set; }
- public TextInlineRange(int p, int n) { Start = p; End = p + n - 1; }
+ public int Start { get; set; } = p;
+ public int End { get; set; } = p + n - 1;
}
public class TextDiffLine
@@ -30,6 +29,7 @@ namespace SourceGit.Models
public int OldLineNumber { get; set; } = 0;
public int NewLineNumber { get; set; } = 0;
public List Highlights { get; set; } = new List();
+ public bool NoNewLineEndOfFile { get; set; } = false;
public string OldLine => OldLineNumber == 0 ? string.Empty : OldLineNumber.ToString();
public string NewLine => NewLineNumber == 0 ? string.Empty : NewLineNumber.ToString();
@@ -146,7 +146,7 @@ namespace SourceGit.Models
public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output)
{
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
- var fileGuid = isTracked ? fileBlobGuid.Substring(0, 8) : "00000000";
+ var fileGuid = isTracked ? fileBlobGuid : "00000000";
var builder = new StringBuilder();
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
@@ -555,7 +555,7 @@ namespace SourceGit.Models
}
else if (test.Type == TextDiffLineType.Added)
{
- if (i < start - 1)
+ if (i < start - 1 || isOldSide)
{
if (revert)
{
@@ -565,18 +565,7 @@ namespace SourceGit.Models
}
else
{
- if (isOldSide)
- {
- if (revert)
- {
- newCount++;
- oldCount++;
- }
- }
- else
- {
- newCount++;
- }
+ newCount++;
}
if (i == end - 1 && tailed)
@@ -654,9 +643,7 @@ namespace SourceGit.Models
public string NewImageSize => New != null ? $"{New.PixelSize.Width} x {New.PixelSize.Height}" : "0 x 0";
}
- public class NoOrEOLChange
- {
- }
+ public class NoOrEOLChange;
public class FileModeDiff
{
diff --git a/src/Models/DirtyState.cs b/src/Models/DirtyState.cs
new file mode 100644
index 00000000..2b9d898d
--- /dev/null
+++ b/src/Models/DirtyState.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace SourceGit.Models
+{
+ [Flags]
+ public enum DirtyState
+ {
+ None = 0,
+ HasLocalChanges = 1 << 0,
+ HasPendingPullOrPush = 1 << 1,
+ }
+}
diff --git a/src/Models/ExternalMerger.cs b/src/Models/ExternalMerger.cs
index 49d31df5..fe67ad6a 100644
--- a/src/Models/ExternalMerger.cs
+++ b/src/Models/ExternalMerger.cs
@@ -42,6 +42,8 @@ namespace SourceGit.Models
new ExternalMerger(7, "win_merge", "WinMerge", "WinMergeU.exe", "\"$MERGED\"", "-u -e -sw \"$LOCAL\" \"$REMOTE\""),
new ExternalMerger(8, "codium", "VSCodium", "VSCodium.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""),
new ExternalMerger(9, "p4merge", "P4Merge", "p4merge.exe", "-tw 4 \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", "-tw 4 \"$LOCAL\" \"$REMOTE\""),
+ new ExternalMerger(10, "plastic_merge", "Plastic SCM", "mergetool.exe", "-s=\"$REMOTE\" -b=\"$BASE\" -d=\"$LOCAL\" -r=\"$MERGED\" --automatic", "-s=\"$LOCAL\" -d=\"$REMOTE\""),
+ new ExternalMerger(11, "meld", "Meld", "Meld.exe", "\"$LOCAL\" \"$BASE\" \"$REMOTE\" --output \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""),
};
}
else if (OperatingSystem.IsMacOS())
diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs
index 103e91bc..697c171a 100644
--- a/src/Models/ExternalTool.cs
+++ b/src/Models/ExternalTool.cs
@@ -107,8 +107,7 @@ namespace SourceGit.Models
// Ignore
}
- if (_customPaths == null)
- _customPaths = new ExternalToolPaths();
+ _customPaths ??= new ExternalToolPaths();
}
public void TryAdd(string name, string icon, Func finder, Func execArgsGenerator = null)
diff --git a/src/Models/GitFlow.cs b/src/Models/GitFlow.cs
new file mode 100644
index 00000000..5d26072b
--- /dev/null
+++ b/src/Models/GitFlow.cs
@@ -0,0 +1,46 @@
+namespace SourceGit.Models
+{
+ public enum GitFlowBranchType
+ {
+ None = 0,
+ Feature,
+ Release,
+ Hotfix,
+ }
+
+ public class GitFlow
+ {
+ public string Master { get; set; } = string.Empty;
+ public string Develop { get; set; } = string.Empty;
+ public string FeaturePrefix { get; set; } = string.Empty;
+ public string ReleasePrefix { get; set; } = string.Empty;
+ public string HotfixPrefix { get; set; } = string.Empty;
+
+ public bool IsValid
+ {
+ get
+ {
+ return !string.IsNullOrEmpty(Master) &&
+ !string.IsNullOrEmpty(Develop) &&
+ !string.IsNullOrEmpty(FeaturePrefix) &&
+ !string.IsNullOrEmpty(ReleasePrefix) &&
+ !string.IsNullOrEmpty(HotfixPrefix);
+ }
+ }
+
+ public string GetPrefix(GitFlowBranchType type)
+ {
+ switch (type)
+ {
+ case GitFlowBranchType.Feature:
+ return FeaturePrefix;
+ case GitFlowBranchType.Release:
+ return ReleasePrefix;
+ case GitFlowBranchType.Hotfix:
+ return HotfixPrefix;
+ default:
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/Models/GitVersions.cs b/src/Models/GitVersions.cs
index 394a9518..8aae63a3 100644
--- a/src/Models/GitVersions.cs
+++ b/src/Models/GitVersions.cs
@@ -5,21 +5,16 @@
///
/// The minimal version of Git that required by this app.
///
- public static readonly System.Version MINIMAL = new System.Version(2, 23, 0);
+ public static readonly System.Version MINIMAL = new(2, 25, 1);
///
- /// The minimal version of Git that supports the `add` command with the `--pathspec-from-file` option.
+ /// The minimal version of Git that supports the `stash push` command with the `--pathspec-from-file` option.
///
- public static readonly System.Version ADD_WITH_PATHSPECFILE = new System.Version(2, 25, 0);
+ public static readonly System.Version STASH_PUSH_WITH_PATHSPECFILE = new(2, 26, 0);
///
- /// The minimal version of Git that supports the `stash` command with the `--pathspec-from-file` option.
+ /// The minimal version of Git that supports the `stash push` command with the `--staged` option.
///
- public static readonly System.Version STASH_WITH_PATHSPECFILE = new System.Version(2, 26, 0);
-
- ///
- /// The minimal version of Git that supports the `stash` command with the `--staged` option.
- ///
- public static readonly System.Version STASH_ONLY_STAGED = new System.Version(2, 35, 0);
+ public static readonly System.Version STASH_PUSH_ONLY_STAGED = new(2, 35, 0);
}
}
diff --git a/src/Models/Hyperlink.cs b/src/Models/Hyperlink.cs
deleted file mode 100644
index 81dc980e..00000000
--- a/src/Models/Hyperlink.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-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/ICommandLog.cs b/src/Models/ICommandLog.cs
new file mode 100644
index 00000000..34ec7031
--- /dev/null
+++ b/src/Models/ICommandLog.cs
@@ -0,0 +1,7 @@
+namespace SourceGit.Models
+{
+ public interface ICommandLog
+ {
+ void AppendLine(string line);
+ }
+}
diff --git a/src/Models/IRepository.cs b/src/Models/IRepository.cs
index 12b1adba..2fc7c612 100644
--- a/src/Models/IRepository.cs
+++ b/src/Models/IRepository.cs
@@ -2,8 +2,7 @@
{
public interface IRepository
{
- string FullPath { get; set; }
- string GitDir { get; set; }
+ bool MayHaveSubmodules();
void RefreshBranches();
void RefreshWorktrees();
diff --git a/src/Models/ImageDecoder.cs b/src/Models/ImageDecoder.cs
new file mode 100644
index 00000000..6fe0f428
--- /dev/null
+++ b/src/Models/ImageDecoder.cs
@@ -0,0 +1,10 @@
+namespace SourceGit.Models
+{
+ public enum ImageDecoder
+ {
+ None = 0,
+ Builtin,
+ Pfim,
+ Tiff,
+ }
+}
diff --git a/src/Models/InlineElement.cs b/src/Models/InlineElement.cs
new file mode 100644
index 00000000..ea7bcee8
--- /dev/null
+++ b/src/Models/InlineElement.cs
@@ -0,0 +1,37 @@
+namespace SourceGit.Models
+{
+ public enum InlineElementType
+ {
+ Keyword = 0,
+ Link,
+ CommitSHA,
+ Code,
+ }
+
+ public class InlineElement
+ {
+ public InlineElementType Type { get; }
+ public int Start { get; }
+ public int Length { get; }
+ public string Link { get; }
+
+ public InlineElement(InlineElementType type, int start, int length, string link)
+ {
+ Type = type;
+ Start = start;
+ Length = length;
+ Link = link;
+ }
+
+ public bool IsIntersecting(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/InlineElementCollector.cs b/src/Models/InlineElementCollector.cs
new file mode 100644
index 00000000..d81aaf8d
--- /dev/null
+++ b/src/Models/InlineElementCollector.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+
+namespace SourceGit.Models
+{
+ public class InlineElementCollector
+ {
+ public int Count => _implementation.Count;
+ public InlineElement this[int index] => _implementation[index];
+
+ public InlineElement Intersect(int start, int length)
+ {
+ foreach (var elem in _implementation)
+ {
+ if (elem.IsIntersecting(start, length))
+ return elem;
+ }
+
+ return null;
+ }
+
+ public void Add(InlineElement element)
+ {
+ _implementation.Add(element);
+ }
+
+ public void Sort()
+ {
+ _implementation.Sort((l, r) => l.Start.CompareTo(r.Start));
+ }
+
+ public void Clear()
+ {
+ _implementation.Clear();
+ }
+
+ private readonly List _implementation = [];
+ }
+}
diff --git a/src/Models/InteractiveRebase.cs b/src/Models/InteractiveRebase.cs
index 691aadeb..d1710d4a 100644
--- a/src/Models/InteractiveRebase.cs
+++ b/src/Models/InteractiveRebase.cs
@@ -27,6 +27,8 @@ namespace SourceGit.Models
public class InteractiveRebaseJobCollection
{
+ public string OrigHead { get; set; } = string.Empty;
+ public string Onto { get; set; } = string.Empty;
public List Jobs { get; set; } = new List();
}
}
diff --git a/src/Models/IpcChannel.cs b/src/Models/IpcChannel.cs
new file mode 100644
index 00000000..001c65a6
--- /dev/null
+++ b/src/Models/IpcChannel.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+using System.IO.Pipes;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SourceGit.Models
+{
+ public class IpcChannel : IDisposable
+ {
+ public bool IsFirstInstance { get; }
+
+ public event Action MessageReceived;
+
+ public IpcChannel()
+ {
+ try
+ {
+ _singletonLock = File.Open(Path.Combine(Native.OS.DataDir, "process.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ IsFirstInstance = true;
+ _server = new NamedPipeServerStream(
+ "SourceGitIPCChannel" + Environment.UserName,
+ PipeDirection.In,
+ -1,
+ PipeTransmissionMode.Byte,
+ PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);
+ _cancellationTokenSource = new CancellationTokenSource();
+ Task.Run(StartServer);
+ }
+ catch
+ {
+ IsFirstInstance = false;
+ }
+ }
+
+ public void SendToFirstInstance(string cmd)
+ {
+ try
+ {
+ using (var client = new NamedPipeClientStream(".", "SourceGitIPCChannel" + Environment.UserName, PipeDirection.Out, PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly))
+ {
+ client.Connect(1000);
+ if (!client.IsConnected)
+ return;
+
+ using (var writer = new StreamWriter(client))
+ {
+ writer.WriteLine(cmd);
+ writer.Flush();
+ }
+
+ if (OperatingSystem.IsWindows())
+ client.WaitForPipeDrain();
+ else
+ Thread.Sleep(1000);
+ }
+ }
+ catch
+ {
+ // IGNORE
+ }
+ }
+
+ public void Dispose()
+ {
+ _cancellationTokenSource?.Cancel();
+ _singletonLock?.Dispose();
+ }
+
+ private async void StartServer()
+ {
+ using var reader = new StreamReader(_server);
+
+ while (!_cancellationTokenSource.IsCancellationRequested)
+ {
+ try
+ {
+ await _server.WaitForConnectionAsync(_cancellationTokenSource.Token);
+
+ if (!_cancellationTokenSource.IsCancellationRequested)
+ {
+ var line = await reader.ReadToEndAsync(_cancellationTokenSource.Token);
+ MessageReceived?.Invoke(line?.Trim());
+ }
+
+ _server.Disconnect();
+ }
+ catch
+ {
+ if (!_cancellationTokenSource.IsCancellationRequested && _server.IsConnected)
+ _server.Disconnect();
+ }
+ }
+ }
+
+ private FileStream _singletonLock = null;
+ private NamedPipeServerStream _server = null;
+ private CancellationTokenSource _cancellationTokenSource = null;
+ }
+}
diff --git a/src/Models/IssueTrackerRule.cs b/src/Models/IssueTrackerRule.cs
index 29487a16..40c84b9e 100644
--- a/src/Models/IssueTrackerRule.cs
+++ b/src/Models/IssueTrackerRule.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
+using System.Text.RegularExpressions;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -46,7 +45,7 @@ namespace SourceGit.Models
set => SetProperty(ref _urlTemplate, value);
}
- public void Matches(List outs, string message)
+ public void Matches(InlineElementCollector outs, string message)
{
if (_regex == null || string.IsNullOrEmpty(_urlTemplate))
return;
@@ -60,17 +59,7 @@ namespace SourceGit.Models
var start = match.Index;
var len = match.Length;
- var intersect = false;
- foreach (var exist in outs)
- {
- if (exist.Intersect(start, len))
- {
- intersect = true;
- break;
- }
- }
-
- if (intersect)
+ if (outs.Intersect(start, len) != null)
continue;
var link = _urlTemplate;
@@ -81,8 +70,7 @@ namespace SourceGit.Models
link = link.Replace($"${j}", group.Value);
}
- var range = new Hyperlink(start, len, link);
- outs.Add(range);
+ outs.Add(new InlineElement(InlineElementType.Link, start, len, link));
}
}
diff --git a/src/Models/LFSObject.cs b/src/Models/LFSObject.cs
index 0f281253..8bc2dda2 100644
--- a/src/Models/LFSObject.cs
+++ b/src/Models/LFSObject.cs
@@ -1,8 +1,22 @@
-namespace SourceGit.Models
+using System.Text.RegularExpressions;
+
+namespace SourceGit.Models
{
- public class LFSObject
+ public partial class LFSObject
{
+ [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")]
+ private static partial Regex REG_FORMAT();
+
public string Oid { get; set; } = string.Empty;
public long Size { get; set; } = 0;
+
+ public static LFSObject Parse(string content)
+ {
+ var match = REG_FORMAT().Match(content);
+ if (match.Success)
+ return new() { Oid = match.Groups[1].Value, Size = long.Parse(match.Groups[2].Value) };
+
+ return null;
+ }
}
}
diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs
index d5e1534c..1788a9b2 100644
--- a/src/Models/Locales.cs
+++ b/src/Models/Locales.cs
@@ -14,9 +14,12 @@ namespace SourceGit.Models
new Locale("Français", "fr_FR"),
new Locale("Italiano", "it_IT"),
new Locale("Português (Brasil)", "pt_BR"),
+ new Locale("Українська", "uk_UA"),
new Locale("Русский", "ru_RU"),
new Locale("简体中文", "zh_CN"),
new Locale("繁體中文", "zh_TW"),
+ new Locale("日本語", "ja_JP"),
+ new Locale("தமிழ் (Tamil)", "ta_IN"),
};
public Locale(string name, string key)
diff --git a/src/Models/MergeMode.cs b/src/Models/MergeMode.cs
index 15e3f7e9..5dc70030 100644
--- a/src/Models/MergeMode.cs
+++ b/src/Models/MergeMode.cs
@@ -5,8 +5,9 @@
public static readonly MergeMode[] Supported =
[
new MergeMode("Default", "Fast-forward if possible", ""),
+ new MergeMode("Fast-forward", "Refuse to merge when fast-forward is not possible", "--ff-only"),
new MergeMode("No Fast-forward", "Always create a merge commit", "--no-ff"),
- new MergeMode("Squash", "Use '--squash'", "--squash"),
+ new MergeMode("Squash", "Squash merge", "--squash"),
new MergeMode("Don't commit", "Merge without commit", "--no-ff --no-commit"),
];
diff --git a/src/Models/Null.cs b/src/Models/Null.cs
index e22ef8b3..1820d4d0 100644
--- a/src/Models/Null.cs
+++ b/src/Models/Null.cs
@@ -1,6 +1,4 @@
namespace SourceGit.Models
{
- public class Null
- {
- }
+ public class Null;
}
diff --git a/src/Models/NumericSort.cs b/src/Models/NumericSort.cs
index ed5002e6..baaf3da4 100644
--- a/src/Models/NumericSort.cs
+++ b/src/Models/NumericSort.cs
@@ -1,4 +1,6 @@
-namespace SourceGit.Models
+using System;
+
+namespace SourceGit.Models
{
public static class NumericSort
{
@@ -10,52 +12,35 @@
int marker1 = 0;
int marker2 = 0;
- char[] tmp1 = new char[len1];
- char[] tmp2 = new char[len2];
-
while (marker1 < len1 && marker2 < len2)
{
char c1 = s1[marker1];
char c2 = s2[marker2];
- int loc1 = 0;
- int loc2 = 0;
bool isDigit1 = char.IsDigit(c1);
bool isDigit2 = char.IsDigit(c2);
if (isDigit1 != isDigit2)
return c1.CompareTo(c2);
- do
- {
- tmp1[loc1] = c1;
- loc1++;
- marker1++;
+ int subLen1 = 1;
+ while (marker1 + subLen1 < len1 && char.IsDigit(s1[marker1 + subLen1]) == isDigit1)
+ subLen1++;
- if (marker1 < len1)
- c1 = s1[marker1];
- else
- break;
- } while (char.IsDigit(c1) == isDigit1);
+ int subLen2 = 1;
+ while (marker2 + subLen2 < len2 && char.IsDigit(s2[marker2 + subLen2]) == isDigit2)
+ subLen2++;
- do
- {
- tmp2[loc2] = c2;
- loc2++;
- marker2++;
+ string sub1 = s1.Substring(marker1, subLen1);
+ string sub2 = s2.Substring(marker2, subLen2);
- if (marker2 < len2)
- c2 = s2[marker2];
- else
- break;
- } while (char.IsDigit(c2) == isDigit2);
+ marker1 += subLen1;
+ marker2 += subLen2;
- string sub1 = new string(tmp1, 0, loc1);
- string sub2 = new string(tmp2, 0, loc2);
int result;
if (isDigit1)
- result = loc1 == loc2 ? string.CompareOrdinal(sub1, sub2) : loc1 - loc2;
+ result = (subLen1 == subLen2) ? string.CompareOrdinal(sub1, sub2) : (subLen1 - subLen2);
else
- result = string.CompareOrdinal(sub1, sub2);
+ result = string.Compare(sub1, sub2, StringComparison.OrdinalIgnoreCase);
if (result != 0)
return result;
diff --git a/src/Models/OpenAI.cs b/src/Models/OpenAI.cs
index 76daacd0..22fbcd51 100644
--- a/src/Models/OpenAI.cs
+++ b/src/Models/OpenAI.cs
@@ -107,15 +107,7 @@ namespace SourceGit.Models
public string Server
{
get => _server;
- set
- {
- // migrate old server value
- if (!string.IsNullOrEmpty(value) && value.EndsWith("/chat/completions", StringComparison.Ordinal))
- {
- value = value.Substring(0, value.Length - "/chat/completions".Length);
- }
- SetProperty(ref _server, value);
- }
+ set => SetProperty(ref _server, value);
}
public string ApiKey
diff --git a/src/Models/Remote.cs b/src/Models/Remote.cs
index ec9b8f20..6e36cfb9 100644
--- a/src/Models/Remote.cs
+++ b/src/Models/Remote.cs
@@ -6,8 +6,10 @@ namespace SourceGit.Models
{
public partial class Remote
{
- [GeneratedRegex(@"^https?://([-a-zA-Z0-9:%._\+~#=]+@)?[-a-zA-Z0-9:%._\+~#=]{1,256}(\.[a-zA-Z0-9()]{1,6})?(:[0-9]{1,5})?\b(/[-a-zA-Z0-9()@:%_\+.~#?&=]+)+(\.git)?$")]
+ [GeneratedRegex(@"^https?://[^/]+/.+[^/\.]$")]
private static partial Regex REG_HTTPS();
+ [GeneratedRegex(@"^git://[^/]+/.+[^/\.]$")]
+ private static partial Regex REG_GIT();
[GeneratedRegex(@"^[\w\-]+@[\w\.\-]+(\:[0-9]+)?:([a-zA-z0-9~%][\w\-\./~%]*)?[a-zA-Z0-9](\.git)?$")]
private static partial Regex REG_SSH1();
[GeneratedRegex(@"^ssh://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/([a-zA-z0-9~%][\w\-\./~%]*)?[a-zA-Z0-9](\.git)?$")]
@@ -18,6 +20,7 @@ namespace SourceGit.Models
private static readonly Regex[] URL_FORMATS = [
REG_HTTPS(),
+ REG_GIT(),
REG_SSH1(),
REG_SSH2(),
];
@@ -30,13 +33,10 @@ namespace SourceGit.Models
if (string.IsNullOrWhiteSpace(url))
return false;
- for (int i = 1; i < URL_FORMATS.Length; i++)
- {
- if (URL_FORMATS[i].IsMatch(url))
- return true;
- }
+ if (REG_SSH1().IsMatch(url))
+ return true;
- return false;
+ return REG_SSH2().IsMatch(url);
}
public static bool IsValidURL(string url)
@@ -50,7 +50,10 @@ namespace SourceGit.Models
return true;
}
- return url.EndsWith(".git", StringComparison.Ordinal) && Directory.Exists(url);
+ return url.StartsWith("file://", StringComparison.Ordinal) ||
+ url.StartsWith("./", StringComparison.Ordinal) ||
+ url.StartsWith("../", StringComparison.Ordinal) ||
+ Directory.Exists(url);
}
public bool TryGetVisitURL(out string url)
diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs
index 76a7f160..4e51b368 100644
--- a/src/Models/RepositorySettings.cs
+++ b/src/Models/RepositorySettings.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text;
@@ -32,12 +32,24 @@ namespace SourceGit.Models
set;
} = false;
- public bool OnlyHighlighCurrentBranchInHistories
+ public bool OnlyHighlightCurrentBranchInHistories
{
get;
set;
} = false;
+ public BranchSortMode LocalBranchSortMode
+ {
+ get;
+ set;
+ } = BranchSortMode.Name;
+
+ public BranchSortMode RemoteBranchSortMode
+ {
+ get;
+ set;
+ } = BranchSortMode.Name;
+
public TagSortMode TagSortMode
{
get;
@@ -68,18 +80,6 @@ namespace SourceGit.Models
set;
} = true;
- public bool FetchWithoutTagsOnPull
- {
- get;
- set;
- } = false;
-
- public bool FetchAllBranchesOnPull
- {
- get;
- set;
- } = true;
-
public bool CheckSubmodulesOnPush
{
get;
@@ -110,6 +110,12 @@ namespace SourceGit.Models
set;
} = true;
+ public bool UpdateSubmodulesOnCheckoutBranch
+ {
+ get;
+ set;
+ } = true;
+
public AvaloniaList HistoriesFilters
{
get;
@@ -170,19 +176,13 @@ namespace SourceGit.Models
set;
} = false;
- public bool KeepIndexWhenStash
+ public int ChangesAfterStashing
{
get;
set;
- } = false;
+ } = 0;
- public bool AutoRestoreAfterStash
- {
- get;
- set;
- } = false;
-
- public string PreferedOpenAIService
+ public string PreferredOpenAIService
{
get;
set;
@@ -224,6 +224,18 @@ namespace SourceGit.Models
set;
} = [];
+ public int PreferredMergeMode
+ {
+ get;
+ set;
+ } = 0;
+
+ public string LastCommitMessage
+ {
+ get;
+ set;
+ } = string.Empty;
+
public Dictionary CollectHistoriesFilters()
{
var map = new Dictionary();
@@ -269,9 +281,8 @@ namespace SourceGit.Models
return false;
}
- for (int i = 0; i < HistoriesFilters.Count; i++)
+ foreach (var filter in HistoriesFilters)
{
- var filter = HistoriesFilters[i];
if (filter.Type != type)
continue;
@@ -303,128 +314,81 @@ namespace SourceGit.Models
public string BuildHistoriesFilter()
{
+ var includedRefs = new List();
var excludedBranches = new List();
var excludedRemotes = new List();
var excludedTags = new List();
- var includedBranches = new List();
- var includedRemotes = new List();
- var includedTags = new List();
foreach (var filter in HistoriesFilters)
{
if (filter.Type == FilterType.LocalBranch)
{
- var name = filter.Pattern.Substring(11);
- var b = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
-
if (filter.Mode == FilterMode.Included)
- includedBranches.Add(b);
+ includedRefs.Add(filter.Pattern);
else if (filter.Mode == FilterMode.Excluded)
- excludedBranches.Add(b);
+ excludedBranches.Add($"--exclude=\"{filter.Pattern.AsSpan(11)}\" --decorate-refs-exclude=\"{filter.Pattern}\"");
}
else if (filter.Type == FilterType.LocalBranchFolder)
{
if (filter.Mode == FilterMode.Included)
- includedBranches.Add($"{filter.Pattern.Substring(11)}/*");
+ includedRefs.Add($"--branches={filter.Pattern.AsSpan(11)}/*");
else if (filter.Mode == FilterMode.Excluded)
- excludedBranches.Add($"{filter.Pattern.Substring(11)}/*");
+ excludedBranches.Add($"--exclude=\"{filter.Pattern.AsSpan(11)}/*\" --decorate-refs-exclude=\"{filter.Pattern}/*\"");
}
else if (filter.Type == FilterType.RemoteBranch)
{
- var name = filter.Pattern.Substring(13);
- var r = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
-
if (filter.Mode == FilterMode.Included)
- includedRemotes.Add(r);
+ includedRefs.Add(filter.Pattern);
else if (filter.Mode == FilterMode.Excluded)
- excludedRemotes.Add(r);
+ excludedRemotes.Add($"--exclude=\"{filter.Pattern.AsSpan(13)}\" --decorate-refs-exclude=\"{filter.Pattern}\"");
}
else if (filter.Type == FilterType.RemoteBranchFolder)
{
if (filter.Mode == FilterMode.Included)
- includedRemotes.Add($"{filter.Pattern.Substring(13)}/*");
+ includedRefs.Add($"--remotes={filter.Pattern.AsSpan(13)}/*");
else if (filter.Mode == FilterMode.Excluded)
- excludedRemotes.Add($"{filter.Pattern.Substring(13)}/*");
+ excludedRemotes.Add($"--exclude=\"{filter.Pattern.AsSpan(13)}/*\" --decorate-refs-exclude=\"{filter.Pattern}/*\"");
}
else if (filter.Type == FilterType.Tag)
{
- var name = filter.Pattern;
- var t = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
-
if (filter.Mode == FilterMode.Included)
- includedTags.Add(t);
+ includedRefs.Add($"refs/tags/{filter.Pattern}");
else if (filter.Mode == FilterMode.Excluded)
- excludedTags.Add(t);
+ excludedTags.Add($"--exclude=\"{filter.Pattern}\" --decorate-refs-exclude=\"refs/tags/{filter.Pattern}\"");
}
}
- bool hasIncluded = includedBranches.Count > 0 || includedRemotes.Count > 0 || includedTags.Count > 0;
- bool hasExcluded = excludedBranches.Count > 0 || excludedRemotes.Count > 0 || excludedTags.Count > 0;
-
var builder = new StringBuilder();
- if (hasIncluded)
+ if (includedRefs.Count > 0)
{
- foreach (var b in includedBranches)
+ foreach (var r in includedRefs)
+ {
+ builder.Append(r);
+ builder.Append(' ');
+ }
+ }
+ else if (excludedBranches.Count + excludedRemotes.Count + excludedTags.Count > 0)
+ {
+ foreach (var b in excludedBranches)
{
- builder.Append("--branches=");
builder.Append(b);
builder.Append(' ');
}
- foreach (var r in includedRemotes)
+ builder.Append("--exclude=HEAD --branches ");
+
+ foreach (var r in excludedRemotes)
{
- builder.Append("--remotes=");
builder.Append(r);
builder.Append(' ');
}
- foreach (var t in includedTags)
+ builder.Append("--exclude=origin/HEAD --remotes ");
+
+ foreach (var t in excludedTags)
{
- builder.Append("--tags=");
builder.Append(t);
builder.Append(' ');
}
- }
- else if (hasExcluded)
- {
- if (excludedBranches.Count > 0)
- {
- foreach (var b in excludedBranches)
- {
- builder.Append("--exclude=");
- builder.Append(b);
- builder.Append(" --decorate-refs-exclude=refs/heads/");
- builder.Append(b);
- builder.Append(' ');
- }
- }
-
- builder.Append("--exclude=HEA[D] --branches ");
-
- if (excludedRemotes.Count > 0)
- {
- foreach (var r in excludedRemotes)
- {
- builder.Append("--exclude=");
- builder.Append(r);
- builder.Append(" --decorate-refs-exclude=refs/remotes/");
- builder.Append(r);
- builder.Append(' ');
- }
- }
-
- builder.Append("--exclude=origin/HEA[D] --remotes ");
-
- if (excludedTags.Count > 0)
- {
- foreach (var t in excludedTags)
- {
- builder.Append("--exclude=");
- builder.Append(t);
- builder.Append(" --decorate-refs-exclude=refs/tags/");
- builder.Append(t);
- builder.Append(' ');
- }
- }
builder.Append("--tags ");
}
@@ -434,6 +398,7 @@ namespace SourceGit.Models
public void PushCommitMessage(string message)
{
+ message = message.Trim().ReplaceLineEndings("\n");
var existIdx = CommitMessages.IndexOf(message);
if (existIdx == 0)
return;
@@ -481,5 +446,19 @@ namespace SourceGit.Models
if (act != null)
CustomActions.Remove(act);
}
+
+ public void MoveCustomActionUp(CustomAction act)
+ {
+ var idx = CustomActions.IndexOf(act);
+ if (idx > 0)
+ CustomActions.Move(idx - 1, idx);
+ }
+
+ public void MoveCustomActionDown(CustomAction act)
+ {
+ var idx = CustomActions.IndexOf(act);
+ if (idx < CustomActions.Count - 1)
+ CustomActions.Move(idx + 1, idx);
+ }
}
}
diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs
index 8cc1be2a..29a23efa 100644
--- a/src/Models/RevisionFile.cs
+++ b/src/Models/RevisionFile.cs
@@ -1,4 +1,6 @@
-using Avalonia.Media.Imaging;
+using System.Globalization;
+using System.IO;
+using Avalonia.Media.Imaging;
namespace SourceGit.Models
{
@@ -9,10 +11,17 @@ namespace SourceGit.Models
public class RevisionImageFile
{
- public Bitmap Image { get; set; } = null;
- public long FileSize { get; set; } = 0;
- public string ImageType { get; set; } = string.Empty;
+ public Bitmap Image { get; }
+ public long FileSize { get; }
+ public string ImageType { get; }
public string ImageSize => Image != null ? $"{Image.PixelSize.Width} x {Image.PixelSize.Height}" : "0 x 0";
+
+ public RevisionImageFile(string file, Bitmap img, long size)
+ {
+ Image = img;
+ FileSize = size;
+ ImageType = Path.GetExtension(file)!.Substring(1).ToUpper(CultureInfo.CurrentCulture);
+ }
}
public class RevisionTextFile
diff --git a/src/Models/Version.cs b/src/Models/SelfUpdate.cs
similarity index 66%
rename from src/Models/Version.cs
rename to src/Models/SelfUpdate.cs
index 35c21778..05fa6124 100644
--- a/src/Models/Version.cs
+++ b/src/Models/SelfUpdate.cs
@@ -1,4 +1,5 @@
-using System.Reflection;
+using System;
+using System.Reflection;
using System.Text.Json.Serialization;
namespace SourceGit.Models
@@ -32,5 +33,22 @@ namespace SourceGit.Models
}
}
- public class AlreadyUpToDate { }
+ public class AlreadyUpToDate;
+
+ public class SelfUpdateFailed
+ {
+ public string Reason
+ {
+ get;
+ private set;
+ }
+
+ public SelfUpdateFailed(Exception e)
+ {
+ if (e.InnerException is { } inner)
+ Reason = inner.Message;
+ else
+ Reason = e.Message;
+ }
+ }
}
diff --git a/src/Models/ShellOrTerminal.cs b/src/Models/ShellOrTerminal.cs
index 3ada2cf9..7dfb2237 100644
--- a/src/Models/ShellOrTerminal.cs
+++ b/src/Models/ShellOrTerminal.cs
@@ -42,7 +42,8 @@ namespace SourceGit.Models
new ShellOrTerminal("mac-terminal", "Terminal", ""),
new ShellOrTerminal("iterm2", "iTerm", ""),
new ShellOrTerminal("warp", "Warp", ""),
- new ShellOrTerminal("ghostty", "Ghostty", "")
+ new ShellOrTerminal("ghostty", "Ghostty", ""),
+ new ShellOrTerminal("kitty", "kitty", "")
};
}
else
@@ -58,6 +59,7 @@ namespace SourceGit.Models
new ShellOrTerminal("foot", "Foot", "foot"),
new ShellOrTerminal("wezterm", "WezTerm", "wezterm"),
new ShellOrTerminal("ptyxis", "Ptyxis", "ptyxis"),
+ new ShellOrTerminal("kitty", "kitty", "kitty"),
new ShellOrTerminal("custom", "Custom", ""),
};
}
diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs
index 8dca3bdb..2b77be50 100644
--- a/src/Models/Stash.cs
+++ b/src/Models/Stash.cs
@@ -10,8 +10,25 @@ namespace SourceGit.Models
public List Parents { get; set; } = [];
public ulong Time { get; set; } = 0;
public string Message { get; set; } = "";
- public bool HasUntracked => Parents.Count == 3;
- public string TimeStr => DateTime.UnixEpoch.AddSeconds(Time).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime);
+ public string Subject
+ {
+ get
+ {
+ var idx = Message.IndexOf('\n', StringComparison.Ordinal);
+ return idx > 0 ? Message.Substring(0, idx).Trim() : Message;
+ }
+ }
+
+ public string TimeStr
+ {
+ get
+ {
+ return DateTime.UnixEpoch
+ .AddSeconds(Time)
+ .ToLocalTime()
+ .ToString(DateTimeFormat.Active.DateTime);
+ }
+ }
}
}
diff --git a/src/Models/Statistics.cs b/src/Models/Statistics.cs
index 969d3945..a86380c3 100644
--- a/src/Models/Statistics.cs
+++ b/src/Models/Statistics.cs
@@ -11,54 +11,47 @@ using SkiaSharp;
namespace SourceGit.Models
{
- public enum StaticsticsMode
+ public enum StatisticsMode
{
All,
ThisMonth,
ThisWeek,
}
- public class StaticsticsAuthor(string name, int count)
+ public class StatisticsAuthor(User user, int count)
{
- public string Name { get; set; } = name;
- public int Count { get; set; } = count;
- }
-
- public class StaticsticsSample(DateTime time, int count)
- {
- public DateTime Time { get; set; } = time;
+ public User User { get; set; } = user;
public int Count { get; set; } = count;
}
public class StatisticsReport
{
- public static readonly string[] WEEKDAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
-
public int Total { get; set; } = 0;
- public List Authors { get; set; } = new List();
- public List Series { get; set; } = new List();
- public List XAxes { get; set; } = new List();
- public List YAxes { get; set; } = new List();
+ public List Authors { get; set; } = new();
+ public List Series { get; set; } = new();
+ public List XAxes { get; set; } = new();
+ public List YAxes { get; set; } = new();
+ public StatisticsAuthor SelectedAuthor { get => _selectedAuthor; set => ChangeAuthor(value); }
- public StatisticsReport(StaticsticsMode mode, DateTime start)
+ public StatisticsReport(StatisticsMode mode, DateTime start)
{
_mode = mode;
- YAxes = [new Axis()
+ YAxes.Add(new Axis()
{
TextSize = 10,
MinLimit = 0,
SeparatorsPaint = new SolidColorPaint(new SKColor(0x40808080)) { StrokeThickness = 1 }
- }];
+ });
- if (mode == StaticsticsMode.ThisWeek)
+ if (mode == StatisticsMode.ThisWeek)
{
for (int i = 0; i < 7; i++)
_mapSamples.Add(start.AddDays(i), 0);
XAxes.Add(new DateTimeAxis(TimeSpan.FromDays(1), v => WEEKDAYS[(int)v.DayOfWeek]) { TextSize = 10 });
}
- else if (mode == StaticsticsMode.ThisMonth)
+ else if (mode == StatisticsMode.ThisMonth)
{
var now = DateTime.Now;
var maxDays = DateTime.DaysInMonth(now.Year, now.Month);
@@ -73,12 +66,12 @@ namespace SourceGit.Models
}
}
- public void AddCommit(DateTime time, string author)
+ public void AddCommit(DateTime time, User author)
{
Total++;
- var normalized = DateTime.MinValue;
- if (_mode == StaticsticsMode.ThisWeek || _mode == StaticsticsMode.ThisMonth)
+ DateTime normalized;
+ if (_mode == StatisticsMode.ThisWeek || _mode == StatisticsMode.ThisMonth)
normalized = time.Date;
else
normalized = new DateTime(time.Year, time.Month, 1).ToLocalTime();
@@ -92,10 +85,30 @@ namespace SourceGit.Models
_mapUsers[author] = vu + 1;
else
_mapUsers.Add(author, 1);
+
+ if (_mapUserSamples.TryGetValue(author, out var vus))
+ {
+ if (vus.TryGetValue(normalized, out var n))
+ vus[normalized] = n + 1;
+ else
+ vus.Add(normalized, 1);
+ }
+ else
+ {
+ _mapUserSamples.Add(author, new Dictionary
+ {
+ { normalized, 1 }
+ });
+ }
}
public void Complete()
{
+ foreach (var kv in _mapUsers)
+ Authors.Add(new StatisticsAuthor(kv.Key, kv.Value));
+
+ Authors.Sort((l, r) => r.Count - l.Count);
+
var samples = new List();
foreach (var kv in _mapSamples)
samples.Add(new DateTimePoint(kv.Key, kv.Value));
@@ -110,65 +123,111 @@ namespace SourceGit.Models
}
);
- foreach (var kv in _mapUsers)
- Authors.Add(new StaticsticsAuthor(kv.Key, kv.Value));
-
- Authors.Sort((l, r) => r.Count - l.Count);
-
_mapUsers.Clear();
_mapSamples.Clear();
}
public void ChangeColor(uint color)
{
- if (Series is [ColumnSeries series])
- series.Fill = new SolidColorPaint(new SKColor(color));
+ _fillColor = color;
+
+ var fill = new SKColor(color);
+
+ if (Series.Count > 0 && Series[0] is ColumnSeries total)
+ total.Fill = new SolidColorPaint(_selectedAuthor == null ? fill : fill.WithAlpha(51));
+
+ if (Series.Count > 1 && Series[1] is ColumnSeries user)
+ user.Fill = new SolidColorPaint(fill);
}
- private StaticsticsMode _mode = StaticsticsMode.All;
- private Dictionary _mapUsers = new Dictionary();
- private Dictionary _mapSamples = new Dictionary();
+ public void ChangeAuthor(StatisticsAuthor author)
+ {
+ if (author == _selectedAuthor)
+ return;
+
+ _selectedAuthor = author;
+ Series.RemoveRange(1, Series.Count - 1);
+ if (author == null || !_mapUserSamples.TryGetValue(author.User, out var userSamples))
+ {
+ ChangeColor(_fillColor);
+ return;
+ }
+
+ var samples = new List();
+ foreach (var kv in userSamples)
+ samples.Add(new DateTimePoint(kv.Key, kv.Value));
+
+ Series.Add(
+ new ColumnSeries()
+ {
+ Values = samples,
+ Stroke = null,
+ Fill = null,
+ Padding = 1,
+ }
+ );
+
+ ChangeColor(_fillColor);
+ }
+
+ private static readonly string[] WEEKDAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
+ private StatisticsMode _mode;
+ private Dictionary _mapUsers = new();
+ private Dictionary _mapSamples = new();
+ private Dictionary> _mapUserSamples = new();
+ private StatisticsAuthor _selectedAuthor = null;
+ private uint _fillColor = 255;
}
public class Statistics
{
- public StatisticsReport All { get; set; }
- public StatisticsReport Month { get; set; }
- public StatisticsReport Week { get; set; }
+ public StatisticsReport All { get; }
+ public StatisticsReport Month { get; }
+ public StatisticsReport Week { get; }
public Statistics()
{
- _today = DateTime.Now.ToLocalTime().Date;
- var weekOffset = (7 + (int)_today.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) % 7;
- _thisWeekStart = _today.AddDays(-weekOffset);
- _thisMonthStart = _today.AddDays(1 - _today.Day);
+ var today = DateTime.Now.ToLocalTime().Date;
+ var weekOffset = (7 + (int)today.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) % 7;
+ _thisWeekStart = today.AddDays(-weekOffset);
+ _thisMonthStart = today.AddDays(1 - today.Day);
- All = new StatisticsReport(StaticsticsMode.All, DateTime.MinValue);
- Month = new StatisticsReport(StaticsticsMode.ThisMonth, _thisMonthStart);
- Week = new StatisticsReport(StaticsticsMode.ThisWeek, _thisWeekStart);
+ All = new StatisticsReport(StatisticsMode.All, DateTime.MinValue);
+ Month = new StatisticsReport(StatisticsMode.ThisMonth, _thisMonthStart);
+ Week = new StatisticsReport(StatisticsMode.ThisWeek, _thisWeekStart);
}
public void AddCommit(string author, double timestamp)
{
+ var emailIdx = author.IndexOf('±', StringComparison.Ordinal);
+ var email = author.Substring(emailIdx + 1).ToLower(CultureInfo.CurrentCulture);
+ if (!_users.TryGetValue(email, out var user))
+ {
+ user = User.FindOrAdd(author);
+ _users.Add(email, user);
+ }
+
var time = DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime();
if (time >= _thisWeekStart)
- Week.AddCommit(time, author);
+ Week.AddCommit(time, user);
if (time >= _thisMonthStart)
- Month.AddCommit(time, author);
+ Month.AddCommit(time, user);
- All.AddCommit(time, author);
+ All.AddCommit(time, user);
}
public void Complete()
{
+ _users.Clear();
+
All.Complete();
Month.Complete();
Week.Complete();
}
- private readonly DateTime _today;
private readonly DateTime _thisMonthStart;
private readonly DateTime _thisWeekStart;
+ private readonly Dictionary _users = new();
}
}
diff --git a/src/Models/Submodule.cs b/src/Models/Submodule.cs
index ce00ac02..ca73a8de 100644
--- a/src/Models/Submodule.cs
+++ b/src/Models/Submodule.cs
@@ -1,8 +1,20 @@
namespace SourceGit.Models
{
+ public enum SubmoduleStatus
+ {
+ Normal = 0,
+ NotInited,
+ RevisionChanged,
+ Unmerged,
+ Modified,
+ }
+
public class Submodule
{
- public string Path { get; set; } = "";
- public bool IsDirty { get; set; } = false;
+ public string Path { get; set; } = string.Empty;
+ public string SHA { get; set; } = string.Empty;
+ public string URL { get; set; } = string.Empty;
+ public SubmoduleStatus Status { get; set; } = SubmoduleStatus.Normal;
+ public bool IsDirty => Status > SubmoduleStatus.NotInited;
}
}
diff --git a/src/Models/Tag.cs b/src/Models/Tag.cs
index 51681d93..87944637 100644
--- a/src/Models/Tag.cs
+++ b/src/Models/Tag.cs
@@ -5,13 +5,13 @@ namespace SourceGit.Models
public enum TagSortMode
{
CreatorDate = 0,
- NameInAscending,
- NameInDescending,
+ Name,
}
public class Tag : ObservableObject
{
public string Name { get; set; } = string.Empty;
+ public bool IsAnnotated { get; set; } = false;
public string SHA { get; set; } = string.Empty;
public ulong CreatorDate { get; set; } = 0;
public string Message { get; set; } = string.Empty;
diff --git a/src/Models/TemplateEngine.cs b/src/Models/TemplateEngine.cs
index 8472750c..8f60bd74 100644
--- a/src/Models/TemplateEngine.cs
+++ b/src/Models/TemplateEngine.cs
@@ -102,7 +102,7 @@ namespace SourceGit.Models
private int? Integer()
{
var start = _pos;
- while (Peek() is char c && c >= '0' && c <= '9')
+ while (Peek() is >= '0' and <= '9')
{
_pos++;
}
@@ -118,7 +118,7 @@ namespace SourceGit.Models
// text token start
var tok = _pos;
bool esc = false;
- while (Next() is char c)
+ while (Next() is { } c)
{
if (esc)
{
@@ -129,7 +129,7 @@ namespace SourceGit.Models
{
case ESCAPE:
// allow to escape only \ and $
- if (Peek() is char nc && (nc == ESCAPE || nc == VARIABLE_ANCHOR))
+ if (Peek() is { } nc && (nc == ESCAPE || nc == VARIABLE_ANCHOR))
{
esc = true;
FlushText(tok, _pos - 1);
@@ -173,7 +173,7 @@ namespace SourceGit.Models
if (Next() != VARIABLE_START)
return null;
int name_start = _pos;
- while (Next() is char c)
+ while (Next() is { } c)
{
// name character, continue advancing
if (IsNameChar(c))
@@ -228,7 +228,7 @@ namespace SourceGit.Models
var sb = new StringBuilder();
var tok = _pos;
var esc = false;
- while (Next() is char c)
+ while (Next() is { } c)
{
if (esc)
{
@@ -277,7 +277,7 @@ namespace SourceGit.Models
var sb = new StringBuilder();
var tok = _pos;
var esc = false;
- while (Next() is char c)
+ while (Next() is { } c)
{
if (esc)
{
@@ -402,7 +402,7 @@ namespace SourceGit.Models
sb.AppendJoin(", ", paths);
if (max < context.changes.Count)
- sb.AppendFormat(" and {0} other files", context.changes.Count - max);
+ sb.Append($" and {context.changes.Count - max} other files");
return sb.ToString();
}
diff --git a/src/Models/TextInlineChange.cs b/src/Models/TextInlineChange.cs
index c96d839f..afe5bec4 100644
--- a/src/Models/TextInlineChange.cs
+++ b/src/Models/TextInlineChange.cs
@@ -2,30 +2,22 @@
namespace SourceGit.Models
{
- public class TextInlineChange
+ public class TextInlineChange(int dp, int dc, int ap, int ac)
{
- public int DeletedStart { get; set; }
- public int DeletedCount { get; set; }
- public int AddedStart { get; set; }
- public int AddedCount { get; set; }
+ public int DeletedStart { get; set; } = dp;
+ public int DeletedCount { get; set; } = dc;
+ public int AddedStart { get; set; } = ap;
+ public int AddedCount { get; set; } = ac;
- class Chunk
+ private class Chunk(int hash, int start, int size)
{
- public int Hash;
+ public readonly int Hash = hash;
+ public readonly int Start = start;
+ public readonly int Size = size;
public bool Modified;
- public int Start;
- public int Size;
-
- public Chunk(int hash, int start, int size)
- {
- Hash = hash;
- Modified = false;
- Start = start;
- Size = size;
- }
}
- enum Edit
+ private enum Edit
{
None,
DeletedRight,
@@ -34,7 +26,7 @@ namespace SourceGit.Models
AddedLeft,
}
- class EditResult
+ private class EditResult
{
public Edit State;
public int DeleteStart;
@@ -43,14 +35,6 @@ namespace SourceGit.Models
public int AddEnd;
}
- public TextInlineChange(int dp, int dc, int ap, int ac)
- {
- DeletedStart = dp;
- DeletedCount = dc;
- AddedStart = ap;
- AddedCount = ac;
- }
-
public static List Compare(string oldValue, string newValue)
{
var hashes = new Dictionary();
@@ -204,11 +188,10 @@ namespace SourceGit.Models
for (int i = 0; i <= half; i++)
{
-
for (int j = -i; j <= i; j += 2)
{
var idx = j + half;
- int o, n;
+ int o;
if (j == -i || (j != i && forward[idx - 1] < forward[idx + 1]))
{
o = forward[idx + 1];
@@ -220,7 +203,7 @@ namespace SourceGit.Models
rs.State = Edit.DeletedRight;
}
- n = o - j;
+ var n = o - j;
var startX = o;
var startY = n;
@@ -258,7 +241,7 @@ namespace SourceGit.Models
for (int j = -i; j <= i; j += 2)
{
var idx = j + half;
- int o, n;
+ int o;
if (j == -i || (j != i && reverse[idx + 1] <= reverse[idx - 1]))
{
o = reverse[idx + 1] - 1;
@@ -270,7 +253,7 @@ namespace SourceGit.Models
rs.State = Edit.AddedLeft;
}
- n = o - (j + delta);
+ var n = o - (j + delta);
var endX = o;
var endY = n;
@@ -312,8 +295,7 @@ namespace SourceGit.Models
private static void AddChunk(List chunks, Dictionary hashes, string data, int start)
{
- int hash;
- if (hashes.TryGetValue(data, out hash))
+ if (hashes.TryGetValue(data, out var hash))
{
chunks.Add(new Chunk(hash, start, data.Length));
}
diff --git a/src/Models/TextMateHelper.cs b/src/Models/TextMateHelper.cs
index 0eb5e489..b7efae72 100644
--- a/src/Models/TextMateHelper.cs
+++ b/src/Models/TextMateHelper.cs
@@ -21,10 +21,11 @@ namespace SourceGit.Models
{
private static readonly ExtraGrammar[] s_extraGrammars =
[
- new ExtraGrammar("source.toml", ".toml", "toml.json"),
- new ExtraGrammar("source.kotlin", ".kotlin", "kotlin.json"),
- new ExtraGrammar("source.hx", ".hx", "haxe.json"),
- new ExtraGrammar("source.hxml", ".hxml", "hxml.json"),
+ new ExtraGrammar("source.toml", [".toml"], "toml.json"),
+ new ExtraGrammar("source.kotlin", [".kotlin", ".kt", ".kts"], "kotlin.json"),
+ new ExtraGrammar("source.hx", [".hx"], "haxe.json"),
+ new ExtraGrammar("source.hxml", [".hxml"], "hxml.json"),
+ new ExtraGrammar("text.html.jsp", [".jsp", ".jspf", ".tag"], "jsp.json"),
];
public static string GetScope(string file, RegistryOptions reg)
@@ -36,13 +37,14 @@ namespace SourceGit.Models
extension = ".xml";
else if (extension == ".command")
extension = ".sh";
- else if (extension == ".kt" || extension == ".kts")
- extension = ".kotlin";
foreach (var grammar in s_extraGrammars)
{
- if (grammar.Extension.Equals(extension, StringComparison.OrdinalIgnoreCase))
- return grammar.Scope;
+ foreach (var ext in grammar.Extensions)
+ {
+ if (ext.Equals(extension, StringComparison.OrdinalIgnoreCase))
+ return grammar.Scope;
+ }
}
return reg.GetScopeByExtension(extension);
@@ -71,10 +73,10 @@ namespace SourceGit.Models
return reg.GetGrammar(scopeName);
}
- private record ExtraGrammar(string Scope, string Extension, string File)
+ private record ExtraGrammar(string Scope, List Extensions, string File)
{
public readonly string Scope = Scope;
- public readonly string Extension = Extension;
+ public readonly List Extensions = Extensions;
public readonly string File = File;
}
}
diff --git a/src/Models/User.cs b/src/Models/User.cs
index 066ab747..0b4816fe 100644
--- a/src/Models/User.cs
+++ b/src/Models/User.cs
@@ -26,11 +26,7 @@ namespace SourceGit.Models
public override bool Equals(object obj)
{
- if (obj == null || !(obj is User))
- return false;
-
- var other = obj as User;
- return Name == other.Name && Email == other.Email;
+ return obj is User other && Name == other.Name && Email == other.Email;
}
public override int GetHashCode()
diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs
index d0ccdea5..4d6656e3 100644
--- a/src/Models/Watcher.cs
+++ b/src/Models/Watcher.cs
@@ -8,38 +8,54 @@ namespace SourceGit.Models
{
public class Watcher : IDisposable
{
- public Watcher(IRepository repo)
+ public Watcher(IRepository repo, string fullpath, string gitDir)
{
_repo = repo;
- _wcWatcher = new FileSystemWatcher();
- _wcWatcher.Path = _repo.FullPath;
- _wcWatcher.Filter = "*";
- _wcWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.CreationTime;
- _wcWatcher.IncludeSubdirectories = true;
- _wcWatcher.Created += OnWorkingCopyChanged;
- _wcWatcher.Renamed += OnWorkingCopyChanged;
- _wcWatcher.Changed += OnWorkingCopyChanged;
- _wcWatcher.Deleted += OnWorkingCopyChanged;
- _wcWatcher.EnableRaisingEvents = true;
+ var testGitDir = new DirectoryInfo(Path.Combine(fullpath, ".git")).FullName;
+ var desiredDir = new DirectoryInfo(gitDir).FullName;
+ if (testGitDir.Equals(desiredDir, StringComparison.Ordinal))
+ {
+ var combined = new FileSystemWatcher();
+ combined.Path = fullpath;
+ combined.Filter = "*";
+ combined.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.CreationTime;
+ combined.IncludeSubdirectories = true;
+ combined.Created += OnRepositoryChanged;
+ combined.Renamed += OnRepositoryChanged;
+ combined.Changed += OnRepositoryChanged;
+ combined.Deleted += OnRepositoryChanged;
+ combined.EnableRaisingEvents = true;
- // If this repository is a worktree repository, just watch the main repository's gitdir.
- var gitDirNormalized = _repo.GitDir.Replace("\\", "/");
- var worktreeIdx = gitDirNormalized.IndexOf(".git/worktrees/", StringComparison.Ordinal);
- var repoWatchDir = _repo.GitDir;
- if (worktreeIdx > 0)
- repoWatchDir = _repo.GitDir.Substring(0, worktreeIdx + 4);
+ _watchers.Add(combined);
+ }
+ else
+ {
+ var wc = new FileSystemWatcher();
+ wc.Path = fullpath;
+ wc.Filter = "*";
+ wc.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.CreationTime;
+ wc.IncludeSubdirectories = true;
+ wc.Created += OnWorkingCopyChanged;
+ wc.Renamed += OnWorkingCopyChanged;
+ wc.Changed += OnWorkingCopyChanged;
+ wc.Deleted += OnWorkingCopyChanged;
+ wc.EnableRaisingEvents = true;
- _repoWatcher = new FileSystemWatcher();
- _repoWatcher.Path = repoWatchDir;
- _repoWatcher.Filter = "*";
- _repoWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName;
- _repoWatcher.IncludeSubdirectories = true;
- _repoWatcher.Created += OnRepositoryChanged;
- _repoWatcher.Renamed += OnRepositoryChanged;
- _repoWatcher.Changed += OnRepositoryChanged;
- _repoWatcher.Deleted += OnRepositoryChanged;
- _repoWatcher.EnableRaisingEvents = true;
+ var git = new FileSystemWatcher();
+ git.Path = gitDir;
+ git.Filter = "*";
+ git.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName;
+ git.IncludeSubdirectories = true;
+ git.Created += OnGitDirChanged;
+ git.Renamed += OnGitDirChanged;
+ git.Changed += OnGitDirChanged;
+ git.Deleted += OnGitDirChanged;
+ git.EnableRaisingEvents = true;
+
+ _watchers.Add(wc);
+ _watchers.Add(git);
+ }
_timer = new Timer(Tick, null, 100, 100);
}
@@ -84,22 +100,13 @@ namespace SourceGit.Models
public void Dispose()
{
- _repoWatcher.EnableRaisingEvents = false;
- _repoWatcher.Created -= OnRepositoryChanged;
- _repoWatcher.Renamed -= OnRepositoryChanged;
- _repoWatcher.Changed -= OnRepositoryChanged;
- _repoWatcher.Deleted -= OnRepositoryChanged;
- _repoWatcher.Dispose();
- _repoWatcher = null;
-
- _wcWatcher.EnableRaisingEvents = false;
- _wcWatcher.Created -= OnWorkingCopyChanged;
- _wcWatcher.Renamed -= OnWorkingCopyChanged;
- _wcWatcher.Changed -= OnWorkingCopyChanged;
- _wcWatcher.Deleted -= OnWorkingCopyChanged;
- _wcWatcher.Dispose();
- _wcWatcher = null;
+ foreach (var watcher in _watchers)
+ {
+ watcher.EnableRaisingEvents = false;
+ watcher.Dispose();
+ }
+ _watchers.Clear();
_timer.Dispose();
_timer = null;
}
@@ -114,7 +121,6 @@ namespace SourceGit.Models
{
_updateBranch = 0;
_updateWC = 0;
- _updateSubmodules = 0;
if (_updateTags > 0)
{
@@ -122,10 +128,15 @@ namespace SourceGit.Models
Task.Run(_repo.RefreshTags);
}
+ if (_updateSubmodules > 0 || _repo.MayHaveSubmodules())
+ {
+ _updateSubmodules = 0;
+ Task.Run(_repo.RefreshSubmodules);
+ }
+
Task.Run(_repo.RefreshBranches);
Task.Run(_repo.RefreshCommits);
Task.Run(_repo.RefreshWorkingCopyChanges);
- Task.Run(_repo.RefreshSubmodules);
Task.Run(_repo.RefreshWorktrees);
}
@@ -157,14 +168,63 @@ namespace SourceGit.Models
private void OnRepositoryChanged(object o, FileSystemEventArgs e)
{
- if (string.IsNullOrEmpty(e.Name) || e.Name.EndsWith(".lock", StringComparison.Ordinal))
+ if (string.IsNullOrEmpty(e.Name) || e.Name.Equals(".git", StringComparison.Ordinal))
return;
- var name = e.Name.Replace("\\", "/");
- if (name.StartsWith("modules", StringComparison.Ordinal) && name.EndsWith("HEAD", StringComparison.Ordinal))
+ var name = e.Name.Replace('\\', '/').TrimEnd('/');
+ if (name.EndsWith("/.git", StringComparison.Ordinal))
+ return;
+
+ if (name.StartsWith(".git/", StringComparison.Ordinal))
+ HandleGitDirFileChanged(name.Substring(5));
+ else
+ HandleWorkingCopyFileChanged(name);
+ }
+
+ private void OnGitDirChanged(object o, FileSystemEventArgs e)
+ {
+ if (string.IsNullOrEmpty(e.Name))
+ return;
+
+ var name = e.Name.Replace('\\', '/').TrimEnd('/');
+ HandleGitDirFileChanged(name);
+ }
+
+ private void OnWorkingCopyChanged(object o, FileSystemEventArgs e)
+ {
+ if (string.IsNullOrEmpty(e.Name))
+ return;
+
+ var name = e.Name.Replace('\\', '/').TrimEnd('/');
+ if (name.Equals(".git", StringComparison.Ordinal) ||
+ name.StartsWith(".git/", StringComparison.Ordinal) ||
+ name.EndsWith("/.git", StringComparison.Ordinal))
+ return;
+
+ HandleWorkingCopyFileChanged(name);
+ }
+
+ private void HandleGitDirFileChanged(string name)
+ {
+ if (name.Contains("fsmonitor--daemon/", StringComparison.Ordinal) ||
+ name.EndsWith(".lock", StringComparison.Ordinal) ||
+ name.StartsWith("lfs/", StringComparison.Ordinal))
+ return;
+
+ if (name.StartsWith("modules", StringComparison.Ordinal))
{
- _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
- _updateWC = DateTime.Now.AddSeconds(1).ToFileTime();
+ if (name.EndsWith("/HEAD", StringComparison.Ordinal) ||
+ name.EndsWith("/ORIG_HEAD", StringComparison.Ordinal))
+ {
+ _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
+ _updateWC = DateTime.Now.AddSeconds(1).ToFileTime();
+ }
+ }
+ else if (name.Equals("MERGE_HEAD", StringComparison.Ordinal) ||
+ name.Equals("AUTO_MERGE", StringComparison.Ordinal))
+ {
+ if (_repo.MayHaveSubmodules())
+ _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
}
else if (name.StartsWith("refs/tags", StringComparison.Ordinal))
{
@@ -175,6 +235,7 @@ namespace SourceGit.Models
_updateStashes = DateTime.Now.AddSeconds(.5).ToFileTime();
}
else if (name.Equals("HEAD", StringComparison.Ordinal) ||
+ name.Equals("BISECT_START", StringComparison.Ordinal) ||
name.StartsWith("refs/heads/", StringComparison.Ordinal) ||
name.StartsWith("refs/remotes/", StringComparison.Ordinal) ||
(name.StartsWith("worktrees/", StringComparison.Ordinal) && name.EndsWith("/HEAD", StringComparison.Ordinal)))
@@ -187,14 +248,16 @@ namespace SourceGit.Models
}
}
- private void OnWorkingCopyChanged(object o, FileSystemEventArgs e)
+ private void HandleWorkingCopyFileChanged(string name)
{
- if (string.IsNullOrEmpty(e.Name))
+ if (name.StartsWith(".vs/", StringComparison.Ordinal))
return;
- var name = e.Name.Replace("\\", "/");
- if (name == ".git" || name.StartsWith(".git/", StringComparison.Ordinal))
+ if (name.Equals(".gitmodules", StringComparison.Ordinal))
+ {
+ _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
return;
+ }
lock (_lockSubmodule)
{
@@ -212,8 +275,7 @@ namespace SourceGit.Models
}
private readonly IRepository _repo = null;
- private FileSystemWatcher _repoWatcher = null;
- private FileSystemWatcher _wcWatcher = null;
+ private List _watchers = [];
private Timer _timer = null;
private int _lockCount = 0;
private long _updateWC = 0;
@@ -222,7 +284,7 @@ namespace SourceGit.Models
private long _updateStashes = 0;
private long _updateTags = 0;
- private object _lockSubmodule = new object();
+ private readonly Lock _lockSubmodule = new();
private List _submodules = new List();
}
}
diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs
index bc40e320..26f88a8a 100644
--- a/src/Models/Worktree.cs
+++ b/src/Models/Worktree.cs
@@ -1,4 +1,5 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.Models
{
@@ -22,12 +23,12 @@ namespace SourceGit.Models
get
{
if (IsDetached)
- return $"deteched HEAD at {Head.Substring(10)}";
+ return $"detached HEAD at {Head.AsSpan(10)}";
- if (Branch.StartsWith("refs/heads/", System.StringComparison.Ordinal))
+ if (Branch.StartsWith("refs/heads/", StringComparison.Ordinal))
return Branch.Substring(11);
- if (Branch.StartsWith("refs/remotes/", System.StringComparison.Ordinal))
+ if (Branch.StartsWith("refs/remotes/", StringComparison.Ordinal))
return Branch.Substring(13);
return Branch;
diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs
index bfb98500..3f6de903 100644
--- a/src/Native/Linux.cs
+++ b/src/Native/Linux.cs
@@ -5,6 +5,8 @@ using System.IO;
using System.Runtime.Versioning;
using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Platform;
namespace SourceGit.Native
{
@@ -16,6 +18,21 @@ namespace SourceGit.Native
builder.With(new X11PlatformOptions() { EnableIme = true });
}
+ public void SetupWindow(Window window)
+ {
+ if (OS.UseSystemWindowFrame)
+ {
+ window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.Default;
+ window.ExtendClientAreaToDecorationsHint = false;
+ }
+ else
+ {
+ window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
+ window.ExtendClientAreaToDecorationsHint = true;
+ window.Classes.Add("custom_window_frame");
+ }
+ }
+
public string FindGitExecutable()
{
return FindExecutable("git");
@@ -103,8 +120,8 @@ namespace SourceGit.Native
private string FindExecutable(string filename)
{
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
- var pathes = pathVariable.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
- foreach (var path in pathes)
+ var paths = pathVariable.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var path in paths)
{
var test = Path.Combine(path, filename);
if (File.Exists(test))
diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs
index 123b160b..b76d239a 100644
--- a/src/Native/MacOS.cs
+++ b/src/Native/MacOS.cs
@@ -5,6 +5,8 @@ using System.IO;
using System.Runtime.Versioning;
using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Platform;
namespace SourceGit.Native
{
@@ -31,11 +33,17 @@ namespace SourceGit.Native
var env = File.ReadAllText(customPathFile).Trim();
if (!string.IsNullOrEmpty(env))
path = env;
- }
+ }
Environment.SetEnvironmentVariable("PATH", path);
}
+ public void SetupWindow(Window window)
+ {
+ window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome;
+ window.ExtendClientAreaToDecorationsHint = true;
+ }
+
public string FindGitExecutable()
{
var gitPathVariants = new List() {
@@ -59,6 +67,8 @@ namespace SourceGit.Native
return "Warp";
case "ghostty":
return "Ghostty";
+ case "kitty":
+ return "kitty";
}
return string.Empty;
diff --git a/src/Native/OS.cs b/src/Native/OS.cs
index f11d1e7f..ad6f8104 100644
--- a/src/Native/OS.cs
+++ b/src/Native/OS.cs
@@ -6,6 +6,7 @@ using System.Text;
using System.Text.RegularExpressions;
using Avalonia;
+using Avalonia.Controls;
namespace SourceGit.Native
{
@@ -14,6 +15,7 @@ namespace SourceGit.Native
public interface IBackend
{
void SetupApp(AppBuilder builder);
+ void SetupWindow(Window window);
string FindGitExecutable();
string FindTerminal(Models.ShellOrTerminal shell);
@@ -68,6 +70,12 @@ namespace SourceGit.Native
set;
} = [];
+ public static bool UseSystemWindowFrame
+ {
+ get => OperatingSystem.IsLinux() && _enableSystemWindowFrame;
+ set => _enableSystemWindowFrame = value;
+ }
+
static OS()
{
if (OperatingSystem.IsWindows())
@@ -116,11 +124,16 @@ namespace SourceGit.Native
Directory.CreateDirectory(DataDir);
}
- public static void SetupEnternalTools()
+ public static void SetupExternalTools()
{
ExternalTools = _backend.FindExternalTools();
}
+ public static void SetupForWindow(Window window)
+ {
+ _backend.SetupWindow(window);
+ }
+
public static string FindGitExecutable()
{
return _backend.FindGitExecutable();
@@ -162,6 +175,15 @@ namespace SourceGit.Native
_backend.OpenWithDefaultEditor(file);
}
+ public static string GetAbsPath(string root, string sub)
+ {
+ var fullpath = Path.Combine(root, sub);
+ if (OperatingSystem.IsWindows())
+ return fullpath.Replace('/', '\\');
+
+ return fullpath;
+ }
+
private static void UpdateGitVersion()
{
if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable))
@@ -216,5 +238,6 @@ namespace SourceGit.Native
private static IBackend _backend = null;
private static string _gitExecutable = string.Empty;
+ private static bool _enableSystemWindowFrame = false;
}
}
diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs
index eb354f10..06d80bf0 100644
--- a/src/Native/Windows.cs
+++ b/src/Native/Windows.cs
@@ -8,6 +8,7 @@ using System.Text;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Platform;
using Avalonia.Threading;
namespace SourceGit.Native
@@ -15,16 +16,12 @@ namespace SourceGit.Native
[SupportedOSPlatform("windows")]
internal class Windows : OS.IBackend
{
- [StructLayout(LayoutKind.Sequential)]
- internal struct RTL_OSVERSIONINFOEX
+ internal struct RECT
{
- internal uint dwOSVersionInfoSize;
- internal uint dwMajorVersion;
- internal uint dwMinorVersion;
- internal uint dwBuildNumber;
- internal uint dwPlatformId;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- internal string szCSDVersion;
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
@@ -36,9 +33,6 @@ namespace SourceGit.Native
public int cyBottomHeight;
}
- [DllImport("ntdll.dll")]
- private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
-
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
@@ -54,41 +48,96 @@ namespace SourceGit.Native
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
+ [DllImport("user32.dll")]
+ private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
+
public void SetupApp(AppBuilder builder)
{
// Fix drop shadow issue on Windows 10
- RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
- v.dwOSVersionInfoSize = (uint)Marshal.SizeOf();
- if (RtlGetVersion(ref v) == 0 && (v.dwMajorVersion < 10 || v.dwBuildNumber < 22000))
+ if (!OperatingSystem.IsWindowsVersionAtLeast(10, 22000))
{
Window.WindowStateProperty.Changed.AddClassHandler((w, _) => FixWindowFrameOnWin10(w));
Control.LoadedEvent.AddClassHandler((w, _) => FixWindowFrameOnWin10(w));
}
}
+ public void SetupWindow(Window window)
+ {
+ window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
+ window.ExtendClientAreaToDecorationsHint = true;
+ window.Classes.Add("fix_maximized_padding");
+
+ Win32Properties.AddWndProcHookCallback(window, (IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
+ {
+ // Custom WM_NCHITTEST
+ if (msg == 0x0084)
+ {
+ handled = true;
+
+ if (window.WindowState == WindowState.FullScreen || window.WindowState == WindowState.Maximized)
+ return 1; // HTCLIENT
+
+ var p = IntPtrToPixelPoint(lParam);
+ GetWindowRect(hWnd, out var rcWindow);
+
+ var borderThickness = (int)(4 * window.RenderScaling);
+ int y = 1;
+ int x = 1;
+ if (p.X >= rcWindow.left && p.X < rcWindow.left + borderThickness)
+ x = 0;
+ else if (p.X < rcWindow.right && p.X >= rcWindow.right - borderThickness)
+ x = 2;
+
+ if (p.Y >= rcWindow.top && p.Y < rcWindow.top + borderThickness)
+ y = 0;
+ else if (p.Y < rcWindow.bottom && p.Y >= rcWindow.bottom - borderThickness)
+ y = 2;
+
+ var zone = y * 3 + x;
+ switch (zone)
+ {
+ case 0:
+ return 13; // HTTOPLEFT
+ case 1:
+ return 12; // HTTOP
+ case 2:
+ return 14; // HTTOPRIGHT
+ case 3:
+ return 10; // HTLEFT
+ case 4:
+ return 1; // HTCLIENT
+ case 5:
+ return 11; // HTRIGHT
+ case 6:
+ return 16; // HTBOTTOMLEFT
+ case 7:
+ return 15; // HTBOTTOM
+ default:
+ return 17; // HTBOTTOMRIGHT
+ }
+ }
+
+ return IntPtr.Zero;
+ });
+ }
+
public string FindGitExecutable()
{
var reg = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.LocalMachine,
Microsoft.Win32.RegistryView.Registry64);
- var git = reg.OpenSubKey("SOFTWARE\\GitForWindows");
- if (git != null && git.GetValue("InstallPath") is string installPath)
- {
+ var git = reg.OpenSubKey(@"SOFTWARE\GitForWindows");
+ if (git?.GetValue("InstallPath") is string installPath)
return Path.Combine(installPath, "bin", "git.exe");
- }
var builder = new StringBuilder("git.exe", 259);
if (!PathFindOnPath(builder, null))
- {
return null;
- }
var exePath = builder.ToString();
if (!string.IsNullOrEmpty(exePath))
- {
return exePath;
- }
return null;
}
@@ -126,7 +175,7 @@ namespace SourceGit.Native
break;
case "cmd":
- return "C:\\Windows\\System32\\cmd.exe";
+ return @"C:\Windows\System32\cmd.exe";
case "wt":
var wtFinder = new StringBuilder("wt.exe", 512);
if (PathFindOnPath(wtFinder, null))
@@ -144,8 +193,8 @@ namespace SourceGit.Native
finder.VSCode(FindVSCode);
finder.VSCodeInsiders(FindVSCodeInsiders);
finder.VSCodium(FindVSCodium);
- finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
- finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox");
+ finder.Fleet(() => $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Programs\Fleet\Fleet.exe");
+ finder.FindJetBrainsFromToolbox(() => $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\JetBrains\Toolbox");
finder.SublimeText(FindSublimeText);
finder.TryAdd("Visual Studio", "vs", FindVisualStudio, GenerateCommandlineArgsForVisualStudio);
return finder.Founded;
@@ -228,6 +277,12 @@ namespace SourceGit.Native
}, DispatcherPriority.Render);
}
+ private PixelPoint IntPtrToPixelPoint(IntPtr param)
+ {
+ var v = IntPtr.Size == 4 ? param.ToInt32() : (int)(param.ToInt64() & 0xFFFFFFFF);
+ return new PixelPoint((short)(v & 0xffff), (short)(v >> 16));
+ }
+
#region EXTERNAL_EDITOR_FINDER
private string FindVSCode()
{
@@ -238,9 +293,7 @@ namespace SourceGit.Native
// VSCode (system)
var systemVScode = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1");
if (systemVScode != null)
- {
return systemVScode.GetValue("DisplayIcon") as string;
- }
var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.CurrentUser,
@@ -249,9 +302,7 @@ namespace SourceGit.Native
// VSCode (user)
var vscode = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1");
if (vscode != null)
- {
return vscode.GetValue("DisplayIcon") as string;
- }
return string.Empty;
}
@@ -265,9 +316,7 @@ namespace SourceGit.Native
// VSCode - Insiders (system)
var systemVScodeInsiders = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1");
if (systemVScodeInsiders != null)
- {
return systemVScodeInsiders.GetValue("DisplayIcon") as string;
- }
var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.CurrentUser,
@@ -276,9 +325,7 @@ namespace SourceGit.Native
// VSCode - Insiders (user)
var vscodeInsiders = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1");
if (vscodeInsiders != null)
- {
return vscodeInsiders.GetValue("DisplayIcon") as string;
- }
return string.Empty;
}
@@ -290,11 +337,9 @@ namespace SourceGit.Native
Microsoft.Win32.RegistryView.Registry64);
// VSCodium (system)
- var systemVScodium = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{88DA3577-054F-4CA1-8122-7D820494CFFB}_is1");
- if (systemVScodium != null)
- {
- return systemVScodium.GetValue("DisplayIcon") as string;
- }
+ var systemVSCodium = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{88DA3577-054F-4CA1-8122-7D820494CFFB}_is1");
+ if (systemVSCodium != null)
+ return systemVSCodium.GetValue("DisplayIcon") as string;
var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.CurrentUser,
@@ -303,9 +348,7 @@ namespace SourceGit.Native
// VSCodium (user)
var vscodium = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{2E1F05D1-C245-4562-81EE-28188DB6FD17}_is1");
if (vscodium != null)
- {
return vscodium.GetValue("DisplayIcon") as string;
- }
return string.Empty;
}
@@ -342,15 +385,13 @@ namespace SourceGit.Native
Microsoft.Win32.RegistryView.Registry64);
// Get default class for VisualStudio.Launcher.sln - the handler for *.sln files
- if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher)
+ if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is { } launcher)
{
// Get actual path to the executable
if (launcher.GetValue(string.Empty) is string CLSID &&
- localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv &&
+ localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is { } devenv &&
devenv.GetValue(string.Empty) is string localServer32)
- {
return localServer32!.Trim('\"');
- }
}
return string.Empty;
diff --git a/src/Resources/Grammars/haxe.json b/src/Resources/Grammars/haxe.json
index 12acc538..3f78154d 100644
--- a/src/Resources/Grammars/haxe.json
+++ b/src/Resources/Grammars/haxe.json
@@ -1,7 +1,9 @@
{
"information_for_contributors": [
"This file has been copied from https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/haxe.tmLanguage",
- "and converted to JSON using https://marketplace.visualstudio.com/items?itemName=pedro-w.tmlanguage"
+ "and converted to JSON using https://marketplace.visualstudio.com/items?itemName=pedro-w.tmlanguage",
+ "The original file was licensed under the MIT License",
+ "https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/LICENSE.md"
],
"fileTypes": [
"hx",
@@ -2485,4 +2487,4 @@
"name": "variable.other.hx"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Resources/Grammars/hxml.json b/src/Resources/Grammars/hxml.json
index 829c403e..3be42577 100644
--- a/src/Resources/Grammars/hxml.json
+++ b/src/Resources/Grammars/hxml.json
@@ -1,7 +1,9 @@
{
"information_for_contributors": [
"This file has been copied from https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/hxml.tmLanguage",
- "and converted to JSON using https://marketplace.visualstudio.com/items?itemName=pedro-w.tmlanguage"
+ "and converted to JSON using https://marketplace.visualstudio.com/items?itemName=pedro-w.tmlanguage",
+ "The original file was licensed under the MIT License",
+ "https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/LICENSE.md"
],
"fileTypes": [
"hxml"
@@ -67,4 +69,4 @@
],
"scopeName": "source.hxml",
"uuid": "CB1B853A-C4C8-42C3-BA70-1B1605BE51C1"
-}
\ No newline at end of file
+}
diff --git a/src/Resources/Grammars/jsp.json b/src/Resources/Grammars/jsp.json
new file mode 100644
index 00000000..2fbfd97c
--- /dev/null
+++ b/src/Resources/Grammars/jsp.json
@@ -0,0 +1,100 @@
+{
+ "information_for_contributors": [
+ "This file has been copied from https://github.com/samuel-weinhardt/vscode-jsp-lang/blob/0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355/syntaxes/jsp.tmLanguage.json",
+ "The original file was licensed under the MIT License",
+ "https://github.com/samuel-weinhardt/vscode-jsp-lang/blob/0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355/LICENSE"
+ ],
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+ "name": "Jakarta Server Pages",
+ "fileTypes": ["jsp", "jspf", "tag"],
+ "scopeName": "text.html.jsp",
+ "patterns": [
+ { "include": "#comment" },
+ { "include": "#directive" },
+ { "include": "#expression" },
+ { "include": "text.html.derivative" }
+ ],
+ "injections": {
+ "L:text.html.jsp -comment -meta.tag.directive.jsp -meta.tag.scriptlet.jsp": {
+ "patterns": [
+ { "include": "#scriptlet" }
+ ],
+ "comment": "allow scriptlets anywhere except comments and nested"
+ },
+ "L:meta.attribute (string.quoted.single.html | string.quoted.double.html) -string.template.expression.jsp": {
+ "patterns": [
+ { "include": "#expression" },
+ { "include": "text.html.derivative" }
+ ],
+ "comment": "allow expressions and tags within HTML attributes (not nested)"
+ }
+ },
+ "repository": {
+ "comment": {
+ "name": "comment.block.jsp",
+ "begin": "<%--",
+ "end": "--%>"
+ },
+ "directive": {
+ "name": "meta.tag.directive.jsp",
+ "begin": "(<)(%@)",
+ "end": "(%)(>)",
+ "beginCaptures": {
+ "1": { "name": "punctuation.definition.tag.jsp" },
+ "2": { "name": "entity.name.tag.jsp" }
+ },
+ "endCaptures": {
+ "1": { "name": "entity.name.tag.jsp" },
+ "2": { "name": "punctuation.definition.tag.jsp" }
+ },
+ "patterns": [
+ {
+ "match": "\\b(attribute|include|page|tag|taglib|variable)\\b(?!\\s*=)",
+ "name": "keyword.control.directive.jsp"
+ },
+ { "include": "text.html.basic#attribute" }
+ ]
+ },
+ "scriptlet": {
+ "name": "meta.tag.scriptlet.jsp",
+ "contentName": "meta.embedded.block.java",
+ "begin": "(<)(%[\\s!=])",
+ "end": "(%)(>)",
+ "beginCaptures": {
+ "1": { "name": "punctuation.definition.tag.jsp" },
+ "2": { "name": "entity.name.tag.jsp" }
+ },
+ "endCaptures": {
+ "1": { "name": "entity.name.tag.jsp" },
+ "2": { "name": "punctuation.definition.tag.jsp" }
+ },
+ "patterns": [
+ {
+ "match": "\\{(?=\\s*(%>|$))",
+ "comment": "consume trailing curly brackets for fragmented scriptlets"
+ },
+ { "include": "source.java" }
+ ]
+ },
+ "expression": {
+ "name": "string.template.expression.jsp",
+ "contentName": "meta.embedded.block.java",
+ "begin": "[$#]\\{",
+ "end": "\\}",
+ "beginCaptures": {
+ "0": { "name": "punctuation.definition.template-expression.begin.jsp" }
+ },
+ "endCaptures": {
+ "0": { "name": "punctuation.definition.template-expression.end.jsp" }
+ },
+ "patterns": [
+ { "include": "#escape" },
+ { "include": "source.java" }
+ ]
+ },
+ "escape": {
+ "match": "\\\\.",
+ "name": "constant.character.escape.jsp"
+ }
+ }
+}
diff --git a/src/Resources/Grammars/kotlin.json b/src/Resources/Grammars/kotlin.json
index e8f844d0..2857f717 100644
--- a/src/Resources/Grammars/kotlin.json
+++ b/src/Resources/Grammars/kotlin.json
@@ -1,6 +1,8 @@
{
"information_for_contributors": [
- "This file has been copied from https://github.com/eclipse/buildship/blob/6bb773e7692f913dec27105129ebe388de34e68b/org.eclipse.buildship.kotlindsl.provider/kotlin.tmLanguage.json"
+ "This file has been copied from https://github.com/eclipse/buildship/blob/6bb773e7692f913dec27105129ebe388de34e68b/org.eclipse.buildship.kotlindsl.provider/kotlin.tmLanguage.json",
+ "The original file was licensed under the Eclipse Public License, Version 1.0",
+ "https://github.com/eclipse-buildship/buildship/blob/6bb773e7692f913dec27105129ebe388de34e68b/README.md"
],
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "Kotlin",
@@ -698,4 +700,4 @@
"name": "variable.language.this.kotlin"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Resources/Grammars/toml.json b/src/Resources/Grammars/toml.json
index 86c2ef87..6be4678f 100644
--- a/src/Resources/Grammars/toml.json
+++ b/src/Resources/Grammars/toml.json
@@ -3,7 +3,10 @@
"scopeName": "source.toml",
"uuid": "8b4e5008-c50d-11ea-a91b-54ee75aeeb97",
"information_for_contributors": [
- "Originally was maintained by aster (galaster@foxmail.com). This notice is only kept here for the record, please don't send e-mails about bugs and other issues."
+ "Originally was maintained by aster (galaster@foxmail.com). This notice is only kept here for the record, please don't send e-mails about bugs and other issues.",
+ "This file has been copied from https://github.com/kkiyama117/coc-toml/blob/main/toml.tmLanguage.json",
+ "The original file was licensed under the MIT License",
+ "https://github.com/kkiyama117/coc-toml/blob/main/LICENSE"
],
"patterns": [
{
diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml
index 9426d20a..001c7ee7 100644
--- a/src/Resources/Icons.axaml
+++ b/src/Resources/Icons.axaml
@@ -2,7 +2,9 @@
M41 512c0-128 46-241 138-333C271 87 384 41 512 41s241 46 333 138c92 92 138 205 138 333s-46 241-138 333c-92 92-205 138-333 138s-241-46-333-138C87 753 41 640 41 512zm87 0c0 108 36 195 113 271s164 113 271 113c108 0 195-36 271-113s113-164 113-271-36-195-113-271c-77-77-164-113-271-113-108 0-195 36-271 113C164 317 128 404 128 512zm256 148V292l195 113L768 512l-195 113-195 113v-77zm148-113-61 36V440l61 36 61 36-61 36z
M304 464a128 128 0 01128-128c71 0 128 57 128 128v224a32 32 0 01-64 0V592h-128v95a32 32 0 01-64 0v-224zm64 1v64h128v-64a64 64 0 00-64-64c-35 0-64 29-64 64zM688 337c18 0 32 14 32 32v319a32 32 0 01-32 32c-18 0-32-14-32-32v-319a32 32 0 0132-32zM84 911l60-143A446 446 0 0164 512C64 265 265 64 512 64s448 201 448 448-201 448-448 448c-54 0-105-9-153-27l-242 22a32 32 0 01-32-44zm133-150-53 126 203-18 13 5c41 15 85 23 131 23 212 0 384-172 384-384S724 128 512 128 128 300 128 512c0 82 26 157 69 220l20 29z
M296 392h64v64h-64zM296 582v160h128V582h-64v-62h-64v62zm80 48v64h-32v-64h32zM360 328h64v64h-64zM296 264h64v64h-64zM360 456h64v64h-64zM360 200h64v64h-64zM855 289 639 73c-6-6-14-9-23-9H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V311c0-9-3-17-9-23zM790 326H602V138L790 326zm2 562H232V136h64v64h64v-64h174v216c0 23 19 42 42 42h216v494z
+ M851 755q0 23-16 39l-78 78q-16 16-39 16t-39-16l-168-168-168 168q-16 16-39 16t-39-16l-78-78q-16-16-16-39t16-39l168-168-168-168q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 168 168-168q16-16 39-16t39 16l78 78q16 16 16 39t-16 39l-168 168 168 168q16 16 16 39z
M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z
+ M128 384a43 43 0 0043-43V213a43 43 0 0143-43h128a43 43 0 000-85H213a128 128 0 00-128 128v128a43 43 0 0043 43zm213 469H213a43 43 0 01-43-43v-128a43 43 0 00-85 0v128a128 128 0 00128 128h128a43 43 0 000-85zm384-299a43 43 0 000-85h-49A171 171 0 00555 347V299a43 43 0 00-85 0v49A171 171 0 00347 469H299a43 43 0 000 85h49A171 171 0 00469 677V725a43 43 0 0085 0v-49A171 171 0 00677 555zm-213 43a85 85 0 1185-85 85 85 0 01-85 85zm384 43a43 43 0 00-43 43v128a43 43 0 01-43 43h-128a43 43 0 000 85h128a128 128 0 00128-128v-128a43 43 0 00-43-43zM811 85h-128a43 43 0 000 85h128a43 43 0 0143 43v128a43 43 0 0085 0V213a128 128 0 00-128-128z
M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z
M832 64H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V96c0-18-14-32-32-32zM736 596 624 502 506 596V131h230v318z
M509 546 780 275 871 366 509 728 147 366 238 275zM509 728h-362v128h724v-128z
@@ -46,6 +48,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
+ M505 74c-145 3-239 68-239 68-12 8-15 25-7 37 9 13 25 15 38 6 0 0 184-136 448 2 12 7 29 3 36-10 8-13 3-29-12-37-71-38-139-56-199-63-23-3-44-3-65-3m17 111c-254-3-376 201-376 201-8 12-5 29 7 37 12 8 29 4 39-10 0 0 103-178 329-175 226 3 325 173 325 173 8 12 24 17 37 9 14-8 17-24 9-37 0 0-117-195-370-199m-31 106c-72 5-140 31-192 74C197 449 132 603 204 811c5 14 20 21 34 17 14-5 21-20 16-34-66-191-7-316 79-388 84-69 233-85 343-17 54 34 96 93 118 151 22 58 20 114 3 141-18 28-54 38-86 30-32-8-58-31-59-80-1-73-58-118-118-125-57-7-123 24-140 92-32 125 49 302 238 361 14 4 29-3 34-17 4-14-3-29-18-34-163-51-225-206-202-297 10-41 46-55 84-52 37 4 69 26 69 73 2 70 48 117 100 131 52 13 112-3 144-52 33-50 28-120 3-188-26-68-73-136-140-178a356 356 0 00-213-52m15 104v0c-76 3-152 42-195 125-56 106-31 215 7 293 38 79 90 131 90 131 10 11 27 11 38 0s11-26 0-38c0 0-46-47-79-116s-54-157-8-244c48-90 133-111 208-90 76 22 140 88 138 186-2 15 9 28 24 29 15 1 27-10 29-27 3-122-79-210-176-239a246 246 0 00-75-9m9 213c-15 0-26 13-26 27 0 0 1 63 36 124 36 61 112 119 244 107 15-1 26-13 25-28-1-15-14-26-30-25-116 11-165-33-193-81-28-47-29-98-29-98a27 27 0 00-27-27z
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
@@ -54,6 +57,7 @@
M939 94v710L512 998 85 805V94h-64A21 21 0 010 73v-0C0 61 10 51 21 51h981c12 0 21 10 21 21v0c0 12-10 21-21 21h-64zm-536 588L512 624l109 58c6 3 13 4 20 3a32 32 0 0026-37l-21-122 88-87c5-5 8-11 9-18a32 32 0 00-27-37l-122-18-54-111a32 32 0 00-57 0l-54 111-122 18c-7 1-13 4-18 9a33 33 0 001 46l88 87-21 122c-1 7-0 14 3 20a32 32 0 0043 14z
M236 542a32 32 0 109 63l86-12a180 180 0 0022 78l-71 47a32 32 0 1035 53l75-50a176 176 0 00166 40L326 529zM512 16C238 16 16 238 16 512s222 496 496 496 496-222 496-496S786 16 512 16zm0 896c-221 0-400-179-400-400a398 398 0 0186-247l561 561A398 398 0 01512 912zm314-154L690 622a179 179 0 004-29l85 12a32 32 0 109-63l-94-13v-49l94-13a32 32 0 10-9-63l-87 12a180 180 0 00-20-62l71-47A32 32 0 10708 252l-75 50a181 181 0 00-252 10l-115-115A398 398 0 01512 112c221 0 400 179 400 400a398 398 0 01-86 247z
M884 159l-18-18a43 43 0 00-38-12l-235 43a166 166 0 00-101 60L400 349a128 128 0 00-148 47l-120 171a21 21 0 005 29l17 12a128 128 0 00178-32l27-38 124 124-38 27a128 128 0 00-32 178l12 17a21 21 0 0029 5l171-120a128 128 0 0047-148l117-92A166 166 0 00853 431l43-235a43 43 0 00-12-38zm-177 249a64 64 0 110-90 64 64 0 010 90zm-373 312a21 21 0 010 30l-139 139a21 21 0 01-30 0l-30-30a21 21 0 010-30l139-139a21 21 0 0130 0z
+ M525 0C235 0 0 235 0 525c0 232 150 429 359 498 26 5 36-11 36-25 0-12-1-54-1-97-146 31-176-63-176-63-23-61-58-76-58-76-48-32 3-32 3-32 53 3 81 54 81 54 47 80 123 57 153 43 4-34 18-57 33-70-116-12-239-57-239-259 0-57 21-104 54-141-5-13-23-67 5-139 0 0 44-14 144 54 42-11 87-17 131-17s90 6 131 17C756 203 801 217 801 217c29 72 10 126 5 139 34 37 54 83 54 141 0 202-123 246-240 259 19 17 36 48 36 97 0 70-1 127-1 144 0 14 10 30 36 25 209-70 359-266 359-498C1050 235 814 0 525 0z
M590 74 859 342V876c0 38-31 68-68 68H233c-38 0-68-31-68-68V142c0-38 31-68 68-68h357zm-12 28H233a40 40 0 00-40 38L193 142v734a40 40 0 0038 40L233 916h558a40 40 0 0040-38L831 876V354L578 102zM855 371h-215c-46 0-83-36-84-82l0-2V74h28v213c0 30 24 54 54 55l2 0h215v28zM57 489m28 0 853 0q28 0 28 28l0 284q0 28-28 28l-853 0q-28 0-28-28l0-284q0-28 28-28ZM157 717c15 0 29-6 37-13v-51h-41v22h17v18c-2 2-6 3-10 3-21 0-30-13-30-34 0-21 12-34 28-34 9 0 15 4 20 9l14-17C184 610 172 603 156 603c-29 0-54 21-54 57 0 37 24 56 54 56zM245 711v-108h-34v108h34zm69 0v-86H341V603H262v22h28V711h24zM393 711v-108h-34v108h34zm66 6c15 0 29-6 37-13v-51h-41v22h17v18c-2 2-6 3-10 3-21 0-30-13-30-34 0-21 12-34 28-34 9 0 15 4 20 9l14-17C485 610 474 603 458 603c-29 0-54 21-54 57 0 37 24 56 54 56zm88-6v-36c0-13-2-28-3-40h1l10 24 25 52H603v-108h-23v36c0 13 2 28 3 40h-1l-10-24L548 603H523v108h23zM677 717c30 0 51-22 51-57 0-36-21-56-51-56-30 0-51 20-51 56 0 36 21 57 51 57zm3-23c-16 0-26-12-26-32 0-19 10-31 26-31 16 0 26 11 26 31S696 694 680 694zm93 17v-38h13l21 38H836l-25-43c12-5 19-15 19-31 0-26-20-34-44-34H745v108h27zm16-51H774v-34h15c16 0 25 4 25 16s-9 18-25 18zM922 711v-22h-43v-23h35v-22h-35V625h41V603H853v108h68z
M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z
M0 512M1024 512M512 0M512 1024M955 323q0 23-16 39l-414 414-78 78q-16 16-39 16t-39-16l-78-78-207-207q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 169 375-375q16-16 39-16t39 16l78 78q16 16 16 39z
@@ -77,6 +81,7 @@
M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z
M976 0h-928A48 48 0 000 48v652a48 48 0 0048 48h416V928H200a48 48 0 000 96h624a48 48 0 000-96H560v-180h416a48 48 0 0048-48V48A48 48 0 00976 0zM928 652H96V96h832v556z
M832 464h-68V240a128 128 0 00-128-128h-248a128 128 0 00-128 128v224H192c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32v-384c0-18-14-32-32-32zm-292 237v53a8 8 0 01-8 8h-40a8 8 0 01-8-8v-53a48 48 0 1156 0zm152-237H332V240a56 56 0 0156-56h248a56 56 0 0156 56v224z
+ M908 366h-25V248a18 18 0 00-0-2 20 20 0 00-5-13L681 7 681 7a19 19 0 00-4-3c-0-0-1-1-1-1a29 29 0 00-4-2L671 1a24 24 0 00-5-1H181a40 40 0 00-40 40v326h-25c-32 0-57 26-57 57v298c0 32 26 57 57 57h25v204c0 22 18 40 40 40H843a40 40 0 0040-40v-204h25c32 0 57-26 57-57V424a57 57 0 00-57-57zM181 40h465v205c0 11 9 20 20 20h177v101H181V40zm413 527c0 89-54 143-134 143-81 0-128-61-128-138 0-82 52-143 132-143 84 0 129 63 129 138zm-440 139V433h62v220h108v52h-170zm690 267H181v-193H843l0 193zm18-280a305 305 0 01-91 15c-50 0-86-12-111-37-25-23-39-59-38-99 0-90 66-142 155-142 35 0 62 7 76 13l-13 49c-15-6-33-12-63-12-51 0-90 29-90 88 0 56 35 89 86 89 14 0 25-2 30-4v-57h-42v-48h101v143zM397 570c0 53 25 91 66 91 42 0 65-40 65-92 0-49-23-91-66-91-42 0-66 40-66 93z
M192 192m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM864 160H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 480H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 800H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32z
M824 645V307c0-56-46-102-102-102h-102V102l-154 154 154 154V307h102v338c-46 20-82 67-82 123 0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123zm-51 195c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72zM384 256c0-72-61-133-133-133-72 0-133 61-133 133 0 56 36 102 82 123v266C154 666 118 712 118 768c0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123V379C348 358 384 312 384 256zM323 768c0 41-31 72-72 72-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72zM251 328c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72z
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
@@ -84,7 +89,8 @@
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
M64 363l0 204 265 0L329 460c0-11 6-18 14-20C349 437 355 437 362 441c93 60 226 149 226 149 33 22 34 60 0 82 0 0-133 89-226 149-14 9-32-3-32-18l-1-110L64 693l0 117c0 41 34 75 75 75l746 0c41 0 75-34 75-74L960 364c0-0 0-1 0-1L64 363zM64 214l0 75 650 0-33-80c-16-38-62-69-103-69l-440 0C97 139 64 173 64 214z
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
- M841 627A43 43 0 00811 555h-299v85h196l-183 183A43 43 0 00555 896h299v-85h-196l183-183zM299 170H213v512H85l171 171 171-171H299zM725 128h-85c-18 0-34 11-40 28l-117 313h91L606 384h154l32 85h91l-117-313A43 43 0 00725 128zm-88 171 32-85h26l32 85h-90z
+ M841 627A43 43 0 00811 555h-299v85h196l-183 183A43 43 0 00555 896h299v-85h-196l183-183zM299 170H213v512H85l171 171 171-171H299zM725 128h-85c-18 0-34 11-40 28l-117 313h91L606 384h154l32 85h91l-117-313A43 43 0 00725 128zm-88 171 32-85h26l32 85h-90z
+ M512 0a512 512 0 01512 512 57 57 0 01-114 0 398 398 0 10-398 398 57 57 0 010 114A512 512 0 01512 0zm367 600 121 120a57 57 0 01-80 81l-40-40V967a57 57 0 01-50 57l-7 0a57 57 0 01-57-57v-205l-40 40a57 57 0 01-75 5l-5-5a57 57 0 01-0-80l120-121a80 80 0 01113-0zM512 272a57 57 0 0157 57V499h114a57 57 0 0156 50L740 556a57 57 0 01-57 57H512a57 57 0 01-57-57v-228a57 57 0 0150-57L512 272z
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
@@ -114,6 +120,7 @@
M558 545 790 403c24-15 31-47 16-71-15-24-46-31-70-17L507 457 277 315c-24-15-56-7-71 17-15 24-7 56 17 71l232 143V819c0 28 23 51 51 51 28 0 51-23 51-51V545h0zM507 0l443 256v512L507 1024 63 768v-512L507 0z
M770 320a41 41 0 00-56-14l-252 153L207 306a41 41 0 10-43 70l255 153 2 296a41 41 0 0082 0l-2-295 255-155a41 41 0 0014-56zM481 935a42 42 0 01-42 0L105 741a42 42 0 01-21-36v-386a42 42 0 0121-36L439 89a42 42 0 0142 0l335 193a42 42 0 0121 36v87h84v-87a126 126 0 00-63-109L523 17a126 126 0 00-126 0L63 210a126 126 0 00-63 109v386a126 126 0 0063 109l335 193a126 126 0 00126 0l94-54-42-72zM1029 700h-126v-125a42 42 0 00-84 0v126h-126a42 42 0 000 84h126v126a42 42 0 1084 0v-126h126a42 42 0 000-84z
M416 587c21 0 37 17 37 37v299A37 37 0 01416 960h-299a37 37 0 01-37-37v-299c0-21 17-37 37-37h299zm448 0c21 0 37 17 37 37v299A37 37 0 01864 960h-299a37 37 0 01-37-37v-299c0-21 17-37 37-37h299zM758 91l183 189a37 37 0 010 52l-182 188a37 37 0 01-53 1l-183-189a37 37 0 010-52l182-188a37 37 0 0153-1zM416 139c21 0 37 17 37 37v299A37 37 0 01416 512h-299a37 37 0 01-37-37v-299c0-21 17-37 37-37h299z
+ M653 435l-26 119H725c9 0 13 4 13 13v47c0 9-4 13-13 13h-107l-21 115c0 9-4 13-13 13h-47c-9 0-13-4-13-13l21-111H427l-21 115c0 9-4 13-13 13H346c-9 0-13-4-13-13l21-107h-85c-4-9-9-21-13-34v-38c0-9 4-13 13-13h98l26-119H294c-9 0-13-4-13-13V375c0-9 4-13 13-13h115l13-81c0-9 4-13 13-13h43c9 0 13 4 13 13L469 363h119l13-81c0-9 4-13 13-13h47c9 0 13 4 13 13l-13 77h85c9 0 13 4 13 13v47c0 9-4 13-13 13h-98v4zM512 0C230 0 0 230 0 512c0 145 60 282 166 375L90 1024H512c282 0 512-230 512-512S794 0 512 0zm-73 559h124l26-119h-128l-21 119z
M875 128h-725A107 107 0 0043 235v555A107 107 0 00149 896h725a107 107 0 00107-107v-555A107 107 0 00875 128zm-115 640h-183v-58l25-3c15 0 19-8 14-24l-22-61H419l-28 82 39 2V768h-166v-58l18-3c18-2 22-11 26-24l125-363-40-4V256h168l160 448 39 3zM506 340l-72 218h145l-71-218h-2z
M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z
M683 537h-144v-142h-142V283H239a44 44 0 00-41 41v171a56 56 0 0014 34l321 321a41 41 0 0058 0l174-174a41 41 0 000-58zm-341-109a41 41 0 110-58a41 41 0 010 58zM649 284V142h-69v142h-142v68h142v142h69v-142h142v-68h-142z
@@ -128,6 +135,7 @@
M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z
M832 464H332V240c0-31 25-56 56-56h248c31 0 56 25 56 56v68c0 4 4 8 8 8h56c4 0 8-4 8-8v-68c0-71-57-128-128-128H388c-71 0-128 57-128 128v224h-68c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32V496c0-18-14-32-32-32zM540 701v53c0 4-4 8-8 8h-40c-4 0-8-4-8-8v-53c-12-9-20-23-20-39 0-27 22-48 48-48s48 22 48 48c0 16-8 30-20 39z
M170 831l343-342L855 831l105-105-448-448L64 726 170 831z
+ M667 607c-3-2-7-14-0-38 73-77 118-187 118-290C784 115 668 0 508 0 348 0 236 114 236 278c0 104 45 215 119 292 7 24-2 33-8 35C274 631 0 725 0 854L0 1024l1024 0 0-192C989 714 730 627 667 607L667 607z
M880 128A722 722 0 01555 13a77 77 0 00-85 0 719 719 0 01-325 115c-40 4-71 38-71 80v369c0 246 329 446 439 446 110 0 439-200 439-446V207c0-41-31-76-71-80zM465 692a36 36 0 01-53 0L305 579a42 42 0 010-57 36 36 0 0153 0l80 85L678 353a36 36 0 0153 0 42 42 0 01-0 57L465 692z
M812 864h-29V654c0-21-11-40-28-52l-133-88 134-89c18-12 28-31 28-52V164h28c18 0 32-14 32-32s-14-32-32-32H212c-18 0-32 14-32 32s14 32 32 32h30v210c0 21 11 40 28 52l133 88-134 89c-18 12-28 31-28 52V864H212c-18 0-32 14-32 32s14 32 32 32h600c18 0 32-14 32-32s-14-32-32-32zM441 566c18-12 28-31 28-52s-11-40-28-52L306 373V164h414v209l-136 90c-18 12-28 31-28 52 0 21 11 40 28 52l135 89V695c-9-7-20-13-32-19-30-15-93-41-176-41-63 0-125 14-175 38-12 6-22 12-31 18v-36l136-90z
M0 512M1024 512M512 0M512 1024M762 412v100h-500v-100h-150v200h800v-200h-150z
diff --git a/src/Resources/Images/ExternalToolIcons/plastic_merge.png b/src/Resources/Images/ExternalToolIcons/plastic_merge.png
new file mode 100644
index 00000000..0d82fc86
Binary files /dev/null and b/src/Resources/Images/ExternalToolIcons/plastic_merge.png differ
diff --git a/src/Resources/Images/ShellIcons/kitty.png b/src/Resources/Images/ShellIcons/kitty.png
new file mode 100644
index 00000000..465c2863
Binary files /dev/null and b/src/Resources/Images/ShellIcons/kitty.png differ
diff --git a/src/Resources/Images/github.png b/src/Resources/Images/github.png
index 3a7abb16..d3c211da 100644
Binary files a/src/Resources/Images/github.png and b/src/Resources/Images/github.png differ
diff --git a/src/Resources/Images/unreal.png b/src/Resources/Images/unreal.png
index 4faae92b..01ceeb31 100644
Binary files a/src/Resources/Images/unreal.png and b/src/Resources/Images/unreal.png differ
diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml
index aff8ffc5..99d9f77c 100644
--- a/src/Resources/Locales/de_DE.axaml
+++ b/src/Resources/Locales/de_DE.axaml
@@ -2,41 +2,29 @@
+
Info
Über SourceGit
- • Erstellt mit
- • Grafik gerendert durch
- • Texteditor von
- • Monospace-Schriftarten von
- • Quelltext findest du auf
Open Source & freier Git GUI Client
Worktree hinzufügen
- Was auschecken:
- Existierender Branch
- Neuen Branch erstellen
Ordner:
Pfad für diesen Worktree. Relativer Pfad wird unterstützt.
Branch Name:
Optional. Standard ist der Zielordnername.
Branch verfolgen:
Remote-Branch verfolgen
+ Was auschecken:
+ Neuen Branch erstellen
+ Existierender Branch
OpenAI Assistent
Neu generieren
Verwende OpenAI, um Commit-Nachrichten zu generieren
Als Commit-Nachricht verwenden
Patch
- Fehler
- Fehler werfen und anwenden des Patches verweigern
- Alle Fehler
- Ähnlich wie 'Fehler', zeigt aber mehr an
Patch-Datei:
Wähle die anzuwendende .patch-Datei
Ignoriere Leerzeichenänderungen
- Keine Warnungen
- Keine Warnung vor Leerzeichen am Zeilenende
Patch anwenden
- Warnen
- Gibt eine Warnung für ein paar solcher Fehler aus, aber wendet es an
Leerzeichen:
Stash anwenden
Nach dem Anwenden löschen
@@ -51,17 +39,25 @@
ALS UNVERÄNDERT ANGENOMMENE DATEIEN
KEINE ALS UNVERÄNDERT ANGENOMMENEN DATEIEN
ENTFERNEN
+ Bild laden...
+ Aktualisieren
BINÄRE DATEI NICHT UNTERSTÜTZT!!!
+ Bisect
+ Abbrechen
+ Schlecht
+ Bisecting. Ist der aktuelle HEAD gut oder fehlerhaft?
+ Gut
+ Überspringen
+ Bisecting. Aktuellen Commit als gut oder schlecht markieren und einen anderen auschecken.
Blame
BLAME WIRD BEI DIESER DATEI NICHT UNTERSTÜTZT!!!
Auschecken von ${0}$...
- Mit HEAD vergleichen
+ Mit ${0}$ vergleichen
Mit Worktree vergleichen
Branch-Namen kopieren
Benutzerdefinierte Aktion
Lösche ${0}$...
Lösche alle ausgewählten {0} Branches
- Alle Änderungen verwerfen
Fast-Forward zu ${0}$
Fetche ${0}$ in ${1}$ hinein...
Git Flow - Abschließen ${0}$
@@ -72,12 +68,14 @@
Push ${0}$
Rebase ${0}$ auf ${1}$...
Benenne ${0}$ um...
+ Setze ${0}$ zurück auf ${1}$...
Setze verfolgten Branch...
Branch Vergleich
+ Ungültiger upstream!
Bytes
ABBRECHEN
- Auf diese Revision zurücksetzen
Auf Vorgänger-Revision zurücksetzen
+ Auf diese Revision zurücksetzen
Generiere Commit-Nachricht
ANZEIGE MODUS ÄNDERN
Zeige als Datei- und Ordnerliste
@@ -85,12 +83,15 @@
Zeige als Dateisystembaum
Branch auschecken
Commit auschecken
- Warnung: Beim Auschecken eines Commits wird dein HEAD losgelöst (detached) sein!
Commit:
- Branch:
+ Warnung: Beim Auschecken eines Commits wird dein HEAD losgelöst (detached) sein!
Lokale Änderungen:
Verwerfen
Stashen & wieder anwenden
+ Alle Submodule updaten
+ Branch:
+ Auschecken & Fast-Forward
+ Fast-Forward zu:
Cherry Pick
Quelle an Commit-Nachricht anhängen
Commit(s):
@@ -109,17 +110,21 @@
Repository URL:
SCHLIESSEN
Editor
+ Commit auschecken
Diesen Commit cherry-picken
Mehrere cherry-picken
- Commit auschecken
Mit HEAD vergleichen
Mit Worktree vergleichen
- Info kopieren
- SHA kopieren
+ Author
+ Committer
+ Information
+ SHA
+ Betreff
Benutzerdefinierte Aktion
Interactives Rebase von ${0}$ auf diesen Commit
Merge in ${0}$ hinein
Merge ...
+ Push ${0}$ zu ${1}$
Rebase von ${0}$ auf diesen Commit
Reset ${0}$ auf diesen Commit
Commit rückgängig machen
@@ -128,6 +133,7 @@
Squash in den Vorgänger
Squash Nachfolger Commits bis hier
ÄNDERUNGEN
+ geänderte Datei(en)
Änderungen durchsuchen...
DATEIEN
LFS DATEI
@@ -146,12 +152,13 @@
REFS
SHA
Im Browser öffnen
- Commit-Nachricht
Details
+ Betreff
+ Commit-Nachricht
Repository Einstellungen
COMMIT TEMPLATE
- Template Name:
Template Inhalt:
+ Template Name:
BENUTZERDEFINIERTE AKTION
Argumente:
${REPO} - Repository Pfad; ${SHA} - SHA-Wert des selektierten Commits
@@ -161,19 +168,22 @@
Branch
Commit
Repository
+ Auf Beenden der Aktion warten
Email Adresse
Email Adresse
GIT
Remotes automatisch fetchen
Minute(n)
Standard Remote
+ Bevorzugter Merge Modus
TICKETSYSTEM
+ Beispiel Azure DevOps Rule hinzufügen
Beispiel für Gitee Issue Regel einfügen
Beispiel für Gitee Pull Request Regel einfügen
Beispiel für Github-Regel hinzufügen
- Beispiel für Jira-Regel hinzufügen
Beispiel für Gitlab Issue Regel einfügen
Beispiel für Gitlab Merge Request einfügen
+ Beispiel für Jira-Regel hinzufügen
Neue Regel
Ticketnummer Regex-Ausdruck:
Name:
@@ -188,7 +198,12 @@
Benutzername für dieses Repository
Arbeitsplätze
Farbe
+ Name
Zuletzt geöffnete Tabs beim Starten wiederherstellen
+ WEITER
+ Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty)?
+ ALLES STAGEN & COMMITTEN
+ Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty) oder alle Änderungen stagen und dann committen?
Konventionelle Commit-Hilfe
Breaking Change:
Geschlossenes Ticket:
@@ -198,8 +213,8 @@
Typ der Änderung:
Kopieren
Kopiere gesamten Text
+ Ganzen Pfad kopieren
Pfad kopieren
- Dateinamen kopieren
Branch erstellen...
Basierend auf:
Erstellten Branch auschecken
@@ -210,6 +225,7 @@
Branch-Namen eingeben.
Leerzeichen werden durch Bindestriche ersetzt.
Lokalen Branch erstellen
+ Überschreibe existierenden Branch
Tag erstellen...
Neuer Tag auf:
Mit GPG signieren
@@ -224,6 +240,9 @@
Ohne Anmerkung
Halte Strg gedrückt, um direkt auszuführen
Ausschneiden
+ De-initialisiere Submodul
+ Erzwinge De-Initialisierung, selbst wenn es lokale Änderungen enthält.
+ Submodul:
Branch löschen
Branch:
Du löscht gerade einen Remote-Branch!!!
@@ -234,9 +253,9 @@
Remote:
Pfad:
Ziel:
- Bestätige löschen von Gruppe
Alle Nachfolger werden aus der Liste entfernt.
Dadurch wird es nur aus der Liste entfernt, nicht von der Festplatte!
+ Bestätige löschen von Gruppe
Bestätige löschen von Repository
Lösche Submodul
Submodul Pfad:
@@ -248,7 +267,9 @@
ALT
Kopieren
Dateimodus geändert
+ Erste Differenz
Ignoriere Leerzeichenänderungen
+ Letzte Differenz
LFS OBJEKT ÄNDERUNG
Nächste Änderung
KEINE ÄNDERUNG ODER NUR ZEILEN-ENDE ÄNDERUNGEN
@@ -257,6 +278,7 @@
Zeige versteckte Symbole
Nebeneinander
SUBMODUL
+ GELÖSCHT
NEU
Seiten wechseln
Syntax Hervorhebung
@@ -281,7 +303,6 @@
Ausgewähltes Repository bearbeiten
Führe benutzerdefinierte Aktion aus
Name der Aktion:
- Fast-Forward (ohne Auschecken)
Fetch
Alle Remotes fetchen
Aktiviere '--force' Option
@@ -303,11 +324,11 @@
Unstage
{0} Dateien unstagen
Änderungen in ausgewählten Zeilen unstagen
- "Ihre" verwenden (checkout --theirs)
"Meine" verwenden (checkout --ours)
+ "Ihre" verwenden (checkout --theirs)
Datei Historie
- INHALT
ÄNDERUNGEN
+ INHALT
Git-Flow
Development-Branch:
Feature:
@@ -316,6 +337,8 @@
FLOW - Finish Hotfix
FLOW - Finish Release
Ziel:
+ Push zu Remote(s) nach Durchführung des Finish
+ Squash beim Merge
Hotfix:
Hotfix-Prefix:
Git-Flow initialisieren
@@ -337,8 +360,8 @@
Eigenes Muster:
Verfolgungsmuster zu Git LFS hinzufügen
Fetch
- LFS Objekte fetchen
Führt `git lfs fetch` aus um Git LFS Objekte herunterzuladen. Das aktualisiert nicht die Arbeitskopie.
+ LFS Objekte fetchen
Installiere Git LFS Hooks
Sperren anzeigen
Keine gesperrten Dateien
@@ -350,11 +373,11 @@
Prune
Führt `git lfs prune` aus um alte LFS Dateien von lokalem Speicher zu löschen
Pull
- LFS Objekte pullen
Führt `git lfs pull` aus um alle Git LFS Dasteien für aktuellen Ref & Checkout herunterzuladen
+ LFS Objekte pullen
Push
- LFS Objekte pushen
Pushe große Dateien in der Warteschlange zum Git LFS Endpunkt
+ LFS Objekte pushen
Remote:
Verfolge alle '{0}' Dateien
Verfolge alle *{0} Dateien
@@ -373,10 +396,12 @@
Aktuelles Popup schließen
Klone neues Repository
Aktuellen Tab schließen
- Zum vorherigen Tab wechseln
Zum nächsten Tab wechseln
+ Zum vorherigen Tab wechseln
Neuen Tab erstellen
Einstellungen öffnen
+ Aktiven Arbeitsplatz wechseln
+ Aktiven Tab wechseln
REPOSITORY
Gestagte Änderungen committen
Gestagte Änderungen committen und pushen
@@ -385,11 +410,11 @@
Ausgewählte Änderungen verwerfen
Fetch, wird direkt ausgeführt
Dashboard Modus (Standard)
+ Commit-Suchmodus
Pull, wird direkt ausgeführt
Push, wird direkt ausgeführt
Erzwinge Neuladen des Repositorys
Ausgewählte Änderungen stagen/unstagen
- Commit-Suchmodus
Wechsle zu 'Änderungen'
Wechsle zu 'Verlauf'
Wechsle zu 'Stashes'
@@ -397,10 +422,11 @@
Suchpanel schließen
Suche nächste Übereinstimmung
Suche vorherige Übereinstimmung
+ Öffne mit externem Diff/Merge Tool
Öffne Suchpanel
+ Verwerfen
Stagen
Unstagen
- Verwerfen
Initialisiere Repository
Pfad:
Cherry-Pick wird durchgeführt.
@@ -412,13 +438,16 @@
Revert wird durchgeführt.
Reverte commit
Interaktiver Rebase
- Ziel Branch:
Auf:
- In Browser öffnen
+ Ziel Branch:
Link kopieren
+ In Browser öffnen
FEHLER
INFO
+ Arbeitsplätze
+ Tabs
Branch mergen
+ Merge-Nachricht anpassen
Ziel-Branch:
Merge Option:
Quelle:
@@ -441,26 +470,29 @@
Kopiere Repository-Pfad
Repositories
Einfügen
- Gerade eben
- Vor {0} Minuten
+ Vor {0} Tagen
Vor 1 Stunde
Vor {0} Stunden
- Gestern
- Vor {0} Tagen
+ Gerade eben
Letzter Monat
- Vor {0} Monaten
Leztes Jahr
+ Vor {0} Minuten
+ Vor {0} Monaten
Vor {0} Jahren
+ Gestern
+ Verwende 'Shift+Enter' um eine neue Zeile einzufügen. 'Enter' ist das Kürzel für den OK Button
Einstellungen
OPEN AI
Analysierung des Diff Befehl
API Schlüssel
Generiere Nachricht Befehl
+ Modell
Name
Server
- Modell
+ Streaming aktivieren
DARSTELLUNG
Standardschriftart
+ Editor Tab Breite
Schriftgröße
Standard
Texteditor
@@ -481,6 +513,7 @@
Commit-Historie
Zeige Autor Zeitpunkt anstatt Commit Zeitpunkt
Zeige Nachfolger in den Commit Details
+ Zeige Tags im Commit Graph
Längenvorgabe für Commit-Nachrichten
GIT
Aktiviere Auto-CRLF
@@ -488,36 +521,36 @@
Benutzer Email
Globale Git Benutzer Email
Aktivere --prune beim fetchen
+ Aktiviere --ignore-cr-at-eol beim Unterschied
+ Diese App setzt Git (>= 2.25.1) voraus
Installationspfad
Aktiviere HTTP SSL Verifizierung
Benutzername
Globaler Git Benutzername
Git Version
- Diese App setzt Git (>= 2.23.0) voraus
GPG SIGNIERUNG
Commit-Signierung
- Tag-Signierung
GPG Format
GPG Installationspfad
Installationspfad zum GPG Programm
+ Tag-Signierung
Benutzer Signierungsschlüssel
GPG Benutzer Signierungsschlüssel
EINBINDUNGEN
SHELL/TERMINAL
- Shell/Terminal
Pfad
+ Shell/Terminal
Remote löschen
Ziel:
Worktrees löschen
- Worktree Informationen in `$GIT_DIR/worktrees` löschen
+ Worktree Informationen in `$GIT_COMMON_DIR/worktrees` löschen
Pull
Remote-Branch:
- Alle Branches fetchen
Lokaler Branch:
Lokale Änderungen:
Verwerfen
Stashen & wieder anwenden
- Ohne Tags fetchen
+ Alle Submodule aktualisieren
Remote:
Pull (Fetch & Merge)
Rebase anstatt Merge verwenden
@@ -526,6 +559,8 @@
Erzwinge Push
Lokaler Branch:
Remote:
+ Revision:
+ Push Revision zu Remote-Branch
Push
Remote-Branch:
Remote-Branch verfolgen
@@ -539,7 +574,6 @@
Lokale Änderungen stashen & wieder anwenden
Auf:
Rebase:
- Aktualisieren
Remote hinzufügen
Remote bearbeiten
Name:
@@ -561,13 +595,18 @@
Branch:
ABBRECHEN
Änderungen automatisch von Remote fetchen...
+ Sortieren
+ Nach Commit Datum
+ Nach Name
Aufräumen (GC & Prune)
Führt `git gc` auf diesem Repository aus.
Filter aufheben
+ Löschen
Repository Einstellungen
WEITER
Benutzerdefinierte Aktionen
Keine benutzerdefinierten Aktionen
+ Alle Änderungen verwerfen
Aktiviere '--reflog' Option
Öffne im Datei-Browser
Suche Branches/Tags/Submodule
@@ -595,10 +634,12 @@
Commit suchen
Autor
Committer
+ Inhalt
Dateiname
Commit-Nachricht
SHA
Aktueller Branch
+ Submodule als Baum anzeigen
Zeige Tags als Baum
ÜBERSPRINGEN
Statistiken
@@ -608,11 +649,12 @@
TAGS
NEUER TAG
Nach Erstellungsdatum
- Nach Namen (Aufsteigend)
- Nach Namen (Absteigend)
+ Nach Namen
Sortiere
Öffne im Terminal
Verwende relative Zeitangaben in Verlauf
+ Logs ansehen
+ Öffne '{0}' im Browser
WORKTREES
WORKTREE HINZUFÜGEN
PRUNE
@@ -621,12 +663,14 @@
Rücksetzmodus:
Verschiebe zu:
Aktueller Branch:
+ Reset Branch (ohne Checkout)
+ Auf:
+ Branch:
Zeige im Datei-Explorer
Commit rückgängig machen
Commit:
Commit Änderungen rückgängig machen
Commit Nachricht umformulieren
- Verwende 'Shift+Enter' um eine neue Zeile einzufügen. 'Enter' ist das Kürzel für den OK Button
Bitte warten...
SPEICHERN
Speichern als...
@@ -652,18 +696,17 @@
Pfad zum privaten SSH Schlüssel
START
Stash
- Automatisch wiederherstellen nach dem Stashen
- Die Arbeitsdateien bleiben unverändert, aber ein Stash wird gespeichert.
Inklusive nicht-verfolgter Dateien
- Behalte gestagte Dateien
Name:
- Optional. Name dieses Stashes
+ Optional. Informationen zu dieses Stashes
+ Modus:
Nur gestagte Änderungen
Gestagte und unstagte Änderungen der ausgewähleten Datei(en) werden gestasht!!!
Lokale Änderungen stashen
Anwenden
+ Kopiere Nachricht
Entfernen
- Anwenden und entfernen
+ Als Path speichern...
Stash entfernen
Entfernen:
Stashes
@@ -672,32 +715,43 @@
Statistiken
COMMITS
COMMITTER
+ ÜBERSICHT
MONAT
WOCHE
- COMMITS:
AUTOREN:
- ÜBERSICHT
+ COMMITS:
SUBMODULE
Submodul hinzufügen
Relativen Pfad kopieren
+ De-initialisiere Submodul
Untergeordnete Submodule fetchen
Öffne Submodul Repository
Relativer Pfad:
Relativer Ordner um dieses Submodul zu speichern.
Submodul löschen
+ STATUS
+ geändert
+ nicht initialisiert
+ Revision geändert
+ nicht gemerged
+ URL
OK
Tag-Namen kopieren
Tag-Nachricht kopieren
Lösche ${0}$...
Merge ${0}$ in ${1}$ hinein...
Pushe ${0}$...
- URL:
Submodule aktualisieren
Alle Submodule
Initialisiere wenn nötig
Rekursiv
Submodul:
Verwende `--remote` Option
+ URL:
+ Logs
+ ALLES LÖSCHEN
+ Kopieren
+ Löschen
Warnung
Willkommensseite
Erstelle Gruppe
@@ -717,7 +771,7 @@
Git Ignore
Ignoriere alle *{0} Dateien
Ignoriere *{0} Datein im selben Ordner
- Ignoriere Dateien im selben Ordner
+ Ignoriere nicht-verfolgte Dateien in diesem Ordner
Ignoriere nur diese Datei
Amend
Du kannst diese Datei jetzt stagen.
@@ -727,12 +781,18 @@
Klick-Ereignis auslösen
Commit (Bearbeitung)
Alle Änderungen stagen und committen
- Leerer Commit erkannt! Fortfahren (--allow-empty)?
+ Du hast {0} Datei(en) gestaged, aber nur {1} werden angezeigt ({2} sind herausgefiltert). Willst du trotzdem fortfahren?
KONFLIKTE ERKANNT
+ EXTERNES MERGE-TOOL ÖFFNEN
+ ALLE KONFLIKTE IN EXTERNEM MERGE-TOOL ÖFFNEN
DATEI KONFLIKTE GELÖST
+ MEINE VERSION VERWENDEN
+ DEREN VERSION VERWENDEN
NICHT-VERFOLGTE DATEIEN INKLUDIEREN
KEINE BISHERIGEN COMMIT-NACHRICHTEN
KEINE COMMIT TEMPLATES
+ Autor zurücksetzen
+ Rechtsklick auf selektierte Dateien und wähle die Konfliktlösungen aus.
SignOff
GESTAGED
UNSTAGEN
@@ -740,9 +800,8 @@
UNSTAGED
STAGEN
ALLES STAGEN
- ALS UNVERÄNDERT ANGENOMMENE ANZEIGEN
+ ALS UNVERÄNDERT ANGENOMMENE ANZEIGEN
Template: ${0}$
- Rechtsklick auf selektierte Dateien und wähle die Konfliktlösungen aus.
ARBEITSPLATZ:
Arbeitsplätze konfigurieren...
WORKTREE
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 818bd9bb..d4b4d26f 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -1,39 +1,26 @@
About
About SourceGit
- • Build with
- • Chart is rendered by
- • TextEditor from
- • Monospace fonts come from
- • Source code can be found at
Opensource & Free Git GUI Client
Add Worktree
- What to Checkout:
- Existing Branch
- Create New Branch
Location:
Path for this worktree. Relative path is supported.
Branch Name:
Optional. Default is the destination folder name.
Track Branch:
Tracking remote branch
+ What to Checkout:
+ Create New Branch
+ Existing Branch
AI Assistant
RE-GENERATE
Use AI to generate commit message
APPLY AS COMMIT MESSAGE
Patch
- Error
- Raise errors and refuses to apply the patch
- Error All
- Similar to 'error', but shows more
Patch File:
Select .patch file to apply
Ignore whitespace changes
- No Warn
- Turns off the trailing whitespace warning
Apply Patch
- Warn
- Outputs warnings for a few such errors, but applies
Whitespace:
Apply Stash
Delete after applying
@@ -48,17 +35,25 @@
FILES ASSUME UNCHANGED
NO FILES ASSUMED AS UNCHANGED
REMOVE
+ Load Image...
+ Refresh
BINARY FILE NOT SUPPORTED!!!
+ Bisect
+ Abort
+ Bad
+ Bisecting. Is current HEAD good or bad?
+ Good
+ Skip
+ Bisecting. Mark current commit as good or bad and checkout another one.
Blame
BLAME ON THIS FILE IS NOT SUPPORTED!!!
Checkout ${0}$...
- Compare with HEAD
+ Compare with ${0}$
Compare with Worktree
Copy Branch Name
Custom Action
Delete ${0}$...
Delete selected {0} branches
- Discard all changes
Fast-Forward to ${0}$
Fetch ${0}$ into ${1}$...
Git Flow - Finish ${0}$
@@ -69,13 +64,14 @@
Push ${0}$
Rebase ${0}$ on ${1}$...
Rename ${0}$...
+ Reset ${0}$ to ${1}$...
Set Tracking Branch...
Branch Compare
Invalid upstream!
Bytes
CANCEL
- Reset to This Revision
Reset to Parent Revision
+ Reset to This Revision
Generate commit message
CHANGE DISPLAY MODE
Show as File and Dir List
@@ -83,12 +79,15 @@
Show as Filesystem Tree
Checkout Branch
Checkout Commit
- Warning: By doing a commit checkout, your Head will be detached
Commit:
- Branch:
+ Warning: By doing a commit checkout, your Head will be detached
Local Changes:
Discard
Stash & Reapply
+ Update all submodules
+ Branch:
+ Checkout & Fast-Forward
+ Fast-Forward to:
Cherry Pick
Append source to commit message
Commit(s):
@@ -107,17 +106,21 @@
Repository URL:
CLOSE
Editor
+ Checkout Commit
Cherry-Pick Commit
Cherry-Pick ...
- Checkout Commit
Compare with HEAD
Compare with Worktree
- Copy Info
- Copy SHA
+ Author
+ Committer
+ Information
+ SHA
+ Subject
Custom Action
Interactively Rebase ${0}$ on Here
Merge to ${0}$
Merge ...
+ Push ${0}$ to ${1}$
Rebase ${0}$ on Here
Reset ${0}$ to Here
Revert Commit
@@ -126,6 +129,7 @@
Squash into Parent
Squash Children into Here
CHANGES
+ changed file(s)
Search Changes...
FILES
LFS File
@@ -144,12 +148,13 @@
REFS
SHA
Open in Browser
- Enter commit subject
Description
+ SUBJECT
+ Enter commit subject
Repository Configure
COMMIT TEMPLATE
- Template Name:
Template Content:
+ Template Name:
CUSTOM ACTION
Arguments:
${REPO} - Repository's path; ${BRANCH} - Selected branch; ${SHA} - Selected commit's SHA
@@ -166,7 +171,9 @@
Fetch remotes automatically
Minute(s)
Default Remote
+ Preferred Merge Mode
ISSUE TRACKER
+ Add Sample Azure DevOps Rule
Add Sample Gitee Issue Rule
Add Sample Gitee Pull Request Rule
Add Sample Github Rule
@@ -187,7 +194,12 @@
User name for this repository
Workspaces
Color
+ Name
Restore tabs on startup
+ CONTINUE
+ Empty commit detected! Do you want to continue (--allow-empty)?
+ STAGE ALL & COMMIT
+ Empty commit detected! Do you want to continue (--allow-empty) or stage all then commit?
Conventional Commit Helper
Breaking Change:
Closed Issue:
@@ -197,8 +209,8 @@
Type of Change:
Copy
Copy All Text
+ Copy Full Path
Copy Path
- Copy File Name
Create Branch...
Based On:
Check out the created branch
@@ -209,6 +221,7 @@
Enter branch name.
Spaces will be replaced with dashes.
Create Local Branch
+ Overwrite existing branch
Create Tag...
New Tag At:
GPG signing
@@ -223,6 +236,9 @@
lightweight
Hold Ctrl to start directly
Cut
+ De-initialize Submodule
+ Force de-init even if it contains local changes.
+ Submodule:
Delete Branch
Branch:
You are about to delete a remote branch!!!
@@ -234,8 +250,8 @@
Path:
Target:
All children will be removed from list.
- Confirm Deleting Group
This will only remove it from list, not from disk!
+ Confirm Deleting Group
Confirm Deleting Repository
Delete Submodule
Submodule Path:
@@ -248,7 +264,7 @@
Copy
File Mode Changed
First Difference
- Ignore Whitespace Change
+ Ignore All Whitespace Changes
Last Difference
LFS OBJECT CHANGE
Next Difference
@@ -258,6 +274,7 @@
Show hidden symbols
Side-By-Side Diff
SUBMODULE
+ DELETED
NEW
Swap
Syntax Highlighting
@@ -282,7 +299,6 @@
Edit Selected Repository
Run Custom Action
Action Name:
- Fast-Forward (without checkout)
Fetch
Fetch all remotes
Force override local refs
@@ -304,11 +320,11 @@
Unstage
Unstage {0} files
Unstage Changes in Selected Line(s)
- Use Theirs (checkout --theirs)
Use Mine (checkout --ours)
+ Use Theirs (checkout --theirs)
File History
- CONTENT
CHANGE
+ CONTENT
Git-Flow
Development Branch:
Feature:
@@ -317,6 +333,8 @@
FLOW - Finish Hotfix
FLOW - Finish Release
Target:
+ Push to remote(s) after performing finish
+ Squash during merge
Hotfix:
Hotfix Prefix:
Initialize Git-Flow
@@ -338,8 +356,8 @@
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.
+ Fetch LFS Objects
Install Git LFS hooks
Show Locks
No Locked Files
@@ -351,11 +369,11 @@
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
+ Pull LFS Objects
Push
- Push LFS Objects
Push queued large files to the Git LFS endpoint
+ Push LFS Objects
Remote:
Track files named '{0}'
Track all *{0} files
@@ -374,10 +392,12 @@
Cancel current popup
Clone new repository
Close current page
- Go to previous page
Go to next page
+ Go to previous page
Create new page
Open Preferences dialog
+ Switch active workspace
+ Switch active page
REPOSITORY
Commit staged changes
Commit and push staged changes
@@ -386,11 +406,11 @@
Discard selected changes
Fetch, starts directly
Dashboard mode (Default)
+ Commit search mode
Pull, starts directly
Push, starts directly
Force to reload this repository
Stage/Unstage selected changes
- Commit search mode
Switch to 'Changes'
Switch to 'Histories'
Switch to 'Stashes'
@@ -398,10 +418,11 @@
Close search panel
Find next match
Find previous match
+ Open with external diff/merge tool
Open search panel
+ Discard
Stage
Unstage
- Discard
Initialize Repository
Path:
Cherry-Pick in progress.
@@ -413,13 +434,16 @@
Revert in progress.
Reverting commit
Interactive Rebase
- Target Branch:
On:
- Open in Browser
+ Target Branch:
Copy Link
+ Open in Browser
ERROR
NOTICE
+ Workspaces
+ Pages
Merge Branch
+ Customize merge message
Into:
Merge Option:
Source:
@@ -442,16 +466,17 @@
Copy Repository Path
Repositories
Paste
- Just now
- {0} minutes ago
+ {0} days ago
1 hour ago
{0} hours ago
- Yesterday
- {0} days ago
+ Just now
Last month
- {0} months ago
Last year
+ {0} minutes ago
+ {0} months ago
{0} years ago
+ Yesterday
+ Use 'Shift+Enter' to input a new line. 'Enter' is the hotkey of OK button
Preferences
AI
Analyze Diff Prompt
@@ -484,6 +509,7 @@
History Commits
Show author time instead of commit time in graph
Show children in the commit details
+ Show tags in commit graph
Subject Guide Length
GIT
Enable Auto CRLF
@@ -491,36 +517,36 @@
User Email
Global git user email
Enable --prune on fetch
+ Enable --ignore-cr-at-eol in diff
+ Git (>= 2.25.1) is required by this app
Install Path
Enable HTTP SSL Verify
User Name
Global git user name
Git version
- Git (>= 2.23.0) is required by this app
GPG SIGNING
Commit GPG signing
- Tag GPG signing
GPG Format
Program Install Path
Input path for installed gpg program
+ Tag GPG signing
User Signing Key
User's gpg signing key
INTEGRATION
SHELL/TERMINAL
- Shell/Terminal
Path
+ Shell/Terminal
Prune Remote
Target:
Prune Worktrees
- Prune worktree information in `$GIT_DIR/worktrees`
+ Prune worktree information in `$GIT_COMMON_DIR/worktrees`
Pull
Remote Branch:
- Fetch all branches
Into:
Local Changes:
Discard
Stash & Reapply
- Fetch without tags
+ Update all submodules
Remote:
Pull (Fetch & Merge)
Use rebase instead of merge
@@ -529,6 +555,8 @@
Force push
Local Branch:
Remote:
+ Revision:
+ Push Revision To Remote
Push Changes To Remote
Remote Branch:
Set as tracking branch
@@ -542,7 +570,6 @@
Stash & reapply local changes
On:
Rebase:
- Refresh
Add Remote
Edit Remote
Name:
@@ -564,13 +591,18 @@
Branch:
ABORT
Auto fetching changes from remotes...
+ Sort
+ By Committer Date
+ By Name
Cleanup(GC & Prune)
Run `git gc` command for this repository.
Clear all
+ Clear
Configure this repository
CONTINUE
Custom Actions
No Custom Actions
+ Discard all changes
Enable '--reflog' Option
Open in File Browser
Search Branches/Tags/Submodules
@@ -594,42 +626,47 @@
Open in External Tools
Refresh
REMOTES
- ADD REMOTE
+ Add Remote
Search Commit
Author
Committer
+ Content
File
Message
SHA
Current Branch
+ Show Submodules as Tree
Show Tags as Tree
SKIP
Statistics
SUBMODULES
- ADD SUBMODULE
- UPDATE SUBMODULE
+ Add Submodule
+ Update Submodule
TAGS
- NEW TAG
+ New Tag
By Creator Date
- By Name (Ascending)
- By Name (Descending)
+ By Name
Sort
Open in Terminal
Use relative time in histories
+ View Logs
+ Visit '{0}' in Browser
WORKTREES
- ADD WORKTREE
- PRUNE
+ Add Worktree
+ Prune
Git Repository URL
Reset Current Branch To Revision
Reset Mode:
Move To:
Current Branch:
+ Reset Branch (Without Checkout)
+ Move To:
+ 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...
@@ -655,18 +692,16 @@
Private SSH key store path
START
Stash
- Auto-restore after stashing
- Your working files remain unchanged, but a stash is saved.
Include untracked files
- Keep staged files
Message:
- Optional. Name of this stash
+ Optional. Message of this stash
+ Mode:
Only staged changes
Both staged and unstaged changes of selected file(s) will be stashed!!!
Stash Local Changes
Apply
+ Copy Message
Drop
- Pop
Save as Patch...
Drop Stash
Drop:
@@ -676,32 +711,43 @@
Statistics
COMMITS
COMMITTER
+ OVERVIEW
MONTH
WEEK
- COMMITS:
AUTHORS:
- OVERVIEW
+ COMMITS:
SUBMODULES
Add Submodule
Copy Relative Path
+ De-initialize Submodule
Fetch nested submodules
Open Submodule Repository
Relative Path:
Relative folder to store this module.
Delete Submodule
+ STATUS
+ modified
+ not initialized
+ revision changed
+ unmerged
+ URL
OK
Copy Tag Name
Copy Tag Message
Delete ${0}$...
Merge ${0}$ into ${1}$...
Push ${0}$...
- URL:
Update Submodules
All submodules
Initialize as needed
Recursively
Submodule:
Use --remote option
+ URL:
+ Logs
+ CLEAR ALL
+ Copy
+ Delete
Warning
Welcome Page
Create Group
@@ -721,7 +767,7 @@
Git Ignore
Ignore all *{0} files
Ignore *{0} files in the same folder
- Ignore files in the same folder
+ Ignore untracked files in this folder
Ignore this file only
Amend
You can stage this file now.
@@ -731,12 +777,18 @@
Trigger click event
Commit (Edit)
Stage all changes and commit
- Empty commit detected! Do you want to continue (--allow-empty)?
+ You have staged {0} file(s) but only {1} file(s) displayed ({2} files are filtered out). Do you want to continue?
CONFLICTS DETECTED
+ OPEN EXTERNAL MERGETOOL
+ OPEN ALL CONFLICTS IN EXTERNAL MERGETOOL
FILE CONFLICTS ARE RESOLVED
+ USE MINE
+ USE THEIRS
INCLUDE UNTRACKED FILES
NO RECENT INPUT MESSAGES
NO COMMIT TEMPLATES
+ Reset Author
+ Right-click the selected file(s), and make your choice to resolve conflicts.
SignOff
STAGED
UNSTAGE
@@ -744,9 +796,8 @@
UNSTAGED
STAGE
STAGE ALL
- VIEW ASSUME UNCHANGED
+ VIEW ASSUME UNCHANGED
Template: ${0}$
- Right-click the selected file(s), and make your choice to resolve conflicts.
WORKSPACE:
Configure Workspaces...
WORKTREE
diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml
index e909a14e..d66e515a 100644
--- a/src/Resources/Locales/es_ES.axaml
+++ b/src/Resources/Locales/es_ES.axaml
@@ -2,41 +2,29 @@
+
Acerca de
Acerca de SourceGit
- • Construido con
- • El gráfico es renderizado por
- • Editor de texto de
- • Las fuentes monoespaciadas provienen de
- • El código fuente se puede encontrar en
Cliente Git GUI de código abierto y gratuito
Agregar Worktree
- Qué Checkout:
- Rama Existente
- Crear Nueva Rama
Ubicación:
Ruta para este worktree. Se admite ruta relativa.
Nombre de la Rama:
Opcional. Por defecto es el nombre de la carpeta de destino.
Rama de Seguimiento:
Seguimiento de rama remota
+ Qué Checkout:
+ Crear Nueva Rama
+ Rama Existente
Asistente OpenAI
RE-GENERAR
Usar OpenAI para generar mensaje de commit
APLICAR CÓMO MENSAJE DE COMMIT
Aplicar Parche
- Error
- Genera errores y se niega a aplicar el parche
- Error Todo
- Similar a 'error', pero muestra más
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 Parche
- Advertencia
- Genera advertencias para algunos de estos errores, pero aplica
Espacios en Blanco:
Aplicar Stash
Borrar después de aplicar
@@ -51,17 +39,25 @@
ARCHIVOS ASUMIDOS COMO SIN CAMBIOS
NO HAY ARCHIVOS ASUMIDOS COMO SIN CAMBIOS
REMOVER
+ Cargar Imagen...
+ Refrescar
¡ARCHIVO BINARIO NO SOPORTADO!
+ Bisect
+ Abortar
+ Malo
+ Bisecting. ¿Es el HEAD actual bueno o malo?
+ Bueno
+ Saltar
+ Bisecting. Marcar el commit actual cómo bueno o malo y revisar otro.
Blame
¡BLAME EN ESTE ARCHIVO NO SOPORTADO!
Checkout ${0}$...
- Comparar con HEAD
+ Comparar con ${0}$
Comparar con Worktree
Copiar Nombre de Rama
Acción personalizada
Eliminar ${0}$...
Eliminar {0} ramas seleccionadas
- Descartar todos los cambios
Fast-Forward a ${0}$
Fetch ${0}$ en ${1}$...
Git Flow - Finalizar ${0}$
@@ -72,13 +68,14 @@
Push ${0}$
Rebase ${0}$ en ${1}$...
Renombrar ${0}$...
+ Resetear ${0}$ a ${1}$...
Establecer Rama de Seguimiento...
Comparar Ramas
¡Upstream inválido!
Bytes
CANCELAR
- Resetear a Esta Revisión
Resetear a Revisión Padre
+ Resetear a Esta Revisión
Generar mensaje de commit
CAMBIAR MODO DE VISUALIZACIÓN
Mostrar como Lista de Archivos y Directorios
@@ -86,12 +83,15 @@
Mostrar como Árbol de Sistema de Archivos
Checkout Rama
Checkout Commit
- Advertencia: Al hacer un checkout de commit, tu Head se separará
Commit:
- Rama:
+ Advertencia: Al hacer un checkout de commit, tu Head se separará
Cambios Locales:
Descartar
Stash & Reaplicar
+ Actualizar todos los submódulos
+ Rama:
+ Checkout & Fast-Forward
+ Fast-Forward a:
Cherry Pick
Añadir fuente al mensaje de commit
Commit(s):
@@ -106,17 +106,20 @@
Nombre Local:
Nombre del repositorio. Opcional.
Carpeta Padre:
- Inicializar y actualizar submodulos
+ Inicializar y actualizar submódulos
URL del Repositorio:
CERRAR
Editor
+ Checkout Commit
Cherry-Pick Este Commit
Cherry-Pick ...
- Checkout Commit
Comparar con HEAD
Comparar con Worktree
- Copiar Información
- Copiar SHA
+ Autor
+ Committer
+ Información
+ SHA
+ Asunto
Acción personalizada
Rebase Interactivo ${0}$ hasta Aquí
Merge a ${0}$
@@ -129,6 +132,7 @@
Squash en Parent
Squash Commits Hijos hasta Aquí
CAMBIOS
+ archivo(s) modificado(s)
Buscar Cambios...
ARCHIVOS
Archivo LFS
@@ -147,12 +151,13 @@
REFS
SHA
Abrir en Navegador
- Introducir asunto del commit
Descripción
+ ASUNTO
+ Introducir asunto del commit
Configurar Repositorio
PLANTILLA DE COMMIT
- Nombre de la Plantilla:
Contenido de la Plantilla:
+ Nombre de la Plantilla:
ACCIÓN PERSONALIZADA
Argumentos:
${REPO} - Ruta del repositorio; ${SHA} - SHA del commit seleccionado
@@ -169,13 +174,15 @@
Fetch remotos automáticamente
Minuto(s)
Remoto por Defecto
+ Modo preferido de Merge
SEGUIMIENTO DE INCIDENCIAS
+ Añadir Regla de Ejemplo para Azure DevOps
Añadir Regla de Ejemplo para Incidencias de Gitee
Añadir Regla de Ejemplo para Pull Requests de Gitee
Añadir Regla de Ejemplo para Github
- Añadir Regla de Ejemplo para Jira
Añadir Regla de Ejemplo para Incidencias de GitLab
Añadir Regla de Ejemplo para Merge Requests de GitLab
+ Añadir Regla de Ejemplo para Jira
Nueva Regla
Expresión Regex para Incidencias:
Nombre de la Regla:
@@ -190,7 +197,12 @@
Nombre de usuario para este repositorio
Espacios de Trabajo
Color
+ Nombre
Restaurar pestañas al iniciar
+ CONTINUAR
+ ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty)?
+ HACER STAGE A TODO & COMMIT
+ ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty) o hacer stage a todo y después commit?
Asistente de Commit Convencional
Cambio Importante:
Incidencia Cerrada:
@@ -200,8 +212,8 @@
Tipo de Cambio:
Copiar
Copiar Todo el Texto
+ Copiar Ruta Completa
Copiar Ruta
- Copiar Nombre del Archivo
Crear Rama...
Basado En:
Checkout de la rama creada
@@ -212,6 +224,7 @@
Introduzca el nombre de la rama.
Los espacios serán reemplazados con guiones.
Crear Rama Local
+ Sobrescribir la rama existente
Crear Etiqueta...
Nueva Etiqueta En:
Firma GPG
@@ -226,6 +239,9 @@
ligera
Mantenga Ctrl para iniciar directamente
Cortar
+ Desinicializar Submódulo
+ Forzar desinicialización incluso si contiene cambios locales.
+ Submódulo:
Eliminar Rama
Rama:
¡Estás a punto de eliminar una rama remota!
@@ -237,8 +253,8 @@
Ruta:
Destino:
Todos los hijos serán removidos de la lista.
- Confirmar Eliminación de Grupo
¡Esto solo lo removera de la lista, no del disco!
+ Confirmar Eliminación de Grupo
Confirmar Eliminación de Repositorio
Eliminar Submódulo
Ruta del Submódulo:
@@ -261,6 +277,7 @@
Mostrar símbolos ocultos
Diferencia Lado a Lado
SUBMÓDULO
+ BORRADO
NUEVO
Intercambiar
Resaltado de Sintaxis
@@ -285,7 +302,6 @@
Editar Repositorio Seleccionado
Ejecutar Acción Personalizada
Nombre de la Acción:
- Fast-Forward (sin checkout)
Fetch
Fetch todos los remotos
Utilizar opción '--force'
@@ -307,11 +323,11 @@
Unstage
Unstage {0} archivos
Unstage Cambios en Línea(s) Seleccionada(s)
- Usar Suyos (checkout --theirs)
Usar Míos (checkout --ours)
+ Usar Suyos (checkout --theirs)
Historial de Archivos
- CONTENIDO
CAMBIO
+ CONTENIDO
Git-Flow
Rama de Desarrollo:
Feature:
@@ -320,6 +336,8 @@
FLOW - Finalizar Hotfix
FLOW - Finalizar Release
Destino:
+ Push al/los remoto(s) después de Finalizar
+ Squash durante el merge
Hotfix:
Prefijo de Hotfix:
Inicializar Git-Flow
@@ -341,8 +359,8 @@
Patrón Personalizado:
Añadir Patrón de Seguimiento a Git LFS
Fetch
- Fetch Objetos LFS
Ejecuta `git lfs fetch` para descargar objetos Git LFS. Esto no actualiza la copia de trabajo.
+ Fetch Objetos LFS
Instalar hooks de Git LFS
Mostrar Bloqueos
No hay archivos bloqueados
@@ -354,11 +372,11 @@
Prune
Ejecuta `git lfs prune` para eliminar archivos LFS antiguos del almacenamiento local
Pull
- Pull Objetos LFS
Ejecuta `git lfs pull` para descargar todos los archivos Git LFS para la referencia actual y hacer checkout
+ Pull Objetos LFS
Push
- Push Objetos LFS
Push archivos grandes en cola al endpoint de Git LFS
+ Push Objetos LFS
Remoto:
Seguir archivos llamados '{0}'
Seguir todos los archivos *{0}
@@ -377,10 +395,12 @@
Cancelar popup actual
Clonar repositorio nuevo
Cerrar página actual
- Ir a la página anterior
Ir a la siguiente página
+ Ir a la página anterior
Crear nueva página
Abrir diálogo de preferencias
+ Cambiar espacio de trabajo activo
+ Cambiar página activa
REPOSITORIO
Commit cambios staged
Commit y push cambios staged
@@ -389,11 +409,11 @@
Descartar cambios seleccionados
Fetch, empieza directamente
Modo Dashboard (Por Defecto)
+ Modo de búsqueda de commits
Pull, empieza directamente
Push, empieza directamente
Forzar a recargar este repositorio
Stage/Unstage cambios seleccionados
- Modo de búsqueda de commits
Cambiar a 'Cambios'
Cambiar a 'Historias'
Cambiar a 'Stashes'
@@ -401,10 +421,11 @@
Cerrar panel de búsqueda
Buscar siguiente coincidencia
Buscar coincidencia anterior
+ Abrir con herramienta diff/merge externa
Abrir panel de búsqueda
+ Descartar
Stage
Unstage
- Descartar
Inicializar Repositorio
Ruta:
Cherry-Pick en progreso.
@@ -416,12 +437,14 @@
Revert en progreso.
Haciendo revert del commit
Rebase Interactivo
- Rama Objetivo:
En:
- Abrir en el Navegador
+ Rama Objetivo:
Copiar Enlace
+ Abrir en el Navegador
ERROR
AVISO
+ Espacios de trabajo
+ Páginas
Merge Rama
En:
Opción de Merge:
@@ -445,18 +468,18 @@
Copiar Ruta del Repositorio
Repositorios
Pegar
- Justo ahora
- Hace {0} minutos
+ Hace {0} días
Hace 1 hora
Hace {0} horas
- Ayer
- Hace {0} días
+ Justo ahora
Último mes
- Hace {0} meses
Último año
+ Hace {0} minutos
+ Hace {0} meses
Hace {0} años
+ Ayer
+ Usa 'Shift+Enter' para introducir una nueva línea. 'Enter' es el atajo del botón OK
Preferencias
- Opciones Avanzadas
OPEN AI
Analizar Diff Prompt
Clave API
@@ -488,6 +511,7 @@
Commits en el historial
Mostrar hora del autor en lugar de la hora del commit en el gráfico
Mostrar hijos en los detalles de commit
+ Mostrar etiquetas en el gráfico de commit
Longitud de la guía del asunto
GIT
Habilitar Auto CRLF
@@ -495,36 +519,36 @@
Email de usuario
Email global del usuario git
Habilitar --prune para fetch
+ Habilitar --ignore-cr-at-eol en diff
+ Se requiere Git (>= 2.25.1) para esta aplicación
Ruta de instalación
Habilitar verificación HTTP SSL
Nombre de usuario
Nombre global del usuario git
Versión de Git
- Se requiere Git (>= 2.23.0) para esta aplicación
FIRMA GPG
Firma GPG en commit
- Firma GPG en etiqueta
Formato GPG
Ruta de instalación del programa
Introducir ruta para el programa gpg instalado
+ Firma GPG en etiqueta
Clave de firma del usuario
Clave de firma gpg del usuario
INTEGRACIÓN
SHELL/TERMINAL
- Shell/Terminal
Ruta
+ Shell/Terminal
Podar Remoto
Destino:
Podar Worktrees
- Podar información de worktree en `$GIT_DIR/worktrees`
+ Podar información de worktree en `$GIT_COMMON_DIR/worktrees`
Pull
Rama Remota:
- Fetch todas las ramas
En:
Cambios Locales:
Descartar
Stash & Reaplicar
- Fetch sin etiquetas
+ Actualizar todos los submódulos
Remoto:
Pull (Fetch & Merge)
Usar rebase en lugar de merge
@@ -546,7 +570,6 @@
Stash & reaplicar cambios locales
En:
Rebase:
- Refrescar
Añadir Remoto
Editar Remoto
Nombre:
@@ -568,13 +591,18 @@
Rama:
ABORTAR
Auto fetching cambios desde remotos...
+ Ordenar
+ Por Fecha de Committer
+ Por Nombre
Limpiar (GC & Prune)
Ejecutar comando `git gc` para este repositorio.
Limpiar todo
+ Limpiar
Configurar este repositorio
CONTINUAR
Acciones Personalizadas
No hay ninguna Acción Personalizada
+ Descartar todos los cambios
Habilitar Opción '--reflog'
Abrir en el Explorador
Buscar Ramas/Etiquetas/Submódulos
@@ -602,10 +630,12 @@
Buscar Commit
Autor
Committer
+ Contenido
Archivo
Mensaje
SHA
Rama Actual
+ Mostrar Submódulos como Árbol
Mostrar Etiquetas como Árbol
OMITIR
Estadísticas
@@ -615,11 +645,12 @@
ETIQUETAS
NUEVA ETIQUETA
Por Fecha de Creación
- Por Nombre (Ascendiente)
- Por Nombre (Descendiente)
- Sort
+ Por Nombre
+ Ordenar
Abrir en Terminal
Usar tiempo relativo en las historias
+ Ver Logs
+ Visitar '{0}' en el Navegador
WORKTREES
AÑADIR WORKTREE
PRUNE
@@ -628,12 +659,14 @@
Modo de Reset:
Mover a:
Rama Actual:
+ Resetear Rama (Sin hacer Checkout)
+ Mover A:
+ Rama:
Revelar en el Explorador de Archivos
Revertir Commit
Commit:
Commit revertir cambios
Reescribir Mensaje de Commit
- Usa 'Shift+Enter' para introducir una nueva línea. 'Enter' es el atajo del botón OK
Ejecutando. Por favor espera...
GUARDAR
Guardar Como...
@@ -659,18 +692,14 @@
Ruta de almacenamiento de la clave privada SSH
INICIAR
Stash
- Restaurar automáticamente después del stashing
- Tus archivos de trabajo permanecen sin cambios, pero se guarda un stash.
Incluir archivos no rastreados
- Mantener archivos staged
Mensaje:
- Opcional. Nombre de este stash
+ Opcional. Información de este stash
Solo cambios staged
¡Tanto los cambios staged como los no staged de los archivos seleccionados serán stashed!
Stash Cambios Locales
Aplicar
Eliminar
- Pop
Guardar como Parche...
Eliminar Stash
Eliminar:
@@ -680,32 +709,43 @@
Estadísticas
COMMITS
COMMITTER
+ GENERAL
MES
SEMANA
- COMMITS:
AUTORES:
- GENERAL
+ COMMITS:
SUBMÓDULOS
Añadir Submódulo
Copiar Ruta Relativa
+ Desinicializar Submódulo
Fetch submódulos anidados
Abrir Repositorio del Submódulo
Ruta Relativa:
Carpeta relativa para almacenar este módulo.
Eliminar Submódulo
+ ESTADO
+ modificado
+ no inicializado
+ revisión cambiada
+ unmerged
+ URL
OK
Copiar Nombre de la Etiqueta
Copiar Mensaje de la Etiqueta
Eliminar ${0}$...
Merge ${0}$ en ${1}$...
Push ${0}$...
- URL:
Actualizar Submódulos
Todos los submódulos
Inicializar según sea necesario
Recursivamente
Submódulo:
Usar opción --remote
+ URL:
+ Logs
+ LIMPIAR TODO
+ Copiar
+ Borrar
Advertencia
Página de Bienvenida
Crear Grupo
@@ -725,22 +765,27 @@
Git Ignore
Ignorar todos los archivos *{0}
Ignorar archivos *{0} en la misma carpeta
- Ignorar archivos en la misma carpeta
Ignorar solo este archivo
Enmendar
- Puedes stagear este archivo ahora.
+ Puedes hacer stage a este archivo ahora.
COMMIT
COMMIT & PUSH
Plantilla/Historias
Activar evento de clic
Commit (Editar)
- Stagear todos los cambios y commit
- ¡Commit vacío detectado! ¿Quieres continuar (--allow-empty)?
+ Hacer stage a todos los cambios y commit
+ Tienes {0} archivo(s) en stage, pero solo {1} archivo(s) mostrado(s) ({2} archivo(s) están filtrados). ¿Quieres continuar?
CONFLICTOS DETECTADOS
+ ABRIR HERRAMIENTA DE MERGE EXTERNA
+ ABRIR TODOS LOS CONFLICTOS EN HERRAMIENTA DE MERGE EXTERNA
LOS CONFLICTOS DE ARCHIVOS ESTÁN RESUELTOS
+ USAR MÍOS
+ USAR SUYOS
INCLUIR ARCHIVOS NO RASTREADOS
NO HAY MENSAJES DE ENTRADA RECIENTES
NO HAY PLANTILLAS DE COMMIT
+ Restablecer Autor
+ Haz clic derecho en el(los) archivo(s) seleccionado(s) y elige tu opción para resolver conflictos.
Firmar
STAGED
UNSTAGE
@@ -748,9 +793,8 @@
UNSTAGED
STAGE
STAGE TODO
- VER ASSUME UNCHANGED
+ VER ASSUME UNCHANGED
Plantilla: ${0}$
- Haz clic derecho en el(los) archivo(s) seleccionado(s) y elige tu opción para resolver conflictos.
ESPACIO DE TRABAJO:
Configura Espacios de Trabajo...
WORKTREE
diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml
index aecea9ad..8e1fd7bd 100644
--- a/src/Resources/Locales/fr_FR.axaml
+++ b/src/Resources/Locales/fr_FR.axaml
@@ -2,40 +2,34 @@
+
À propos
À propos de SourceGit
- • Compilé avec
- • Le graphique est rendu par
- • TextEditor de
- • Les polices Monospace proviennent de
- • Le code source est disponible sur
Client Git Open Source et Gratuit
Ajouter un Worktree
- Que récupérer :
- Créer une nouvelle branche
- Branche existante
Emplacement :
Chemin vers ce worktree. Relatif supporté.
Nom de branche:
Optionnel. Nom du dossier de destination par défaut.
Suivre la branche :
Suivi de la branche distante
+ Que récupérer :
+ Créer une nouvelle branche
+ Branche existante
Assistant IA
+ RE-GÉNÉRER
Utiliser l'IA pour générer un message de commit
+ APPLIQUER COMME MESSAGE DE COMMIT
Appliquer
- Erreur
- Soulever les erreurs et refuser d'appliquer le patch
- Toutes les erreurs
- Similaire à 'Erreur', mais plus détaillé
Fichier de patch :
Selectionner le fichier .patch à appliquer
Ignorer les changements d'espaces blancs
- Pas d'avertissement
- Désactiver l'avertissement sur les espaces blancs terminaux
Appliquer le patch
- Avertissement
- Affiche des avertissements pour ce type d'erreurs tout en appliquant le patch
Espaces blancs :
+ Appliquer le Stash
+ Supprimer après application
+ Rétablir les changements de l'index
+ Stash:
Archiver...
Enregistrer l'archive sous :
Sélectionnez le chemin du fichier d'archive
@@ -45,16 +39,17 @@
FICHIERS PRÉSUMÉS INCHANGÉS
PAS DE FICHIERS PRÉSUMÉS INCHANGÉS
SUPPRIMER
+ Rafraîchir
FICHIER BINAIRE NON SUPPORTÉ !!!
Blâme
LE BLÂME SUR CE FICHIER N'EST PAS SUPPORTÉ!!!
Récupérer ${0}$...
- Comparer avec HEAD
+ Comparer avec ${0}$
Comparer avec le worktree
Copier le nom de la branche
+ Action personnalisée
Supprimer ${0}$...
Supprimer {0} branches sélectionnées
- Rejeter tous les changements
Fast-Forward vers ${0}$
Fetch ${0}$ vers ${1}$...
Git Flow - Terminer ${0}$
@@ -67,6 +62,7 @@
Renommer ${0}$...
Définir la branche de suivi...
Comparer les branches
+ Branche en amont invalide!
Octets
ANNULER
Réinitialiser à la révision parente
@@ -80,17 +76,16 @@
Récupérer ce commit
Commit :
Avertissement: une récupération vers un commit aboutiera vers un HEAD détaché
- Branche :
Changements locaux :
Annuler
Mettre en stash et réappliquer
+ Branche :
Cherry-Pick de ce commit
Ajouter la source au message de commit
Commit :
Commit tous les changements
Ligne principale :
Habituellement, on ne peut pas cherry-pick un commit car on ne sait pas quel côté devrait être considéré comme principal. Cette option permet de rejouer les changements relatifs au parent spécifié.
- Cherry Pick
Supprimer les stashes
Vous essayez de supprimer tous les stashes. Êtes-vous sûr de vouloir continuer ?
Cloner repository distant
@@ -99,6 +94,7 @@
Nom local :
Nom de dépôt. Optionnel.
Dossier parent :
+ Initialiser et mettre à jour les sous-modules
URL du dépôt :
FERMER
Éditeur
@@ -107,8 +103,8 @@
Cherry-Pick ...
Comparer avec HEAD
Comparer avec le worktree
- Copier les informations
- Copier le SHA
+ Informations
+ SHA
Action personnalisée
Rebase interactif de ${0}$ ici
Fusionner dans ${0}$
@@ -139,20 +135,22 @@
REFS
SHA
Ouvrir dans le navigateur
- Entrez le message du commit
Description
+ Entrez le message du commit
Configurer le dépôt
MODÈLE DE COMMIT
- Nom de modèle:
Contenu de modèle:
+ Nom de modèle:
ACTION PERSONNALISÉE
Arguments :
${REPO} - Chemin du repository; ${SHA} - SHA du commit sélectionné
Fichier exécutable :
Nom :
Portée :
+ Branche
Commit
Repository
+ Attendre la fin de l'action
Adresse e-mail
Adresse e-mail
GIT
@@ -160,12 +158,13 @@
minute(s)
Dépôt par défaut
SUIVI DES PROBLÈMES
+ Ajouter une règle d'exemple Azure DevOps
Ajouter une règle d'exemple Gitee
Ajouter une règle d'exemple pour Pull Request Gitee
Ajouter une règle d'exemple Github
- Ajouter une règle d'exemple Jira
Ajouter une règle d'exemple pour Incidents GitLab
Ajouter une règle d'exemple pour Merge Request GitLab
+ Ajouter une règle d'exemple Jira
Nouvelle règle
Issue Regex Expression:
Nom de règle :
@@ -180,6 +179,7 @@
Nom d'utilisateur pour ce dépôt
Espaces de travail
Couleur
+ Nom
Restaurer les onglets au démarrage
Assistant Commits Conventionnels
Changement Radical :
@@ -190,7 +190,7 @@
Type de Changement :
Copier
Copier tout le texte
- Copier le nom de fichier
+ Copier le chemin complet
Copier le chemin
Créer une branche...
Basé sur :
@@ -200,6 +200,7 @@
Stash & Réappliquer
Nom de la nouvelle branche :
Entrez le nom de la branche.
+ Les espaces seront remplacés par des tirets.
Créer une branche locale
Créer un tag...
Nouveau tag à :
@@ -223,7 +224,10 @@
Vous essayez de supprimer plusieurs branches à la fois. Assurez-vous de revérifier avant de procéder !
Supprimer Remote
Remote :
+ Chemin:
Cible :
+ Tous les enfants seront retirés de la liste.
+ Cela le supprimera uniquement de la liste, pas du disque !
Confirmer la suppression du groupe
Confirmer la suppression du dépôt
Supprimer le sous-module
@@ -236,7 +240,9 @@
ANCIEN
Copier
Mode de fichier changé
+ Première différence
Ignorer les changements d'espaces
+ Dernière différence
CHANGEMENT D'OBJET LFS
Différence suivante
PAS DE CHANGEMENT OU SEULEMENT EN FIN DE LIGNE
@@ -249,8 +255,8 @@
Permuter
Coloration syntaxique
Retour à la ligne
- Ouvrir dans l'outil de fusion
Activer la navigation par blocs
+ Ouvrir dans l'outil de fusion
Voir toutes les lignes
Réduit le nombre de ligne visibles
Augmente le nombre de ligne visibles
@@ -269,7 +275,6 @@
Éditer le dépôt sélectionné
Lancer action personnalisée
Nom de l'action :
- Fast-Forward (sans récupération)
Fetch
Fetch toutes les branches distantes
Outrepasser les vérifications de refs
@@ -294,8 +299,8 @@
Utiliser les miennes (checkout --ours)
Utiliser les leurs (checkout --theirs)
Historique du fichier
- CONTENU
MODIFICATION
+ CONTENU
Git-Flow
Branche de développement :
Feature:
@@ -325,8 +330,8 @@
Pattern personnalisé :
Ajouter un pattern de suivi à Git LFS
Fetch
- Fetch les objets LFS
Lancer `git lfs fetch` pour télécharger les objets Git LFS. Cela ne met pas à jour la copie de travail.
+ Fetch les objets LFS
Installer les hooks Git LFS
Afficher les verrous
Pas de fichiers verrouillés
@@ -338,11 +343,11 @@
Elaguer
Lancer `git lfs prune` pour supprimer les anciens fichier LFS du stockage local
Pull
- Pull les objets LFS
Lancer `git lfs pull` pour télécharger tous les fichier Git LFS de la référence actuelle & récupérer
+ Pull les objets LFS
Pousser
- Pousser les objets LFS
Transférer les fichiers volumineux en file d'attente vers le point de terminaison Git LFS
+ Pousser les objets LFS
Dépôt :
Suivre les fichiers appelés '{0}'
Suivre tous les fichiers *{0}
@@ -361,8 +366,8 @@
Annuler le popup en cours
Cloner un nouveau dépôt
Fermer la page en cours
- Aller à la page précédente
Aller à la page suivante
+ Aller à la page précédente
Créer une nouvelle page
Ouvrir le dialogue des préférences
DÉPÔT
@@ -373,11 +378,11 @@
Rejeter les changements sélectionnés
Fetch, démarre directement
Mode tableau de bord (Défaut)
+ Recherche de commit
Pull, démarre directement
Push, démarre directement
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'
@@ -386,25 +391,34 @@
Trouver la prochaine correspondance
Trouver la correspondance précédente
Ouvrir le panneau de recherche
+ Rejeter
Indexer
Retirer de l'index
- Rejeter
Initialiser le repository
Chemin :
Cherry-Pick en cours.
+ Traitement du commit
Merge request en cours.
+ Fusionnement
Rebase en cours.
+ Arrêté à
Annulation en cours.
+ Annulation du commit
Rebase interactif
- Branche cible :
Sur :
- Ouvrir dans le navigateur
+ Branche cible :
Copier le lien
+ Ouvrir dans le navigateur
ERREUR
NOTICE
Merger la branche
Dans :
Option de merge:
+ Source:
+ Fusionner (Plusieurs)
+ Commit tous les changement
+ Stratégie:
+ Cibles:
Déplacer le noeud du repository
Sélectionnier le noeud parent pour :
Nom :
@@ -420,16 +434,17 @@
Copier le chemin vers le dépôt
Dépôts
Coller
- A l'instant
- il y a {0} minutes
+ il y a {0} jours
il y a 1 heure
il y a {0} heures
- Hier
- il y a {0} jours
+ A l'instant
Le mois dernier
- il y a {0} mois
L'an dernier
+ il y a {0} minutes
+ il y a {0} mois
il y a {0} ans
+ Hier
+ Utiliser 'Maj+Entrée' pour insérer une nouvelle ligne. 'Entrée' est la touche pour valider
Préférences
IA
Analyser Diff Prompt
@@ -438,10 +453,13 @@
Modèle
Nom
Serveur
+ Activer le streaming
APPARENCE
Police par défaut
- Taille de police par défaut
- Taille de police de l'éditeur
+ Largeur de tab dans l'éditeur
+ Taille de police
+ Défaut
+ Éditeur
Police monospace
N'utiliser que des polices monospace pour l'éditeur de texte
Thème
@@ -454,9 +472,12 @@
Outil
GÉNÉRAL
Vérifier les mises à jour au démarrage
+ Format de date
Language
Historique de commits
Afficher l'heure de l'auteur au lieu de l'heure de validation dans le graphique
+ Afficher les enfants dans les détails du commit
+ Afficher les tags dans le graphique des commits
Guide de longueur du sujet
GIT
Activer auto CRLF
@@ -464,35 +485,34 @@
E-mail utilsateur
E-mail utilsateur global
Activer --prune pour fetch
+ Cette application requière Git (>= 2.25.1)
Chemin d'installation
+ Activer la vérification HTTP SSL
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
+ Signature GPG de tag
Clé de signature de l'utilisateur
Clé de signature GPG de l'utilisateur
INTEGRATION
SHELL/TERMINAL
- Shell/Terminal
Chemin
- Élaguer une branche distant
+ Shell/Terminal
+ Élaguer une branche distant
Cible :
Élaguer les Worktrees
- Élaguer les information de worktree dans `$GIT_DIR/worktrees`
+ Élaguer les information de worktree dans `$GIT_COMMON_DIR/worktrees`
Pull
Branche distante :
- Fetch toutes les branches
Dans :
Changements locaux :
Rejeter
Stash & Réappliquer
- Fetch sans les tags
Dépôt distant :
Pull (Fetch & Merge)
Utiliser rebase au lieu de merge
@@ -514,7 +534,6 @@
Stash & réappliquer changements locaux
Sur :
Rebase :
- Rafraîchir
Ajouter dépôt distant
Modifier dépôt distant
Nom :
@@ -541,14 +560,27 @@
Tout effacer
Configurer ce repository
CONTINUER
+ Actions personnalisées
Pas d'actions personnalisées
+ Rejeter tous les changements
Activer l'option '--reflog'
Ouvrir dans l'explorateur de fichiers
Rechercher Branches/Tags/Submodules
+ Visibilité dans le graphique
+ Réinitialiser
+ Cacher dans le graphique des commits
+ Filtrer dans le graphique des commits
Activer l'option '--first-parent'
+ DISPOSITION
+ Horizontal
+ Vertical
+ ORDRE DES COMMITS
+ Date du commit
+ Topologiquement
BRANCHES LOCALES
Naviguer vers le HEAD
Créer une branche
+ EFFACER LES NOTIFICATIONS
Mettre la branche courante en surbrillance dans le graph
Ouvrir dans {0}
Ouvrir dans un outil externe
@@ -563,13 +595,18 @@
SHA
Branche actuelle
Voir les Tags en tant qu'arbre
+ PASSER
Statistiques
SUBMODULES
AJOUTER SUBMODULE
METTRE A JOUR SUBMODULE
TAGS
NOUVEAU TAG
+ Par date de créateur
+ Par nom
+ Trier
Ouvrir dans un terminal
+ Utiliser le temps relatif dans les historiques
WORKTREES
AJOUTER WORKTREE
ELAGUER
@@ -583,11 +620,11 @@
Commit :
Commit les changements de l'annulation
Reformuler le message de commit
- Utiliser 'Maj+Entrée' pour insérer une nouvelle ligne. 'Entrée' est la touche pour valider
En exécution. Veuillez patienter...
SAUVEGARDER
Sauvegarder en tant que...
Le patch a été sauvegardé !
+ Analyser les repositories
Dossier racine :
Rechercher des mises à jour...
Une nouvelle version du logiciel est disponible :
@@ -596,23 +633,27 @@
Passer cette version
Mise à jour du logiciel
Il n'y a pas de mise à jour pour le moment.
+ Définir la branche suivie
+ Branche:
+ Retirer la branche amont
+ En amont:
Copier le SHA
- Squash Commits
+ Aller à
+ Squash les commits
Dans :
Clé privée SSH :
Chemin du magasin de clés privées SSH
START
Stash
Inclure les fichiers non-suivis
- Garder les fichiers indexés
Message :
- Optionnel. Nom de ce stash
+ Optionnel. Information de ce stash
Seulement les changements indexés
Les modifications indexées et non-indexées des fichiers sélectionnés seront stockées!!!
Stash les changements locaux
Appliquer
Effacer
- Extraire
+ Sauver comme Patch...
Effacer le Stash
Effacer :
Stashes
@@ -621,11 +662,11 @@
Statistiques
COMMITS
COMMITTER
+ APERCU
MOIS
SEMAINE
- COMMITS:
AUTEURS :
- APERCU
+ COMMITS:
SOUS-MODULES
Ajouter un sous-module
Copier le chemin relatif
@@ -640,13 +681,13 @@
Supprimer ${0}$...
Fusionner ${0}$ dans ${1}$...
Pousser ${0}$...
- URL :
Actualiser les sous-modules
Tous les sous-modules
Initialiser au besoin
Récursivement
Sous-module :
Utiliser l'option --remote
+ URL :
Avertissement
Page d'accueil
Créer un groupe
@@ -666,7 +707,6 @@
Git Ignore
Ignorer tous les *{0} fichiers
Ignorer *{0} fichiers dans le même dossier
- Ignorer les fichiers dans le même dossier
N'ignorer que ce fichier
Amender
Vous pouvez indexer ce fichier.
@@ -674,22 +714,23 @@
COMMIT & POUSSER
Modèles/Historiques
Trigger click event
+ Commit (Modifier)
Indexer tous les changements et commit
- Un commit vide a été détecté ! Voulez-vous continuer (--allow-empty) ?
CONFLITS DÉTECTÉS
LES CONFLITS DE FICHIER SONT RÉSOLUS
INCLURE LES FICHIERS NON-SUIVIS
PAS DE MESSAGE D'ENTRÉE RÉCENT
PAS DE MODÈLES DE COMMIT
+ Faites un clique droit sur les fichiers sélectionnés et faites vos choix pour la résoluion des conflits.
+ SignOff
INDEXÉ
RETIRER DE L'INDEX
RETIRER TOUT DE L'INDEX
NON INDEXÉ
INDEXER
INDEXER TOUT
- VOIR LES FICHIERS PRÉSUMÉS INCHANGÉS
+ VOIR LES FICHIERS PRÉSUMÉS INCHANGÉS
Modèle: ${0}$
- Faites un clique droit sur les fichiers sélectionnés et faites vos choix pour la résoluion des conflits.
ESPACE DE TRAVAIL :
Configurer les espaces de travail...
WORKTREE
diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml
index 4dcc8771..62f15e38 100644
--- a/src/Resources/Locales/it_IT.axaml
+++ b/src/Resources/Locales/it_IT.axaml
@@ -2,41 +2,29 @@
+
Informazioni
Informazioni su SourceGit
- • Creato con
- • Il grafico è reso da
- • Editor di testo da
- • I font monospaziati provengono da
- • Il codice sorgente è disponibile su
Client GUI Git open source e gratuito
Aggiungi Worktree
- Di cosa fare il checkout:
- Branch esistente
- Crea nuovo branch
Posizione:
Percorso per questo worktree. Supportato il percorso relativo.
Nome Branch:
Facoltativo. Predefinito è il nome della cartella di destinazione.
Traccia Branch:
Traccia branch remoto
+ Di cosa fare il checkout:
+ Crea nuovo branch
+ Branch esistente
Assistente AI
RIGENERA
Usa AI per generare il messaggio di commit
APPLICA COME MESSAGGIO DI COMMIT
Applica
- Errore
- Genera errori e si rifiuta di applicare la patch
- Tutti gli errori
- Simile a 'errore', ma mostra di più
File Patch:
Seleziona file .patch da applicare
Ignora modifiche agli spazi
- Nessun avviso
- Disattiva l'avviso sugli spazi finali
Applica Patch
- Avviso
- Mostra avvisi per alcuni errori, ma applica comunque
Spazi:
Applica lo stash
Rimuovi dopo aver applicato
@@ -51,17 +39,24 @@
FILE ASSUNTI COME INVARIATI
NESSUN FILE ASSUNTO COME INVARIATO
RIMUOVI
+ Aggiorna
FILE BINARIO NON SUPPORTATO!!!
+ Biseca
+ Annulla
+ Cattiva
+ Bisecando. La HEAD corrente è buona o cattiva?
+ Buona
+ Salta
+ Bisecando. Marca il commit corrente come buono o cattivo e fai checkout di un altro.
Attribuisci
L'ATTRIBUZIONE SU QUESTO FILE NON È SUPPORTATA!!!
Checkout ${0}$...
- Confronta con HEAD
+ Confronta con ${0}$
Confronta con Worktree
Copia Nome Branch
Azione personalizzata
Elimina ${0}$...
Elimina i {0} branch selezionati
- Scarta tutte le modifiche
Avanzamento Veloce a ${0}$
Recupera ${0}$ in ${1}$...
Git Flow - Completa ${0}$
@@ -77,8 +72,8 @@
Upstream non valido
Byte
ANNULLA
- Ripristina Questa Revisione
Ripristina la Revisione Padre
+ Ripristina Questa Revisione
Genera messaggio di commit
CAMBIA MODALITÀ DI VISUALIZZAZIONE
Mostra come elenco di file e cartelle
@@ -86,12 +81,13 @@
Mostra come albero del filesystem
Checkout Branch
Checkout Commit
- Avviso: Effettuando un checkout del commit, la tua HEAD sarà separata
Commit:
- Branch:
+ Avviso: Effettuando un checkout del commit, la tua HEAD sarà separata
Modifiche Locali:
Scarta
Stasha e Ripristina
+ Aggiorna tutti i sottomoduli
+ Branch:
Cherry Pick
Aggiungi sorgente al messaggio di commit
Commit(s):
@@ -110,13 +106,16 @@
URL del Repository:
CHIUDI
Editor
+ Checkout Commit
Cherry-Pick Questo Commit
Cherry-Pick...
- Checkout Commit
Confronta con HEAD
Confronta con Worktree
- Copia Info
- Copia SHA
+ Autore
+ Committer
+ Informazioni
+ SHA
+ Oggetto
Azione Personalizzata
Riallinea Interattivamente ${0}$ fino a Qui
Unisci a ${0}$
@@ -147,12 +146,13 @@
RIFERIMENTI
SHA
Apri nel Browser
- Inserisci l'oggetto del commit
Descrizione
+ OGGETTO
+ Inserisci l'oggetto del commit
Configura Repository
TEMPLATE DI COMMIT
- Nome Template:
Contenuto Template:
+ Nome Template:
AZIONE PERSONALIZZATA
Argomenti:
${REPO} - Percorso del repository; ${SHA} - SHA del commit selezionato
@@ -169,13 +169,15 @@
Recupera automaticamente i remoti
Minuto/i
Remoto Predefinito
+ Modalità di Merge Preferita
TRACCIAMENTO ISSUE
+ Aggiungi una regola di esempio per Azure DevOps
Aggiungi una regola di esempio per un Issue Gitee
Aggiungi una regola di esempio per un Pull Request Gitee
Aggiungi una regola di esempio per GitHub
- Aggiungi una regola di esempio per Jira
Aggiungi una regola di esempio per Issue GitLab
Aggiungi una regola di esempio per una Merge Request GitLab
+ Aggiungi una regola di esempio per Jira
Nuova Regola
Espressione Regex Issue:
Nome Regola:
@@ -190,7 +192,12 @@
Nome utente per questo repository
Spazi di Lavoro
Colore
+ Nome
Ripristina schede all'avvio
+ CONTINUA
+ Trovato un commit vuoto! Vuoi procedere (--allow-empty)?
+ STAGE DI TUTTO E COMMITTA
+ Trovato un commit vuoto! Vuoi procedere (--allow-empty) o fare lo stage di tutto e committare?
Guida Commit Convenzionali
Modifica Sostanziale:
Issue Chiusa:
@@ -200,8 +207,8 @@
Tipo di Modifica:
Copia
Copia Tutto il Testo
+ Copia Intero Percorso
Copia Percorso
- Copia Nome File
Crea Branch...
Basato Su:
Checkout del Branch Creato
@@ -257,7 +264,6 @@
Differenza Successiva
NESSUNA MODIFICA O SOLO CAMBIAMENTI DI FINE LINEA
Differenza Precedente
- Abilita la navigazione a blocchi
Salva come Patch
Mostra Simboli Nascosti
Diff Affiancato
@@ -266,6 +272,7 @@
Scambia
Evidenziazione Sintassi
Avvolgimento delle Parole
+ Abilita la navigazione a blocchi
Apri nello Strumento di Merge
Mostra Tutte le Righe
Diminuisci Numero di Righe Visibili
@@ -285,7 +292,6 @@
Modifica Repository Selezionato
Esegui Azione Personalizzata
Nome Azione:
- Avanzamento Veloce (senza verifica)
Recupera
Recupera da tutti i remoti
Forza la sovrascrittura dei riferimenti locali
@@ -307,12 +313,11 @@
Rimuovi da Stage
Rimuovi da Stage {0} file
Rimuovi le Righe Selezionate da Stage
- Usa Il Loro (checkout --theirs)
Usa Il Mio (checkout --ours)
+ Usa Il Loro (checkout --theirs)
Cronologia File
- CONTENUTO
MODIFICA
- FILTRO
+ CONTENUTO
Git-Flow
Branch di Sviluppo:
Feature:
@@ -321,6 +326,8 @@
FLOW - Completa Hotfix
FLOW - Completa Rilascio
Target:
+ Invia al remote dopo aver finito
+ Esegui squash durante il merge
Hotfix:
Prefisso Hotfix:
Inizializza Git-Flow
@@ -342,8 +349,8 @@
Modello Personalizzato:
Aggiungi Modello di Tracciamento a Git LFS
Recupera
- Recupera Oggetti LFS
Esegui `git lfs fetch` per scaricare gli oggetti Git LFS. Questo non aggiorna la copia di lavoro.
+ Recupera Oggetti LFS
Installa hook di Git LFS
Mostra Blocchi
Nessun File Bloccato
@@ -355,11 +362,11 @@
Elimina
Esegui `git lfs prune` per eliminare vecchi file LFS dallo storage locale
Scarica
- Scarica Oggetti LFS
Esegui `git lfs pull` per scaricare tutti i file LFS per il ref corrente e fare il checkout
+ Scarica Oggetti LFS
Invia
- Invia Oggetti LFS
Invia grandi file in coda al punto finale di Git LFS
+ Invia Oggetti LFS
Remoto:
Traccia file con nome '{0}'
Traccia tutti i file *{0}
@@ -378,8 +385,8 @@
Annulla il popup corrente
Clona una nuova repository
Chiudi la pagina corrente
- Vai alla pagina precedente
Vai alla pagina successiva
+ Vai alla pagina precedente
Crea una nuova pagina
Apri la finestra delle preferenze
REPOSITORY
@@ -390,11 +397,11 @@
Scarta le modifiche selezionate
Recupera, avvia direttamente
Modalità Dashboard (Predefinita)
+ Modalità ricerca commit
Scarica, avvia direttamente
Invia, avvia direttamente
Forza l'aggiornamento di questo repository
Aggiungi/Rimuovi da stage le modifiche selezionate
- Modalità ricerca commit
Passa a 'Modifiche'
Passa a 'Storico'
Passa a 'Stashes'
@@ -402,10 +409,11 @@
Chiudi il pannello di ricerca
Trova il prossimo risultato
Trova il risultato precedente
+ Apri con uno strumento di diff/merge esterno
Apri il pannello di ricerca
+ Scarta
Aggiungi in stage
Rimuovi
- Scarta
Inizializza Repository
Percorso:
Cherry-Pick in corso.
@@ -417,10 +425,10 @@
Ripristino in corso.
Ripristinando il commit
Riallinea Interattivamente
- Branch di destinazione:
Su:
- Apri nel Browser
+ Branch di destinazione:
Copia il Link
+ Apri nel Browser
ERRORE
AVVISO
Unisci Branch
@@ -446,16 +454,17 @@
Copia Percorso Repository
Repository
Incolla
- Proprio ora
- {0} minuti fa
+ {0} giorni fa
1 ora fa
{0} ore fa
- Ieri
- {0} giorni fa
+ Proprio ora
Il mese scorso
- {0} mesi fa
L'anno scorso
+ {0} minuti fa
+ {0} mesi fa
{0} anni fa
+ Ieri
+ Usa 'Shift+Enter' per inserire una nuova riga. 'Enter' è il tasto rapido per il pulsante OK
Preferenze
AI
Analizza il Prompt Differenza
@@ -467,7 +476,8 @@
Abilita streaming
ASPETTO
Font Predefinito
- Font Size
+ Larghezza della Tab Editor
+ Dimensione Font
Dimensione Font Predefinita
Dimensione Font Editor
Font Monospaziato
@@ -487,6 +497,7 @@
Numero massimo di commit nella cronologia
Mostra nel grafico l'orario dell'autore anziché quello del commit
Mostra i figli nei dettagli del commit
+ Mostra i tag nel grafico dei commit
Lunghezza Guida Oggetto
GIT
Abilita Auto CRLF
@@ -494,36 +505,35 @@
Email Utente
Email utente Git globale
Abilita --prune durante il fetch
+ Abilita --ignore-cr-at-eol nel diff
+ Questa applicazione richiede Git (>= 2.25.1)
Percorso Installazione
+ Abilita la verifica HTTP SSL
Nome Utente
Nome utente Git globale
Versione di Git
- Questa applicazione richiede Git (>= 2.23.0)
- Abilita la verifica HTTP SSL
FIRMA GPG
Firma GPG per commit
- Firma GPG per tag
Formato GPG
Percorso Programma Installato
Inserisci il percorso per il programma GPG installato
+ Firma GPG per tag
Chiave Firma Utente
Chiave GPG dell'utente per la firma
INTEGRAZIONE
SHELL/TERMINALE
- Shell/Terminale
Percorso
+ Shell/Terminale
Potatura Remota
Destinazione:
Potatura Worktrees
- Potatura delle informazioni di worktree in `$GIT_DIR/worktrees`
+ Potatura delle informazioni di worktree in `$GIT_COMMON_DIR/worktrees`
Scarica
Branch Remoto:
- Recupera tutti i branch
In:
Modifiche Locali:
Scarta
Stasha e Riapplica
- Recupera senza tag
Remoto:
Scarica (Recupera e Unisci)
Riallineare anziché unire
@@ -545,7 +555,6 @@
Stasha e Riapplica modifiche locali
Su:
Riallinea:
- Aggiorna
Aggiungi Remoto
Modifica Remoto
Nome:
@@ -567,6 +576,9 @@
Branch:
ANNULLA
Recupero automatico delle modifiche dai remoti...
+ Ordina
+ Per data del committer
+ Per nome
Pulizia (GC e Potatura)
Esegui il comando `git gc` per questo repository.
Cancella tutto
@@ -574,10 +586,10 @@
CONTINUA
Azioni Personalizzate
Nessuna Azione Personalizzata
+ Scarta tutte le modifiche
Abilita opzione '--reflog'
Apri nell'Esplora File
Cerca Branch/Tag/Sottomodulo
- FILTRATO DA:
Visibilità nel grafico
Non impostato
Nascondi nel grafico dei commit
@@ -601,11 +613,13 @@
AGGIUNGI REMOTO
Cerca Commit
Autore
- Committente
+ Committer
+ Contenuto
File
Messaggio
SHA
Branch Corrente
+ Mostra i Sottomoduli Come Albero
Mostra Tag come Albero
SALTA
Statistiche
@@ -615,11 +629,12 @@
TAG
NUOVO TAG
Per data di creazione
- Per nome (ascendente)
- Per nome (discendente)
+ Per nome
Ordina
Apri nel Terminale
Usa tempo relativo nello storico
+ Visualizza i Log
+ Visita '{0}' nel Browser
WORKTREE
AGGIUNGI WORKTREE
POTATURA
@@ -633,7 +648,6 @@
Commit:
Commit delle modifiche di ripristino
Modifica Messaggio di Commit
- Usa 'Shift+Enter' per inserire una nuova riga. 'Enter' è il tasto rapido per il pulsante OK
In esecuzione. Attendere...
SALVA
Salva come...
@@ -659,18 +673,14 @@
Percorso per la chiave SSH privata
AVVIA
Stasha
- Auto-ripristino dopo lo stash
- I tuoi file di lavoro rimangono inalterati, ma viene salvato uno stash.
Includi file non tracciati
- Mantieni file in stage
Messaggio:
- Opzionale. Nome di questo stash
+ Opzionale. Informazioni di questo stash
Solo modifiche in stage
Sia le modifiche in stage che quelle non in stage dei file selezionati saranno stashate!!!
Stasha Modifiche Locali
Applica
Elimina
- Estrai
Salva come Patch...
Elimina Stash
Elimina:
@@ -680,11 +690,11 @@
Statistiche
COMMIT
COMMITTER
+ PANORAMICA
MESE
SETTIMANA
- COMMIT:
AUTORI:
- PANORAMICA
+ COMMIT:
SOTTOMODULI
Aggiungi Sottomodulo
Copia Percorso Relativo
@@ -693,19 +703,29 @@
Percorso Relativo:
Cartella relativa per memorizzare questo modulo.
Elimina Sottomodulo
+ STATO
+ modificato
+ non inizializzato
+ revisione cambiata
+ non unito
+ URL
OK
Copia Nome Tag
Copia Messaggio Tag
Elimina ${0}$...
Unisci ${0}$ in ${1}$...
Invia ${0}$...
- URL:
Aggiorna Sottomoduli
Tutti i sottomoduli
Inizializza se necessario
Ricorsivamente
Sottomodulo:
Usa opzione --remote
+ URL:
+ Log
+ CANCELLA TUTTO
+ Copia
+ Elimina
Avviso
Pagina di Benvenuto
Crea Gruppo
@@ -725,7 +745,6 @@
Git Ignore
Ignora tutti i file *{0}
Ignora i file *{0} nella stessa cartella
- Ignora i file nella stessa cartella
Ignora solo questo file
Modifica
Puoi aggiungere in stage questo file ora.
@@ -735,22 +754,26 @@
Attiva evento click
Commit (Modifica)
Stage di tutte le modifiche e fai il commit
- Trovato un commit vuoto! Vuoi continuare (--allow-empty)?
+ Hai stageato {0} file ma solo {1} file mostrati ({2} file sono stati filtrati). Vuoi procedere?
CONFLITTI RILEVATI
+ APRI STRUMENTO DI MERGE ESTERNO
+ APRI TUTTI I CONFLITTI NELLO STRUMENTO DI MERGE ESTERNO
CONFLITTI NEI FILE RISOLTI
+ USO IL MIO
+ USO IL LORO
INCLUDI FILE NON TRACCIATI
NESSUN MESSAGGIO RECENTE INSERITO
NESSUN TEMPLATE DI COMMIT
+ Clicca con il tasto destro sul(i) file selezionato, quindi scegli come risolvere i conflitti.
+ SignOff
IN STAGE
RIMUOVI DA STAGE
RIMUOVI TUTTO DA STAGE
NON IN STAGE
FAI LO STAGE
FAI LO STAGE DI TUTTO
- VISUALIZZA COME NON MODIFICATO
+ VISUALIZZA COME NON MODIFICATO
Template: ${0}$
- Clicca con il tasto destro sul(i) file selezionato, quindi scegli come risolvere i conflitti.
- SignOff
WORKSPACE:
Configura Workspaces...
WORKTREE
diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml
new file mode 100644
index 00000000..bf9fe586
--- /dev/null
+++ b/src/Resources/Locales/ja_JP.axaml
@@ -0,0 +1,739 @@
+
+
+
+
+
+ 概要
+ SourceGitについて
+ オープンソース & フリーなGit GUIクライアント
+ ワークツリーを追加
+ 場所:
+ ワークツリーのパスを入力してください。相対パスも使用することができます。
+ ブランチの名前:
+ 任意。デフォルトでは宛先フォルダ名が使用されます。
+ 追跡するブランチ:
+ 追跡中のリモートブランチ
+ チェックアウトする内容:
+ 新しいブランチを作成
+ 既存のブランチ
+ OpenAI アシスタント
+ 再生成
+ OpenAIを使用してコミットメッセージを生成
+ コミットメッセージとして適用
+ 適用
+ パッチファイル:
+ 適用する .patchファイルを選択
+ 空白文字の変更を無視
+ パッチを適用
+ 空白文字:
+ スタッシュを適用
+ 適用後に削除
+ インデックスの変更を復元
+ スタッシュ:
+ アーカイブ...
+ アーカイブの保存先:
+ アーカイブファイルのパスを選択
+ リビジョン:
+ アーカイブ
+ SourceGit Askpass
+ 変更されていないとみなされるファイル
+ 変更されていないとみなされるファイルはありません
+ 削除
+ 更新
+ バイナリファイルはサポートされていません!!!
+ Blame
+ BLAMEではこのファイルはサポートされていません!!!
+ ${0}$ をチェックアウトする...
+ ワークツリーと比較
+ ブランチ名をコピー
+ カスタムアクション
+ ${0}$を削除...
+ 選択中の{0}個のブランチを削除
+ ${0}$ へ早送りする
+ ${0}$ から ${1}$ へフェッチする
+ Git Flow - Finish ${0}$
+ ${0}$ を ${1}$ にマージする...
+ 選択中の{0}個のブランチを現在のブランチにマージする
+ ${0}$ をプルする
+ ${0}$ を ${1}$ にプルする...
+ ${0}$ をプッシュする
+ ${0}$ を ${1}$ でリベースする...
+ ${0}$ をリネームする...
+ トラッキングブランチを設定...
+ ブランチの比較
+ 無効な上流ブランチ!
+ バイト
+ キャンセル
+ 親リビジョンにリセット
+ このリビジョンにリセット
+ コミットメッセージを生成
+ 変更表示の切り替え
+ ファイルとディレクトリのリストを表示
+ パスのリストを表示
+ ファイルシステムのツリーを表示
+ ブランチをチェックアウト
+ コミットをチェックアウト
+ コミット:
+ 警告: コミットをチェックアウトするとHEADが切断されます
+ ローカルの変更:
+ 破棄
+ スタッシュして再適用
+ ブランチ:
+ チェリーピック
+ ソースをコミットメッセージに追加
+ コミット(複数可):
+ すべての変更をコミット
+ メインライン:
+ 通常、マージをチェリーピックすることはできません。どちらのマージ元をメインラインとして扱うべきかが分からないためです。このオプションを使用すると、指定した親に対して変更を再適用する形でチェリーピックを実行できます。
+ スタッシュをクリア
+ すべてのスタッシュをクリアします。続行しますか?
+ リモートリポジトリをクローン
+ 追加の引数:
+ リポジトリをクローンする際の追加パラメータ(任意)。
+ ローカル名:
+ リポジトリの名前(任意)。
+ 親フォルダ:
+ サブモジュールを初期化して更新
+ リポジトリのURL:
+ 閉じる
+ エディタ
+ コミットをチェックアウト
+ このコミットをチェリーピック
+ チェリーピック...
+ HEADと比較
+ ワークツリーと比較
+ 情報
+ SHA
+ カスタムアクション
+ ${0}$ ブランチをここにインタラクティブリベース
+ ${0}$ にマージ
+ マージ...
+ ${0}$ をここにリベース
+ ${0}$ ブランチをここにリセット
+ コミットを戻す
+ 書き直す
+ パッチとして保存...
+ 親にスカッシュ
+ 子コミットをここにスカッシュ
+ 変更
+ 変更を検索...
+ ファイル
+ LFSファイル
+ ファイルを検索...
+ サブモジュール
+ コミットの情報
+ 著者
+ 変更
+ 子
+ コミッター
+ このコミットを含む参照を確認
+ コミットが含まれるか確認
+ 最初の100件の変更のみが表示されています。すべての変更は'変更'タブで確認できます。
+ メッセージ
+ 親
+ 参照
+ SHA
+ ブラウザで開く
+ 説明
+ コミットのタイトルを入力
+ リポジトリの設定
+ コミットテンプレート
+ テンプレート内容:
+ テンプレート名:
+ カスタムアクション
+ 引数:
+ ${REPO} - リポジトリのパス; ${BRANCH} - 選択中のブランチ; ${SHA} - 選択中のコミットのSHA
+ 実行ファイル:
+ 名前:
+ スコープ:
+ ブランチ
+ コミット
+ リポジトリ
+ アクションの終了を待機
+ Eメールアドレス
+ Eメールアドレス
+ GIT
+ 自動的にリモートからフェッチ 間隔:
+ 分(s)
+ リモートの初期値
+ ISSUEトラッカー
+ サンプルのAzure DevOpsルールを追加
+ サンプルのGitee Issueルールを追加
+ サンプルのGiteeプルリクエストルールを追加
+ サンプルのGithubルールを追加
+ サンプルのGitLab Issueルールを追加
+ サンプルのGitLabマージリクエストルールを追加
+ サンプルのJiraルールを追加
+ 新しいルール
+ Issueの正規表現:
+ ルール名:
+ リザルトURL:
+ 正規表現のグループ値に$1, $2を使用してください。
+ AI
+ 優先するサービス:
+ 優先するサービスが設定されている場合、SourceGitはこのリポジトリでのみそれを使用します。そうでない場合で複数サービスが利用できる場合は、そのうちの1つを選択するためのコンテキストメニューが表示されます。
+ HTTP プロキシ
+ このリポジトリで使用するHTTPプロキシ
+ ユーザー名
+ このリポジトリにおけるユーザー名
+ ワークスペース
+ 色
+ 名前
+ 起動時にタブを復元
+ Conventional Commitヘルパー
+ 破壊的変更:
+ 閉じたIssue:
+ 詳細な変更:
+ スコープ:
+ 短い説明:
+ 変更の種類:
+ コピー
+ すべてのテキストをコピー
+ 絶対パスをコピー
+ パスをコピー
+ ブランチを作成...
+ 派生元:
+ 作成したブランチにチェックアウト
+ ローカルの変更:
+ 破棄
+ スタッシュして再適用
+ 新しいブランチの名前:
+ ブランチの名前を入力
+ スペースはダッシュに置き換えられます。
+ ローカルブランチを作成
+ タグを作成...
+ 付与されるコミット:
+ GPG署名を使用
+ タグメッセージ:
+ 任意。
+ タグの名前:
+ 推奨フォーマット: v1.0.0-alpha
+ 作成後にすべてのリモートにプッシュ
+ 新しいタグを作成
+ 種類:
+ 注釈付き
+ 軽量
+ Ctrlキーを押しながらクリックで実行
+ 切り取り
+ ブランチを削除
+ ブランチ:
+ リモートブランチを削除しようとしています!!!
+ もしリモートブランチを削除する場合、${0}$も削除します。
+ 複数のブランチを削除
+ 一度に複数のブランチを削除しようとしています! 操作を行う前に再度確認してください!
+ リモートを削除
+ リモート:
+ パス:
+ 対象:
+ すべての子ノードがリストから削除されます。
+ これはリストからのみ削除され、ディスクには保存されません!
+ グループを削除
+ リポジトリを削除
+ サブモジュールを削除
+ サブモジュールのパス:
+ タグを削除
+ タグ:
+ リモートリポジトリから削除
+ バイナリの差分
+ NEW
+ OLD
+ コピー
+ ファイルモードが変更されました
+ 先頭の差分
+ 空白の変更を無視
+ 最後の差分
+ LFSオブジェクトの変更
+ 次の差分
+ 変更がない、もしくはEOLの変更のみ
+ 前の差分
+ パッチとして保存
+ 隠されたシンボルを表示
+ 差分の分割表示
+ サブモジュール
+ 新規
+ スワップ
+ シンタックスハイライト
+ 行の折り返し
+ ブロックナビゲーションを有効化
+ マージツールで開く
+ すべての行を表示
+ 表示する行数を減らす
+ 表示する行数を増やす
+ ファイルを選択すると、変更内容が表示されます
+ マージツールで開く
+ 変更を破棄
+ ワーキングディレクトリのすべての変更を破棄
+ 変更:
+ 無視したファイルを含める
+ {0}個の変更を破棄します。
+ この操作を元に戻すことはできません!!!
+ ブックマーク:
+ 新しい名前:
+ 対象:
+ 選択中のグループを編集
+ 選択中のリポジトリを編集
+ カスタムアクションを実行
+ アクション名:
+ フェッチ
+ すべてのリモートをフェッチ
+ ローカル参照を強制的に上書き
+ タグなしでフェッチ
+ リモート:
+ リモートの変更をフェッチ
+ 変更されていないとみなされる
+ 破棄...
+ {0}個のファイルを破棄...
+ 選択された行の変更を破棄
+ 外部マージツールで開く
+ ${0}$ を使用して解決
+ パッチとして保存...
+ ステージ
+ {0}個のファイルをステージ...
+ 選択された行の変更をステージ
+ スタッシュ...
+ {0}個のファイルをスタッシュ...
+ アンステージ
+ {0}個のファイルをアンステージ...
+ 選択された行の変更をアンステージ
+ 自分の変更を使用 (checkout --ours)
+ 相手の変更を使用 (checkout --theirs)
+ ファイルの履歴
+ 変更
+ コンテンツ
+ Git-Flow
+ 開発ブランチ:
+ Feature:
+ Feature プレフィックス:
+ FLOW - Finish Feature
+ FLOW - Finish Hotfix
+ FLOW - Finish Release
+ 対象:
+ Hotfix:
+ Hotfix プレフィックス:
+ Git-Flowを初期化
+ ブランチを保持
+ プロダクション ブランチ:
+ Release:
+ Release プレフィックス:
+ Start Feature...
+ FLOW - Start Feature
+ Start Hotfix...
+ FLOW - Start Hotfix
+ 名前を入力
+ Start Release...
+ FLOW - Start Release
+ Versionタグ プレフィックス:
+ Git LFS
+ トラックパターンを追加...
+ パターンをファイル名として扱う
+ カスタム パターン:
+ Git LFSにトラックパターンを追加
+ フェッチ
+ `git lfs fetch`を実行して、Git LFSオブジェクトをダウンロードします。ワーキングコピーは更新されません。
+ LFSオブジェクトをフェッチ
+ Git LFSフックをインストール
+ ロックを表示
+ ロックされているファイルはありません
+ ロック
+ 私のロックのみ表示
+ LFSロック
+ ロック解除
+ 強制的にロック解除
+ 削除
+ `git lfs prune`を実行して、ローカルの保存領域から古いLFSファイルを削除します。
+ プル
+ `git lfs pull`を実行して、現在の参照とチェックアウトのすべてのGit LFSファイルをダウンロードします。
+ LFSオブジェクトをプル
+ プッシュ
+ キュー内の大容量ファイルをGit LFSエンドポイントにプッシュします。
+ LFSオブジェクトをプッシュ
+ リモート:
+ {0}という名前のファイルをトラック
+ すべての*{0}ファイルをトラック
+ 履歴
+ 著者
+ 著者時間
+ グラフ & コミットのタイトル
+ SHA
+ 日時
+ {0} コミットを選択しました
+ 'Ctrl'キーまたは'Shift'キーを押すと、複数のコミットを選択できます。
+ ⌘ または ⇧ キーを押して複数のコミットを選択します。
+ TIPS:
+ キーボードショートカットを確認
+ 総合
+ 現在のポップアップをキャンセル
+ 新しくリポジトリをクローン
+ 現在のページを閉じる
+ 次のページに移動
+ 前のページに移動
+ 新しいページを作成
+ 設定ダイアログを開く
+ リポジトリ
+ ステージ済みの変更をコミット
+ ステージ済みの変更をコミットしてプッシュ
+ 全ての変更をステージしてコミット
+ 選択中のコミットから新たなブランチを作成
+ 選択した変更を破棄
+ 直接フェッチを実行
+ ダッシュボードモード (初期値)
+ コミット検索モード
+ 直接プルを実行
+ 直接プッシュを実行
+ 現在のリポジトリを強制的に再読み込み
+ 選択中の変更をステージ/アンステージ
+ '変更'に切り替える
+ '履歴'に切り替える
+ 'スタッシュ'に切り替える
+ テキストエディタ
+ 検索パネルを閉じる
+ 次のマッチを検索
+ 前のマッチを検索
+ 検索パネルを開く
+ 破棄
+ ステージ
+ アンステージ
+ リポジトリの初期化
+ パス:
+ チェリーピックが進行中です。'中止'を押すと元のHEADが復元されます。
+ コミットを処理中
+ マージリクエストが進行中です。'中止'を押すと元のHEADが復元されます。
+ マージ中
+ リベースが進行中です。'中止'を押すと元のHEADが復元されます。
+ 停止しました
+ 元に戻す処理が進行中です。'中止'を押すと元のHEADが復元されます。
+ コミットを元に戻しています
+ インタラクティブ リベース
+ On:
+ 対象のブランチ:
+ リンクをコピー
+ ブラウザで開く
+ エラー
+ 通知
+ ブランチのマージ
+ 宛先:
+ マージオプション:
+ ソースブランチ:
+ マージ (複数)
+ すべての変更をコミット
+ マージ戦略:
+ 対象:
+ リポジトリノードの移動
+ 親ノードを選択:
+ 名前:
+ Gitが設定されていません。まず[設定]に移動して設定を行ってください。
+ アプリケーションデータのディレクトリを開く
+ 外部ツールで開く...
+ 任意。
+ 新しいページを開く
+ ブックマーク
+ タブを閉じる
+ 他のタブを閉じる
+ 右のタブを閉じる
+ リポジトリパスをコピー
+ リポジトリ
+ 貼り付け
+ {0} 日前
+ 1 時間前
+ {0} 時間前
+ たった今
+ 先月
+ 昨年
+ {0} 分前
+ {0} ヶ月前
+ {0} 年前
+ 昨日
+ 改行には'Shift+Enter'キーを使用します。 'Enter"はOKボタンのホットキーとして機能します。
+ 設定
+ AI
+ 差分分析プロンプト
+ APIキー
+ タイトル生成プロンプト
+ モデル
+ 名前
+ サーバー
+ ストリーミングを有効化
+ 外観
+ デフォルトのフォント
+ エディタのタブ幅
+ フォントサイズ
+ デフォルト
+ エディタ
+ 等幅フォント
+ テキストエディタでは等幅フォントのみを使用する
+ テーマ
+ テーマの上書き
+ タイトルバーの固定タブ幅を使用
+ ネイティブウィンドウフレームを使用
+ 差分/マージ ツール
+ インストール パス
+ 差分/マージ ツールのパスを入力
+ ツール
+ 総合
+ 起動時にアップデートを確認
+ 日時のフォーマット
+ 言語
+ コミット履歴
+ グラフにコミット時間の代わりに著者の時間を表示する
+ コミット詳細に子コミットを表示
+ コミットグラフにタグを表示
+ コミットタイトル枠の大きさ
+ GIT
+ 自動CRLFを有効化
+ デフォルトのクローンディレクトリ
+ ユーザー Eメールアドレス
+ グローバルgitのEメールアドレス
+ フェッチ時に--pruneを有効化
+ Git (>= 2.25.1) はこのアプリで必要です
+ インストール パス
+ HTTP SSL 検証を有効にする
+ ユーザー名
+ グローバルのgitユーザー名
+ Gitバージョン
+ GPG 署名
+ コミットにGPG署名を行う
+ GPGフォーマット
+ プログラムのインストールパス
+ インストールされたgpgプログラムのパスを入力
+ タグにGPG署名を行う
+ ユーザー署名キー
+ ユーザーのGPG署名キー
+ 統合
+ シェル/ターミナル
+ パス
+ シェル/ターミナル
+ リモートを削除
+ 対象:
+ 作業ツリーを削除
+ `$GIT_DIR/worktrees` の作業ツリー情報を削除
+ プル
+ ブランチ:
+ 宛先:
+ ローカルの変更:
+ 破棄
+ スタッシュして再適用
+ リモート:
+ プル (フェッチ & マージ)
+ マージの代わりにリベースを使用
+ プッシュ
+ サブモジュールがプッシュされていることを確認
+ 強制的にプッシュ
+ ローカル ブランチ:
+ リモート:
+ 変更をリモートにプッシュ
+ リモート ブランチ:
+ 追跡ブランチとして設定
+ すべてのタグをプッシュ
+ リモートにタグをプッシュ
+ すべてのリモートにプッシュ
+ リモート:
+ タグ:
+ 終了
+ 現在のブランチをリベース
+ ローカルの変更をスタッシュして再適用
+ On:
+ リベース:
+ リモートを追加
+ リモートを編集
+ 名前:
+ リモートの名前
+ リポジトリのURL:
+ リモートのgitリポジトリのURL
+ URLをコピー
+ 削除...
+ 編集...
+ フェッチ
+ ブラウザで開く
+ 削除
+ ワークツリーの削除を確認
+ `--force` オプションを有効化
+ 対象:
+ ブランチの名前を編集
+ 新しい名前:
+ このブランチにつける一意な名前
+ ブランチ:
+ 中止
+ リモートから変更を自動取得中...
+ クリーンアップ(GC & Prune)
+ このリポジトリに対して`git gc`コマンドを実行します。
+ すべてのフィルターをクリア
+ リポジトリの設定
+ 続ける
+ カスタムアクション
+ カスタムアクションがありません
+ すべての変更を破棄
+ `--reflog` オプションを有効化
+ ファイルブラウザーで開く
+ ブランチ/タグ/サブモジュールを検索
+ 解除
+ コミットグラフで非表示
+ コミットグラフでフィルター
+ `--first-parent` オプションを有効化
+ レイアウト
+ 水平
+ 垂直
+ コミットの並び順
+ 日時
+ トポロジカルソート
+ ローカル ブランチ
+ HEADに移動
+ ブランチを作成
+ 通知をクリア
+ グラフで現在のブランチを強調表示
+ {0} で開く
+ 外部ツールで開く
+ 更新
+ リモート
+ リモートを追加
+ コミットを検索
+ 著者
+ コミッター
+ ファイル
+ メッセージ
+ SHA
+ 現在のブランチ
+ タグをツリーとして表示
+ スキップ
+ 統計
+ サブモジュール
+ サブモジュールを追加
+ サブモジュールを更新
+ タグ
+ 新しいタグを作成
+ 作成者日時
+ 名前
+ ソート
+ ターミナルで開く
+ 履歴に相対時間を使用
+ ワークツリー
+ ワークツリーを追加
+ 削除
+ GitリポジトリのURL
+ 現在のブランチをリビジョンにリセット
+ リセットモード:
+ 移動先:
+ 現在のブランチ:
+ ファイルエクスプローラーで表示
+ コミットを戻す
+ コミット:
+ コミットの変更を戻す
+ コミットメッセージを書き直す
+ 実行中です。しばらくお待ちください...
+ 保存
+ 名前を付けて保存...
+ パッチが正常に保存されました!
+ リポジトリをスキャン
+ ルートディレクトリ:
+ 更新を確認
+ 新しいバージョンのソフトウェアが利用可能です:
+ 更新の確認に失敗しました!
+ ダウンロード
+ このバージョンをスキップ
+ ソフトウェアの更新
+ 利用可能なアップデートはありません
+ トラッキングブランチを設定
+ ブランチ:
+ 上流ブランチを解除
+ 上流ブランチ:
+ SHAをコピー
+ Go to
+ スカッシュコミット
+ 宛先:
+ SSH プライベートキー:
+ プライベートSSHキーストアのパス
+ スタート
+ スタッシュ
+ 追跡されていないファイルを含める
+ メッセージ:
+ オプション. このスタッシュの情報
+ ステージされた変更のみ
+ 選択したファイルの、ステージされた変更とステージされていない変更の両方がスタッシュされます!!!
+ ローカルの変更をスタッシュ
+ 適用
+ 破棄
+ パッチとして保存
+ スタッシュを破棄
+ 破棄:
+ スタッシュ
+ 変更
+ スタッシュ
+ 統計
+ コミット
+ コミッター
+ 概要
+ 月間
+ 週間
+ 著者:
+ コミット:
+ サブモジュール
+ サブモジュールを追加
+ 相対パスをコピー
+ ネストされたサブモジュールを取得する
+ サブモジュールのリポジトリを開く
+ 相対パス:
+ このモジュールを保存するフォルダの相対パス
+ サブモジュールを削除
+ OK
+ タグ名をコピー
+ タグメッセージをコピー
+ ${0}$ を削除...
+ ${0}$ を ${1}$ にマージ...
+ ${0}$ をプッシュ...
+ サブモジュールを更新
+ すべてのサブモジュール
+ 必要に応じて初期化
+ 再帰的に更新
+ サブモジュール:
+ --remoteオプションを使用
+ URL:
+ 警告
+ ようこそ
+ グループを作成
+ サブグループを作成
+ リポジトリをクローンする
+ 削除
+ ドラッグ & ドロップでフォルダを追加できます. グループを作成したり、変更したりできます。
+ 編集
+ 別のグループに移動
+ すべてのリポジトリを開く
+ リポジトリを開く
+ ターミナルを開く
+ デフォルトのクローンディレクトリ内のリポジトリを再スキャン
+ リポジトリを検索...
+ ソート
+ 変更
+ Git Ignore
+ すべての*{0}ファイルを無視
+ 同じフォルダ内の*{0}ファイルを無視
+ このファイルのみを無視
+ Amend
+ このファイルを今すぐステージできます。
+ コミット
+ コミットしてプッシュ
+ メッセージのテンプレート/履歴
+ クリックイベントをトリガー
+ コミット (Edit)
+ すべての変更をステージしてコミット
+ 競合が検出されました
+ ファイルの競合は解決されました
+ 追跡されていないファイルを含める
+ 最近の入力メッセージはありません
+ コミットテンプレートはありません
+ 選択したファイルを右クリックし、競合を解決する操作を選択してください。
+ サインオフ
+ ステージしたファイル
+ ステージを取り消し
+ すべてステージを取り消し
+ 未ステージのファイル
+ ステージへ移動
+ すべてステージへ移動
+ 変更されていないとみなしたものを表示
+ テンプレート: ${0}$
+ ワークスペース:
+ ワークスペースを設定...
+ ワークツリー
+ パスをコピー
+ ロック
+ 削除
+ ロック解除
+
diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml
index b146bf0e..fecd9540 100644
--- a/src/Resources/Locales/pt_BR.axaml
+++ b/src/Resources/Locales/pt_BR.axaml
@@ -3,63 +3,26 @@
-
-
Sobre
Sobre o SourceGit
- • Construído com
- • Gráfico desenhado por
- • Editor de Texto de
- • Fontes monoespaçadas de
- • Código-fonte pode ser encontrado em
Cliente Git GUI Livre e de Código Aberto
Adicionar Worktree
- O que Checar:
- Branch Existente
- Criar Novo Branch
Localização:
Caminho para este worktree. Caminho relativo é suportado.
Nome do Branch:
Opcional. O padrão é o nome da pasta de destino.
Rastrear Branch:
Rastreando branch remoto
+ O que Checar:
+ Criar Novo Branch
+ Branch Existente
Assietente IA
Utilizar IA para gerar mensagem de commit
Patch
- Erro
- Erros levantados e se recusa a aplicar o patch
- Erro Total
- Semelhante a 'erro', mas mostra mais
Arquivo de Patch:
Selecione o arquivo .patch para aplicar
Ignorar mudanças de espaço em branco
- Sem Aviso
- Desativa o aviso de espaço em branco no final
Aplicar Patch
- Aviso
- Emite avisos para alguns erros, mas aplica
Espaço em Branco:
Arquivar...
Salvar Arquivo Como:
@@ -70,16 +33,16 @@
ARQUIVOS CONSIDERADOS SEM ALTERAÇÕES
NENHUM ARQUIVO CONSIDERADO SEM ALTERAÇÕES
REMOVER
+ Atualizar
ARQUIVO BINÁRIO NÃO SUPORTADO!!!
Blame
BLAME NESTE ARQUIVO NÃO É SUPORTADO!!!
Checkout ${0}$...
- Comparar com HEAD
+ Comparar com ${0}$
Comparar com Worktree
Copiar Nome do Branch
Excluir ${0}$...
Excluir {0} branches selecionados
- Descartar todas as alterações
Fast-Forward para ${0}$
Buscar ${0}$ em ${1}$...
Git Flow - Finalizar ${0}$
@@ -93,8 +56,8 @@
Comparação de Branches
Bytes
CANCELAR
- Resetar para Esta Revisão
Resetar para Revisão Pai
+ Resetar para Esta Revisão
Gerar mensagem de commit
ALTERAR MODO DE EXIBIÇÃO
Exibir como Lista de Arquivos e Diretórios
@@ -102,12 +65,12 @@
Exibir como Árvore de Sistema de Arquivos
Checkout Branch
Checkout Commit
- Aviso: Ao fazer o checkout de um commit, seu Head ficará desanexado
Commit:
- Branch:
+ Aviso: Ao fazer o checkout de um commit, seu Head ficará desanexado
Alterações Locais:
Descartar
Stash & Reaplicar
+ Branch:
Cherry-Pick
Adicionar origem à mensagem de commit
Commit(s):
@@ -125,13 +88,13 @@
URL do Repositório:
FECHAR
Editor
+ Checar Commit
Cherry-Pick este commit
Cherry-Pick ...
- Checar Commit
Comparar com HEAD
Comparar com Worktree
- Copiar Informações
- Copiar SHA
+ Informações
+ SHA
Ação customizada
Rebase Interativo ${0}$ até Aqui
Rebase ${0}$ até Aqui
@@ -158,12 +121,12 @@
REFERÊNCIAS
SHA
Abrir no navegador
- Insira o assunto do commit
Descrição
+ Insira o assunto do commit
Configurar Repositório
TEMPLATE DE COMMIT
- Nome do Template:
Conteúdo do Template:
+ Nome do Template:
AÇÃO CUSTOMIZADA
Argumentos:
${REPO} - Caminho do repositório; ${SHA} - SHA do commit selecionado
@@ -179,10 +142,11 @@
Minuto(s)
Remoto padrão
RASTREADOR DE PROBLEMAS
+ Adicionar Regra de Exemplo do Azure DevOps
Adicionar Regra de Exemplo do Github
- Adicionar Regra de Exemplo do Jira
Adicionar Regra de Exemplo do GitLab
Adicionar regra de exemplo de Merge Request do GitLab
+ Adicionar Regra de Exemplo do Jira
Nova Regra
Expressão Regex de Issue:
Nome da Regra:
@@ -197,6 +161,7 @@
Nome de usuário para este repositório
Workspaces
Cor
+ Nome
Restaurar abas ao inicializar
Assistente de Conventional Commit
Breaking Change:
@@ -208,7 +173,6 @@
Copiar
Copiar todo o texto
Copiar Caminho
- Copiar Nome do Arquivo
Criar Branch...
Baseado Em:
Checar o branch criado
@@ -285,7 +249,6 @@
Editar Repositório Selecionado
Executar ação customizada
Nome da ação:
- Fast-Forward (sem checkout)
Buscar
Buscar todos os remotos
Buscar sem tags
@@ -305,11 +268,11 @@
Desfazer Preparação
Desfazer Preparação de {0} arquivos
Desfazer Preparação nas Linhas Selecionadas
- Usar Deles (checkout --theirs)
Usar Meu (checkout --ours)
+ Usar Deles (checkout --theirs)
Histórico de Arquivos
- CONTEUDO
MUDANÇA
+ CONTEUDO
Git-Flow
Branch de Desenvolvimento:
Feature:
@@ -339,8 +302,8 @@
Padrão Personalizado:
Adicionar Padrão de Rastreamento ao Git LFS
Buscar
- Buscar Objetos LFS
Execute `git lfs fetch` para baixar objetos Git LFS. Isso não atualiza a cópia de trabalho.
+ Buscar Objetos LFS
Instalar hooks do Git LFS
Exibir bloqueios
Sem Arquivos Bloqueados
@@ -352,11 +315,11 @@
Prune
Execute `git lfs prune` para excluir arquivos LFS antigos do armazenamento local
Puxar
- Puxar Objetos LFS
Execute `git lfs pull` para baixar todos os arquivos Git LFS para a referência atual e checkout
+ Puxar Objetos LFS
Enviar
- Enviar Objetos LFS
Envie arquivos grandes enfileirados para o endpoint Git LFS
+ Enviar Objetos LFS
Remoto:
Rastrear arquivos nomeados '{0}'
Rastrear todos os arquivos *{0}
@@ -374,8 +337,8 @@
GLOBAL
Cancelar popup atual
Fechar página atual
- Ir para a página anterior
Ir para a próxima página
+ Ir para a página anterior
Criar nova página
Abrir diálogo de preferências
REPOSITÓRIO
@@ -386,11 +349,11 @@
Descartar mudanças selecionadas
Buscar, imediatamente
Modo de Dashboard (Padrão)
+ Modo de busca de commits
Puxar, imediatamente
Enviar, imediatamente
Forçar recarregamento deste repositório
Preparar/Despreparar mudanças selecionadas
- Modo de busca de commits
Alternar para 'Mudanças'
Alternar para 'Históricos'
Alternar para 'Stashes'
@@ -399,9 +362,9 @@
Encontrar próxima correspondência
Encontrar correspondência anterior
Abrir painel de busca
+ Descartar
Preparar
Despreparar
- Descartar
Inicializar Repositório
Caminho:
Cherry-Pick em andamento.
@@ -409,10 +372,10 @@
Rebase em andamento.
Revert em andamento.
Rebase Interativo
- Ramo Alvo:
Em:
- Abrir no navegador
+ Ramo Alvo:
Copiar link
+ Abrir no navegador
ERRO
AVISO
Mesclar Ramo
@@ -433,16 +396,17 @@
Copiar Caminho do Repositório
Repositórios
Colar
- Agora mesmo
- {0} minutos atrás
+ {0} dias atrás
1 hora atrás
{0} horas atrás
- Ontem
- {0} dias atrás
+ Agora mesmo
Mês passado
- {0} meses atrás
Ano passado
+ {0} minutos atrás
+ {0} meses atrás
{0} anos atrás
+ Ontem
+ Use 'Shift+Enter' para inserir uma nova linha. 'Enter' é a tecla de atalho do botão OK
Preferências
INTELIGÊNCIA ARTIFICIAL
Prompt para Analisar Diff
@@ -478,35 +442,33 @@
Email do Usuário
Email global do usuário git
Habilita --prune ao buscar
+ Git (>= 2.25.1) é necessário para este aplicativo
Caminho de Instalação
Nome do Usuário
Nome global do usuário git
Versão do Git
- Git (>= 2.23.0) é necessário para este aplicativo
ASSINATURA GPG
Assinatura GPG de commit
- Assinatura GPG de tag
Formato GPG
Caminho de Instalação do Programa
Insira o caminho para o programa gpg instalado
+ Assinatura GPG de tag
Chave de Assinatura do Usuário
Chave de assinatura gpg do usuário
INTEGRAÇÃO
SHELL/TERMINAL
- Shell/Terminal
Caminho
+ Shell/Terminal
Prunar Remoto
Alvo:
Podar Worktrees
- Podar informações de worktree em `$GIT_DIR/worktrees`
+ Podar informações de worktree em `$GIT_COMMON_DIR/worktrees`
Puxar
Branch Remoto:
- Buscar todos os branches
Para:
Alterações Locais:
Descartar
Guardar & Reaplicar
- Buscar sem tags
Remoto:
Puxar (Buscar & Mesclar)
Usar rebase em vez de merge
@@ -528,7 +490,6 @@
Guardar & reaplicar alterações locais
Em:
Rebase:
- Atualizar
Adicionar Remoto
Editar Remoto
Nome:
@@ -557,6 +518,7 @@
CONTINUAR
Ações customizada
Nenhuma ação customizada
+ Descartar todas as alterações
Habilitar opção '--reflog'
Abrir no Navegador de Arquivos
Pesquisar Branches/Tags/Submódulos
@@ -602,7 +564,6 @@
Commit:
Commitar alterações de reversão
Reescrever Mensagem do Commit
- Use 'Shift+Enter' para inserir uma nova linha. 'Enter' é a tecla de atalho do botão OK
Executando. Por favor, aguarde...
SALVAR
Salvar Como...
@@ -624,15 +585,13 @@
INICIAR
Stash
Incluir arquivos não rastreados
- Manter arquivos em stage
Mensagem:
- Opcional. Nome deste stash
+ Opcional. Informações deste stash
Apenas mudanças em stage
Tanto mudanças em stage e fora de stage dos arquivos selecionados serão enviadas para stash!!!
Guardar Alterações Locais
Aplicar
Descartar
- Pop
Descartar Stash
Descartar:
Stashes
@@ -641,11 +600,11 @@
Estatísticas
COMMITS
COMMITTER
+ VISÃO GERAL
MÊS
SEMANA
- COMMITS:
AUTORES:
- VISÃO GERAL
+ COMMITS:
SUBMÓDULOS
Adicionar Submódulo
Copiar Caminho Relativo
@@ -660,13 +619,13 @@
Excluir ${0}$...
Mesclar ${0}$ em ${1}$...
Enviar ${0}$...
- URL:
Atualizar Submódulos
Todos os submódulos
Inicializar conforme necessário
Recursivamente
Submódulo:
Usar opção --remote
+ URL:
Aviso
Página de Boas-vindas
Criar Grupo Raíz
@@ -686,7 +645,6 @@
Git Ignore
Ignorar todos os arquivos *{0}
Ignorar arquivos *{0} na mesma pasta
- Ignorar arquivos na mesma pasta
Ignorar apenas este arquivo
Corrigir
Você pode stagear este arquivo agora.
@@ -695,21 +653,20 @@
Modelo/Históricos
Acionar evento de clique
Preparar todas as mudanças e commitar
- Commit vazio detectado! Deseja continuar (--allow-empty)?
CONFLITOS DETECTADOS
CONFLITOS DE ARQUIVO RESOLVIDOS
INCLUIR ARQUIVOS NÃO RASTREADOS
SEM MENSAGENS DE ENTRADA RECENTES
SEM MODELOS DE COMMIT
+ Clique com o botão direito nos arquivos selecionados e escolha como resolver conflitos.
STAGED
UNSTAGE
UNSTAGE TODOS
UNSTAGED
STAGE
STAGE TODOS
- VER SUPOR NÃO ALTERADO
+ VER SUPOR NÃO ALTERADO
Template: ${0}$
- Clique com o botão direito nos arquivos selecionados e escolha como resolver conflitos.
Workspaces:
Configurar workspaces...
WORKTREE
diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml
index 07fb7c94..708ff7cc 100644
--- a/src/Resources/Locales/ru_RU.axaml
+++ b/src/Resources/Locales/ru_RU.axaml
@@ -2,41 +2,29 @@
+
О программе
О SourceGit
- • Сборка с
- • Диаграмма отображается с помощью
- • Текстовый редактор от
- • Моноширинные шрифты взяты из
- • Исходный код можно найти по адресу
Бесплатный графический клиент Git с исходным кодом
Добавить рабочий каталог
- Переключиться на:
- ветку из списка
- создать новую ветку
Расположение:
Путь к рабочему каталогу (поддерживается относительный путь)
Имя ветки:
Имя целевого каталога по умолчанию. (необязательно)
Отслеживание ветки:
Отслеживание внешней ветки
+ Переключиться на:
+ Создать новую ветку
+ Ветку из списка
Помощник OpenAI
ПЕРЕСОЗДАТЬ
Использовать OpenAI для создания сообщения о ревизии
ПРИМЕНИТЬ КАК СООБЩЕНИЕ РЕВИЗИИ
Исправить
- Ошибка
- Выдает ошибки и отказывается применять заплатку
- Все ошибки
- Аналогично «ошибке», но показывает больше
Файл заплатки:
Выберите файл .patch для применения
Игнорировать изменения пробелов
- Нет предупреждений
- Отключить предупреждения о пробелах в конце
Применить заплатку
- Предупреждать
- Выдавать предупреждения о нескольких таких ошибках, но применять
Пробел:
Отложить
Удалить после применения
@@ -51,34 +39,43 @@
НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ
СПИСОК ПУСТ
УДАЛИТЬ
+ Загрузить картинку...
+ Обновить
ДВОИЧНЫЙ ФАЙЛ НЕ ПОДДЕРЖИВАЕТСЯ!!!
+ Раздвоить
+ О
+ Плохая
+ Раздвоение. Текущая ГОЛОВА (HEAD) хорошая или плохая?
+ Хорошая
+ Пропустить
+ Раздвоение. Сделать текущую ревизию хорошей или плохой и переключиться на другой.
Расследование
РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!!
- Проверить ${0}$...
- Сравнить с ГОЛОВОЙ (HEAD)
+ Переключиться на ${0}$...
+ Сравнить с ${0}$
Сравнить с рабочим каталогом
Копировать имя ветки
Изменить действие
Удалить ${0}$...
Удалить выбранные {0} ветки
- Отклонить все изменения.
Перемотать вперёд к ${0}$
Извлечь ${0}$ в ${1}$...
- Поток Git - Завершение ${0}$
+ Git-процесс - Завершение ${0}$
Влить ${0}$ в ${1}$...
Влить {0} выделенных веток в текущую
- Забрать ${0}$
- Забрать ${0}$ в ${1}$...
+ Загрузить ${0}$
+ Загрузить ${0}$ в ${1}$...
Выложить ${0}$
Переместить ${0}$ на ${1}$...
Переименовать ${0}$...
+ Сбросить ${0}$ к ${1}$...
Отслеживать ветку...
Сравнение веток
Недопустимая основная ветка!
Байты
ОТМЕНА
- Сбросить эту ревизию
Сбросить родительскую ревизию
+ Сбросить эту ревизию
Произвести сообщение о ревизии
ИЗМЕНИТЬ РЕЖИМ ОТОБРАЖЕНИЯ
Показывать в виде списка файлов и каталогов
@@ -86,12 +83,15 @@
Показывать в виде дерева файловой системы
Переключить ветку
Переключение ревизии
- Предупреждение: После переключения ревизии ваша Голова (HEAD) будет отсоединена
Ревизия:
- Ветка:
+ Предупреждение: После переключения ревизии ваша Голова (HEAD) будет отсоединена
Локальные изменения:
Отклонить
Отложить и примненить повторно
+ Обновить все подкаталоги
+ Ветка:
+ Переключиться и перемотать
+ Перемотать к:
Частичный выбор
Добавить источник для ревизии сообщения
Ревизия(и):
@@ -110,17 +110,21 @@
Адрес репозитория:
ЗАКРЫТЬ
Редактор
+ Переключиться на эту ревизию
Применить эту ревизию (cherry-pick)
Применить несколько ревизий ...
- Переключиться на эту ревизию
Сравнить c ГОЛОВОЙ (HEAD)
Сравнить с рабочим каталогом
- Копировать информацию
- Копировать SHA
+ Автор
+ Ревизор
+ Информацию
+ SHA
+ Субъект
Пользовательское действие
Интерактивное перемещение (rebase -i) ${0}$ сюда
Влить в ${0}$
Влить ...
+ Выложить ${0}$ в ${1}$
Переместить ${0}$ сюда
Сбросить ${0}$ сюда
Отменить ревизию
@@ -129,6 +133,7 @@
Объединить с предыдущей ревизией
Объединить все следующие ревизии с этим
ИЗМЕНЕНИЯ
+ изменённый(х) файл(ов)
Найти изменения....
ФАЙЛЫ
Файл LFS
@@ -147,12 +152,13 @@
ССЫЛКИ
SHA
Открыть в браузере
- Введите тему ревизии
Описание
+ СУБЪЕКТ
+ Введите тему ревизии
Настройка репозитория
ШАБЛОН РЕВИЗИИ
- Название:
Cодержание:
+ Название:
ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ
Аргументы:
${REPO} - Путь к репозиторию; ${SHA} - SHA ревизий
@@ -169,13 +175,15 @@
Автозагрузка изменений
Минут(а/ы)
Внешний репозиторий по умолчанию
+ Предпочтительный режим слияния
ОТСЛЕЖИВАНИЕ ПРОБЛЕМ
+ Добавить пример правила Azure DevOps
Добавить пример правила для тем в Gitea
Добавить пример правила запроса скачивания из Gitea
Добавить пример правила для Git
- Добавить пример правила Jira
Добавить пример правила выдачи GitLab
Добавить пример правила запроса на слияние в GitLab
+ Добавить пример правила Jira
Новое правило
Проблема с регулярным выражением:
Имя правила:
@@ -189,9 +197,13 @@
Имя пользователя
Имя пользователя репозитория
Рабочие пространства
- Имя
Цвет
+ Имя
Восстанавливать вкладки при запуске
+ ПРОДОЛЖИТЬ
+ Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty)?
+ Сформировать всё и зафиксировать ревизию
+ Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить всё, затем зафиксировать ревизию?
Общепринятый помощник по ревизии
Кардинальные изменения:
Закрытая тема:
@@ -201,11 +213,11 @@
Тип изменения:
Копировать
Копировать весь текст
+ Копировать полный путь
Копировать путь
- Копировать имя файла
Создать ветку...
Основан на:
- Проверить созданную ветку
+ Переключиться на созданную ветку
Локальные изменения:
Отклонить
Отложить и применить повторно
@@ -213,10 +225,11 @@
Введите имя ветки.
Пробелы будут заменены на тире.
Создать локальную ветку
+ Перезаписать существующую ветку
Создать метку...
Новая метка у:
GPG подпись
- Сообщение с
меткой:
+ Сообщение с
меткой:
Необязательно.
Имя метки:
Рекомендуемый формат: v1.0.0-alpha
@@ -225,8 +238,11 @@
Вид:
С примечаниями
Простой
- Удерживайте Ctrl, чтобы начать сразу
+ Удерживайте Ctrl, чтобы сразу начать
Вырезать
+ Удалить подмодуль
+ Принудительно удалить даже если содержит локальные изменения.
+ Подмодуль:
Удалить ветку
Ветка:
Вы собираетесь удалить внешнюю ветку!!!
@@ -238,30 +254,31 @@
Путь:
Цель:
Все дочерние элементы будут удалены из списка.
- Подтвердите удаление группы
Будет удалён из списка. На диске останется.
+ Подтвердите удаление группы
Подтвердите удаление репозитория
Удалить подмодуль
Путь подмодуля:
Удалить метку
Метка:
Удалить из внешнего репозитория
- РАЗНИЦА БИНАРНИКОВ
+ СРАВНЕНИЕ БИНАРНИКОВ
НОВЫЙ
СТАРЫЙ
Копировать
Режим файла изменён
- Первое различие
- Игнорировать изменение пробелов
- Последнее различие
- Показывать скрытые символы
+ Первое сравнение
+ Игнорировать изменения пробелов
+ Последнее сравнение
ИЗМЕНЕНИЕ ОБЪЕКТА LFS
- Следующее различие
+ Следующее сравнение
НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL
- Предыдущее различие
+ Предыдущее сравнение
Сохранить как заплатку
- Различие рядом
+ Показывать скрытые символы
+ Сравнение рядом
ПОДМОДУЛЬ
+ УДАЛЁН
НОВЫЙ
Обмен
Подсветка синтаксиса
@@ -286,7 +303,6 @@
Редактировать выбранный репозиторий
Выполнить пользовательское действие
Имя действия:
- Быстрая перемотка вперёд (без проверки)
Извлечь
Извлечь все внешние репозитории
Разрешить опцию (--force)
@@ -300,41 +316,43 @@
Открыть расширенный инструмент слияния
Взять версию ${0}$
Сохранить как файл заплатки...
- Подготовить
- Подготовленные {0} файлы
- Подготовленные изменения в выбранной(ых) строке(ах)
+ Сформировать
+ Сформированные {0} файлы
+ Сформированные изменения в выбранной(ых) строке(ах)
Отложить...
Отложить {0} файлов...
- Снять подготовленный
- Неподготовленные {0} файлы
- Неподготовленные изменения в выбранной(ых) строке(ах)
- Использовать их (checkout --theirs)
+ Расформировать
+ Несформированные {0} файлы
+ Несформированные изменения в выбранной(ых) строке(ах)
Использовать мой (checkout --ours)
+ Использовать их (checkout --theirs)
История файлов
- СОДЕРЖИМОЕ
ИЗМЕНИТЬ
- Git-поток
+ СОДЕРЖИМОЕ
+ Git-процесс
Ветка разработчика:
Свойство:
Свойство префикса:
- ПОТОК - Свойства завершения
- ПОТОК - Закончить исправление
- ПОТОК - Завершить выпуск
+ ПРОЦЕСС - Свойства завершения
+ ПРОЦЕСС - Закончить исправление
+ ПРОЦЕСС - Завершить выпуск
Цель:
+ Выложить на удалённый(ые) после завершения
+ Втиснуть при слиянии
Исправление:
Префикс исправлений:
- Создать Git-поток
+ Создать Git-процесс
Держать ветку
Производственная ветка:
Выпуск:
Префикс выпуска:
Свойство запуска...
- ПОТОК - Свойство запуска
+ ПРОЦЕСС - Свойство запуска
Запуск исправлений...
- ПОТОК - Запуск исправлений
+ ПРОЦЕСС - Запуск исправлений
Ввести имя
Запуск выпуска...
- ПОТОК - Запуск выпуска
+ ПРОЦЕСС - Запуск выпуска
Префикс метки версии:
Git LFS (хранилище больших файлов)
Добавить шаблон отслеживания...
@@ -342,8 +360,8 @@
Изменить шаблон:
Добавить шаблон отслеживания в LFS Git
Извлечь
+ Запустить (git lfs fetch), чтобы загрузить объекты LFS Git. При этом рабочая копия не обновляется.
Извлечь объекты LFS
- Запустить «git lfs fetch», чтобы загрузить объекты LFS Git. При этом рабочая копия не обновляется.
Установить перехват LFS Git
Показывать блокировки
Нет заблокированных файлов
@@ -353,13 +371,13 @@
Разблокировать
Принудительно разблокировать
Обрезать
- Запустить «git lfs prune», чтобы удалить старые файлы LFS из локального хранилища
- Забрать
- Забрать объекты LFS
- Запустить «git lfs pull», чтобы загрузить все файлы LFS Git для текущей ссылки и проверить
+ Запустить (git lfs prune), чтобы удалить старые файлы LFS из локального хранилища
+ Загрузить
+ Запустить (git lfs pull), чтобы загрузить все файлы LFS Git для текущей ссылки и проверить
+ Загрузить объекты LFS
Выложить
- Выложить объекты LFS
Отправляйте большие файлы, помещенные в очередь, в конечную точку LFS Git
+ Выложить объекты LFS
Внешнее хранилище:
Отслеживать файлы с именем «{0}»
Отслеживать все *{0} файлов
@@ -378,23 +396,25 @@
Закрыть окно
Клонировать репозиторий
Закрыть вкладку
- Перейти на предыдущую вкладку
Перейти на следующую вкладку
+ Перейти на предыдущую вкладку
Создать новую вкладку
Открыть диалоговое окно настроек
+ Переключить активное рабочее место
+ Переключить активную страницу
РЕПОЗИТОРИЙ
- Зафиксировать подготовленные изменения
- Зафиксировать и выложить подготовленные изменения
- Подготовить все изменения и зафиксировать
+ Зафиксировать сформированные изменения
+ Зафиксировать и выложить сформированные изменения
+ Сформировать все изменения и зафиксировать
Создать новую ветку на основе выбранной ветки
Отклонить выбранные изменения
Извлечение, запускается сразу
Режим доски (по умолчанию)
- Принудительно перезагрузить репозиторий
- Забрать, запускается сразу
- Выложить, запускается сразу
- Подготовленные/Неподготовленные выбранные изменения
Режим поиска ревизий
+ Загрузить, запускается сразу
+ Выложить, запускается сразу
+ Принудительно перезагрузить репозиторий
+ Сформированные/Несформированные выбранные изменения
Переключить на «Изменения»
Переключить на «Истории»
Переключить на «Отложенные»
@@ -402,10 +422,11 @@
Закрыть панель поиска
Найти следующее совпадение
Найти предыдущее совпадение
+ Открыть с внешним инструментом сравнения/слияние
Открыть панель поиска
- Подготовить
- Снять из подготовленных
Отклонить
+ Сформировать
+ Расформировать
Создать репозиторий
Путь:
Выполняется частичный перенос ревизий (cherry-pick).
@@ -417,13 +438,16 @@
Выполняется отмена ревизии.
Выполняется отмена
Интерактивное перемещение
- Целевая ветка:
На:
- Открыть в браузере
+ Целевая ветка:
Копировать ссылку
- ОШИБКА
+ Открыть в браузере
+ ОШИБКА
УВЕДОМЛЕНИЕ
+ Рабочие места
+ Страницы
Влить ветку
+ Изменить сообщение слияния
В:
Опции слияния:
Источник:
@@ -446,30 +470,31 @@
Копировать путь репозитория
Репозитории
Вставить
- Сейчас
- {0} минут назад
+ {0} дней назад
1 час назад
{0} часов назад
- Вчера
- {0} дней назад
+ Сейчас
Последний месяц
- {0} месяцев назад
В прошлом году
+ {0} минут назад
+ {0} месяцев назад
{0} лет назад
+ Вчера
+ Используйте «Shift+Enter» для ввода новой строки. «Enter» - это горячая клавиша кнопки «OK»
Параметры
ОТКРЫТЬ ИИ
+ Запрос на анализ сравнения
Ключ API
- Запрос на анализ различий
- Произвести запрос на тему
+ Создать запрос на тему
Модель
Имя:
Сервер
Разрешить потоковую передачу
ВИД
Шрифт по умолчанию
+ Редактировать ширину вкладки
Размер шрифта
По умолчанию
- Редактировать ширину вкладки
Редактор
Моноширный шрифт
В текстовом редакторе используется только моноширный шрифт
@@ -477,9 +502,9 @@
Переопределение темы
Использовать фиксированную ширину табуляции в строке заголовка.
Использовать системное окно
- ИНСТРУМЕНТ РАЗЛИЧИЙ/СЛИЯНИЯ
+ ИНСТРУМЕНТ СРАВНЕНИЙ/СЛИЯНИЯ
Путь установки
- Введите путь для инструмента различия/слияния
+ Введите путь для инструмента сравнения/слияния
Инструмент
ОСНОВНЫЕ
Проверить обновления при старте
@@ -488,6 +513,7 @@
Максимальная длина истории
Показывать время автора вместо времени ревизии на графике
Показать наследника в деталях комментария
+ Показывать метки на графике
Длина темы ревизии
GIT
Включить автозавершение CRLF
@@ -495,44 +521,46 @@
Электроная почта пользователя
Общая электроная почта пользователя git
Разрешить (--prune) при скачивании
+ Разрешить (--ignore-cr-at-eol) в сравнении
+ Для работы программы требуется версия Git (>= 2.25.1)
Путь установки
Разрешить верификацию HTTP SSL
Имя пользователя
Общее имя пользователя git
Версия Git
- Для работы программы требуется версия Git (>= 2.23.0)
GPG ПОДПИСЬ
GPG подпись ревизии
- GPG подпись метки
Формат GPG
Путь установки программы
Введите путь для установленной программы GPG
+ GPG подпись метки
Ключ подписи пользователя
Ключ GPG подписи пользователя
ВНЕДРЕНИЕ
ОБОЛОЧКА/ТЕРМИНАЛ
- Оболочка/Терминал
Путь
+ Оболочка/Терминал
Удалить внешний репозиторий
Цель:
Удалить рабочий каталог
- Информация об обрезке рабочего каталога в «$GIT_DIR/worktrees»
- Забрать
+ Информация об обрезке рабочего каталога в «$GIT_COMMON_DIR/worktrees»
+ Загрузить
Ветка внешнего репозитория:
- Извлечь все ветки
В:
Локальные изменения:
Отклонить
Отложить и применить повторно
- Забрать без меток
+ Обновить все подмодули
Внешний репозиторий:
- Забрать (Получить и слить)
+ Загрузить (Получить и слить)
Использовать перемещение вместо слияния
Выложить
Убедитесь, что подмодули были вставлены
Принудительно выложить
Локальная ветка:
Внешний репозиторий:
+ Ревизия:
+ Выложить ревизию на удалёную
Выложить изменения на внешний репозиторий
Ветка внешнего репозитория:
Отслеживать ветку
@@ -546,7 +574,6 @@
Отложить и применить повторно локальные изменения
На:
Переместить:
- Обновить
Добавить внешний репозиторий
Редактировать внешний репозиторий
Имя:
@@ -568,13 +595,18 @@
Ветка:
Отказ
Автоматическое извлечение изменений с внешних репозиторий...
+ Сортировать
+ По дате ревизора (исполнителя)
+ По имени
Очистить (Сбор мусора и удаление)
- Запустить команду «git gc» для данного репозитория.
+ Запустить команду (git gc) для данного репозитория.
Очистить всё
+ Очистить
Настройка репозитория
ПРОДОЛЖИТЬ
Изменить действия
Не изменять действия
+ Отклонить все изменения.
Разрешить опцию --reflog
Открыть в файловом менеджере
Поиск веток, меток и подмодулей
@@ -582,7 +614,6 @@
Не установлен (по умолчанию)
Скрыть в графе ревизии
Фильтр в графе ревизии
- ОТФИЛЬТРОВАНО:
Включить опцию (--first-parent)
РАСПОЛОЖЕНИЕ
Горизонтально
@@ -603,10 +634,12 @@
Поиск ревизии
Автор
Ревизор
+ Содержимое
Файл
Сообщение
SHA
Текущая ветка
+ Показывать подмодули как дерево
Показывать метки как катлог
ПРОПУСТИТЬ
Статистикa
@@ -616,11 +649,12 @@
МЕТКИ
НОВАЯ МЕТКА
По дате создания
- По имени (по возрастанию)
- По имени (по убыванию)
+ По имени
Сортировать
Открыть в терминале
Использовать относительное время в историях
+ Просмотр журналов
+ Посетить '{0}' в браузере
РАБОЧИЕ КАТАЛОГИ
ДОБАВИТЬ РАБОЧИЙ КАТАЛОГ
ОБРЕЗАТЬ
@@ -629,20 +663,20 @@
Режим сброса:
Переместить в:
Текущая ветка:
+ Сброс ветки (без переключения)
+ Переместить в:
+ Ветка:
Открыть в файловом менеджере
Отменить ревизию
Ревизия:
Отмена ревизии
Изменить комментарий ревизии
- Используйте «Shift+Enter» для ввода новой строки. «Enter» - это горячая клавиша кнопки «OK»
Запуск. Подождите пожалуйста...
СОХРАНИТЬ
Сохранить как...
Заплатка успешно сохранена!
Сканирование репозиторий
Корневой каталог:
- Копировать SHA
- Перейти
Проверка для обновления...
Доступна новая версия программного обеспечения:
Не удалось проверить наличие обновлений!
@@ -654,24 +688,24 @@
Ветка:
Снять основную ветку
Основная ветка:
+ Копировать SHA
+ Перейти
Втиснуть ревизии
В:
Приватный ключ SSH:
Путь хранения приватного ключа SSH
- Только подготовленные изменения
- Подготовленные так и неподготовленные изменения выбранных файлов будут сохранены!!!
ЗАПУСК
Отложить
- Автоматически восстанавливать после откладывания
- Ваши рабочие файлы остаются неизменными, но отложенные сохранятся.
Включить неотслеживаемые файлы
- Хранить отложенные файлы
Сообщение:
Имя тайника (необязательно)
+ Режим:
+ Только сформированные изменения
+ Сформированные так и несформированные изменения выбранных файлов будут сохранены!!!
Отложить локальные изменения
Принять
+ Копировать сообщение
Отбросить
- Применить
Сохранить как заплатку...
Отбросить тайник
Отбросить:
@@ -681,32 +715,43 @@
Статистика
РЕВИЗИИ
РЕВИЗОРЫ (ИСПОЛНИТЕЛИ)
+ ОБЗОР
МЕСЯЦ
НЕДЕЛЯ
- РЕВИЗИИ:
АВТОРЫ:
- ОБЗОР
+ РЕВИЗИИ:
ПОДМОДУЛИ
Добавить подмодули
Копировать относительный путь
+ Удалить подмодуль
Извлечение вложенных подмодулей
Открыть подмодуль репозитория
Каталог:
Относительный путь для хранения подмодуля.
Удалить подмодуль
+ СОСТОЯНИЕ
+ изменён
+ не создан
+ ревизия изменена
+ не слита
+ URL-адрес
ОК
Копировать имя метки
Копировать сообщение с метки
Удалить ${0}$...
Влить ${0}$ в ${1}$...
Выложить ${0}$...
- Сетевой адрес:
Обновление подмодулей
Все подмодули
Создавать по необходимости
Рекурсивно
Подмодуль:
Использовать опцию (--remote)
+ Сетевой адрес:
+ Журналы
+ ОЧИСТИТЬ ВСЁ
+ Копировать
+ Удалить
Предупреждение
Приветствие
Создать группу
@@ -719,39 +764,44 @@
Открыть все репозитории
Открыть репозиторий
Открыть терминал
- Повторное сканирование репозиториев в каталоге клонирования по умолчанию
+ Повторное сканирование репозиториев в каталоге клонирования по умолчанию
Поиск репозиториев...
Сортировка
Изменения
Игнорировать Git
Игнорировать все *{0} файлы
Игнорировать *{0} файлы в том же каталоге
- Игнорировать файлы в том же каталоге
+ Игнорировать неотслеживаемые файлы в этом каталоге
Игнорировать только эти файлы
Изменить
- Теперь вы можете подготовить этот файл.
+ Теперь вы можете сформировать этот файл.
ЗАФИКСИРОВАТЬ
ЗАФИКСИРОВАТЬ и ОТПРАВИТЬ
Шаблон/Истории
Запустить событие щелчка
Зафиксировать (Редактировать)
- Подготовить все изменения и зафиксировать
- Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty)?
+ Сформировать все изменения и зафиксировать
+ Вы сформировали {0} файл(ов), но отображается только {1} файл(ов) ({2} файл(ов) отфильтровано). Вы хотите продолжить?
ОБНАРУЖЕНЫ КОНФЛИКТЫ
+ ОТКРЫТЬ ВНЕШНИЙ ИНСТРУМЕНТ СЛИЯНИЯ
+ ОТКРЫТЬ ВСЕ КОНФЛИКТЫ ВО ВНЕШНЕМ ИНСТРУМЕНТЕ СЛИЯНИЯ
КОНФЛИКТЫ ФАЙЛОВ РАЗРЕШЕНЫ
+ ИСПОЛЬЗОВАТЬ МОИ
+ ИСПОЛЬЗОВАТЬ ИХ
ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ
НЕТ ПОСЛЕДНИХ ВХОДНЫХ СООБЩЕНИЙ
НЕТ ШАБЛОНОВ РЕВИЗИИ
- Завершение работы
- ПОДГОТОВЛЕННЫЕ
- СНЯТЬ ПОДГОТОВЛЕННЫЙ
- СНЯТЬ ВСЕ ПОДГОТОВЛЕННЫЕ
- НЕПОДГОТОВЛЕННЫЕ
- ПОДГОТОВИТЬ
- ВСЕ ПОДГОТОВИТЬ
- ОТКРЫТЬ СПИСОК НЕОТСЛЕЖИВАЕМЫХ ФАЙЛОВ
- Шаблон: ${0}$
+ Сбросить автора
Щёлкните правой кнопкой мыши выбранный файл(ы) и разрешите конфликты.
+ Завершение работы
+ СФОРМИРОВАННЫЕ
+ РАСФОРМИРОВАТЬ
+ РАСФОРМИРОВАТЬ ВСЁ
+ НЕСФОРМИРОВАННЫЕ
+ СФОРМИРОВАТЬ
+ СФОРМИРОВАТЬ ВСЁ
+ ОТКРЫТЬ СПИСОК НЕОТСЛЕЖИВАЕМЫХ ФАЙЛОВ
+ Шаблон: ${0}$
РАБОЧЕЕ ПРОСТРАНСТВО:
Настройка рабочего пространства...
РАБОЧИЙ КАТАЛОГ
diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml
new file mode 100644
index 00000000..248d5f42
--- /dev/null
+++ b/src/Resources/Locales/ta_IN.axaml
@@ -0,0 +1,740 @@
+
+
+
+
+
+ பற்றி
+ மூலஅறிவிலி பற்றி
+ திறந்தமூல & கட்டற்ற அறிவிலி இடைமுக வாடிக்கயாளர்
+ பணிமரத்தைச் சேர்
+ இடம்:
+ இந்த பணிமரத்திற்கான பாதை. தொடர்புடைய பாதை ஆதரிக்கப்படுகிறது.
+ கிளை பெயர்:
+ விருப்பத்தேர்வு. இயல்புநிலை இலக்கு கோப்புறை பெயர்.
+ கிளை கண்காணி:
+ தொலை கிளையைக் கண்காணித்தல்
+ என்ன சரிபார்க்க வேண்டும்:
+ புதிய கிளையை உருவாக்கு
+ ஏற்கனவே உள்ள கிளை
+ செநு உதவியாளர்
+ மறு-உருவாக்கு
+ உறுதிமொழி செய்தியை உருவாக்க செநுவைப் பயன்படுத்து
+ உறுதிமொழி செய்தி என இடு
+ ஒட்டு
+ ஒட்டு கோப்பு:
+ .ஒட்டு இடுவதற்கு கோப்பைத் தேர்ந்தெடு
+ வெள்ளைவெளி மாற்றங்களைப் புறக்கணி
+ ஒட்டு இடு
+ வெள்ளைவெளி:
+ பதுக்கிவைத்ததை இடு
+ பயன்படுத்திய பின் நீக்கு
+ குறியீட்டின் மாற்றங்களை மீண்டும் நிறுவு
+ பதுக்கிவை:
+ காப்பகம்...
+ இதற்கு காப்பகத்தை சேமி:
+ காப்பகக் கோப்பு பாதையைத் தேர்ந்தெடு
+ திருத்தம்:
+ காப்பகம்
+ மூலஅறிவிலி கடவுகேள்
+ கோப்புகள் மாற்றப்படவில்லை எனக் கருதப்படுகிறது
+ எந்த கோப்புகளும் மாற்றப்படவில்லை எனக் கருதப்படுகிறது
+ நீக்கு
+ புதுப்பி
+ இருமம் கோப்பு ஆதரிக்கப்படவில்லை!!!
+ குற்றச்சாட்டு
+ இந்த கோப்பில் குற்றம் சாட்ட ஆதரிக்கப்படவில்லை!!!
+ ${0}$ சரிபார்...
+ பணிமரத்துடன் ஒப்பிடுக
+ கிளை பெயரை நகலெடு
+ தனிப்பயன் செயல்
+ ${0}$ ஐ நீக்கு...
+ தேர்ந்தெடுக்கப்பட்ட {0} கிளைகளை நீக்கு
+ ${0}$ இதற்கு வேகமாக முன்னோக்கிச் செல்
+ ${0}$ ஐ ${1}$இல் பெறு...
+ அறிவிலி ஓட்டம் - முடி ${0}$
+ ${0}$ ஐ ${1}$இல் இணை...
+ தேர்ந்தெடுக்கப்பட்ட {0} கிளைகளை தற்பொதையதில் இணை
+ இழு ${0}$
+ இழு ${0}$ஐ ${1}$-க்குள்...
+ தள்ளு ${0}$
+ மறுதளம் ${0}$ இதன்மேல் ${1}$...
+ மறுபெயரிடு ${0}$...
+ கண்காணிப்பு கிளையை அமை...
+ கிளை ஒப்பிடு
+ தவறான மேல்ஓடை!
+ எண்மங்கள்
+ விடு
+ பெற்றோர் திருத்தத்திற்கு மீட்டமை
+ இந்த திருத்தத்திற்கு மீட்டமை
+ உறுதிமொழி செய்தி உருவாக்கு
+ காட்சி பயன்முறையை மாற்று
+ கோப்பு மற்றும் கோப்புறை பட்டியலாக காட்டு
+ பாதை பட்டியலாகக் காட்டு
+ கோப்பு முறைமை மரமாகக் காட்டு
+ கிளை சரிபார்
+ உறுதிமொழி சரிபார்
+ உறுதிமொழி:
+ முன்னறிவிப்பு: ஒரு உறுதிமொழி சரிபார்பதன் மூலம், உங்கள் தலை பிரிக்கப்படும்
+ உள்ளக மாற்றங்கள்:
+ நிராகரி
+ பதுக்கிவை & மீண்டும் இடு
+ கிளை:
+ கனி பறி
+ உறுதிமொழி செய்திக்கு மூலத்தைச் சேர்
+ உறுதிமொழி(கள்):
+ அனைத்து மாற்றங்களையும் உறுதிமொழி
+ முதன்மைகோடு:
+ பொதுவாக நீங்கள் ஒரு ஒன்றிணையை கனி-பறிக்க முடியாது, ஏனெனில் இணைப்பின் எந்தப் பக்கத்தை முதன்மையாகக் கருத வேண்டும் என்பது உங்களுக்குத் தெரியாது. இந்த விருப்பம் குறிப்பிட்ட பெற்றோருடன் தொடர்புடைய மாற்றத்தை மீண்டும் இயக்க கனி-பறி அனுமதிக்கிறது.
+ பதுக்கிவைத்தையும் அழி
+ நீங்கள் அனைத்து பதுக்கிவைத்தையும் அழிக்க முயற்சிக்கிறீர்கள் தொடர விரும்புகிறீர்களா?
+ நகலி தொலை களஞ்சியம்
+ கூடுதல் அளவுருக்கள்:
+ நகலி களஞ்சியத்திற்கான கூடுதல் வாதங்கள். விருப்பத்தேர்வு.
+ உள்ளக பெயர்:
+ களஞ்சியப் பெயர். விருப்பத்தேர்வு.
+ பெற்றோர் கோப்புறை:
+ துவக்கு & துணை தொகுதிகளைப் புதுப்பி
+ களஞ்சிய முகவரி:
+ மூடு
+ திருத்தி
+ உறுதிமொழி சரிபார்
+ கனி-பறி உறுதிமொழி
+ கனி-பறி ...
+ தலையுடன் ஒப்பிடுக
+ பணிமரத்துடன் ஒப்பிடுக
+ தகவலை
+ பாகொவ-வை
+ தனிப்பயன் செயல்
+ இங்கே ${0}$ ஐ ஊடாடும் வகையில் மறுதளம்
+ ${0}$ இதற்கு ஒன்றிணை
+ ஒன்றிணை ...
+ இங்கே ${0}$ ஐ மறுதளம்
+ ${0}$ ஐ இங்கே மீட்டமை
+ உறுதிமொழி திரும்பபெறு
+ வேறுமொழி
+ ஒட்டாக சேமி...
+ பெற்றோர்களில் நொறுக்கு
+ நொறுக்கு குழந்தைகள் இங்கே சேர்
+ மாற்றங்கள்
+ மாற்றங்களைத் தேடு...
+ கோப்புகள்
+ பெகோஅ கோப்பு
+ கோப்புகளைத் தேடு...
+ துணைத்தொகுதி
+ தகவல்
+ ஆசிரியர்
+ மாற்றப்பட்டது
+ குழந்தைகள்
+ உறுதிமொழியாளர்
+ இந்த உறுதிமொழிடைக் கொண்ட குறிப்புகளைச் சரிபார்
+ உறுதிமொழி இதில் உள்ளது
+ முதல் 100 மாற்றங்களை மட்டும் காட்டுகிறது மாற்றங்கள் தாவலில் அனைத்து மாற்றங்களையும் காண்க.
+ செய்தி
+ பெற்றோர்கள்
+ குறிகள்
+ பாகொவ
+ உலாவியில் திற
+ விளக்கம்
+ உறுதிமொழி பொருளை உள்ளிடவும்
+ களஞ்சியம் உள்ளமை
+ உறுதிமொழி வளர்புரு
+ வார்ப்புரு உள்ளடக்கம்:
+ வார்ப்புரு பெயர்:
+ தனிப்பயன் செயல்
+ வாதங்கள்:
+ ${களஞ்சிய} - களஞ்சியத்தின் பாதை; ${கிளை} - தேர்ந்தெடுக்கப்பட்ட கிளை; ${பாகொவ} - தேர்ந்தெடுக்கப்பட்ட உறுதிமொழிடியின் பாகொவ
+ இயக்கக்கூடிய கோப்பு:
+ பெயர்:
+ நோக்கம்:
+ கிளை
+ உறுதிமொழி
+ களஞ்சியம்
+ செயல்பாட்டிலிருந்து வெளியேற காத்திரு
+ மின்னஞ்சல் முகவரி
+ மின்னஞ்சல் முகவரி
+ அறிவிலி
+ தொலைகளை தானாக எடு
+ நிமையங்கள்
+ இயல்புநிலை தொலை
+ சிக்கல் கண்காணி
+ மாதிரி அசூர் வளர்பணிகள் விதியைச் சேர்
+ மாதிரி அறிவிலிஈ சிக்கலுக்கான விதியைச் சேர்
+ மாதிரி அறிவிலிஈ இழு கோரிக்கை விதியைச் சேர்
+ மாதிரி அறிவிலிமையம் விதியைச் சேர்
+ மாதிரி அறிவிலிஆய்வு சிக்கலுக்கான விதியைச் சேர்
+ மாதிரி அறிவிலிஆய்வு இணைப்பு கோரிக்கை விதியைச் சேர்
+ மாதிரி சீரா விதியைச் சேர்
+ புதிய விதி
+ வழக்கவெளி வெளிப்பாடு வெளியீடு:
+ விதியின் பெயர்:
+ முடிவு முகவரி:
+ வழக்கவெளி குழுக்கள் மதிப்புகளை அணுக $1, $2 ஐப் பயன்படுத்து
+ செநு
+ விருப்பமான சேவை:
+ 'விருப்பமான சேவை' அமைக்கப்பட்டிருந்தால், மூலஅறிவிலி இந்த களஞ்சியத்தில் மட்டுமே அதைப் பயன்படுத்தும். இல்லையெனில், ஒன்றுக்கு மேற்பட்ட சேவைகள் இருந்தால், அவற்றில் ஒன்றைத் தேர்ந்தெடுப்பதற்கான சூழல் பட்டயல் காண்பிக்கப்படும்.
+ உஉபநெ பதிலாள்
+ இந்த களஞ்சியத்தால் பயன்படுத்தப்படும் உஉபநெ பதிலாள்
+ பயனர் பெயர்
+ இந்த களஞ்சியத்திற்கான பயனர் பெயர்
+ பணியிடங்கள்
+ நிறம்
+ பெயர்
+ தாவல்களை மீட்டமை
+ வழக்கமான உறுதிமொழி உதவியாளர்
+ உடைக்கும் மாற்றம்:
+ மூடப்பட்ட வெளியீடு சிக்கல்:
+ மாற்ற விவரங்கள்:
+ நோக்கம்:
+ குறுகிய விளக்கம்:
+ மாற்ற வகை:
+ நகல்
+ அனைத்து உரையையும் நகலெடு
+ முழு பாதையை நகலெடு
+ நகல் பாதை
+ கிளையை உருவாக்கு...
+ இதன் அடிப்படையில்:
+ உருவாக்கப்பட்ட கிளையைப் சரிபார்
+ உள்ளக மாற்றங்கள்:
+ நிராகரி
+ பதுக்கிவை & மீண்டும் இடு
+ புதிய கிளை பெயர்:
+ கிளை பெயரை உள்ளிடவும்.
+ இடைவெளிகள் கோடுகளால் மாற்றப்படும்.
+ உள்ளக கிளையை உருவாக்கு
+ குறிச்சொல்லை உருவாக்கு...
+ இங்கு புதிய குறிச்சொல்:
+ சீபிசீ கையொப்பமிடுதல்
+ குறிச்சொல் செய்தி:
+ விருப்பத்தேர்வு.
+ குறிச்சொல் பெயர்:
+ பரிந்துரைக்கப்பட்ட வடிவம்: ப1.0.0-ஆனா
+ உருவாக்கப்பட்ட பிறகு அனைத்து தொலைகளுக்கும் தள்ளு
+ புதிய குறிசொல் உருவாக்கு
+ வகை:
+ annotated
+ குறைந்தஎடை
+ நேரடியாகத் தொடங்க கட்டுப்பாட்டை அழுத்திப் பிடி
+ வெட்டு
+ கிளையை நீக்கு
+ கிளை:
+ நீங்கள் ஒரு தொலை கிளையை நீக்கப் போகிறீர்கள்!!!
+ தொலை ${0}$ கிளையையும் நீக்கு
+ பல கிளைகளை நீக்கு
+ நீங்கள் ஒரே நேரத்தில் பல கிளைகளை நீக்க முயற்சிக்கிறீர்கள் நடவடிக்கை எடுப்பதற்கு முன் மீண்டும் சரிபார்!
+ தொலையை நீக்கு
+ தொலை:
+ பாதை:
+ இலக்கு:
+ எல்லா குழந்தைகளும் பட்டியலிலிருந்து நீக்கப்படுவார்கள்.
+ இது பட்டியலிலிருந்து மட்டுமே அகற்றும், வட்டிலிருந்து அல்ல!
+ குழுவை நீக்குவதை உறுதிப்படுத்து
+ களஞ்சியத்தை நீக்குவதை உறுதிப்படுத்து
+ துணைத்தொகுதியை நீக்கு
+ துணைத்தொகுதி பாதை:
+ குறிச்சொல்லை நீக்கு
+ குறிசொல்:
+ தொலை களஞ்சியங்களிலிருந்து நீக்கு
+ இருமம் வேறுபாடு
+ புதிய
+ பழைய
+ நகல்
+ கோப்பு முறை மாற்றப்பட்டது
+ முதல் வேறுபாடு
+ வெள்ளைவெளி மாற்றத்தை புறக்கணி
+ கடைசி வேறுபாடு
+ பெகோஅ பொருள் மாற்றம்
+ அடுத்த வேறுபாடு
+ மாற்றங்கள் இல்லை அல்லது வரிமுடிவு மாற்றங்கள் மட்டும்
+ முந்தைய வேறுபாடு
+ ஒட்டாகச் சேமி
+ மறைக்கப்பட்ட சின்னங்களைக் காட்டு
+ பக்கவாட்டு வேறுபாடு
+ துணைத் தொகுதி
+ புதிய
+ இடமாற்று
+ தொடரியல் சிறப்பம்சமாக்கல்
+ வரி சொல் மடக்கு
+ தடுப்பு-வழிசெலுத்தலை இயக்கு
+ ஒன்றிணை கருவியில் திற
+ அனைத்து வரிகளையும் காட்டு
+ தெரியும் வரிகளின் எண்ணிக்கையைக் குறை
+ தெரியும் வரிகளின் எண்ணிக்கையை அதிகரி
+ மாற்றங்களைக் காண கோப்பைத் தேர்ந்தெடு
+ ஒன்றிணை கருவியில் திற
+ மாற்றங்களை நிராகரி
+ செயல்படும் நகலில் உள்ள அனைத்து உள்ளக மாற்றங்கள்.
+ மாற்றங்கள்:
+ புறக்கணிக்கப்பட்ட கோப்புகளைச் சேர்
+ {0} மாற்றங்கள் நிராகரிக்கப்படும்
+ இந்தச் செயலை நீங்கள் செயல்தவிர்க்க முடியாது!!!
+ புத்தகக்குறி:
+ புதிய பெயர்:
+ இலக்கு:
+ தேர்ந்தெடுக்கப்பட்ட குழுவைத் திருத்து
+ தேர்ந்தெடுக்கப்பட்ட களஞ்சியத்தைத் திருத்து
+ தனிப்பயன் செயலை இயக்கு
+ செயல் பெயர்:
+ பெறு
+ எல்லா தொலைகளையும் பெறு
+ உள்ளக குறிப்புகளை கட்டாயமாக மீறு
+ குறிச்சொற்கள் இல்லாமல் பெறு
+ தொலை:
+ தொலை மாற்றங்களைப் பெறு
+ மாறாமல் என கருது
+ நிராகரி...
+ {0} கோப்புகளை நிராகரி...
+ தேர்ந்தெடுக்கப்பட்ட வரிகளில் மாற்றங்களை நிராகரி
+ வெளிப்புற இணைப்பு கருவியைத் திற
+ ${0}$ஐப் பயன்படுத்தி தீர்
+ ஒட்டு என சேமி...
+ நிலைபடுத்து
+ {0} fகோப்புகள் நிலைபடுத்து
+ தேர்ந்தெடுக்கப்பட்ட வரிகளில் மாற்றங்களை நிலைபடுத்து
+ பதுக்கிவை...
+ {0} கோப்புகள் பதுக்கிவை...
+ நிலைநீக்கு
+ நிலைநீக்கு {0} கோப்புகள்
+ தேர்ந்தெடுக்கப்பட்ட வரிகளில் மாற்றங்களை நிலைநீக்கு
+ என்னுடையதைப் பயன்படுத்து (சரிபார் --நமது)
+ அவர்களுடையதைப் பயன்படுத்து (சரிபார் --அவர்களது)
+ கோப்பு வரலாறு
+ மாற்றம்
+ உள்ளடக்கம்
+ அறிவிலி-ஓட்டம்
+ மேம்பாட்டு கிளை:
+ நற்பொருத்தம்:
+ நற்பொருத்தம் முன்னொட்டு:
+ ஓட்டம் - நற்பொருத்தம் முடி
+ ஓட்டம் - சூடானதிருத்தம் முடி
+ ஓட்டம் - வெளியீட்டை முடி
+ இலக்கு:
+ சூடானதிருத்தம்:
+ சூடானதிருத்தம் முன்னொட்டு:
+ அறிவிலி-ஓட்டம் துவக்கு
+ கிளையை வைத்திரு
+ உற்பத்தி கிளை:
+ வெளியீடு:
+ வெளியீடு முன்னொட்டு:
+ நற்பொருத்தம் தொடங்கு...
+ ஓட்டம் - நற்பொருத்தம் தொடங்கு
+ சூடானதிருத்தம் தொடங்கு...
+ ஓட்டம் - சூடானதிருத்தம் தொடங்கு
+ பெயரை உள்ளிடு
+ வெளியீட்டைத் தொடங்கு...
+ ஓட்டம் - வெளியீட்டைத் தொடங்கு
+ பதிப்பு குறிச்சொல் முன்னொட்டு:
+ அறிவிலி பெகோஅ
+ அறிவிலி கண்காணி வடிவத்தைச் சேர்...
+ வடிவம் என்பது கோப்பு பெயர்
+ தனிப்பயன் வடிவம்:
+ அறிவிலி பெகோஅ இல் கண்காணி வடிவங்களைச் சேர்
+ பெறு
+ அறிவிலி பெகோஅ பொருள்களைப் பதிவிறக்க `அறிவிலி பெகோஅ பெறு` ஐ இயக்கவும் இது செயல்படும் நகலை புதுப்பிக்காது.
+ அறிவிலி பெகோஅ பொருள்களைப் பெறு
+ அறிவிலி பெகோஅ கொக்கிகளை நிறுவு
+ பூட்டுகளைக் காட்டு
+ பூட்டப்பட்ட கோப்புகள் இல்லை
+ பூட்டு
+ எனது பூட்டுகளை மட்டும் காட்டு
+ பெகோஅ பூட்டுகள்
+ திற
+ கட்டாயம் திற
+ கத்தரி
+ உள்ளக சேமிப்பகத்திலிருந்து பழைய பெகோஅ கோப்புகளை நீக்க `அறிவிலி பெகோஅ கத்தரி` ஐ இயக்கு
+ இழு
+ தற்போதைய குறிக்கு அனைத்து அறிவிலி பெகோஅ கோப்புகளையும் பதிவிறக்கி சரிபார்க்க `அறிவிலி பெகோஅ இழு`ஐ இயக்கு
+ பெகோஅ பொருள்களை இழு
+ தள்ளு
+ வரிசைப்படுத்தப்பட்ட பெரிய கோப்புகளை அறிவிலி பெகோஅ முடிவுபுள்ளிக்கு தள்ளு
+ பெகோஅ பொருள்கள் தள்ளு
+ தொலை:
+ '{0}' என பெயரிடப்பட்ட கோப்புகளைக் கண்காணி
+ அனைத்து *{0} கோப்புகளையும் கண்காணி
+ வரலாறு
+ ஆசிரியர்
+ ஆசிரியர் நேரம்
+ வரைபடம் & பொருள்
+ பாகொவ
+ உறுதிமொழி நேரம்
+ தேர்ந்தெடுக்கப்பட்ட {0} உறுதிமொழிகள்
+ பல உறுதிமொழிகளைத் தேர்ந்தெடுக்க 'கட்டுப்பாடு' அல்லது 'உயர்த்து'ஐ அழுத்திப் பிடி.
+ பல உறுதிமொழிகளைத் தேர்ந்தெடுக்க ⌘ அல்லது ⇧ ஐ அழுத்திப் பிடி.
+ குறிப்புகள்:
+ விசைப்பலகை குறுக்குவழிகள் குறிப்பு
+ உலகளாவிய
+ தற்போதைய மேல்தோன்றலை Cancel
+ புதிய களஞ்சியத்தை நகலி செய்
+ தற்போதைய பக்கத்தை மூடு
+ அடுத்த பக்கத்திற்குச் செல்
+ முந்தைய பக்கத்திற்குச் செல்
+ புதிய பக்கத்தை உருவாக்கு
+ விருப்பத்தேர்வுகள் உரையாடலைத் திற
+ களஞ்சியம்
+ நிலைபடுத்திய மாற்றங்களை உறுதிமொழி
+ நிலைபடுத்திய மாற்றங்களை உறுதிமொழி மற்றும் தள்ளு
+ அனைத்து மாற்றங்களையும் நிலைபடுத்தி உறுதிமொழி
+ தேர்ந்தெடுக்கப்பட்ட உறுதிமொழியின் அடிப்படையில் ஒரு புதிய கிளையை உருவாக்குகிறது
+ தேர்ந்தெடுக்கப்பட்ட மாற்றங்களை நிராகரி
+ எடு, நேரடியாகத் தொடங்குகிறது
+ முகப்பலகை பயன்முறை (இயல்புநிலை)
+ உறுதிமொழி தேடல் பயன்முறை
+ இழு, நேரடியாகத் தொடங்குகிறது
+ தள்ளு, நேரடியாகத் தொடங்குகிறது
+ இந்த களஞ்சியத்தை மீண்டும் ஏற்ற கட்டாயப்படுத்து
+ தேர்ந்தெடுக்கப்பட்ட மாற்றங்களை நிலைபடுத்து/நிலைநீக்கு
+ 'மாற்றங்கள்' என்பதற்கு மாறு
+ 'வரலாறுகள்' என்பதற்கு மாறு
+ 'பதுகிவைத்தவை' என்பதற்கு மாறு
+ உரை திருத்தி
+ தேடல் பலகத்தை மூடு
+ அடுத்த பொருத்தத்தைக் கண்டறி
+ முந்தைய பொருத்தத்தைக் கண்டறி
+ தேடல் பலகத்தைத் திற
+ நிராகரி
+ நிலைபடுத்து
+ நிலைநீக்கு
+ களஞ்சியத்தைத் துவக்கு
+ பாதை:
+ கனி-பறி செயல்பாட்டில் உள்ளது.
+ உறுதிமொழி செயலாக்குதல்
+ இணைத்தல் செயல்பாட்டில் உள்ளது.
+ இணைத்தல்
+ மறுதளம் செயல்பாட்டில் உள்ளது
+ இல் நிறுத்தப்பட்டது
+ திரும்ப்பெறும் செயல்பாட்டில் உள்ளது.
+ திரும்பபெறும் உறுதிமொழி
+ ஊடாடும் மறுதளம்
+ மேல்:
+ இலக்கு கிளை:
+ இணைப்பை நகலெடு
+ உலாவியில் திற
+ பிழை
+ அறிவிப்பு
+ கிளையை ஒன்றிணை
+ Into:
+ இணைப்பு விருப்பம்:
+ இதனுள்:
+ ஒன்றிணை (பல)
+ அனைத்து மாற்றங்களையும் உறுதிமொழி
+ சூழ்ச்சிமுறை:
+ இலக்குகள்:
+ களஞ்சிய முனையை நகர்த்து
+ இதற்கான பெற்றோர் முனையைத் தேர்ந்தெடு
+ பெயர்:
+ அறிவிலி உள்ளமைக்கப்படவில்லை. [விருப்பத்தேர்வுகள்]க்குச் சென்று முதலில் அதை உள்ளமை.
+ தரவு சேமிப்பக கோப்பகத்தைத் திற
+ இதனுடன் திற...
+ விருப்பத்தேர்வு.
+ புதிய பக்கத்தை உருவாக்கு
+ புத்தகக்குறி
+ மூடு தாவல்
+ பிற தாவல்களை மூடு
+ வலதுபுறத்தில் உள்ள தாவல்களை மூடு
+ களஞ்சிய பாதை நகலெடு
+ களஞ்சியங்கள்
+ ஒட்டு
+ {0} நாட்களுக்கு முன்பு
+ 1 மணி நேரத்திற்கு முன்பு
+ {0} மணி நேரத்திற்கு முன்பு
+ சற்றுமுன்
+ கடந்த திங்கள்
+ கடந்த ஆண்டு
+ {0} நிமையங்களுக்கு முன்பு
+ {0} திங்களுக்கு முன்பு
+ {0} ஆண்டுகளுக்கு முன்பு
+ நேற்று
+ புதிய வரியை உள்ளிட 'உயர்த்து+நுழை' ஐப் பயன்படுத்தவும். 'நுழை' என்பது சரி பொத்தானின் சூடானவிசை ஆகும்
+ விருப்பத்தேர்வுகள்
+ செநு
+ வேறுபாடு உடனடியாக பகுப்பாய்வு செய்
+ பநிஇ திறவுகோல்
+ பொருள் உடனடியாக உருவாக்கு
+ மாதிரி
+ பெயர்
+ சேவையகம்
+ ஓடையை இயக்கு
+ தோற்றம்
+ இயல்புநிலை எழுத்துரு
+ திருத்தி தாவல் அகலம்
+ எழுத்துரு அளவு
+ இயல்புநிலை
+ திருத்தி
+ ஒற்றைவெளி எழுத்துரு
+ ஒற்றைவெளி எழுத்துருவை உரை திருத்தியில் மட்டும் பயன்படுத்து
+ கருப்பொருள்
+ கருப்பொருள் மேலெழுதப்படுகிறது
+ தலைப்புப்பட்டியில் நிலையான தாவல் அகலத்தைப் பயன்படுத்து
+ சொந்த சாளர சட்டத்தைப் பயன்படுத்து
+ வேறு/ஒன்றிணை கருவி
+ நிறுவல் பாதை
+ வேறு/ஒன்றிணை கருவிக்கான பாதை உள்ளிடு
+ கருவி
+ பொது
+ தொடக்கத்தில் புதுப்பிப்புகளைச் சரிபார்
+ தேதி வடிவம்
+ மொழி
+ வரலாற்று உறுதிமொழிகள்
+ வரைபடத்தில் உறுதிமொழி நேரத்திற்குப் பதிலாக ஆசிரியர் நேரத்தைக் காட்டு
+ உறுதிமொழி விவரங்களில் குழந்தைகளைக் காட்டு
+ உறுதிமொழி வரைபடத்தில் குறிச்சொற்களைக் காட்டு
+ பொருள் வழிகாட்டி நீளம்
+ அறிவிலி
+ தானியங்கி வரிமுடிவை இயக்கு
+ இயல்புநிலை நகலி அடைவு
+ பயனர் மின்னஞ்சல்
+ உலகளாவிய அறிவிலி பயனர் மின்னஞ்சல்
+ --prune எடுக்கும்போது இயக்கு
+ அறிவிலி (>= 2.25.1) இந்த பயன்பாட்டிற்கு தேவைப்படுகிறது
+ நிறுவல் பாதை
+ உஉபநெ பாகுஅ சரிபார்ப்பை இயக்கு
+ பயனர் பெயர்
+ உலகளாவிய அறிவிலி பயனர் பெயர்
+ அறிவிலி பதிப்பு
+ சிபிசி கையொப்பமிடுதல்
+ சிபிசி கையொப்பமிடுதல் உறுதிமொழி
+ சிபிசி வடிவம்
+ நிரல் நிறுவல் பாதை
+ நிறுவப்பட்ட சிபிசி நிரலுக்கான உள்ளீட்டு பாதை
+ சிபிசி கையொப்பமிடுதலை குறிச்சொலிடு
+ பயனர் கையொப்பமிடும் திறவுகோல்
+ பயனரின் கையொப்பமிடும் திறவுகோல்
+ ஒருங்கிணைப்பு
+ ஓடு/முனையம்
+ பாதை
+ ஓடு/முனையம்
+ தொலை கத்தரி
+ இலக்கு:
+ பணிமரங்கள் கத்தரி
+ `$GIT_COMMON_DIR/பணிமரங்கள்` இதில் பணிமரம் தகவலை கத்தரி
+ இழு
+ தொலை கிளை:
+ இதனுள்:
+ உள்ளக மாற்றங்கள்:
+ நிராகரி
+ பதுக்கிவை & மீண்டும் இடு
+ தொலை:
+ இழு (எடுத்து ஒன்றிணை)
+ ஒன்றிணை என்பதற்குப் பதிலாக மறுதளத்தைப் பயன்படுத்து
+ தள்ளு
+ துணைத் தொகுதிகள் தள்ளப்பட்டது என்பதை உறுதிசெய்
+ கட்டாயமாக தள்ளு
+ உள்ளக கிளை:
+ தொலை:
+ மாற்றங்களை தொலைக்கு தள்ளு
+ தொலை கிளை:
+ கண்காணிப்பு கிளையாக அமை
+ அனைத்து குறிச்சொற்களையும் தள்ளு
+ தொலைக்கு குறிச்சொல்லை தள்ளு
+ அனைத்து தொலைகளுக்கும் தள்ளு
+ தொலை:
+ குறிச்சொல்:
+ வெளியேறு
+ தற்போதைய கிளையை மறுதளம் செய்
+ உள்ளக மாற்றங்களை பதுக்கிவை & மீண்டும் இடு
+ மேல்:
+ மறுதளம்:
+ தொலையைச் சேர்
+ தொலையைத் திருத்து
+ பெயர்:
+ களஞ்சிய பெயர்
+ களஞ்சிய முகவரி:
+ தொலை அறிவிலி களஞ்சிய முகவரி:
+ முகவரியை நகலெடு
+ நீக்கு...
+ திருத்து...
+ பெறு
+ உலாவியில் திற
+ கத்தரித்தல்
+ பணிமரத்தை அகற்றுவதை உறுதிப்படுத்து
+ `--கட்டாயம்` விருப்பத்தை இயக்கு
+ இலக்கு:
+ கிளையை மறுபெயரிடு
+ புதிய பெயர்:
+ இந்தக் கிளைக்கான தனித்துவமான பெயர்
+ கிளை:
+ நிறுத்து
+ தொலைகளிலிருந்து மாற்றங்களைத் தானாகப் பெறுதல்...
+ சுத்தப்படுத்தல்(சீசி & கத்தரித்தல்)
+ இந்த களஞ்சியத்திற்கு `அறிவிலி சீசி` கட்டளையை இயக்கு.
+ அனைத்தையும் அழி
+ இந்த களஞ்சியத்தை உள்ளமை
+ தொடர்க
+ தனிப்பயன் செயல்கள்
+ தனிப்பயன் செயல்கள் இல்லை
+ எல்லா மாற்றங்களையும் நிராகரி
+ '--குறிபதிவு' விருப்பத்தை இயக்கு
+ கோப்பு உலாவியில் திற
+ கிளைகள்/குறிச்சொற்கள்/துணைத் தொகுதிகளைத் தேடு
+ வரைபடத்தில் தெரிவுநிலை
+ அமைவை நீக்கு
+ உறுதிமொழி வரைபடத்தில் மறை
+ உறுதிமொழி வரைபடத்தில் வடிகட்டு
+ '--first-parent' விருப்பம் இயக்கு
+ தளவமைப்பு
+ கிடைமட்டம்
+ செங்குத்து
+ உறுதிமொழி வரிசை
+ உறுதிமொழி தேதி
+ இடவியல் மூலமாக
+ உள்ளக கிளைகள்
+ தலைக்கு செல்
+ கிளையை உருவாக்கு
+ அறிவிப்புகளை அழி
+ வரைபடத்தில் தற்போதைய கிளையை மட்டும் முன்னிலை படுத்து
+ {0} இல் திற
+ வெளிப்புற கருவிகளில் திற
+ புதுப்பி
+ தொலைகள்
+ தொலையைச் சேர்
+ உறுதிமொழி தேடு
+ ஆசிரியர்
+ உறுதிமொழியாளர்
+ கோப்பு
+ செய்தி
+ பாகொவ
+ தற்போதைய கிளை
+ குறிச்சொற்களை மரமாகக் காட்டு
+ தவிர்
+ புள்ளிவிவரங்கள்
+ துணைத் தொகுதிகள்
+ துணைத் தொகுதியைச் சேர்
+ துணைத் தொகுதியைப் புதுப்பி
+ குறிசொற்கள்
+ புதிய குறிசொல்
+ படைப்பாளர் தேதியின்படி
+ பெயர் மூலம்
+ வரிசைப்படுத்து
+ முனையத்தில் திற
+ வரலாறுகளில் உறவு நேரத்தைப் பயன்படுத்து
+ பணிமரங்கள்
+ பணிமரத்தைச் சேர்
+ கத்தரித்தல்
+ அறிவிலி களஞ்சிய முகவரி
+ தற்போதைய கிளையை திருத்தத்திற்கு மீட்டமை
+ மீட்டமை பயன்முறை:
+ இதற்கு நகர்த்து:
+ தற்போதைய கிளை:
+ கோப்பு உலாவியில் வெளிப்படுத்து
+ பின்வாங்கு உறுதிமொழி
+ உறுதிமொழி:
+ பின்வாங்கு மாற்றங்களை உறுதிமொழி
+ மாறுசொல் உறுதிமொழி செய்தி
+ இயங்குகிறது. காத்திருக்கவும்...
+ சேமி
+ எனச் சேமி...
+ ஒட்டு வெற்றிகரமாக சேமிக்கப்பட்டது!
+ களஞ்சியங்களை வருடு
+ வேர் அடைவு:
+ புதுப்பிப்புகளைச் சரிபார்...
+ இந்த மென்பொருளின் புதிய பதிப்பு கிடைக்கிறது:
+ புதுப்பிப்புகளைச் சரிபார்க்க முடியவில்லை!
+ பதிவிறக்கம்
+ இந்தப் பதிப்பைத் தவிர்
+ மென்பொருள் புதுப்பி
+ தற்போது புதுப்பிப்புகள் எதுவும் கிடைக்கவில்லை.
+ கண்காணிப்பு கிளையை அமை
+ கிளை:
+ மேல்ஓடையை நீக்கு
+ மேல்ஓடை:
+ SHA ஐ நகலெடு
+ இதற்கு செல்
+ நொறுக்கு உறுதிமொழிகள்
+ இதில்:
+ பாஓடு தனியார் திறவுகோல்:
+ தனியார் பாஓடு திறவுகோல் கடை பாதை
+ தொடங்கு
+ பதுக்கிவை
+ கண்காணிக்கப்படாத கோப்புகளைச் சேர்
+ செய்தி:
+ விருப்பத்தேர்வு. இந்த பதுக்கலின் பெயர்
+ நிலைப்படுத்தப்பட்ட மாற்றங்கள் மட்டும்
+ தேர்ந்தெடுக்கப்பட்ட கோப்புகளின் நிலைப்படுத்தப்பட்ட மற்றும் நிலைப்படுத்தப்படாத மாற்றங்கள் இரண்டும் பதுக்கிவைக்கப்படும்!!!
+ உள்ளக மாற்றங்களை பதுக்கிவை
+ இடு
+ கைவிடு
+ ஒட்டாகச் சேமி...
+ பதுக்கிவைத்தவை கைவிடு
+ கைவிடு:
+ பதுக்கிவைத்தவைகள்
+ மாற்றங்கள்
+ பதுக்கிவைத்தவைகள்
+ புள்ளிவிவரங்கள்
+ உறுதிமொழிகள்
+ உறுதிமொழியாளர்
+ மேலோட்டப் பார்வை
+ திங்கள்
+ வாரம்
+ ஆசிரியர்கள்:
+ உறுதிமொழிகள்:
+ துணைத் தொகுதி
+ துணைத் தொகுதியைச் சேர்
+ உறவு பாதையை நகலெடு
+ உள்ளமைக்கப்பட்ட துணைத் தொகுதிகளை எடு
+ துணைத் தொகுதி களஞ்சியத்தைத் திற
+ உறவு பாதை:
+ இந்த தொகுதியை சேமிப்பதற்கான தொடர்புடைய கோப்புறை.
+ துணை தொகுதியை நீக்கு
+ சரி
+ குறிச்சொல் பெயரை நகலெடு
+ குறிச்சொல் செய்தியை நகலெடு
+ நீக்கு ${0}$...
+ ${0}$ இதை ${1}$ இல் இணை...
+ தள்ளு ${0}$...
+ துணைத்தொகுதிகளைப் புதுப்பி
+ அனைத்து துணைத்தொகுதிகள்
+ தேவைக்கேற்றப துவக்கு
+ சுழற்சி முறையில்
+ --தொலை விருப்பத்தைப் பயன்படுத்து
+ முகவரி:
+ முன்னறிவிப்பு
+ வரவேற்பு பக்கம்
+ குழுவை உருவாக்கு
+ துணைக் குழுவை உருவாக்கு
+ நகலி களஞ்சியம்
+ நீக்கு
+ கோப்புறையை இழுத்து & விடு ஆதரிக்கப்படுகிறது. தனிப்பயன் குழுவாக்க ஆதரவு.
+ திருத்து
+ வேறொரு குழுவிற்கு நகர்த்து
+ அனைத்து களஞ்சியங்களையும் திற
+ களஞ்சியத்தைத் திற
+ முனையத்தைத் திற
+ இயல்புநிலை நகலி அடைவில் களஞ்சியங்களை மீண்டும் வருடு
+ களஞ்சியங்களைத் தேடு...
+ வரிசைப்படுத்து
+ உள்ளக மாற்றங்கள்
+ அறிவிலி புறக்கணி
+ எல்லா *{0} கோப்புகளையும் புறக்கணி
+ ஒரே கோப்புறையில் *{0} கோப்புகளைப் புறக்கணி
+ இந்த கோப்பை மட்டும் புறக்கணி
+ பின்னொட்டு
+ இந்த கோப்பை இப்போது நீங்கள் நிலைப்படுத்தலாம்.
+ உறுதிமொழி
+ உறுதிமொழி & தள்ளு
+ வளர்புரு/வரலாறுகள்
+ சொடுக்கு நிகழ்வைத் தூண்டு
+ உறுதிமொழி (திருத்து)
+ அனைத்து மாற்றங்களையும் நிலைப்படுத்தி உறுதிமொழி
+ நீங்கள் {0} கோப்புகளை நிலைப்படுத்தியுள்ளீர்கள், ஆனால் {1} கோப்புகள் மட்டுமே காட்டப்பட்டுள்ளன ({2} கோப்புகள் வடிகட்டப்பட்டுள்ளன). தொடர விரும்புகிறீர்களா?
+ மோதல்கள் கண்டறியப்பட்டது
+ கோப்பு மோதல்கள் தீர்க்கப்பட்டது
+ கண்காணிக்கப்படாத கோப்புகளைச் சேர்
+ அண்மைக் கால உள்ளீட்டு செய்திகள் இல்லை
+ உறுதிமொழி வளர்புருகள் இல்லை
+ தேர்ந்தெடுக்கப்பட்ட கோப்பு(களை) வலது சொடுக்கு செய்து, முரண்பாடுகளைத் தீர்க்க உங்கள் விருப்பத்தைத் தேர்ந்தெடு.
+ கையெழுத்திடு
+ நிலைபடுத்தியது
+ நிலைநீக்கு
+ அனைத்தும் நிலைநீக்கு
+ நிலைநீக்கு
+ நிலைபடுத்து
+ அனைத்தும் நிலைபடுத்து
+ மாறாதது எனநினைப்பதை பார்
+ வளர்புரு: ${0}$
+ பணியிடம்:
+ பணியிடங்களை உள்ளமை...
+ பணிமரம்
+ பாதையை நகலெடு
+ பூட்டு
+ நீக்கு
+ திற
+
diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml
new file mode 100644
index 00000000..5d120117
--- /dev/null
+++ b/src/Resources/Locales/uk_UA.axaml
@@ -0,0 +1,750 @@
+
+
+
+
+
+ Про програму
+ Про SourceGit
+ Безкоштовний Git GUI клієнт з відкритим кодом
+ Додати робоче дерево
+ Розташування:
+ Шлях для цього робочого дерева. Відносний шлях підтримується.
+ Назва гілки:
+ Необов'язково. За замовчуванням — назва кінцевої папки.
+ Відстежувати гілку:
+ Відстежувати віддалену гілку
+ Що перемкнути:
+ Створити нову гілку
+ Наявна гілка
+ AI Асистент
+ ПЕРЕГЕНЕРУВАТИ
+ Використати AI для генерації повідомлення коміту
+ ЗАСТОСУВАТИ ЯК ПОВІДОМЛЕННЯ КОМІТУ
+ Застосувати
+ Файл патчу:
+ Виберіть файл .patch для застосування
+ Ігнорувати зміни пробілів
+ Застосувати Патч
+ Пробіли:
+ Застосувати схованку
+ Видалити після застосування
+ Відновити зміни індексу
+ Схованка:
+ Архівувати...
+ Зберегти архів у:
+ Виберіть шлях до файлу архіву
+ Ревізія:
+ Архівувати
+ SourceGit Askpass
+ ФАЙЛИ, ЩО ВВАЖАЮТЬСЯ НЕЗМІНЕНИМИ
+ НЕМАЄ ФАЙЛІВ, ЩО ВВАЖАЮТЬСЯ НЕЗМІНЕНИМИ
+ ВИДАЛИТИ
+ Оновити
+ БІНАРНИЙ ФАЙЛ НЕ ПІДТРИМУЄТЬСЯ!!!
+ Автор рядка
+ ПОШУК АВТОРА РЯДКА ДЛЯ ЦЬОГО ФАЙЛУ НЕ ПІДТРИМУЄТЬСЯ!!!
+ Перейти на ${0}$...
+ Порівняти з ${0}$
+ Порівняти з робочим деревом
+ Копіювати назву гілки
+ Спеціальна дія
+ Видалити ${0}$...
+ Видалити вибрані {0} гілок
+ Перемотати до ${0}$
+ Отримати ${0}$ в ${1}$...
+ Git Flow - Завершити ${0}$
+ Злиття ${0}$ в ${1}$...
+ Злити вибрані {0} гілок в поточну
+ Витягти ${0}$
+ Витягти ${0}$ в ${1}$...
+ Надіслати ${0}$
+ Перебазувати ${0}$ на ${1}$...
+ Перейменувати ${0}$...
+ Встановити відстежувану гілку...
+ Порівняти гілки
+ Недійсний upstream!
+ Байтів
+ СКАСУВАТИ
+ Скинути до батьківської ревізії
+ Скинути до цієї ревізії
+ Згенерувати повідомлення коміту
+ ЗМІНИТИ РЕЖИМ ВІДОБРАЖЕННЯ
+ Показати як список файлів та тек
+ Показати як список шляхів
+ Показати як дерево файлової системи
+ Перейти на гілку
+ Перейти на коміт
+ Коміт:
+ Попередження: Перехід на коміт призведе до стану "від'єднаний HEAD"
+ Локальні зміни:
+ Скасувати
+ Сховати та Застосувати
+ Гілка:
+ Cherry-pick
+ Додати джерело до повідомлення коміту
+ Коміт(и):
+ Закомітити всі зміни
+ Батьківський коміт:
+ Зазвичай неможливо cherry-pick злиття, бо невідомо, яку сторону злиття вважати батьківською (mainline). Ця опція дозволяє відтворити зміни відносно вказаного батьківського коміту.
+ Очистити схованки
+ Ви намагаєтеся очистити всі схованки. Ви впевнені?
+ Клонувати віддалене сховище
+ Додаткові параметри:
+ Додаткові аргументи для клонування сховища. Необов'язково.
+ Локальна назва:
+ Назва сховища. Необов'язково.
+ Батьківська тека:
+ Ініціалізувати та оновити підмодулі
+ URL сховища:
+ ЗАКРИТИ
+ Редактор
+ Перейти на коміт
+ Cherry-pick коміт
+ Cherry-pick ...
+ Порівняти з HEAD
+ Порівняти з робочим деревом
+ Iнформацію
+ SHA
+ Спеціальна дія
+ Інтерактивно перебазувати ${0}$ сюди
+ Злиття в ${0}$
+ Злити ...
+ Перебазувати ${0}$ сюди
+ Скинути ${0}$ сюди
+ Скасувати коміт
+ Змінити повідомлення
+ Зберегти як патч...
+ Склеїти з батьківським комітом
+ Склеїти дочірні коміти сюди
+ ЗМІНИ
+ Пошук змін...
+ ФАЙЛИ
+ LFS Файл
+ Пошук файлів...
+ Підмодуль
+ ІНФОРМАЦІЯ
+ АВТОР
+ ЗМІНЕНО
+ ДОЧІРНІ
+ КОМІТЕР
+ Перевірити посилання, що містять цей коміт
+ КОМІТ МІСТИТЬСЯ В
+ Показано лише перші 100 змін. Дивіться всі зміни на вкладці ЗМІНИ.
+ ПОВІДОМЛЕННЯ
+ БАТЬКІВСЬКІ
+ ПОСИЛАННЯ (Refs)
+ SHA
+ Відкрити в браузері
+ Опис
+ Введіть тему коміту
+ Налаштування сховища
+ ШАБЛОН КОМІТУ
+ Зміст шаблону:
+ Назва шаблону:
+ СПЕЦІАЛЬНА ДІЯ
+ Аргументи:
+ ${REPO} - Шлях до сховища; ${BRANCH} - Вибрана гілка; ${SHA} - SHA вибраного коміту
+ Виконуваний файл:
+ Назва:
+ Область застосування:
+ Гілка
+ Коміт
+ Репозиторій
+ Чекати завершення дії
+ Адреса Email
+ Адреса електронної пошти
+ GIT
+ Автоматично отримувати зміни з віддалених сховищ
+ хвилин(и)
+ Віддалене сховище за замовчуванням
+ Бажаний режим злиття
+ ТРЕКЕР ЗАВДАНЬ
+ Додати приклад правила для Azure DevOps
+ Додати приклад правила для Gitee Issue
+ Додати приклад правила для Gitee Pull Request
+ Додати приклад правила для Github
+ Додати приклад правила для GitLab Issue
+ Додати приклад правила для GitLab Merge Request
+ Додати приклад правила для Jira
+ Нове правило
+ Регулярний вираз для завдання:
+ Назва правила:
+ URL результату:
+ Використовуйте $1, $2 для доступу до значень груп регулярного виразу.
+ AI
+ Бажаний сервіс:
+ Якщо 'Бажаний сервіс' встановлено, SourceGit буде використовувати лише його у цьому сховищі. Інакше, якщо доступно більше одного сервісу, буде показано контекстне меню для вибору.
+ HTTP Проксі
+ HTTP проксі, що використовується цим сховищем
+ Ім'я користувача
+ Ім'я користувача для цього сховища
+ Робочі простори
+ Колір
+ Відновлювати вкладки при запуску
+ ПРОДОВЖИТИ
+ Виявлено порожній коміт! Продовжити (--allow-empty)?
+ ІНДЕКСУВАТИ ВСЕ ТА ЗАКОМІТИТИ
+ Виявлено порожній коміт! Продовжити (--allow-empty) чи індексувати все та закомітити?
+ Допомога Conventional Commit
+ Зворотньо несумісні зміни:
+ Закрите завдання:
+ Детальні зміни:
+ Область застосування:
+ Короткий опис:
+ Тип зміни:
+ Копіювати
+ Копіювати весь текст
+ Копіювати повний шлях
+ Копіювати шлях
+ Створити гілку...
+ На основі:
+ Перейти на створену гілку
+ Локальні зміни:
+ Скасувати
+ Сховати та Застосувати
+ Назва нової гілки:
+ Введіть назву гілки.
+ Пробіли будуть замінені на тире.
+ Створити локальну гілку
+ Створити тег...
+ Новий тег для:
+ Підпис GPG
+ Повідомлення тегу:
+ Необов'язково.
+ Назва тегу:
+ Рекомендований формат: v1.0.0-alpha
+ Надіслати на всі віддалені сховища після створення
+ Створити Новий Тег
+ Тип:
+ анотований
+ легкий
+ Утримуйте Ctrl для запуску без діалогу
+ Вирізати
+ Видалити гілку
+ Гілка:
+ Ви збираєтеся видалити віддалену гілку!!!
+ Також видалити віддалену гілку ${0}$
+ Видалити кілька гілок
+ Ви намагаєтеся видалити кілька гілок одночасно. Перевірте ще раз перед виконанням!
+ Видалити віддалене сховище
+ Віддалене сховище:
+ Шлях:
+ Ціль:
+ Усі дочірні елементи будуть видалені зі списку.
+ Це видалить сховище лише зі списку, а не з диска!
+ Підтвердити видалення групи
+ Підтвердити видалення сховища
+ Видалити підмодуль
+ Шлях до підмодуля:
+ Видалити тег
+ Тег:
+ Видалити з віддалених сховищ
+ РІЗНИЦЯ ДЛЯ БІНАРНИХ ФАЙЛІВ
+ НОВИЙ
+ СТАРИЙ
+ Копіювати
+ Змінено режим файлу
+ Перша відмінність
+ Ігнорувати зміни пробілів
+ Остання відмінність
+ ЗМІНА ОБ'ЄКТА LFS
+ Наступна відмінність
+ НЕМАЄ ЗМІН АБО ЛИШЕ ЗМІНИ КІНЦЯ РЯДКА
+ Попередня відмінність
+ Зберегти як патч
+ Показати приховані символи
+ Порівняння пліч-о-пліч
+ ПІДМОДУЛЬ
+ НОВИЙ
+ Поміняти місцями
+ Підсвітка синтаксису
+ Перенос слів
+ Увімкнути навігацію блоками
+ Відкрити в інструменті злиття
+ Показати всі рядки
+ Зменшити кількість видимих рядків
+ Збільшити кількість видимих рядків
+ ОБЕРІТЬ ФАЙЛ ДЛЯ ПЕРЕГЛЯДУ ЗМІН
+ Відкрити в інструменті злиття
+ Скасувати зміни
+ Усі локальні зміни в робочій копії.
+ Зміни:
+ Включити файли, які ігноруються
+ {0} змін будуть відхилені
+ Ви не можете скасувати цю дію!!!
+ Закладка:
+ Нова назва:
+ Ціль:
+ Редагувати вибрану групу
+ Редагувати вибраний репозиторій
+ Виконати спеціальну дію
+ Ім'я дії:
+ Витягти
+ Витягти всі віддалені сховища
+ Примусово перезаписати локальні refs
+ Витягти без тегів
+ Віддалений:
+ Витягти зміни з віддалених репозиторіїв
+ Вважати незмінними
+ Скасувати...
+ Скасувати {0} файлів...
+ Скасувати зміни в вибраних рядках
+ Відкрити зовнішній інструмент злиття
+ Розв'язати за допомогою ${0}$
+ Зберегти як патч...
+ Стагнути
+ Стагнути {0} файлів
+ Стагнути зміни в вибраних рядках
+ Схованка...
+ Схованка {0} файлів...
+ Скинути стаг
+ Скинути {0} файлів
+ Скинути зміни в вибраних рядках
+ Використовувати Mine (checkout --ours)
+ Використовувати Theirs (checkout --theirs)
+ Історія файлу
+ ЗМІНА
+ ЗМІСТ
+ Git-Flow
+ Розробка гілки:
+ Функція:
+ Префікс функції:
+ FLOW - Завершити функцію
+ FLOW - Завершити гарячу поправку
+ FLOW - Завершити реліз
+ Ціль:
+ Гаряча поправка:
+ Префікс гарячої поправки:
+ Ініціалізувати Git-Flow
+ Залишити гілку
+ Гілка виробництва:
+ Реліз:
+ Префікс релізу:
+ Почати функцію...
+ FLOW - Почати функцію
+ Почати гарячу поправку...
+ FLOW - Почати гарячу поправку
+ Введіть назву
+ Почати реліз...
+ FLOW - Почати реліз
+ Тег версії Префікс:
+ Git LFS
+ Додати шаблон для відстеження...
+ Шаблон є ім'ям файлу
+ Спеціальний шаблон:
+ Додати шаблон для відстеження до Git LFS
+ Витягти
+ Запустіть `git lfs fetch`, щоб завантажити об'єкти Git LFS. Це не оновлює робочу копію.
+ Витягти об'єкти LFS
+ Встановити Git LFS hooks
+ Показати блокування
+ Немає заблокованих файлів
+ Заблокувати
+ Показати лише мої блокування
+ LFS блокування
+ Розблокувати
+ Примусово розблокувати
+ Принт
+ Запустіть `git lfs prune`, щоб видалити старі файли з локального сховища
+ Витягти
+ Запустіть `git lfs pull`, щоб завантажити всі файли Git LFS для поточної ref & checkout
+ Витягти об'єкти LFS
+ Надіслати
+ Надіслати чернетки великих файлів до кінця Git LFS
+ Надіслати об'єкти LFS
+ Віддалений:
+ Відстежувати файли, названі '{0}'
+ Відстежувати всі *{0} файли
+ ІСТОРІЯ
+ АВТОР
+ ЧАС АВТОРА
+ ГРАФ ТА ТЕМА
+ SHA
+ ЧАС КОМІТУ
+ ВИБРАНО {0} КОМІТІВ
+ Утримуйте 'Ctrl' або 'Shift' для вибору кількох комітів.
+ Утримуйте ⌘ або ⇧ для вибору кількох комітів.
+ ПОРАДИ:
+ Гарячі клавіші
+ ГЛОБАЛЬНІ
+ Скасувати поточне спливаюче вікно
+ Клонувати нове сховище
+ Закрити поточну вкладку
+ Перейти до наступної вкладки
+ Перейти до попередньої вкладки
+ Створити нову вкладку
+ Відкрити діалог Налаштування
+ СХОВИЩЕ
+ Закомітити проіндексовані зміни
+ Закомітити та надіслати проіндексовані зміни
+ Індексувати всі зміни та закомітити
+ Створити нову гілку на основі вибраного коміту
+ Скасувати вибрані зміни
+ Fetch, запускається без діалогу
+ Режим панелі керування (за замовчуванням)
+ Режим пошуку комітів
+ Pull, запускається без діалогу
+ Push, запускається без діалогу
+ Примусово перезавантажити це сховище
+ Індексувати/Видалити з індексу вибрані зміни
+ Перейти до 'Зміни'
+ Перейти до 'Історія'
+ Перейти до 'Схованки'
+ ТЕКСТОВИЙ РЕДАКТОР
+ Закрити панель пошуку
+ Знайти наступний збіг
+ Знайти попередній збіг
+ Відкрити панель пошуку
+ Скасувати
+ Індексувати
+ Видалити з індексу
+ Ініціалізувати сховище
+ Шлях:
+ Cherry-pick в процесі.
+ Обробка коміту
+ Злиття в процесі.
+ Виконується злиття
+ Перебазування в процесі.
+ Зупинено на
+ Скасування в процесі.
+ Скасування коміту
+ Інтерактивне перебазування
+ На:
+ Цільова гілка:
+ Копіювати посилання
+ Відкрити в браузері
+ ПОМИЛКА
+ ПОВІДОМЛЕННЯ
+ Злиття гілки
+ В:
+ Опція злиття:
+ Джерело:
+ Злиття (Кілька)
+ Закомітити всі зміни
+ Стратегія:
+ Цілі:
+ Перемістити вузол сховища
+ Виберіть батьківський вузол для:
+ Назва:
+ Git не налаштовано. Будь ласка, перейдіть до [Налаштування] та налаштуйте його.
+ Відкрити теку зберігання даних
+ Відкрити за допомогою...
+ Необов'язково.
+ Створити нову вкладку
+ Закладка
+ Закрити вкладку
+ Закрити інші вкладки
+ Закрити вкладки праворуч
+ Копіювати шлях до сховища
+ Сховища
+ Вставити
+ {0} днів тому
+ годину тому
+ {0} годин тому
+ Щойно
+ Минулого місяця
+ Минулого року
+ {0} хвилин тому
+ {0} місяців тому
+ {0} років тому
+ Вчора
+ Використовуйте 'Shift+Enter' для введення нового рядка. 'Enter' - гаряча клавіша кнопки OK
+ Налаштування
+ AI
+ Промпт для аналізу різниці
+ Ключ API
+ Промпт для генерації теми
+ Модель
+ Назва
+ Сервер
+ Увімкнути потокове відтворення
+ ВИГЛЯД
+ Шрифт за замовчуванням
+ Ширина табуляції в редакторі
+ Розмір шрифту
+ За замовчуванням
+ Редактор
+ Моноширинний шрифт
+ Використовувати моноширинний шрифт лише в текстовому редакторі
+ Тема
+ Перевизначення теми
+ Використовувати фіксовану ширину вкладки в заголовку
+ Використовувати системну рамку вікна
+ ІНСТРУМЕНТ DIFF/MERGE
+ Шлях встановлення
+ Введіть шлях до інструменту diff/merge
+ Інструмент
+ ЗАГАЛЬНІ
+ Перевіряти оновлення при запуску
+ Формат дати
+ Мова
+ Кількість комітів в історії
+ Показувати час автора замість часу коміту в графі
+ Показувати дочірні коміти в деталях
+ Показувати теги в графі комітів
+ Довжина лінії-орієнтира для теми
+ GIT
+ Увімкнути авто-CRLF
+ Тека клонування за замовчуванням
+ Email користувача
+ Глобальний email користувача git
+ Увімкнути --prune при fetch
+ Git (>= 2.25.1) є обов'язковим для цієї програми
+ Шлях встановлення
+ Увімкнути перевірку HTTP SSL
+ Ім'я користувача
+ Глобальне ім'я користувача git
+ Версія Git
+ ПІДПИС GPG
+ Підпис GPG для комітів
+ Формат GPG
+ Шлях встановлення програми
+ Введіть шлях до встановленої програми GPG
+ Підпис GPG для тегів
+ Ключ підпису користувача
+ Ключ підпису GPG користувача
+ ІНТЕГРАЦІЯ
+ КОНСОЛЬ/ТЕРМІНАЛ
+ Шлях
+ Консоль/Термінал
+ Prune для віддаленого сховища
+ Ціль:
+ Prune для робочих дерев
+ Видалити застарілу інформацію про робочі дерева в `$GIT_COMMON_DIR/worktrees`
+ Pull (Витягти)
+ Віддалена гілка:
+ В:
+ Локальні зміни:
+ Скасувати
+ Сховати та Застосувати
+ Віддалене сховище:
+ Pull (Fetch & Merge)
+ Використовувати rebase замість merge
+ Push (Надіслати)
+ Переконатися, що підмодулі надіслано
+ Примусовий push
+ Локальна гілка:
+ Віддалене сховище:
+ Надіслати зміни на віддалене сховище
+ Віддалена гілка:
+ Встановити як відстежувану гілку
+ Надіслати всі теги
+ Надіслати тег на віддалене сховище
+ Надіслати на всі віддалені сховища
+ Віддалене сховище:
+ Тег:
+ Вийти
+ Перебазувати поточну гілку
+ Сховати та застосувати локальні зміни
+ На:
+ Перебазувати:
+ Додати віддалене сховище
+ Редагувати віддалене сховище
+ Назва:
+ Назва віддаленого сховища
+ URL сховища:
+ URL віддаленого git сховища
+ Копіювати URL
+ Видалити...
+ Редагувати...
+ Fetch (Отримати)
+ Відкрити у браузері
+ Prune (Очистити)
+ Підтвердити видалення робочого дерева
+ Увімкнути опцію `--force`
+ Ціль:
+ Перейменувати гілку
+ Нова назва:
+ Унікальна назва для цієї гілки
+ Гілка:
+ ПЕРЕРВАТИ
+ Автоматичне отримання змін з віддалених сховищ...
+ Очистка (GC & Prune)
+ Виконати команду `git gc` для цього сховища.
+ Очистити все
+ Налаштувати це сховище
+ ПРОДОВЖИТИ
+ Спеціальні дії
+ Немає спеціальних дій
+ Скасувати всі зміни
+ Увімкнути опцію '--reflog'
+ Відкрити у файловому менеджері
+ Пошук гілок/тегів/підмодулів
+ Видимість у графі
+ Не встановлено
+ Приховати в графі комітів
+ Фільтрувати в графі комітів
+ Увімкнути опцію '--first-parent'
+ РОЗТАШУВАННЯ
+ Горизонтальне
+ Вертикальне
+ ПОРЯДОК КОМІТІВ
+ За датою коміту
+ Топологічний
+ ЛОКАЛЬНІ ГІЛКИ
+ Перейти до HEAD
+ Створити гілку
+ ОЧИСТИТИ СПОВІЩЕННЯ
+ Виділяти лише поточну гілку в графі
+ Відкрити в {0}
+ Відкрити в зовнішніх інструментах
+ Оновити
+ ВІДДАЛЕНІ СХОВИЩА
+ ДОДАТИ ВІДДАЛЕНЕ СХОВИЩЕ
+ Пошук коміту
+ Автор
+ Комітер
+ Файл
+ Повідомлення
+ SHA
+ Поточна гілка
+ Показати теги як дерево
+ ПРОПУСТИТИ
+ Статистика
+ ПІДМОДУЛІ
+ ДОДАТИ ПІДМОДУЛЬ
+ ОНОВИТИ ПІДМОДУЛЬ
+ ТЕГИ
+ НОВИЙ ТЕГ
+ За датою створення
+ За назвою
+ Сортувати
+ Відкрити в терміналі
+ Використовувати відносний час в історії
+ РОБОЧІ ДЕРЕВА
+ ДОДАТИ РОБОЧЕ ДЕРЕВО
+ PRUNE (ОЧИСТИТИ)
+ URL Git сховища
+ Скинути поточну гілку до ревізії
+ Режим скидання:
+ Перемістити до:
+ Поточна гілка:
+ Показати у файловому менеджері
+ Revert (Скасувати коміт)
+ Коміт:
+ Закомітити зміни скасування
+ Змінити повідомлення коміту
+ Виконується. Будь ласка, зачекайте...
+ ЗБЕРЕГТИ
+ Зберегти як...
+ Патч успішно збережено!
+ Сканувати сховища
+ Коренева тека:
+ Перевірити оновлення...
+ Доступна нова версія програми:
+ Не вдалося перевірити оновлення!
+ Завантажити
+ Пропустити цю версію
+ Оновлення програми
+ У вас встановлена остання версія.
+ Встановити відстежувану гілку
+ Гілка:
+ Скасувати upstream
+ Upstream:
+ Копіювати SHA
+ Перейти до
+ Squash (Склеїти коміти)
+ В:
+ Приватний ключ SSH:
+ Шлях до сховища приватного ключа SSH
+ ПОЧАТИ
+ Stash (Сховати)
+ Включити невідстежувані файли
+ Повідомлення:
+ Необов'язково. Назва цієї схованки
+ Лише проіндексовані зміни
+ Будуть сховані як проіндексовані, так і не проіндексовані зміни вибраних файлів!!!
+ Сховати локальні зміни
+ Застосувати
+ Видалити
+ Зберегти як патч...
+ Видалити схованку
+ Видалити:
+ СХОВАНКИ
+ ЗМІНИ
+ СХОВАНКИ
+ Статистика
+ КОМІТИ
+ КОМІТЕР
+ ОГЛЯД
+ МІСЯЦЬ
+ ТИЖДЕНЬ
+ АВТОРІВ:
+ КОМІТІВ:
+ ПІДМОДУЛІ
+ Додати підмодуль
+ Копіювати відносний шлях
+ Отримати вкладені підмодулі
+ Відкрити сховище підмодуля
+ Відносний шлях:
+ Відносна тека для зберігання цього модуля.
+ Видалити підмодуль
+ OK
+ Копіювати назву тегу
+ Копіювати повідомлення тегу
+ Видалити ${0}$...
+ Злиття ${0}$ в ${1}$...
+ Надіслати ${0}$...
+ Оновити підмодулі
+ Усі підмодулі
+ Ініціалізувати за потреби
+ Рекурсивно
+ Підмодуль:
+ Використовувати опцію --remote
+ URL:
+ Попередження
+ Вітальна сторінка
+ Створити групу
+ Створити підгрупу
+ Клонувати сховище
+ Видалити
+ ПІДТРИМУЄТЬСЯ ПЕРЕТЯГУВАННЯ ТЕК. МОЖЛИВЕ ГРУПУВАННЯ.
+ Редагувати
+ Перемістити до іншої групи
+ Відкрити всі сховища
+ Відкрити сховище
+ Відкрити термінал
+ Пересканувати сховища у теці клонування за замовчуванням
+ Пошук сховищ...
+ Сортувати
+ ЛОКАЛЬНІ ЗМІНИ
+ Git Ignore
+ Ігнорувати всі файли *{0}
+ Ігнорувати файли *{0} у цій же теці
+ Ігнорувати лише цей файл
+ Amend (Доповнити)
+ Тепер ви можете проіндексувати цей файл.
+ КОМІТ
+ КОМІТ ТА PUSH
+ Шаблон/Історії
+ Викликати подію кліку
+ Коміт (Редагувати)
+ Індексувати всі зміни та закомітити
+ Ви проіндексували {0} файл(ів), але відображено лише {1} ({2} файлів відфільтровано). Продовжити?
+ ВИЯВЛЕНО КОНФЛІКТИ
+ ВІДКРИТИ ЗОВНІШНІЙ ІНСТРУМЕНТ ЗЛИТТЯ
+ ВІДКРИТИ ВСІ КОНФЛІКТИ В ЗОВНІШНЬОМУ ІНСТРУМЕНТІ ЗЛИТТЯ
+ КОНФЛІКТИ ФАЙЛІВ ВИРІШЕНО
+ ВИКОРИСТАТИ МОЮ ВЕРСІЮ
+ ВИКОРИСТАТИ ЇХНЮ ВЕРСІЮ
+ ВКЛЮЧИТИ НЕВІДСТЕЖУВАНІ ФАЙЛИ
+ НЕМАЄ ОСТАННІХ ПОВІДОМЛЕНЬ
+ НЕМАЄ ШАБЛОНІВ КОМІТІВ
+ Клацніть правою кнопкою миші на вибраних файлах та оберіть спосіб вирішення конфліктів.
+ Підпис
+ ПРОІНДЕКСОВАНІ
+ ВИДАЛИТИ З ІНДЕКСУ
+ ВИДАЛИТИ ВСЕ З ІНДЕКСУ
+ НЕПРОІНДЕКСОВАНІ
+ ІНДЕКСУВАТИ
+ ІНДЕКСУВАТИ ВСЕ
+ ПЕРЕГЛЯНУТИ ФАЙЛИ, ЩО ВВАЖАЮТЬСЯ НЕЗМІНЕНИМИ
+ Шаблон: ${0}$
+ РОБОЧИЙ ПРОСТІР:
+ Налаштувати робочі простори...
+ РОБОЧЕ ДЕРЕВО
+ Копіювати шлях
+ Заблокувати
+ Видалити
+ Розблокувати
+
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index 2d160ad2..2a5991fd 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -2,41 +2,29 @@
+
关于软件
关于本软件
- • 项目依赖于
- • 图表绘制组件来自
- • 文本编辑器使用
- • 等宽字体来自于
- • 项目源代码地址
开源免费的Git客户端
新增工作树
- 检出分支方式 :
- 已有分支
- 创建新分支
工作树路径 :
填写该工作树的路径。支持相对路径。
分支名 :
选填。默认使用目标文件夹名称。
跟踪分支
设置上游跟踪分支
+ 检出分支方式 :
+ 创建新分支
+ 已有分支
AI助手
重新生成
使用AI助手生成提交信息
应用本次生成
应用补丁(apply)
- 错误
- 输出错误,并终止应用补丁
- 更多错误
- 与【错误】级别相似,但输出内容更多
补丁文件 :
选择补丁文件
忽略空白符号
- 忽略
- 关闭所有警告
应用补丁
- 警告
- 应用补丁,输出关于空白符的警告
空白符号处理 :
应用贮藏
在成功应用后丢弃该贮藏
@@ -51,17 +39,25 @@
不跟踪更改的文件
没有不跟踪更改的文件
移除
+ 加载本地图片
+ 重新加载
二进制文件不支持该操作!!!
+ 二分定位(bisect)
+ 终止
+ 标记错误
+ 二分定位进行中。当前提交是 '正确' 还是 '错误' ?
+ 标记正确
+ 无法判定
+ 二分定位进行中。请标记当前的提交是 '正确' 还是 '错误',然后检出另一个提交。
逐行追溯(blame)
选中文件不支持该操作!!!
检出(checkout) ${0}$...
- 与当前HEAD比较
+ 与当前 ${0}$ 比较
与本地工作树比较
复制分支名
自定义操作
删除 ${0}$...
删除选中的 {0} 个分支
- 放弃所有更改
快进(fast-forward)到 ${0}$
拉取(fetch) ${0}$ 至 ${1}$...
GIT工作流 - 完成 ${0}$
@@ -70,15 +66,16 @@
拉回(pull) ${0}$
拉回(pull) ${0}$ 内容至 ${1}$...
推送(push)${0}$
- 变基(rebase) ${0}$ 分支至 ${1}$...
+ 变基(rebase) ${0}$ 至 ${1}$...
重命名 ${0}$...
+ 重置 ${0}$ 到 ${1}$...
切换上游分支 ...
分支比较
跟踪的上游分支不存在或已删除!
字节
取 消
- 重置文件到该版本
重置文件到上一版本
+ 重置文件到该版本
生成提交信息
切换变更显示模式
文件名+路径列表模式
@@ -86,12 +83,15 @@
文件目录树形结构模式
检出(checkout)分支
检出(checkout)提交
- 注意:执行该操作后,当前HEAD会变为游离(detached)状态!
提交 :
- 目标分支 :
+ 注意:执行该操作后,当前HEAD会变为游离(detached)状态!
未提交更改 :
丢弃更改
贮藏并自动恢复
+ 同时更新所有子模块
+ 目标分支 :
+ 检出分支并快进
+ 上游分支 :
挑选提交
提交信息中追加来源信息
提交列表 :
@@ -110,17 +110,21 @@
远程仓库 :
关闭
提交信息编辑器
+ 检出此提交
挑选(cherry-pick)此提交
挑选(cherry-pick)...
- 检出此提交
与当前HEAD比较
与本地工作树比较
- 复制简要信息
- 复制提交指纹
+ 作者
+ 提交者
+ 简要信息
+ 提交指纹
+ 主题
自定义操作
交互式变基(rebase -i) ${0}$ 到此处
合并(merge)此提交至 ${0}$
合并(merge)...
+ 推送(push) ${0}$ 到 ${1}$
变基(rebase) ${0}$ 到此处
重置(reset) ${0}$ 到此处
回滚此提交
@@ -129,6 +133,7 @@
合并此提交到上一个提交
合并之后的提交到此处
变更对比
+ 个文件发生变更
查找变更...
文件列表
LFS文件
@@ -147,12 +152,13 @@
相关引用
提交指纹
浏览器中查看
- 填写提交信息主题
详细描述
+ 主题
+ 填写提交信息主题
仓库配置
提交信息模板
- 模板名 :
模板内容 :
+ 模板名 :
自定义操作
命令行参数 :
请使用${REPO}代替仓库路径,${BRANCH}代替选中的分支,${SHA}代替提交哈希
@@ -169,7 +175,9 @@
启用定时自动拉取远程更新
分钟
默认远程
+ 默认合并方式
ISSUE追踪
+ 新增匹配Azure DevOps规则
新增匹配Gitee议题规则
新增匹配Gitee合并请求规则
新增匹配Github Issue规则
@@ -190,7 +198,12 @@
应用于本仓库的用户名
工作区
颜色
+ 名称
启动时恢复打开的仓库
+ 确认继续
+ 提交未包含变更文件!是否继续(--allow-empty)?
+ 自动暂存并提交
+ 提交未包含变更文件!是否继续(--allow-empty)或是自动暂存所有变更并提交?
规范化提交信息生成
破坏性更新:
关闭的ISSUE:
@@ -200,8 +213,8 @@
类型:
复制
复制全部文本
+ 复制完整路径
复制路径
- 复制文件名
新建分支 ...
新分支基于 :
完成后切换到新分支
@@ -212,6 +225,7 @@
填写分支名称。
空格将被替换为'-'符号
创建本地分支
+ 允许重置已存在的分支
新建标签 ...
标签位于 :
使用GPG签名
@@ -226,6 +240,9 @@
轻量标签
按住Ctrl键点击将以默认参数运行
剪切
+ 取消初始化子模块
+ 强制取消,即使包含本地变更
+ 子模块 :
删除分支确认
分支名 :
您正在删除远程上的分支,请务必小心!!!
@@ -237,8 +254,8 @@
路径 :
目标 :
所有子节点将被同时从列表中移除。
- 删除分组确认
仅从列表中移除,不会删除硬盘中的文件!
+ 删除分组确认
删除仓库确认
删除子模块确认
子模块路径 :
@@ -261,6 +278,7 @@
显示隐藏符号
分列对比
子模块
+ 删除
新增
交换比对双方
语法高亮
@@ -285,7 +303,6 @@
编辑仓库
执行自定义操作
自定义操作 :
- 快进(fast-forward,无需checkout)
拉取(fetch)
拉取所有的远程仓库
强制覆盖本地REFs
@@ -307,11 +324,11 @@
从暂存中移除
从暂存中移除 {0} 个文件
从暂存中移除选中的更改
- 使用 THEIRS (checkout --theirs)
使用 MINE (checkout --ours)
+ 使用 THEIRS (checkout --theirs)
文件历史
- 文件内容
文件变更
+ 文件内容
GIT工作流
开发分支 :
特性分支 :
@@ -320,6 +337,8 @@
结束修复分支
结束版本分支
目标分支 :
+ 完成后自动推送
+ 压缩变更为单一提交后合并分支
修复分支 :
修复分支名前缀 :
初始化GIT工作流
@@ -341,8 +360,8 @@
规则 :
添加LFS追踪文件规则
拉取LFS对象 (fetch)
- 拉取LFS对象
执行`git lfs prune`命令,下载远程LFS对象,但不会更新工作副本。
+ 拉取LFS对象
启用Git LFS支持
显示LFS对象锁
没有锁定的LFS文件
@@ -354,11 +373,11 @@
精简本地LFS对象存储
运行`git lfs prune`命令,从本地存储中精简当前版本不需要的LFS对象
拉回LFS对象 (pull)
- 拉回LFS对象
运行`git lfs pull`命令,下载远程LFS对象并更新工作副本。
+ 拉回LFS对象
推送
- 推送LFS对象
将排队的大文件推送到Git LFS远程服务
+ 推送LFS对象
远程 :
跟踪名为'{0}'的文件
跟踪所有 *{0} 文件
@@ -377,10 +396,12 @@
取消弹出面板
克隆远程仓库
关闭当前页面
- 切换到上一个页面
切换到下一个页面
+ 切换到上一个页面
新建页面
打开偏好设置面板
+ 切换工作区
+ 切换显示页面
仓库页面快捷键
提交暂存区更改
提交暂存区更改并推送
@@ -389,11 +410,11 @@
丢弃选中的更改
拉取 (fetch) 远程变更
切换左边栏为分支/标签等显示模式(默认)
+ 切换左边栏为提交搜索模式
拉回 (pull) 远程变更
推送本地变更到远程
重新加载仓库状态
将选中的变更暂存或从暂存列表中移除
- 切换左边栏为提交搜索模式
显示本地更改
显示历史记录
显示贮藏列表
@@ -401,10 +422,11 @@
关闭搜索
定位到下一个匹配搜索的位置
定位到上一个匹配搜索的位置
+ 使用外部比对工具查看
打开搜索
+ 丢弃
暂存
移出暂存区
- 丢弃
初始化新仓库
路径 :
挑选(Cherry-Pick)操作进行中。
@@ -416,13 +438,16 @@
回滚提交操作进行中。
正在回滚提交
交互式变基
- 目标分支 :
起始提交 :
- 在浏览器中访问
+ 目标分支 :
复制链接地址
+ 在浏览器中访问
出错了
系统提示
+ 工作区列表
+ 页面列表
合并分支
+ 编辑合并信息
目标分支 :
合并方式 :
合并目标 :
@@ -445,16 +470,17 @@
复制仓库路径
新标签页
粘贴
- 刚刚
- {0}分钟前
+ {0}天前
1小时前
{0}小时前
- 昨天
- {0}天前
+ 刚刚
上个月
- {0}个月前
一年前
+ {0}分钟前
+ {0}个月前
{0}年前
+ 昨天
+ 请使用Shift+Enter换行。Enter键已被【确 定】按钮占用。
偏好设置
AI
Analyze Diff Prompt
@@ -470,7 +496,6 @@
字体大小
默认
代码编辑器
- 代码字体大小
等宽字体
仅在文本编辑器中使用等宽字体
主题
@@ -488,6 +513,7 @@
最大历史提交数
在提交路线图中显示修改时间而非提交时间
在提交详情页中显示子提交列表
+ 在提交路线图中显示标签
SUBJECT字数检测
GIT配置
自动换行转换
@@ -495,36 +521,36 @@
邮箱
默认GIT用户邮箱
拉取更新时启用修剪(--prune)
+ 对比文件时,默认忽略换行符变更 (--ignore-cr-at-eol)
+ 本软件要求GIT最低版本为2.25.1
安装路径
启用HTTP SSL验证
用户名
默认GIT用户名
Git 版本
- 本软件要求GIT最低版本为2.23.0
GPG签名
启用提交签名
- 启用标签签名
签名格式
签名程序位置
签名程序所在路径
+ 启用标签签名
用户签名KEY
输入签名提交所使用的KEY
第三方工具集成
终端/SHELL
- 终端/SHELL
安装路径
+ 终端/SHELL
清理远程已删除分支
目标 :
清理工作树
- 清理在`$GIT_DIR/worktrees`中的无效工作树信息
+ 清理在`$GIT_COMMON_DIR/worktrees`中的无效工作树信息
拉回(pull)
拉取分支 :
- 拉取远程中的所有分支变更
本地分支 :
未提交更改 :
丢弃更改
贮藏并自动恢复
- 不拉取远程标签
+ 同时更新所有子模块
远程 :
拉回(拉取并合并)
使用变基方式合并分支
@@ -533,6 +559,8 @@
启用强制推送
本地分支 :
远程仓库 :
+ 修订 :
+ 推送指定修订到远程仓库
推送到远程仓库
远程分支 :
跟踪远程分支
@@ -546,7 +574,6 @@
自动贮藏并恢复本地变更
目标提交 :
分支 :
- 重新加载
添加远程仓库
编辑远程仓库
远程名 :
@@ -568,13 +595,18 @@
分支 :
终止合并
自动拉取远端变更中...
+ 排序方式
+ 按提交时间
+ 按名称
清理本仓库(GC)
本操作将执行`git gc`命令。
清空过滤规则
+ 清空
配置本仓库
下一步
自定义操作
自定义操作未设置
+ 放弃所有更改
启用 --reflog 选项
在文件浏览器中打开
快速查找分支/标签/子模块
@@ -602,10 +634,12 @@
查找提交
作者
提交者
+ 变更内容
文件
提交信息
提交指纹
仅在当前分支中查找
+ 以树型结构展示
以树型结构展示
跳过此提交
提交统计
@@ -615,11 +649,12 @@
标签列表
新建标签
按创建时间
- 按名称(升序)
- 按名称(降序)
+ 按名称
排序
在终端中打开
在提交列表中使用相对时间
+ 查看命令日志
+ 访问远程仓库 '{0}'
工作树列表
新增工作树
清理
@@ -628,12 +663,14 @@
重置模式 :
提交 :
当前分支 :
+ 重置所选分支(非当前分支)
+ 重置点 :
+ 操作分支 :
在文件浏览器中查看
回滚操作确认
目标提交 :
回滚后提交更改
编辑提交信息
- 请使用Shift+Enter换行。Enter键已被【确 定】按钮占用。
执行操作中,请耐心等待...
保 存
另存为...
@@ -659,18 +696,16 @@
SSH密钥文件
开 始
贮藏(stash)
- 贮藏后自动恢复工作区
- 工作区文件保持未修改状态,但贮藏内容已保存。
包含未跟踪的文件
- 保留暂存区文件
信息 :
- 选填,用于命名此贮藏
+ 选填,此贮藏的描述信息
+ 模式 :
仅贮藏暂存区的变更
选中文件的所有变更均会被贮藏!
贮藏本地变更
应用(apply)
+ 复制描述信息
删除(drop)
- 应用并删除(pop)
另存为补丁...
丢弃贮藏确认
丢弃贮藏 :
@@ -680,32 +715,43 @@
提交统计
提交次数
提交者
+ 总览
本月
本周
- 提交次数:
贡献者人数:
- 总览
+ 提交次数:
子模块
添加子模块
复制路径
+ 取消初始化
拉取子孙模块
打开仓库
相对仓库路径 :
本地存放的相对路径。
删除子模块
+ 状态
+ 未提交修改
+ 未初始化
+ SHA变更
+ 未解决冲突
+ 仓库
确 定
复制标签名
复制标签信息
删除 ${0}$...
合并 ${0}$ 到 ${1}$...
推送 ${0}$...
- 仓库地址 :
更新子模块
更新所有子模块
启用 '--init'
启用 '--recursive'
子模块 :
启用 '--remote'
+ 仓库地址 :
+ 日志列表
+ 清空日志
+ 复制
+ 删除
警告
起始页
新建分组
@@ -725,7 +771,7 @@
添加至 .gitignore 忽略列表
忽略所有 *{0} 文件
忽略同目录下所有 *{0} 文件
- 忽略同目录下所有文件
+ 忽略该目录下的新文件
忽略本文件
修补
现在您已可将其加入暂存区中
@@ -735,12 +781,18 @@
触发点击事件
提交(修改原始提交)
自动暂存所有变更并提交
- 提交未包含变更文件!是否继续(--allow-empty)?
+ 当前有 {0} 个文件在暂存区中,但仅显示了 {1} 个文件({2} 个文件被过滤掉了),是否继续提交?
检测到冲突
+ 打开合并工具
+ 打开合并工具解决冲突
文件冲突已解决
+ 使用 MINE
+ 使用 THEIRS
显示未跟踪文件
没有提交信息记录
没有可应用的提交信息模板
+ 重置提交者
+ 请选中冲突文件,打开右键菜单,选择合适的解决方式
署名
已暂存
从暂存区移除选中
@@ -748,9 +800,8 @@
未暂存
暂存选中
暂存所有
- 查看忽略变更文件
+ 查看忽略变更文件
模板:${0}$
- 请选中冲突文件,打开右键菜单,选择合适的解决方式
工作区:
配置工作区...
本地工作树
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index e50a600d..f1736ff8 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -2,41 +2,29 @@
+
關於
關於 SourceGit
- • 專案依賴於
- • 圖表繪製元件來自
- • 文字編輯器使用
- • 等寬字型來自於
- • 專案原始碼網址
開源免費的 Git 客戶端
新增工作區
- 簽出分支方式:
- 已有分支
- 建立新分支
工作區路徑:
填寫該工作區的路徑。支援相對路徑。
分支名稱:
選填。預設使用目標資料夾名稱。
追蹤分支
設定遠端追蹤分支
+ 簽出分支方式:
+ 建立新分支
+ 已有分支
AI 助理
重新產生
使用 AI 產生提交訊息
套用為提交訊息
套用修補檔 (apply patch)
- 錯誤
- 輸出錯誤,並中止套用修補檔
- 更多錯誤
- 與 [錯誤] 級別相似,但輸出更多內容
修補檔:
選擇修補檔
忽略空白符號
- 忽略
- 關閉所有警告
套用修補檔
- 警告
- 套用修補檔,輸出關於空白字元的警告
空白字元處理:
套用擱置變更
套用擱置變更後刪除
@@ -51,17 +39,25 @@
不追蹤變更的檔案
沒有不追蹤變更的檔案
移除
+ 載入本機圖片...
+ 重新載入
二進位檔案不支援該操作!
+ 二分搜尋 (bisect)
+ 中止
+ 標記為錯誤
+ 二分搜尋進行中。目前的提交是「良好」是「錯誤」?
+ 標記為良好
+ 無法確認
+ 二分搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後簽出另一個提交。
逐行溯源 (blame)
所選擇的檔案不支援該操作!
簽出 (checkout) ${0}$...
- 與目前 HEAD 比較
+ 與目前 ${0}$ 比較
與本機工作區比較
複製分支名稱
自訂動作
刪除 ${0}$...
刪除所選的 {0} 個分支
- 捨棄所有變更
快轉 (fast-forward) 到 ${0}$
提取 (fetch) ${0}$ 到 ${1}$...
Git 工作流 - 完成 ${0}$
@@ -72,13 +68,14 @@
推送 (push) ${0}$
重定基底 (rebase) ${0}$ 分支至 ${1}$...
重新命名 ${0}$...
+ 重設 ${0}$ 至 ${1}$...
切換上游分支...
分支比較
- 追蹤上游分支不存在或已刪除!
+ 追蹤上游分支不存在或已刪除!
位元組
取 消
- 重設檔案為此版本
重設檔案到上一版本
+ 重設檔案為此版本
產生提交訊息
切換變更顯示模式
檔案名稱 + 路徑列表模式
@@ -86,12 +83,15 @@
檔案目錄樹狀結構模式
簽出 (checkout) 分支
簽出 (checkout) 提交
- 注意: 執行該操作後,目前 HEAD 會變為分離 (detached) 狀態!
提交:
- 目標分支:
+ 注意: 執行該操作後,目前 HEAD 會變為分離 (detached) 狀態!
未提交變更:
捨棄變更
擱置變更並自動復原
+ 同時更新所有子模組
+ 目標分支:
+ 簽出分支並快轉
+ 上游分支 :
揀選提交
提交資訊中追加來源資訊
提交列表:
@@ -110,17 +110,21 @@
遠端存放庫:
關閉
提交訊息編輯器
+ 簽出 (checkout) 此提交
揀選 (cherry-pick) 此提交
揀選 (cherry-pick)...
- 簽出 (checkout) 此提交
與目前 HEAD 比較
與本機工作區比較
- 複製摘要資訊
- 複製提交編號
+ 作者
+ 提交者
+ 摘要資訊
+ 提交編號
+ 標題
自訂動作
互動式重定基底 (rebase -i) ${0}$ 到此處
合併 (merge) 此提交到 ${0}$
合併 (merge)...
+ 推送(push) ${0}$ 至 ${1}$
重定基底 (rebase) ${0}$ 到此處
重設 (reset) ${0}$ 到此處
復原此提交
@@ -129,6 +133,7 @@
合併此提交到上一個提交
合併之後的提交到此處
變更對比
+ 個檔案已變更
搜尋變更...
檔案列表
LFS 檔案
@@ -147,12 +152,13 @@
相關參照
提交編號
在瀏覽器中檢視
- 填寫提交訊息標題
詳細描述
+ 標題
+ 填寫提交訊息標題
存放庫設定
提交訊息範本
- 範本名稱:
範本內容:
+ 範本名稱:
自訂動作
指令參數:
使用 ${REPO} 表示存放庫路徑、${BRANCH} 表示所選的分支、${SHA} 表示所選的提交編號
@@ -169,7 +175,9 @@
啟用定時自動提取 (fetch) 遠端更新
分鐘
預設遠端存放庫
+ 預設合併模式
Issue 追蹤
+ 新增符合 Azure DevOps 規則
新增符合 Gitee 議題規則
新增符合 Gitee 合併請求規則
新增符合 GitHub Issue 規則
@@ -190,7 +198,12 @@
用於本存放庫的使用者名稱
工作區
顏色
+ 名稱
啟動時還原上次開啟的存放庫
+ 確認繼續
+ 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)?
+ 自動暫存並提交
+ 未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存全部變更並提交?
產生約定式提交訊息
破壞性變更:
關閉的 Issue:
@@ -200,8 +213,8 @@
類型:
複製
複製全部內容
+ 複製完整路徑
複製路徑
- 複製檔案名稱
新增分支...
新分支基於:
完成後切換到新分支
@@ -212,6 +225,7 @@
輸入分支名稱。
空格將以英文破折號取代
建立本機分支
+ 允許覆寫現有分支
新增標籤...
標籤位於:
使用 GPG 簽章
@@ -226,6 +240,9 @@
輕量標籤
按住 Ctrl 鍵將直接以預設參數執行
剪下
+ 取消初始化子模組
+ 強制取消,即使它包含本地變更
+ 子模組 :
刪除分支確認
分支名稱:
您正在刪除遠端上的分支,請務必小心!
@@ -237,8 +254,8 @@
路徑:
目標:
所有子節點都會從清單中移除。
- 刪除群組確認
只會從清單中移除,而不會刪除磁碟中的檔案!
+ 刪除群組確認
刪除存放庫確認
刪除子模組確認
子模組路徑:
@@ -261,6 +278,7 @@
顯示隱藏符號
並排對比
子模組
+ 已刪除
新增
交換比對雙方
語法上色
@@ -285,7 +303,6 @@
編輯存放庫
執行自訂動作
自訂動作:
- 快進 (fast-forward,無需 checkout)
提取 (fetch)
提取所有的遠端存放庫
強制覆寫本機 REFs
@@ -307,11 +324,11 @@
取消暫存
從暫存中移除 {0} 個檔案
取消暫存選取的變更
- 使用對方版本 (checkout --theirs)
- 使用我方版本 (checkout --ours)
+ 使用我方版本 (ours)
+ 使用對方版本 (theirs)
檔案歷史
- 檔案内容
檔案變更
+ 檔案内容
Git 工作流
開發分支:
功能分支:
@@ -320,6 +337,8 @@
完成修復分支
完成發行分支
目標分支:
+ 完成後自動推送
+ 壓縮為單一提交後合併
修復分支:
修復分支前置詞:
初始化 Git 工作流
@@ -341,8 +360,8 @@
規則:
加入 LFS 追蹤檔案規則
提取 (fetch)
- 提取 LFS 物件
執行 `git lfs fetch` 以下載遠端 LFS 物件,但不會更新工作副本。
+ 提取 LFS 物件
啟用 Git LFS 支援
顯示 LFS 物件鎖
沒有鎖定的 LFS 物件
@@ -354,11 +373,11 @@
清理 (prune)
執行 `git lfs prune` 以從本機中清理目前版本不需要的 LFS 物件
拉取 (pull)
- 拉取 LFS 物件
執行 `git lfs pull` 以下載遠端 LFS 物件並更新工作副本。
+ 拉取 LFS 物件
推送 (push)
- 推送 LFS 物件
將大型檔案推送到 Git LFS 遠端服務
+ 推送 LFS 物件
遠端存放庫:
追蹤名稱為「{0}」的檔案
追蹤所有 *{0} 檔案
@@ -377,10 +396,12 @@
取消彈出面板
複製 (clone) 遠端存放庫
關閉目前頁面
- 切換到上一個頁面
切換到下一個頁面
+ 切換到上一個頁面
新增頁面
開啟偏好設定面板
+ 切換工作區
+ 切換目前頁面
存放庫頁面快速鍵
提交暫存區變更
提交暫存區變更並推送
@@ -389,11 +410,11 @@
捨棄選取的變更
提取 (fetch) 遠端的變更
切換左邊欄為分支/標籤等顯示模式 (預設)
+ 切換左邊欄為歷史搜尋模式
拉取 (pull) 遠端的變更
推送 (push) 本機變更到遠端存放庫
強制重新載入存放庫
暫存或取消暫存選取的變更
- 切換左邊欄為歷史搜尋模式
顯示本機變更
顯示歷史記錄
顯示擱置變更列表
@@ -401,10 +422,11 @@
關閉搜尋面板
前往下一個搜尋相符的位置
前往上一個搜尋相符的位置
+ 使用外部比對工具檢視
開啟搜尋面板
+ 捨棄
暫存
取消暫存
- 捨棄
初始化存放庫
路徑:
揀選 (cherry-pick) 操作進行中。
@@ -416,13 +438,16 @@
復原提交操作進行中。
正在復原提交
互動式重定基底
- 目標分支:
起始提交:
- 在瀏覽器中開啟連結
+ 目標分支:
複製連結
+ 在瀏覽器中開啟連結
發生錯誤
系統提示
+ 工作區列表
+ 頁面列表
合併分支
+ 編輯合併訊息
目標分支:
合併方式:
合併來源:
@@ -445,16 +470,17 @@
複製存放庫路徑
新分頁
貼上
- 剛剛
- {0} 分鐘前
+ {0} 天前
1 小時前
{0} 小時前
- 昨天
- {0} 天前
+ 剛剛
上個月
- {0} 個月前
一年前
+ {0} 分鐘前
+ {0} 個月前
{0} 年前
+ 昨天
+ 請使用 Shift + Enter 換行。Enter 鍵已被 [確定] 按鈕佔用。
偏好設定
AI
分析變更差異提示詞
@@ -466,7 +492,7 @@
啟用串流輸出
外觀設定
預設字型
- 編輯器制表符寬度
+ 編輯器 Tab 寬度
字型大小
預設
程式碼
@@ -487,6 +513,7 @@
最大歷史提交數
在提交路線圖中顯示修改時間而非提交時間
在提交詳細資訊中顯示後續提交
+ 在路線圖中顯示標籤
提交標題字數偵測
Git 設定
自動換行轉換
@@ -494,36 +521,36 @@
電子郵件
預設 Git 使用者電子郵件
拉取變更時進行清理 (--prune)
+ 對比檔案時,預設忽略行尾的 CR 變更 (--ignore-cr-at-eol)
+ 本軟體要求 Git 最低版本為 2.25.1
安裝路徑
啟用 HTTP SSL 驗證
使用者名稱
預設 Git 使用者名稱
Git 版本
- 本軟體要求 Git 最低版本為 2.23.0
GPG 簽章
啟用提交簽章
- 啟用標籤簽章
GPG 簽章格式
可執行檔案路徑
填寫 gpg.exe 所在路徑
+ 啟用標籤簽章
使用者簽章金鑰
填寫簽章提交所使用的金鑰
第三方工具整合
終端機/Shell
- 終端機/Shell
安裝路徑
+ 終端機/Shell
清理遠端已刪除分支
目標:
清理工作區
- 清理在 `$GIT_DIR/worktrees` 中的無效工作區資訊
+ 清理在 `$GIT_COMMON_DIR/worktrees` 中的無效工作區資訊
拉取 (pull)
拉取分支:
- 拉取遠端中的所有分支
本機分支:
未提交變更:
捨棄變更
擱置變更並自動復原
- 不拉取遠端標籤
+ 同時更新所有子模組
遠端:
拉取 (提取並合併)
使用重定基底 (rebase) 合併分支
@@ -532,6 +559,8 @@
啟用強制推送
本機分支:
遠端存放庫:
+ 修訂:
+ 推送修訂到遠端存放庫
推送到遠端存放庫
遠端分支:
追蹤遠端分支
@@ -545,7 +574,6 @@
自動擱置變更並復原本機變更
目標提交:
分支:
- 重新載入
新增遠端存放庫
編輯遠端存放庫
遠端名稱:
@@ -567,13 +595,18 @@
分支:
中止
自動提取遠端變更中...
+ 排序
+ 依建立時間
+ 依名稱升序
清理本存放庫 (GC)
本操作將執行 `git gc` 命令。
清空篩選規則
+ 清空
設定本存放庫
下一步
自訂動作
沒有自訂的動作
+ 捨棄所有變更
啟用 [--reflog] 選項
在檔案瀏覽器中開啟
快速搜尋分支/標籤/子模組
@@ -601,10 +634,12 @@
搜尋提交
作者
提交者
+ 變更內容
檔案
提交訊息
提交編號
僅搜尋目前分支
+ 以樹型結構展示
以樹型結構展示
跳過此提交
提交統計
@@ -614,11 +649,12 @@
標籤列表
新增標籤
依建立時間
- 依名稱升序
- 依名稱降序
+ 依名稱
排序
在終端機中開啟
在提交列表中使用相對時間
+ 檢視 Git 指令記錄
+ 檢視遠端存放庫 '{0}'
工作區列表
新增工作區
清理
@@ -627,12 +663,14 @@
重設模式:
移至提交:
目前分支:
+ 重設選取的分支(非目前分支)
+ 重設位置 :
+ 選取分支 :
在檔案瀏覽器中檢視
復原操作確認
目標提交:
復原後提交變更
編輯提交訊息
- 請使用 Shift + Enter 換行。Enter 鍵已被 [確定] 按鈕佔用。
執行操作中,請耐心等待...
儲存
另存新檔...
@@ -658,18 +696,16 @@
SSH 金鑰檔案
開 始
擱置變更 (stash)
- 擱置變更後自動復原工作區
- 工作區檔案保持未修改,但擱置內容已儲存。
包含未追蹤的檔案
- 保留已暫存的變更
擱置變更訊息:
- 選填,用於命名此擱置變更
+ 選填,用於描述此擱置變更
+ 操作模式:
僅擱置已暫存的變更
已選取的檔案中的變更均會被擱置!
擱置本機變更
套用 (apply)
+ 複製描述訊息
刪除 (drop)
- 套用並刪除 (pop)
另存為修補檔 (patch)...
捨棄擱置變更確認
捨棄擱置變更:
@@ -679,32 +715,43 @@
提交統計
提交次數
提交者
+ 總覽
本月
本週
- 提交次數:
貢獻者人數:
- 總覽
+ 提交次數:
子模組
新增子模組
複製路徑
+ 取消初始化
提取子模組
開啟存放庫
相對存放庫路徑:
本機存放的相對路徑。
刪除子模組
+ 狀態
+ 未提交變更
+ 未初始化
+ SHA 變更
+ 未解決的衝突
+ 存放庫
確 定
複製標籤名稱
複製標籤訊息
刪除 ${0}$...
合併 ${0}$ 到 ${1}$...
推送 ${0}$...
- 存放庫網址:
更新子模組
更新所有子模組
啟用 [--init] 選項
啟用 [--recursive] 選項
子模組:
啟用 [--remote] 選項
+ 存放庫網址:
+ 記錄
+ 清除所有記錄
+ 複製
+ 刪除
警告
起始頁
新增群組
@@ -717,14 +764,14 @@
開啟所有包含存放庫
開啟本機存放庫
開啟終端機
- 快速搜尋存放庫...
重新掃描預設複製 (clone) 目錄下的存放庫
+ 快速搜尋存放庫...
排序
本機變更
加入至 .gitignore 忽略清單
忽略所有 *{0} 檔案
忽略同路徑下所有 *{0} 檔案
- 忽略同路徑下所有檔案
+ 忽略本路徑下的新增檔案
忽略本檔案
修補
現在您已可將其加入暫存區中
@@ -734,12 +781,18 @@
觸發點擊事件
提交 (修改原始提交)
自動暫存全部變更並提交
- 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)?
- 檢測到衝突
+ 您已暫存 {0} 個檔案,但只顯示 {1} 個檔案 ({2} 個檔案被篩選器隱藏)。您確定要繼續提交嗎?
+ 偵測到衝突
+ 使用外部合併工具開啟
+ 使用外部合併工具開啟
檔案衝突已解決
+ 使用我方版本 (ours)
+ 使用對方版本 (theirs)
顯示未追蹤檔案
沒有提交訊息記錄
沒有可套用的提交訊息範本
+ 重設作者
+ 請選擇發生衝突的檔案,開啟右鍵選單,選擇合適的解決方式
署名
已暫存
取消暫存選取的檔案
@@ -747,9 +800,8 @@
未暫存
暫存選取的檔案
暫存所有檔案
- 檢視不追蹤變更的檔案
+ 檢視不追蹤變更的檔案
範本: ${0}$
- 請選擇發生衝突的檔案,開啟右鍵選單,選擇合適的解決方式
工作區:
設定工作區...
本機工作區
diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml
index 38321356..f42160a2 100644
--- a/src/Resources/Styles.axaml
+++ b/src/Resources/Styles.axaml
@@ -16,7 +16,7 @@
12
-
+
@@ -38,9 +38,9 @@
-
+
-
+
+
+
+
-
-
-
+
-
+
-
+
@@ -734,7 +731,7 @@
-
+
@@ -940,6 +947,20 @@
+
+
+
+
+
-
+
-
-
-
+
-
+
-
+
-
-
+ DataContextChanged="OnRowDataContextChanged">
-
-
+
+
+
+
+
+
@@ -66,7 +75,8 @@
-
@@ -75,17 +85,17 @@
+ DataContextChanged="OnRowDataContextChanged">
-
+
+
+
+
-
@@ -107,17 +118,17 @@
+ DataContextChanged="OnRowDataContextChanged">
-
+
+
+
+
diff --git a/src/Views/ChangeCollectionView.axaml.cs b/src/Views/ChangeCollectionView.axaml.cs
index 6ef79861..6623a60b 100644
--- a/src/Views/ChangeCollectionView.axaml.cs
+++ b/src/Views/ChangeCollectionView.axaml.cs
@@ -3,9 +3,11 @@ using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
+using Avalonia.Media;
using Avalonia.VisualTree;
namespace SourceGit.Views
@@ -86,7 +88,7 @@ namespace SourceGit.Views
}
public static readonly StyledProperty AutoSelectFirstChangeProperty =
- AvaloniaProperty.Register(nameof(AutoSelectFirstChange), false);
+ AvaloniaProperty.Register(nameof(AutoSelectFirstChange));
public bool AutoSelectFirstChange
{
@@ -145,6 +147,7 @@ namespace SourceGit.Views
removeCount++;
}
+
tree.Rows.RemoveRange(idx + 1, removeCount);
}
}
@@ -209,6 +212,13 @@ namespace SourceGit.Views
return null;
}
+ public void TakeFocus()
+ {
+ var container = this.FindDescendantOfType();
+ if (container is { IsFocused: false })
+ container.Focus();
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -221,6 +231,28 @@ namespace SourceGit.Views
UpdateSelection();
}
+ private void OnRowDataContextChanged(object sender, EventArgs e)
+ {
+ if (sender is not Control control)
+ return;
+
+ if (control.DataContext is ViewModels.ChangeTreeNode node)
+ {
+ if (node.Change is { } c)
+ UpdateRowTips(control, c);
+ else
+ ToolTip.SetTip(control, node.FullPath);
+ }
+ else if (control.DataContext is Models.Change change)
+ {
+ UpdateRowTips(control, change);
+ }
+ else
+ {
+ ToolTip.SetTip(control, null);
+ }
+ }
+
private void OnRowDoubleTapped(object sender, TappedEventArgs e)
{
var grid = sender as Grid;
@@ -458,6 +490,21 @@ namespace SourceGit.Views
}
}
+ private void UpdateRowTips(Control control, Models.Change change)
+ {
+ var tip = new TextBlock() { TextWrapping = TextWrapping.Wrap };
+ tip.Inlines!.Add(new Run(change.Path));
+ tip.Inlines!.Add(new Run(" • ") { Foreground = Brushes.Gray });
+ tip.Inlines!.Add(new Run(IsUnstagedChange ? change.WorkTreeDesc : change.IndexDesc) { Foreground = Brushes.Gray });
+ if (change.IsConflicted)
+ {
+ tip.Inlines!.Add(new Run(" • ") { Foreground = Brushes.Gray });
+ tip.Inlines!.Add(new Run(change.ConflictDesc) { Foreground = Brushes.Gray });
+ }
+
+ ToolTip.SetTip(control, tip);
+ }
+
private bool _disableSelectionChangingEvent = false;
}
}
diff --git a/src/Views/ChangeStatusIcon.cs b/src/Views/ChangeStatusIcon.cs
index 5d34be09..4cbbf0a6 100644
--- a/src/Views/ChangeStatusIcon.cs
+++ b/src/Views/ChangeStatusIcon.cs
@@ -4,66 +4,26 @@ using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
+using Avalonia.Styling;
namespace SourceGit.Views
{
public class ChangeStatusIcon : Control
{
- private static readonly IBrush[] BACKGROUNDS = [
- Brushes.Transparent,
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
- new LinearGradientBrush
- {
- GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
- StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
- EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
- },
+ private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "★", "!"];
+ private static readonly Color[] COLOR =
+ [
+ Colors.Transparent,
+ Colors.Goldenrod,
+ Colors.Goldenrod,
+ Colors.LimeGreen,
+ Colors.Tomato,
+ Colors.Orchid,
+ Colors.Goldenrod,
+ Colors.LimeGreen,
+ Colors.OrangeRed,
];
- private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "U", "★"];
- private static readonly string[] TIPS = ["Unknown", "Modified", "Type Changed", "Added", "Deleted", "Renamed", "Copied", "Unmerged", "Untracked"];
-
public static readonly StyledProperty IsUnstagedChangeProperty =
AvaloniaProperty.Register(nameof(IsUnstagedChange));
@@ -89,26 +49,20 @@ namespace SourceGit.Views
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
- IBrush background;
- string indicator;
- if (IsUnstagedChange)
+ var idx = (int)(IsUnstagedChange ? Change.WorkTree : Change.Index);
+ var indicator = INDICATOR[idx];
+ var color = COLOR[idx];
+ var hsl = color.ToHsl();
+ var color2 = ActualThemeVariant == ThemeVariant.Dark
+ ? new HslColor(hsl.A, hsl.H, hsl.S, hsl.L - 0.1).ToRgb()
+ : new HslColor(hsl.A, hsl.H, hsl.S, hsl.L + 0.1).ToRgb();
+
+ var background = new LinearGradientBrush
{
- if (Change.IsConflit)
- {
- background = Brushes.OrangeRed;
- indicator = "!";
- }
- else
- {
- background = BACKGROUNDS[(int)Change.WorkTree];
- indicator = INDICATOR[(int)Change.WorkTree];
- }
- }
- else
- {
- background = BACKGROUNDS[(int)Change.Index];
- indicator = INDICATOR[(int)Change.Index];
- }
+ GradientStops = [new GradientStop(color, 0), new GradientStop(color2, 1)],
+ StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
+ EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
+ };
var txt = new FormattedText(
indicator,
@@ -128,23 +82,10 @@ namespace SourceGit.Views
{
base.OnPropertyChanged(change);
- if (change.Property == IsUnstagedChangeProperty || change.Property == ChangeProperty)
- {
- var isUnstaged = IsUnstagedChange;
- var c = Change;
- if (c == null)
- {
- ToolTip.SetTip(this, null);
- return;
- }
-
- if (isUnstaged)
- ToolTip.SetTip(this, c.IsConflit ? "Conflict" : TIPS[(int)c.WorkTree]);
- else
- ToolTip.SetTip(this, TIPS[(int)c.Index]);
-
+ if (change.Property == IsUnstagedChangeProperty ||
+ change.Property == ChangeProperty ||
+ (change.Property.Name == "ActualThemeVariant" && change.NewValue != null))
InvalidateVisual();
- }
}
}
}
diff --git a/src/Views/ChangeViewModeSwitcher.axaml b/src/Views/ChangeViewModeSwitcher.axaml
index 4825c2e2..911fb41d 100644
--- a/src/Views/ChangeViewModeSwitcher.axaml
+++ b/src/Views/ChangeViewModeSwitcher.axaml
@@ -27,7 +27,7 @@
-
+
+
+ Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"/>
+
+
diff --git a/src/Views/CheckoutAndFastForward.axaml b/src/Views/CheckoutAndFastForward.axaml
new file mode 100644
index 00000000..40ca3e14
--- /dev/null
+++ b/src/Views/CheckoutAndFastForward.axaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/FastForwardWithoutCheckout.axaml.cs b/src/Views/CheckoutAndFastForward.axaml.cs
similarity index 52%
rename from src/Views/FastForwardWithoutCheckout.axaml.cs
rename to src/Views/CheckoutAndFastForward.axaml.cs
index 0e3ba20d..c54f5a1f 100644
--- a/src/Views/FastForwardWithoutCheckout.axaml.cs
+++ b/src/Views/CheckoutAndFastForward.axaml.cs
@@ -2,9 +2,9 @@ using Avalonia.Controls;
namespace SourceGit.Views
{
- public partial class FastForwardWithoutCheckout : UserControl
+ public partial class CheckoutAndFastForward : UserControl
{
- public FastForwardWithoutCheckout()
+ public CheckoutAndFastForward()
{
InitializeComponent();
}
diff --git a/src/Views/CheckoutCommit.axaml b/src/Views/CheckoutCommit.axaml
index 9b418823..11b4b5d0 100644
--- a/src/Views/CheckoutCommit.axaml
+++ b/src/Views/CheckoutCommit.axaml
@@ -12,7 +12,7 @@
Classes="bold"
Text="{DynamicResource Text.Checkout.Commit}" />
-
+
-
+
+
+
-
+
-
-
-
+
diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs
index 647c657e..1662bcd7 100644
--- a/src/Views/ChromelessWindow.cs
+++ b/src/Views/ChromelessWindow.cs
@@ -3,7 +3,6 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
-using Avalonia.Platform;
namespace SourceGit.Views
{
@@ -11,38 +10,15 @@ namespace SourceGit.Views
{
public bool UseSystemWindowFrame
{
- get => OperatingSystem.IsLinux() && ViewModels.Preferences.Instance.UseSystemWindowFrame;
+ get => Native.OS.UseSystemWindowFrame;
}
protected override Type StyleKeyOverride => typeof(Window);
public ChromelessWindow()
{
- if (OperatingSystem.IsLinux())
- {
- if (UseSystemWindowFrame)
- {
- ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.Default;
- ExtendClientAreaToDecorationsHint = false;
- }
- else
- {
- ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
- ExtendClientAreaToDecorationsHint = true;
- Classes.Add("custom_window_frame");
- }
- }
- else if (OperatingSystem.IsWindows())
- {
- ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
- ExtendClientAreaToDecorationsHint = true;
- Classes.Add("fix_maximized_padding");
- }
- else
- {
- ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome;
- ExtendClientAreaToDecorationsHint = true;
- }
+ Focusable = true;
+ Native.OS.SetupForWindow(this);
}
public void BeginMoveWindow(object _, PointerPressedEventArgs e)
diff --git a/src/Views/Clone.axaml b/src/Views/Clone.axaml
index 84d1dd74..8c7c9faf 100644
--- a/src/Views/Clone.axaml
+++ b/src/Views/Clone.axaml
@@ -11,14 +11,14 @@
-
-
-
-
-
ValueProperty =
- AvaloniaProperty.Register(nameof(Value), 0);
+ AvaloniaProperty.Register(nameof(Value));
public uint Value
{
@@ -19,7 +19,7 @@ namespace SourceGit.Views
}
// Values are copied from Avalonia: src/Avalonia.Controls.ColorPicker/ColorPalettes/FluentColorPalette.cs
- private static readonly Color[,] COLOR_TABLE = new Color[,]
+ private static readonly Color[,] COLOR_TABLE = new[,]
{
{
Color.FromArgb(255, 255, 67, 67), /* #FF4343 */
@@ -110,7 +110,7 @@ namespace SourceGit.Views
context.DrawLine(pen, new Point(j * 32, 0), new Point(j * 32, 192));
// Selected
- if (_hightlightedTableRect is { } rect)
+ if (_highlightedTableRect is { } rect)
context.DrawRectangle(new Pen(Brushes.White, 2), rect);
}
@@ -143,7 +143,7 @@ namespace SourceGit.Views
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
}
protected override Size MeasureOverride(Size availableSize)
@@ -161,9 +161,9 @@ namespace SourceGit.Views
var col = (int)Math.Floor(p.X / 32.0);
var row = (int)Math.Floor(p.Y / 32.0);
var rect = new Rect(col * 32 + 2, row * 32 + 2, 28, 28);
- if (!rect.Equals(_hightlightedTableRect))
+ if (!rect.Equals(_highlightedTableRect))
{
- _hightlightedTableRect = rect;
+ _highlightedTableRect = rect;
SetCurrentValue(ValueProperty, COLOR_TABLE[row, col].ToUInt32());
}
@@ -172,32 +172,32 @@ namespace SourceGit.Views
if (_darkestRect.Rect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _darkestColor.ToUInt32());
}
else if (_darkerRect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _darkerColor.ToUInt32());
}
else if (_darkRect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _darkColor.ToUInt32());
}
else if (_lightRect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _lightColor.ToUInt32());
}
else if (_lighterRect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _lighterColor.ToUInt32());
}
else if (_lightestRect.Rect.Contains(p))
{
- _hightlightedTableRect = null;
+ _highlightedTableRect = null;
SetCurrentValue(ValueProperty, _lightestColor.ToUInt32());
}
}
@@ -234,7 +234,7 @@ namespace SourceGit.Views
private Rect _lightRect = new Rect(160, 200, 32, 32);
private Rect _lighterRect = new Rect(192, 200, 32, 32);
private RoundedRect _lightestRect = new RoundedRect(new Rect(224, 200, 32, 32), new CornerRadius(0, 4, 4, 0));
- private Rect? _hightlightedTableRect = null;
+ private Rect? _highlightedTableRect = null;
private Color _darkestColor;
private Color _darkerColor;
diff --git a/src/Views/CommandLogContentPresenter.cs b/src/Views/CommandLogContentPresenter.cs
new file mode 100644
index 00000000..a2499f8d
--- /dev/null
+++ b/src/Views/CommandLogContentPresenter.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+
+using AvaloniaEdit;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Editing;
+using AvaloniaEdit.Rendering;
+using AvaloniaEdit.TextMate;
+
+namespace SourceGit.Views
+{
+ public class CommandLogContentPresenter : TextEditor
+ {
+ public class LineStyleTransformer : DocumentColorizingTransformer
+ {
+ protected override void ColorizeLine(DocumentLine line)
+ {
+ var content = CurrentContext.Document.GetText(line);
+ if (content.StartsWith("$ git ", StringComparison.Ordinal))
+ {
+ ChangeLinePart(line.Offset, line.Offset + 1, v =>
+ {
+ v.TextRunProperties.SetForegroundBrush(Brushes.Orange);
+ });
+
+ ChangeLinePart(line.Offset + 2, line.EndOffset, v =>
+ {
+ var old = v.TextRunProperties.Typeface;
+ v.TextRunProperties.SetTypeface(new Typeface(old.FontFamily, old.Style, FontWeight.Bold));
+ });
+ }
+ else if (content.StartsWith("remote: ", StringComparison.Ordinal))
+ {
+ ChangeLinePart(line.Offset, line.Offset + 7, v =>
+ {
+ v.TextRunProperties.SetForegroundBrush(Brushes.SeaGreen);
+ });
+ }
+ else
+ {
+ foreach (var err in _errors)
+ {
+ var idx = content.IndexOf(err, StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ ChangeLinePart(line.Offset + idx, line.Offset + err.Length + 1, v =>
+ {
+ v.TextRunProperties.SetForegroundBrush(Brushes.Red);
+ });
+ }
+ }
+ }
+ }
+
+ private readonly List _errors = ["! [rejected]", "! [remote rejected]"];
+ }
+
+ public static readonly StyledProperty LogProperty =
+ AvaloniaProperty.Register(nameof(Log));
+
+ public ViewModels.CommandLog Log
+ {
+ get => GetValue(LogProperty);
+ set => SetValue(LogProperty, value);
+ }
+
+ public static readonly StyledProperty PureTextProperty =
+ AvaloniaProperty.Register(nameof(PureText));
+
+ public string PureText
+ {
+ get => GetValue(PureTextProperty);
+ set => SetValue(PureTextProperty, value);
+ }
+
+ protected override Type StyleKeyOverride => typeof(TextEditor);
+
+ public CommandLogContentPresenter() : base(new TextArea(), new TextDocument())
+ {
+ IsReadOnly = true;
+ ShowLineNumbers = false;
+ WordWrap = false;
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+ VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
+
+ TextArea.TextView.Margin = new Thickness(4, 0);
+ TextArea.TextView.Options.EnableHyperlinks = false;
+ TextArea.TextView.Options.EnableEmailHyperlinks = false;
+ }
+
+ protected override void OnLoaded(RoutedEventArgs e)
+ {
+ base.OnLoaded(e);
+
+ if (_textMate == null)
+ {
+ _textMate = Models.TextMateHelper.CreateForEditor(this);
+ Models.TextMateHelper.SetGrammarByFileName(_textMate, "Log.log");
+ TextArea.TextView.LineTransformers.Add(new LineStyleTransformer());
+ }
+ }
+
+ protected override void OnUnloaded(RoutedEventArgs e)
+ {
+ base.OnUnloaded(e);
+
+ if (_textMate != null)
+ {
+ _textMate.Dispose();
+ _textMate = null;
+ }
+
+ GC.Collect();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == LogProperty)
+ {
+ if (change.NewValue is ViewModels.CommandLog log)
+ {
+ Text = log.Content;
+ log.Register(OnLogLineReceived);
+ }
+ else
+ {
+ Text = string.Empty;
+ }
+ }
+ else if (change.Property == PureTextProperty)
+ {
+ if (!string.IsNullOrEmpty(PureText))
+ Text = PureText;
+ }
+ }
+
+ private void OnLogLineReceived(string newline)
+ {
+ AppendText("\n");
+ AppendText(newline);
+ }
+
+ private TextMate.Installation _textMate = null;
+ }
+}
diff --git a/src/Views/CommandLogTime.cs b/src/Views/CommandLogTime.cs
new file mode 100644
index 00000000..0e51c1e3
--- /dev/null
+++ b/src/Views/CommandLogTime.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Threading;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Threading;
+
+namespace SourceGit.Views
+{
+ public class CommandLogTime : TextBlock
+ {
+ public static readonly StyledProperty LogProperty =
+ AvaloniaProperty.Register(nameof(Log));
+
+ public ViewModels.CommandLog Log
+ {
+ get => GetValue(LogProperty);
+ set => SetValue(LogProperty, value);
+ }
+
+ protected override Type StyleKeyOverride => typeof(TextBlock);
+
+ protected override void OnUnloaded(RoutedEventArgs e)
+ {
+ base.OnUnloaded(e);
+ StopTimer();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == LogProperty)
+ {
+ StopTimer();
+
+ if (change.NewValue is ViewModels.CommandLog log)
+ SetupCommandLog(log);
+ else
+ Text = string.Empty;
+ }
+ }
+
+ private void SetupCommandLog(ViewModels.CommandLog log)
+ {
+ Text = GetDisplayText(log);
+ if (log.IsComplete)
+ return;
+
+ _refreshTimer = new Timer(_ =>
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ Text = GetDisplayText(log);
+ if (log.IsComplete)
+ StopTimer();
+ });
+ }, null, 0, 100);
+ }
+
+ private void StopTimer()
+ {
+ if (_refreshTimer is not null)
+ {
+ _refreshTimer.Dispose();
+ _refreshTimer = null;
+ }
+ }
+
+ private string GetDisplayText(ViewModels.CommandLog log)
+ {
+ var endTime = log.IsComplete ? log.EndTime : DateTime.Now;
+ var duration = (endTime - log.StartTime).ToString(@"hh\:mm\:ss\.fff");
+ return $"{log.StartTime:T} ({duration})";
+ }
+
+ private Timer _refreshTimer = null;
+ }
+}
diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml
index 692d04b8..2d3b6ff4 100644
--- a/src/Views/CommitBaseInfo.axaml
+++ b/src/Views/CommitBaseInfo.axaml
@@ -55,9 +55,9 @@
-
-
-
@@ -41,20 +41,28 @@
-
+
-
+
-
+
+
+
+
+
+
+
+
@@ -63,8 +71,8 @@
-
diff --git a/src/Views/CommitGraph.cs b/src/Views/CommitGraph.cs
index 015eaca5..5db39300 100644
--- a/src/Views/CommitGraph.cs
+++ b/src/Views/CommitGraph.cs
@@ -55,23 +55,29 @@ namespace SourceGit.Views
if (list == null)
return;
- // Calculate drawing area.
- double width = Bounds.Width - 273 - histories.AuthorNameColumnWidth.Value;
- double height = Bounds.Height;
- double startY = list.Scroll?.Offset.Y ?? 0;
- double endY = startY + height + 28;
+ var container = list.ItemsPanelRoot as VirtualizingStackPanel;
+ if (container == null)
+ return;
+
+ var item = list.ContainerFromIndex(container.FirstRealizedIndex);
+ if (item == null)
+ return;
+
+ var width = histories.CommitListHeader.ColumnDefinitions[0].ActualWidth;
+ var height = Bounds.Height;
+ var rowHeight = item.Bounds.Height;
+ var startY = container.FirstRealizedIndex * rowHeight - item.TranslatePoint(new Point(0, 0), list).Value!.Y;
+ var endY = startY + height + 28;
- // Apply scroll offset and clip.
using (context.PushClip(new Rect(0, 0, width, height)))
using (context.PushTransform(Matrix.CreateTranslation(0, -startY)))
{
- // Draw contents
- DrawCurves(context, graph, startY, endY);
- DrawAnchors(context, graph, startY, endY);
+ DrawCurves(context, graph, startY, endY, rowHeight);
+ DrawAnchors(context, graph, startY, endY, rowHeight);
}
}
- private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom)
+ private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight)
{
var grayedPen = new Pen(new SolidColorBrush(Colors.Gray, 0.4), Models.CommitGraph.Pens[0].Thickness);
var onlyHighlightCurrentBranch = OnlyHighlightCurrentBranch;
@@ -82,16 +88,20 @@ namespace SourceGit.Views
{
if (link.IsMerged)
continue;
- if (link.End.Y < top)
+
+ var startY = link.Start.Y * rowHeight;
+ var endY = link.End.Y * rowHeight;
+
+ if (endY < top)
continue;
- if (link.Start.Y > bottom)
+ if (startY > bottom)
break;
var geo = new StreamGeometry();
using (var ctx = geo.Open())
{
- ctx.BeginFigure(link.Start, false);
- ctx.QuadraticBezierTo(link.Control, link.End);
+ ctx.BeginFigure(new Point(link.Start.X, startY), false);
+ ctx.QuadraticBezierTo(new Point(link.Control.X, link.Control.Y * rowHeight), new Point(link.End.X, endY));
}
context.DrawGeometry(null, grayedPen, geo);
@@ -100,10 +110,11 @@ namespace SourceGit.Views
foreach (var line in graph.Paths)
{
- var last = line.Points[0];
+ var last = new Point(line.Points[0].X, line.Points[0].Y * rowHeight);
var size = line.Points.Count;
+ var endY = line.Points[size - 1].Y * rowHeight;
- if (line.Points[size - 1].Y < top)
+ if (endY < top)
continue;
if (last.Y > bottom)
break;
@@ -117,7 +128,7 @@ namespace SourceGit.Views
var ended = false;
for (int i = 1; i < size; i++)
{
- var cur = line.Points[i];
+ var cur = new Point(line.Points[i].X, line.Points[i].Y * rowHeight);
if (cur.Y < top)
{
last = cur;
@@ -173,23 +184,27 @@ namespace SourceGit.Views
{
if (onlyHighlightCurrentBranch && !link.IsMerged)
continue;
- if (link.End.Y < top)
+
+ var startY = link.Start.Y * rowHeight;
+ var endY = link.End.Y * rowHeight;
+
+ if (endY < top)
continue;
- if (link.Start.Y > bottom)
+ if (startY > bottom)
break;
var geo = new StreamGeometry();
using (var ctx = geo.Open())
{
- ctx.BeginFigure(link.Start, false);
- ctx.QuadraticBezierTo(link.Control, link.End);
+ ctx.BeginFigure(new Point(link.Start.X, startY), false);
+ ctx.QuadraticBezierTo(new Point(link.Control.X, link.Control.Y * rowHeight), new Point(link.End.X, endY));
}
context.DrawGeometry(null, Models.CommitGraph.Pens[link.Color], geo);
}
}
- private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom)
+ private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight)
{
var dotFill = DotBrush;
var dotFillPen = new Pen(dotFill, 2);
@@ -198,9 +213,11 @@ namespace SourceGit.Views
foreach (var dot in graph.Dots)
{
- if (dot.Center.Y < top)
+ var center = new Point(dot.Center.X, dot.Center.Y * rowHeight);
+
+ if (center.Y < top)
continue;
- if (dot.Center.Y > bottom)
+ if (center.Y > bottom)
break;
var pen = Models.CommitGraph.Pens[dot.Color];
@@ -210,16 +227,16 @@ namespace SourceGit.Views
switch (dot.Type)
{
case Models.CommitGraph.DotType.Head:
- context.DrawEllipse(dotFill, pen, dot.Center, 6, 6);
- context.DrawEllipse(pen.Brush, null, dot.Center, 3, 3);
+ context.DrawEllipse(dotFill, pen, center, 6, 6);
+ context.DrawEllipse(pen.Brush, null, center, 3, 3);
break;
case Models.CommitGraph.DotType.Merge:
- context.DrawEllipse(pen.Brush, null, dot.Center, 6, 6);
- context.DrawLine(dotFillPen, new Point(dot.Center.X, dot.Center.Y - 3), new Point(dot.Center.X, dot.Center.Y + 3));
- context.DrawLine(dotFillPen, new Point(dot.Center.X - 3, dot.Center.Y), new Point(dot.Center.X + 3, dot.Center.Y));
+ context.DrawEllipse(pen.Brush, null, center, 6, 6);
+ context.DrawLine(dotFillPen, new Point(center.X, center.Y - 3), new Point(center.X, center.Y + 3));
+ context.DrawLine(dotFillPen, new Point(center.X - 3, center.Y), new Point(center.X + 3, center.Y));
break;
default:
- context.DrawEllipse(dotFill, pen, dot.Center, 3, 3);
+ context.DrawEllipse(dotFill, pen, center, 3, 3);
break;
}
}
diff --git a/src/Views/StandaloneCommitMessageEditor.axaml b/src/Views/CommitMessageEditor.axaml
similarity index 96%
rename from src/Views/StandaloneCommitMessageEditor.axaml
rename to src/Views/CommitMessageEditor.axaml
index 8a0dc91a..c469ae8d 100644
--- a/src/Views/StandaloneCommitMessageEditor.axaml
+++ b/src/Views/CommitMessageEditor.axaml
@@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="SourceGit.Views.StandaloneCommitMessageEditor"
+ x:Class="SourceGit.Views.CommitMessageEditor"
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.CodeEditor}"
diff --git a/src/Views/CommitMessageEditor.axaml.cs b/src/Views/CommitMessageEditor.axaml.cs
new file mode 100644
index 00000000..7a23f85c
--- /dev/null
+++ b/src/Views/CommitMessageEditor.axaml.cs
@@ -0,0 +1,69 @@
+using System;
+using System.IO;
+
+using Avalonia.Interactivity;
+
+namespace SourceGit.Views
+{
+ public partial class CommitMessageEditor : ChromelessWindow
+ {
+ public CommitMessageEditor()
+ {
+ InitializeComponent();
+ }
+
+ public void AsStandalone(string file)
+ {
+ _onSave = msg => File.WriteAllText(file, msg);
+ _shouldExitApp = true;
+
+ var content = File.ReadAllText(file).ReplaceLineEndings("\n").Trim();
+ var firstLineEnd = content.IndexOf('\n', StringComparison.Ordinal);
+ if (firstLineEnd == -1)
+ {
+ Editor.SubjectEditor.Text = content;
+ }
+ else
+ {
+ Editor.SubjectEditor.Text = content.Substring(0, firstLineEnd);
+ Editor.DescriptionEditor.Text = content.Substring(firstLineEnd + 1).Trim();
+ }
+ }
+
+ public void AsBuiltin(string msg, Action onSave)
+ {
+ _onSave = onSave;
+ _shouldExitApp = false;
+
+ var firstLineEnd = msg.IndexOf('\n', StringComparison.Ordinal);
+ if (firstLineEnd == -1)
+ {
+ Editor.SubjectEditor.Text = msg;
+ }
+ else
+ {
+ Editor.SubjectEditor.Text = msg.Substring(0, firstLineEnd);
+ Editor.DescriptionEditor.Text = msg.Substring(firstLineEnd + 1).Trim();
+ }
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+
+ if (_shouldExitApp)
+ App.Quit(_exitCode);
+ }
+
+ private void SaveAndClose(object _1, RoutedEventArgs _2)
+ {
+ _onSave?.Invoke(Editor.Text);
+ _exitCode = 0;
+ Close();
+ }
+
+ private Action _onSave = null;
+ private bool _shouldExitApp = true;
+ private int _exitCode = -1;
+ }
+}
diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs
index 002beda1..61119991 100644
--- a/src/Views/CommitMessagePresenter.cs
+++ b/src/Views/CommitMessagePresenter.cs
@@ -39,7 +39,7 @@ namespace SourceGit.Views
if (string.IsNullOrEmpty(message))
return;
- var links = FullMessage?.Links;
+ var links = FullMessage?.Inlines;
if (links == null || links.Count == 0)
{
Inlines.Add(new Run(message));
@@ -48,13 +48,14 @@ namespace SourceGit.Views
var inlines = new List();
var pos = 0;
- foreach (var link in links)
+ for (var i = 0; i < links.Count; i++)
{
+ var link = links[i];
if (link.Start > pos)
inlines.Add(new Run(message.Substring(pos, link.Start - pos)));
var run = new Run(message.Substring(link.Start, link.Length));
- run.Classes.Add(link.IsCommitSHA ? "commit_link" : "issue_link");
+ run.Classes.Add(link.Type == Models.InlineElementType.CommitSHA ? "commit_link" : "issue_link");
inlines.Add(run);
pos = link.Start + link.Length;
@@ -87,7 +88,7 @@ namespace SourceGit.Views
scrollViewer.LineDown();
}
}
- else if (FullMessage is { Links: { Count: > 0 } links })
+ else if (FullMessage is { Inlines: { Count: > 0 } links })
{
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));
@@ -95,31 +96,10 @@ namespace SourceGit.Views
point = new Point(x, y);
var pos = TextLayout.HitTestPoint(point).TextPosition;
- foreach (var link in links)
- {
- if (!link.Intersect(pos, 1))
- continue;
-
- if (link == _lastHover)
- return;
-
- SetCurrentValue(CursorProperty, Cursor.Parse("Hand"));
-
- _lastHover = link;
- if (!link.IsCommitSHA)
- {
- ToolTip.SetTip(this, link.Link);
- ToolTip.SetIsOpen(this, true);
- }
- else
- {
- ProcessHoverCommitLink(link);
- }
-
- return;
- }
-
- ClearHoveredIssueLink();
+ if (links.Intersect(pos, 1) is { } link)
+ SetHoveredIssueLink(link);
+ else
+ ClearHoveredIssueLink();
}
}
@@ -132,7 +112,7 @@ namespace SourceGit.Views
var link = _lastHover.Link;
e.Pointer.Capture(null);
- if (_lastHover.IsCommitSHA)
+ if (_lastHover.Type == Models.InlineElementType.CommitSHA)
{
var parentView = this.FindAncestorOfType();
if (parentView is { DataContext: ViewModels.CommitDetail detail })
@@ -257,19 +237,14 @@ namespace SourceGit.Views
ClearHoveredIssueLink();
}
- private void ProcessHoverCommitLink(Models.Hyperlink link)
+ private void ProcessHoverCommitLink(Models.InlineElement link)
{
var sha = link.Link;
// If we have already queried this SHA, just use it.
if (_inlineCommits.TryGetValue(sha, out var exist))
{
- if (exist != null)
- {
- ToolTip.SetTip(this, exist);
- ToolTip.SetIsOpen(this, true);
- }
-
+ ToolTip.SetTip(this, exist);
return;
}
@@ -289,21 +264,31 @@ namespace SourceGit.Views
if (currentParent is { DataContext: ViewModels.CommitDetail currentDetail } &&
currentDetail.Commit.SHA == lastDetailCommit)
{
- if (!_inlineCommits.ContainsKey(sha))
- _inlineCommits.Add(sha, c);
+ _inlineCommits.TryAdd(sha, c);
// Make sure user still hovers the target SHA.
if (_lastHover == link && c != null)
- {
ToolTip.SetTip(this, c);
- ToolTip.SetIsOpen(this, true);
- }
}
});
});
}
}
+ private void SetHoveredIssueLink(Models.InlineElement link)
+ {
+ if (link == _lastHover)
+ return;
+
+ SetCurrentValue(CursorProperty, Cursor.Parse("Hand"));
+
+ _lastHover = link;
+ if (link.Type == Models.InlineElementType.Link)
+ ToolTip.SetTip(this, link.Link);
+ else
+ ProcessHoverCommitLink(link);
+ }
+
private void ClearHoveredIssueLink()
{
if (_lastHover != null)
@@ -314,7 +299,7 @@ namespace SourceGit.Views
}
}
- private Models.Hyperlink _lastHover = null;
+ private Models.InlineElement _lastHover = null;
private Dictionary _inlineCommits = new();
}
}
diff --git a/src/Views/CommitMessageTextBox.axaml b/src/Views/CommitMessageTextBox.axaml
index 44d5d5fb..73c3a193 100644
--- a/src/Views/CommitMessageTextBox.axaml
+++ b/src/Views/CommitMessageTextBox.axaml
@@ -12,43 +12,23 @@
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
CornerRadius="4">
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/Views/CommitMessageTextBox.axaml.cs b/src/Views/CommitMessageTextBox.axaml.cs
index 0811d97d..5330852c 100644
--- a/src/Views/CommitMessageTextBox.axaml.cs
+++ b/src/Views/CommitMessageTextBox.axaml.cs
@@ -56,6 +56,9 @@ namespace SourceGit.Views
FromEditor,
}
+ public static readonly StyledProperty ShowAdvancedOptionsProperty =
+ AvaloniaProperty.Register(nameof(ShowAdvancedOptions));
+
public static readonly StyledProperty TextProperty =
AvaloniaProperty.Register(nameof(Text), string.Empty);
@@ -65,6 +68,12 @@ namespace SourceGit.Views
public static readonly StyledProperty DescriptionProperty =
AvaloniaProperty.Register(nameof(Description), string.Empty);
+ public bool ShowAdvancedOptions
+ {
+ get => GetValue(ShowAdvancedOptionsProperty);
+ set => SetValue(ShowAdvancedOptionsProperty, value);
+ }
+
public string Text
{
get => GetValue(TextProperty);
@@ -167,6 +176,48 @@ namespace SourceGit.Views
}
}
+ private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
+ {
+ if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
+ {
+ var menu = vm.CreateContextMenuForCommitMessages();
+ menu.Placement = PlacementMode.TopEdgeAlignedLeft;
+ menu.Open(button);
+ }
+
+ e.Handled = true;
+ }
+
+ private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.WorkingCopy vm && sender is Control control)
+ {
+ var menu = vm.CreateContextForOpenAI();
+ menu?.Open(control);
+ }
+
+ e.Handled = true;
+ }
+
+ private void OnOpenConventionalCommitHelper(object _, RoutedEventArgs e)
+ {
+ var toplevel = TopLevel.GetTopLevel(this);
+ if (toplevel is Window owner)
+ {
+ var vm = new ViewModels.ConventionalCommitMessageBuilder(text => Text = text);
+ var builder = new ConventionalCommitMessageBuilder() { DataContext = vm };
+ builder.ShowDialog(owner);
+ }
+
+ e.Handled = true;
+ }
+
+ private void CopyAllText(object sender, RoutedEventArgs e)
+ {
+ App.CopyText(Text);
+ e.Handled = true;
+ }
+
private TextChangeWay _changingWay = TextChangeWay.None;
}
}
diff --git a/src/Views/CommitRefsPresenter.cs b/src/Views/CommitRefsPresenter.cs
index 7ff6bafb..a5717c6c 100644
--- a/src/Views/CommitRefsPresenter.cs
+++ b/src/Views/CommitRefsPresenter.cs
@@ -17,6 +17,7 @@ namespace SourceGit.Views
public IBrush Brush { get; set; } = null;
public bool IsHead { get; set; } = false;
public double Width { get; set; } = 0.0;
+ public Models.Decorator Decorator { get; set; } = null;
}
public static readonly StyledProperty FontFamilyProperty =
@@ -64,15 +65,6 @@ namespace SourceGit.Views
set => SetValue(UseGraphColorProperty, value);
}
- public static readonly StyledProperty TagBackgroundProperty =
- AvaloniaProperty.Register(nameof(TagBackground), Brushes.White);
-
- public IBrush TagBackground
- {
- get => GetValue(TagBackgroundProperty);
- set => SetValue(TagBackgroundProperty, value);
- }
-
public static readonly StyledProperty AllowWrapProperty =
AvaloniaProperty.Register(nameof(AllowWrap));
@@ -82,6 +74,15 @@ namespace SourceGit.Views
set => SetValue(AllowWrapProperty, value);
}
+ public static readonly StyledProperty ShowTagsProperty =
+ AvaloniaProperty.Register(nameof(ShowTags), true);
+
+ public bool ShowTags
+ {
+ get => GetValue(ShowTagsProperty);
+ set => SetValue(ShowTagsProperty, value);
+ }
+
static CommitRefsPresenter()
{
AffectsMeasure(
@@ -89,8 +90,21 @@ namespace SourceGit.Views
FontSizeProperty,
ForegroundProperty,
UseGraphColorProperty,
- TagBackgroundProperty,
- BackgroundProperty);
+ BackgroundProperty,
+ ShowTagsProperty);
+ }
+
+ public Models.Decorator DecoratorAt(Point point)
+ {
+ var x = 0.0;
+ foreach (var item in _items)
+ {
+ x += item.Width;
+ if (point.X < x)
+ return item.Decorator;
+ }
+
+ return null;
}
public override void Render(DrawingContext context)
@@ -171,15 +185,18 @@ namespace SourceGit.Views
var typefaceBold = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold);
var fg = Foreground;
var normalBG = UseGraphColor ? commit.Brush : Brushes.Gray;
- var tagBG = UseGraphColor ? TagBackground : Brushes.Gray;
var labelSize = FontSize;
var requiredWidth = 0.0;
var requiredHeight = 16.0;
var x = 0.0;
var allowWrap = AllowWrap;
+ var showTags = ShowTags;
foreach (var decorator in refs)
{
+ if (!showTags && decorator.Type == Models.DecoratorType.Tag)
+ continue;
+
var isHead = decorator.Type == Models.DecoratorType.CurrentBranchHead ||
decorator.Type == Models.DecoratorType.CurrentCommitHead;
@@ -195,7 +212,8 @@ namespace SourceGit.Views
{
Label = label,
Brush = normalBG,
- IsHead = isHead
+ IsHead = isHead,
+ Decorator = decorator,
};
StreamGeometry geo;
@@ -209,7 +227,7 @@ namespace SourceGit.Views
geo = this.FindResource("Icons.Remote") as StreamGeometry;
break;
case Models.DecoratorType.Tag:
- item.Brush = tagBG;
+ item.Brush = Brushes.Gray;
geo = this.FindResource("Icons.Tag") as StreamGeometry;
break;
default:
diff --git a/src/Views/CommitRelationTracking.axaml b/src/Views/CommitRelationTracking.axaml
index 5e3574d8..9d036e10 100644
--- a/src/Views/CommitRelationTracking.axaml
+++ b/src/Views/CommitRelationTracking.axaml
@@ -30,7 +30,7 @@
-
+
diff --git a/src/Views/CommitStatusIndicator.cs b/src/Views/CommitStatusIndicator.cs
index c2f4184e..7073011a 100644
--- a/src/Views/CommitStatusIndicator.cs
+++ b/src/Views/CommitStatusIndicator.cs
@@ -35,7 +35,7 @@ namespace SourceGit.Views
set => SetValue(BehindBrushProperty, value);
}
- enum Status
+ private enum Status
{
Normal,
Ahead,
diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs
index 32f6838d..bfeab34f 100644
--- a/src/Views/CommitSubjectPresenter.cs
+++ b/src/Views/CommitSubjectPresenter.cs
@@ -1,18 +1,80 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Globalization;
using System.Text.RegularExpressions;
+
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.Media;
-using Avalonia.Media.TextFormatting;
namespace SourceGit.Views
{
- public partial class CommitSubjectPresenter : TextBlock
+ public partial class CommitSubjectPresenter : Control
{
+ public static readonly StyledProperty FontFamilyProperty =
+ AvaloniaProperty.Register(nameof(FontFamily));
+
+ public FontFamily FontFamily
+ {
+ get => GetValue(FontFamilyProperty);
+ set => SetValue(FontFamilyProperty, value);
+ }
+
+ public static readonly StyledProperty CodeFontFamilyProperty =
+ AvaloniaProperty.Register(nameof(CodeFontFamily));
+
+ public FontFamily CodeFontFamily
+ {
+ get => GetValue(CodeFontFamilyProperty);
+ set => SetValue(CodeFontFamilyProperty, value);
+ }
+
+ public static readonly StyledProperty FontSizeProperty =
+ TextBlock.FontSizeProperty.AddOwner();
+
+ public double FontSize
+ {
+ get => GetValue(FontSizeProperty);
+ set => SetValue(FontSizeProperty, value);
+ }
+
+ public static readonly StyledProperty FontWeightProperty =
+ TextBlock.FontWeightProperty.AddOwner();
+
+ public FontWeight FontWeight
+ {
+ get => GetValue(FontWeightProperty);
+ set => SetValue(FontWeightProperty, value);
+ }
+
+ public static readonly StyledProperty InlineCodeBackgroundProperty =
+ AvaloniaProperty.Register(nameof(InlineCodeBackground), Brushes.Transparent);
+
+ public IBrush InlineCodeBackground
+ {
+ get => GetValue(InlineCodeBackgroundProperty);
+ set => SetValue(InlineCodeBackgroundProperty, value);
+ }
+
+ public static readonly StyledProperty ForegroundProperty =
+ AvaloniaProperty.Register(nameof(Foreground), Brushes.White);
+
+ public IBrush Foreground
+ {
+ get => GetValue(ForegroundProperty);
+ set => SetValue(ForegroundProperty, value);
+ }
+
+ public static readonly StyledProperty LinkForegroundProperty =
+ AvaloniaProperty.Register(nameof(LinkForeground), Brushes.White);
+
+ public IBrush LinkForeground
+ {
+ get => GetValue(LinkForegroundProperty);
+ set => SetValue(LinkForegroundProperty, value);
+ }
+
public static readonly StyledProperty SubjectProperty =
AvaloniaProperty.Register(nameof(Subject));
@@ -31,7 +93,46 @@ namespace SourceGit.Views
set => SetValue(IssueTrackerRulesProperty, value);
}
- protected override Type StyleKeyOverride => typeof(TextBlock);
+ public override void Render(DrawingContext context)
+ {
+ if (_needRebuildInlines)
+ {
+ _needRebuildInlines = false;
+ GenerateFormattedTextElements();
+ }
+
+ if (_inlines.Count == 0)
+ return;
+
+ var ro = new RenderOptions()
+ {
+ TextRenderingMode = TextRenderingMode.SubpixelAntialias,
+ EdgeMode = EdgeMode.Antialias
+ };
+
+ using (context.PushRenderOptions(ro))
+ {
+ var height = Bounds.Height;
+ var width = Bounds.Width;
+ foreach (var inline in _inlines)
+ {
+ if (inline.X > width)
+ return;
+
+ if (inline.Element is { Type: Models.InlineElementType.Code })
+ {
+ var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2);
+ var roundedRect = new RoundedRect(rect, new CornerRadius(4));
+ context.DrawRectangle(InlineCodeBackground, null, roundedRect);
+ context.DrawText(inline.Text, new Point(inline.X + 4, (height - inline.Text.Height) * 0.5));
+ }
+ else
+ {
+ context.DrawText(inline.Text, new Point(inline.X, (height - inline.Text.Height) * 0.5));
+ }
+ }
+ }
+ }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
@@ -39,85 +140,60 @@ namespace SourceGit.Views
if (change.Property == SubjectProperty || change.Property == IssueTrackerRulesProperty)
{
- Inlines!.Clear();
- _matches = null;
+ _elements.Clear();
ClearHoveredIssueLink();
var subject = Subject;
if (string.IsNullOrEmpty(subject))
+ {
+ _needRebuildInlines = true;
+ InvalidateVisual();
return;
+ }
+
+ var rules = IssueTrackerRules ?? [];
+ foreach (var rule in rules)
+ rule.Matches(_elements, subject);
var keywordMatch = REG_KEYWORD_FORMAT1().Match(subject);
if (!keywordMatch.Success)
keywordMatch = REG_KEYWORD_FORMAT2().Match(subject);
- var rules = IssueTrackerRules ?? [];
- var matches = new List();
- foreach (var rule in rules)
- rule.Matches(matches, subject);
+ if (keywordMatch.Success && _elements.Intersect(0, keywordMatch.Length) == null)
+ _elements.Add(new Models.InlineElement(Models.InlineElementType.Keyword, 0, keywordMatch.Length, string.Empty));
- if (matches.Count == 0)
+ var codeMatches = REG_INLINECODE_FORMAT().Matches(subject);
+ for (var i = 0; i < codeMatches.Count; i++)
{
- if (keywordMatch.Success)
- {
- Inlines.Add(new Run(subject.Substring(0, keywordMatch.Length)) { FontWeight = FontWeight.Bold });
- Inlines.Add(new Run(subject.Substring(keywordMatch.Length)));
- }
- else
- {
- Inlines.Add(new Run(subject));
- }
- return;
+ var match = codeMatches[i];
+ if (!match.Success)
+ continue;
+
+ var start = match.Index;
+ var len = match.Length;
+ if (_elements.Intersect(start, len) != null)
+ continue;
+
+ _elements.Add(new Models.InlineElement(Models.InlineElementType.Code, start, len, string.Empty));
}
- matches.Sort((l, r) => l.Start - r.Start);
- _matches = matches;
-
- var inlines = new List();
- var pos = 0;
- foreach (var match in matches)
- {
- if (match.Start > pos)
- {
- if (keywordMatch.Success && pos < keywordMatch.Length)
- {
- if (keywordMatch.Length < match.Start)
- {
- inlines.Add(new Run(subject.Substring(pos, keywordMatch.Length - pos)) { FontWeight = FontWeight.Bold });
- inlines.Add(new Run(subject.Substring(keywordMatch.Length, match.Start - keywordMatch.Length)));
- }
- else
- {
- inlines.Add(new Run(subject.Substring(pos, match.Start - pos)) { FontWeight = FontWeight.Bold });
- }
- }
- else
- {
- inlines.Add(new Run(subject.Substring(pos, match.Start - pos)));
- }
- }
-
- var link = new Run(subject.Substring(match.Start, match.Length));
- link.Classes.Add("issue_link");
- inlines.Add(link);
-
- pos = match.Start + match.Length;
- }
-
- if (pos < subject.Length)
- {
- if (keywordMatch.Success && pos < keywordMatch.Length)
- {
- inlines.Add(new Run(subject.Substring(pos, keywordMatch.Length - pos)) { FontWeight = FontWeight.Bold });
- inlines.Add(new Run(subject.Substring(keywordMatch.Length)));
- }
- else
- {
- inlines.Add(new Run(subject.Substring(pos)));
- }
- }
-
- Inlines.AddRange(inlines);
+ _elements.Sort();
+ _needRebuildInlines = true;
+ InvalidateVisual();
+ }
+ else if (change.Property == FontFamilyProperty ||
+ change.Property == CodeFontFamilyProperty ||
+ change.Property == FontSizeProperty ||
+ change.Property == FontWeightProperty ||
+ change.Property == ForegroundProperty ||
+ change.Property == LinkForegroundProperty)
+ {
+ _needRebuildInlines = true;
+ InvalidateVisual();
+ }
+ else if (change.Property == InlineCodeBackgroundProperty)
+ {
+ InvalidateVisual();
}
}
@@ -125,32 +201,23 @@ namespace SourceGit.Views
{
base.OnPointerMoved(e);
- if (_matches != null)
+ var point = e.GetPosition(this);
+ foreach (var inline in _inlines)
{
- 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);
+ if (inline.Element is not { Type: Models.InlineElementType.Link } link)
+ continue;
- var textPosition = TextLayout.HitTestPoint(point).TextPosition;
- foreach (var match in _matches)
- {
- if (!match.Intersect(textPosition, 1))
- continue;
+ if (inline.X > point.X || inline.X + inline.Text.WidthIncludingTrailingWhitespace < point.X)
+ continue;
- if (match == _lastHover)
- return;
-
- _lastHover = match;
- SetCurrentValue(CursorProperty, Cursor.Parse("Hand"));
- ToolTip.SetTip(this, match.Link);
- ToolTip.SetIsOpen(this, true);
- e.Handled = true;
- return;
- }
-
- ClearHoveredIssueLink();
+ _lastHover = link;
+ SetCurrentValue(CursorProperty, Cursor.Parse("Hand"));
+ ToolTip.SetTip(this, link.Link);
+ e.Handled = true;
+ return;
}
+
+ ClearHoveredIssueLink();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
@@ -167,6 +234,94 @@ namespace SourceGit.Views
ClearHoveredIssueLink();
}
+ private void GenerateFormattedTextElements()
+ {
+ _inlines.Clear();
+
+ var subject = Subject;
+ if (string.IsNullOrEmpty(subject))
+ return;
+
+ var fontFamily = FontFamily;
+ var codeFontFamily = CodeFontFamily;
+ var fontSize = FontSize;
+ var foreground = Foreground;
+ var linkForeground = LinkForeground;
+ var typeface = new Typeface(fontFamily, FontStyle.Normal, FontWeight);
+ var codeTypeface = new Typeface(codeFontFamily, FontStyle.Normal, FontWeight);
+ var pos = 0;
+ var x = 0.0;
+ for (var i = 0; i < _elements.Count; i++)
+ {
+ var elem = _elements[i];
+ if (elem.Start > pos)
+ {
+ var normal = new FormattedText(
+ subject.Substring(pos, elem.Start - pos),
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ typeface,
+ fontSize,
+ foreground);
+
+ _inlines.Add(new Inline(x, normal, null));
+ x += normal.WidthIncludingTrailingWhitespace;
+ }
+
+ if (elem.Type == Models.InlineElementType.Keyword)
+ {
+ var keyword = new FormattedText(
+ subject.Substring(elem.Start, elem.Length),
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ new Typeface(fontFamily, FontStyle.Normal, FontWeight.Bold),
+ fontSize,
+ foreground);
+ _inlines.Add(new Inline(x, keyword, elem));
+ x += keyword.WidthIncludingTrailingWhitespace;
+ }
+ else if (elem.Type == Models.InlineElementType.Link)
+ {
+ var link = new FormattedText(
+ subject.Substring(elem.Start, elem.Length),
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ typeface,
+ fontSize,
+ linkForeground);
+ _inlines.Add(new Inline(x, link, elem));
+ x += link.WidthIncludingTrailingWhitespace;
+ }
+ else if (elem.Type == Models.InlineElementType.Code)
+ {
+ var link = new FormattedText(
+ subject.Substring(elem.Start + 1, elem.Length - 2),
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ codeTypeface,
+ fontSize - 0.5,
+ foreground);
+ _inlines.Add(new Inline(x, link, elem));
+ x += link.WidthIncludingTrailingWhitespace + 8;
+ }
+
+ pos = elem.Start + elem.Length;
+ }
+
+ if (pos < subject.Length)
+ {
+ var normal = new FormattedText(
+ subject.Substring(pos),
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ typeface,
+ fontSize,
+ foreground);
+
+ _inlines.Add(new Inline(x, normal, null));
+ }
+ }
+
private void ClearHoveredIssueLink()
{
if (_lastHover != null)
@@ -177,13 +332,32 @@ namespace SourceGit.Views
}
}
+ [GeneratedRegex(@"`.*?`")]
+ private static partial Regex REG_INLINECODE_FORMAT();
+
[GeneratedRegex(@"^\[[\w\s]+\]")]
private static partial Regex REG_KEYWORD_FORMAT1();
[GeneratedRegex(@"^\S+([\<\(][\w\s_\-\*,]+[\>\)])?\!?\s?:\s")]
private static partial Regex REG_KEYWORD_FORMAT2();
- private List _matches = null;
- private Models.Hyperlink _lastHover = null;
+ private class Inline
+ {
+ public double X { get; set; } = 0;
+ public FormattedText Text { get; set; } = null;
+ public Models.InlineElement Element { get; set; } = null;
+
+ public Inline(double x, FormattedText text, Models.InlineElement elem)
+ {
+ X = x;
+ Text = text;
+ Element = elem;
+ }
+ }
+
+ private Models.InlineElementCollector _elements = new();
+ private List _inlines = [];
+ private Models.InlineElement _lastHover = null;
+ private bool _needRebuildInlines = false;
}
}
diff --git a/src/Views/CommitTimeTextBlock.cs b/src/Views/CommitTimeTextBlock.cs
index db63e8a6..63ecda61 100644
--- a/src/Views/CommitTimeTextBlock.cs
+++ b/src/Views/CommitTimeTextBlock.cs
@@ -19,7 +19,7 @@ namespace SourceGit.Views
}
public static readonly StyledProperty DateTimeFormatProperty =
- AvaloniaProperty.Register(nameof(DateTimeFormat), 0);
+ AvaloniaProperty.Register(nameof(DateTimeFormat));
public int DateTimeFormat
{
diff --git a/src/Views/ConfigureWorkspace.axaml b/src/Views/ConfigureWorkspace.axaml
index 9c18ad04..b239399a 100644
--- a/src/Views/ConfigureWorkspace.axaml
+++ b/src/Views/ConfigureWorkspace.axaml
@@ -66,11 +66,11 @@