Merge branch 'release/v8.32'

This commit is contained in:
leo 2024-09-30 09:30:21 +08:00
commit 6099d54c7b
No known key found for this signature in database
75 changed files with 1476 additions and 1907 deletions

66
.gitattributes vendored
View file

@ -1,78 +1,12 @@
# Auto detect text files and perform LF normalization
# https://www.davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
#
# The above will handle all files NOT found below
#
# Documents
*.bibtex text diff=bibtex
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text
*.tex text diff=tex
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
*.sql text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default.
*.svg text
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
*.eps binary
# Scripts
*.bash text eol=lf
*.fish text eol=lf
*.sh text eol=lf
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
# Serialisation
*.json text
*.toml text
*.xml text
*.yaml text
*.yml text
# Archives
*.7z binary
*.gz binary
*.tar binary
*.tgz binary
*.zip binary
# Text files where line endings should be preserved
*.patch -text
#
# Exclude files from exporting
#
.gitattributes export-ignore
.gitignore export-ignore

588
.gitignore vendored
View file

@ -1,425 +1,13 @@
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
.vscode/
.idea/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Nuke Build - Uncomment if you are using it
.nuke/temp
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.sln.docstates
*.user
*.suo
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
### Linux ###
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
@ -428,175 +16,14 @@ Icon
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Rider ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### VisualStudioCode ###
!.vscode/*.code-snippets
# Local History for Visual Studio Code
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
bin/
obj/
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
# Windows shortcuts
*.lnk
### Specifics ###
# Specials
*.zip
archives
package-lock.json
*.private.env.json
**/**/[Dd]ata/*.json
**/**/[Dd]ata/*.csv
# SpecFlow
*.feature.cs
# Azurite
*azurite*.json
# Build Folders
[Pp]ublish
[Oo]utput
[Ss]cripts
[Tt]ests/[Rr]esults
# LibraryManager
**/lib
# BuildBundlerMinifier
*.min.*
*.map
# Sass Output
**/css
# SQLite files
*.db
*.sqlite3
*.sqlite
*.db-journal
*.sqlite3-journal
*.sqlite-journal
*.db-shm
*.db-wal
# SourceGit output files
build/resources/
build/SourceGit/
build/SourceGit.app/
@ -605,3 +32,4 @@ build/*.tar.gz
build/*.deb
build/*.rpm
build/*.AppImage
SourceGit.app/

View file

@ -1 +1 @@
8.31
8.32

15
build/README.md Normal file
View file

@ -0,0 +1,15 @@
# build
> [!WARNING]
> The files under the `build` folder is used for `Github Action` only, **NOT** for end users.
## How to build this project manually
1. Make sure [.NET SDK 8](https://dotnet.microsoft.com/en-us/download) is installed on your machine.
2. Clone this project
3. Run the follow command under the project root dir
```sh
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.

View file

@ -12,11 +12,6 @@
<string>SOURCE_GIT_VERSION.0</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>LSEnvironment</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>CFBundleExecutable</key>
<string>SourceGit</string>
<key>CFBundleInfoDictionaryVersion</key>

View file

@ -516,27 +516,24 @@ namespace SourceGit
private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop)
{
var launchAsAskpass = Environment.GetEnvironmentVariable("SOURCEGIT_LAUNCH_AS_ASKPASS");
if (launchAsAskpass is not "TRUE")
return false;
var args = desktop.Args;
if (args == null || args.Length != 1)
return false;
if (args?.Length > 0)
{
desktop.MainWindow = new Views.Askpass(args[0]);
return true;
}
var param = args[0];
if (Directory.Exists(param))
return false;
if (!param.StartsWith("enter passphrase", StringComparison.OrdinalIgnoreCase) &&
!param.Contains(" password", StringComparison.OrdinalIgnoreCase))
return false;
desktop.MainWindow = new Views.Askpass(param);
return true;
return false;
}
private void TryLaunchedAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
{
Native.OS.SetupEnternalTools();
Models.AvatarManager.Instance.Start();
Models.AutoFetchManager.Instance.Start();
string startupRepo = null;
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))

View file

@ -40,59 +40,7 @@ namespace SourceGit.Commands
public bool Exec()
{
var start = new ProcessStartInfo();
start.FileName = Native.OS.GitExecutable;
start.Arguments = "--no-pager -c core.quotepath=off ";
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.StandardOutputEncoding = Encoding.UTF8;
start.StandardErrorEncoding = Encoding.UTF8;
// Force using this app as SSH askpass program
var selfExecFile = Process.GetCurrentProcess().MainModule!.FileName;
if (!OperatingSystem.IsLinux())
start.Environment.Add("DISPLAY", "required");
start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec`
start.Environment.Add("SSH_ASKPASS_REQUIRE", "prefer");
// If an SSH private key was provided, sets the environment.
if (!string.IsNullOrEmpty(SSHKey))
{
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -o StrictHostKeyChecking=accept-new -i '{SSHKey}'");
}
else
{
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -o StrictHostKeyChecking=accept-new");
start.Arguments += "-c credential.helper=manager ";
}
// Force using en_US.UTF-8 locale to avoid GCM crash
if (OperatingSystem.IsLinux())
start.Environment.Add("LANG", "en_US.UTF-8");
// Force using this app as git editor.
switch (Editor)
{
case EditorType.CoreEditor:
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --core-editor\" ";
break;
case EditorType.RebaseEditor:
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --rebase-message-editor\" -c sequence.editor=\"\\\"{selfExecFile}\\\" --rebase-todo-editor\" -c rebase.abbreviateCommands=true ";
break;
default:
start.Arguments += "-c core.editor=true ";
break;
}
// Append command args
start.Arguments += Args;
// Working directory
if (!string.IsNullOrEmpty(WorkingDirectory))
start.WorkingDirectory = WorkingDirectory;
var start = CreateGitStartInfo();
var errs = new List<string>();
var proc = new Process() { StartInfo = start };
var isCancelled = false;
@ -178,28 +126,15 @@ namespace SourceGit.Commands
}
return false;
}
else
{
return true;
}
return true;
}
public ReadToEndResult ReadToEnd()
{
var start = new ProcessStartInfo();
start.FileName = Native.OS.GitExecutable;
start.Arguments = "--no-pager -c core.quotepath=off " + Args;
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.StandardOutputEncoding = Encoding.UTF8;
start.StandardErrorEncoding = Encoding.UTF8;
if (!string.IsNullOrEmpty(WorkingDirectory))
start.WorkingDirectory = WorkingDirectory;
var start = CreateGitStartInfo();
var proc = new Process() { StartInfo = start };
try
{
proc.Start();
@ -227,7 +162,68 @@ namespace SourceGit.Commands
return rs;
}
protected virtual void OnReadline(string line) { }
protected virtual void OnReadline(string line)
{
// Implemented by derived class
}
private ProcessStartInfo CreateGitStartInfo()
{
var start = new ProcessStartInfo();
start.FileName = Native.OS.GitExecutable;
start.Arguments = "--no-pager -c core.quotepath=off -c credential.helper=manager ";
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.StandardOutputEncoding = Encoding.UTF8;
start.StandardErrorEncoding = Encoding.UTF8;
// Force using this app as SSH askpass program
var selfExecFile = Process.GetCurrentProcess().MainModule!.FileName;
if (!OperatingSystem.IsLinux())
start.Environment.Add("DISPLAY", "required");
start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec`
start.Environment.Add("SSH_ASKPASS_REQUIRE", "prefer");
start.Environment.Add("SOURCEGIT_LAUNCH_AS_ASKPASS", "TRUE");
// If an SSH private key was provided, sets the environment.
if (!string.IsNullOrEmpty(SSHKey))
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -o StrictHostKeyChecking=accept-new -i '{SSHKey}'");
else
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -o StrictHostKeyChecking=accept-new");
// Force using en_US.UTF-8 locale to avoid GCM crash
if (OperatingSystem.IsLinux())
start.Environment.Add("LANG", "en_US.UTF-8");
// Fix sometimes `LSEnvironment` not working on macOS
if (OperatingSystem.IsMacOS())
start.Environment.Add("PATH", "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin");
// Force using this app as git editor.
switch (Editor)
{
case EditorType.CoreEditor:
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --core-editor\" ";
break;
case EditorType.RebaseEditor:
start.Arguments += $"-c core.editor=\"\\\"{selfExecFile}\\\" --rebase-message-editor\" -c sequence.editor=\"\\\"{selfExecFile}\\\" --rebase-todo-editor\" -c rebase.abbreviateCommands=true ";
break;
default:
start.Arguments += "-c core.editor=true ";
break;
}
// Append command args
start.Arguments += Args;
// Working directory
if (!string.IsNullOrEmpty(WorkingDirectory))
start.WorkingDirectory = WorkingDirectory;
return start;
}
[GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS();

View file

@ -22,8 +22,6 @@ namespace SourceGit.Commands
Args += "--force ";
Args += remote;
Models.AutoFetchManager.Instance.MarkFetched(repo);
}
public Fetch(string repo, string remote, string localBranch, string remoteBranch, Action<string> outputHandler)

View file

@ -19,29 +19,35 @@ namespace SourceGit.Commands
public List<Models.Branch> Result()
{
Exec();
var branches = new List<Models.Branch>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return branches;
foreach (var b in _needQueryTrackStatus)
b.TrackStatus = new QueryTrackStatus(WorkingDirectory, b.Name, b.Upstream).Result();
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var b = ParseLine(line);
if (b != null)
branches.Add(b);
}
return _branches;
return branches;
}
protected override void OnReadline(string line)
private Models.Branch ParseLine(string line)
{
var parts = line.Split('$');
if (parts.Length != 5)
return;
return null;
var branch = new Models.Branch();
var refName = parts[0];
if (refName.EndsWith("/HEAD", StringComparison.Ordinal))
return;
return null;
if (refName.StartsWith(PREFIX_DETACHED_AT, StringComparison.Ordinal) || refName.StartsWith(PREFIX_DETACHED_FROM, StringComparison.Ordinal))
{
branch.IsDetachedHead = true;
}
branch.IsDetachedHead = refName.StartsWith(PREFIX_DETACHED_AT, StringComparison.Ordinal) ||
refName.StartsWith(PREFIX_DETACHED_FROM, StringComparison.Ordinal);
if (refName.StartsWith(PREFIX_LOCAL, StringComparison.Ordinal))
{
@ -53,7 +59,7 @@ namespace SourceGit.Commands
var name = refName.Substring(PREFIX_REMOTE.Length);
var shortNameIdx = name.IndexOf('/', StringComparison.Ordinal);
if (shortNameIdx < 0)
return;
return null;
branch.Remote = name.Substring(0, shortNameIdx);
branch.Name = name.Substring(branch.Remote.Length + 1);
@ -71,14 +77,11 @@ namespace SourceGit.Commands
branch.Upstream = parts[3];
if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal))
_needQueryTrackStatus.Add(branch);
branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result();
else
branch.TrackStatus = new Models.BranchTrackStatus();
_branches.Add(branch);
return branch;
}
private readonly List<Models.Branch> _branches = new List<Models.Branch>();
private List<Models.Branch> _needQueryTrackStatus = new List<Models.Branch>();
}
}

View file

@ -14,21 +14,23 @@ namespace SourceGit.Commands
_findFirstMerged = needFindHead;
}
public QueryCommits(string repo, string filter, Models.CommitSearchMethod method)
public QueryCommits(string repo, string filter, Models.CommitSearchMethod method, bool onlyCurrentBranch)
{
string search;
string search = onlyCurrentBranch ? string.Empty : "--branches --remotes ";
if (method == Models.CommitSearchMethod.ByUser)
{
search = $"-i --author=\"{filter}\" --committer=\"{filter}\"";
search += $"-i --author=\"{filter}\" --committer=\"{filter}\"";
}
else if (method == Models.CommitSearchMethod.ByFile)
{
search = $"-- \"{filter}\"";
search += $"-- \"{filter}\"";
}
else
{
var argsBuilder = new StringBuilder();
argsBuilder.Append(search);
var words = filter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var word in words)
{
@ -36,12 +38,13 @@ namespace SourceGit.Commands
argsBuilder.Append($"--grep=\"{escaped}\" ");
}
argsBuilder.Append("--all-match -i");
search = argsBuilder.ToString();
}
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 --branches --remotes " + search;
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;
_findFirstMerged = false;
}

View file

@ -14,31 +14,43 @@ namespace SourceGit.Commands
public List<Models.Tag> Result()
{
Exec();
return _loaded;
var tags = new List<Models.Tag>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return tags;
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var tag = ParseLine(line);
if (tag != null)
tags.Add(tag);
}
return tags;
}
protected override void OnReadline(string line)
private Models.Tag ParseLine(string line)
{
var subs = line.Split('$', StringSplitOptions.RemoveEmptyEntries);
if (subs.Length == 2)
{
_loaded.Add(new Models.Tag()
return new Models.Tag()
{
Name = subs[0].Substring(10),
SHA = subs[1],
});
};
}
else if (subs.Length == 3)
{
_loaded.Add(new Models.Tag()
return new Models.Tag()
{
Name = subs[0].Substring(10),
SHA = subs[2],
});
};
}
}
private readonly List<Models.Tag> _loaded = new List<Models.Tag>();
return null;
}
}
}

View file

@ -6,21 +6,35 @@ namespace SourceGit.Commands
{
public Statistics(string repo)
{
_statistics = new Models.Statistics();
WorkingDirectory = repo;
Context = repo;
Args = $"log --date-order --branches --remotes --since=\"{_statistics.Since()}\" --pretty=format:\"%ct$%an\"";
Args = $"log --date-order --branches --remotes -40000 --pretty=format:\"%ct$%aN\"";
}
public Models.Statistics Result()
{
Exec();
_statistics.Complete();
return _statistics;
var statistics = new Models.Statistics();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return statistics;
var start = 0;
var end = rs.StdOut.IndexOf('\n', start);
while (end > 0)
{
ParseLine(statistics, rs.StdOut.Substring(start, end - start));
start = end + 1;
end = rs.StdOut.IndexOf('\n', start);
}
if (start < rs.StdOut.Length)
ParseLine(statistics, rs.StdOut.Substring(start));
statistics.Complete();
return statistics;
}
protected override void OnReadline(string line)
private void ParseLine(Models.Statistics statistics, string line)
{
var dateEndIdx = line.IndexOf('$', StringComparison.Ordinal);
if (dateEndIdx == -1)
@ -28,9 +42,7 @@ namespace SourceGit.Commands
var dateStr = line.Substring(0, dateEndIdx);
if (double.TryParse(dateStr, out var date))
_statistics.AddCommit(line.Substring(dateEndIdx + 1), date);
statistics.AddCommit(line.Substring(dateEndIdx + 1), date);
}
private readonly Models.Statistics _statistics = null;
}
}

View file

@ -10,6 +10,9 @@ namespace SourceGit.Converters
public static readonly FuncValueConverter<IList, string> ToCount =
new FuncValueConverter<IList, string>(v => v == null ? " (0)" : $" ({v.Count})");
public static readonly FuncValueConverter<IList, bool> IsNullOrEmpty =
new FuncValueConverter<IList, bool>(v => v == null || v.Count == 0);
public static readonly FuncValueConverter<IList, bool> IsNotNullOrEmpty =
new FuncValueConverter<IList, bool>(v => v != null && v.Count > 0);

View file

@ -18,7 +18,7 @@ namespace SourceGit.Converters
{
if (OperatingSystem.IsWindows())
return v;
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length;
if (v.StartsWith(home, StringComparison.Ordinal))

View file

@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SourceGit.Models
{
public class AutoFetchManager
{
public static AutoFetchManager Instance
{
get
{
if (_instance == null)
_instance = new AutoFetchManager();
return _instance;
}
}
public class Job
{
public string IndexLockFile = string.Empty;
public Commands.Fetch Cmd = null;
public DateTime NextRunTimepoint = DateTime.MinValue;
}
public bool IsEnabled
{
get;
set;
} = false;
public int Interval
{
get => _interval;
set
{
_interval = Math.Max(1, value);
lock (_lock)
{
foreach (var job in _jobs)
job.Value.NextRunTimepoint = DateTime.Now.AddMinutes(_interval * 1.0);
}
}
}
private static AutoFetchManager _instance = null;
private Dictionary<string, Job> _jobs = new Dictionary<string, Job>();
private object _lock = new object();
private int _interval = 10;
public void Start()
{
Task.Run(() =>
{
while (true)
{
if (!IsEnabled)
{
Thread.Sleep(10000);
continue;
}
var now = DateTime.Now;
var uptodate = new List<Job>();
lock (_lock)
{
foreach (var job in _jobs)
{
if (job.Value.NextRunTimepoint.Subtract(now).TotalSeconds <= 0)
uptodate.Add(job.Value);
}
}
foreach (var job in uptodate)
{
if (!File.Exists(job.IndexLockFile))
{
job.Cmd.Exec();
job.NextRunTimepoint = DateTime.Now.AddMinutes(Convert.ToDouble(Interval));
}
}
Thread.Sleep(2000);
}
// ReSharper disable once FunctionNeverReturns
});
}
public void AddRepository(string repo, string gitDir)
{
var job = new Job
{
IndexLockFile = Path.Combine(gitDir, "index.lock"),
Cmd = new Commands.Fetch(repo, "--all", true, false, null) { RaiseError = false },
NextRunTimepoint = DateTime.Now.AddMinutes(Convert.ToDouble(Interval)),
};
lock (_lock)
{
_jobs[repo] = job;
}
}
public void RemoveRepository(string repo)
{
lock (_lock)
{
_jobs.Remove(repo);
}
}
public void MarkFetched(string repo)
{
lock (_lock)
{
if (_jobs.TryGetValue(repo, out var value))
value.NextRunTimepoint = DateTime.Now.AddMinutes(Interval * 1.0);
}
}
}
}

View file

@ -8,85 +8,27 @@ namespace SourceGit.Models
{
public class CommitGraph
{
public class Path
public static List<Pen> Pens { get; } = [];
public static void SetDefaultPens(double thickness = 2)
{
public List<Point> Points = new List<Point>();
public int Color = 0;
SetPens(s_defaultPenColors, thickness);
}
public class PathHelper
public static void SetPens(List<Color> colors, double thickness)
{
public string Next;
public bool IsMerged;
public double LastX;
public double LastY;
public double EndY;
public Path Path;
Pens.Clear();
public PathHelper(string next, bool isMerged, int color, Point start)
{
Next = next;
IsMerged = isMerged;
LastX = start.X;
LastY = start.Y;
EndY = LastY;
foreach (var c in colors)
Pens.Add(new Pen(c.ToUInt32(), thickness));
Path = new Path();
Path.Color = color;
Path.Points.Add(start);
}
s_penCount = colors.Count;
}
public PathHelper(string next, bool isMerged, int color, Point start, Point to)
{
Next = next;
IsMerged = isMerged;
LastX = to.X;
LastY = to.Y;
EndY = LastY;
Path = new Path();
Path.Color = color;
Path.Points.Add(start);
Path.Points.Add(to);
}
public void Add(double x, double y, double halfHeight, bool isEnd = false)
{
if (x > LastX)
{
Add(new Point(LastX, LastY));
Add(new Point(x, y - halfHeight));
if (isEnd)
Add(new Point(x, y));
}
else if (x < LastX)
{
var testY = LastY + halfHeight;
if (y > testY)
Add(new Point(LastX, testY));
if (!isEnd)
y += halfHeight;
Add(new Point(x, y));
}
else if (isEnd)
{
Add(new Point(x, y));
}
LastX = x;
LastY = y;
}
private void Add(Point p)
{
if (EndY < p.Y)
{
Path.Points.Add(p);
EndY = p.Y;
}
}
public class Path(int color)
{
public List<Point> Points { get; } = [];
public int Color { get; } = color;
}
public class Link
@ -111,82 +53,57 @@ namespace SourceGit.Models
public int Color;
}
public List<Path> Paths { get; set; } = new List<Path>();
public List<Link> Links { get; set; } = new List<Link>();
public List<Dot> Dots { get; set; } = new List<Dot>();
public static List<Pen> Pens
{
get;
private set;
} = new List<Pen>();
public static void SetDefaultPens(double thickness = 2)
{
SetPens(_defaultPenColors, thickness);
}
public static void SetPens(List<Color> colors, double thickness)
{
Pens.Clear();
foreach (var c in colors)
Pens.Add(new Pen(c.ToUInt32(), thickness));
_penCount = colors.Count;
}
public List<Path> Paths { get; } = [];
public List<Link> Links { get; } = [];
public List<Dot> Dots { get; } = [];
public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnabled)
{
double UNIT_WIDTH = 12;
double HALF_WIDTH = 6;
double UNIT_HEIGHT = 28;
double HALF_HEIGHT = 14;
double H_MARGIN = 2;
const double unitWidth = 12;
const double halfWidth = 6;
const double unitHeight = 28;
const double halfHeight = 14;
var temp = new CommitGraph();
var unsolved = new List<PathHelper>();
var mapUnsolved = new Dictionary<string, PathHelper>();
var ended = new List<PathHelper>();
var offsetY = -HALF_HEIGHT;
var offsetY = -halfHeight;
var colorIdx = 0;
foreach (var commit in commits)
{
var major = null as PathHelper;
var isMerged = commit.IsMerged;
var oldCount = unsolved.Count;
// Update current y offset
offsetY += UNIT_HEIGHT;
offsetY += unitHeight;
// Find first curves that links to this commit and marks others that links to this commit ended.
double offsetX = H_MARGIN - HALF_WIDTH;
var offsetX = 4 - halfWidth;
var maxOffsetOld = unsolved.Count > 0 ? unsolved[^1].LastX : offsetX + unitWidth;
foreach (var l in unsolved)
{
if (l.Next == commit.SHA)
if (l.Next.Equals(commit.SHA, StringComparison.Ordinal))
{
if (major == null)
{
offsetX += UNIT_WIDTH;
offsetX += unitWidth;
major = l;
if (commit.Parents.Count > 0)
{
major.Next = commit.Parents[0];
if (!mapUnsolved.ContainsKey(major.Next))
mapUnsolved.Add(major.Next, major);
major.Goto(offsetX, offsetY, halfHeight);
}
else
{
major.Next = "ENDED";
major.End(offsetX, offsetY, halfHeight);
ended.Add(l);
}
major.Add(offsetX, offsetY, HALF_HEIGHT);
}
else
{
l.End(major.LastX, offsetY, halfHeight);
ended.Add(l);
}
@ -195,17 +112,20 @@ namespace SourceGit.Models
}
else
{
if (!mapUnsolved.ContainsKey(l.Next))
mapUnsolved.Add(l.Next, l);
offsetX += UNIT_WIDTH;
l.Add(offsetX, offsetY, HALF_HEIGHT);
offsetX += unitWidth;
l.Pass(offsetX, offsetY, halfHeight);
}
}
// Remove ended curves from unsolved
foreach (var l in ended)
unsolved.Remove(l);
ended.Clear();
// Create new curve for branch head
if (major == null)
{
offsetX += UNIT_WIDTH;
offsetX += unitWidth;
if (commit.Parents.Count > 0)
{
@ -214,18 +134,13 @@ namespace SourceGit.Models
temp.Paths.Add(major.Path);
}
colorIdx = (colorIdx + 1) % _penCount;
colorIdx = (colorIdx + 1) % s_penCount;
}
// Calculate link position of this commit.
Point position = new Point(offsetX, offsetY);
int dotColor = 0;
if (major != null)
{
position = new Point(major.LastX, offsetY);
dotColor = major.Path.Color;
}
Dot anchor = new Dot() { Center = position, Color = dotColor };
var position = new Point(major?.LastX ?? offsetX, offsetY);
var dotColor = major?.Path.Color ?? 0;
var anchor = new Dot() { Center = position, Color = dotColor };
if (commit.IsCurrentHead)
anchor.Type = DotType.Head;
else if (commit.Parents.Count > 1)
@ -239,69 +154,176 @@ namespace SourceGit.Models
{
for (int j = 1; j < commit.Parents.Count; j++)
{
var parent = commit.Parents[j];
if (mapUnsolved.TryGetValue(parent, out var value))
var parentHash = commit.Parents[j];
var parent = unsolved.Find(x => x.Next.Equals(parentHash, StringComparison.Ordinal));
if (parent != null)
{
// Try to change the merge state of linked graph
var l = value;
if (isMerged)
l.IsMerged = true;
parent.IsMerged = true;
var link = new Link();
link.Start = position;
link.End = new Point(l.LastX, offsetY + HALF_HEIGHT);
link.Control = new Point(link.End.X, link.Start.Y);
link.Color = l.Path.Color;
temp.Links.Add(link);
temp.Links.Add(new Link
{
Start = position,
End = new Point(parent.LastX, offsetY + halfHeight),
Control = new Point(parent.LastX, position.Y),
Color = parent.Path.Color
});
}
else
{
offsetX += UNIT_WIDTH;
offsetX += unitWidth;
// Create new curve for parent commit that not includes before
var l = new PathHelper(parent, isMerged, colorIdx, position, new Point(offsetX, position.Y + HALF_HEIGHT));
var l = new PathHelper(parentHash, isMerged, colorIdx, position, new Point(offsetX, position.Y + halfHeight));
unsolved.Add(l);
temp.Paths.Add(l.Path);
colorIdx = (colorIdx + 1) % _penCount;
colorIdx = (colorIdx + 1) % s_penCount;
}
}
}
// Remove ended curves from unsolved
foreach (var l in ended)
{
l.Add(position.X, position.Y, HALF_HEIGHT, true);
unsolved.Remove(l);
}
// Margins & merge state (used by Views.Histories).
commit.IsMerged = isMerged;
commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH + H_MARGIN) + H_MARGIN, 0, 0, 0);
// Clean up
ended.Clear();
mapUnsolved.Clear();
commit.Margin = new Thickness(Math.Max(offsetX, maxOffsetOld) + halfWidth + 2, 0, 0, 0);
}
// Deal with curves haven't ended yet.
for (int i = 0; i < unsolved.Count; i++)
for (var i = 0; i < unsolved.Count; i++)
{
var path = unsolved[i];
var endY = (commits.Count - 0.5) * UNIT_HEIGHT;
var endY = (commits.Count - 0.5) * unitHeight;
if (path.Path.Points.Count == 1 && Math.Abs(path.Path.Points[0].Y - endY) < 0.0001)
continue;
path.Add((i + 0.5) * UNIT_WIDTH + H_MARGIN, endY + HALF_HEIGHT, HALF_HEIGHT, true);
path.End((i + 0.5) * unitWidth + 4, endY + halfHeight, halfHeight);
}
unsolved.Clear();
return temp;
}
private static int _penCount = 0;
private static readonly List<Color> _defaultPenColors = [
private class PathHelper
{
public Path Path { get; }
public string Next { get; set; }
public bool IsMerged { get; set; }
public double LastX { get; private set; }
public PathHelper(string next, bool isMerged, int color, Point start)
{
Next = next;
IsMerged = isMerged;
LastX = start.X;
_lastY = start.Y;
Path = new Path(color);
Path.Points.Add(start);
}
public PathHelper(string next, bool isMerged, int color, Point start, Point to)
{
Next = next;
IsMerged = isMerged;
LastX = to.X;
_lastY = to.Y;
Path = new Path(color);
Path.Points.Add(start);
Path.Points.Add(to);
}
/// <summary>
/// A path that just passed this row.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="halfHeight"></param>
public void Pass(double x, double y, double halfHeight)
{
if (x > LastX)
{
Add(LastX, _lastY);
Add(x, y - halfHeight);
}
else if (x < LastX)
{
Add(LastX, y - halfHeight);
y += halfHeight;
Add(x, y);
}
LastX = x;
_lastY = y;
}
/// <summary>
/// A path that has commit in this row but not ended
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="halfHeight"></param>
public void Goto(double x, double y, double halfHeight)
{
if (x > LastX)
{
Add(LastX, _lastY);
Add(x, y - halfHeight);
}
else if (x < LastX)
{
var minY = y - halfHeight;
if (minY > _lastY)
minY -= halfHeight;
Add(LastX, minY);
Add(x, y);
}
LastX = x;
_lastY = y;
}
/// <summary>
/// A path that has commit in this row and end.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="halfHeight"></param>
public void End(double x, double y, double halfHeight)
{
if (x > LastX)
{
Add(LastX, _lastY);
Add(x, y - halfHeight);
}
else if (x < LastX)
{
Add(LastX, y - halfHeight);
}
Add(x, y);
LastX = x;
_lastY = y;
}
private void Add(double x, double y)
{
if (_endY < y)
{
Path.Points.Add(new Point(x, y));
_endY = y;
}
}
private double _lastY = 0;
private double _endY = 0;
}
private static int s_penCount = 0;
private static readonly List<Color> s_defaultPenColors = [
Colors.Orange,
Colors.ForestGreen,
Colors.Gold,

16
src/Models/IRepository.cs Normal file
View file

@ -0,0 +1,16 @@
namespace SourceGit.Models
{
public interface IRepository
{
string FullPath { get; set; }
string GitDir { get; set; }
void RefreshBranches();
void RefreshWorktrees();
void RefreshTags();
void RefreshCommits();
void RefreshSubmodules();
void RefreshWorkingCopyChanges();
void RefreshStashes();
}
}

View file

@ -94,6 +94,18 @@ namespace SourceGit.Models
set;
} = new AvaloniaList<IssueTrackerRule>();
public bool EnableAutoFetch
{
get;
set;
} = false;
public int AutoFetchInterval
{
get;
set;
} = 10;
public void PushCommitMessage(string message)
{
var existIdx = CommitMessages.IndexOf(message);

View file

@ -8,6 +8,8 @@ namespace SourceGit.Models
[
new ResetMode("Soft", "Keep all changes. Stage differences", "--soft", Brushes.Green),
new ResetMode("Mixed", "Keep all changes. Unstage differences", "--mixed", Brushes.Orange),
new ResetMode("Merge", "Reset while keeping unmerged changes", "--merge", Brushes.Purple),
new ResetMode("Keep", "Reset while keeping local modifications", "--keep", Brushes.Purple),
new ResetMode("Hard", "Discard all changes", "--hard", Brushes.Red),
];

View file

@ -1,122 +1,172 @@
using System;
using System.Collections.Generic;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using SkiaSharp;
namespace SourceGit.Models
{
public class StatisticsSample(string name)
public enum StaticsticsMode
{
All,
ThisMonth,
ThisWeek,
}
public class StaticsticsAuthor(string name, int count)
{
public string Name { get; set; } = name;
public int Count { get; set; } = 0;
public int Count { get; set; } = count;
}
public class StaticsticsSample(DateTime time, int count)
{
public DateTime Time { get; set; } = time;
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<StatisticsSample> Samples { get; set; } = new List<StatisticsSample>();
public List<StatisticsSample> ByAuthor { get; set; } = new List<StatisticsSample>();
public List<StaticsticsAuthor> Authors { get; set; } = new List<StaticsticsAuthor>();
public List<ISeries> Series { get; set; } = new List<ISeries>();
public List<Axis> XAxes { get; set; } = new List<Axis>();
public List<Axis> YAxes { get; set; } = new List<Axis>();
public void AddCommit(int index, string author)
public StatisticsReport(StaticsticsMode mode, DateTime start)
{
Total++;
Samples[index].Count++;
_mode = mode;
if (_mapUsers.TryGetValue(author, out var value))
YAxes = [new Axis()
{
value.Count++;
TextSize = 10,
MinLimit = 0,
SeparatorsPaint = new SolidColorPaint(new SKColor(0x40808080)) { StrokeThickness = 1 }
}];
if (mode == StaticsticsMode.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)
{
var now = DateTime.Now;
var maxDays = DateTime.DaysInMonth(now.Year, now.Month);
for (int i = 0; i < maxDays; i++)
_mapSamples.Add(start.AddDays(i), 0);
XAxes.Add(new DateTimeAxis(TimeSpan.FromDays(1), v => $"{v:MM/dd}") { TextSize = 10 });
}
else
{
var sample = new StatisticsSample(author);
sample.Count++;
_mapUsers.Add(author, sample);
ByAuthor.Add(sample);
XAxes.Add(new DateTimeAxis(TimeSpan.FromDays(30), v => $"{v:yyyy/MM}") { TextSize = 10 });
}
}
public void AddCommit(DateTime time, string author)
{
Total++;
var normalized = DateTime.MinValue;
if (_mode == StaticsticsMode.ThisWeek || _mode == StaticsticsMode.ThisMonth)
normalized = time.Date;
else
normalized = new DateTime(time.Year, time.Month, 1).ToLocalTime();
if (_mapSamples.TryGetValue(normalized, out var vs))
_mapSamples[normalized] = vs + 1;
else
_mapSamples.Add(normalized, 1);
if (_mapUsers.TryGetValue(author, out var vu))
_mapUsers[author] = vu + 1;
else
_mapUsers.Add(author, 1);
}
public void Complete()
{
ByAuthor.Sort((l, r) => r.Count - l.Count);
var samples = new List<DateTimePoint>();
foreach (var kv in _mapSamples)
samples.Add(new DateTimePoint(kv.Key, kv.Value));
Series.Add(
new ColumnSeries<DateTimePoint>()
{
Values = samples,
Stroke = null,
Fill = null,
Padding = 1,
}
);
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();
}
private readonly Dictionary<string, StatisticsSample> _mapUsers = new Dictionary<string, StatisticsSample>();
public void ChangeColor(uint color)
{
if (Series is [ColumnSeries<DateTimePoint> series])
series.Fill = new SolidColorPaint(new SKColor(color));
}
private StaticsticsMode _mode = StaticsticsMode.All;
private Dictionary<string, int> _mapUsers = new Dictionary<string, int>();
private Dictionary<DateTime, int> _mapSamples = new Dictionary<DateTime, int>();
}
public class Statistics
{
public StatisticsReport Year { get; set; } = new StatisticsReport();
public StatisticsReport Month { get; set; } = new StatisticsReport();
public StatisticsReport Week { get; set; } = new StatisticsReport();
public StatisticsReport All { get; set; }
public StatisticsReport Month { get; set; }
public StatisticsReport Week { get; set; }
public Statistics()
{
_today = DateTime.Today;
_thisWeekStart = _today.AddSeconds(-(int)_today.DayOfWeek * 3600 * 24 - _today.Hour * 3600 - _today.Minute * 60 - _today.Second);
_thisWeekEnd = _thisWeekStart.AddDays(7);
_today = DateTime.Now.ToLocalTime().Date;
_thisWeekStart = _today.AddSeconds(-(int)_today.DayOfWeek * 3600 * 24);
_thisMonthStart = _today.AddDays(1 - _today.Day);
for (int i = 0; i < 12; i++)
Year.Samples.Add(new StatisticsSample(""));
var monthDays = DateTime.DaysInMonth(_today.Year, _today.Month);
for (int i = 0; i < monthDays; i++)
Month.Samples.Add(new StatisticsSample($"{i + 1}"));
string[] weekDayNames = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
for (int i = 0; i < weekDayNames.Length; i++)
Week.Samples.Add(new StatisticsSample(weekDayNames[i]));
}
public string Since()
{
return _today.AddMonths(-11).ToString("yyyy-MM-01 00:00:00");
All = new StatisticsReport(StaticsticsMode.All, DateTime.MinValue);
Month = new StatisticsReport(StaticsticsMode.ThisMonth, _thisMonthStart);
Week = new StatisticsReport(StaticsticsMode.ThisWeek, _thisWeekStart);
}
public void AddCommit(string author, double timestamp)
{
var time = DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime();
if (time.CompareTo(_thisWeekStart) >= 0 && time.CompareTo(_thisWeekEnd) < 0)
Week.AddCommit((int)time.DayOfWeek, author);
if (time >= _thisWeekStart)
Week.AddCommit(time, author);
if (time.Month == _today.Month)
Month.AddCommit(time.Day - 1, author);
if (time >= _thisMonthStart)
Month.AddCommit(time, author);
Year.AddCommit(time.Month - 1, author);
All.AddCommit(time, author);
}
public void Complete()
{
Year.Complete();
All.Complete();
Month.Complete();
Week.Complete();
// Year is start from 11 months ago from now.
var thisYear = _today.Year;
var start = _today.AddMonths(-11);
if (start.Month == 1)
{
for (int i = 0; i < 12; i++)
Year.Samples[i].Name = $"{thisYear}/{i + 1:00}";
}
else
{
var lastYearIdx = start.Month - 1;
var lastYearMonths = Year.Samples.GetRange(lastYearIdx, 12 - lastYearIdx);
for (int i = 0; i < lastYearMonths.Count; i++)
lastYearMonths[i].Name = $"{thisYear - 1}/{lastYearIdx + i + 1:00}";
var thisYearMonths = Year.Samples.GetRange(0, lastYearIdx);
for (int i = 0; i < thisYearMonths.Count; i++)
thisYearMonths[i].Name = $"{thisYear}/{i + 1:00}";
Year.Samples.Clear();
Year.Samples.AddRange(lastYearMonths);
Year.Samples.AddRange(thisYearMonths);
}
}
private readonly DateTime _today;
private readonly DateTime _thisMonthStart;
private readonly DateTime _thisWeekStart;
private readonly DateTime _thisWeekEnd;
}
}

View file

@ -38,7 +38,7 @@ namespace SourceGit.Models
extension = ".sh";
else if (extension == ".kt" || extension == ".kts")
extension = ".kotlin";
foreach (var grammar in s_extraGrammars)
{
if (grammar.Extension.Equals(extension, StringComparison.OrdinalIgnoreCase))
@ -87,7 +87,7 @@ namespace SourceGit.Models
public ICollection<string> GetInjections(string scopeName) => _backend.GetInjections(scopeName);
public IRawGrammar GetGrammar(string scopeName) => GrammarUtility.GetGrammar(scopeName, _backend);
public string GetScope(string filename) => GrammarUtility.GetScope(filename, _backend);
private readonly RegistryOptions _backend = new(defaultTheme);
}
@ -95,8 +95,8 @@ namespace SourceGit.Models
{
public static TextMate.Installation CreateForEditor(TextEditor editor)
{
return editor.InstallTextMate(Application.Current?.ActualThemeVariant == ThemeVariant.Dark ?
new RegistryOptionsWrapper(ThemeName.DarkPlus) :
return editor.InstallTextMate(Application.Current?.ActualThemeVariant == ThemeVariant.Dark ?
new RegistryOptionsWrapper(ThemeName.DarkPlus) :
new RegistryOptionsWrapper(ThemeName.LightPlus));
}

View file

@ -6,20 +6,6 @@ using System.Threading.Tasks;
namespace SourceGit.Models
{
public interface IRepository
{
string FullPath { get; set; }
string GitDir { get; set; }
void RefreshBranches();
void RefreshWorktrees();
void RefreshTags();
void RefreshCommits();
void RefreshSubmodules();
void RefreshWorkingCopyChanges();
void RefreshStashes();
}
public class Watcher : IDisposable
{
public Watcher(IRepository repo)

View file

@ -201,9 +201,9 @@ namespace SourceGit.Native
private void FixWindowFrameOnWin10(Window w)
{
if (w.WindowState != WindowState.Normal)
if (w.WindowState == WindowState.Maximized || w.WindowState == WindowState.FullScreen)
w.SystemDecorations = SystemDecorations.Full;
else
else if (w.WindowState == WindowState.Normal)
w.SystemDecorations = SystemDecorations.BorderOnly;
}

View file

@ -14,6 +14,7 @@
<StreamGeometry x:Key="Icons.Clean">M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z</StreamGeometry>
<StreamGeometry x:Key="Icons.Clone">M1280 704c0 141-115 256-256 256H288C129 960 0 831 0 672c0-126 80-232 192-272A327 327 0 01192 384c0-177 143-320 320-320 119 0 222 64 277 160C820 204 857 192 896 192c106 0 192 86 192 192 0 24-5 48-13 69C1192 477 1280 580 1280 704zm-493-128H656V352c0-18-14-32-32-32h-96c-18 0-32 14-32 32v224h-131c-29 0-43 34-23 55l211 211c12 12 33 12 45 0l211-211c20-20 6-55-23-55z</StreamGeometry>
<StreamGeometry x:Key="Icons.Code">M853 102H171C133 102 102 133 102 171v683C102 891 133 922 171 922h683C891 922 922 891 922 853V171C922 133 891 102 853 102zM390 600l-48 48L205 512l137-137 48 48L301 512l88 88zM465 819l-66-18L559 205l66 18L465 819zm218-171L634 600 723 512l-88-88 48-48L819 512 683 649z</StreamGeometry>
<StreamGeometry x:Key="Icons.ColorPicker">M128 854h768v86H128zM390 797c13 13 29 19 48 19s35-6 45-19l291-288c26-22 26-64 0-90L435 83l-61 61L426 192l-272 269c-22 22-22 64 0 90l237 246zm93-544 211 211-32 32H240l243-243zM707 694c0 48 38 86 86 86 48 0 86-38 86-86 0-22-10-45-26-61L794 576l-61 61c-13 13-26 35-26 58z</StreamGeometry>
<StreamGeometry x:Key="Icons.Commit">M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688</StreamGeometry>
<StreamGeometry x:Key="Icons.Compare">M645 448l64 64 220-221L704 64l-64 64 115 115H128v90h628zM375 576l-64-64-220 224L314 960l64-64-116-115H896v-90H262z</StreamGeometry>
<StreamGeometry x:Key="Icons.Conflict">M608 0q48 0 88 23t63 63 23 87v70h55q35 0 67 14t57 38 38 57 14 67V831q0 34-14 66t-38 57-57 38-67 13H426q-34 0-66-13t-57-38-38-57-14-66v-70h-56q-34 0-66-14t-57-38-38-57-13-67V174q0-47 23-87T109 23 196 0h412m175 244H426q-46 0-86 22T278 328t-26 85v348H608q47 0 86-22t63-62 25-85l1-348m-269 318q18 0 31 13t13 31-13 31-31 13-31-13-13-31 13-31 31-13m0-212q13 0 22 9t11 22v125q0 14-9 23t-22 10-23-7-11-22l-1-126q0-13 10-23t23-10z</StreamGeometry>

View file

@ -99,6 +99,7 @@
<x:String x:Key="Text.Close" xml:space="preserve">SCHLIESSEN</x:String>
<x:String x:Key="Text.CodeEditor" xml:space="preserve">Editor</x:String>
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Diesen Commit cherry-picken</x:String>
<x:String x:Key="Text.CommitCM.CherryPickMultiple" xml:space="preserve">Mehrere cherry-picken</x:String>
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Commit auschecken</x:String>
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Mit HEAD vergleichen</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Mit Worktree vergleichen</x:String>
@ -128,6 +129,7 @@
<x:String x:Key="Text.CommitDetail.Info.Parents" xml:space="preserve">VORGÄNGER</x:String>
<x:String x:Key="Text.CommitDetail.Info.Refs" xml:space="preserve">REFS</x:String>
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitDetail.Info.WebLinks" xml:space="preserve">Im Browser öffnen</x:String>
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">Commit-Nachricht</x:String>
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">Details</x:String>
<x:String x:Key="Text.Configure" xml:space="preserve">Repository Einstellungen</x:String>
@ -137,6 +139,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Email Adresse</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Email Adresse</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Remotes automatisch fetchen</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minute(n)</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">TICKETSYSTEM</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Beispiel für Github-Regel hinzufügen</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Beispiel für Jira-Regel hinzufügen</x:String>
@ -290,6 +294,7 @@
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">Sperren anzeigen</x:String>
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">Keine gesperrten Dateien</x:String>
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">Sperre</x:String>
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">Zeige nur meine Sperren</x:String>
<x:String x:Key="Text.GitLFS.Locks.Title" xml:space="preserve">LFS Sperren</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">Entsperren</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">Erzwinge entsperren</x:String>
@ -410,9 +415,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">Commit-Historie</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Längenvorgabe für Commit-Nachrichten</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">Remotes automatisch fetchen</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">Auto-Fetch Intervall</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minute(n)</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Aktiviere Auto-CRLF</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">Clone Standardordner</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">Benutzer Email</x:String>
@ -430,6 +432,10 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Installationspfad zum GPG Programm</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">Benutzer Signierungsschlüssel</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">GPG Benutzer Signierungsschlüssel</x:String>
<x:String x:Key="Text.Preference.Integration" xml:space="preserve">EINBINDUNGEN</x:String>
<x:String x:Key="Text.Preference.Shell" xml:space="preserve">SHELL/TERMINAL</x:String>
<x:String x:Key="Text.Preference.Shell.Type" xml:space="preserve">Shell/Terminal</x:String>
<x:String x:Key="Text.Preference.Shell.Path" xml:space="preserve">Pfad</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">Remote löschen</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">Ziel:</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">Worktrees löschen</x:String>
@ -486,6 +492,7 @@
<x:String x:Key="Text.RenameBranch.Name.Placeholder" xml:space="preserve">Eindeutiger Name für diesen Branch</x:String>
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">ABBRECHEN</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">Änderungen automatisch von Remote fetchen...</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">Aufräumen (GC &amp; Prune)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Führt `git gc` auf diesem Repository aus.</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Alles löschen</x:String>
@ -505,7 +512,6 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">REMOTE HINZUFÜGEN</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">KONFLIKTE BEHEBEN</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Commit suchen</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Suche über</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">Dateiname</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Commit-Nachricht</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
@ -546,6 +552,7 @@
<x:String x:Key="Text.SelfUpdate.Title" xml:space="preserve">Software Update</x:String>
<x:String x:Key="Text.SelfUpdate.UpToDate" xml:space="preserve">Es sind momentan kein Updates verfügbar.</x:String>
<x:String x:Key="Text.Squash" xml:space="preserve">Squash Commits</x:String>
<x:String x:Key="Text.Squash.Into" xml:space="preserve">In:</x:String>
<x:String x:Key="Text.SSHKey" xml:space="preserve">SSH privater Schlüssel:</x:String>
<x:String x:Key="Text.SSHKey.Placeholder" xml:space="preserve">Pfad zum privaten SSH Schlüssel</x:String>
<x:String x:Key="Text.Start" xml:space="preserve">START</x:String>
@ -567,9 +574,9 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">COMMITTER</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">MONAT</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">WOCHE</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">JAHR</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMITS: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">AUTOREN: </x:String>
<x:String x:Key="Text.Statistics.Overview" xml:space="preserve">ÜBERSICHT</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SUBMODULE</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">Submodul hinzufügen</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">Relativen Pfad kopieren</x:String>
@ -616,7 +623,6 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Template/Historie</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">STRG + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">KONFLIKTE ERKANNT</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">DATEI KONFLIKTE GELÖST</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">NICHT-VERFOLGTE DATEIEN INKLUDIEREN</x:String>

View file

@ -136,6 +136,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Email Address</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Email address</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Fetch remotes automatically</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minute(s)</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ISSUE TRACKER</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Add Sample Github Rule</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Add Sample Jira Rule</x:String>
@ -289,6 +291,7 @@
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">Show Locks</x:String>
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">No Locked Files</x:String>
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">Lock</x:String>
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">Show only my locks</x:String>
<x:String x:Key="Text.GitLFS.Locks.Title" xml:space="preserve">LFS Locks</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">Unlock</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">Force Unlock</x:String>
@ -324,6 +327,7 @@
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">REPOSITORY</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Commit staged changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Commit and push staged changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">Stage all changes and commit</x:String>
<x:String x:Key="Text.Hotkeys.Repo.DiscardSelected" xml:space="preserve">Discard selected changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">Dashboard mode (Default)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">Force to reload this repository</x:String>
@ -409,9 +413,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">History Commits</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Subject Guide Length</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">Fetch remotes automatically</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">Auto Fetch Interval</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minute(s)</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Enable Auto CRLF</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">Default Clone Dir</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">User Email</x:String>
@ -489,6 +490,7 @@
<x:String x:Key="Text.RenameBranch.Name.Placeholder" xml:space="preserve">Unique name for this branch</x:String>
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">ABORT</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">Auto fetching changes from remotes...</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">Cleanup(GC &amp; Prune)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Run `git gc` command for this repository.</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Clear all</x:String>
@ -508,11 +510,11 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">ADD REMOTE</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">RESOLVE</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Search Commit</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Search By</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">File</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Author &amp; Committer</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Current Branch</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Show Tags as Tree</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String>
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">SUBMODULES</x:String>
@ -571,9 +573,9 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">COMMITTER</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">MONTH</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">WEEK</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">YEAR</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMITS: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">AUTHORS: </x:String>
<x:String x:Key="Text.Statistics.Overview" xml:space="preserve">OVERVIEW</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SUBMODULES</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">Add Submodule</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">Copy Relative Path</x:String>
@ -620,7 +622,8 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Template/Histories</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Trigger click event</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Stage all changes and commit</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLICTS DETECTED</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">FILE CONFLICTS ARE RESOLVED</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUDE UNTRACKED FILES</x:String>

View file

@ -2,178 +2,181 @@
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://SourceGit/Resources/Locales/en_US.axaml"/>
</ResourceDictionary.MergedDictionaries>
<x:String x:Key="Text.About" xml:space="preserve">A propos</x:String>
<x:String x:Key="Text.About.Menu" xml:space="preserve">A propos de SourceGit</x:String>
<x:String x:Key="Text.About" xml:space="preserve">À propos</x:String>
<x:String x:Key="Text.About.BuildWith" xml:space="preserve">• Compilé avec </x:String>
<x:String x:Key="Text.About.Copyright" xml:space="preserve">© 2024 sourcegit-scm</x:String>
<x:String x:Key="Text.About.Editor" xml:space="preserve">• TextEditor de </x:String>
<!-- Seemingly not used yet -->
<x:String x:Key="Text.About.Fonts" xml:space="preserve">• La police Monospace vient de </x:String>
<x:String x:Key="Text.About.Fonts" xml:space="preserve">• Les polices Monospace proviennent de </x:String>
<x:String x:Key="Text.About.Menu" xml:space="preserve">À propos de SourceGit</x:String>
<x:String x:Key="Text.About.SourceCode" xml:space="preserve">• Le code source est disponible sur </x:String>
<x:String x:Key="Text.About.SubTitle" xml:space="preserve">Client Git gratuit et open source</x:String>
<x:String x:Key="Text.About.SubTitle" xml:space="preserve">Client Git Open Source et Gratuit</x:String>
<x:String x:Key="Text.AddWorktree" xml:space="preserve">Ajouter un Worktree</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout" xml:space="preserve">What to Checkout:</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branche existante</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Créer une nouvelle branche</x:String>
<x:String x:Key="Text.AddWorktree.Location" xml:space="preserve">Location:</x:String>
<x:String x:Key="Text.AddWorktree.Location" xml:space="preserve">Emplacement :</x:String>
<x:String x:Key="Text.AddWorktree.Location.Placeholder" xml:space="preserve">Chemin vers ce worktree. Relatif supporté.</x:String>
<x:String x:Key="Text.AddWorktree.Name" xml:space="preserve">Nom de branche:</x:String>
<x:String x:Key="Text.AddWorktree.Name.Placeholder" xml:space="preserve">Optionnel. Par défaut le nom du dossier de destination.</x:String>
<x:String x:Key="Text.AddWorktree.Tracking" xml:space="preserve">Track Branch:</x:String>
<x:String x:Key="Text.AddWorktree.Tracking.Toggle" xml:space="preserve">Tracking remote branch</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
<x:String x:Key="Text.AddWorktree.Name.Placeholder" xml:space="preserve">Optionnel. Nom du dossier de destination par défaut.</x:String>
<x:String x:Key="Text.AddWorktree.Tracking" xml:space="preserve">Suivre la branche :</x:String>
<x:String x:Key="Text.AddWorktree.Tracking.Toggle" xml:space="preserve">Suivi de la branche distante</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout" xml:space="preserve">What to Checkout:</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Créer une nouvelle branche</x:String>
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branche existante</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Appliquer</x:String>
<x:String x:Key="Text.Apply.Error" xml:space="preserve">Erreur</x:String>
<x:String x:Key="Text.Apply.Error.Desc" xml:space="preserve">Soulever les erreurs et refuser d'appliquer le patch</x:String>
<x:String x:Key="Text.Apply.ErrorAll" xml:space="preserve">Toutes erreurs</x:String>
<x:String x:Key="Text.Apply.ErrorAll.Desc" xml:space="preserve">Similaire à 'error', mais plus détaillé</x:String>
<x:String x:Key="Text.Apply.ErrorAll" xml:space="preserve">Toutes les erreurs</x:String>
<x:String x:Key="Text.Apply.ErrorAll.Desc" xml:space="preserve">Similaire à 'Erreur', mais plus détaillé</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">Fichier de patch :</x:String>
<x:String x:Key="Text.Apply.File.Placeholder" xml:space="preserve">Selectionner le fichier .patch à appliquer</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">Ignorer les espaces blancs</x:String>
<x:String x:Key="Text.Apply.IgnoreWS" xml:space="preserve">Ignorer les changements d'espaces blancs</x:String>
<x:String x:Key="Text.Apply.NoWarn" xml:space="preserve">Pas d'avertissement</x:String>
<x:String x:Key="Text.Apply.NoWarn.Desc" xml:space="preserve">Désactive l'avertissement des espaces blancs terminaux</x:String>
<x:String x:Key="Text.Apply.NoWarn.Desc" xml:space="preserve">Désactiver l'avertissement sur les espaces blancs terminaux</x:String>
<x:String x:Key="Text.Apply.Title" xml:space="preserve">Appliquer le patch</x:String>
<x:String x:Key="Text.Apply.Warn" xml:space="preserve">Warn</x:String>
<x:String x:Key="Text.Apply.Warn.Desc" xml:space="preserve">Affiche les avertissements Outputs warnings for a few such errors, mais applique</x:String>
<x:String x:Key="Text.Apply.WS" xml:space="preserve">Espace blanc:</x:String>
<x:String x:Key="Text.Archive" xml:space="preserve">Archive...</x:String>
<x:String x:Key="Text.Archive.File" xml:space="preserve">Sauvegarder l'archive vers :</x:String>
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">Selectionner le chemin de l'archive</x:String>
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">Révision:</x:String>
<x:String x:Key="Text.Archive.Title" xml:space="preserve">Archive</x:String>
<x:String x:Key="Text.Apply.Warn" xml:space="preserve">Avertissement</x:String>
<x:String x:Key="Text.Apply.Warn.Desc" xml:space="preserve">Affiche des avertissements pour ce type d'erreurs tout en appliquant le patch</x:String>
<x:String x:Key="Text.Apply.WS" xml:space="preserve">Espaces blancs :</x:String>
<x:String x:Key="Text.Archive" xml:space="preserve">Archiver...</x:String>
<x:String x:Key="Text.Archive.File" xml:space="preserve">Enregistrer l'archive sous :</x:String>
<x:String x:Key="Text.Archive.File.Placeholder" xml:space="preserve">Sélectionnez le chemin du fichier d'archive</x:String>
<x:String x:Key="Text.Archive.Revision" xml:space="preserve">Révision :</x:String>
<x:String x:Key="Text.Archive.Title" xml:space="preserve">Archiver</x:String>
<x:String x:Key="Text.Askpass" xml:space="preserve">SourceGit Askpass</x:String>
<x:String x:Key="Text.AssumeUnchanged" xml:space="preserve">FICHIERS PRÉSUMÉS INCHANGÉS</x:String>
<x:String x:Key="Text.AssumeUnchanged.Empty" xml:space="preserve">PAS DE FICHIERS PRÉSUMÉS INCHANGÉS</x:String>
<x:String x:Key="Text.AssumeUnchanged.Remove" xml:space="preserve">REMOVE</x:String>
<x:String x:Key="Text.AssumeUnchanged.Remove" xml:space="preserve">SUPPRIMER</x:String>
<x:String x:Key="Text.BinaryNotSupported" xml:space="preserve">FICHIER BINAIRE NON SUPPORTÉ !!!</x:String>
<x:String x:Key="Text.Blame" xml:space="preserve">Blame</x:String>
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">BLAME SUR CE FICHIER NON SUPPORTÉ !!!</x:String>
<x:String x:Key="Text.Blame" xml:space="preserve">Blâme</x:String>
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">LE BLÂME SUR CE FICHIER N'EST PAS SUPPORTÉ!!!</x:String>
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">Checkout ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.CompareWithBranch" xml:space="preserve">Comparer avec la branche</x:String>
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Comparer avec HEAD</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Comparer avec le Worktree</x:String>
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Comparer avec le worktree</x:String>
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copier le nom de la branche</x:String>
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Supprimer ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Supprimer {0} branches sélectionnées</x:String>
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Rejeter tous les changements</x:String>
<x:String x:Key="Text.BranchCM.FastForward" xml:space="preserve">Fast-Forward to ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Finish" xml:space="preserve">Git Flow - Finir ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Merge" xml:space="preserve">Merger ${0}$ dans ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Pull" xml:space="preserve">Pull ${0}$</x:String>
<x:String x:Key="Text.BranchCM.PullInto" xml:space="preserve">Pull ${0}$ dans ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Push" xml:space="preserve">Push ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">Rebase ${0}$ sur ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.FastForward" xml:space="preserve">Fast-Forward vers ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Finish" xml:space="preserve">Git Flow - Terminer ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Merge" xml:space="preserve">Fusionner ${0}$ dans ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Pull" xml:space="preserve">Tirer ${0}$</x:String>
<x:String x:Key="Text.BranchCM.PullInto" xml:space="preserve">Tirer ${0}$ dans ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Push" xml:space="preserve">Pousser ${0}$</x:String>
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">Rebaser ${0}$ sur ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Renommer ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Définir la branche de suivi</x:String>
<x:String x:Key="Text.BranchCM.UnsetUpstream" xml:space="preserve">Unset Upstream</x:String>
<x:String x:Key="Text.BranchCM.UnsetUpstream" xml:space="preserve">Ne plus suivre la branche distante</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">Comparer les branches</x:String>
<x:String x:Key="Text.Bytes" xml:space="preserve">Octets</x:String>
<x:String x:Key="Text.Cancel" xml:space="preserve">ANNULER</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Reset vers cette révision</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Reset vers la révision parente</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Réinitialiser à la révision parente</x:String>
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Réinitialiser à cette révision</x:String>
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">CHANGER LE MODE D'AFFICHAGE</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Afficher comme liste de dossiers/fichiers</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Afficher comme liste de chemins</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Afficher comme arborescence</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Avertissement: un checkout vers un commit aboutiera vers un HEAD détaché</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout ce commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit :</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branche :</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Changement locaux:</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Rejeter</x:String>
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Avertissement: un checkout vers un commit aboutiera vers un HEAD détaché</x:String>
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Changements locaux :</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Annuler</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.DoNothing" xml:space="preserve">Ne rien faire</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">Stash et Réappliquer</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-Pick</x:String>
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit(s) :</x:String>
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">Mettre en stash et réappliquer</x:String>
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branche :</x:String>
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-Pick de ce commit</x:String>
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit :</x:String>
<x:String x:Key="Text.CherryPick.CommitChanges" xml:space="preserve">Commit tous les changements</x:String>
<x:String x:Key="Text.ClearStashes" xml:space="preserve">Vider les Stashes</x:String>
<x:String x:Key="Text.ClearStashes.Message" xml:space="preserve">Voulez-vous vider tous les stashes. Êtes-vous sûr de vouloir continuer ?</x:String>
<x:String x:Key="Text.Clone" xml:space="preserve">Cloner le dépôt distant</x:String>
<x:String x:Key="Text.CherryPick.Title" xml:space="preserve">Cherry Pick</x:String>
<x:String x:Key="Text.ClearStashes" xml:space="preserve">Supprimer les stashes</x:String>
<x:String x:Key="Text.ClearStashes.Message" xml:space="preserve">Vous essayez de supprimer tous les stashes. Êtes-vous sûr de vouloir continuer ?</x:String>
<x:String x:Key="Text.Clone.AdditionalParam" xml:space="preserve">Paramètres supplémentaires :</x:String>
<x:String x:Key="Text.Clone.AdditionalParam.Placeholder" xml:space="preserve">Arguments additionnels au clônage. Optionnel.</x:String>
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">Nom local :</x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">Nom de dépôt. Optionnel.</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Dossier parent :</x:String>
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">URL du dépôt :</x:String>
<x:String x:Key="Text.Clone" xml:space="preserve">Cloner le dépôt distant</x:String>
<x:String x:Key="Text.Close" xml:space="preserve">FERMER</x:String>
<x:String x:Key="Text.CodeEditor" xml:space="preserve">Éditeur</x:String>
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Changer de commit</x:String>
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Cherry-Pick ce commit</x:String>
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout ce commit</x:String>
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Comparer avec HEAD</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Comparer avec le Worktree</x:String>
<x:String x:Key="Text.CommitCM.CopyInfo" xml:space="preserve">Copier les infos</x:String>
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Comparer avec le worktree</x:String>
<x:String x:Key="Text.CommitCM.CopyInfo" xml:space="preserve">Copier les informations</x:String>
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">Copier le SHA</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Rebase interactif de ${0}$ ici</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebase ${0}$ ici</x:String>
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">Reset ${0}$ ici</x:String>
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebaser ${0}$ ici</x:String>
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">Réinitialiser ${0}$ ici</x:String>
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Annuler le commit</x:String>
<x:String x:Key="Text.CommitCM.Reword" xml:space="preserve">Reformuler</x:String>
<x:String x:Key="Text.CommitCM.SaveAsPatch" xml:space="preserve">Sauver en tant que patch...</x:String>
<x:String x:Key="Text.CommitCM.SaveAsPatch" xml:space="preserve">Enregistrer en tant que patch...</x:String>
<x:String x:Key="Text.CommitCM.Squash" xml:space="preserve">Squash dans le parent</x:String>
<x:String x:Key="Text.CommitDetail.Changes" xml:space="preserve">CHANGEMENTS</x:String>
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Chercher des changements...</x:String>
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Rechercher les changements...</x:String>
<x:String x:Key="Text.CommitDetail.Files" xml:space="preserve">FICHIERS</x:String>
<x:String x:Key="Text.CommitDetail.Files.LFS" xml:space="preserve">Fichiers LFS</x:String>
<x:String x:Key="Text.CommitDetail.Files.LFS" xml:space="preserve">Fichier LFS</x:String>
<x:String x:Key="Text.CommitDetail.Files.Submodule" xml:space="preserve">Sous-module</x:String>
<x:String x:Key="Text.CommitDetail.Info" xml:space="preserve">INFORMATION</x:String>
<x:String x:Key="Text.CommitDetail.Info" xml:space="preserve">INFORMATIONS</x:String>
<x:String x:Key="Text.CommitDetail.Info.Author" xml:space="preserve">AUTEUR</x:String>
<x:String x:Key="Text.CommitDetail.Info.Changed" xml:space="preserve">CHANGÉ</x:String>
<x:String x:Key="Text.CommitDetail.Info.Committer" xml:space="preserve">COMMITTER</x:String>
<x:String x:Key="Text.CommitDetail.Info.ContainsIn" xml:space="preserve">Vérifier les références contenant ce commit</x:String>
<x:String x:Key="Text.CommitDetail.Info.ContainsIn.Title" xml:space="preserve">LE COMMIT EST CONTENU PAR</x:String>
<x:String x:Key="Text.CommitDetail.Info.GotoChangesPage" xml:space="preserve">Afficher seulement les 100 premiers changements. Voir tous les changements dans l'onglet CHANGEMENTS.</x:String>
<x:String x:Key="Text.CommitDetail.Info.Message" xml:space="preserve">MESSAGE</x:String>
<x:String x:Key="Text.CommitDetail.Info.Parents" xml:space="preserve">PARENTS</x:String>
<x:String x:Key="Text.CommitDetail.Info.Refs" xml:space="preserve">REFS</x:String>
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">Enter le sujet du commit</x:String>
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">Description</x:String>
<x:String x:Key="Text.Configure" xml:space="preserve">Repository Configurer</x:String>
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">Entrez le message du commit</x:String>
<x:String x:Key="Text.Configure" xml:space="preserve">Configurer le dépôt</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate" xml:space="preserve">MODÈLE DE COMMIT</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.Name" xml:space="preserve">Nom de modèle:</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.Content" xml:space="preserve">Contenu de modèle:</x:String>
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Addresse e-mail</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Addresse e-mail</x:String>
<x:String x:Key="Text.Configure.CommitMessageTemplate.Name" xml:space="preserve">Nom de modèle:</x:String>
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Adresse e-mail</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Adresse e-mail</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ISSUE TRACKER</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Add Sample Github Rule</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Add Sample Jira Rule</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Fetch les dépôts distants automatiquement</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">minute(s)</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">SUIVI DES PROBLÈMES</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Ajouter une règle d'exemple Github</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Ajouter une règle d'exemple Jira</x:String>
<x:String x:Key="Text.Configure.IssueTracker.NewRule" xml:space="preserve">Nouvelle règle</x:String>
<x:String x:Key="Text.Configure.IssueTracker.Regex" xml:space="preserve">Issue Regex Expression:</x:String>
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Nom de règle :</x:String>
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">Veuillez utiliser $1, $2 pour accéder aux valeurs des groupes regex.</x:String>
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">URL résultant:</x:String>
<!-- A vérifier -->
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">Please use $1, $2 to access regex groups values.</x:String>
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP Proxy</x:String>
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP proxy used by this repository</x:String>
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">Proxy HTTP</x:String>
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">Proxy HTTP utilisé par ce dépôt</x:String>
<x:String x:Key="Text.Configure.User" xml:space="preserve">Nom d'utilisateur</x:String>
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">Nom d'utilisateur pour ce dépôt</x:String>
<x:String x:Key="Text.Copy" xml:space="preserve">Copier</x:String>
<x:String x:Key="Text.CopyAllText" xml:space="preserve">Copier tout le texte</x:String>
<x:String x:Key="Text.CopyFileName" xml:space="preserve">Copier le nom de fichier</x:String>
<x:String x:Key="Text.CopyMessage" xml:space="preserve">COPIER LE MESSAGE</x:String>
<x:String x:Key="Text.CopyPath" xml:space="preserve">Copier le chemin</x:String>
<x:String x:Key="Text.CopyFileName" xml:space="preserve">Copier le nom de fichier</x:String>
<x:String x:Key="Text.CreateBranch" xml:space="preserve">Créer une branche...</x:String>
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Basé sur :</x:String>
<!-- A vérifier si féminin-->
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Check out the created branch</x:String>
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Passer à la branche créée</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Changements locaux :</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Rejeter</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.DoNothing" xml:space="preserve">Ne rien faire</x:String>
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">Stash &amp; Réappliquer</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nouveau nom de branche:</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Enter le nom de branche.</x:String>
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">Nom de la nouvelle branche :</x:String>
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Entrez le nom de la branche.</x:String>
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Créer une branche locale</x:String>
<x:String x:Key="Text.CreateTag" xml:space="preserve">Créer un tag...</x:String>
<x:String x:Key="Text.CreateTag.BasedOn" xml:space="preserve">Nouveau tag à :</x:String>
<x:String x:Key="Text.CreateTag.GPGSign" xml:space="preserve">Signature GPG</x:String>
<x:String x:Key="Text.CreateTag.Message" xml:space="preserve">Message de tag :</x:String>
<x:String x:Key="Text.CreateTag.Message" xml:space="preserve">Message du tag :</x:String>
<x:String x:Key="Text.CreateTag.Message.Placeholder" xml:space="preserve">Optionnel.</x:String>
<x:String x:Key="Text.CreateTag.Name" xml:space="preserve">Nom de tag :</x:String>
<x:String x:Key="Text.CreateTag.Name.Placeholder" xml:space="preserve">Format Recommendé : v1.0.0-alpha</x:String>
<x:String x:Key="Text.CreateTag.PushToAllRemotes" xml:space="preserve">Push vers tous les arpès création</x:String>
<x:String x:Key="Text.CreateTag.Name" xml:space="preserve">Nom du tag :</x:String>
<x:String x:Key="Text.CreateTag.Name.Placeholder" xml:space="preserve">Format recommandé : v1.0.0-alpha</x:String>
<x:String x:Key="Text.CreateTag.PushToAllRemotes" xml:space="preserve">Pousser sur tous les dépôts distants après création</x:String>
<x:String x:Key="Text.CreateTag.Title" xml:space="preserve">Créer un nouveau tag</x:String>
<x:String x:Key="Text.CreateTag.Type" xml:space="preserve">Kind:</x:String>
<x:String x:Key="Text.CreateTag.Type" xml:space="preserve">Type :</x:String>
<x:String x:Key="Text.CreateTag.Type.Annotated" xml:space="preserve">annoté</x:String>
<!-- A vérifier si féminin-->
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">lightweight</x:String>
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">léger</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Maintenir Ctrl pour commencer directement</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Couper</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">Supprimer la branche</x:String>
@ -187,39 +190,39 @@
<x:String x:Key="Text.DeleteRepositoryNode.Target" xml:space="preserve">Cible :</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TitleForGroup" xml:space="preserve">Confirmer la suppression du groupe</x:String>
<x:String x:Key="Text.DeleteRepositoryNode.TitleForRepository" xml:space="preserve">Confirmer la suppression du dépôt</x:String>
<x:String x:Key="Text.DeleteSubmodule" xml:space="preserve">Supprimer le Sous-module</x:String>
<x:String x:Key="Text.DeleteSubmodule" xml:space="preserve">Supprimer le sous-module</x:String>
<x:String x:Key="Text.DeleteSubmodule.Path" xml:space="preserve">Chemin du sous-module :</x:String>
<x:String x:Key="Text.DeleteTag" xml:space="preserve">Supprimer le tag</x:String>
<x:String x:Key="Text.DeleteTag.Tag" xml:space="preserve">Tag:</x:String>
<x:String x:Key="Text.DeleteTag.WithRemote" xml:space="preserve">Delete from remote repositories</x:String>
<x:String x:Key="Text.DeleteTag.Tag" xml:space="preserve">Tag :</x:String>
<x:String x:Key="Text.DeleteTag.WithRemote" xml:space="preserve">Supprimer des dépôts distants</x:String>
<x:String x:Key="Text.Diff.Binary" xml:space="preserve">DIFF BINAIRE</x:String>
<x:String x:Key="Text.Diff.Binary.New" xml:space="preserve">NOUVEAU</x:String>
<x:String x:Key="Text.Diff.Binary.Old" xml:space="preserve">ANCIEN</x:String>
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">Copier</x:String>
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">Mode de fichier changé</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS OBJECT CHANGE</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">CHANGEMENT D'OBJET LFS</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Différence suivante</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">PAS DE CHANGEMENT OU SEULEMENT EN FIN DE LIGNE</x:String>
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">Différence précédente</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Afficher les caractères invisibles</x:String>
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">Diff côte-à-côte</x:String>
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">SOUS-MODULE</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">NOUVEAU</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Permuter</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Coloration syntaxique</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Retour à la ligne</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Ouvrir dans l'outil de merge</x:String>
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Ouvrir dans l'outil de fusion</x:String>
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Réduit le nombre de ligne visibles</x:String>
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">Augmente le nombre de ligne visibles</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SÉLECTIONNER UN FICHIER POUR VOIR LES CHANGEMENTS</x:String>
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Afficher les caractères invisibles</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Permuter</x:String>
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">Ouvrir dans l'outil de merge</x:String>
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SÉLECTIONNEZ UN FICHIER POUR VOIR LES CHANGEMENTS</x:String>
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">Ouvrir dans l'outil de fusion</x:String>
<x:String x:Key="Text.Discard" xml:space="preserve">Rejeter les changements</x:String>
<x:String x:Key="Text.Discard.All" xml:space="preserve">Tous les changements dans la copie de travail.</x:String>
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Changements :</x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">{0} changements seront rejetés</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">Vous ne pouvez pas annuler cette action !!!</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">Bookmark:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Name" xml:space="preserve">Nouveau nom:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">Signet :</x:String>
<x:String x:Key="Text.EditRepositoryNode.Name" xml:space="preserve">Nouveau nom :</x:String>
<x:String x:Key="Text.EditRepositoryNode.Target" xml:space="preserve">Cible :</x:String>
<x:String x:Key="Text.EditRepositoryNode.TitleForGroup" xml:space="preserve">Éditer le groupe sélectionné</x:String>
<x:String x:Key="Text.EditRepositoryNode.TitleForRepository" xml:space="preserve">Éditer le dépôt sélectionné</x:String>
@ -227,28 +230,28 @@
<x:String x:Key="Text.Fetch" xml:space="preserve">Fetch</x:String>
<x:String x:Key="Text.Fetch.AllRemotes" xml:space="preserve">Fetch toutes les branches distantes</x:String>
<x:String x:Key="Text.Fetch.NoTags" xml:space="preserve">Fetch sans les tags</x:String>
<x:String x:Key="Text.Fetch.Prune" xml:space="preserve">Purger les branches distantes mortes</x:String>
<x:String x:Key="Text.Fetch.Prune" xml:space="preserve">Élaguer les branches mortes distantes</x:String>
<x:String x:Key="Text.Fetch.Remote" xml:space="preserve">Remote :</x:String>
<x:String x:Key="Text.Fetch.Title" xml:space="preserve">Fetch des changements distants</x:String>
<x:String x:Key="Text.Fetch.Title" xml:space="preserve">Récupérer les changements distants</x:String>
<x:String x:Key="Text.FileCM.AssumeUnchanged" xml:space="preserve">Présumer inchangé</x:String>
<x:String x:Key="Text.FileCM.Discard" xml:space="preserve">Rejeter...</x:String>
<x:String x:Key="Text.FileCM.DiscardMulti" xml:space="preserve">Rejeter {0} fichiers...</x:String>
<x:String x:Key="Text.FileCM.DiscardSelectedLines" xml:space="preserve">Rejeter les changements dans les lignes sélectionnées</x:String>
<x:String x:Key="Text.FileCM.OpenWithExternalMerger" xml:space="preserve">Ouvrir l'outil de merge externe</x:String>
<x:String x:Key="Text.FileCM.SaveAsPatch" xml:space="preserve">Savegarder le patch sous...</x:String>
<x:String x:Key="Text.FileCM.Stage" xml:space="preserve">Stage</x:String>
<!-- Est-ce un verbe ou un nom ? -->
<x:String x:Key="Text.FileCM.StageMulti" xml:space="preserve">Stage {0} files</x:String>
<x:String x:Key="Text.FileCM.StageSelectedLines" xml:space="preserve">Stage Changes in Selected Line(s)</x:String>
<x:String x:Key="Text.FileCM.OpenWithExternalMerger" xml:space="preserve">Ouvrir l'outil de fusion externe</x:String>
<x:String x:Key="Text.FileCM.SaveAsPatch" xml:space="preserve">Enregistrer en tant que patch...</x:String>
<x:String x:Key="Text.FileCM.Stage" xml:space="preserve">Indexer</x:String>
<x:String x:Key="Text.FileCM.StageMulti" xml:space="preserve">Indexer {0} fichiers</x:String>
<x:String x:Key="Text.FileCM.StageSelectedLines" xml:space="preserve">Indexer les changements dans les lignes sélectionnées</x:String>
<x:String x:Key="Text.FileCM.Stash" xml:space="preserve">Stash...</x:String>
<x:String x:Key="Text.FileCM.StashMulti" xml:space="preserve">Stash {0} fichiers...</x:String>
<x:String x:Key="Text.FileCM.Unstage" xml:space="preserve">Unstage</x:String>
<x:String x:Key="Text.FileCM.UnstageMulti" xml:space="preserve">Unstage {0} files</x:String>
<x:String x:Key="Text.FileCM.UnstageSelectedLines" xml:space="preserve">Unstage Changes in Selected Line(s)</x:String>
<x:String x:Key="Text.FileCM.UseTheirs" xml:space="preserve">Use Theirs (checkout --theirs)</x:String>
<x:String x:Key="Text.FileCM.UseMine" xml:space="preserve">Use Mine (checkout --ours)</x:String>
<x:String x:Key="Text.FileHistory" xml:space="preserve">File History</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTER</x:String>
<x:String x:Key="Text.FileCM.Unstage" xml:space="preserve">Désindexer</x:String>
<x:String x:Key="Text.FileCM.UnstageMulti" xml:space="preserve">Désindexer {0} fichiers</x:String>
<x:String x:Key="Text.FileCM.UnstageSelectedLines" xml:space="preserve">Désindexer les changements dans les lignes sélectionnées</x:String>
<x:String x:Key="Text.FileCM.UseMine" xml:space="preserve">Utiliser les miennes (checkout --ours)</x:String>
<x:String x:Key="Text.FileCM.UseTheirs" xml:space="preserve">Utiliser les leurs (checkout --theirs)</x:String>
<x:String x:Key="Text.FileHistory" xml:space="preserve">Historique du fichier</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENU</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTRER</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Development Branch:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
@ -397,9 +400,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">Historique de commits</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Guide de longueur du sujet</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">Fetch les dépôts distants automatiquement</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">Intervalle de fetch auto</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">minute(s)</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Activer auto CRLF</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">Répertoire de clônage par défaut</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">E-mail utilsateur</x:String>
@ -492,7 +492,6 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">ADD REMOTE</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">RESOLVE</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Search Commit</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Search By</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">File</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
@ -552,7 +551,6 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">COMMITTER</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">MONTH</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">WEEK</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">YEAR</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMITS: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">AUTHORS: </x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SUBMODULES</x:String>
@ -599,7 +597,6 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Modèles/Historiques</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Entrée</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLITS DÉTECTÉS</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">LES CONFLITS DE FICHIER SONT RÉSOLUS</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLURE LES FICHIERS NON-SUIVIS</x:String>

View file

@ -132,6 +132,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Endereço de Email</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Endereço de email</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Buscar remotos automaticamente</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minuto(s)</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">RASTREADOR DE PROBLEMAS</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Adicionar Regra de Exemplo do Github</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Adicionar Regra de Exemplo do Jira</x:String>
@ -391,9 +393,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">Commits do Histórico</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Comprimento do Guia de Assunto</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">Buscar remotos automaticamente</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">Intervalo de Busca Automática</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">Minuto(s)</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Habilitar Auto CRLF</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">Diretório Padrão de Clone</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">E-mail do Usuário</x:String>
@ -485,7 +484,6 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">ADICIONAR REMOTO</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">RESOLVER</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Pesquisar Commit</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Pesquisar Por</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">Arquivo</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Mensagem</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
@ -545,7 +543,6 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">COMMITTER</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">MÊS</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">SEMANA</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">ANO</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">COMMITS: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">AUTORES: </x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">SUBMÓDULOS</x:String>
@ -592,7 +589,6 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Template/Histories</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLITOS DETECTADOS</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">CONFLITOS DE ARQUIVOS RESOLVIDOS</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUIR ARQUIVOS NÃO RASTREADOS</x:String>

View file

@ -1,4 +1,7 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://SourceGit/Resources/Locales/en_US.axaml"/>
</ResourceDictionary.MergedDictionaries>
<x:String x:Key="Text.About" xml:space="preserve">О программе</x:String>
<x:String x:Key="Text.About.Menu" xml:space="preserve">О SourceGit</x:String>
<x:String x:Key="Text.About.BuildWith" xml:space="preserve">• Сборка с </x:String>
@ -118,7 +121,7 @@
<x:String x:Key="Text.CommitDetail.Info" xml:space="preserve">ИНФОРМАЦИЯ</x:String>
<x:String x:Key="Text.CommitDetail.Info.Author" xml:space="preserve">АВТОР</x:String>
<x:String x:Key="Text.CommitDetail.Info.Changed" xml:space="preserve">ИЗМЕНЁННЫЙ</x:String>
<x:String x:Key="Text.CommitDetail.Info.Committer" xml:space="preserve">ФИКСАТОР</x:String>
<x:String x:Key="Text.CommitDetail.Info.Committer" xml:space="preserve">ИСПОЛНИТЕЛЬ</x:String>
<x:String x:Key="Text.CommitDetail.Info.ContainsIn" xml:space="preserve">Проверить ссылки, содержащие эту фиксацию</x:String>
<x:String x:Key="Text.CommitDetail.Info.ContainsIn.Title" xml:space="preserve">ФИКСАЦИЯ СОДЕРЖИТСЯ В</x:String>
<x:String x:Key="Text.CommitDetail.Info.GotoChangesPage" xml:space="preserve">Отображаются только первые 100 изменений. Смотрите все изменения на вкладке ИЗМЕНЕНИЯ.</x:String>
@ -136,6 +139,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">Адрес электронной почты</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">Адрес электронной почты</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Автоматическое извлечение внешних хранилищ</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Минут(а/ы)</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ОТСЛЕЖИВАНИЕ ПРОБЛЕМ</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Добавить пример правила для Git</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Добавить пример правила Jira</x:String>
@ -176,8 +181,8 @@
<x:String x:Key="Text.CreateTag.PushToAllRemotes" xml:space="preserve">Выложить на все внешние хранилища после создания</x:String>
<x:String x:Key="Text.CreateTag.Title" xml:space="preserve">Создать новый тег</x:String>
<x:String x:Key="Text.CreateTag.Type" xml:space="preserve">Добрый:</x:String>
<x:String x:Key="Text.CreateTag.Type.Annotated" xml:space="preserve">аннотированный</x:String>
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">лёгкий</x:String>
<x:String x:Key="Text.CreateTag.Type.Annotated" xml:space="preserve">Аннотированный</x:String>
<x:String x:Key="Text.CreateTag.Type.Lightweight" xml:space="preserve">Лёгкий</x:String>
<x:String x:Key="Text.CtrlClickTip" xml:space="preserve">Удерживайте Ctrl, чтобы начать непосредственно</x:String>
<x:String x:Key="Text.Cut" xml:space="preserve">Вырезать</x:String>
<x:String x:Key="Text.DeleteBranch" xml:space="preserve">Удалить ветку</x:String>
@ -289,10 +294,11 @@
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">Показать блокировки</x:String>
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">Нет заблокированных файлов</x:String>
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">Блокировка</x:String>
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">Показывать только мои блокировки</x:String>
<x:String x:Key="Text.GitLFS.Locks.Title" xml:space="preserve">Блокировки ХБФ</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">Разблокировка</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">Принудительная разблокировка</x:String>
<x:String x:Key="Text.GitLFS.Prune" xml:space="preserve">Обрезка</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">Разблокировать</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">Принудительно разблокировать</x:String>
<x:String x:Key="Text.GitLFS.Prune" xml:space="preserve">Обрезать</x:String>
<x:String x:Key="Text.GitLFS.Prune.Tips" xml:space="preserve">Запустите `git lfs prune", чтобы удалить старые файлы ХБФ из локального хранилища</x:String>
<x:String x:Key="Text.GitLFS.Pull" xml:space="preserve">Забрать</x:String>
<x:String x:Key="Text.GitLFS.Pull.Title" xml:space="preserve">Забрать объекты ХБФ</x:String>
@ -324,6 +330,7 @@
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">ХРАНИЛИЩЕ</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">Фиксация подготовленных изменений</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">Фиксировать и выложить подготовленные изменения</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">Подготовить все изменения и зафиксировать</x:String>
<x:String x:Key="Text.Hotkeys.Repo.DiscardSelected" xml:space="preserve">Отклонить выбранные изменения</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">Режим доски (по-умолчанию)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">Принудительно перезагрузить этот репозиторий</x:String>
@ -342,10 +349,10 @@
<x:String x:Key="Text.Hunk.Discard" xml:space="preserve">Отклонить</x:String>
<x:String x:Key="Text.Init" xml:space="preserve">Инициализировать хранилище</x:String>
<x:String x:Key="Text.Init.Path" xml:space="preserve">Путь:</x:String>
<x:String x:Key="Text.InProgress.CherryPick" xml:space="preserve">Выполняется частичный забор. Нажмите 'Отбой' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Merge" xml:space="preserve">Выполняет запрос слияния. Нажмите 'Отбой' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Rebase" xml:space="preserve">Выполняется перенос. Нажмите 'Отбой' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Revert" xml:space="preserve">Выполняется возврат. Нажмите 'Отбой' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.CherryPick" xml:space="preserve">Выполняется частичный забор. Нажмите 'Отказ' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Merge" xml:space="preserve">Выполняет запрос слияния. Нажмите 'Отказ' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Rebase" xml:space="preserve">Выполняется перенос. Нажмите 'Отказ' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InProgress.Revert" xml:space="preserve">Выполняется возврат. Нажмите 'Отказ' для восстановления заголовка.</x:String>
<x:String x:Key="Text.InteractiveRebase" xml:space="preserve">Интерактивное перемещение</x:String>
<x:String x:Key="Text.InteractiveRebase.Target" xml:space="preserve">целевая ветка:</x:String>
<x:String x:Key="Text.InteractiveRebase.On" xml:space="preserve">На:</x:String>
@ -409,9 +416,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">История фиксаций</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Длина темы фиксации</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">Автоматическое извлечение внешних хранилищ</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">Интервал автоматического извлечения</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">Минут(а/ы)</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Включить автозавершение CRLF</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">Каталог клонирования по-умолчанию</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">Электроная почта пользователя</x:String>
@ -488,14 +492,15 @@
<x:String x:Key="Text.RenameBranch.Name" xml:space="preserve">Новое имя:</x:String>
<x:String x:Key="Text.RenameBranch.Name.Placeholder" xml:space="preserve">Уникальное имя для данной ветки</x:String>
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">Ветка:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">ОТБОЙ</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">Отказ</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">Автоматическое извлечение изменений с внешних хранилищ...</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">Очистка (Сбор мусора и удаление) </x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Запустить команду `git gc` для данного хранилища.</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Очистить всё</x:String>
<x:String x:Key="Text.Repository.Configure" xml:space="preserve">Настройка этого хранилища</x:String>
<x:String x:Key="Text.Repository.Continue" xml:space="preserve">ПРОДОЛЖИТЬ</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Открыть в файловом менеджере</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">поиск веток, тегов и подмодулей</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Поиск веток, тегов и подмодулей</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">ОТФИЛЬТРОВАНО ОТ:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">ЛОКАЛЬНЫЕ ВЕТКИ</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Навигация по заголовку</x:String>
@ -508,11 +513,11 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">ДОБАВИТЬ ВНЕШНЕЕ ХРАНИЛИЩЕ</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">РАЗРЕШИТЬ</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Поиск фиксации</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">Поиск по</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">Файл</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Сообщение</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Автор и фиксатор</x:String>
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Автор и исполнитель</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Текущая ветка</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Показать теги как дерево</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Статистики </x:String>
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">ПОДМОДУЛИ</x:String>
@ -523,7 +528,7 @@
<x:String x:Key="Text.Repository.Terminal" xml:space="preserve">Открыть в терминале</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">РАБОЧИЕ ДЕРЕВЬЯ</x:String>
<x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">ДОБАВИТЬ РАБОЧЕЕ ДЕРЕВО</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">ОБРЕЗКА</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">ОБРЕЗАТЬ</x:String>
<x:String x:Key="Text.RepositoryURL" xml:space="preserve">Адрес хранилища Git</x:String>
<x:String x:Key="Text.Reset" xml:space="preserve">Сбросить текущую втеку до версии</x:String>
<x:String x:Key="Text.Reset.Mode" xml:space="preserve">Режим сброса:</x:String>
@ -568,12 +573,12 @@
<x:String x:Key="Text.Stashes.Stashes" xml:space="preserve">ОТЛОЖЕННЫЕ</x:String>
<x:String x:Key="Text.Statistics" xml:space="preserve">Статистики</x:String>
<x:String x:Key="Text.Statistics.CommitAmount" xml:space="preserve">ФИКСАЦИИ</x:String>
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">ФИКСАТОРЫ</x:String>
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">ИСПОЛНИТЕЛИ</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">МЕСЯЦ</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">НЕДЕЛЯ</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">ГОД</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">ФИКСАЦИИ: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">АВТОРЫ: </x:String>
<x:String x:Key="Text.Statistics.Overview" xml:space="preserve">ОБЗОР</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">ПОДМОДУЛИ</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">Добавить подмодули</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">Копировать относительный путь</x:String>
@ -581,7 +586,7 @@
<x:String x:Key="Text.Submodule.Open" xml:space="preserve">Открыть подмодуль хранилища</x:String>
<x:String x:Key="Text.Submodule.RelativePath" xml:space="preserve">Относительный путь:</x:String>
<x:String x:Key="Text.Submodule.RelativePath.Placeholder" xml:space="preserve">Относительный каталог для хранения подмодуля.</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">удалить подмодуль</x:String>
<x:String x:Key="Text.Submodule.Remove" xml:space="preserve">Удалить подмодуль</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">ОК</x:String>
<x:String x:Key="Text.TagCM.Copy" xml:space="preserve">Копировать имя тега</x:String>
<x:String x:Key="Text.TagCM.Delete" xml:space="preserve">Удалить ${0}$...</x:String>
@ -617,10 +622,11 @@
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Изменить</x:String>
<x:String x:Key="Text.WorkingCopy.AutoStage" xml:space="preserve">Автоподготовка</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">Теперь вы можете подготовитть этот файл.</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">ФИКСАЦИЯ</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">ФИКСАЦИЯ и ОТПРАВКА</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">ЗАФИКСИРОВАТЬ</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">ЗАФИКСИРОВАТЬ и ОТПРАВИТЬ</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Шаблон/Истории</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Запуск события щелчка</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Подготовить все изменения и зафиксировать</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">ОБНАРУЖЕНЫ КОНФЛИКТЫ</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">КОНФЛИКТЫ ФАЙЛОВ РАЗРЕШЕНЫ</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ</x:String>

View file

@ -139,6 +139,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">电子邮箱</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">邮箱地址</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">GIT配置</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">启用定时自动拉取远程更新</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">分钟</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ISSUE追踪</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">新增匹配Github Issue规则</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">新增匹配Jira规则</x:String>
@ -292,6 +294,7 @@
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">显示LFS对象锁</x:String>
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">没有锁定的LFS文件</x:String>
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">锁定</x:String>
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">仅显示被我锁定的文件</x:String>
<x:String x:Key="Text.GitLFS.Locks.Title" xml:space="preserve">LFS对象锁状态</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">解锁</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">强制解锁</x:String>
@ -327,6 +330,7 @@
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">仓库页面快捷键</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">提交暂存区更改</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">提交暂存区更改并推送</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">自动暂存全部变更并提交</x:String>
<x:String x:Key="Text.Hotkeys.Repo.DiscardSelected" xml:space="preserve">丢弃选中的更改</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">切换左边栏为分支/标签等显示模式(默认)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">重新加载仓库状态</x:String>
@ -408,9 +412,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">最大历史提交数</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">SUBJECT字数检测</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT配置</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">启用定时自动拉取远程更新</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">自动拉取间隔</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">分钟</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">自动换行转换</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">默认克隆路径</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">邮箱</x:String>
@ -487,6 +488,7 @@
<x:String x:Key="Text.RenameBranch.Name.Placeholder" xml:space="preserve">新的分支名不能与现有分支名相同</x:String>
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支 </x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">终止合并</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自动拉取远端变更中...</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本仓库(GC)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作将执行`git gc`命令。</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空过滤规则</x:String>
@ -506,11 +508,11 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">添加远程</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">解决冲突</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">查找提交</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">搜索途径</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">文件</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交信息</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交指纹</x:String>
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">作者及提交者</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">仅在当前分支中查找</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以树型结构展示</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交统计</x:String>
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">子模块列表</x:String>
@ -569,9 +571,9 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">提交者</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">本月</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">本周</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">最近一年</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">提交次数: </x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">贡献者人数: </x:String>
<x:String x:Key="Text.Statistics.Overview" xml:space="preserve">总览</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">子模块</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">添加子模块</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">复制路径</x:String>
@ -618,7 +620,8 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交并推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">历史输入/模板</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">触发点击事件</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">自动暂存所有变更并提交</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">检测到冲突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">文件冲突已解决</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">显示未跟踪文件</x:String>

View file

@ -139,6 +139,8 @@
<x:String x:Key="Text.Configure.Email" xml:space="preserve">電子郵件</x:String>
<x:String x:Key="Text.Configure.Email.Placeholder" xml:space="preserve">電子郵件地址</x:String>
<x:String x:Key="Text.Configure.Git" xml:space="preserve">Git 設定</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">啟用定時自動提取 (fetch) 遠端更新</x:String>
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">分鐘</x:String>
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">Issue 追蹤</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">新增符合 GitHub Issue 規則</x:String>
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">新增符合 Jira 規則</x:String>
@ -292,6 +294,7 @@
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">顯示 LFS 物件鎖</x:String>
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">沒有鎖定的 LFS 物件</x:String>
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">鎖定</x:String>
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">僅顯示被我鎖定的檔案</x:String>
<x:String x:Key="Text.GitLFS.Locks.Title" xml:space="preserve">LFS 物件鎖</x:String>
<x:String x:Key="Text.GitLFS.Locks.Unlock" xml:space="preserve">解鎖</x:String>
<x:String x:Key="Text.GitLFS.Locks.UnlockForce" xml:space="preserve">強制解鎖</x:String>
@ -327,6 +330,7 @@
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">存放庫頁面快速鍵</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Commit" xml:space="preserve">提交暫存區變更</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitAndPush" xml:space="preserve">提交暫存區變更並推送</x:String>
<x:String x:Key="Text.Hotkeys.Repo.CommitWithAutoStage" xml:space="preserve">自動暫存全部變更並提交</x:String>
<x:String x:Key="Text.Hotkeys.Repo.DiscardSelected" xml:space="preserve">捨棄選取的變更</x:String>
<x:String x:Key="Text.Hotkeys.Repo.GoHome" xml:space="preserve">切換左邊欄為分支/標籤等顯示模式 (預設)</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">強制重新載入存放庫</x:String>
@ -412,9 +416,6 @@
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">最大歷史提交數</x:String>
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">提交標題字數偵測</x:String>
<x:String x:Key="Text.Preference.Git" xml:space="preserve">Git 設定</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetch" xml:space="preserve">啟用定時自動提取 (fetch) 遠端更新</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchInterval" xml:space="preserve">自動提取間隔</x:String>
<x:String x:Key="Text.Preference.Git.AutoFetchIntervalSuffix" xml:space="preserve">分鐘</x:String>
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">自動換行轉換</x:String>
<x:String x:Key="Text.Preference.Git.DefaultCloneDir" xml:space="preserve">預設複製 (clone) 路徑</x:String>
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">電子郵件</x:String>
@ -492,6 +493,7 @@
<x:String x:Key="Text.RenameBranch.Name.Placeholder" xml:space="preserve">新的分支名稱不能與現有分支名稱相同</x:String>
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">中止</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自動提取遠端變更中...</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本存放庫 (GC)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作將執行 `git gc` 命令。</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空篩選規則</x:String>
@ -511,11 +513,11 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">新增遠端</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">解決衝突</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">搜尋提交</x:String>
<x:String x:Key="Text.Repository.Search.By" xml:space="preserve">搜尋方式</x:String>
<x:String x:Key="Text.Repository.Search.ByFile" xml:space="preserve">檔案</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交訊息</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交編號</x:String>
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">作者及提交者</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">僅搜尋當前分支</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以樹型結構展示</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交統計</x:String>
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">子模組列表</x:String>
@ -574,9 +576,9 @@
<x:String x:Key="Text.Statistics.Committer" xml:space="preserve">提交者</x:String>
<x:String x:Key="Text.Statistics.ThisMonth" xml:space="preserve">本月</x:String>
<x:String x:Key="Text.Statistics.ThisWeek" xml:space="preserve">本週</x:String>
<x:String x:Key="Text.Statistics.MostRecentYear" xml:space="preserve">最近一年</x:String>
<x:String x:Key="Text.Statistics.TotalCommits" xml:space="preserve">提交次數:</x:String>
<x:String x:Key="Text.Statistics.TotalAuthors" xml:space="preserve">貢獻者人數:</x:String>
<x:String x:Key="Text.Statistics.Overview" xml:space="preserve">總覽</x:String>
<x:String x:Key="Text.Submodule" xml:space="preserve">子模組</x:String>
<x:String x:Key="Text.Submodule.Add" xml:space="preserve">新增子模組</x:String>
<x:String x:Key="Text.Submodule.CopyPath" xml:space="preserve">複製路徑</x:String>
@ -623,7 +625,8 @@
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提 交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交並推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">歷史輸入/範本</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">觸發點擊事件</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">自動暫存全部變更並提交</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">檢測到衝突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">檔案衝突已解決</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">顯示未追蹤檔案</x:String>

View file

@ -192,6 +192,7 @@
<Setter Property="MaxWidth" Value="1024"/>
<Setter Property="MaxHeight" Value="768"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Cursor" Value="Arrow"/>
<Setter Property="Template">
<ControlTemplate>
@ -962,12 +963,13 @@
</Style>
</Style>
<Style Selector="RadioButton.icon_button">
<Style Selector="RadioButton.switch_button">
<Setter Property="Height" Value="24"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="Transparent">
<Border CornerRadius="12" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="PART_ContentPresenter"
Margin="0"
HorizontalAlignment="Center"
@ -980,9 +982,17 @@
</ControlTemplate>
</Setter>
<Style Selector="^:checked">
<Setter Property="Background" Value="{DynamicResource Brush.Accent}"/>
</Style>
<Style Selector="^:checked Path">
<Setter Property="Fill" Value="{DynamicResource Brush.Accent}"/>
<Setter Property="Opacity" Value="0.65"/>
<Setter Property="Fill" Value="White"/>
</Style>
<Style Selector="^ TextBlock">
<Setter Property="Foreground" Value="{DynamicResource Brush.FG2}"/>
</Style>
<Style Selector="^:checked TextBlock">
<Setter Property="Foreground" Value="White"/>
</Style>
</Style>

View file

@ -45,7 +45,8 @@
<PackageReference Include="Avalonia.Diagnostics" Version="11.1.3" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc3.3" />
<PackageReference Include="TextMateSharp" Version="1.0.63" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.63" />
</ItemGroup>

View file

@ -104,6 +104,7 @@ namespace SourceGit.ViewModels
}
CallUIThread(() =>
{
_repo.MarkFetched();
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
});

View file

@ -15,10 +15,15 @@ namespace SourceGit.ViewModels
public string Name { get; private set; } = string.Empty;
public object Backend { get; private set; } = null;
public int Depth { get; set; } = 0;
public bool IsFiltered { get; set; } = false;
public bool IsSelected { get; set; } = false;
public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>();
public bool IsFiltered
{
get => _isFiltered;
set => SetProperty(ref _isFiltered, value);
}
public bool IsExpanded
{
get => _isExpanded;
@ -46,6 +51,7 @@ namespace SourceGit.ViewModels
get => Backend is Models.Branch b ? b.FriendlyName : null;
}
private bool _isFiltered = false;
private bool _isExpanded = false;
private CornerRadius _cornerRadius = new CornerRadius(4);

View file

@ -62,7 +62,16 @@ namespace SourceGit.ViewModels
rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
}
CallUIThread(() => _repo.SetWatcherEnabled(true));
CallUIThread(() =>
{
var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch);
if (b != null)
_repo.AutoAddBranchFilterPostCheckout(b);
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
});
return rs;
});
}

View file

@ -321,6 +321,9 @@ namespace SourceGit.ViewModels
menu.Items.Add(resetToFirstParent);
menu.Items.Add(new MenuItem { Header = "-" });
if (File.Exists(Path.Combine(fullPath)))
TryToAddContextMenuItemsForGitLFS(menu, change.Path);
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
@ -346,6 +349,7 @@ namespace SourceGit.ViewModels
public ContextMenu CreateRevisionFileContextMenu(Models.Object file)
{
var menu = new ContextMenu();
var fullPath = Path.Combine(_repo.FullPath, file.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
@ -385,6 +389,10 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
menu.Items.Add(explore);
menu.Items.Add(saveAs);
menu.Items.Add(new MenuItem() { Header = "-" });
var history = new MenuItem();
history.Header = App.Text("FileHistory");
history.Icon = App.CreateMenuIcon("Icons.Histories");
@ -406,6 +414,10 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
menu.Items.Add(history);
menu.Items.Add(blame);
menu.Items.Add(new MenuItem() { Header = "-" });
var resetToThisRevision = new MenuItem();
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
@ -428,6 +440,13 @@ namespace SourceGit.ViewModels
ev.Handled = true;
};
menu.Items.Add(resetToThisRevision);
menu.Items.Add(resetToFirstParent);
menu.Items.Add(new MenuItem() { Header = "-" });
if (File.Exists(Path.Combine(fullPath)))
TryToAddContextMenuItemsForGitLFS(menu, file.Path);
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
@ -446,16 +465,6 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(explore);
menu.Items.Add(saveAs);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(history);
menu.Items.Add(blame);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(resetToThisRevision);
menu.Items.Add(resetToFirstParent);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFileName);
return menu;
@ -532,6 +541,90 @@ namespace SourceGit.ViewModels
}
}
private void TryToAddContextMenuItemsForGitLFS(ContextMenu menu, string path)
{
var lfsEnabled = new Commands.LFS(_repo.FullPath).IsEnabled();
if (!lfsEnabled)
return;
var lfs = new MenuItem();
lfs.Header = App.Text("GitLFS");
lfs.Icon = App.CreateMenuIcon("Icons.LFS");
var lfsLock = new MenuItem();
lfsLock.Header = App.Text("GitLFS.Locks.Lock");
lfsLock.Icon = App.CreateMenuIcon("Icons.Lock");
lfsLock.IsEnabled = _repo.Remotes.Count > 0;
if (_repo.Remotes.Count == 1)
{
lfsLock.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(_repo.Remotes[0].Name, path));
if (succ)
App.SendNotification(_repo.FullPath, $"Lock file \"{path}\" successfully!");
e.Handled = true;
};
}
else
{
foreach (var remote in _repo.Remotes)
{
var remoteName = remote.Name;
var lockRemote = new MenuItem();
lockRemote.Header = remoteName;
lockRemote.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(remoteName, path));
if (succ)
App.SendNotification(_repo.FullPath, $"Lock file \"{path}\" successfully!");
e.Handled = true;
};
lfsLock.Items.Add(lockRemote);
}
}
lfs.Items.Add(lfsLock);
var lfsUnlock = new MenuItem();
lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock");
lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock");
lfsUnlock.IsEnabled = _repo.Remotes.Count > 0;
if (_repo.Remotes.Count == 1)
{
lfsUnlock.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(_repo.Remotes[0].Name, path, false));
if (succ)
App.SendNotification(_repo.FullPath, $"Unlock file \"{path}\" successfully!");
e.Handled = true;
};
}
else
{
foreach (var remote in _repo.Remotes)
{
var remoteName = remote.Name;
var unlockRemote = new MenuItem();
unlockRemote.Header = remoteName;
unlockRemote.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(remoteName, path, false));
if (succ)
App.SendNotification(_repo.FullPath, $"Unlock file \"{path}\" successfully!");
e.Handled = true;
};
lfsUnlock.Items.Add(unlockRemote);
}
}
lfs.Items.Add(lfsUnlock);
menu.Items.Add(lfs);
menu.Items.Add(new MenuItem() { Header = "-" });
}
[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_LFS_FORMAT();

View file

@ -126,6 +126,15 @@ namespace SourceGit.ViewModels
CallUIThread(() =>
{
if (CheckoutAfterCreated)
{
_repo.AutoAddBranchFilterPostCheckout(new Models.Branch()
{
FullName = $"refs/heads/{_name}",
Upstream = BasedOn is Models.Branch { IsLocal: false } remoteBranch ? remoteBranch.FullName : string.Empty,
});
}
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
});

View file

@ -62,7 +62,12 @@ namespace SourceGit.ViewModels
new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, Prune, NoTags, SetProgressDescription).Exec();
}
CallUIThread(() => _repo.SetWatcherEnabled(true));
CallUIThread(() =>
{
_repo.MarkFetched();
_repo.SetWatcherEnabled(true);
});
return true;
});
}

View file

@ -41,12 +41,12 @@ namespace SourceGit.ViewModels
}
}
public int ViewMode
public bool IsViewContent
{
get => _viewMode;
get => _isViewContent;
set
{
if (SetProperty(ref _viewMode, value))
if (SetProperty(ref _isViewContent, value))
RefreshViewContent();
}
}
@ -93,10 +93,10 @@ namespace SourceGit.ViewModels
return;
}
if (_viewMode == 0)
SetViewContentAsDiff();
else
if (_isViewContent)
SetViewContentAsRevisionFile();
else
SetViewContentAsDiff();
}
private void SetViewContentAsRevisionFile()
@ -197,7 +197,7 @@ namespace SourceGit.ViewModels
private bool _isLoading = true;
private List<Models.Commit> _commits = null;
private Models.Commit _selectedCommit = null;
private int _viewMode = 0;
private bool _isViewContent = false;
private object _viewContent = null;
}
}

View file

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@ -9,40 +9,49 @@ namespace SourceGit.ViewModels
{
public class LFSLocks : ObservableObject
{
public bool HasValidUserName
{
get;
private set;
} = false;
public bool IsLoading
{
get => _isLoading;
private set => SetProperty(ref _isLoading, value);
}
public bool IsEmpty
public bool ShowOnlyMyLocks
{
get => _isEmpty;
private set => SetProperty(ref _isEmpty, value);
get => _showOnlyMyLocks;
set
{
if (SetProperty(ref _showOnlyMyLocks, value))
UpdateVisibleLocks();
}
}
public AvaloniaList<Models.LFSLock> Locks
public List<Models.LFSLock> VisibleLocks
{
get;
private set;
get => _visibleLocks;
private set => SetProperty(ref _visibleLocks, value);
}
public LFSLocks(string repo, string remote)
{
_repo = repo;
_remote = remote;
Locks = new AvaloniaList<Models.LFSLock>();
_userName = new Commands.Config(repo).Get("user.name");
HasValidUserName = !string.IsNullOrEmpty(_userName);
Task.Run(() =>
{
var collect = new Commands.LFS(_repo).Locks(_remote);
_cachedLocks = new Commands.LFS(_repo).Locks(_remote);
Dispatcher.UIThread.Invoke(() =>
{
if (collect.Count > 0)
Locks.AddRange(collect);
UpdateVisibleLocks();
IsLoading = false;
IsEmpty = collect.Count == 0;
});
});
}
@ -59,17 +68,41 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(() =>
{
if (succ)
Locks.Remove(lfsLock);
{
_cachedLocks.Remove(lfsLock);
UpdateVisibleLocks();
}
IsLoading = false;
IsEmpty = Locks.Count == 0;
});
});
}
private void UpdateVisibleLocks()
{
if (!_showOnlyMyLocks)
{
VisibleLocks = _cachedLocks;
}
else
{
var visible = new List<Models.LFSLock>();
foreach (var lfsLock in _cachedLocks)
{
if (lfsLock.User == _userName)
visible.Add(lfsLock);
}
VisibleLocks = visible;
}
}
private string _repo;
private string _remote;
private bool _isLoading = true;
private bool _isEmpty = false;
private List<Models.LFSLock> _cachedLocks = [];
private List<Models.LFSLock> _visibleLocks = [];
private bool _showOnlyMyLocks = false;
private string _userName;
}
}

View file

@ -32,8 +32,8 @@ namespace SourceGit.ViewModels
{
PopupHost.Active = value;
if (!_ignoreIndexChange && value is { Data: Repository })
ActiveWorkspace.ActiveIdx = Pages.IndexOf(value);
if (!_ignoreIndexChange && value is { Data: Repository repo })
ActiveWorkspace.ActiveIdx = ActiveWorkspace.Repositories.IndexOf(repo.FullPath);
}
}
}
@ -131,10 +131,22 @@ namespace SourceGit.ViewModels
public void MoveTab(LauncherPage from, LauncherPage to)
{
_ignoreIndexChange = true;
var fromIdx = Pages.IndexOf(from);
var toIdx = Pages.IndexOf(to);
Pages.Move(fromIdx, toIdx);
ActivePage = from;
ActiveWorkspace.Repositories.Clear();
foreach (var p in Pages)
{
if (p.Data is Repository r)
ActiveWorkspace.Repositories.Add(r.FullPath);
}
ActiveWorkspace.ActiveIdx = ActiveWorkspace.Repositories.IndexOf(from.Node.Id);
_ignoreIndexChange = false;
}
public void GotoNextTab()
@ -164,8 +176,9 @@ namespace SourceGit.ViewModels
var last = Pages[0];
if (last.Data is Repository repo)
{
ActiveWorkspace.Repositories.Remove(repo.FullPath);
Models.AutoFetchManager.Instance.RemoveRepository(repo.FullPath);
ActiveWorkspace.Repositories.Clear();
ActiveWorkspace.ActiveIdx = 0;
repo.Close();
Welcome.Instance.ClearSearchFilter();
@ -180,6 +193,7 @@ namespace SourceGit.ViewModels
App.Quit(0);
}
_ignoreIndexChange = false;
return;
}
@ -213,6 +227,8 @@ namespace SourceGit.ViewModels
if (Pages.Count == 1)
return;
_ignoreIndexChange = true;
var id = ActivePage.Node.Id;
foreach (var one in Pages)
{
@ -221,12 +237,17 @@ namespace SourceGit.ViewModels
}
Pages = new AvaloniaList<LauncherPage> { ActivePage };
ActiveWorkspace.ActiveIdx = 0;
OnPropertyChanged(nameof(Pages));
_ignoreIndexChange = false;
GC.Collect();
}
public void CloseRightTabs()
{
_ignoreIndexChange = true;
var endIdx = Pages.IndexOf(ActivePage);
for (var i = Pages.Count - 1; i > endIdx; i--)
{
@ -234,6 +255,7 @@ namespace SourceGit.ViewModels
Pages.Remove(Pages[i]);
}
_ignoreIndexChange = false;
GC.Collect();
}
@ -270,8 +292,6 @@ namespace SourceGit.ViewModels
};
repo.Open();
ActiveWorkspace.AddRepository(repo.FullPath);
Models.AutoFetchManager.Instance.AddRepository(repo.FullPath, repo.GitDir);
if (page == null)
{
@ -294,6 +314,16 @@ namespace SourceGit.ViewModels
}
ActivePage = page;
ActiveWorkspace.Repositories.Clear();
foreach (var p in Pages)
{
if (p.Data is Repository r)
ActiveWorkspace.Repositories.Add(r.FullPath);
}
if (!_ignoreIndexChange)
ActiveWorkspace.ActiveIdx = ActiveWorkspace.Repositories.IndexOf(node.Id);
}
public void DispatchNotification(string pageId, string message, bool isError)
@ -490,7 +520,6 @@ namespace SourceGit.ViewModels
if (removeFromWorkspace)
ActiveWorkspace.Repositories.Remove(repo.FullPath);
Models.AutoFetchManager.Instance.RemoveRepository(repo.FullPath);
repo.Close();
}

View file

@ -44,6 +44,14 @@ namespace SourceGit.ViewModels
return _node.Id;
}
public override bool IsInProgress()
{
if (_data is Repository { IsAutoFetching: true })
return true;
return base.IsInProgress();
}
public void CopyPath()
{
if (_node.IsRepository)

View file

@ -18,7 +18,7 @@ namespace SourceGit.ViewModels
public static bool CanCreatePopup()
{
return Active != null && (Active._popup == null || !Active._popup.InProgress);
return Active?.IsInProgress() != true;
}
public static void ShowPopup(Popup popup)
@ -40,6 +40,11 @@ namespace SourceGit.ViewModels
return string.Empty;
}
public virtual bool IsInProgress()
{
return _popup is { InProgress: true };
}
public async void ProcessPopup()
{
if (_popup != null)

View file

@ -216,35 +216,6 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _gitDefaultCloneDir, value);
}
public bool GitAutoFetch
{
get => Models.AutoFetchManager.Instance.IsEnabled;
set
{
if (Models.AutoFetchManager.Instance.IsEnabled != value)
{
Models.AutoFetchManager.Instance.IsEnabled = value;
OnPropertyChanged();
}
}
}
public int? GitAutoFetchInterval
{
get => Models.AutoFetchManager.Instance.Interval;
set
{
if (value is null || value < 1)
return;
if (Models.AutoFetchManager.Instance.Interval != value)
{
Models.AutoFetchManager.Instance.Interval = (int)value;
OnPropertyChanged();
}
}
}
public int ShellOrTerminal
{
get => _shellOrTerminal;
@ -337,6 +308,12 @@ namespace SourceGit.ViewModels
}
}
public uint StatisticsSampleColor
{
get => _statisticsSampleColor;
set => SetProperty(ref _statisticsSampleColor, value);
}
public List<RepositoryNode> RepositoryNodes
{
get;
@ -621,5 +598,7 @@ namespace SourceGit.ViewModels
private int _shellOrTerminal = -1;
private int _externalMergeToolType = 0;
private string _externalMergeToolPath = string.Empty;
private uint _statisticsSampleColor = 0xFF00FF00;
}
}

View file

@ -140,6 +140,8 @@ namespace SourceGit.ViewModels
if (!rs)
return false;
_repo.MarkFetched();
// Use merge/rebase instead of pull as fetch is done manually.
if (UseRebase)
{

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Collections;
@ -209,6 +210,12 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _isSearchLoadingVisible, value);
}
public bool OnlySearchCommitsInCurrentBranch
{
get => _onlySearchCommitsInCurrentBranch;
set => SetProperty(ref _onlySearchCommitsInCurrentBranch, value);
}
public int SearchCommitFilterType
{
get => _searchCommitFilterType;
@ -323,6 +330,12 @@ namespace SourceGit.ViewModels
}
}
public bool IsAutoFetching
{
get;
private set;
}
public void Open()
{
var settingsFile = Path.Combine(_gitDir, "sourcegit.settings");
@ -359,6 +372,7 @@ namespace SourceGit.ViewModels
_inProgressContext = null;
_hasUnsolvedConflicts = false;
_autoFetchTimer = new Timer(AutoFetchImpl, null, 5000, 5000);
RefreshAll();
}
@ -377,6 +391,9 @@ namespace SourceGit.ViewModels
}
_settings = null;
_autoFetchTimer.Dispose();
_autoFetchTimer = null;
_watcher?.Dispose();
_histories.Cleanup();
_workingCopy.Cleanup();
@ -578,13 +595,13 @@ namespace SourceGit.ViewModels
visible.Add(commit);
break;
case 1:
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByUser).Result();
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByUser, _onlySearchCommitsInCurrentBranch).Result();
break;
case 2:
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByMessage).Result();
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByMessage, _onlySearchCommitsInCurrentBranch).Result();
break;
case 3:
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByFile).Result();
visible = new Commands.QueryCommits(_fullpath, _searchCommitFilter, Models.CommitSearchMethod.ByFile, _onlySearchCommitsInCurrentBranch).Result();
break;
}
@ -628,6 +645,11 @@ namespace SourceGit.ViewModels
_watcher.MarkWorkingCopyDirtyManually();
}
public void MarkFetched()
{
_lastFetchTime = DateTime.Now;
}
public void NavigateToCommit(string sha)
{
if (_histories != null)
@ -643,20 +665,49 @@ namespace SourceGit.ViewModels
NavigateToCommit(_currentBranch.Head);
}
public void UpdateFilter(string filter, bool toggle)
public void AutoAddBranchFilterPostCheckout(Models.Branch local)
{
if (_settings.Filters.Count == 0 || _settings.Filters.Contains(local.FullName))
return;
var hasLeft = false;
foreach (var b in _branches)
{
if (!b.FullName.Equals(local.FullName, StringComparison.Ordinal) &&
!b.FullName.Equals(local.Upstream, StringComparison.Ordinal) &&
!_settings.Filters.Contains(b.FullName))
{
hasLeft = true;
break;
}
}
if (!hasLeft)
_settings.Filters.Clear();
else if (string.IsNullOrEmpty(local.Upstream) || _settings.Filters.Contains(local.Upstream))
_settings.Filters.Add(local.FullName);
else
_settings.Filters.AddRange([local.FullName, local.Upstream]);
}
public void UpdateFilters(List<string> filters, bool toggle)
{
var changed = false;
if (toggle)
{
if (!_settings.Filters.Contains(filter))
foreach (var filter in filters)
{
_settings.Filters.Add(filter);
changed = true;
if (!_settings.Filters.Contains(filter))
{
_settings.Filters.Add(filter);
changed = true;
}
}
}
else
{
changed = _settings.Filters.Remove(filter);
foreach (var filter in filters)
changed |= _settings.Filters.Remove(filter);
}
if (changed)
@ -837,32 +888,14 @@ namespace SourceGit.ViewModels
var hasUnsolvedConflict = _workingCopy.SetData(changes);
var inProgress = null as InProgressContext;
var rebaseMergeFolder = Path.Combine(_gitDir, "rebase-merge");
var rebaseApplyFolder = Path.Combine(_gitDir, "rebase-apply");
if (File.Exists(Path.Combine(_gitDir, "CHERRY_PICK_HEAD")))
{
inProgress = new CherryPickInProgress(_fullpath);
}
else if (File.Exists(Path.Combine(_gitDir, "REBASE_HEAD")) && Directory.Exists(rebaseMergeFolder))
{
else if (File.Exists(Path.Combine(_gitDir, "REBASE_HEAD")) && Directory.Exists(Path.Combine(_gitDir, "rebase-merge")))
inProgress = new RebaseInProgress(this);
}
else if (File.Exists(Path.Combine(_gitDir, "REVERT_HEAD")))
{
inProgress = new RevertInProgress(_fullpath);
}
else if (File.Exists(Path.Combine(_gitDir, "MERGE_HEAD")))
{
inProgress = new MergeInProgress(_fullpath);
}
else
{
if (Directory.Exists(rebaseMergeFolder))
Directory.Delete(rebaseMergeFolder, true);
if (Directory.Exists(rebaseApplyFolder))
Directory.Delete(rebaseApplyFolder, true);
}
Dispatcher.UIThread.Invoke(() =>
{
@ -2005,6 +2038,28 @@ namespace SourceGit.ViewModels
}
}
private void AutoFetchImpl(object sender)
{
if (!_settings.EnableAutoFetch || IsAutoFetching)
return;
var lockFile = Path.Combine(_gitDir, "index.lock");
if (File.Exists(lockFile))
return;
var now = DateTime.Now;
var desire = _lastFetchTime.AddMinutes(_settings.AutoFetchInterval);
if (desire > now)
return;
IsAutoFetching = true;
Dispatcher.UIThread.Invoke(() => OnPropertyChanged(nameof(IsAutoFetching)));
new Commands.Fetch(_fullpath, "--all", true, false, null) { RaiseError = false }.Exec();
_lastFetchTime = DateTime.Now;
IsAutoFetching = false;
Dispatcher.UIThread.Invoke(() => OnPropertyChanged(nameof(IsAutoFetching)));
}
private string _fullpath = string.Empty;
private string _gitDir = string.Empty;
private Models.RepositorySettings _settings = null;
@ -2023,6 +2078,7 @@ namespace SourceGit.ViewModels
private bool _isSearchLoadingVisible = false;
private bool _isSearchCommitSuggestionOpen = false;
private int _searchCommitFilterType = 2;
private bool _onlySearchCommitsInCurrentBranch = false;
private bool _enableFirstParentInHistories = false;
private string _searchCommitFilter = string.Empty;
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
@ -2050,5 +2106,8 @@ namespace SourceGit.ViewModels
private InProgressContext _inProgressContext = null;
private bool _hasUnsolvedConflicts = false;
private Models.Commit _searchResultSelectedCommit = null;
private Timer _autoFetchTimer = null;
private DateTime _lastFetchTime = DateTime.MinValue;
}
}

View file

@ -42,6 +42,26 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _httpProxy, value);
}
public bool EnableAutoFetch
{
get => _repo.Settings.EnableAutoFetch;
set => _repo.Settings.EnableAutoFetch = value;
}
public int? AutoFetchInterval
{
get => _repo.Settings.AutoFetchInterval;
set
{
if (value is null || value < 1)
return;
var interval = (int)value;
if (_repo.Settings.AutoFetchInterval != interval)
_repo.Settings.AutoFetchInterval = interval;
}
}
public AvaloniaList<Models.CommitTemplate> CommitTemplates
{
get => _repo.Settings.CommitTemplates;

View file

@ -1,5 +1,8 @@
using System.Threading.Tasks;
using Avalonia.Media;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
@ -28,6 +31,25 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _selectedReport, value);
}
public uint SampleColor
{
get => Preference.Instance.StatisticsSampleColor;
set
{
if (value != Preference.Instance.StatisticsSampleColor)
{
Preference.Instance.StatisticsSampleColor = value;
OnPropertyChanged(nameof(SampleBrush));
_selectedReport?.ChangeColor(value);
}
}
}
public IBrush SampleBrush
{
get => new SolidColorBrush(SampleColor);
}
public Statistics(string repo)
{
Task.Run(() =>
@ -47,18 +69,15 @@ namespace SourceGit.ViewModels
if (_data == null)
return;
switch (_selectedIndex)
var report = _selectedIndex switch
{
case 0:
SelectedReport = _data.Year;
break;
case 1:
SelectedReport = _data.Month;
break;
default:
SelectedReport = _data.Week;
break;
}
0 => _data.All,
1 => _data.Month,
_ => _data.Week,
};
report.ChangeColor(SampleColor);
SelectedReport = report;
}
private bool _isLoading = true;

View file

@ -240,11 +240,6 @@ namespace SourceGit.ViewModels
_cached = changes;
_count = _cached.Count;
var unstaged = new List<Models.Change>();
var staged = new List<Models.Change>();
var selectedUnstaged = new List<Models.Change>();
var selectedStaged = new List<Models.Change>();
var lastSelectedUnstaged = new HashSet<string>();
var lastSelectedStaged = new HashSet<string>();
if (_selectedUnstaged != null && _selectedUnstaged.Count > 0)
@ -258,6 +253,8 @@ namespace SourceGit.ViewModels
lastSelectedStaged.Add(c.Path);
}
var unstaged = new List<Models.Change>();
var selectedUnstaged = new List<Models.Change>();
var hasConflict = false;
foreach (var c in changes)
{
@ -271,7 +268,8 @@ namespace SourceGit.ViewModels
}
}
staged = GetStagedChanges();
var staged = GetStagedChanges();
var selectedStaged = new List<Models.Change>();
foreach (var c in staged)
{
if (lastSelectedStaged.Contains(c.Path))
@ -418,12 +416,17 @@ namespace SourceGit.ViewModels
public void Commit()
{
DoCommit(false);
DoCommit(AutoStageBeforeCommit, false);
}
public void CommitWithAutoStage()
{
DoCommit(true, false);
}
public void CommitWithPush()
{
DoCommit(true);
DoCommit(AutoStageBeforeCommit, true);
}
public ContextMenu CreateContextMenuForUnstagedChanges()
@ -1276,7 +1279,7 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(true);
}
private void DoCommit(bool autoPush)
private void DoCommit(bool autoStage, bool autoPush)
{
if (!PopupHost.CanCreatePopup())
{
@ -1290,7 +1293,6 @@ namespace SourceGit.ViewModels
return;
}
var autoStage = AutoStageBeforeCommit;
if (!_useAmend)
{
if (autoStage)

View file

@ -51,12 +51,6 @@ namespace SourceGit.ViewModels
get => new SolidColorBrush(_color);
}
public void AddRepository(string repo)
{
if (!Repositories.Contains(repo))
Repositories.Add(repo);
}
private string _name = string.Empty;
private uint _color = 4278221015;
private bool _isActive = false;

View file

@ -33,7 +33,7 @@ namespace SourceGit.Views
return;
var view = TextView;
if (view != null && view.VisualLinesValid)
if (view is { VisualLinesValid: true })
{
var typeface = view.CreateTypeface();
var underlinePen = new Pen(Brushes.DarkOrange);
@ -142,12 +142,12 @@ namespace SourceGit.Views
return new Size(maxWidth, 0);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerPressed(e);
base.OnPointerMoved(e);
var view = TextView;
if (!e.Handled && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && view != null && view.VisualLinesValid)
if (!e.Handled && view is { VisualLinesValid: true })
{
var pos = e.GetPosition(this);
var typeface = view.CreateTypeface();
@ -158,7 +158,48 @@ namespace SourceGit.Views
continue;
var lineNumber = line.FirstDocumentLine.LineNumber;
if (lineNumber >= _editor.BlameData.LineInfos.Count)
if (lineNumber > _editor.BlameData.LineInfos.Count)
break;
var info = _editor.BlameData.LineInfos[lineNumber - 1];
var y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop) - view.VerticalOffset;
var shaLink = new FormattedText(
info.CommitSHA,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
_editor.FontSize,
Brushes.DarkOrange);
var rect = new Rect(0, y, shaLink.Width, shaLink.Height);
if (rect.Contains(pos))
{
Cursor = Cursor.Parse("Hand");
return;
}
}
}
Cursor = Cursor.Default;
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
var view = TextView;
if (!e.Handled && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && view is { VisualLinesValid: true })
{
var pos = e.GetPosition(this);
var typeface = view.CreateTypeface();
foreach (var line in view.VisualLines)
{
if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted)
continue;
var lineNumber = line.FirstDocumentLine.LineNumber;
if (lineNumber > _editor.BlameData.LineInfos.Count)
break;
var info = _editor.BlameData.LineInfos[lineNumber - 1];
@ -262,7 +303,7 @@ namespace SourceGit.Views
continue;
var lineNumber = line.FirstDocumentLine.LineNumber;
if (lineNumber >= BlameData.LineInfos.Count)
if (lineNumber > BlameData.LineInfos.Count)
break;
var info = BlameData.LineInfos[lineNumber - 1];
@ -321,7 +362,7 @@ namespace SourceGit.Views
return;
var caret = TextArea.Caret;
if (caret == null || caret.Line >= BlameData.LineInfos.Count)
if (caret == null || caret.Line > BlameData.LineInfos.Count)
return;
_highlight = BlameData.LineInfos[caret.Line - 1].CommitSHA;

View file

@ -54,10 +54,10 @@
<!-- Name -->
<TextBlock Grid.Column="1"
Text="{Binding Name}"
Classes="primary"
FontWeight="{Binding NameFontWeight}"
TextTrimming="CharacterEllipsis"/>
Text="{Binding Name}"
Classes="primary"
FontWeight="{Binding NameFontWeight}"
TextTrimming="CharacterEllipsis"/>
<!-- Tracking status -->
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"
@ -72,9 +72,9 @@
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
IsCheckedChanged="OnToggleFilter"
IsVisible="{Binding IsBranch}"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid>
</Grid>

View file

@ -428,12 +428,23 @@ namespace SourceGit.Views
}
}
private void OnToggleFilter(object sender, RoutedEventArgs e)
private void OnToggleFilterClicked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton toggle && DataContext is ViewModels.Repository repo)
if (DataContext is ViewModels.Repository repo &&
sender is ToggleButton toggle &&
toggle.DataContext is ViewModels.BranchTreeNode { Backend: Models.Branch branch } node)
{
if (toggle.DataContext is ViewModels.BranchTreeNode { Backend: Models.Branch branch })
repo.UpdateFilter(branch.FullName, toggle.IsChecked == true);
bool filtered = toggle.IsChecked == true;
List<string> filters = [branch.FullName];
if (branch.IsLocal && !string.IsNullOrEmpty(branch.Upstream))
{
filters.Add(branch.Upstream);
node.IsFiltered = filtered;
UpdateUpstreamFilterState(repo.RemoteBranchTrees, branch.Upstream, filtered);
}
repo.UpdateFilters(filters, filtered);
}
e.Handled = true;
@ -466,6 +477,23 @@ namespace SourceGit.Views
CollectBranchesInNode(outs, sub);
}
private bool UpdateUpstreamFilterState(List<ViewModels.BranchTreeNode> collection, string upstream, bool isFiltered)
{
foreach (var node in collection)
{
if (node.Backend is Models.Branch b && b.FullName == upstream)
{
node.IsFiltered = isFiltered;
return true;
}
if (node.Backend is Models.Remote r && upstream.StartsWith($"refs/remotes/{r.Name}/", StringComparison.Ordinal))
return UpdateUpstreamFilterState(node.Children, upstream, isFiltered);
}
return false;
}
private bool _disableSelectionChangingEvent = false;
}
}

View file

@ -41,7 +41,7 @@ namespace SourceGit.Views
e.Handled = true;
}
}
if (!e.Handled && e.Key != Key.Space)
base.OnKeyDown(e);
}
@ -166,10 +166,10 @@ namespace SourceGit.Views
{
if (lastUnselected == -1)
continue;
break;
}
lastUnselected = i;
}
}
@ -186,10 +186,10 @@ namespace SourceGit.Views
{
if (lastUnselected == -1)
continue;
break;
}
lastUnselected = i;
}
@ -244,7 +244,7 @@ namespace SourceGit.Views
_disableSelectionChangingEvent = true;
var selected = new List<Models.Change>();
if (sender is ListBox { SelectedItems: {} selectedItems })
if (sender is ListBox { SelectedItems: { } selectedItems })
{
foreach (var item in selectedItems)
{

View file

@ -9,9 +9,9 @@
x:DataType="vm:CommitDetail">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.CommitDetailChangesLeftWidth, Mode=TwoWay}" MinWidth="200" MaxWidth="480"/>
<ColumnDefinition Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.CommitDetailChangesLeftWidth, Mode=TwoWay}" MinWidth="200"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowDefinitions="26,*">

View file

@ -1,5 +1,4 @@
using System.ComponentModel;
using Avalonia.Controls;
namespace SourceGit.Views
@ -15,7 +14,7 @@ namespace SourceGit.Views
menu.Closing += OnContextMenuClosing; // Clear context menu because it is dynamic.
control.ContextMenu = menu;
control.ContextMenu.Open();
control.ContextMenu?.Open();
}
private static void OnContextMenuClosing(object sender, CancelEventArgs e)

View file

@ -87,6 +87,7 @@
<TextBlock Grid.Column="2"
Classes="primary"
Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
Cursor="Hand"
Background="Transparent"
Foreground="DarkOrange"
TextDecorations="Underline"
@ -115,75 +116,19 @@
IsVisible="{Binding IsLoading}"/>
<Grid Grid.Column="2" RowDefinitions="Auto,*,Auto" IsVisible="{Binding !IsLoading}">
<ListBox Grid.Row="0"
Margin="0,8"
SelectedIndex="{Binding ViewMode, Mode=TwoWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
CornerRadius="14"
Padding="3,0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<StackPanel Grid.Row="0" Margin="0,8" Height="28" HorizontalAlignment="Center" Orientation="Horizontal">
<RadioButton Classes="switch_button"
GroupName="SearchGroup"
IsChecked="{Binding !IsViewContent, Mode=OneWay}">
<TextBlock Margin="16,0" Text="{DynamicResource Text.FileHistory.FileChange}" FontWeight="Bold"/>
</RadioButton>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Height" Value="28"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="ListBoxItem Border.switcher_bg">
<Setter Property="Height" Value="22"/>
<Setter Property="CornerRadius" Value="11"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="16,0"/>
</Style>
<Style Selector="ListBoxItem:selected Border.switcher_bg">
<Setter Property="Background" Value="{DynamicResource Brush.Accent}"/>
</Style>
<Style Selector="TextBlock.view_mode_switcher">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="{DynamicResource Brush.FG2}"/>
</Style>
<Style Selector="ListBoxItem:pointerover TextBlock.view_mode_switcher">
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
</Style>
<Style Selector="ListBoxItem:selected TextBlock.view_mode_switcher">
<Setter Property="Foreground" Value="White"/>
</Style>
</ListBox.Styles>
<ListBoxItem>
<Border Classes="switcher_bg">
<TextBlock Classes="view_mode_switcher" Text="{DynamicResource Text.FileHistory.FileChange}"/>
</Border>
</ListBoxItem>
<ListBoxItem>
<Border Classes="switcher_bg">
<TextBlock Classes="view_mode_switcher" Text="{DynamicResource Text.FileHistory.FileContent}"/>
</Border>
</ListBoxItem>
</ListBox>
<RadioButton Classes="switch_button"
GroupName="SearchGroup"
IsChecked="{Binding IsViewContent, Mode=TwoWay}">
<TextBlock Margin="16,0" Text="{DynamicResource Text.FileHistory.FileContent}" FontWeight="Bold"/>
</RadioButton>
</StackPanel>
<ContentControl Grid.Row="1" Margin="4,4,8,8" Content="{Binding ViewContent}">
<ContentControl.DataTemplates>

View file

@ -78,7 +78,7 @@
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}"
Margin="0,8"/>
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="primary bold" Text="{OnPlatform Ctrl+Shift+H, macOS=⌘+⇧+H}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.GoHome}" />
@ -102,12 +102,15 @@
<TextBlock Grid.Row="7" Grid.Column="0" Classes="primary bold" Text="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"/>
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Commit}" />
<TextBlock Grid.Row="8" Grid.Column="0" Classes="primary bold" Text="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+⇧+Enter}"/>
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.CommitAndPush}" />
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.CommitWithAutoStage}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="primary bold" Text="F5"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="primary bold" Text="{OnPlatform Alt+Enter, macOS=⌥+Enter}"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.CommitAndPush}" />
<TextBlock Grid.Row="10" Grid.Column="0" Classes="primary bold" Text="F5"/>
<TextBlock Grid.Row="10" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
</Grid>
<TextBlock Text="{DynamicResource Text.Hotkeys.TextEditor}"

View file

@ -5,6 +5,7 @@
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.LFSLocks"
x:DataType="vm:LFSLocks"
@ -13,7 +14,7 @@
Title="{DynamicResource Text.GitLFS.Locks.Title}"
Width="600" Height="400"
WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,*">
<Grid RowDefinitions="Auto,Auto,*">
<!-- TitleBar -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto" Height="30" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
<Border Grid.Column="0" Grid.ColumnSpan="3"
@ -43,11 +44,25 @@
IsVisible="{OnPlatform True, macOS=False}"/>
</Grid>
<!-- Filter and Unlock All -->
<CheckBox Grid.Row="1"
Margin="8,0,0,0"
Content="{DynamicResource Text.GitLFS.Locks.OnlyMine}"
IsChecked="{Binding ShowOnlyMyLocks, Mode=TwoWay}"
VerticalAlignment="Center">
<CheckBox.IsEnabled>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="HasValidUserName"/>
<Binding Path="!IsLoading"/>
</MultiBinding>
</CheckBox.IsEnabled>
</CheckBox>
<!-- Locked Files -->
<Grid Grid.Row="1">
<ListBox Margin="8"
<Grid Grid.Row="2">
<ListBox Margin="8,0,8,8"
Background="{DynamicResource Brush.Contents}"
ItemsSource="{Binding Locks}"
ItemsSource="{Binding VisibleLocks}"
SelectionMode="Single"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
@ -89,9 +104,14 @@
</ListBox>
<!-- Empty -->
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding IsEmpty}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!IsLoading"/>
<Binding Path="VisibleLocks" Converter="{x:Static c:ListConverters.IsNullOrEmpty}"/>
</MultiBinding>
</StackPanel.IsVisible>
<Path Width="48" Height="48" Data="{StaticResource Icons.Empty}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Margin="0,16,0,0" Text="{DynamicResource Text.GitLFS.Locks.Empty}" Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel>

View file

@ -70,7 +70,7 @@
</Button>
<!-- Workspace Switcher -->
<Button Grid.Column="1" Classes="icon_button" VerticalAlignment="Bottom" Click="OnOpenWorkspaceMenu">
<Button Grid.Column="1" Classes="icon_button" VerticalAlignment="Bottom" Margin="0,0,0,1" Click="OnOpenWorkspaceMenu">
<ToolTip.Tip>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{DynamicResource Text.Workspace}" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}"/>

View file

@ -58,7 +58,7 @@ namespace SourceGit.Views
var geo = new StreamGeometry();
var angle = Math.PI / 2;
var y = height + 0.1;
var y = height + 0.5;
using (var ctx = geo.Open())
{
double x;
@ -94,17 +94,17 @@ namespace SourceGit.Views
x = drawRightX;
y = 6;
ctx.ArcTo(new Point(x, y), new Size(6, 6), angle, false, SweepDirection.Clockwise);
y = height + 0.1 - 5;
y = height + 0.5 - 5;
ctx.LineTo(new Point(x, y));
x += 5;
y = height + 0.1;
y = height + 0.5;
ctx.ArcTo(new Point(x, y), new Size(5, 5), angle, false, SweepDirection.CounterClockwise);
}
else
{
x = LauncherTabsScroller.Bounds.Right;
ctx.LineTo(new Point(x, y));
y = height + 0.1;
y = height + 0.5;
ctx.LineTo(new Point(x, y));
}
}

View file

@ -52,7 +52,7 @@
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.General}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.General.Locale}"
HorizontalAlignment="Right"
@ -99,7 +99,7 @@
Margin="0,0,16,0"/>
<Grid Grid.Row="3" Grid.Column="1" ColumnDefinitions="*,64">
<Slider Grid.Column="0"
Minimum="20000" Maximum="100000"
Minimum="5000" Maximum="100000"
TickPlacement="BottomRight" TickFrequency="5000"
IsSnapToTickEnabled="True"
VerticalAlignment="Center"
@ -116,32 +116,6 @@
Height="32"
Content="{DynamicResource Text.Preference.General.Check4UpdatesOnStartup}"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=Check4UpdatesOnStartup, Mode=TwoWay}"/>
<CheckBox Grid.Row="5" Grid.Column="1"
Content="{DynamicResource Text.Preference.Git.AutoFetch}"
IsChecked="{Binding GitAutoFetch, Mode=TwoWay}"/>
<TextBlock Grid.Row="6" Grid.Column="0"
IsVisible="{Binding GitAutoFetch}"
Text="{DynamicResource Text.Preference.Git.AutoFetchInterval}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<Grid Grid.Row="6" Grid.Column="1" Height="32" ColumnDefinitions="*,Auto" IsVisible="{Binding GitAutoFetch}">
<NumericUpDown Grid.Column="0"
Minimum="1" Maximum="60" Increment="1"
Height="28"
Padding="4"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border1}"
CornerRadius="3"
ParsingNumberStyle="Integer"
FormatString="0"
Value="{Binding GitAutoFetchInterval, Mode=TwoWay, FallbackValue=10}"/>
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Margin="5,0,0,0"
Text="{DynamicResource Text.Preference.Git.AutoFetchIntervalSuffix}" />
</Grid>
</Grid>
</TabItem>

View file

@ -19,31 +19,22 @@
<!-- Left Panel -->
<Grid Grid.Column="0" RowDefinitions="Auto,*">
<!-- Page Switcher for Left Contents (Dashboard or CommitSearch) -->
<Grid Grid.Row="0" Height="24" Margin="0,6" HorizontalAlignment="Center" ColumnDefinitions="48,1,48">
<Border Grid.Column="0" Grid.ColumnSpan="3"
Height="24"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
CornerRadius="12"/>
<RadioButton Grid.Column="0"
Classes="icon_button"
<StackPanel Grid.Row="0" Margin="0,6" Height="24" HorizontalAlignment="Center" Orientation="Horizontal">
<RadioButton Classes="switch_button"
Width="48"
GroupName="SearchGroup"
IsChecked="{Binding !IsSearching, Mode=OneWay}">
<Path Width="14" Height="14" Stretch="Fill" HorizontalAlignment="Center" Data="{StaticResource Icons.Home}"/>
<Path Width="12" Height="12" Stretch="Fill" HorizontalAlignment="Center" Data="{StaticResource Icons.Home}"/>
</RadioButton>
<Rectangle Grid.Column="1" Width="0.65" HorizontalAlignment="Center" VerticalAlignment="Stretch" Fill="{DynamicResource Brush.Border2}"/>
<RadioButton Grid.Column="2"
<RadioButton Classes="switch_button"
Width="48"
Classes="icon_button"
GroupName="SearchGroup"
IsChecked="{Binding IsSearching, Mode=TwoWay}">
<Path Width="14" Height="14" Stretch="Fill" HorizontalAlignment="Center" Data="{StaticResource Icons.Search}"/>
<Path Width="12" Height="12" Stretch="Fill" HorizontalAlignment="Center" Data="{StaticResource Icons.Search}"/>
</RadioButton>
</Grid>
</StackPanel>
<!-- Dashboard -->
<Grid Grid.Row="1" Margin="0,0,0,8" RowDefinitions="Auto,Auto,*" IsVisible="{Binding !IsSearching}">
<!-- Page Switcher for Right Panel -->
@ -138,6 +129,12 @@
Watermark="{DynamicResource Text.Repository.Filter}"
Text="{Binding Filter, Mode=TwoWay}"
VerticalContentAlignment="Center">
<TextBox.Styles>
<Style Selector="TextBox /template/ TextBlock#PART_Watermark">
<Setter Property="FontSize" Value="12"/>
</Style>
</TextBox.Styles>
<TextBox.InnerLeftContent>
<Path Width="14" Height="14"
Margin="6,0,0,0"
@ -343,15 +340,15 @@
</Grid>
<!-- Commit Search Panel -->
<Grid Grid.Row="1" RowDefinitions="24,32,*" Margin="8,0,4,8" IsVisible="{Binding IsSearching}" PropertyChanged="OnSearchCommitPanelPropertyChanged">
<Grid Grid.Row="1" RowDefinitions="Auto,26,*" Margin="8,0,4,8" IsVisible="{Binding IsSearching}" PropertyChanged="OnSearchCommitPanelPropertyChanged">
<!-- Search Input Box -->
<Grid Grid.Row="0">
<Grid Grid.Row="0" Height="26" Margin="0,4,0,0">
<TextBox x:Name="TxtSearchCommitsBox"
Height="24"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
Background="{DynamicResource Brush.Contents}"
CornerRadius="4"
CornerRadius="12"
Watermark="{DynamicResource Text.Repository.Search}"
Text="{Binding SearchCommitFilter, Mode=TwoWay}"
VerticalContentAlignment="Center"
@ -430,18 +427,11 @@
</Popup>
</Grid>
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0"
Text="{DynamicResource Text.Repository.Search.By}"
Foreground="{DynamicResource Brush.FG2}"
Margin="2,0,0,0"/>
<ComboBox Grid.Column="1"
MinHeight="24" Height="24"
Padding="8,0"
Background="{DynamicResource Brush.Contents}"
BorderBrush="{DynamicResource Brush.Border2}"
HorizontalAlignment="Right"
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ComboBox MinHeight="24" Height="24"
Padding="4,0"
Background="Transparent"
BorderThickness="0"
SelectedIndex="{Binding SearchCommitFilterType, Mode=TwoWay}">
<ComboBox.Items>
<TextBlock Text="{DynamicResource Text.Repository.Search.BySHA}" FontSize="12"/>
@ -450,9 +440,17 @@
<TextBlock Text="{DynamicResource Text.Repository.Search.ByFile}" FontSize="12"/>
</ComboBox.Items>
</ComboBox>
</Grid>
<CheckBox Height="24"
Margin="4,0,0,0"
IsChecked="{Binding OnlySearchCommitsInCurrentBranch, Mode=TwoWay}"
IsVisible="{Binding SearchCommitFilterType, Converter={x:Static c:IntConverters.IsGreaterThanZero}}">
<TextBlock Text="{DynamicResource Text.Repository.Search.InCurrentBranch}" FontSize="12"/>
</CheckBox>
</StackPanel>
<ListBox Grid.Row="2"
Margin="0,8,0,0"
ItemsSource="{Binding SearchedCommits}"
SelectionMode="Single"
SelectedItem="{Binding SearchResultSelectedCommit, Mode=TwoWay}"
@ -631,5 +629,19 @@
</ContentControl.DataTemplates>
</ContentControl>
</Grid>
<!-- Right (Auto-Fetch) -->
<Border Grid.Column="2" Margin="12" HorizontalAlignment="Center" VerticalAlignment="Center" Effect="drop-shadow(0 0 12 #A0000000)" IsVisible="{Binding IsAutoFetching}">
<Border Background="{DynamicResource Brush.Popup}" CornerRadius="6">
<StackPanel Orientation="Vertical" Margin="16,8">
<TextBlock Text="{DynamicResource Text.Repository.AutoFetching}"/>
<ProgressBar Margin="0,8,0,0"
HorizontalAlignment="Stretch"
IsIndeterminate="{Binding IsAutoFetching}"
Background="{DynamicResource Brush.FG2}" Foreground="{DynamicResource Brush.Accent}"
Minimum="0" Maximum="100"/>
</StackPanel>
</Border>
</Border>
</Grid>
</UserControl>

View file

@ -51,7 +51,7 @@
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.Git}"/>
</TabItem.Header>
<Grid Margin="16,4,16,8" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<Grid Margin="16,4,16,8" RowDefinitions="32,32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@ -106,6 +106,26 @@
<CheckBox Grid.Row="5" Grid.Column="1"
Content="{DynamicResource Text.Preference.GPG.TagEnabled}"
IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/>
<StackPanel Grid.Row="6" Grid.Column="1" Orientation="Horizontal">
<CheckBox x:Name="AutoFetchCheckBox"
Content="{DynamicResource Text.Configure.Git.AutoFetch}"
IsChecked="{Binding EnableAutoFetch, Mode=TwoWay}"/>
<NumericUpDown Minimum="1" Maximum="60" Increment="1"
Height="26" Width="110"
Margin="8,0,0,0" Padding="4"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border1}"
CornerRadius="3"
ParsingNumberStyle="Integer"
FormatString="0"
Value="{Binding AutoFetchInterval, Mode=TwoWay, FallbackValue=10}"
IsEnabled="{Binding #AutoFetchCheckBox.IsChecked}"/>
<TextBlock VerticalAlignment="Center"
Margin="5,0,0,0"
Text="{DynamicResource Text.Configure.Git.AutoFetchIntervalSuffix}" />
</StackPanel>
</Grid>
</TabItem>
@ -186,7 +206,11 @@
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Configure.CommitMessageTemplate.Content}"/>
<v:CommitMessageTextBox Height="150" Text="{Binding Content, Mode=TwoWay}"/>
<v:CommitMessageTextBox Margin="0,4,0,0" Height="100" Text="{Binding Content, Mode=TwoWay}"/>
<TextBlock Margin="0,2,0,0"
Text="You can use ${files_num}, ${files} and ${files:N} where N is the max number of file paths to output."
Foreground="{DynamicResource Brush.FG2}"
TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ContentControl.DataTemplates>

View file

@ -117,7 +117,7 @@ namespace SourceGit.Views
e.Handled = true;
}
}
if (!e.Handled)
base.OnKeyDown(e);
}

View file

@ -9,9 +9,9 @@
x:DataType="vm:CommitDetail">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.CommitDetailFilesLeftWidth, Mode=TwoWay}" MinWidth="200" MaxWidth="480"/>
<ColumnDefinition Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.CommitDetailFilesLeftWidth, Mode=TwoWay}" MinWidth="200"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<!-- File Tree -->

View file

@ -5,12 +5,13 @@
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Statistics"
x:DataType="vm:Statistics"
x:Name="ThisControl"
Title="{DynamicResource Text.Statistics}"
Width="860" Height="500"
Width="800" Height="500"
WindowStartupLocation="CenterOwner"
CanResize="False">
<Grid RowDefinitions="Auto,Auto,*">
@ -62,6 +63,7 @@
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="28"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
@ -80,7 +82,6 @@
<Setter Property="CornerRadius" Value="11"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="16,0"/>
</Style>
<Style Selector="ListBoxItem:selected Border.switcher_bg">
@ -90,6 +91,7 @@
<Style Selector="TextBlock.view_mode_switcher">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="{DynamicResource Brush.FG2}"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<Style Selector="ListBoxItem:pointerover TextBlock.view_mode_switcher">
@ -103,7 +105,7 @@
<ListBoxItem>
<Border Classes="switcher_bg">
<TextBlock Classes="view_mode_switcher" Text="{DynamicResource Text.Statistics.MostRecentYear}"/>
<TextBlock Classes="view_mode_switcher" Text="{DynamicResource Text.Statistics.Overview}"/>
</Border>
</ListBoxItem>
@ -120,15 +122,30 @@
</ListBoxItem>
</ListBox>
<!-- Color Picker -->
<Border Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Background="Transparent" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}" Width="28" Height="28" CornerRadius="14" Margin="0,0,24,0">
<Button.Flyout>
<Flyout>
<Border Padding="8">
<v:ColorPicker Value="{Binding SampleColor, Mode=TwoWay}"/>
</Border>
</Flyout>
</Button.Flyout>
<Path Width="14" Height="14" Data="{StaticResource Icons.ColorPicker}" Fill="{Binding SampleBrush}"/>
</Button>
</Border>
<!-- Contents -->
<ContentControl Grid.Row="2" Content="{Binding SelectedReport, Mode=OneWay}">
<ContentControl Grid.Row="2" Content="{Binding SelectedReport, Mode=OneWay}" Margin="8,8,8,16">
<ContentControl.DataTemplates>
<DataTemplate DataType="m:StatisticsReport">
<Grid ColumnDefinitions="256,*" Margin="8,8,8,16">
<Grid ColumnDefinitions="256,*">
<Grid Grid.Column="0" RowDefinitions="*,16">
<!-- Table By Committer -->
<ListBox Grid.Column="0"
ItemsSource="{Binding ByAuthor}"
ItemsSource="{Binding Authors}"
SelectionMode="Single"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
@ -150,7 +167,7 @@
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="m:StatisticsSample">
<DataTemplate DataType="m:StaticsticsAuthor">
<Border BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}">
<Grid ColumnDefinitions="*,100">
<Border Grid.Column="0" Padding="8,0" ClipToBounds="True">
@ -170,7 +187,7 @@
<!-- Total Committers -->
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Bottom">
<TextBlock Classes="primary" Text="{DynamicResource Text.Statistics.TotalAuthors}" FontSize="11" Foreground="{DynamicResource Brush.FG2}"/>
<TextBlock Classes="primary" Text="{Binding ByAuthor.Count}" FontSize="11" Margin="4,0,0,0"/>
<TextBlock Classes="primary" Text="{Binding Authors.Count}" FontSize="11" Margin="4,0,0,0"/>
</StackPanel>
<!-- Total Commits -->
@ -182,12 +199,12 @@
</Grid>
<!-- Graph -->
<v:Chart Grid.Column="1"
Margin="16"
LabelBrush="{DynamicResource Brush.FG1}"
LineBrush="{DynamicResource Brush.Border2}"
ShapeBrush="{DynamicResource Brush.Accent}"
Samples="{Binding Samples}"/>
<lvc:CartesianChart Grid.Column="1"
Margin="0"
Padding="0"
Series="{Binding Series}"
XAxes="{Binding XAxes}" YAxes="{Binding YAxes}"
ZoomMode="X"/>
</Grid>
</DataTemplate>
</ContentControl.DataTemplates>

View file

@ -1,220 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;
namespace SourceGit.Views
{
public class Chart : Control
{
public static readonly StyledProperty<IBrush> LabelBrushProperty =
AvaloniaProperty.Register<Chart, IBrush>(nameof(LabelBrush), Brushes.Black);
public IBrush LabelBrush
{
get => GetValue(LabelBrushProperty);
set => SetValue(LabelBrushProperty, value);
}
public static readonly StyledProperty<IBrush> LineBrushProperty =
AvaloniaProperty.Register<Chart, IBrush>(nameof(LineBrush), Brushes.Gray);
public IBrush LineBrush
{
get => GetValue(LineBrushProperty);
set => SetValue(LineBrushProperty, value);
}
public static readonly StyledProperty<IBrush> ShapeBrushProperty =
AvaloniaProperty.Register<Chart, IBrush>(nameof(ShapeBrush), Brushes.Gray);
public IBrush ShapeBrush
{
get => GetValue(ShapeBrushProperty);
set => SetValue(ShapeBrushProperty, value);
}
public static readonly StyledProperty<List<Models.StatisticsSample>> SamplesProperty =
AvaloniaProperty.Register<Chart, List<Models.StatisticsSample>>(nameof(Samples));
public List<Models.StatisticsSample> Samples
{
get => GetValue(SamplesProperty);
set => SetValue(SamplesProperty, value);
}
static Chart()
{
SamplesProperty.Changed.AddClassHandler<Chart>((c, _) =>
{
c._hitBoxes.Clear();
c._lastHitIdx = -1;
c.InvalidateMeasure();
});
}
public override void Render(DrawingContext context)
{
if (Samples == null || Bounds.Width == 0)
return;
var samples = Samples;
int maxV = 0;
foreach (var s in samples)
{
if (maxV < s.Count)
maxV = s.Count;
}
if (maxV < 5)
maxV = 5;
else if (maxV < 10)
maxV = 10;
else if (maxV < 50)
maxV = 50;
else if (maxV < 100)
maxV = 100;
else if (maxV < 200)
maxV = 200;
else if (maxV < 500)
maxV = 500;
else
maxV = (int)Math.Ceiling(maxV / 500.0) * 500;
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
var pen = new Pen(LineBrush);
var width = Bounds.Width;
var height = Bounds.Height;
// Draw coordinate
var maxLabel = new FormattedText($"{maxV}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 12.0, LabelBrush);
var horizonStart = maxLabel.Width + 8;
var labelHeight = maxLabel.Height;
var bg = this.FindResource("Brush.Contents") as IBrush;
context.DrawText(maxLabel, new Point(0, -maxLabel.Height * 0.5));
context.DrawRectangle(bg, pen, new Rect(horizonStart, 0, width - horizonStart, height - labelHeight));
if (samples.Count == 0)
return;
// Draw horizontal lines
var stepX = (width - horizonStart) / samples.Count;
var stepV = (height - labelHeight) / 5;
var labelStepV = maxV / 5;
var gridPen = new Pen(LineBrush, 1, new DashStyle() { Dashes = [2, 2, 0, 2], Offset = 1 });
for (int i = 1; i < 5; i++)
{
var vLabel = new FormattedText(
$"{maxV - i * labelStepV}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
12.0,
LabelBrush);
var dashHeight = i * stepV;
var vy = Math.Max(0, dashHeight - vLabel.Height * 0.5);
using (context.PushOpacity(.1))
{
context.DrawLine(gridPen, new Point(horizonStart + 1, dashHeight), new Point(width, dashHeight));
}
context.DrawText(vLabel, new Point(horizonStart - vLabel.Width - 8, vy));
}
// Calculate hit boxes
if (_hitBoxes.Count == 0)
{
var shapeWidth = Math.Min(32, stepX - 4);
for (int i = 0; i < samples.Count; i++)
{
var h = samples[i].Count * (height - labelHeight) / maxV;
var x = horizonStart + 1 + stepX * i + (stepX - shapeWidth) * 0.5;
var y = height - labelHeight - h;
_hitBoxes.Add(new Rect(x, y, shapeWidth, h - 1));
}
}
// Draw shapes
for (int i = 0; i < samples.Count; i++)
{
var hLabel = new FormattedText(
samples[i].Name,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
10.0,
LabelBrush);
var rect = _hitBoxes[i];
var xLabel = rect.X - (hLabel.Width - rect.Width) * 0.5;
var yLabel = height - labelHeight + 4;
context.DrawRectangle(ShapeBrush, null, rect);
var test = (stepX - 4) / hLabel.Width;
if (test < 1.0)
{
var matrix = Matrix.CreateRotation(Math.Acos(test)) * Matrix.CreateTranslation(xLabel + hLabel.Width * 0.5, yLabel);
using (context.PushTransform(matrix))
context.DrawText(hLabel, new Point(0, 0));
}
else
{
context.DrawText(hLabel, new Point(xLabel, yLabel));
}
}
// Draw labels on hover
if (_lastHitIdx >= 0 && _lastHitIdx < samples.Count)
{
var rect = _hitBoxes[_lastHitIdx];
var tooltip = new FormattedText(
$"{samples[_lastHitIdx].Count}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
12.0,
LabelBrush);
var tx = rect.X - (tooltip.Width - rect.Width) * 0.5;
var ty = rect.Y - tooltip.Height - 4;
context.DrawText(tooltip, new Point(tx, ty));
}
}
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
var p = e.GetPosition(this);
for (int i = 0; i < _hitBoxes.Count; i++)
{
if (_hitBoxes[i].Contains(p))
{
if (_lastHitIdx != i)
{
_lastHitIdx = i;
InvalidateVisual();
}
return;
}
}
if (_lastHitIdx != -1)
{
_lastHitIdx = -1;
InvalidateVisual();
}
}
private readonly List<Rect> _hitBoxes = new List<Rect>();
private int _lastHitIdx = -1;
}
public partial class Statistics : ChromelessWindow
{
public Statistics()

View file

@ -47,8 +47,9 @@
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
IsCheckedChanged="OnToggleFilter"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
IsChecked="{Binding IsFiltered, Mode=TwoWay}"
IsVisible="{Binding !IsFolder}"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid>
</DataTemplate>
@ -78,8 +79,8 @@
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
IsCheckedChanged="OnToggleFilter"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
IsChecked="{Binding IsFiltered, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid>
</DataTemplate>

View file

@ -247,7 +247,7 @@ namespace SourceGit.Views
}
}
private void OnToggleFilter(object sender, RoutedEventArgs e)
private void OnToggleFilterClicked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton toggle && DataContext is ViewModels.Repository repo)
{
@ -258,7 +258,7 @@ namespace SourceGit.Views
target = tag;
if (target != null)
repo.UpdateFilter(target.Name, toggle.IsChecked == true);
repo.UpdateFilters([target.Name], toggle.IsChecked == true);
}
e.Handled = true;

View file

@ -437,8 +437,8 @@ namespace SourceGit.Views
base.OnLoaded(e);
TextArea.TextView.ContextRequested += OnTextViewContextRequested;
TextArea.TextView.PointerEntered += OnTextViewPointerEntered;
TextArea.TextView.PointerMoved += OnTextViewPointerMoved;
TextArea.TextView.PointerEntered += OnTextViewPointerChanged;
TextArea.TextView.PointerMoved += OnTextViewPointerChanged;
TextArea.TextView.PointerWheelChanged += OnTextViewPointerWheelChanged;
UpdateTextMate();
@ -449,8 +449,8 @@ namespace SourceGit.Views
base.OnUnloaded(e);
TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
TextArea.TextView.PointerEntered -= OnTextViewPointerEntered;
TextArea.TextView.PointerMoved -= OnTextViewPointerMoved;
TextArea.TextView.PointerEntered -= OnTextViewPointerChanged;
TextArea.TextView.PointerMoved -= OnTextViewPointerChanged;
TextArea.TextView.PointerWheelChanged -= OnTextViewPointerWheelChanged;
if (_textMate != null)
@ -510,25 +510,43 @@ namespace SourceGit.Views
e.Handled = true;
}
private void OnTextViewPointerEntered(object sender, PointerEventArgs e)
{
if (EnableChunkSelection && sender is TextView view)
UpdateSelectedChunk(e.GetPosition(view).Y + view.VerticalOffset);
}
private void OnTextViewPointerMoved(object sender, PointerEventArgs e)
private void OnTextViewPointerChanged(object sender, PointerEventArgs e)
{
if (EnableChunkSelection && sender is TextView view)
{
var chunk = SelectedChunk;
if (chunk != null)
var selection = TextArea.Selection;
if (selection == null || selection.IsEmpty)
{
var rect = new Rect(0, chunk.Y, Bounds.Width, chunk.Height);
if (rect.Contains(e.GetPosition(this)))
return;
if (_lastSelectStart != _lastSelectEnd)
{
_lastSelectStart = TextLocation.Empty;
_lastSelectEnd = TextLocation.Empty;
}
var chunk = SelectedChunk;
if (chunk != null)
{
var rect = new Rect(0, chunk.Y, Bounds.Width, chunk.Height);
if (rect.Contains(e.GetPosition(this)))
return;
}
UpdateSelectedChunk(e.GetPosition(view).Y + view.VerticalOffset);
return;
}
UpdateSelectedChunk(e.GetPosition(view).Y + view.VerticalOffset);
var start = selection.StartPosition.Location;
var end = selection.EndPosition.Location;
if (_lastSelectStart != start || _lastSelectEnd != end)
{
_lastSelectStart = start;
_lastSelectEnd = end;
UpdateSelectedChunk(e.GetPosition(view).Y + view.VerticalOffset);
return;
}
if (SelectedChunk == null)
UpdateSelectedChunk(e.GetPosition(view).Y + view.VerticalOffset);
}
}
@ -647,7 +665,9 @@ namespace SourceGit.Views
}
private TextMate.Installation _textMate = null;
protected LineStyleTransformer _lineStyleTransformer = null;
private TextLocation _lastSelectStart = TextLocation.Empty;
private TextLocation _lastSelectEnd = TextLocation.Empty;
private LineStyleTransformer _lineStyleTransformer = null;
}
public class CombinedTextDiffPresenter : ThemedTextDiffPresenter
@ -674,18 +694,6 @@ namespace SourceGit.Views
return 0;
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
var scroller = (ScrollViewer)e.NameScope.Find("PART_ScrollViewer");
if (scroller != null)
{
scroller.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay));
scroller.GotFocus += (_, _) => TrySetChunk(null);
}
}
public override void UpdateSelectedChunk(double y)
{
var diff = DataContext as Models.TextDiff;
@ -802,6 +810,27 @@ namespace SourceGit.Views
}
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
var scroller = this.FindDescendantOfType<ScrollViewer>();
if (scroller != null)
{
scroller.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay));
scroller.GotFocus += OnTextViewScrollGotFocus;
}
}
protected override void OnUnloaded(RoutedEventArgs e)
{
var scroller = this.FindDescendantOfType<ScrollViewer>();
if (scroller != null)
scroller.GotFocus -= OnTextViewScrollGotFocus;
base.OnUnloaded(e);
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
@ -833,6 +862,16 @@ namespace SourceGit.Views
GC.Collect();
}
private void OnTextViewScrollGotFocus(object sender, GotFocusEventArgs e)
{
if (EnableChunkSelection && sender is ScrollViewer viewer)
{
var area = viewer.FindDescendantOfType<TextArea>();
if (!area.IsPointerOver)
TrySetChunk(null);
}
}
}
public class SingleSideTextDiffPresenter : ThemedTextDiffPresenter
@ -1001,8 +1040,6 @@ namespace SourceGit.Views
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
if (_scrollViewer != null)
{
_scrollViewer.ScrollChanged -= OnTextViewScrollChanged;
@ -1012,6 +1049,7 @@ namespace SourceGit.Views
TextArea.PointerWheelChanged -= OnTextAreaPointerWheelChanged;
base.OnUnloaded(e);
GC.Collect();
}
@ -1047,7 +1085,12 @@ namespace SourceGit.Views
private void OnTextViewScrollGotFocus(object sender, GotFocusEventArgs e)
{
TrySetChunk(null);
if (EnableChunkSelection && sender is ScrollViewer viewer)
{
var area = viewer.FindDescendantOfType<TextArea>();
if (!area.IsPointerOver)
TrySetChunk(null);
}
}
private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e)

View file

@ -25,7 +25,7 @@ namespace SourceGit.Views
e.Handled = true;
}
}
public class RepositoryListBox : ListBox
{
protected override Type StyleKeyOverride => typeof(ListBox);
@ -40,7 +40,7 @@ namespace SourceGit.Views
e.Handled = true;
}
}
if (!e.Handled)
base.OnKeyDown(e);
}

View file

@ -177,7 +177,7 @@
<v:CommitMessageTextBox Grid.Row="2" Text="{Binding CommitMessage, Mode=TwoWay}"/>
<!-- Commit Options -->
<Grid Grid.Row="3" Margin="0,6,0,0" ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto">
<Grid Grid.Row="3" Margin="0,6,0,0" ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto">
<Button Grid.Column="0"
Classes="icon_button"
Margin="4,0,0,0" Padding="0"
@ -185,7 +185,7 @@
ToolTip.Tip="{DynamicResource Text.WorkingCopy.CommitMessageHelper}"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="0">
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
<Path Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
</Button>
<Button Grid.Column="1"
@ -223,19 +223,38 @@
Padding="8,0"
Command="{Binding Commit}"
HotKey="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"
ToolTip.Tip="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="0"/>
ToolTip.VerticalOffset="0">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<TextBlock TextAlignment="Left" TextWrapping="Wrap">
<Run Text="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"/>
<Run Foreground="{DynamicResource Brush.FG2}" Text="{DynamicResource Text.WorkingCopy.CommitTip}"/>
</TextBlock>
<TextBlock TextAlignment="Left" TextWrapping="Wrap" Margin="0,4,0,0">
<Run Text="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+⇧+Enter}"/>
<Run Foreground="{DynamicResource Brush.FG2}" Text="{DynamicResource Text.WorkingCopy.CommitWithAutoStage}"/>
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</Button>
<!-- Invisible button just to add another hotkey `Ctrl+Shift+Enter` to commit with auto-stage -->
<Button Grid.Column="7"
Width="0" Height="0"
Background="Transparent"
Command="{Binding CommitWithAutoStage}"
HotKey="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}"/>
<Button Grid.Column="8"
Classes="flat"
Content="{DynamicResource Text.WorkingCopy.CommitAndPush}"
Height="28"
Margin="8,0,0,0"
Padding="8,0"
Command="{Binding CommitWithPush}"
HotKey="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}"
ToolTip.Tip="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}"
HotKey="Alt+Enter"
ToolTip.Tip="{OnPlatform Alt+Enter, macOS=⌥+Enter}"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="0"
IsVisible="{Binding IsCommitWithPushVisible}"/>

View file

@ -81,7 +81,6 @@ namespace SourceGit.Views
{
vm.Discard(selected);
e.Handled = true;
return;
}
}
}