Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/Commands/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ public async Task<string> GetAsync(string key)
return rs.StdOut.Trim();
}

// Get config value with type canonicalization
// Weird values will be converted by git, like "000" -> "false", "010" -> "true"
// git will report bad values like "fatal: bad boolean config value 'kkk' for 'core.untrackedcache'"
public async Task<bool?> GetBoolAsync(string key)
{
Args = $"config get --bool {key}";

var rs = await ReadToEndAsync().ConfigureAwait(false);
var stdout = rs.StdOut.Trim();
switch (rs.StdOut.Trim())
{
case "true":
return true;
case "false":
return false;
default:
// Illegal values behave as if they are not set
return null;
}
}

public async Task<bool> SetAsync(string key, string value, bool allowEmpty = false)
{
var scope = _isLocal ? "--local" : "--global";
Expand Down
63 changes: 54 additions & 9 deletions src/Commands/QueryLocalChanges.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
Expand All @@ -9,18 +10,21 @@ public partial class QueryLocalChanges : Command
{
[GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")]
private static partial Regex REG_FORMAT();
private static readonly string[] UNTRACKED = ["no", "all"];
private bool _includeUntracked;
private bool _useFastPathForUntrackedFiles;

public QueryLocalChanges(string repo, bool includeUntracked = true)
public QueryLocalChanges(string repo, bool includeUntracked = true, bool useFastPathForUntrackedFiles = false)
{
WorkingDirectory = repo;
Context = repo;
Args = $"--no-optional-locks status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain";
_includeUntracked = includeUntracked;
_useFastPathForUntrackedFiles = useFastPathForUntrackedFiles;
}

public async Task<List<Models.Change>> GetResultAsync()
private async Task<(List<Models.Change>, List<string>)> RunGitAndParseOutput()
{
var outs = new List<Models.Change>();
var outChanges = new List<Models.Change>();
var outUntrackedDirs = new List<string>();

try
{
Expand Down Expand Up @@ -153,16 +157,57 @@ public QueryLocalChanges(string repo, bool includeUntracked = true)
break;
}

if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None)
outs.Add(change);
if (change.WorkTree == Models.ChangeState.Untracked && change.Path.EndsWith("/"))
{
outUntrackedDirs.Add(change.Path);
}
else
{
if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None)
outChanges.Add(change);
}
}
}
catch
{
// Ignore exceptions.
}

return outs;
return (outChanges, outUntrackedDirs);
}
public async Task<List<Models.Change>> GetResultAsync()
{
if (!_useFastPathForUntrackedFiles)
{
Args = $"--no-optional-locks status -u{(_includeUntracked ? "all" : "no")} --ignore-submodules=dirty --porcelain";
var (changes, _) = await RunGitAndParseOutput().ConfigureAwait(false);
return changes;
}
else
{
// Collect untracked dirs
Args = $"--no-optional-locks status --ignore-submodules=dirty --porcelain";
var (changes, untrackedDirs) = await RunGitAndParseOutput().ConfigureAwait(false);

// 'git status' does not support pathspec-from-file
for (int i = 0; i < untrackedDirs.Count; i += 32)
{
var count = Math.Min(32, untrackedDirs.Count - i);
var step = untrackedDirs.GetRange(i, count);

Args = $"--no-optional-locks status -uall --ignore-submodules=dirty --porcelain --";
foreach (var dir in step)
{
Args += $" \"{dir}\"";
}

var (stepChanges, dirs) = await RunGitAndParseOutput().ConfigureAwait(false);
Debug.Assert(dirs.Count == 0);
changes.AddRange(stepChanges);
}

return changes;
}
}
}
}
2 changes: 1 addition & 1 deletion src/ViewModels/DropHead.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public override async Task<bool> Sure()
var log = _repo.CreateLog($"Drop '{Target.SHA}'");
Use(log);

var changes = await new Commands.QueryLocalChanges(_repo.FullPath, true).GetResultAsync();
var changes = await new Commands.QueryLocalChanges(_repo.FullPath, true, _repo.UseFastPathForUntrackedFiles).GetResultAsync();
var needAutoStash = changes.Count > 0;
var succ = false;

Expand Down
34 changes: 31 additions & 3 deletions src/ViewModels/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Avalonia.Threading;

using CommunityToolkit.Mvvm.ComponentModel;
using SourceGit.Commands;

namespace SourceGit.ViewModels
{
Expand Down Expand Up @@ -57,6 +58,11 @@ public bool HasAllowedSignersFile
get => _hasAllowedSignersFile;
}

public bool UseFastPathForUntrackedFiles
{
get => _useFastPathForUntrackedFiles;
}

public int SelectedViewIndex
{
get => _selectedViewIndex;
Expand Down Expand Up @@ -509,8 +515,29 @@ public void Open()
}

_lastFetchTime = DateTime.Now;
_autoFetchTimer = new Timer(AutoFetchByTimer, null, 5000, 5000);
RefreshAll();

Task.Run(async () =>
{
var canonicalizedConfig = new Commands.Config(FullPath);
Task<bool?> fsmonitor = canonicalizedConfig.GetBoolAsync("core.fsmonitor");
Task<bool?> untrackedCache = canonicalizedConfig.GetBoolAsync("core.untrackedCache");
Task<bool?> manyFiles = canonicalizedConfig.GetBoolAsync("feature.manyFiles");
_useFastPathForUntrackedFiles = (await fsmonitor) == true && ((await untrackedCache) == true || ((await untrackedCache) == null && (await manyFiles) == true));

// Run a normal, index-locking 'git status' to populate/update untracked cache
// Slow for the first time
if (_useFastPathForUntrackedFiles)
{
var status = new Command();
status.WorkingDirectory = FullPath;
status.Context = FullPath;
status.Args = "status";
await status.ExecAsync();
}

_autoFetchTimer = new Timer(AutoFetchByTimer, null, 5000, 5000);
RefreshAll();
});
}

public void Close()
Expand Down Expand Up @@ -1308,7 +1335,7 @@ public void RefreshWorkingCopyChanges()

Task.Run(async () =>
{
var changes = await new Commands.QueryLocalChanges(FullPath, _settings.IncludeUntrackedInLocalChanges)
var changes = await new Commands.QueryLocalChanges(FullPath, _settings.IncludeUntrackedInLocalChanges, UseFastPathForUntrackedFiles)
.GetResultAsync()
.ConfigureAwait(false);

Expand Down Expand Up @@ -1901,6 +1928,7 @@ private async Task AutoFetchOnUIThread()
private Models.HistoryFilterCollection _historyFilterCollection = null;
private Models.FilterMode _historyFilterMode = Models.FilterMode.None;
private bool _hasAllowedSignersFile = false;
private bool _useFastPathForUntrackedFiles = false;

private Models.Watcher _watcher = null;
private Histories _histories = null;
Expand Down
2 changes: 1 addition & 1 deletion src/ViewModels/Reword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public override async Task<bool> Sure()
var log = _repo.CreateLog("Reword HEAD");
Use(log);

var changes = await new Commands.QueryLocalChanges(_repo.FullPath, false).GetResultAsync();
var changes = await new Commands.QueryLocalChanges(_repo.FullPath, false, _repo.UseFastPathForUntrackedFiles).GetResultAsync();
var signOff = _repo.Settings.EnableSignOffForCommit;
var noVerify = _repo.Settings.NoVerifyOnCommit;
var needAutoStash = false;
Expand Down
2 changes: 1 addition & 1 deletion src/ViewModels/SquashOrFixupHead.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public override async Task<bool> Sure()
var log = _repo.CreateLog(IsFixupMode ? "Fixup" : "Squash");
Use(log);

var changes = await new Commands.QueryLocalChanges(_repo.FullPath, false).GetResultAsync();
var changes = await new Commands.QueryLocalChanges(_repo.FullPath, false, _repo.UseFastPathForUntrackedFiles).GetResultAsync();
var signOff = _repo.Settings.EnableSignOffForCommit;
var noVerify = _repo.Settings.NoVerifyOnCommit;
var needAutoStash = false;
Expand Down
2 changes: 1 addition & 1 deletion src/ViewModels/StashChanges.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public override async Task<bool> Sure()
}
else
{
var all = await new Commands.QueryLocalChanges(_repo.FullPath, false)
var all = await new Commands.QueryLocalChanges(_repo.FullPath, false, _repo.UseFastPathForUntrackedFiles)
.Use(log)
.GetResultAsync();

Expand Down